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