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