// Copyright 2023, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use core::ptr::null_mut; use crate::defs::*; use crate::{DeviceHandle, EfiEntry, EfiResult}; pub mod android_boot; pub mod block_io; pub mod device_path; pub mod loaded_image; pub mod riscv; pub mod simple_network; pub mod simple_text_input; pub mod simple_text_output; /// ProtocolInfo provides GUID info and the EFI data structure type for a protocol. pub trait ProtocolInfo { /// Data structure type of the interface. type InterfaceType; /// GUID of the protocol. const GUID: EfiGuid; } /// A generic type for representing an EFI protcol. pub struct Protocol<'a, T: ProtocolInfo> { // The handle to the device offering the protocol. It's needed for closing the protocol. device: DeviceHandle, // The interface protocol itself. interface: *mut T::InterfaceType, // The `EfiEntry` data efi_entry: &'a EfiEntry, } /// A base implementation for Protocol. /// Protocol will have additional implementation based on type `T`. impl<'a, T: ProtocolInfo> Protocol<'a, T> { /// Create a new instance with the given device handle, interface pointer and `EfiEntry` data. /// /// # Safety /// /// Caller needs to ensure that /// /// * `interface` points to a valid object of type T::InterfaceType. /// /// * Object pointed to by `interface` must live as long as the create `Protocol` or 'a. pub(crate) unsafe fn new( device: DeviceHandle, interface: *mut T::InterfaceType, efi_entry: &'a EfiEntry, ) -> Self { Self { device, interface, efi_entry } } /// Returns the EFI data structure for the protocol interface. pub fn interface(&self) -> EfiResult<&T::InterfaceType> { // SAFETY: EFI protocol interface data structure. unsafe { self.interface.as_ref() }.ok_or_else(|| EFI_STATUS_INVALID_PARAMETER.into()) } /// Returns the reference to EFI entry. pub fn efi_entry(&self) -> &'a EfiEntry { self.efi_entry } /// Returns the mutable pointer of the interface. Invisible from outside. Application should /// not have any need to alter the content of interface data. pub(crate) fn interface_ptr(&self) -> *mut T::InterfaceType { self.interface } } impl Drop for Protocol<'_, T> { fn drop(&mut self) { // If the device handle is not specified when creating the Protocol, treat the // handle as a static permanent reference and don't close it. An example is // `EFI_SYSTEM_TABLE.ConOut`. if self.device.0 != null_mut() { self.efi_entry.system_table().boot_services().close_protocol::(self.device).unwrap(); } } } impl EfiGuid { pub const fn new(data1: u32, data2: u16, data3: u16, data4: [u8; 8usize]) -> Self { EfiGuid { data1, data2, data3, data4 } } } #[macro_export] macro_rules! efi_call { ( $method:expr, $($x:expr),*$(,)? ) => { { let res: EfiResult<()> = match $method { None => Err(EFI_STATUS_NOT_FOUND.into()), Some(f) => map_efi_err(f($($x,)*)) }; res } }; } // Following are protocol specific implementations for Protocol. // TODO(300168989): Consdier splitting each protocol into separate file as we add more protocols. #[cfg(test)] mod test { use super::*; use crate::test::*; #[test] fn test_dont_close_protocol_without_device_handle() { run_test(|image_handle, systab_ptr| { let efi_entry = EfiEntry { image_handle, systab_ptr }; let mut block_io: EfiBlockIoProtocol = Default::default(); // SAFETY: `block_io` is a EfiBlockIoProtocol and out lives the created Protocol. unsafe { Protocol::::new( DeviceHandle(null_mut()), &mut block_io as *mut _, &efi_entry, ); } efi_call_traces().with(|traces| { assert_eq!(traces.borrow_mut().close_protocol_trace.inputs.len(), 0); }); }) } }