// 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. //! EFI protocol wrappers to provide Rust-safe APIs for usage. use core::ptr::null_mut; use crate::{DeviceHandle, EfiEntry}; use efi_types::*; pub mod block_io; pub mod block_io2; pub mod device_path; pub mod dt_fixup; pub mod gbl_efi_ab_slot; pub mod gbl_efi_avb; pub mod gbl_efi_fastboot; pub mod gbl_efi_fastboot_usb; pub mod gbl_efi_image_loading; pub mod gbl_efi_os_configuration; pub mod loaded_image; pub mod riscv; pub mod simple_network; pub mod simple_text_input; pub mod simple_text_output; use liberror::{Error, Result}; /// 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) -> Result<&T::InterfaceType> { // SAFETY: EFI protocol interface data structure. unsafe { self.interface.as_ref() }.ok_or(Error::InvalidInput) } /// 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() { // Currently we open all protocols using flags BY_HANDLE_PROTOCOL. The flag allows a // protocol to be opened for multiple copies, which is needed if a UEFI protocol // implementation also require access for other protocols. But if any one of them is // closed, all other opened copies will be affected. Therefore for now we don't close // the protocol on drop. In the future when we start using other flags such as // EXCLUSIVE, we should perform protocol close based on the open flags. // self.efi_entry.system_table().boot_services().close_protocol::(self.device).unwrap(); } } } /// Macro to perform an EFI protocol function call. /// /// In the first variant, the first argument is the function pointer, /// and the following arguments are passed through as protocol args. /// /// With our [Protocol] struct, usage generally looks something like: /// /// ``` /// efi_call!( /// self.interface()?.protocol_function_name, /// self.interface, /// arg1, /// arg2, /// ... /// ) /// ``` /// Most efi_call! invocations should use the first variant. /// /// With the second variant, the first argument is an expression that references /// a buffer in-out size parameter. /// This is part of a pattern used by some protocol methods /// that take an output buffer and an in-out buffer size: /// if the method returns EFI_STATUS_BUFFER_TOO_SMALL, /// the size is mutated to contain the minimum required buffer size. /// The caller can then allocate a larger buffer and reattempt the method call. /// /// Usage generally looks something like: /// ``` /// efi_call!( /// @bufsize arg2, /// self.interface()?.protocol_function_name, /// self.interface, /// arg1, /// &mut arg2, /// ... /// ) /// ``` #[macro_export] macro_rules! efi_call { ( $method:expr, $($x:expr),*$(,)? ) => { { use liberror::{Error, Result, efi_status_to_result}; let res: Result<()> = match $method { None => Err(Error::NotFound), Some(f) => efi_status_to_result(f($($x,)*)), }; res } }; ( @bufsize $size:expr, $method:expr, $($x:expr),*$(,)? ) => { { use liberror::{Error, Result, efi_status_to_result}; use efi_types::EFI_STATUS_BUFFER_TOO_SMALL; let res: Result<()> = match $method { None => Err(Error::NotFound), Some(f) => { match f($($x,)*) { EFI_STATUS_BUFFER_TOO_SMALL => Err(Error::BufferTooSmall(Some($size))), r => efi_status_to_result(r), } }, }; 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); }); }) } }