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