1 // Copyright 2023, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! EFI protocol wrappers to provide Rust-safe APIs for usage. 16 17 use core::ptr::null_mut; 18 19 use crate::{DeviceHandle, EfiEntry}; 20 use efi_types::*; 21 22 pub mod block_io; 23 pub mod block_io2; 24 pub mod device_path; 25 pub mod dt_fixup; 26 pub mod gbl_efi_ab_slot; 27 pub mod gbl_efi_avb; 28 pub mod gbl_efi_fastboot; 29 pub mod gbl_efi_fastboot_usb; 30 pub mod gbl_efi_image_loading; 31 pub mod gbl_efi_os_configuration; 32 pub mod loaded_image; 33 pub mod riscv; 34 pub mod simple_network; 35 pub mod simple_text_input; 36 pub mod simple_text_output; 37 38 use liberror::{Error, Result}; 39 40 /// ProtocolInfo provides GUID info and the EFI data structure type for a protocol. 41 pub trait ProtocolInfo { 42 /// Data structure type of the interface. 43 type InterfaceType; 44 /// GUID of the protocol. 45 const GUID: EfiGuid; 46 } 47 48 /// A generic type for representing an EFI protcol. 49 pub struct Protocol<'a, T: ProtocolInfo> { 50 // The handle to the device offering the protocol. It's needed for closing the protocol. 51 device: DeviceHandle, 52 // The interface protocol itself. 53 interface: *mut T::InterfaceType, 54 // The `EfiEntry` data 55 efi_entry: &'a EfiEntry, 56 } 57 58 /// A base implementation for Protocol<T>. 59 /// Protocol<T> will have additional implementation based on type `T`. 60 impl<'a, T: ProtocolInfo> Protocol<'a, T> { 61 /// Create a new instance with the given device handle, interface pointer and `EfiEntry` data. 62 /// 63 /// # Safety 64 /// 65 /// Caller needs to ensure that 66 /// 67 /// * `interface` points to a valid object of type T::InterfaceType. 68 /// 69 /// * Object pointed to by `interface` must live as long as the create `Protocol` or 'a. new( device: DeviceHandle, interface: *mut T::InterfaceType, efi_entry: &'a EfiEntry, ) -> Self70 pub(crate) unsafe fn new( 71 device: DeviceHandle, 72 interface: *mut T::InterfaceType, 73 efi_entry: &'a EfiEntry, 74 ) -> Self { 75 Self { device, interface, efi_entry } 76 } 77 78 /// Returns the EFI data structure for the protocol interface. interface(&self) -> Result<&T::InterfaceType>79 pub fn interface(&self) -> Result<&T::InterfaceType> { 80 // SAFETY: EFI protocol interface data structure. 81 unsafe { self.interface.as_ref() }.ok_or(Error::InvalidInput) 82 } 83 84 /// Returns the reference to EFI entry. efi_entry(&self) -> &'a EfiEntry85 pub fn efi_entry(&self) -> &'a EfiEntry { 86 self.efi_entry 87 } 88 89 /// Returns the mutable pointer of the interface. Invisible from outside. Application should 90 /// not have any need to alter the content of interface data. interface_ptr(&self) -> *mut T::InterfaceType91 pub(crate) fn interface_ptr(&self) -> *mut T::InterfaceType { 92 self.interface 93 } 94 } 95 96 impl<T: ProtocolInfo> Drop for Protocol<'_, T> { drop(&mut self)97 fn drop(&mut self) { 98 // If the device handle is not specified when creating the Protocol<T>, treat the 99 // handle as a static permanent reference and don't close it. An example is 100 // `EFI_SYSTEM_TABLE.ConOut`. 101 if self.device.0 != null_mut() { 102 // Currently we open all protocols using flags BY_HANDLE_PROTOCOL. The flag allows a 103 // protocol to be opened for multiple copies, which is needed if a UEFI protocol 104 // implementation also require access for other protocols. But if any one of them is 105 // closed, all other opened copies will be affected. Therefore for now we don't close 106 // the protocol on drop. In the future when we start using other flags such as 107 // EXCLUSIVE, we should perform protocol close based on the open flags. 108 109 // self.efi_entry.system_table().boot_services().close_protocol::<T>(self.device).unwrap(); 110 } 111 } 112 } 113 114 /// Macro to perform an EFI protocol function call. 115 /// 116 /// In the first variant, the first argument is the function pointer, 117 /// and the following arguments are passed through as protocol args. 118 /// 119 /// With our [Protocol] struct, usage generally looks something like: 120 /// 121 /// ``` 122 /// efi_call!( 123 /// self.interface()?.protocol_function_name, 124 /// self.interface, 125 /// arg1, 126 /// arg2, 127 /// ... 128 /// ) 129 /// ``` 130 /// Most efi_call! invocations should use the first variant. 131 /// 132 /// With the second variant, the first argument is an expression that references 133 /// a buffer in-out size parameter. 134 /// This is part of a pattern used by some protocol methods 135 /// that take an output buffer and an in-out buffer size: 136 /// if the method returns EFI_STATUS_BUFFER_TOO_SMALL, 137 /// the size is mutated to contain the minimum required buffer size. 138 /// The caller can then allocate a larger buffer and reattempt the method call. 139 /// 140 /// Usage generally looks something like: 141 /// ``` 142 /// efi_call!( 143 /// @bufsize arg2, 144 /// self.interface()?.protocol_function_name, 145 /// self.interface, 146 /// arg1, 147 /// &mut arg2, 148 /// ... 149 /// ) 150 /// ``` 151 #[macro_export] 152 macro_rules! efi_call { 153 ( $method:expr, $($x:expr),*$(,)? ) => { 154 { 155 use liberror::{Error, Result, efi_status_to_result}; 156 let res: Result<()> = match $method { 157 None => Err(Error::NotFound), 158 Some(f) => efi_status_to_result(f($($x,)*)), 159 }; 160 res 161 } 162 }; 163 ( @bufsize $size:expr, $method:expr, $($x:expr),*$(,)? ) => { 164 { 165 use liberror::{Error, Result, efi_status_to_result}; 166 use efi_types::EFI_STATUS_BUFFER_TOO_SMALL; 167 let res: Result<()> = match $method { 168 None => Err(Error::NotFound), 169 Some(f) => { 170 match f($($x,)*) { 171 EFI_STATUS_BUFFER_TOO_SMALL => Err(Error::BufferTooSmall(Some($size))), 172 r => efi_status_to_result(r), 173 } 174 }, 175 }; 176 res 177 } 178 }; 179 } 180 181 // Following are protocol specific implementations for Protocol<T>. 182 // TODO(300168989): Consdier splitting each protocol into separate file as we add more protocols. 183 184 #[cfg(test)] 185 mod test { 186 use super::*; 187 use crate::test::*; 188 189 #[test] test_dont_close_protocol_without_device_handle()190 fn test_dont_close_protocol_without_device_handle() { 191 run_test(|image_handle, systab_ptr| { 192 let efi_entry = EfiEntry { image_handle, systab_ptr }; 193 let mut block_io: EfiBlockIoProtocol = Default::default(); 194 // SAFETY: `block_io` is a EfiBlockIoProtocol and out lives the created Protocol. 195 unsafe { 196 Protocol::<block_io::BlockIoProtocol>::new( 197 DeviceHandle(null_mut()), 198 &mut block_io as *mut _, 199 &efi_entry, 200 ); 201 } 202 efi_call_traces().with(|traces| { 203 assert_eq!(traces.borrow_mut().close_protocol_trace.inputs.len(), 0); 204 }); 205 }) 206 } 207 } 208