• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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