• Home
  • 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) -> liberror::Result<()> {
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 #[cfg(not(test))]
57 mod allocation;
58 
59 #[cfg(not(test))]
60 pub mod libc;
61 
62 #[cfg(not(test))]
63 pub use allocation::EfiAllocator;
64 
65 /// The Android EFI protocol implementation of an A/B slot manager.
66 pub mod ab_slots;
67 /// Local fastboot/bootmenu support.
68 pub mod local_session;
69 /// Idiomatic wrappers around EFI protocols.
70 pub mod protocol;
71 pub mod utils;
72 
73 #[cfg(not(test))]
74 use core::{fmt::Write, panic::PanicInfo};
75 
76 use core::{marker::PhantomData, ptr::null_mut, slice::from_raw_parts, time::Duration};
77 use efi_types::{
78     EfiBootService, EfiConfigurationTable, EfiEvent, EfiGuid, EfiHandle,
79     EfiMemoryAttributesTableHeader, EfiMemoryDescriptor, EfiMemoryType, EfiRuntimeService,
80     EfiSystemTable, EfiTimerDelay, EFI_EVENT_TYPE_NOTIFY_SIGNAL, EFI_EVENT_TYPE_NOTIFY_WAIT,
81     EFI_EVENT_TYPE_RUNTIME, EFI_EVENT_TYPE_SIGNAL_EXIT_BOOT_SERVICES,
82     EFI_EVENT_TYPE_SIGNAL_VIRTUAL_ADDRESS_CHANGE, EFI_EVENT_TYPE_TIMER,
83     EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL, EFI_OPEN_PROTOCOL_ATTRIBUTE_BY_HANDLE_PROTOCOL,
84     EFI_RESET_TYPE, EFI_RESET_TYPE_EFI_RESET_COLD, EFI_STATUS, EFI_STATUS_SUCCESS,
85 };
86 use liberror::{Error, Result};
87 use libutils::aligned_subslice;
88 use protocol::{
89     simple_text_output::SimpleTextOutputProtocol,
90     {Protocol, ProtocolInfo},
91 };
92 use zerocopy::{FromBytes, Ref};
93 
94 /// `EfiEntry` stores the EFI system table pointer and image handle passed from the entry point.
95 /// It's the root data structure that derives all other wrapper APIs and structures.
96 pub struct EfiEntry {
97     image_handle: EfiHandle,
98     systab_ptr: *const EfiSystemTable,
99 }
100 
101 impl EfiEntry {
102     /// Gets an instance of `SystemTable`.
103     ///
104     /// Panics if the pointer is NULL.
system_table(&self) -> SystemTable105     pub fn system_table(&self) -> SystemTable {
106         self.system_table_checked().unwrap()
107     }
108 
109     /// Gets an instance of `SystemTable` if pointer is valid.
system_table_checked(&self) -> Result<SystemTable>110     pub fn system_table_checked(&self) -> Result<SystemTable> {
111         // SAFETY: Pointers to UEFI data strucutres.
112         Ok(SystemTable {
113             efi_entry: self,
114             table: unsafe { self.systab_ptr.as_ref() }.ok_or(Error::Unsupported)?,
115         })
116     }
117 
118     /// Gets the image handle.
image_handle(&self) -> DeviceHandle119     pub fn image_handle(&self) -> DeviceHandle {
120         DeviceHandle(self.image_handle)
121     }
122 }
123 
124 /// The vendor GUID for UEFI variables defined by GBL.
125 pub const GBL_EFI_VENDOR_GUID: EfiGuid =
126     EfiGuid::new(0x5a6d92f3, 0xa2d0, 0x4083, [0x91, 0xa1, 0xa5, 0x0f, 0x6c, 0x3d, 0x98, 0x30]);
127 
128 /// GUID for UEFI Memory Attributes Table
129 pub const EFI_MEMORY_ATTRIBUTES_GUID: EfiGuid =
130     EfiGuid::new(0xdcfa911d, 0x26eb, 0x469f, [0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20]);
131 
132 /// The name of the UEFI variable that GBL defines to determine whether to boot Fuchsia.
133 /// The value of the variable is ignored: if the variable is present,
134 /// it indicates that the bootloader should attempt to boot a Fuchsia target.
135 /// This may include reinitializing GPT partitions and partition contents.
136 pub const GBL_EFI_OS_BOOT_TARGET_VARNAME: &str = "gbl_os_boot_fuchsia";
137 
138 /// Creates an `EfiEntry` and initialize EFI global allocator.
139 ///
140 /// # Safety
141 ///
142 /// The API modifies internal global state. It should only be called once upon EFI entry to obtain
143 /// an instance of `EfiEntry` for accessing other APIs. Calling it again when EFI APIs are already
144 /// being used can introduce a risk of race.
145 #[cfg(not(test))]
initialize( image_handle: EfiHandle, systab_ptr: *const EfiSystemTable, ) -> Result<EfiEntry>146 pub unsafe fn initialize(
147     image_handle: EfiHandle,
148     systab_ptr: *const EfiSystemTable,
149 ) -> Result<EfiEntry> {
150     // SAFETY: By safety requirement of this function, `initialize` is only called once upon
151     // entering EFI application, where there should be no event notify function that can be
152     // triggered.
153     unsafe {
154         // Create one for internal global allocator.
155         allocation::init_efi_global_alloc(EfiEntry { image_handle, systab_ptr })?;
156     }
157     Ok(EfiEntry { image_handle, systab_ptr })
158 }
159 
160 /// Exits boot service and returns the memory map in the given buffer.
161 ///
162 /// The API takes ownership of the given `entry` and causes it to go out of scope.
163 /// This enforces strict compile time check that any reference/borrow in effect will cause compile
164 /// errors.
165 ///
166 /// Existing heap allocated memories will maintain their states. All system memory including them
167 /// will be under onwership of the subsequent OS or OS loader code.
exit_boot_services(entry: EfiEntry, mmap_buffer: &mut [u8]) -> Result<EfiMemoryMap>168 pub fn exit_boot_services(entry: EfiEntry, mmap_buffer: &mut [u8]) -> Result<EfiMemoryMap> {
169     let aligned = aligned_subslice(mmap_buffer, core::mem::align_of::<EfiMemoryDescriptor>())?;
170 
171     let res = entry.system_table().boot_services().get_memory_map(aligned)?;
172     entry.system_table().boot_services().exit_boot_services(&res)?;
173     // SAFETY:
174     // At this point, UEFI has successfully exited boot services and no event/notification can be
175     // triggered.
176     #[cfg(not(test))]
177     unsafe {
178         allocation::exit_efi_global_alloc();
179     }
180     Ok(res)
181 }
182 
183 /// `SystemTable` provides methods for accessing fields in `EFI_SYSTEM_TABLE`.
184 #[derive(Clone, Copy)]
185 pub struct SystemTable<'a> {
186     efi_entry: &'a EfiEntry,
187     table: &'a EfiSystemTable,
188 }
189 
190 impl<'a> SystemTable<'a> {
191     /// Creates an instance of `BootServices`
192     ///
193     /// Panics if not implemented by UEFI.
boot_services(&self) -> BootServices<'a>194     pub fn boot_services(&self) -> BootServices<'a> {
195         self.boot_services_checked().unwrap()
196     }
197 
198     /// Creates an instance of `BootServices`
199     ///
200     /// Returns Err(()) if not implemented by UEFI.
boot_services_checked(&self) -> Result<BootServices<'a>>201     pub fn boot_services_checked(&self) -> Result<BootServices<'a>> {
202         Ok(BootServices {
203             efi_entry: self.efi_entry,
204             // SAFETY: Pointers to UEFI data strucutres.
205             boot_services: unsafe { self.table.boot_services.as_ref() }
206                 .ok_or(Error::Unsupported)?,
207         })
208     }
209 
210     /// Creates an instance of `RuntimeServices`
211     ///
212     /// Panics if run time services is not implemented.
runtime_services(&self) -> RuntimeServices213     pub fn runtime_services(&self) -> RuntimeServices {
214         self.runtime_services_checked().unwrap()
215     }
216 
217     /// Creates an instance of `RuntimeServices` if available from system table.
runtime_services_checked(&self) -> Result<RuntimeServices>218     pub fn runtime_services_checked(&self) -> Result<RuntimeServices> {
219         Ok(RuntimeServices {
220             // SAFETY: Pointers to UEFI data strucutres.
221             runtime_services: *unsafe { self.table.runtime_services.as_ref() }
222                 .ok_or(Error::Unsupported)?,
223         })
224     }
225 
226     /// Gets the `EFI_SYSTEM_TABLE.ConOut` field.
con_out(&self) -> Result<Protocol<'a, SimpleTextOutputProtocol>>227     pub fn con_out(&self) -> Result<Protocol<'a, SimpleTextOutputProtocol>> {
228         // SAFETY: `EFI_SYSTEM_TABLE.ConOut` is a pointer to EfiSimpleTextOutputProtocol structure
229         // by definition. It lives until ExitBootService and thus as long as `self.efi_entry` or,
230         // 'a
231         Ok(unsafe {
232             Protocol::<SimpleTextOutputProtocol>::new(
233                 // No device handle. This protocol is a permanent reference.
234                 DeviceHandle(null_mut()),
235                 self.table.con_out,
236                 self.efi_entry,
237             )
238         })
239     }
240 
241     /// Gets the `EFI_SYSTEM_TABLE.ConfigurationTable` array.
configuration_table(&self) -> Option<&[EfiConfigurationTable]>242     pub fn configuration_table(&self) -> Option<&[EfiConfigurationTable]> {
243         match self.table.configuration_table.is_null() {
244             true => None,
245             // SAFETY: Non-null pointer to EFI configuration table.
246             false => unsafe {
247                 Some(from_raw_parts(
248                     self.table.configuration_table,
249                     self.table.number_of_table_entries,
250                 ))
251             },
252         }
253     }
254 }
255 
256 /// `BootServices` provides methods for accessing various EFI_BOOT_SERVICES interfaces.
257 #[derive(Clone, Copy)]
258 pub struct BootServices<'a> {
259     efi_entry: &'a EfiEntry,
260     boot_services: &'a EfiBootService,
261 }
262 
263 impl<'a> BootServices<'a> {
264     /// Wrapper of `EFI_BOOT_SERVICES.AllocatePool()`.
265     #[allow(dead_code)]
allocate_pool( &self, pool_type: EfiMemoryType, size: usize, ) -> Result<*mut core::ffi::c_void>266     fn allocate_pool(
267         &self,
268         pool_type: EfiMemoryType,
269         size: usize,
270     ) -> Result<*mut core::ffi::c_void> {
271         let mut out: *mut core::ffi::c_void = null_mut();
272         // SAFETY: `EFI_BOOT_SERVICES` method call.
273         unsafe {
274             efi_call!(self.boot_services.allocate_pool, pool_type, size, &mut out)?;
275         }
276         Ok(out)
277     }
278 
279     /// Wrapper of `EFI_BOOT_SERVICES.FreePool()`.
free_pool(&self, buf: *mut core::ffi::c_void) -> Result<()>280     fn free_pool(&self, buf: *mut core::ffi::c_void) -> Result<()> {
281         // SAFETY: `EFI_BOOT_SERVICES` method call.
282         unsafe { efi_call!(self.boot_services.free_pool, buf) }
283     }
284 
285     /// Wrapper of `EFI_BOOT_SERVICES.OpenProtocol()`.
open_protocol<T: ProtocolInfo>(&self, handle: DeviceHandle) -> Result<Protocol<'a, T>>286     pub fn open_protocol<T: ProtocolInfo>(&self, handle: DeviceHandle) -> Result<Protocol<'a, T>> {
287         let mut out_handle: EfiHandle = null_mut();
288         // SAFETY: EFI_BOOT_SERVICES method call.
289         unsafe {
290             efi_call!(
291                 self.boot_services.open_protocol,
292                 handle.0,
293                 &T::GUID,
294                 &mut out_handle as *mut _,
295                 self.efi_entry.image_handle().0,
296                 null_mut(),
297                 EFI_OPEN_PROTOCOL_ATTRIBUTE_BY_HANDLE_PROTOCOL
298             )?;
299         }
300         // SAFETY: `EFI_SYSTEM_TABLE.OpenProtocol` returns a valid pointer to `T::InterfaceType`
301         // on success. The pointer remains valid until closed by
302         // `EFI_BOOT_SERVICES.CloseProtocol()` when Protocol goes out of scope.
303         Ok(unsafe { Protocol::<T>::new(handle, out_handle as *mut _, self.efi_entry) })
304     }
305 
306     /// Wrapper of `EFI_BOOT_SERVICES.CloseProtocol()`.
307     #[allow(dead_code)]
close_protocol<T: ProtocolInfo>(&self, handle: DeviceHandle) -> Result<()>308     fn close_protocol<T: ProtocolInfo>(&self, handle: DeviceHandle) -> Result<()> {
309         // SAFETY: EFI_BOOT_SERVICES method call.
310         unsafe {
311             efi_call!(
312                 self.boot_services.close_protocol,
313                 handle.0,
314                 &T::GUID,
315                 self.efi_entry.image_handle().0,
316                 null_mut()
317             )
318         }
319     }
320 
321     /// Call `EFI_BOOT_SERVICES.LocateHandleBuffer()` with fixed
322     /// `EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL` and without search key.
locate_handle_buffer_by_protocol<T: ProtocolInfo>(&self) -> Result<LocatedHandles<'a>>323     pub fn locate_handle_buffer_by_protocol<T: ProtocolInfo>(&self) -> Result<LocatedHandles<'a>> {
324         let mut num_handles: usize = 0;
325         let mut handles: *mut EfiHandle = null_mut();
326         // SAFETY: EFI_BOOT_SERVICES method call.
327         unsafe {
328             efi_call!(
329                 self.boot_services.locate_handle_buffer,
330                 EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL,
331                 &T::GUID,
332                 null_mut(),
333                 &mut num_handles as *mut usize as *mut _,
334                 &mut handles as *mut *mut EfiHandle
335             )?
336         };
337         // `handles` should be a valid pointer if the above succeeds. But just double check
338         // to be safe. If assert fails, then there's a bug in the UEFI firmware.
339         assert!(!handles.is_null());
340         Ok(LocatedHandles::new(handles, num_handles, self.efi_entry))
341     }
342 
343     /// Search and open the first found target EFI protocol.
find_first_and_open<T: ProtocolInfo>(&self) -> Result<Protocol<'a, T>>344     pub fn find_first_and_open<T: ProtocolInfo>(&self) -> Result<Protocol<'a, T>> {
345         // We don't use EFI_BOOT_SERVICES.LocateProtocol() because it doesn't give device handle
346         // which is required to close the protocol.
347         let handle = *self
348             .locate_handle_buffer_by_protocol::<T>()?
349             .handles()
350             .first()
351             .ok_or(Error::NotFound)?;
352         self.open_protocol::<T>(handle)
353     }
354 
355     /// Wrapper of `EFI_BOOT_SERVICE.GetMemoryMap()`.
get_memory_map<'b>(&self, mmap_buffer: &'b mut [u8]) -> Result<EfiMemoryMap<'b>>356     pub fn get_memory_map<'b>(&self, mmap_buffer: &'b mut [u8]) -> Result<EfiMemoryMap<'b>> {
357         let mut mmap_size = mmap_buffer.len();
358         let mut map_key: usize = 0;
359         let mut descriptor_size: usize = 0;
360         let mut descriptor_version: u32 = 0;
361         // SAFETY: EFI_BOOT_SERVICES method call.
362         unsafe {
363             efi_call!(
364                 self.boot_services.get_memory_map,
365                 &mut mmap_size,
366                 mmap_buffer.as_mut_ptr() as *mut _,
367                 &mut map_key,
368                 &mut descriptor_size,
369                 &mut descriptor_version
370             )
371         }?;
372         Ok(EfiMemoryMap::new(
373             &mut mmap_buffer[..mmap_size],
374             map_key,
375             descriptor_size,
376             descriptor_version,
377         ))
378     }
379 
380     /// Wrapper of `EFI_BOOT_SERVICE.ExitBootServices()`.
exit_boot_services<'b>(&self, mmap: &'b EfiMemoryMap<'b>) -> Result<()>381     fn exit_boot_services<'b>(&self, mmap: &'b EfiMemoryMap<'b>) -> Result<()> {
382         // SAFETY: EFI_BOOT_SERVICES method call.
383         unsafe {
384             efi_call!(
385                 self.boot_services.exit_boot_services,
386                 self.efi_entry.image_handle().0,
387                 mmap.map_key()
388             )
389         }
390     }
391 
392     /// Wrapper of `EFI_BOOT_SERVICE.Stall()`.
stall(&self, micro: usize) -> Result<()>393     pub fn stall(&self, micro: usize) -> Result<()> {
394         // SAFETY: EFI_BOOT_SERVICES method call.
395         unsafe { efi_call!(self.boot_services.stall, micro) }
396     }
397 
398     /// Wraps `EFI_BOOT_SERVICE.CreateEvent()`.
399     ///
400     /// This function creates an event without a notification callback function; to create an event
401     /// with a notification, see [create_event_with_notification].
402     ///
403     /// # Arguments
404     /// * `event_type`: The EFI event type.
create_event(&self, event_type: EventType) -> Result<Event<'a, 'static>>405     pub fn create_event(&self, event_type: EventType) -> Result<Event<'a, 'static>> {
406         let mut efi_event: EfiEvent = null_mut();
407         // SAFETY:
408         // * all parameters obey the `CreateEvent()` spec
409         // * on success we take ownership of the provided `efi_event`
410         unsafe {
411             efi_call!(
412                 self.boot_services.create_event,
413                 event_type as u32,
414                 0,
415                 None,
416                 null_mut(),
417                 &mut efi_event
418             )?;
419         }
420         Ok(Event::new(self.efi_entry, efi_event, None))
421     }
422 
423     /// Wraps `EFI_BOOT_SERVICE.CreateEvent()`.
424     ///
425     /// This function creates an event with a notification callback function.
426     ///
427     /// Unlike [create_event], this function is unsafe because the callback will be executed
428     /// concurrently with the main application code at a higher interrupt level, and there are
429     /// a few cases where this can lead to races.
430     ///
431     /// # Arguments
432     /// * `event_type`: The EFI event type.
433     /// * `cb`: An [EventNotify] which implements the event notification function and provides the
434     ///         task level priority setting.
435     ///
436     /// # Safety
437     /// Most of the safety conditions are enforced at compile-time by the [Sync] requirement on
438     /// [EventNotifyCallback] - this ensures that e.g. callers cannot capture their raw [EfiEntry]
439     /// in a callback, but will need to wrap it in a [Sync] type which will ensure safe sharing
440     /// between the main application and the callback.
441     ///
442     /// The exception is the global allocation and panic hooks, which use a separate global
443     /// [EfiEntry] that is not synchronized outside the main application. The caller must ensure
444     /// that the main application code is not using its [EfiEntry] while a notification callback
445     /// is trying to concurrently use the global [EfiEntry].
446     ///
447     /// The easiest way to accomplish this is to write notifications callbacks that:
448     /// * do not allocate or deallocate heap memory
449     /// * do not panic
450     /// Callbacks following these guidelines are safe as they do not use the global [EfiEntry].
451     ///
452     /// If that is not possible, then the caller must ensure that nothing else makes any calls into
453     /// UEFI while the returned [Event] is alive; the callback function must have exclusive access
454     /// to the UEFI APIs so it can use the globals without triggering UEFI reentry.
455     ///
456     /// In unittests there is no global [EfiEntry] so this is always safe.
create_event_with_notification<'e>( &self, event_type: EventType, notify: &'e mut EventNotify, ) -> Result<Event<'a, 'e>>457     pub unsafe fn create_event_with_notification<'e>(
458         &self,
459         event_type: EventType,
460         notify: &'e mut EventNotify,
461     ) -> Result<Event<'a, 'e>> {
462         let mut efi_event: EfiEvent = null_mut();
463         // SAFETY:
464         // Pointers passed are output/callback context pointers which will not be retained by the
465         // callback (`fn efi_event_cb()`).
466         // The returned `Event` enforces a borrow to `cb` for 'e. It closes the event when it
467         // goes out of scope. This ensures that `cb` lives at least as long as the event is in
468         // effect and there can be no other borrows to `cb`.
469         unsafe {
470             efi_call!(
471                 self.boot_services.create_event,
472                 event_type as u32,
473                 notify.tpl as usize,
474                 Some(efi_event_cb),
475                 notify as *mut _ as *mut _,
476                 &mut efi_event
477             )?;
478         }
479         Ok(Event::new(self.efi_entry, efi_event, Some(notify.cb)))
480     }
481 
482     /// Wrapper of `EFI_BOOT_SERVICE.CloseEvent()`.
close_event(&self, event: &Event) -> Result<()>483     fn close_event(&self, event: &Event) -> Result<()> {
484         // SAFETY: EFI_BOOT_SERVICES method call.
485         unsafe { efi_call!(self.boot_services.close_event, event.efi_event) }
486     }
487 
488     /// Wrapper of `EFI_BOOT_SERVICE.CheckEvent()`.
489     ///
490     /// On success, returns true if the event is signaled, false if not.
check_event(&self, event: &Event) -> Result<bool>491     pub fn check_event(&self, event: &Event) -> Result<bool> {
492         // SAFETY: EFI_BOOT_SERVICES method call.
493         match unsafe { efi_call!(self.boot_services.check_event, event.efi_event) } {
494             Err(e) if e != Error::NotReady => Err(e),
495             Ok(()) => Ok(true),
496             _ => Ok(false),
497         }
498     }
499 
500     /// Wrapper of `EFI_BOOT_SERVICE.SetTimer()`.
set_timer( &self, event: &Event, delay_type: EfiTimerDelay, trigger_time: Duration, ) -> Result<()>501     pub fn set_timer(
502         &self,
503         event: &Event,
504         delay_type: EfiTimerDelay,
505         trigger_time: Duration,
506     ) -> Result<()> {
507         // SAFETY: EFI_BOOT_SERVICES method call.
508         unsafe {
509             efi_call!(
510                 self.boot_services.set_timer,
511                 event.efi_event,
512                 delay_type,
513                 (trigger_time.as_nanos() / 100).try_into()?
514             )
515         }
516     }
517 }
518 
519 /// `RuntimeServices` provides methods for accessing various EFI_RUNTIME_SERVICES interfaces.
520 #[derive(Clone, Copy)]
521 pub struct RuntimeServices {
522     runtime_services: EfiRuntimeService,
523 }
524 
525 impl RuntimeServices {
526     /// Wrapper of `EFI_RUNTIME_SERVICES.GetVariable()`.
get_variable(&self, guid: &EfiGuid, name: &str, out: &mut [u8]) -> Result<usize>527     pub fn get_variable(&self, guid: &EfiGuid, name: &str, out: &mut [u8]) -> Result<usize> {
528         let mut size = out.len();
529 
530         let mut name_utf16: Vec<u16> = name.encode_utf16().collect();
531         name_utf16.push(0); // null-terminator
532 
533         // SAFETY:
534         // * `&mut size` and `&mut out` are input/output params only and will not be retained
535         // * `&mut size` and `&mut out` are valid pointers and outlive the call
536         unsafe {
537             efi_call!(
538                 @bufsize size,
539                 self.runtime_services.get_variable,
540                 name_utf16.as_ptr(),
541                 guid,
542                 null_mut(),
543                 &mut size,
544                 out.as_mut_ptr() as *mut core::ffi::c_void
545             )?;
546         }
547         Ok(size)
548     }
549 
550     /// Wrapper of `EFI_RUNTIME_SERVICES.SetVariable()`.
set_variable(&self, guid: &EfiGuid, name: &str, data: &[u8]) -> Result<()>551     pub fn set_variable(&self, guid: &EfiGuid, name: &str, data: &[u8]) -> Result<()> {
552         let mut name_utf16: Vec<u16> = name.encode_utf16().collect();
553         name_utf16.push(0); // null-terminator
554 
555         // SAFETY:
556         // * `data.as_mut_ptr()` and `name_utf16.as_ptr()` are valid pointers,
557         // * outlive the call, and are not retained.
558         unsafe {
559             efi_call!(
560                 self.runtime_services.set_variable,
561                 name_utf16.as_ptr(),
562                 guid,
563                 0,
564                 data.len(),
565                 data.as_ptr() as *const core::ffi::c_void
566             )
567         }
568     }
569 
570     /// Wrapper of `EFI_RUNTIME_SERVICES.reset_system`.
reset_system( &self, reset_type: EFI_RESET_TYPE, reset_status: EFI_STATUS, reset_data: Option<&mut [u8]>, ) -> !571     pub fn reset_system(
572         &self,
573         reset_type: EFI_RESET_TYPE,
574         reset_status: EFI_STATUS,
575         reset_data: Option<&mut [u8]>,
576     ) -> ! {
577         let (reset_data_len, reset_data_ptr) = match reset_data {
578             Some(v) => (v.len(), v.as_mut_ptr() as _),
579             _ => (0, null_mut()),
580         };
581         // SAFETY:
582         // * `reset_data_ptr` is either a valid pointer or NULL which by UEFI spec is allowed.
583         // * The call reboots the device and thus is not expected to return.
584         unsafe {
585             self.runtime_services.reset_system.unwrap()(
586                 reset_type,
587                 reset_status,
588                 reset_data_len,
589                 reset_data_ptr,
590             );
591         }
592 
593         unreachable!();
594     }
595 
596     /// Performs a cold reset without status code or data.
cold_reset(&self) -> !597     pub fn cold_reset(&self) -> ! {
598         self.reset_system(EFI_RESET_TYPE_EFI_RESET_COLD, EFI_STATUS_SUCCESS, None)
599     }
600 }
601 
602 /// EFI Event type to pass to BootServicess::create_event.
603 /// See UEFI documentation for details.
604 #[allow(missing_docs)]
605 #[repr(u32)]
606 pub enum EventType {
607     Timer = EFI_EVENT_TYPE_TIMER,
608     RunTime = EFI_EVENT_TYPE_RUNTIME,
609     NotifyWait = EFI_EVENT_TYPE_NOTIFY_WAIT,
610     NotifySignal = EFI_EVENT_TYPE_NOTIFY_SIGNAL,
611     SignalExitBootServices = EFI_EVENT_TYPE_SIGNAL_EXIT_BOOT_SERVICES,
612     SignalVirtualAddressChange = EFI_EVENT_TYPE_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
613 
614     // Valid combinations:
615     TimerNotifySignal = EFI_EVENT_TYPE_TIMER | EFI_EVENT_TYPE_NOTIFY_SIGNAL,
616 }
617 
618 /// EFI task level priority setting for event notify function.
619 /// See UEFI documentation for details.
620 #[allow(missing_docs)]
621 #[repr(usize)]
622 #[derive(Copy, Clone)]
623 pub enum Tpl {
624     Application = 4,
625     Callback = 8,
626     Notify = 16,
627     HighLevel = 31,
628 }
629 
630 /// Event notification callback function.
631 ///
632 /// The callback function itself takes the [EfiEvent] as an argument and has no return value.
633 /// This type is a mutable borrow of a closure to ensure that it will outlive the [EfiEvent] and
634 /// that the callback has exclusive access to it.
635 ///
636 /// Additionally, the function must be [Sync] because it will be run concurrently to the main app
637 /// code at a higher interrupt level. One consequence of this is that we cannot capture an
638 /// [EfiEntry] or any related object in the closure, as they are not [Sync]. This is intentional;
639 /// in general UEFI APIs are not reentrant except in very limited ways, and we could trigger
640 /// undefined behavior if we try to call into UEFI while the main application code is also in the
641 /// middle of a UEFI call. Instead, the notification should signal the main app code to make any
642 /// necessary UEFI calls once it regains control.
643 pub type EventNotifyCallback<'a> = &'a mut (dyn FnMut(EfiEvent) + Sync);
644 
645 /// `EventNotify` contains the task level priority setting and a mutable reference to a
646 /// closure for the callback. It is passed as the context pointer to low level EFI event
647 /// notification function entry (`unsafe extern "C" fn efi_event_cb(...)`).
648 pub struct EventNotify<'e> {
649     tpl: Tpl,
650     cb: EventNotifyCallback<'e>,
651 }
652 
653 impl<'e> EventNotify<'e> {
654     /// Creates a new [EventNotify].
new(tpl: Tpl, cb: EventNotifyCallback<'e>) -> Self655     pub fn new(tpl: Tpl, cb: EventNotifyCallback<'e>) -> Self {
656         Self { tpl, cb }
657     }
658 }
659 
660 /// `Event` wraps the raw `EfiEvent` handle and internally enforces a borrow of the registered
661 /// callback for the given life time `'n`. The event is automatically closed when going out of
662 /// scope.
663 pub struct Event<'a, 'n> {
664     // If `efi_entry` is None, it represents an unowned Event and won't get closed on drop.
665     efi_entry: Option<&'a EfiEntry>,
666     efi_event: EfiEvent,
667     // The actual callback has been passed into UEFI via raw pointer in [create_event], so we
668     // use [PhantomData] to ensure the callback will outlive the event.
669     cb: PhantomData<Option<EventNotifyCallback<'n>>>,
670 }
671 
672 impl<'a, 'n> Event<'a, 'n> {
673     /// Creates an instance of owned `Event`. The `Event` is closed when going out of scope.
new( efi_entry: &'a EfiEntry, efi_event: EfiEvent, _cb: Option<EventNotifyCallback<'n>>, ) -> Self674     fn new(
675         efi_entry: &'a EfiEntry,
676         efi_event: EfiEvent,
677         _cb: Option<EventNotifyCallback<'n>>,
678     ) -> Self {
679         Self { efi_entry: Some(efi_entry), efi_event, cb: PhantomData }
680     }
681 
682     /// Creates an  unowned `Event`. The `Event` is not closed when going out of scope.
683     // TODO allow unused?
684     #[allow(dead_code)]
new_unowned(efi_event: EfiEvent) -> Self685     fn new_unowned(efi_event: EfiEvent) -> Self {
686         Self { efi_entry: None, efi_event: efi_event, cb: PhantomData }
687     }
688 }
689 
690 impl Drop for Event<'_, '_> {
drop(&mut self)691     fn drop(&mut self) {
692         if let Some(efi_entry) = self.efi_entry {
693             efi_entry.system_table().boot_services().close_event(self).unwrap();
694         }
695     }
696 }
697 
698 /// Event notify function entry for EFI events.
699 ///
700 /// Safety:
701 ///
702 ///   `ctx` must point to a `EventNotify` type object.
703 ///   `ctx` must live longer than the event.
704 ///   There should be no other references to `ctx`.
efi_event_cb(event: EfiEvent, ctx: *mut core::ffi::c_void)705 unsafe extern "C" fn efi_event_cb(event: EfiEvent, ctx: *mut core::ffi::c_void) {
706     // SAFETY: By safety requirement of this function, ctx points to a valid `EventNotify` object,
707     // outlives the event/the function call, and there is no other borrows.
708     let event_cb = unsafe { (ctx as *mut EventNotify).as_mut() }.unwrap();
709     (event_cb.cb)(event);
710 }
711 
712 /// A type for accessing memory map.
713 #[derive(Debug)]
714 pub struct EfiMemoryMap<'a> {
715     buffer: &'a mut [u8],
716     map_key: usize,
717     descriptor_size: usize,
718     descriptor_version: u32,
719 }
720 
721 /// Iterator for traversing `EfiMemoryDescriptor` items in `EfiMemoryMap::buffer`.
722 pub struct EfiMemoryMapIter<'a: 'b, 'b> {
723     memory_map: &'b EfiMemoryMap<'a>,
724     offset: usize,
725 }
726 
727 impl<'a, 'b> Iterator for EfiMemoryMapIter<'a, 'b> {
728     type Item = &'b EfiMemoryDescriptor;
729 
next(&mut self) -> Option<Self::Item>730     fn next(&mut self) -> Option<Self::Item> {
731         if self.offset >= self.memory_map.buffer.len() {
732             return None;
733         }
734         let bytes = &self.memory_map.buffer[self.offset..][..self.memory_map.descriptor_size];
735         self.offset += self.memory_map.descriptor_size;
736         Some(Ref::into_ref(Ref::<_, EfiMemoryDescriptor>::new_from_prefix(bytes).unwrap().0))
737     }
738 }
739 
740 impl<'a> EfiMemoryMap<'a> {
741     /// 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, ) -> Self742     fn new(
743         buffer: &'a mut [u8],
744         map_key: usize,
745         descriptor_size: usize,
746         descriptor_version: u32,
747     ) -> Self {
748         Self { buffer, map_key, descriptor_size, descriptor_version }
749     }
750 
751     /// Returns the buffer.
buffer(&self) -> &[u8]752     pub fn buffer(&self) -> &[u8] {
753         self.buffer
754     }
755 
756     /// Returns the value of `map_key`.
map_key(&self) -> usize757     pub fn map_key(&self) -> usize {
758         self.map_key
759     }
760 
761     /// Returns the value of `descriptor_version`.
descriptor_version(&self) -> u32762     pub fn descriptor_version(&self) -> u32 {
763         self.descriptor_version
764     }
765 
766     /// Returns the number of descriptors.
len(&self) -> usize767     pub fn len(&self) -> usize {
768         self.buffer.len() / self.descriptor_size
769     }
770 }
771 
772 impl<'a: 'b, 'b> IntoIterator for &'b EfiMemoryMap<'a> {
773     type Item = &'b EfiMemoryDescriptor;
774     type IntoIter = EfiMemoryMapIter<'a, 'b>;
775 
into_iter(self) -> Self::IntoIter776     fn into_iter(self) -> Self::IntoIter {
777         EfiMemoryMapIter { memory_map: self, offset: 0 }
778     }
779 }
780 
781 /// A type for accessing Memory attributes table
782 pub struct EfiMemoryAttributesTable<'a> {
783     /// EfiMemoryAttributesTable header
784     pub header: &'a EfiMemoryAttributesTableHeader,
785     tail: &'a [u8],
786 }
787 
788 /// Iterator for traversing `EfiMemoryAttributesTable` descriptors.
789 pub struct EfiMemoryAttributesTableIter<'a> {
790     descriptor_size: usize,
791     tail: &'a [u8],
792 }
793 
794 impl<'a> Iterator for EfiMemoryAttributesTableIter<'a> {
795     type Item = &'a EfiMemoryDescriptor;
796 
next(&mut self) -> Option<Self::Item>797     fn next(&mut self) -> Option<Self::Item> {
798         // Descriptor size can be greater than `EfiMemoryDescriptor`, so we potentially slice off
799         // pieces greater than struct size. Thus can't just convert buffer to slice of
800         // corresponding type.
801         if let Some((desc_bytes, tail_new)) = self.tail.split_at_checked(self.descriptor_size) {
802             let desc = Ref::into_ref(
803                 Ref::<_, EfiMemoryDescriptor>::new_from_prefix(desc_bytes).unwrap().0,
804             );
805             self.tail = tail_new;
806             Some(desc)
807         } else {
808             None
809         }
810     }
811 }
812 
813 impl<'a> EfiMemoryAttributesTable<'a> {
814     /// Creates a new instance with the given parameters obtained from `get_memory_map()`.
815     ///
816     /// # Returns
817     /// Ok(EfiMemoryAttributesTable) - on success
818     /// Err(Error::NotFound) - if table type is incorrect
819     /// Err(e) - if error `e` occurred parsing table buffer
820     //
821     // SAFETY:
822     // `configuration_table` must be valid EFI Configuration Table object.
new( configuration_table: EfiConfigurationTable, ) -> Result<EfiMemoryAttributesTable<'a>>823     pub unsafe fn new(
824         configuration_table: EfiConfigurationTable,
825     ) -> Result<EfiMemoryAttributesTable<'a>> {
826         if configuration_table.vendor_guid != EFI_MEMORY_ATTRIBUTES_GUID {
827             return Err(Error::NotFound);
828         }
829         let buf = configuration_table.vendor_table;
830 
831         // SAFETY: Buffer provided by EFI configuration table.
832         let header = unsafe {
833             let header_bytes =
834                 from_raw_parts(buf as *const u8, size_of::<EfiMemoryAttributesTableHeader>());
835             EfiMemoryAttributesTableHeader::ref_from(header_bytes).ok_or(Error::InvalidInput)?
836         };
837 
838         // Note: `descriptor_size` may be bigger than `EfiMemoryDescriptor`.
839         let descriptor_size: usize = header.descriptor_size.try_into().unwrap();
840         let descriptors_count: usize = header.number_of_entries.try_into().unwrap();
841 
842         // SAFETY: Buffer provided by EFI configuration table.
843         let tail = unsafe {
844             from_raw_parts(
845                 (buf as *const u8).add(core::mem::size_of_val(header)),
846                 descriptors_count * descriptor_size,
847             )
848         };
849 
850         Ok(Self { header, tail })
851     }
852 }
853 
854 impl<'a> IntoIterator for &EfiMemoryAttributesTable<'a> {
855     type Item = &'a EfiMemoryDescriptor;
856     type IntoIter = EfiMemoryAttributesTableIter<'a>;
857 
into_iter(self) -> Self::IntoIter858     fn into_iter(self) -> Self::IntoIter {
859         let descriptor_size = usize::try_from(self.header.descriptor_size).unwrap();
860         let tail = &self.tail[..];
861         EfiMemoryAttributesTableIter { descriptor_size, tail }
862     }
863 }
864 
865 /// A type representing a UEFI handle to a UEFI device.
866 #[derive(Debug, Copy, Clone, PartialEq)]
867 pub struct DeviceHandle(EfiHandle);
868 
869 impl DeviceHandle {
870     /// Public constructor
new(handle: EfiHandle) -> Self871     pub fn new(handle: EfiHandle) -> Self {
872         Self(handle)
873     }
874 }
875 
876 /// `LocatedHandles` holds the array of handles return by
877 /// `BootServices::locate_handle_buffer_by_protocol()`.
878 pub struct LocatedHandles<'a> {
879     handles: &'a [DeviceHandle],
880     efi_entry: &'a EfiEntry,
881 }
882 
883 impl<'a> LocatedHandles<'a> {
new(handles: *mut EfiHandle, len: usize, efi_entry: &'a EfiEntry) -> Self884     pub(crate) fn new(handles: *mut EfiHandle, len: usize, efi_entry: &'a EfiEntry) -> Self {
885         // Implementation is not suppose to call this with a NULL pointer.
886         debug_assert!(!handles.is_null());
887         Self {
888             // SAFETY: Given correct UEFI firmware, non-null pointer points to valid memory.
889             // The memory is owned by the objects.
890             handles: unsafe { from_raw_parts(handles as *mut DeviceHandle, len) },
891             efi_entry: efi_entry,
892         }
893     }
894     /// Get the list of handles as a slice.
handles(&self) -> &[DeviceHandle]895     pub fn handles(&self) -> &[DeviceHandle] {
896         self.handles
897     }
898 }
899 
900 impl Drop for LocatedHandles<'_> {
drop(&mut self)901     fn drop(&mut self) {
902         self.efi_entry
903             .system_table()
904             .boot_services()
905             .free_pool(self.handles.as_ptr() as *mut _)
906             .unwrap();
907     }
908 }
909 
910 /// Helper macro for printing message via `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL` in
911 /// `EFI_SYSTEM_TABLE.ConOut`.
912 #[macro_export]
913 macro_rules! efi_print {
914     ( $efi_entry:expr, $( $x:expr ),* $(,)? ) => {
915             write!($efi_entry.system_table().con_out().unwrap(), $($x,)*).unwrap()
916     };
917 }
918 
919 /// Similar to [efi_print!], but automatically adds the UEFI newline sequence (`\r\n`).
920 #[macro_export]
921 macro_rules! efi_println {
922     ( $efi_entry:expr, $( $x:expr ),* $(,)? ) => {
923         {
924             efi_print!($efi_entry, $($x,)*);
925             efi_print!($efi_entry, "\r\n");
926         }
927     };
928 }
929 
930 /// Resets system. Hangs if not supported.
931 #[cfg(not(test))]
reset() -> !932 pub fn reset() -> ! {
933     efi_try_print!("Resetting...\r\n");
934     match allocation::internal_efi_entry_and_rt().1 {
935         Some(rt) => rt.cold_reset(),
936         _ => efi_try_print!("Runtime services not supported. Hangs...\r\n"),
937     }
938     loop {}
939 }
940 
941 /// Provides a builtin panic handler.
942 /// In the long term, to improve flexibility, consider allowing application to install a custom
943 /// handler into `EfiEntry` to be called here.
944 /// Don't set this as the panic handler so that other crates' tests can depend on libefi.
945 #[cfg(not(test))]
panic(panic: &PanicInfo) -> !946 pub fn panic(panic: &PanicInfo) -> ! {
947     efi_try_print!("Panics! {}\r\n", panic);
948     reset();
949 }
950 
951 #[cfg(test)]
952 mod test {
953     use super::*;
954     use crate::protocol::block_io::BlockIoProtocol;
955     use efi_types::{
956         EfiBlockIoProtocol, EfiEventNotify, EfiLocateHandleSearchType, EfiStatus, EfiTpl,
957         EFI_MEMORY_TYPE_LOADER_CODE, EFI_MEMORY_TYPE_LOADER_DATA, EFI_STATUS_NOT_FOUND,
958         EFI_STATUS_NOT_READY, EFI_STATUS_SUCCESS, EFI_STATUS_UNSUPPORTED,
959         EFI_TIMER_DELAY_TIMER_PERIODIC,
960     };
961     use std::{cell::RefCell, collections::VecDeque, mem::size_of, slice::from_raw_parts_mut};
962     use utils::RecurringTimer;
963     use zerocopy::IntoBytes;
964 
965     /// Helper function to generate a Protocol from an interface type.
generate_protocol<'a, P: ProtocolInfo>( efi_entry: &'a EfiEntry, proto: &'a mut P::InterfaceType, ) -> Protocol<'a, P>966     pub fn generate_protocol<'a, P: ProtocolInfo>(
967         efi_entry: &'a EfiEntry,
968         proto: &'a mut P::InterfaceType,
969     ) -> Protocol<'a, P> {
970         // SAFETY: proto is a valid pointer and lasts at least as long as efi_entry.
971         unsafe { Protocol::<'a, P>::new(DeviceHandle::new(null_mut()), proto, efi_entry) }
972     }
973 
974     /// A structure to store the traces of arguments/outputs for EFI methods.
975     #[derive(Default)]
976     pub struct EfiCallTraces {
977         pub free_pool_trace: FreePoolTrace,
978         pub open_protocol_trace: OpenProtocolTrace,
979         pub close_protocol_trace: CloseProtocolTrace,
980         pub locate_handle_buffer_trace: LocateHandleBufferTrace,
981         pub get_memory_map_trace: GetMemoryMapTrace,
982         pub exit_boot_services_trace: ExitBootServicespTrace,
983         pub create_event_trace: CreateEventTrace,
984         pub close_event_trace: CloseEventTrace,
985         pub check_event_trace: CheckEventTrace,
986         pub set_timer_trace: SetTimerTrace,
987     }
988 
989     // Declares a global instance of EfiCallTraces.
990     // Need to use thread local storage because rust unit test is multi-threaded.
991     thread_local! {
992         static EFI_CALL_TRACES: RefCell<EfiCallTraces> = RefCell::new(Default::default());
993     }
994 
995     /// Exports for unit-test in submodules.
efi_call_traces() -> &'static std::thread::LocalKey<RefCell<EfiCallTraces>>996     pub fn efi_call_traces() -> &'static std::thread::LocalKey<RefCell<EfiCallTraces>> {
997         &EFI_CALL_TRACES
998     }
999 
1000     /// EFI_BOOT_SERVICE.FreePool() test implementation.
1001     #[derive(Default)]
1002     pub struct FreePoolTrace {
1003         // Capture `buf`
1004         pub inputs: VecDeque<*mut core::ffi::c_void>,
1005     }
1006 
1007     /// Mock of the `EFI_BOOT_SERVICE.FreePool` C API in test environment.
free_pool(buf: *mut core::ffi::c_void) -> EfiStatus1008     extern "C" fn free_pool(buf: *mut core::ffi::c_void) -> EfiStatus {
1009         EFI_CALL_TRACES.with(|traces| {
1010             traces.borrow_mut().free_pool_trace.inputs.push_back(buf);
1011             EFI_STATUS_SUCCESS
1012         })
1013     }
1014 
1015     /// EFI_BOOT_SERVICE.OpenProtocol() test implementation.
1016     #[derive(Default)]
1017     pub struct OpenProtocolTrace {
1018         // Capture `handle`, `protocol_guid`, `agent_handle`.
1019         pub inputs: VecDeque<(DeviceHandle, EfiGuid, EfiHandle)>,
1020         // Return `intf`, EfiStatus.
1021         pub outputs: VecDeque<(EfiHandle, EfiStatus)>,
1022     }
1023 
1024     /// Mock of the `EFI_BOOT_SERVICE.OpenProtocol` C API in test environment.
1025     ///
1026     /// # Safety
1027     ///
1028     ///   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, ) -> EfiStatus1029     unsafe extern "C" fn open_protocol(
1030         handle: EfiHandle,
1031         protocol_guid: *const EfiGuid,
1032         intf: *mut *mut core::ffi::c_void,
1033         agent_handle: EfiHandle,
1034         _: EfiHandle,
1035         attr: u32,
1036     ) -> EfiStatus {
1037         assert_eq!(attr, EFI_OPEN_PROTOCOL_ATTRIBUTE_BY_HANDLE_PROTOCOL);
1038         EFI_CALL_TRACES.with(|traces| {
1039             let trace = &mut traces.borrow_mut().open_protocol_trace;
1040             trace.inputs.push_back((
1041                 DeviceHandle(handle),
1042                 // SAFETY: function safety docs require valid `protocol_guid`.
1043                 unsafe { *protocol_guid },
1044                 agent_handle,
1045             ));
1046 
1047             let (intf_handle, status) = trace.outputs.pop_front().unwrap();
1048             // SAFETY: function safety docs require valid `intf`.
1049             unsafe { *intf = intf_handle };
1050 
1051             status
1052         })
1053     }
1054 
1055     /// EFI_BOOT_SERVICE.CloseProtocol() test implementation.
1056     #[derive(Default)]
1057     pub struct CloseProtocolTrace {
1058         // Capture `handle`, `protocol_guid`, `agent_handle`
1059         pub inputs: VecDeque<(DeviceHandle, EfiGuid, EfiHandle)>,
1060     }
1061 
1062     /// Mock of the `EFI_BOOT_SERVICE.CloseProtocol` C API in test environment.
1063     ///
1064     /// # Safety
1065     ///
1066     ///   Caller should guarantee that `protocol_guid` points to valid memory location.
close_protocol( handle: EfiHandle, protocol_guid: *const EfiGuid, agent_handle: EfiHandle, _: EfiHandle, ) -> EfiStatus1067     unsafe extern "C" fn close_protocol(
1068         handle: EfiHandle,
1069         protocol_guid: *const EfiGuid,
1070         agent_handle: EfiHandle,
1071         _: EfiHandle,
1072     ) -> EfiStatus {
1073         EFI_CALL_TRACES.with(|traces| {
1074             traces.borrow_mut().close_protocol_trace.inputs.push_back((
1075                 DeviceHandle(handle),
1076                 // SAFETY: function safety docs require valid `protocol_guid`.
1077                 unsafe { *protocol_guid },
1078                 agent_handle,
1079             ));
1080             EFI_STATUS_SUCCESS
1081         })
1082     }
1083 
1084     /// EFI_BOOT_SERVICE.LocateHandleBuffer.
1085     #[derive(Default)]
1086     pub struct LocateHandleBufferTrace {
1087         // Capture `protocol`.
1088         pub inputs: VecDeque<EfiGuid>,
1089         // For returning in `num_handles` and `buf`.
1090         pub outputs: VecDeque<(usize, *mut DeviceHandle)>,
1091     }
1092 
1093     /// Mock of the `EFI_BOOT_SERVICE.LocateHandleBuffer` C API in test environment.
1094     ///
1095     /// # Safety
1096     /// Caller should guarantee that `protocol`, `num_handles`, and `buf` point to valid memory
1097     /// 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, ) -> EfiStatus1098     unsafe extern "C" fn locate_handle_buffer(
1099         search_type: EfiLocateHandleSearchType,
1100         protocol: *const EfiGuid,
1101         search_key: *mut core::ffi::c_void,
1102         num_handles: *mut usize,
1103         buf: *mut *mut EfiHandle,
1104     ) -> EfiStatus {
1105         assert_eq!(search_type, EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL);
1106         assert_eq!(search_key, null_mut());
1107         EFI_CALL_TRACES.with(|traces| {
1108             let trace = &mut traces.borrow_mut().locate_handle_buffer_trace;
1109             // SAFETY: function safety docs require valid `protocol`.
1110             unsafe { trace.inputs.push_back(*protocol) };
1111 
1112             let (num, handles) = trace.outputs.pop_front().unwrap();
1113             // SAFETY: function safety docs require valid `num_handles`.
1114             unsafe { *num_handles = num as usize };
1115             // SAFETY: function safety docs require valid `buf`.
1116             unsafe { *buf = handles as *mut EfiHandle };
1117 
1118             EFI_STATUS_SUCCESS
1119         })
1120     }
1121 
1122     /// EFI_BOOT_SERVICE.GetMemoryMap.
1123     #[derive(Default)]
1124     pub struct GetMemoryMapTrace {
1125         // Capture `memory_map_size` and `memory_map` argument.
1126         pub inputs: VecDeque<(usize, *mut EfiMemoryDescriptor)>,
1127         // Output value `map_key`, `memory_map_size`.
1128         pub outputs: VecDeque<(usize, usize)>,
1129     }
1130 
1131     /// Mock of the `EFI_BOOT_SERVICE.GetMemoryMap` C API in test environment.
1132     ///
1133     /// # Safety
1134     ///
1135     ///   Caller should guarantee that `memory_map_size`, `map_key` and `desc_size` point to valid
1136     ///   memory locations.
get_memory_map( memory_map_size: *mut usize, memory_map: *mut EfiMemoryDescriptor, map_key: *mut usize, desc_size: *mut usize, _: *mut u32, ) -> EfiStatus1137     unsafe extern "C" fn get_memory_map(
1138         memory_map_size: *mut usize,
1139         memory_map: *mut EfiMemoryDescriptor,
1140         map_key: *mut usize,
1141         desc_size: *mut usize,
1142         _: *mut u32,
1143     ) -> EfiStatus {
1144         EFI_CALL_TRACES.with(|traces| {
1145             let trace = &mut traces.borrow_mut().get_memory_map_trace;
1146             trace.inputs.push_back((unsafe { *memory_map_size }, memory_map));
1147             // SAFETY: function safety docs require valid `memory_map_size`and `map_key`.
1148             unsafe { (*map_key, *memory_map_size) = trace.outputs.pop_front().unwrap() };
1149             // SAFETY: function safety docs require valid `desc_size`.
1150             unsafe { *desc_size = size_of::<EfiMemoryDescriptor>() };
1151             EFI_STATUS_SUCCESS
1152         })
1153     }
1154 
1155     /// EFI_BOOT_SERVICE.ExitBootServices.
1156     #[derive(Default)]
1157     pub struct ExitBootServicespTrace {
1158         // Capture `image_handle`, `map_key`
1159         pub inputs: VecDeque<(EfiHandle, usize)>,
1160     }
1161 
1162     /// Mock of the `EFI_BOOT_SERVICE.ExitBootServices` C API in test environment.
exit_boot_services(image_handle: EfiHandle, map_key: usize) -> EfiStatus1163     extern "C" fn exit_boot_services(image_handle: EfiHandle, map_key: usize) -> EfiStatus {
1164         EFI_CALL_TRACES.with(|traces| {
1165             let trace = &mut traces.borrow_mut().exit_boot_services_trace;
1166             trace.inputs.push_back((image_handle, map_key));
1167             EFI_STATUS_SUCCESS
1168         })
1169     }
1170 
1171     /// EFI_BOOT_SERVICE.CreateEvent.
1172     #[derive(Default)]
1173     pub struct CreateEventTrace {
1174         // Capture `type_`, `notify_tpl`, `notify_fn`, `notify_ctx`
1175         pub inputs: VecDeque<(u32, EfiTpl, EfiEventNotify, *mut core::ffi::c_void)>,
1176         // Output a EfiEvent.
1177         pub outputs: VecDeque<EfiEvent>,
1178     }
1179 
1180     /// Mock of the `EFI_BOOT_SERVICE.CreateEvent` C API in test environment.
1181     ///
1182     /// # Safety
1183     ///
1184     ///   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, ) -> EfiStatus1185     unsafe extern "C" fn create_event(
1186         type_: u32,
1187         notify_tpl: EfiTpl,
1188         notify_fn: EfiEventNotify,
1189         notify_ctx: *mut core::ffi::c_void,
1190         event: *mut EfiEvent,
1191     ) -> EfiStatus {
1192         EFI_CALL_TRACES.with(|traces| {
1193             let trace = &mut traces.borrow_mut().create_event_trace;
1194             trace.inputs.push_back((type_, notify_tpl, notify_fn, notify_ctx));
1195             // SAFETY: function safety docs require valid `event`.
1196             unsafe { *event = trace.outputs.pop_front().unwrap() };
1197             EFI_STATUS_SUCCESS
1198         })
1199     }
1200 
1201     /// EFI_BOOT_SERVICE.CloseEvent.
1202     #[derive(Default)]
1203     pub struct CloseEventTrace {
1204         // Capture `event`
1205         pub inputs: VecDeque<EfiEvent>,
1206     }
1207 
1208     /// Mock of the `EFI_BOOT_SERVICE.CloseEvent` C API in test environment.
close_event(event: EfiEvent) -> EfiStatus1209     extern "C" fn close_event(event: EfiEvent) -> EfiStatus {
1210         EFI_CALL_TRACES.with(|traces| {
1211             let trace = &mut traces.borrow_mut().close_event_trace;
1212             trace.inputs.push_back(event);
1213             EFI_STATUS_SUCCESS
1214         })
1215     }
1216 
1217     /// EFI_BOOT_SERVICE.CheckEvent.
1218     #[derive(Default)]
1219     pub struct CheckEventTrace {
1220         // EfiStatus for return.
1221         pub outputs: VecDeque<EfiStatus>,
1222     }
1223 
1224     /// Mock of the `EFI_BOOT_SERVICE.CheckEvent` C API in test environment.
check_event(_: EfiEvent) -> EfiStatus1225     extern "C" fn check_event(_: EfiEvent) -> EfiStatus {
1226         EFI_CALL_TRACES.with(|traces| {
1227             let trace = &mut traces.borrow_mut().check_event_trace;
1228             trace.outputs.pop_front().unwrap()
1229         })
1230     }
1231 
1232     /// EFI_BOOT_SERVICE.SetTimer.
1233     #[derive(Default)]
1234     pub struct SetTimerTrace {
1235         // Capture call params
1236         pub inputs: VecDeque<(EfiEvent, EfiTimerDelay, u64)>,
1237         // EfiStatus for return
1238         pub outputs: VecDeque<EfiStatus>,
1239     }
1240 
1241     /// Mock of the `EFI_BOOT_SERVICE.SetTimer` C API in test environment.
set_timer( event: EfiEvent, delay_type: EfiTimerDelay, duration: u64, ) -> EfiStatus1242     extern "C" fn set_timer(
1243         event: EfiEvent,
1244         delay_type: EfiTimerDelay,
1245         duration: u64,
1246     ) -> EfiStatus {
1247         EFI_CALL_TRACES.with(|trace| {
1248             let trace = &mut trace.borrow_mut().set_timer_trace;
1249             trace.inputs.push_back((event, delay_type, duration));
1250             trace.outputs.pop_front().unwrap()
1251         })
1252     }
1253 
1254     /// A test wrapper that sets up a system table, image handle and runs a test function like it
1255     /// is an EFI application.
1256     /// TODO(300168989): Investigate using procedural macro to generate test that auto calls this.
run_test(func: impl FnOnce(EfiHandle, *mut EfiSystemTable) -> ())1257     pub fn run_test(func: impl FnOnce(EfiHandle, *mut EfiSystemTable) -> ()) {
1258         // Reset all traces
1259         EFI_CALL_TRACES.with(|trace| {
1260             *trace.borrow_mut() = Default::default();
1261         });
1262 
1263         let mut systab: EfiSystemTable = Default::default();
1264         let mut boot_services: EfiBootService = Default::default();
1265 
1266         boot_services.free_pool = Some(free_pool);
1267         boot_services.open_protocol = Some(open_protocol);
1268         boot_services.close_protocol = Some(close_protocol);
1269         boot_services.locate_handle_buffer = Some(locate_handle_buffer);
1270         boot_services.get_memory_map = Some(get_memory_map);
1271         boot_services.exit_boot_services = Some(exit_boot_services);
1272         boot_services.create_event = Some(create_event);
1273         boot_services.close_event = Some(close_event);
1274         boot_services.check_event = Some(check_event);
1275         boot_services.set_timer = Some(set_timer);
1276         systab.boot_services = &mut boot_services as *mut _;
1277         let image_handle: usize = 1234; // Don't care.
1278 
1279         func(image_handle as EfiHandle, &mut systab as *mut _);
1280 
1281         // Reset all traces
1282         EFI_CALL_TRACES.with(|trace| {
1283             *trace.borrow_mut() = Default::default();
1284         });
1285     }
1286 
1287     /// Constructs a mock protocol `P` and run the given callback on it.
1288     ///
1289     /// This is similar to `run_test()`, but also provides the construction of a single mock
1290     /// protocol to reduce boilerplate for tests to check the interface between a C EFI protocol
1291     /// struct and our Rust wrappers.
1292     ///
1293     /// # Arguments
1294     /// * `c_interface`: the raw C struct interface implementing the desired protocol.
1295     /// * `f`: the callback function to run, given the resulting protocol as an argument.
run_test_with_mock_protocol<P: ProtocolInfo>( mut c_interface: P::InterfaceType, f: impl FnOnce(&Protocol<P>), )1296     pub fn run_test_with_mock_protocol<P: ProtocolInfo>(
1297         mut c_interface: P::InterfaceType,
1298         f: impl FnOnce(&Protocol<P>),
1299     ) {
1300         run_test(|image_handle, systab_ptr| {
1301             let efi_entry = EfiEntry { image_handle, systab_ptr };
1302             // SAFETY:
1303             // * `c_interface` is a valid C interface for proto `P`
1304             // * `c_interface` outlives the created `protocol`
1305             let protocol = unsafe {
1306                 Protocol::new(DeviceHandle::new(null_mut()), &mut c_interface, &efi_entry)
1307             };
1308             f(&protocol);
1309         });
1310     }
1311 
1312     /// Get the pointer to an object as an EfiHandle type.
as_efi_handle<T>(val: &mut T) -> EfiHandle1313     pub fn as_efi_handle<T>(val: &mut T) -> EfiHandle {
1314         val as *mut T as *mut _
1315     }
1316 
1317     #[test]
test_open_close_protocol()1318     fn test_open_close_protocol() {
1319         run_test(|image_handle, systab_ptr| {
1320             let efi_entry = EfiEntry { image_handle, systab_ptr };
1321 
1322             // Set up open_protocol trace
1323             let mut block_io: EfiBlockIoProtocol = Default::default();
1324             EFI_CALL_TRACES.with(|traces| {
1325                 traces.borrow_mut().open_protocol_trace.outputs =
1326                     VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_SUCCESS)]);
1327             });
1328 
1329             let mut device_handle: usize = 0; // Don't care
1330             {
1331                 // Open a protocol
1332                 let protocol = efi_entry
1333                     .system_table()
1334                     .boot_services()
1335                     .open_protocol::<BlockIoProtocol>(DeviceHandle(as_efi_handle(
1336                         &mut device_handle,
1337                     )))
1338                     .unwrap();
1339 
1340                 // Validate call args
1341                 EFI_CALL_TRACES.with(|trace| {
1342                     assert_eq!(
1343                         trace.borrow_mut().open_protocol_trace.inputs,
1344                         [(
1345                             DeviceHandle(as_efi_handle(&mut device_handle)),
1346                             BlockIoProtocol::GUID,
1347                             image_handle
1348                         ),]
1349                     );
1350 
1351                     // close_protocol not called yet.
1352                     assert_eq!(trace.borrow_mut().close_protocol_trace.inputs, []);
1353                 });
1354 
1355                 // The protocol gets the correct EfiBlockIoProtocol structure we pass in.
1356                 assert_eq!(protocol.interface_ptr(), &mut block_io as *mut _);
1357             }
1358 
1359             // Close protocol is called as `protocol` goes out of scope.
1360             EFI_CALL_TRACES
1361                 .with(|trace| assert_eq!(trace.borrow_mut().close_protocol_trace.inputs, []));
1362         })
1363     }
1364 
1365     #[test]
test_null_efi_method()1366     fn test_null_efi_method() {
1367         // Test that wrapper call fails if efi method is None.
1368         run_test(|image_handle, systab_ptr| {
1369             let efi_entry = EfiEntry { image_handle, systab_ptr };
1370 
1371             // Set up open_protocol trace
1372             let mut block_io: EfiBlockIoProtocol = Default::default();
1373             EFI_CALL_TRACES.with(|traces| {
1374                 traces.borrow_mut().open_protocol_trace.outputs =
1375                     VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_SUCCESS)]);
1376             });
1377 
1378             // Set the method to None.
1379             // SAFETY:
1380             // run_test() guarantees `boot_services` pointer points to valid object.
1381             unsafe { (*(*systab_ptr).boot_services).open_protocol = None };
1382 
1383             let mut device_handle: usize = 0; // Don't care
1384             assert!(efi_entry
1385                 .system_table()
1386                 .boot_services()
1387                 .open_protocol::<BlockIoProtocol>(DeviceHandle(as_efi_handle(&mut device_handle)))
1388                 .is_err());
1389         })
1390     }
1391 
1392     #[test]
test_error_efi_method()1393     fn test_error_efi_method() {
1394         // Test that wrapper call fails if efi method returns error.
1395         run_test(|image_handle, systab_ptr| {
1396             let efi_entry = EfiEntry { image_handle, systab_ptr };
1397 
1398             // Set up open_protocol trace.
1399             let mut block_io: EfiBlockIoProtocol = Default::default();
1400             EFI_CALL_TRACES.with(|traces| {
1401                 traces.borrow_mut().open_protocol_trace.outputs =
1402                     VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_NOT_FOUND)]);
1403             });
1404 
1405             let mut device_handle: usize = 0; // Don't care
1406             assert!(efi_entry
1407                 .system_table()
1408                 .boot_services()
1409                 .open_protocol::<BlockIoProtocol>(DeviceHandle(as_efi_handle(&mut device_handle)))
1410                 .is_err());
1411         })
1412     }
1413 
1414     #[test]
test_locate_handle_buffer_by_protocol()1415     fn test_locate_handle_buffer_by_protocol() {
1416         run_test(|image_handle, systab_ptr| {
1417             let efi_entry = EfiEntry { image_handle, systab_ptr };
1418 
1419             // Set up locate_handle_buffer_trace trace.
1420             let mut located_handles: [DeviceHandle; 3] =
1421                 [DeviceHandle(1 as *mut _), DeviceHandle(2 as *mut _), DeviceHandle(3 as *mut _)];
1422             EFI_CALL_TRACES.with(|traces| {
1423                 traces.borrow_mut().locate_handle_buffer_trace.outputs =
1424                     VecDeque::from([(located_handles.len(), located_handles.as_mut_ptr())]);
1425             });
1426 
1427             {
1428                 let handles = efi_entry
1429                     .system_table()
1430                     .boot_services()
1431                     .locate_handle_buffer_by_protocol::<BlockIoProtocol>()
1432                     .unwrap();
1433 
1434                 // Returned handles are expected.
1435                 assert_eq!(handles.handles().to_vec(), located_handles);
1436             }
1437 
1438             EFI_CALL_TRACES.with(|traces| {
1439                 let traces = traces.borrow_mut();
1440                 // Arguments are passed correctly.
1441                 assert_eq!(traces.locate_handle_buffer_trace.inputs, [BlockIoProtocol::GUID]);
1442                 // Free pool is called with the correct address.
1443                 assert_eq!(traces.free_pool_trace.inputs, [located_handles.as_mut_ptr() as *mut _]);
1444             });
1445         })
1446     }
1447 
1448     #[test]
test_find_first_and_open()1449     fn test_find_first_and_open() {
1450         run_test(|image_handle, systab_ptr| {
1451             let efi_entry = EfiEntry { image_handle, systab_ptr };
1452 
1453             // Set up locate_handle_buffer_trace trace.
1454             let mut located_handles: [DeviceHandle; 3] =
1455                 [DeviceHandle(1 as *mut _), DeviceHandle(2 as *mut _), DeviceHandle(3 as *mut _)];
1456             EFI_CALL_TRACES.with(|traces| {
1457                 traces.borrow_mut().locate_handle_buffer_trace.outputs =
1458                     VecDeque::from([(located_handles.len(), located_handles.as_mut_ptr())]);
1459             });
1460 
1461             // Set up open_protocol trace.
1462             let mut block_io: EfiBlockIoProtocol = Default::default();
1463             EFI_CALL_TRACES.with(|traces| {
1464                 traces.borrow_mut().open_protocol_trace.outputs =
1465                     VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_SUCCESS)]);
1466             });
1467 
1468             efi_entry
1469                 .system_table()
1470                 .boot_services()
1471                 .find_first_and_open::<BlockIoProtocol>()
1472                 .unwrap();
1473 
1474             // Check open_protocol is called on the first handle.
1475             EFI_CALL_TRACES.with(|traces| {
1476                 assert_eq!(
1477                     traces.borrow_mut().open_protocol_trace.inputs,
1478                     [(DeviceHandle(1 as *mut _), BlockIoProtocol::GUID, image_handle),]
1479                 );
1480             });
1481         })
1482     }
1483 
1484     #[test]
test_exit_boot_services()1485     fn test_exit_boot_services() {
1486         run_test(|image_handle, systab_ptr| {
1487             let efi_entry = EfiEntry { image_handle, systab_ptr };
1488             // Create a buffer large enough to hold two EfiMemoryDescriptor.
1489             let mut descriptors: [EfiMemoryDescriptor; 2] = [
1490                 EfiMemoryDescriptor {
1491                     memory_type: EFI_MEMORY_TYPE_LOADER_DATA,
1492                     padding: 0,
1493                     physical_start: 0,
1494                     virtual_start: 0,
1495                     number_of_pages: 0,
1496                     attributes: 0,
1497                 },
1498                 EfiMemoryDescriptor {
1499                     memory_type: EFI_MEMORY_TYPE_LOADER_CODE,
1500                     padding: 0,
1501                     physical_start: 0,
1502                     virtual_start: 0,
1503                     number_of_pages: 0,
1504                     attributes: 0,
1505                 },
1506             ];
1507             let map_key: usize = 12345;
1508             // Set up get_memory_map trace.
1509             EFI_CALL_TRACES.with(|traces| {
1510                 // Output only the first EfiMemoryDescriptor.
1511                 traces.borrow_mut().get_memory_map_trace.outputs =
1512                     VecDeque::from([(map_key, 1 * size_of::<EfiMemoryDescriptor>())]);
1513             });
1514 
1515             // SAFETY: Buffer is guaranteed valid.
1516             let buffer = unsafe {
1517                 from_raw_parts_mut(
1518                     descriptors.as_mut_ptr() as *mut u8,
1519                     descriptors.len() * size_of::<EfiMemoryDescriptor>(),
1520                 )
1521             };
1522 
1523             // Test `exit_boot_services`
1524             let desc = super::exit_boot_services(efi_entry, buffer).unwrap();
1525 
1526             // Validate that UEFI APIs are correctly called.
1527             EFI_CALL_TRACES.with(|traces| {
1528                 assert_eq!(
1529                     traces.borrow_mut().get_memory_map_trace.inputs,
1530                     [(
1531                         descriptors.len() * size_of::<EfiMemoryDescriptor>(),
1532                         descriptors.as_mut_ptr()
1533                     )]
1534                 );
1535 
1536                 assert_eq!(
1537                     traces.borrow_mut().exit_boot_services_trace.inputs,
1538                     [(image_handle, map_key)],
1539                 );
1540             });
1541 
1542             // Validate that the returned `EfiMemoryMap` contains only 1 EfiMemoryDescriptor.
1543             assert_eq!(desc.into_iter().map(|v| *v).collect::<Vec<_>>(), descriptors[..1].to_vec());
1544             // Validate that the returned `EfiMemoryMap` has the correct map_key.
1545             assert_eq!(desc.map_key(), map_key);
1546         })
1547     }
1548 
1549     #[test]
test_exit_boot_services_unaligned_buffer()1550     fn test_exit_boot_services_unaligned_buffer() {
1551         run_test(|image_handle, systab_ptr| {
1552             let efi_entry = EfiEntry { image_handle, systab_ptr };
1553             // Create a buffer for 2 EfiMemoryDescriptor.
1554             let descriptors: [EfiMemoryDescriptor; 2] = [
1555                 EfiMemoryDescriptor {
1556                     memory_type: EFI_MEMORY_TYPE_LOADER_DATA,
1557                     padding: 0,
1558                     physical_start: 0,
1559                     virtual_start: 0,
1560                     number_of_pages: 0,
1561                     attributes: 0,
1562                 },
1563                 EfiMemoryDescriptor {
1564                     memory_type: EFI_MEMORY_TYPE_LOADER_CODE,
1565                     padding: 0,
1566                     physical_start: 0,
1567                     virtual_start: 0,
1568                     number_of_pages: 0,
1569                     attributes: 0,
1570                 },
1571             ];
1572 
1573             let map_key: usize = 12345;
1574             // Set up get_memory_map trace.
1575             EFI_CALL_TRACES.with(|traces| {
1576                 traces.borrow_mut().get_memory_map_trace.outputs =
1577                     VecDeque::from([(map_key, 2 * size_of::<EfiMemoryDescriptor>())]);
1578             });
1579 
1580             // Construct the destination buffer.
1581             let mut buffer = [0u8; 256];
1582             let alignment = core::mem::align_of::<EfiMemoryDescriptor>();
1583             let size = core::mem::size_of::<EfiMemoryDescriptor>();
1584             let aligned = aligned_subslice(&mut buffer[..], alignment).unwrap();
1585             // Offset by 1 element so that we can make an unaligned buffer starting somewhere in
1586             // between.
1587             let start = aligned.get_mut(size..).unwrap();
1588             start[..size].clone_from_slice(descriptors[0].as_bytes());
1589             start[size..][..size].clone_from_slice(descriptors[1].as_bytes());
1590             // Pass an unaligned address.
1591             let desc = super::exit_boot_services(efi_entry, &mut aligned[size - 1..]).unwrap();
1592             // Validate that the returned `EfiMemoryMap` contains the correct EfiMemoryDescriptor.
1593             assert_eq!(desc.into_iter().map(|v| *v).collect::<Vec<_>>(), descriptors[..2].to_vec());
1594             // Validate that the returned `EfiMemoryMap` has the correct map_key.
1595             assert_eq!(desc.map_key(), map_key);
1596         });
1597     }
1598 
1599     #[test]
test_create_event_with_notify_fn()1600     fn test_create_event_with_notify_fn() {
1601         run_test(|image_handle, systab_ptr| {
1602             let efi_entry = EfiEntry { image_handle, systab_ptr };
1603             let mut cb_impl = |_: EfiEvent| {};
1604             let mut cb = EventNotify::new(Tpl::Callback, &mut cb_impl);
1605             let event: EfiEvent = 1234usize as _;
1606             EFI_CALL_TRACES.with(|traces| {
1607                 traces.borrow_mut().create_event_trace.outputs.push_back(event);
1608             });
1609             {
1610                 // SAFETY: event notifications are always safe in unittests.
1611                 let _ = unsafe {
1612                     efi_entry
1613                         .system_table()
1614                         .boot_services()
1615                         .create_event_with_notification(EventType::Timer, &mut cb)
1616                 }
1617                 .unwrap();
1618             }
1619             let efi_cb: EfiEventNotify = Some(efi_event_cb);
1620             EFI_CALL_TRACES.with(|traces| {
1621                 assert_eq!(
1622                     traces.borrow_mut().create_event_trace.inputs,
1623                     [(
1624                         EventType::Timer as _,
1625                         Tpl::Callback as _,
1626                         efi_cb,
1627                         &mut cb as *mut _ as *mut _
1628                     )]
1629                 )
1630             });
1631             // Verify close_event is called.
1632             EFI_CALL_TRACES
1633                 .with(|traces| assert_eq!(traces.borrow_mut().close_event_trace.inputs, [event]));
1634         });
1635     }
1636 
1637     #[test]
test_create_event_wo_notify_fn()1638     fn test_create_event_wo_notify_fn() {
1639         run_test(|image_handle, systab_ptr| {
1640             let efi_entry = EfiEntry { image_handle, systab_ptr };
1641             let event: EfiEvent = 1234usize as _;
1642             EFI_CALL_TRACES.with(|traces| {
1643                 traces.borrow_mut().create_event_trace.outputs.push_back(event);
1644             });
1645             {
1646                 let _ = efi_entry
1647                     .system_table()
1648                     .boot_services()
1649                     .create_event(EventType::Timer)
1650                     .unwrap();
1651             }
1652             EFI_CALL_TRACES.with(|traces| {
1653                 assert_eq!(
1654                     traces.borrow_mut().create_event_trace.inputs,
1655                     [(EventType::Timer as _, 0, None, null_mut())]
1656                 )
1657             });
1658         });
1659     }
1660 
1661     #[test]
test_check_event()1662     fn test_check_event() {
1663         run_test(|image_handle, systab_ptr| {
1664             let efi_entry = EfiEntry { image_handle, systab_ptr };
1665             let event: EfiEvent = 1234usize as _;
1666             EFI_CALL_TRACES.with(|traces| {
1667                 traces.borrow_mut().create_event_trace.outputs.push_back(event);
1668                 traces.borrow_mut().check_event_trace.outputs.push_back(EFI_STATUS_SUCCESS);
1669                 traces.borrow_mut().check_event_trace.outputs.push_back(EFI_STATUS_NOT_READY);
1670                 traces.borrow_mut().check_event_trace.outputs.push_back(EFI_STATUS_UNSUPPORTED);
1671             });
1672             let res =
1673                 efi_entry.system_table().boot_services().create_event(EventType::Timer).unwrap();
1674             assert_eq!(efi_entry.system_table().boot_services().check_event(&res), Ok(true));
1675             assert_eq!(efi_entry.system_table().boot_services().check_event(&res), Ok(false));
1676             assert!(efi_entry.system_table().boot_services().check_event(&res).is_err());
1677         });
1678     }
1679 
1680     #[test]
test_check_recurring_timer()1681     fn test_check_recurring_timer() {
1682         run_test(|image_handle, systab_ptr| {
1683             let efi_entry = EfiEntry { image_handle, systab_ptr };
1684             let event: EfiEvent = 666usize as _;
1685 
1686             EFI_CALL_TRACES.with(|traces| {
1687                 let mut t = traces.borrow_mut();
1688                 t.create_event_trace.outputs.push_back(event);
1689                 t.set_timer_trace.outputs.push_back(EFI_STATUS_SUCCESS);
1690                 t.check_event_trace.outputs.push_back(EFI_STATUS_SUCCESS);
1691             });
1692 
1693             let recurring_timer =
1694                 RecurringTimer::new(&efi_entry, Duration::from_nanos(2112)).unwrap();
1695 
1696             EFI_CALL_TRACES.with(|traces| {
1697                 let traces = traces.borrow();
1698                 assert_eq!(
1699                     traces.create_event_trace.inputs,
1700                     [(EventType::Timer as _, 0, None, null_mut())]
1701                 );
1702                 assert_eq!(
1703                     traces.set_timer_trace.inputs,
1704                     [(event, EFI_TIMER_DELAY_TIMER_PERIODIC, 21u64)]
1705                 );
1706                 // Make sure timer doesn't check itself automatically during construction.
1707                 assert_eq!(traces.check_event_trace.outputs, [EFI_STATUS_SUCCESS]);
1708             });
1709 
1710             assert_eq!(recurring_timer.check(), Ok(true));
1711         });
1712     }
1713 }
1714