xref: /aosp_15_r20/bootable/libbootloader/gbl/libefi/src/protocol.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
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