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 //! The library implements Rust wrappers for a set of UEFI interfaces needed by GBL. It also
16 //! provides a global allocator and supports auto release of dynamic UEFI resource such as
17 //! protocols and UEFI allocated buffers.
18 //!
19 //! # Examples
20 //!
21 //! The following example covers the basic use pattern of the library. It scans all block devices
22 //! and prints out the device path, block size and io alignment info for each of them.
23 //!
24 //! ```
25 //! fn main(image: EfiHandle, systab_ptr: *mut EfiSystemTable) -> efi::EfiResult<()> {
26 //!     let efi_entry = initialize(image, systab_ptr)?;
27 //!     let mut con_out = efi_entry.system_table().con_out()?;
28 //!     let boot_services = efi_entry.system_table().boot_services();
29 //!     let path_to_text = boot_services.find_first_and_open::<DevicePathToTextProtocol>()?;
30 //!
31 //!     write!(con_out, "Scanning block devices...\n")?;
32 //!
33 //!     let block_handles = boot_services.locate_handle_buffer_by_protocol::<BlockIoProtocol>()?;
34 //!
35 //!     for (i, handle) in block_handles.handles().iter().enumerate() {
36 //!         let path = boot_services.open_protocol::<DevicePathProtocol>(*handle)?;
37 //!         write!(con_out, "Block Device #{}: ", i)?;
38 //!         path_to_text.convert_device_path_to_text(&path, false, false)?.print()?;
39 //!         write!(con_out, "\n")?;
40 //!
41 //!         let block_io_protocol = boot_services.open_protocol::<BlockIoProtocol>(*handle)?;
42 //!         let media = block_io_protocol.media()?;
43 //!         write!(con_out, "  block size = {}\n", media.block_size)?;
44 //!         write!(con_out, "  io alignment = {}\n", media.io_align)?;
45 //!     }
46 //!
47 //!     Ok(())
48 //! }
49 //! ```
50 
51 #![cfg_attr(not(test), no_std)]
52 
53 extern crate alloc;
54 use alloc::vec::Vec;
55 
56 use core::ptr::null_mut;
57 use core::slice::from_raw_parts;
58 #[cfg(not(test))]
59 use core::{fmt::Write, panic::PanicInfo};
60 
61 use zerocopy::Ref;
62 
63 #[rustfmt::skip]
64 pub mod defs;
65 use defs::*;
66 
67 #[cfg(not(test))]
68 mod allocation;
69 
70 #[cfg(not(test))]
71 pub use allocation::{efi_free, efi_malloc};
72 
73 pub mod protocol;
74 use protocol::simple_text_output::SimpleTextOutputProtocol;
75 use protocol::{Protocol, ProtocolInfo};
76 
77 mod error {
78     use super::defs::EFI_STATUS_SUCCESS;
79     use super::EfiStatus;
80 
81     #[derive(Debug, Copy, Clone, PartialEq)]
82     pub enum ErrorTypes {
83         Unknown,
84         EfiStatusError(EfiStatus),
85     }
86 
87     #[derive(Debug, PartialEq)]
88     pub struct EfiError(ErrorTypes);
89 
90     impl EfiError {
err(&self) -> ErrorTypes91         pub fn err(&self) -> ErrorTypes {
92             self.0
93         }
94 
95         /// Checks if the error is a particular EFI error.
is_efi_err(&self, code: EfiStatus) -> bool96         pub fn is_efi_err(&self, code: EfiStatus) -> bool {
97             *self == code.into()
98         }
99     }
100 
101     impl From<EfiStatus> for EfiError {
from(efi_status: EfiStatus) -> EfiError102         fn from(efi_status: EfiStatus) -> EfiError {
103             EfiError(match efi_status {
104                 EFI_STATUS_SUCCESS => ErrorTypes::Unknown,
105                 _ => ErrorTypes::EfiStatusError(
106                     // Remove the highest bit in the error code so that it's eaiser to interpret
107                     // when printing.
108                     efi_status & !(1 << (core::mem::size_of::<EfiStatus>() * 8 - 1)),
109                 ),
110             })
111         }
112     }
113 }
114 
115 pub use error::*;
116 
117 /// Result type for this library.
118 pub type EfiResult<T> = core::result::Result<T, EfiError>;
119 
120 /// Helper method to convert an EFI status code to EfiResult.
map_efi_err(code: EfiStatus) -> EfiResult<()>121 fn map_efi_err(code: EfiStatus) -> EfiResult<()> {
122     match code {
123         EFI_STATUS_SUCCESS => Ok(()),
124         _ => Err(code.into()),
125     }
126 }
127 
128 /// `EfiEntry` stores the EFI system table pointer and image handle passed from the entry point.
129 /// It's the root data structure that derives all other wrapper APIs and structures.
130 pub struct EfiEntry {
131     image_handle: EfiHandle,
132     systab_ptr: *const EfiSystemTable,
133 }
134 
135 impl EfiEntry {
136     /// Gets an instance of `SystemTable`.
system_table(&self) -> SystemTable137     pub fn system_table(&self) -> SystemTable {
138         // SAFETY: Pointers to UEFI data strucutres.
139         SystemTable { efi_entry: self, table: unsafe { self.systab_ptr.as_ref() }.unwrap() }
140     }
141 
142     /// Gets the image handle.
image_handle(&self) -> DeviceHandle143     pub fn image_handle(&self) -> DeviceHandle {
144         DeviceHandle(self.image_handle)
145     }
146 }
147 
148 /// Creates an `EfiEntry` and initialize EFI global allocator.
149 ///
150 /// # Safety
151 ///
152 /// The API modifies internal global state. It should only be called once upon EFI entry to obtain
153 /// an instance of `EfiEntry` for accessing other APIs. Calling it again when EFI APIs are already
154 /// being used can introduce a risk of race.
155 #[cfg(not(test))]
initialize( image_handle: EfiHandle, systab_ptr: *const EfiSystemTable, ) -> EfiResult<EfiEntry>156 pub unsafe fn initialize(
157     image_handle: EfiHandle,
158     systab_ptr: *const EfiSystemTable,
159 ) -> EfiResult<EfiEntry> {
160     let efi_entry = EfiEntry { image_handle, systab_ptr };
161     // SAFETY: By safety requirement of this function, `initialize` is only called once upon
162     // entering EFI application, where there should be no event notify function that can be
163     // triggered.
164     unsafe {
165         // Create another one for internal global allocator.
166         allocation::init_efi_global_alloc(EfiEntry { image_handle, systab_ptr })?;
167     }
168     Ok(efi_entry)
169 }
170 
171 /// A helper for getting a subslice with an aligned address.
aligned_subslice(buffer: &mut [u8], alignment: usize) -> Option<&mut [u8]>172 pub fn aligned_subslice(buffer: &mut [u8], alignment: usize) -> Option<&mut [u8]> {
173     let addr = buffer.as_ptr() as usize;
174     let aligned_offset = addr
175         .checked_add(alignment - 1)?
176         .checked_div(alignment)?
177         .checked_mul(alignment)?
178         .checked_sub(addr)?;
179     buffer.get_mut(aligned_offset..)
180 }
181 
182 /// Exits boot service and returns the memory map in the given buffer.
183 ///
184 /// The API takes ownership of the given `entry` and causes it to go out of scope.
185 /// This enforces strict compile time check that any reference/borrow in effect will cause compile
186 /// errors.
187 ///
188 /// Existing heap allocated memories will maintain their states. All system memory including them
189 /// will be under onwership of the subsequent OS or OS loader code.
exit_boot_services(entry: EfiEntry, mmap_buffer: &mut [u8]) -> EfiResult<EfiMemoryMap>190 pub fn exit_boot_services(entry: EfiEntry, mmap_buffer: &mut [u8]) -> EfiResult<EfiMemoryMap> {
191     let aligned = aligned_subslice(mmap_buffer, core::mem::align_of::<EfiMemoryDescriptor>())
192         .ok_or_else::<EfiError, _>(|| EFI_STATUS_BUFFER_TOO_SMALL.into())?;
193 
194     let res = entry.system_table().boot_services().get_memory_map(aligned)?;
195     entry.system_table().boot_services().exit_boot_services(&res)?;
196     // SAFETY:
197     // At this point, UEFI has successfully exited boot services and no event/notification can be
198     // triggered.
199     #[cfg(not(test))]
200     unsafe {
201         allocation::exit_efi_global_alloc();
202     }
203     Ok(res)
204 }
205 
206 /// `SystemTable` provides methods for accessing fields in `EFI_SYSTEM_TABLE`.
207 #[derive(Clone, Copy)]
208 pub struct SystemTable<'a> {
209     efi_entry: &'a EfiEntry,
210     table: &'a EfiSystemTable,
211 }
212 
213 impl<'a> SystemTable<'a> {
214     /// Creates an instance of `BootServices`
boot_services(&self) -> BootServices<'a>215     pub fn boot_services(&self) -> BootServices<'a> {
216         BootServices {
217             efi_entry: self.efi_entry,
218             // SAFETY: Pointers to UEFI data strucutres.
219             boot_services: unsafe { self.table.boot_services.as_ref() }.unwrap(),
220         }
221     }
222 
223     /// Creates an instance of `RuntimeServices`
runtime_services(&self) -> RuntimeServices<'a>224     pub fn runtime_services(&self) -> RuntimeServices<'a> {
225         RuntimeServices {
226             // SAFETY: Pointers to UEFI data strucutres.
227             runtime_services: unsafe { self.table.runtime_services.as_ref() }.unwrap(),
228         }
229     }
230 
231     /// Gets the `EFI_SYSTEM_TABLE.ConOut` field.
con_out(&self) -> EfiResult<Protocol<'a, SimpleTextOutputProtocol>>232     pub fn con_out(&self) -> EfiResult<Protocol<'a, SimpleTextOutputProtocol>> {
233         // SAFETY: `EFI_SYSTEM_TABLE.ConOut` is a pointer to EfiSimpleTextOutputProtocol structure
234         // by definition. It lives until ExitBootService and thus as long as `self.efi_entry` or,
235         // 'a
236         Ok(unsafe {
237             Protocol::<SimpleTextOutputProtocol>::new(
238                 // No device handle. This protocol is a permanent reference.
239                 DeviceHandle(null_mut()),
240                 self.table.con_out,
241                 self.efi_entry,
242             )
243         })
244     }
245 
246     /// Gets the `EFI_SYSTEM_TABLE.ConfigurationTable` array.
configuration_table(&self) -> Option<&[EfiConfigurationTable]>247     pub fn configuration_table(&self) -> Option<&[EfiConfigurationTable]> {
248         match self.table.configuration_table.is_null() {
249             true => None,
250             // SAFETY: Non-null pointer to EFI configuration table.
251             false => unsafe {
252                 Some(from_raw_parts(
253                     self.table.configuration_table,
254                     self.table.number_of_table_entries,
255                 ))
256             },
257         }
258     }
259 }
260 
261 /// `BootServices` provides methods for accessing various EFI_BOOT_SERVICES interfaces.
262 #[derive(Clone, Copy)]
263 pub struct BootServices<'a> {
264     efi_entry: &'a EfiEntry,
265     boot_services: &'a EfiBootService,
266 }
267 
268 impl<'a> BootServices<'a> {
269     /// Wrapper of `EFI_BOOT_SERVICES.AllocatePool()`.
270     #[allow(dead_code)]
allocate_pool( &self, pool_type: EfiMemoryType, size: usize, ) -> EfiResult<*mut core::ffi::c_void>271     fn allocate_pool(
272         &self,
273         pool_type: EfiMemoryType,
274         size: usize,
275     ) -> EfiResult<*mut core::ffi::c_void> {
276         let mut out: *mut core::ffi::c_void = null_mut();
277         // SAFETY: `EFI_BOOT_SERVICES` method call.
278         unsafe {
279             efi_call!(self.boot_services.allocate_pool, pool_type, size, &mut out)?;
280         }
281         Ok(out)
282     }
283 
284     /// Wrapper of `EFI_BOOT_SERVICES.FreePool()`.
free_pool(&self, buf: *mut core::ffi::c_void) -> EfiResult<()>285     fn free_pool(&self, buf: *mut core::ffi::c_void) -> EfiResult<()> {
286         // SAFETY: `EFI_BOOT_SERVICES` method call.
287         unsafe { efi_call!(self.boot_services.free_pool, buf) }
288     }
289 
290     /// Wrapper of `EFI_BOOT_SERVICES.OpenProtocol()`.
open_protocol<T: ProtocolInfo>( &self, handle: DeviceHandle, ) -> EfiResult<Protocol<'a, T>>291     pub fn open_protocol<T: ProtocolInfo>(
292         &self,
293         handle: DeviceHandle,
294     ) -> EfiResult<Protocol<'a, T>> {
295         let mut out_handle: EfiHandle = null_mut();
296         // SAFETY: EFI_BOOT_SERVICES method call.
297         unsafe {
298             efi_call!(
299                 self.boot_services.open_protocol,
300                 handle.0,
301                 &T::GUID,
302                 &mut out_handle as *mut _,
303                 self.efi_entry.image_handle().0,
304                 null_mut(),
305                 EFI_OPEN_PROTOCOL_ATTRIBUTE_BY_HANDLE_PROTOCOL
306             )?;
307         }
308         // SAFETY: `EFI_SYSTEM_TABLE.OpenProtocol` returns a valid pointer to `T::InterfaceType`
309         // on success. The pointer remains valid until closed by
310         // `EFI_BOOT_SERVICES.CloseProtocol()` when Protocol goes out of scope.
311         Ok(unsafe { Protocol::<T>::new(handle, out_handle as *mut _, self.efi_entry) })
312     }
313 
314     /// Wrapper of `EFI_BOOT_SERVICES.CloseProtocol()`.
close_protocol<T: ProtocolInfo>(&self, handle: DeviceHandle) -> EfiResult<()>315     fn close_protocol<T: ProtocolInfo>(&self, handle: DeviceHandle) -> EfiResult<()> {
316         // SAFETY: EFI_BOOT_SERVICES method call.
317         unsafe {
318             efi_call!(
319                 self.boot_services.close_protocol,
320                 handle.0,
321                 &T::GUID,
322                 self.efi_entry.image_handle().0,
323                 null_mut()
324             )
325         }
326     }
327 
328     /// Call `EFI_BOOT_SERVICES.LocateHandleBuffer()` with fixed
329     /// `EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL` and without search key.
locate_handle_buffer_by_protocol<T: ProtocolInfo>( &self, ) -> EfiResult<LocatedHandles<'a>>330     pub fn locate_handle_buffer_by_protocol<T: ProtocolInfo>(
331         &self,
332     ) -> EfiResult<LocatedHandles<'a>> {
333         let mut num_handles: usize = 0;
334         let mut handles: *mut EfiHandle = null_mut();
335         // SAFETY: EFI_BOOT_SERVICES method call.
336         unsafe {
337             efi_call!(
338                 self.boot_services.locate_handle_buffer,
339                 EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL,
340                 &T::GUID,
341                 null_mut(),
342                 &mut num_handles as *mut usize as *mut _,
343                 &mut handles as *mut *mut EfiHandle
344             )?
345         };
346         // `handles` should be a valid pointer if the above succeeds. But just double check
347         // to be safe. If assert fails, then there's a bug in the UEFI firmware.
348         assert!(!handles.is_null());
349         Ok(LocatedHandles::new(handles, num_handles, self.efi_entry))
350     }
351 
352     /// Search and open the first found target EFI protocol.
find_first_and_open<T: ProtocolInfo>(&self) -> EfiResult<Protocol<'a, T>>353     pub fn find_first_and_open<T: ProtocolInfo>(&self) -> EfiResult<Protocol<'a, T>> {
354         // We don't use EFI_BOOT_SERVICES.LocateProtocol() because it doesn't give device handle
355         // which is required to close the protocol.
356         let handle = *self
357             .locate_handle_buffer_by_protocol::<T>()?
358             .handles()
359             .first()
360             .ok_or::<EfiError>(EFI_STATUS_NOT_FOUND.into())?;
361         self.open_protocol::<T>(handle)
362     }
363 
364     /// Wrapper of `EFI_BOOT_SERVICE.GetMemoryMap()`.
get_memory_map<'b>(&self, mmap_buffer: &'b mut [u8]) -> EfiResult<EfiMemoryMap<'b>>365     pub fn get_memory_map<'b>(&self, mmap_buffer: &'b mut [u8]) -> EfiResult<EfiMemoryMap<'b>> {
366         let mut mmap_size = mmap_buffer.len();
367         let mut map_key: usize = 0;
368         let mut descriptor_size: usize = 0;
369         let mut descriptor_version: u32 = 0;
370         // SAFETY: EFI_BOOT_SERVICES method call.
371         unsafe {
372             efi_call!(
373                 self.boot_services.get_memory_map,
374                 &mut mmap_size,
375                 mmap_buffer.as_mut_ptr() as *mut _,
376                 &mut map_key,
377                 &mut descriptor_size,
378                 &mut descriptor_version
379             )
380         }?;
381         Ok(EfiMemoryMap::new(
382             &mut mmap_buffer[..mmap_size],
383             map_key,
384             descriptor_size,
385             descriptor_version,
386         ))
387     }
388 
389     /// Wrapper of `EFI_BOOT_SERVICE.ExitBootServices()`.
exit_boot_services<'b>(&self, mmap: &'b EfiMemoryMap<'b>) -> EfiResult<()>390     fn exit_boot_services<'b>(&self, mmap: &'b EfiMemoryMap<'b>) -> EfiResult<()> {
391         // SAFETY: EFI_BOOT_SERVICES method call.
392         unsafe {
393             efi_call!(
394                 self.boot_services.exit_boot_services,
395                 self.efi_entry.image_handle().0,
396                 mmap.map_key()
397             )
398         }
399     }
400 
401     /// Wrapper of `EFI_BOOT_SERVICE.Stall()`.
stall(&self, micro: usize) -> EfiResult<()>402     pub fn stall(&self, micro: usize) -> EfiResult<()> {
403         // SAFETY: EFI_BOOT_SERVICES method call.
404         unsafe { efi_call!(self.boot_services.stall, micro) }
405     }
406 
407     /// Wrapper of `EFI_BOOT_SERVICE.CreateEvent()`.
408     ///
409     /// Args:
410     ///
411     ///  * `event_type`: The EFI event type.
412     ///  * `cb`: An optional `&'e mut EventNotify`, which implements the event
413     ///          notification function and provides the task level priority setting.
create_event<'n, 'e: 'n>( &self, event_type: EventType, mut cb: Option<&'n mut EventNotify<'e>>, ) -> EfiResult<Event<'a, 'n>>414     pub fn create_event<'n, 'e: 'n>(
415         &self,
416         event_type: EventType,
417         mut cb: Option<&'n mut EventNotify<'e>>,
418     ) -> EfiResult<Event<'a, 'n>> {
419         let mut efi_event: EfiEvent = null_mut();
420         let (tpl, c_callback, cookie): (EfiTpl, EfiEventNotify, *mut core::ffi::c_void) = match cb {
421             Some(ref mut event_notify) => {
422                 (event_notify.tpl as _, Some(efi_event_cb), *event_notify as *mut _ as _)
423             }
424             None => (0, None, null_mut()),
425         };
426         // SAFETY:
427         // Pointers passed are output/callback context pointers which will not be retained by the
428         // callback (`fn efi_event_cb()`).
429         // The returned `Event` enforces a borrow to `cb` for 'e. It closes the event when it
430         // goes out of scope. This ensures that `cb` lives at least as long as the event is in
431         // effect and there can be no other borrows to `cb`.
432         unsafe {
433             efi_call!(
434                 self.boot_services.create_event,
435                 event_type as u32,
436                 tpl as usize,
437                 c_callback,
438                 cookie,
439                 &mut efi_event
440             )?;
441         }
442         Ok(Event::new(
443             Some(self.efi_entry),
444             efi_event,
445             cb.map::<&'n mut dyn FnMut(EfiEvent), _>(|v| v.cb),
446         ))
447     }
448 
449     /// Wrapper of `EFI_BOOT_SERVICE.CloseEvent()`.
close_event(&self, event: &Event) -> EfiResult<()>450     fn close_event(&self, event: &Event) -> EfiResult<()> {
451         // SAFETY: EFI_BOOT_SERVICES method call.
452         unsafe { efi_call!(self.boot_services.close_event, event.efi_event) }
453     }
454 
455     /// Wrapper of `EFI_BOOT_SERVICE.CheckEvent()`.
456     ///
457     /// On success, returns true if the event is signaled, false if not.
check_event(&self, event: &Event) -> EfiResult<bool>458     pub fn check_event(&self, event: &Event) -> EfiResult<bool> {
459         // SAFETY: EFI_BOOT_SERVICES method call.
460         match unsafe { efi_call!(self.boot_services.check_event, event.efi_event) } {
461             Err(e) if e != EFI_STATUS_NOT_READY.into() => Err(e),
462             Ok(()) => Ok(true),
463             _ => Ok(false),
464         }
465     }
466 
467     /// Wrapper of `EFI_BOOT_SERVICE.SetTimer()`.
set_timer( &self, event: &Event, delay_type: EfiTimerDelay, trigger_time: u64, ) -> EfiResult<()>468     pub fn set_timer(
469         &self,
470         event: &Event,
471         delay_type: EfiTimerDelay,
472         trigger_time: u64,
473     ) -> EfiResult<()> {
474         // SAFETY: EFI_BOOT_SERVICES method call.
475         unsafe {
476             efi_call!(self.boot_services.set_timer, event.efi_event, delay_type, trigger_time)
477         }
478     }
479 }
480 
481 /// `RuntimeServices` provides methods for accessing various EFI_RUNTIME_SERVICES interfaces.
482 #[derive(Clone, Copy)]
483 pub struct RuntimeServices<'a> {
484     runtime_services: &'a EfiRuntimeService,
485 }
486 
487 impl<'a> RuntimeServices<'a> {
488     /// Wrapper of `EFI_RUNTIME_SERVICES.GetVariable()`.
get_variable(&self, guid: &EfiGuid, name: &str, out: &mut [u8]) -> EfiResult<usize>489     pub fn get_variable(&self, guid: &EfiGuid, name: &str, out: &mut [u8]) -> EfiResult<usize> {
490         let mut size = out.len();
491 
492         let mut name_utf16: Vec<u16> = name.encode_utf16().collect();
493         name_utf16.push(0); // null-terminator
494 
495         // SAFETY:
496         // * `&mut size` and `&mut out` are input/output params only and will not be retained
497         // * `&mut size` and `&mut out` are valid pointers and outlive the call
498         match unsafe {
499             efi_call!(
500                 self.runtime_services.get_variable,
501                 name_utf16.as_ptr(),
502                 guid,
503                 null_mut(),
504                 &mut size,
505                 out.as_mut_ptr() as *mut core::ffi::c_void
506             )
507         } {
508             Ok(()) => Ok(size),
509             Err(e) => Err(e),
510         }
511     }
512 }
513 
514 /// EFI Event type to pass to BootServicess::create_event;
515 #[repr(u32)]
516 pub enum EventType {
517     Timer = EFI_EVENT_TYPE_TIMER,
518     RunTime = EFI_EVENT_TYPE_RUNTIME,
519     NotifyWait = EFI_EVENT_TYPE_NOTIFY_WAIT,
520     NotifySignal = EFI_EVENT_TYPE_NOTIFY_SIGNAL,
521     SignalExitBootServices = EFI_EVENT_TYPE_SIGNAL_EXIT_BOOT_SERVICES,
522     SignalVirtualAddressChange = EFI_EVENT_TYPE_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
523 
524     // Valid combinations:
525     TimerNotifySignal = EFI_EVENT_TYPE_TIMER | EFI_EVENT_TYPE_NOTIFY_SIGNAL,
526 }
527 
528 /// EFI task level priority setting for event notify function.
529 #[repr(usize)]
530 #[derive(Copy, Clone)]
531 pub enum Tpl {
532     Application = 4,
533     Callback = 8,
534     Notify = 16,
535     HighLevel = 31,
536 }
537 
538 /// `EventNotify` contains the task level priority setting and a mutable reference to a
539 /// closure for the callback. It is passed as the context pointer to low level EFI event
540 /// notification function entry (`unsafe extern "C" fn efi_event_cb(...)`).
541 pub struct EventNotify<'e> {
542     tpl: Tpl,
543     cb: &'e mut dyn FnMut(EfiEvent),
544 }
545 
546 impl<'e> EventNotify<'e> {
new(tpl: Tpl, cb: &'e mut dyn FnMut(EfiEvent)) -> Self547     pub fn new(tpl: Tpl, cb: &'e mut dyn FnMut(EfiEvent)) -> Self {
548         Self { tpl, cb }
549     }
550 }
551 
552 /// `Event` wraps the raw `EfiEvent` handle and internally enforces a borrow of the registered
553 /// callback for the given life time `e. The event is automatically closed when going out of scope.
554 pub struct Event<'a, 'n> {
555     // If `efi_entry` is None, it represents an unowned Event and won't get closed on drop.
556     efi_entry: Option<&'a EfiEntry>,
557     efi_event: EfiEvent,
558     _cb: Option<&'n mut dyn FnMut(EfiEvent)>,
559 }
560 
561 impl<'a, 'n> Event<'a, 'n> {
562     /// Creates an instance of owned `Event`. The `Event` is closed when going out of scope.
new( efi_entry: Option<&'a EfiEntry>, efi_event: EfiEvent, _cb: Option<&'n mut dyn FnMut(EfiEvent)>, ) -> Self563     fn new(
564         efi_entry: Option<&'a EfiEntry>,
565         efi_event: EfiEvent,
566         _cb: Option<&'n mut dyn FnMut(EfiEvent)>,
567     ) -> Self {
568         Self { efi_entry, efi_event, _cb }
569     }
570 
571     /// Creates an  unowned `Event`. The `Event` is not closed when going out of scope.
new_unowned(efi_event: EfiEvent) -> Self572     fn new_unowned(efi_event: EfiEvent) -> Self {
573         Self { efi_entry: None, efi_event: efi_event, _cb: None }
574     }
575 }
576 
577 impl Drop for Event<'_, '_> {
drop(&mut self)578     fn drop(&mut self) {
579         if let Some(efi_entry) = self.efi_entry {
580             efi_entry.system_table().boot_services().close_event(self).unwrap();
581         }
582     }
583 }
584 
585 /// Event notify function entry for EFI events.
586 ///
587 /// Safety:
588 ///
589 ///   `ctx` must point to a `EventNotify` type object.
590 ///   `ctx` must live longer than the event.
591 ///   There should be no other references to `ctx`.
efi_event_cb(event: EfiEvent, ctx: *mut core::ffi::c_void)592 unsafe extern "C" fn efi_event_cb(event: EfiEvent, ctx: *mut core::ffi::c_void) {
593     // SAFETY: By safety requirement of this function, ctx points to a valid `EventNotify` object,
594     // outlives the event/the function call, and there is no other borrows.
595     let event_cb = unsafe { (ctx as *mut EventNotify).as_mut() }.unwrap();
596     (event_cb.cb)(event);
597 }
598 
599 /// A type for accessing memory map.
600 pub struct EfiMemoryMap<'a> {
601     buffer: &'a mut [u8],
602     map_key: usize,
603     descriptor_size: usize,
604     descriptor_version: u32,
605 }
606 
607 /// Iterator for traversing `EfiMemoryDescriptor` items in `EfiMemoryMap::buffer`.
608 pub struct EfiMemoryMapIter<'a: 'b, 'b> {
609     memory_map: &'b EfiMemoryMap<'a>,
610     offset: usize,
611 }
612 
613 impl<'a, 'b> Iterator for EfiMemoryMapIter<'a, 'b> {
614     type Item = &'b EfiMemoryDescriptor;
615 
next(&mut self) -> Option<Self::Item>616     fn next(&mut self) -> Option<Self::Item> {
617         if self.offset >= self.memory_map.buffer.len() {
618             return None;
619         }
620         let bytes = &self.memory_map.buffer[self.offset..][..self.memory_map.descriptor_size];
621         self.offset += self.memory_map.descriptor_size;
622         Some(Ref::<_, EfiMemoryDescriptor>::new_from_prefix(bytes).unwrap().0.into_ref())
623     }
624 }
625 
626 impl<'a> EfiMemoryMap<'a> {
627     /// Creates a new instance with the given parameters obtained from `get_memory_map()`.
new( buffer: &'a mut [u8], map_key: usize, descriptor_size: usize, descriptor_version: u32, ) -> Self628     fn new(
629         buffer: &'a mut [u8],
630         map_key: usize,
631         descriptor_size: usize,
632         descriptor_version: u32,
633     ) -> Self {
634         Self { buffer, map_key, descriptor_size, descriptor_version }
635     }
636 
637     /// Returns the buffer.
buffer(&self) -> &[u8]638     pub fn buffer(&self) -> &[u8] {
639         self.buffer
640     }
641 
642     /// Returns the value of `map_key`.
map_key(&self) -> usize643     pub fn map_key(&self) -> usize {
644         self.map_key
645     }
646 
647     /// Returns the value of `descriptor_version`.
descriptor_version(&self) -> u32648     pub fn descriptor_version(&self) -> u32 {
649         self.descriptor_version
650     }
651 
652     /// Returns the number of descriptors.
len(&self) -> usize653     pub fn len(&self) -> usize {
654         self.buffer.len() / self.descriptor_size
655     }
656 }
657 
658 impl<'a: 'b, 'b> IntoIterator for &'b EfiMemoryMap<'a> {
659     type Item = &'b EfiMemoryDescriptor;
660     type IntoIter = EfiMemoryMapIter<'a, 'b>;
661 
into_iter(self) -> Self::IntoIter662     fn into_iter(self) -> Self::IntoIter {
663         EfiMemoryMapIter { memory_map: self, offset: 0 }
664     }
665 }
666 
667 /// A type representing a UEFI handle to a UEFI device.
668 #[derive(Debug, Copy, Clone, PartialEq)]
669 pub struct DeviceHandle(EfiHandle);
670 
671 /// `LocatedHandles` holds the array of handles return by
672 /// `BootServices::locate_handle_buffer_by_protocol()`.
673 pub struct LocatedHandles<'a> {
674     handles: &'a [DeviceHandle],
675     efi_entry: &'a EfiEntry,
676 }
677 
678 impl<'a> LocatedHandles<'a> {
new(handles: *mut EfiHandle, len: usize, efi_entry: &'a EfiEntry) -> Self679     pub(crate) fn new(handles: *mut EfiHandle, len: usize, efi_entry: &'a EfiEntry) -> Self {
680         // Implementation is not suppose to call this with a NULL pointer.
681         debug_assert!(!handles.is_null());
682         Self {
683             // SAFETY: Given correct UEFI firmware, non-null pointer points to valid memory.
684             // The memory is owned by the objects.
685             handles: unsafe { from_raw_parts(handles as *mut DeviceHandle, len) },
686             efi_entry: efi_entry,
687         }
688     }
689     /// Get the list of handles as a slice.
handles(&self) -> &[DeviceHandle]690     pub fn handles(&self) -> &[DeviceHandle] {
691         self.handles
692     }
693 }
694 
695 impl Drop for LocatedHandles<'_> {
drop(&mut self)696     fn drop(&mut self) {
697         self.efi_entry
698             .system_table()
699             .boot_services()
700             .free_pool(self.handles.as_ptr() as *mut _)
701             .unwrap();
702     }
703 }
704 
705 /// Helper macro for printing message via `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL` in
706 /// `EFI_SYSTEM_TABLE.ConOut`.
707 #[macro_export]
708 macro_rules! efi_print {
709     ( $efi_entry:expr, $( $x:expr ),* $(,)? ) => {
710             write!($efi_entry.system_table().con_out().unwrap(), $($x,)*).unwrap()
711     };
712 }
713 
714 #[macro_export]
715 macro_rules! efi_println {
716     ( $efi_entry:expr, $( $x:expr ),* ) => {
717         efi_print!($efi_entry, $($x,)*);
718         efi_print!($efi_entry, "\r\n");
719     };
720 }
721 
722 /// Provides a builtin panic handler.
723 /// In the long term, to improve flexibility, consider allowing application to install a custom
724 /// handler into `EfiEntry` to be called here.
725 #[cfg(not(test))]
726 #[panic_handler]
panic(panic: &PanicInfo) -> !727 fn panic(panic: &PanicInfo) -> ! {
728     // If there is a valid internal `efi_entry` from global allocator, print the panic info.
729     let entry = allocation::internal_efi_entry();
730     if let Some(e) = entry {
731         match e.system_table().con_out() {
732             Ok(mut con_out) => {
733                 let _ = write!(con_out, "Panics! {}\r\n", panic);
734             }
735             _ => {}
736         }
737     }
738     loop {}
739 }
740 
741 #[cfg(test)]
742 mod test {
743     use super::*;
744     use crate::protocol::block_io::BlockIoProtocol;
745     use std::cell::RefCell;
746     use std::collections::VecDeque;
747     use std::mem::size_of;
748     use std::slice::from_raw_parts_mut;
749 
750     use zerocopy::AsBytes;
751 
752     /// A structure to store the traces of arguments/outputs for EFI methods.
753     #[derive(Default)]
754     pub struct EfiCallTraces {
755         pub free_pool_trace: FreePoolTrace,
756         pub open_protocol_trace: OpenProtocolTrace,
757         pub close_protocol_trace: CloseProtocolTrace,
758         pub locate_handle_buffer_trace: LocateHandleBufferTrace,
759         pub get_memory_map_trace: GetMemoryMapTrace,
760         pub exit_boot_services_trace: ExitBootServicespTrace,
761         pub create_event_trace: CreateEventTrace,
762         pub close_event_trace: CloseEventTrace,
763         pub check_event_trace: CheckEventTrace,
764     }
765 
766     // Declares a global instance of EfiCallTraces.
767     // Need to use thread local storage because rust unit test is multi-threaded.
768     thread_local! {
769         static EFI_CALL_TRACES: RefCell<EfiCallTraces> = RefCell::new(Default::default());
770     }
771 
772     /// Exports for unit-test in submodules.
efi_call_traces() -> &'static std::thread::LocalKey<RefCell<EfiCallTraces>>773     pub fn efi_call_traces() -> &'static std::thread::LocalKey<RefCell<EfiCallTraces>> {
774         &EFI_CALL_TRACES
775     }
776 
777     /// EFI_BOOT_SERVICE.FreePool() test implementation.
778     #[derive(Default)]
779     pub struct FreePoolTrace {
780         // Capture `buf`
781         pub inputs: VecDeque<*mut core::ffi::c_void>,
782     }
783 
784     /// Mock of the `EFI_BOOT_SERVICE.FreePool` C API in test environment.
free_pool(buf: *mut core::ffi::c_void) -> EfiStatus785     extern "C" fn free_pool(buf: *mut core::ffi::c_void) -> EfiStatus {
786         EFI_CALL_TRACES.with(|traces| {
787             traces.borrow_mut().free_pool_trace.inputs.push_back(buf);
788             EFI_STATUS_SUCCESS
789         })
790     }
791 
792     /// EFI_BOOT_SERVICE.OpenProtocol() test implementation.
793     #[derive(Default)]
794     pub struct OpenProtocolTrace {
795         // Capture `handle`, `protocol_guid`, `agent_handle`.
796         pub inputs: VecDeque<(DeviceHandle, EfiGuid, EfiHandle)>,
797         // Return `intf`, EfiStatus.
798         pub outputs: VecDeque<(EfiHandle, EfiStatus)>,
799     }
800 
801     /// Mock of the `EFI_BOOT_SERVICE.OpenProtocol` C API in test environment.
802     ///
803     /// # Safety
804     ///
805     ///   Caller should guarantee that `intf` and `protocol_guid` point to valid memory locations.
open_protocol( handle: EfiHandle, protocol_guid: *const EfiGuid, intf: *mut *mut core::ffi::c_void, agent_handle: EfiHandle, _: EfiHandle, attr: u32, ) -> EfiStatus806     unsafe extern "C" fn open_protocol(
807         handle: EfiHandle,
808         protocol_guid: *const EfiGuid,
809         intf: *mut *mut core::ffi::c_void,
810         agent_handle: EfiHandle,
811         _: EfiHandle,
812         attr: u32,
813     ) -> EfiStatus {
814         assert_eq!(attr, EFI_OPEN_PROTOCOL_ATTRIBUTE_BY_HANDLE_PROTOCOL);
815         EFI_CALL_TRACES.with(|traces| {
816             let trace = &mut traces.borrow_mut().open_protocol_trace;
817             trace.inputs.push_back((DeviceHandle(handle), *protocol_guid, agent_handle));
818 
819             let (intf_handle, status) = trace.outputs.pop_front().unwrap();
820             *intf = intf_handle;
821 
822             status
823         })
824     }
825 
826     /// EFI_BOOT_SERVICE.CloseProtocol() test implementation.
827     #[derive(Default)]
828     pub struct CloseProtocolTrace {
829         // Capture `handle`, `protocol_guid`, `agent_handle`
830         pub inputs: VecDeque<(DeviceHandle, EfiGuid, EfiHandle)>,
831     }
832 
833     /// Mock of the `EFI_BOOT_SERVICE.CloseProtocol` C API in test environment.
834     ///
835     /// # Safety
836     ///
837     ///   Caller should guarantee that `protocol_guid` points to valid memory location.
close_protocol( handle: EfiHandle, protocol_guid: *const EfiGuid, agent_handle: EfiHandle, _: EfiHandle, ) -> EfiStatus838     unsafe extern "C" fn close_protocol(
839         handle: EfiHandle,
840         protocol_guid: *const EfiGuid,
841         agent_handle: EfiHandle,
842         _: EfiHandle,
843     ) -> EfiStatus {
844         EFI_CALL_TRACES.with(|traces| {
845             traces.borrow_mut().close_protocol_trace.inputs.push_back((
846                 DeviceHandle(handle),
847                 *protocol_guid,
848                 agent_handle,
849             ));
850             EFI_STATUS_SUCCESS
851         })
852     }
853 
854     /// EFI_BOOT_SERVICE.LocateHandleBuffer.
855     #[derive(Default)]
856     pub struct LocateHandleBufferTrace {
857         // Capture `protocol`.
858         pub inputs: VecDeque<EfiGuid>,
859         // For returning in `num_handles` and `buf`.
860         pub outputs: VecDeque<(usize, *mut DeviceHandle)>,
861     }
862 
863     /// Mock of the `EFI_BOOT_SERVICE.LocateHandleBuffer` C API in test environment.
864     ///
865     /// # Safety
866     ///
867     ///   Caller should guarantee that `num_handles` and `buf` point to valid memory locations.
locate_handle_buffer( search_type: EfiLocateHandleSearchType, protocol: *const EfiGuid, search_key: *mut core::ffi::c_void, num_handles: *mut usize, buf: *mut *mut EfiHandle, ) -> EfiStatus868     unsafe extern "C" fn locate_handle_buffer(
869         search_type: EfiLocateHandleSearchType,
870         protocol: *const EfiGuid,
871         search_key: *mut core::ffi::c_void,
872         num_handles: *mut usize,
873         buf: *mut *mut EfiHandle,
874     ) -> EfiStatus {
875         assert_eq!(search_type, EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL);
876         assert_eq!(search_key, null_mut());
877         EFI_CALL_TRACES.with(|traces| {
878             let trace = &mut traces.borrow_mut().locate_handle_buffer_trace;
879             trace.inputs.push_back(*protocol);
880 
881             let (num, handles) = trace.outputs.pop_front().unwrap();
882             *num_handles = num as usize;
883             *buf = handles as *mut EfiHandle;
884 
885             EFI_STATUS_SUCCESS
886         })
887     }
888 
889     /// EFI_BOOT_SERVICE.GetMemoryMap.
890     #[derive(Default)]
891     pub struct GetMemoryMapTrace {
892         // Capture `memory_map_size` and `memory_map` argument.
893         pub inputs: VecDeque<(usize, *mut EfiMemoryDescriptor)>,
894         // Output value `map_key`, `memory_map_size`.
895         pub outputs: VecDeque<(usize, usize)>,
896     }
897 
898     /// Mock of the `EFI_BOOT_SERVICE.GetMemoryMap` C API in test environment.
899     ///
900     /// # Safety
901     ///
902     ///   Caller should guarantee that `memory_map_size`, `map_key` and `desc_size` point to valid
903     ///   memory locations.
get_memory_map( memory_map_size: *mut usize, memory_map: *mut EfiMemoryDescriptor, map_key: *mut usize, desc_size: *mut usize, _: *mut u32, ) -> EfiStatus904     unsafe extern "C" fn get_memory_map(
905         memory_map_size: *mut usize,
906         memory_map: *mut EfiMemoryDescriptor,
907         map_key: *mut usize,
908         desc_size: *mut usize,
909         _: *mut u32,
910     ) -> EfiStatus {
911         EFI_CALL_TRACES.with(|traces| {
912             let trace = &mut traces.borrow_mut().get_memory_map_trace;
913             trace.inputs.push_back((unsafe { *memory_map_size }, memory_map));
914             (*map_key, *memory_map_size) = trace.outputs.pop_front().unwrap();
915             *desc_size = size_of::<EfiMemoryDescriptor>();
916             EFI_STATUS_SUCCESS
917         })
918     }
919 
920     /// EFI_BOOT_SERVICE.ExitBootServices.
921     #[derive(Default)]
922     pub struct ExitBootServicespTrace {
923         // Capture `image_handle`, `map_key`
924         pub inputs: VecDeque<(EfiHandle, usize)>,
925     }
926 
927     /// Mock of the `EFI_BOOT_SERVICE.ExitBootServices` C API in test environment.
exit_boot_services(image_handle: EfiHandle, map_key: usize) -> EfiStatus928     extern "C" fn exit_boot_services(image_handle: EfiHandle, map_key: usize) -> EfiStatus {
929         EFI_CALL_TRACES.with(|traces| {
930             let trace = &mut traces.borrow_mut().exit_boot_services_trace;
931             trace.inputs.push_back((image_handle, map_key));
932             EFI_STATUS_SUCCESS
933         })
934     }
935 
936     /// EFI_BOOT_SERVICE.CreateEvent.
937     #[derive(Default)]
938     pub struct CreateEventTrace {
939         // Capture `type_`, `notify_tpl`, `notify_fn`, `notify_ctx`
940         pub inputs: VecDeque<(u32, EfiTpl, EfiEventNotify, *mut core::ffi::c_void)>,
941         // Output a EfiEvent.
942         pub outputs: VecDeque<EfiEvent>,
943     }
944 
945     /// Mock of the `EFI_BOOT_SERVICE.CreateEvent` C API in test environment.
946     ///
947     /// # Safety
948     ///
949     ///   Caller should guarantee that `event` points to valid memory location.
create_event( type_: u32, notify_tpl: EfiTpl, notify_fn: EfiEventNotify, notify_ctx: *mut core::ffi::c_void, event: *mut EfiEvent, ) -> EfiStatus950     unsafe extern "C" fn create_event(
951         type_: u32,
952         notify_tpl: EfiTpl,
953         notify_fn: EfiEventNotify,
954         notify_ctx: *mut core::ffi::c_void,
955         event: *mut EfiEvent,
956     ) -> EfiStatus {
957         EFI_CALL_TRACES.with(|traces| {
958             let trace = &mut traces.borrow_mut().create_event_trace;
959             trace.inputs.push_back((type_, notify_tpl, notify_fn, notify_ctx));
960             *event = trace.outputs.pop_front().unwrap();
961             EFI_STATUS_SUCCESS
962         })
963     }
964 
965     /// EFI_BOOT_SERVICE.CloseEvent.
966     #[derive(Default)]
967     pub struct CloseEventTrace {
968         // Capture `event`
969         pub inputs: VecDeque<EfiEvent>,
970     }
971 
972     /// Mock of the `EFI_BOOT_SERVICE.CloseEvent` C API in test environment.
close_event(event: EfiEvent) -> EfiStatus973     extern "C" fn close_event(event: EfiEvent) -> EfiStatus {
974         EFI_CALL_TRACES.with(|traces| {
975             let trace = &mut traces.borrow_mut().close_event_trace;
976             trace.inputs.push_back(event);
977             EFI_STATUS_SUCCESS
978         })
979     }
980 
981     /// EFI_BOOT_SERVICE.CheckEvent.
982     #[derive(Default)]
983     pub struct CheckEventTrace {
984         // EfiStatus for return.
985         pub outputs: VecDeque<EfiStatus>,
986     }
987 
988     /// Mock of the `EFI_BOOT_SERVICE.CheckEvent` C API in test environment.
check_event(_: EfiEvent) -> EfiStatus989     extern "C" fn check_event(_: EfiEvent) -> EfiStatus {
990         EFI_CALL_TRACES.with(|traces| {
991             let trace = &mut traces.borrow_mut().check_event_trace;
992             trace.outputs.pop_front().unwrap()
993         })
994     }
995 
996     /// A test wrapper that sets up a system table, image handle and runs a test function like it
997     /// is an EFI application.
998     /// TODO(300168989): Investigate using procedural macro to generate test that auto calls this.
run_test(func: fn(EfiHandle, *mut EfiSystemTable) -> ())999     pub fn run_test(func: fn(EfiHandle, *mut EfiSystemTable) -> ()) {
1000         // Reset all traces
1001         EFI_CALL_TRACES.with(|trace| {
1002             *trace.borrow_mut() = Default::default();
1003         });
1004 
1005         let mut systab: EfiSystemTable = Default::default();
1006         let mut boot_services: EfiBootService = Default::default();
1007 
1008         boot_services.free_pool = Some(free_pool);
1009         boot_services.open_protocol = Some(open_protocol);
1010         boot_services.close_protocol = Some(close_protocol);
1011         boot_services.locate_handle_buffer = Some(locate_handle_buffer);
1012         boot_services.get_memory_map = Some(get_memory_map);
1013         boot_services.exit_boot_services = Some(exit_boot_services);
1014         boot_services.create_event = Some(create_event);
1015         boot_services.close_event = Some(close_event);
1016         boot_services.check_event = Some(check_event);
1017         systab.boot_services = &mut boot_services as *mut _;
1018         let image_handle: usize = 1234; // Don't care.
1019 
1020         func(image_handle as EfiHandle, &mut systab as *mut _);
1021 
1022         // Reset all traces
1023         EFI_CALL_TRACES.with(|trace| {
1024             *trace.borrow_mut() = Default::default();
1025         });
1026     }
1027 
1028     /// Get the pointer to an object as an EfiHandle type.
as_efi_handle<T>(val: &mut T) -> EfiHandle1029     fn as_efi_handle<T>(val: &mut T) -> EfiHandle {
1030         val as *mut T as *mut _
1031     }
1032 
1033     #[test]
test_open_close_protocol()1034     fn test_open_close_protocol() {
1035         run_test(|image_handle, systab_ptr| {
1036             let efi_entry = EfiEntry { image_handle, systab_ptr };
1037 
1038             // Set up open_protocol trace
1039             let mut block_io: EfiBlockIoProtocol = Default::default();
1040             EFI_CALL_TRACES.with(|traces| {
1041                 traces.borrow_mut().open_protocol_trace.outputs =
1042                     VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_SUCCESS)]);
1043             });
1044 
1045             let mut device_handle: usize = 0; // Don't care
1046             {
1047                 // Open a protocol
1048                 let protocol = efi_entry
1049                     .system_table()
1050                     .boot_services()
1051                     .open_protocol::<BlockIoProtocol>(DeviceHandle(as_efi_handle(
1052                         &mut device_handle,
1053                     )))
1054                     .unwrap();
1055 
1056                 // Validate call args
1057                 EFI_CALL_TRACES.with(|trace| {
1058                     assert_eq!(
1059                         trace.borrow_mut().open_protocol_trace.inputs,
1060                         [(
1061                             DeviceHandle(as_efi_handle(&mut device_handle)),
1062                             BlockIoProtocol::GUID,
1063                             image_handle
1064                         ),]
1065                     );
1066 
1067                     // close_protocol not called yet.
1068                     assert_eq!(trace.borrow_mut().close_protocol_trace.inputs, []);
1069                 });
1070 
1071                 // The protocol gets the correct EfiBlockIoProtocol structure we pass in.
1072                 assert_eq!(protocol.interface_ptr(), &mut block_io as *mut _);
1073             }
1074 
1075             // Close protocol is called as `protocol` goes out of scope.
1076             EFI_CALL_TRACES.with(|trace| {
1077                 assert_eq!(
1078                     trace.borrow_mut().close_protocol_trace.inputs,
1079                     [(
1080                         DeviceHandle(as_efi_handle(&mut device_handle)),
1081                         BlockIoProtocol::GUID,
1082                         image_handle
1083                     ),]
1084                 )
1085             });
1086         })
1087     }
1088 
1089     #[test]
test_null_efi_method()1090     fn test_null_efi_method() {
1091         // Test that wrapper call fails if efi method is None.
1092         run_test(|image_handle, systab_ptr| {
1093             let efi_entry = EfiEntry { image_handle, systab_ptr };
1094 
1095             // Set up open_protocol trace
1096             let mut block_io: EfiBlockIoProtocol = Default::default();
1097             EFI_CALL_TRACES.with(|traces| {
1098                 traces.borrow_mut().open_protocol_trace.outputs =
1099                     VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_SUCCESS)]);
1100             });
1101 
1102             // Set the method to None.
1103             // SAFETY:
1104             // run_test() guarantees `boot_services` pointer points to valid object.
1105             unsafe { (*(*systab_ptr).boot_services).open_protocol = None };
1106 
1107             let mut device_handle: usize = 0; // Don't care
1108             assert!(efi_entry
1109                 .system_table()
1110                 .boot_services()
1111                 .open_protocol::<BlockIoProtocol>(DeviceHandle(as_efi_handle(&mut device_handle)))
1112                 .is_err());
1113         })
1114     }
1115 
1116     #[test]
test_error_efi_method()1117     fn test_error_efi_method() {
1118         // Test that wrapper call fails if efi method returns error.
1119         run_test(|image_handle, systab_ptr| {
1120             let efi_entry = EfiEntry { image_handle, systab_ptr };
1121 
1122             // Set up open_protocol trace.
1123             let mut block_io: EfiBlockIoProtocol = Default::default();
1124             EFI_CALL_TRACES.with(|traces| {
1125                 traces.borrow_mut().open_protocol_trace.outputs =
1126                     VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_NOT_FOUND)]);
1127             });
1128 
1129             let mut device_handle: usize = 0; // Don't care
1130             assert!(efi_entry
1131                 .system_table()
1132                 .boot_services()
1133                 .open_protocol::<BlockIoProtocol>(DeviceHandle(as_efi_handle(&mut device_handle)))
1134                 .is_err());
1135         })
1136     }
1137 
1138     #[test]
test_locate_handle_buffer_by_protocol()1139     fn test_locate_handle_buffer_by_protocol() {
1140         run_test(|image_handle, systab_ptr| {
1141             let efi_entry = EfiEntry { image_handle, systab_ptr };
1142 
1143             // Set up locate_handle_buffer_trace trace.
1144             let mut located_handles: [DeviceHandle; 3] =
1145                 [DeviceHandle(1 as *mut _), DeviceHandle(2 as *mut _), DeviceHandle(3 as *mut _)];
1146             EFI_CALL_TRACES.with(|traces| {
1147                 traces.borrow_mut().locate_handle_buffer_trace.outputs =
1148                     VecDeque::from([(located_handles.len(), located_handles.as_mut_ptr())]);
1149             });
1150 
1151             {
1152                 let handles = efi_entry
1153                     .system_table()
1154                     .boot_services()
1155                     .locate_handle_buffer_by_protocol::<BlockIoProtocol>()
1156                     .unwrap();
1157 
1158                 // Returned handles are expected.
1159                 assert_eq!(handles.handles().to_vec(), located_handles);
1160             }
1161 
1162             EFI_CALL_TRACES.with(|traces| {
1163                 let traces = traces.borrow_mut();
1164                 // Arguments are passed correctly.
1165                 assert_eq!(traces.locate_handle_buffer_trace.inputs, [BlockIoProtocol::GUID]);
1166                 // Free pool is called with the correct address.
1167                 assert_eq!(traces.free_pool_trace.inputs, [located_handles.as_mut_ptr() as *mut _]);
1168             });
1169         })
1170     }
1171 
1172     #[test]
test_find_first_and_open()1173     fn test_find_first_and_open() {
1174         run_test(|image_handle, systab_ptr| {
1175             let efi_entry = EfiEntry { image_handle, systab_ptr };
1176 
1177             // Set up locate_handle_buffer_trace trace.
1178             let mut located_handles: [DeviceHandle; 3] =
1179                 [DeviceHandle(1 as *mut _), DeviceHandle(2 as *mut _), DeviceHandle(3 as *mut _)];
1180             EFI_CALL_TRACES.with(|traces| {
1181                 traces.borrow_mut().locate_handle_buffer_trace.outputs =
1182                     VecDeque::from([(located_handles.len(), located_handles.as_mut_ptr())]);
1183             });
1184 
1185             // Set up open_protocol trace.
1186             let mut block_io: EfiBlockIoProtocol = Default::default();
1187             EFI_CALL_TRACES.with(|traces| {
1188                 traces.borrow_mut().open_protocol_trace.outputs =
1189                     VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_SUCCESS)]);
1190             });
1191 
1192             efi_entry
1193                 .system_table()
1194                 .boot_services()
1195                 .find_first_and_open::<BlockIoProtocol>()
1196                 .unwrap();
1197 
1198             // Check open_protocol is called on the first handle.
1199             EFI_CALL_TRACES.with(|traces| {
1200                 assert_eq!(
1201                     traces.borrow_mut().open_protocol_trace.inputs,
1202                     [(DeviceHandle(1 as *mut _), BlockIoProtocol::GUID, image_handle),]
1203                 );
1204             });
1205         })
1206     }
1207 
1208     #[test]
test_exit_boot_services()1209     fn test_exit_boot_services() {
1210         run_test(|image_handle, systab_ptr| {
1211             let efi_entry = EfiEntry { image_handle, systab_ptr };
1212             // Create a buffer large enough to hold two EfiMemoryDescriptor.
1213             let mut descriptors: [EfiMemoryDescriptor; 2] = [
1214                 EfiMemoryDescriptor {
1215                     memory_type: EFI_MEMORY_TYPE_LOADER_DATA,
1216                     padding: 0,
1217                     physical_start: 0,
1218                     virtual_start: 0,
1219                     number_of_pages: 0,
1220                     attributes: 0,
1221                 },
1222                 EfiMemoryDescriptor {
1223                     memory_type: EFI_MEMORY_TYPE_LOADER_CODE,
1224                     padding: 0,
1225                     physical_start: 0,
1226                     virtual_start: 0,
1227                     number_of_pages: 0,
1228                     attributes: 0,
1229                 },
1230             ];
1231             let map_key: usize = 12345;
1232             // Set up get_memory_map trace.
1233             EFI_CALL_TRACES.with(|traces| {
1234                 // Output only the first EfiMemoryDescriptor.
1235                 traces.borrow_mut().get_memory_map_trace.outputs =
1236                     VecDeque::from([(map_key, 1 * size_of::<EfiMemoryDescriptor>())]);
1237             });
1238 
1239             // SAFETY: Buffer is guaranteed valid.
1240             let buffer = unsafe {
1241                 from_raw_parts_mut(
1242                     descriptors.as_mut_ptr() as *mut u8,
1243                     descriptors.len() * size_of::<EfiMemoryDescriptor>(),
1244                 )
1245             };
1246 
1247             // Test `exit_boot_services`
1248             let desc = super::exit_boot_services(efi_entry, buffer).unwrap();
1249 
1250             // Validate that UEFI APIs are correctly called.
1251             EFI_CALL_TRACES.with(|traces| {
1252                 assert_eq!(
1253                     traces.borrow_mut().get_memory_map_trace.inputs,
1254                     [(
1255                         descriptors.len() * size_of::<EfiMemoryDescriptor>(),
1256                         descriptors.as_mut_ptr()
1257                     )]
1258                 );
1259 
1260                 assert_eq!(
1261                     traces.borrow_mut().exit_boot_services_trace.inputs,
1262                     [(image_handle, map_key)],
1263                 );
1264             });
1265 
1266             // Validate that the returned `EfiMemoryMap` contains only 1 EfiMemoryDescriptor.
1267             assert_eq!(desc.into_iter().map(|v| *v).collect::<Vec<_>>(), descriptors[..1].to_vec());
1268             // Validate that the returned `EfiMemoryMap` has the correct map_key.
1269             assert_eq!(desc.map_key(), map_key);
1270         })
1271     }
1272 
1273     #[test]
test_exit_boot_services_unaligned_buffer()1274     fn test_exit_boot_services_unaligned_buffer() {
1275         run_test(|image_handle, systab_ptr| {
1276             let efi_entry = EfiEntry { image_handle, systab_ptr };
1277             // Create a buffer for 2 EfiMemoryDescriptor.
1278             let descriptors: [EfiMemoryDescriptor; 2] = [
1279                 EfiMemoryDescriptor {
1280                     memory_type: EFI_MEMORY_TYPE_LOADER_DATA,
1281                     padding: 0,
1282                     physical_start: 0,
1283                     virtual_start: 0,
1284                     number_of_pages: 0,
1285                     attributes: 0,
1286                 },
1287                 EfiMemoryDescriptor {
1288                     memory_type: EFI_MEMORY_TYPE_LOADER_CODE,
1289                     padding: 0,
1290                     physical_start: 0,
1291                     virtual_start: 0,
1292                     number_of_pages: 0,
1293                     attributes: 0,
1294                 },
1295             ];
1296 
1297             let map_key: usize = 12345;
1298             // Set up get_memory_map trace.
1299             EFI_CALL_TRACES.with(|traces| {
1300                 traces.borrow_mut().get_memory_map_trace.outputs =
1301                     VecDeque::from([(map_key, 2 * size_of::<EfiMemoryDescriptor>())]);
1302             });
1303 
1304             // Construct the destination buffer.
1305             let mut buffer = [0u8; 256];
1306             let alignment = core::mem::align_of::<EfiMemoryDescriptor>();
1307             let size = core::mem::size_of::<EfiMemoryDescriptor>();
1308             let aligned = aligned_subslice(&mut buffer[..], alignment).unwrap();
1309             // Offset by 1 element so that we can make an unaligned buffer starting somewhere in
1310             // between.
1311             let start = aligned.get_mut(size..).unwrap();
1312             start[..size].clone_from_slice(descriptors[0].as_bytes());
1313             start[size..][..size].clone_from_slice(descriptors[1].as_bytes());
1314             // Pass an unaligned address.
1315             let desc = super::exit_boot_services(efi_entry, &mut aligned[size - 1..]).unwrap();
1316             // Validate that the returned `EfiMemoryMap` contains the correct EfiMemoryDescriptor.
1317             assert_eq!(desc.into_iter().map(|v| *v).collect::<Vec<_>>(), descriptors[..2].to_vec());
1318             // Validate that the returned `EfiMemoryMap` has the correct map_key.
1319             assert_eq!(desc.map_key(), map_key);
1320         });
1321     }
1322 
1323     #[test]
test_create_event_with_notify_fn()1324     fn test_create_event_with_notify_fn() {
1325         run_test(|image_handle, systab_ptr| {
1326             let efi_entry = EfiEntry { image_handle, systab_ptr };
1327             let mut cb_impl = |_: EfiEvent| {};
1328             let mut cb = EventNotify::new(Tpl::Callback, &mut cb_impl);
1329             let event: EfiEvent = 1234usize as _;
1330             EFI_CALL_TRACES.with(|traces| {
1331                 traces.borrow_mut().create_event_trace.outputs.push_back(event);
1332             });
1333             {
1334                 let _ = efi_entry
1335                     .system_table()
1336                     .boot_services()
1337                     .create_event(EventType::Timer, Some(&mut cb))
1338                     .unwrap();
1339             }
1340             let efi_cb: EfiEventNotify = Some(efi_event_cb);
1341             EFI_CALL_TRACES.with(|traces| {
1342                 assert_eq!(
1343                     traces.borrow_mut().create_event_trace.inputs,
1344                     [(
1345                         EventType::Timer as _,
1346                         Tpl::Callback as _,
1347                         efi_cb,
1348                         &mut cb as *mut _ as *mut _
1349                     )]
1350                 )
1351             });
1352             // Verify close_event is called.
1353             EFI_CALL_TRACES
1354                 .with(|traces| assert_eq!(traces.borrow_mut().close_event_trace.inputs, [event]));
1355         });
1356     }
1357 
1358     #[test]
test_create_event_wo_notify_fn()1359     fn test_create_event_wo_notify_fn() {
1360         run_test(|image_handle, systab_ptr| {
1361             let efi_entry = EfiEntry { image_handle, systab_ptr };
1362             let event: EfiEvent = 1234usize as _;
1363             EFI_CALL_TRACES.with(|traces| {
1364                 traces.borrow_mut().create_event_trace.outputs.push_back(event);
1365             });
1366             {
1367                 let _ = efi_entry
1368                     .system_table()
1369                     .boot_services()
1370                     .create_event(EventType::Timer, None)
1371                     .unwrap();
1372             }
1373             EFI_CALL_TRACES.with(|traces| {
1374                 assert_eq!(
1375                     traces.borrow_mut().create_event_trace.inputs,
1376                     [(EventType::Timer as _, 0, None, null_mut())]
1377                 )
1378             });
1379         });
1380     }
1381 
1382     #[test]
test_check_event()1383     fn test_check_event() {
1384         run_test(|image_handle, systab_ptr| {
1385             let efi_entry = EfiEntry { image_handle, systab_ptr };
1386             let event: EfiEvent = 1234usize as _;
1387             EFI_CALL_TRACES.with(|traces| {
1388                 traces.borrow_mut().create_event_trace.outputs.push_back(event);
1389                 traces.borrow_mut().check_event_trace.outputs.push_back(EFI_STATUS_SUCCESS);
1390                 traces.borrow_mut().check_event_trace.outputs.push_back(EFI_STATUS_NOT_READY);
1391                 traces.borrow_mut().check_event_trace.outputs.push_back(EFI_STATUS_UNSUPPORTED);
1392             });
1393             let res = efi_entry
1394                 .system_table()
1395                 .boot_services()
1396                 .create_event(EventType::Timer, None)
1397                 .unwrap();
1398             assert_eq!(efi_entry.system_table().boot_services().check_event(&res), Ok(true));
1399             assert_eq!(efi_entry.system_table().boot_services().check_event(&res), Ok(false));
1400             assert!(efi_entry.system_table().boot_services().check_event(&res).is_err());
1401         });
1402     }
1403 
1404     #[test]
test_efi_error()1405     fn test_efi_error() {
1406         let res: EfiResult<()> = Err(EFI_STATUS_NOT_FOUND.into());
1407         assert_eq!(res.unwrap_err().err(), ErrorTypes::EfiStatusError(14));
1408     }
1409 }
1410