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 use core::ptr::null_mut;
16 
17 use crate::defs::*;
18 use crate::{DeviceHandle, EfiEntry, EfiResult};
19 
20 pub mod android_boot;
21 pub mod block_io;
22 pub mod device_path;
23 pub mod loaded_image;
24 pub mod riscv;
25 pub mod simple_network;
26 pub mod simple_text_input;
27 pub mod simple_text_output;
28 
29 /// ProtocolInfo provides GUID info and the EFI data structure type for a protocol.
30 pub trait ProtocolInfo {
31     /// Data structure type of the interface.
32     type InterfaceType;
33     /// GUID of the protocol.
34     const GUID: EfiGuid;
35 }
36 
37 /// A generic type for representing an EFI protcol.
38 pub struct Protocol<'a, T: ProtocolInfo> {
39     // The handle to the device offering the protocol. It's needed for closing the protocol.
40     device: DeviceHandle,
41     // The interface protocol itself.
42     interface: *mut T::InterfaceType,
43     // The `EfiEntry` data
44     efi_entry: &'a EfiEntry,
45 }
46 
47 /// A base implementation for Protocol<T>.
48 /// Protocol<T> will have additional implementation based on type `T`.
49 impl<'a, T: ProtocolInfo> Protocol<'a, T> {
50     /// Create a new instance with the given device handle, interface pointer and `EfiEntry` data.
51     ///
52     /// # Safety
53     ///
54     /// Caller needs to ensure that
55     ///
56     /// * `interface` points to a valid object of type T::InterfaceType.
57     ///
58     /// * 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, ) -> Self59     pub(crate) unsafe fn new(
60         device: DeviceHandle,
61         interface: *mut T::InterfaceType,
62         efi_entry: &'a EfiEntry,
63     ) -> Self {
64         Self { device, interface, efi_entry }
65     }
66 
67     /// Returns the EFI data structure for the protocol interface.
interface(&self) -> EfiResult<&T::InterfaceType>68     pub fn interface(&self) -> EfiResult<&T::InterfaceType> {
69         // SAFETY: EFI protocol interface data structure.
70         unsafe { self.interface.as_ref() }.ok_or_else(|| EFI_STATUS_INVALID_PARAMETER.into())
71     }
72 
73     /// Returns the reference to EFI entry.
efi_entry(&self) -> &'a EfiEntry74     pub fn efi_entry(&self) -> &'a EfiEntry {
75         self.efi_entry
76     }
77 
78     /// Returns the mutable pointer of the interface. Invisible from outside. Application should
79     /// not have any need to alter the content of interface data.
interface_ptr(&self) -> *mut T::InterfaceType80     pub(crate) fn interface_ptr(&self) -> *mut T::InterfaceType {
81         self.interface
82     }
83 }
84 
85 impl<T: ProtocolInfo> Drop for Protocol<'_, T> {
drop(&mut self)86     fn drop(&mut self) {
87         // If the device handle is not specified when creating the Protocol<T>, treat the
88         // handle as a static permanent reference and don't close it. An example is
89         // `EFI_SYSTEM_TABLE.ConOut`.
90         if self.device.0 != null_mut() {
91             self.efi_entry.system_table().boot_services().close_protocol::<T>(self.device).unwrap();
92         }
93     }
94 }
95 
96 impl EfiGuid {
new(data1: u32, data2: u16, data3: u16, data4: [u8; 8usize]) -> Self97     pub const fn new(data1: u32, data2: u16, data3: u16, data4: [u8; 8usize]) -> Self {
98         EfiGuid { data1, data2, data3, data4 }
99     }
100 }
101 
102 #[macro_export]
103 macro_rules! efi_call {
104     ( $method:expr, $($x:expr),*$(,)? ) => {
105         {
106             let res: EfiResult<()> = match $method {
107                 None => Err(EFI_STATUS_NOT_FOUND.into()),
108                 Some(f) => map_efi_err(f($($x,)*))
109             };
110             res
111         }
112     };
113 }
114 
115 // Following are protocol specific implementations for Protocol<T>.
116 // TODO(300168989): Consdier splitting each protocol into separate file as we add more protocols.
117 
118 #[cfg(test)]
119 mod test {
120     use super::*;
121     use crate::test::*;
122 
123     #[test]
test_dont_close_protocol_without_device_handle()124     fn test_dont_close_protocol_without_device_handle() {
125         run_test(|image_handle, systab_ptr| {
126             let efi_entry = EfiEntry { image_handle, systab_ptr };
127             let mut block_io: EfiBlockIoProtocol = Default::default();
128             // SAFETY: `block_io` is a EfiBlockIoProtocol and out lives the created Protocol.
129             unsafe {
130                 Protocol::<block_io::BlockIoProtocol>::new(
131                     DeviceHandle(null_mut()),
132                     &mut block_io as *mut _,
133                     &efi_entry,
134                 );
135             }
136             efi_call_traces().with(|traces| {
137                 assert_eq!(traces.borrow_mut().close_protocol_trace.inputs.len(), 0);
138             });
139         })
140     }
141 }
142