1 //! UEFI boot services.
2 //!
3 //! These functions will panic if called after exiting boot services.
4 //!
5 //! # Accessing protocols
6 //!
7 //! Protocols can be opened using several functions in this module. Most
8 //! commonly, [`open_protocol_exclusive`] should be used. This ensures that
9 //! nothing else can use the protocol until it is closed, and returns a
10 //! [`ScopedProtocol`] that takes care of closing the protocol when it is
11 //! dropped.
12 //!
13 //! Other methods for opening protocols:
14 //!
15 //! * [`open_protocol`]
16 //! * [`get_image_file_system`]
17 //!
18 //! For protocol definitions, see the [`proto`] module.
19 //!
20 //! [`proto`]: crate::proto
21 
22 pub use uefi_raw::table::boot::{
23     EventType, MemoryAttribute, MemoryDescriptor, MemoryType, Tpl, PAGE_SIZE,
24 };
25 
26 use crate::data_types::PhysicalAddress;
27 use crate::mem::memory_map::{MemoryMapBackingMemory, MemoryMapKey, MemoryMapMeta, MemoryMapOwned};
28 use crate::polyfill::maybe_uninit_slice_assume_init_ref;
29 #[cfg(doc)]
30 use crate::proto::device_path::LoadedImageDevicePath;
31 use crate::proto::device_path::{DevicePath, FfiDevicePath};
32 use crate::proto::loaded_image::LoadedImage;
33 use crate::proto::media::fs::SimpleFileSystem;
34 use crate::proto::{BootPolicy, Protocol, ProtocolPointer};
35 use crate::runtime::{self, ResetType};
36 use crate::table::Revision;
37 use crate::util::opt_nonnull_to_ptr;
38 use crate::{table, Char16, Error, Event, Guid, Handle, Result, Status, StatusExt};
39 use core::ffi::c_void;
40 use core::mem::MaybeUninit;
41 use core::ops::{Deref, DerefMut};
42 use core::ptr::{self, NonNull};
43 use core::sync::atomic::{AtomicPtr, Ordering};
44 use core::{mem, slice};
45 use uefi_raw::table::boot::InterfaceType;
46 #[cfg(feature = "alloc")]
47 use {alloc::vec::Vec, uefi::ResultExt};
48 
49 /// Global image handle. This is only set by [`set_image_handle`], and it is
50 /// only read by [`image_handle`].
51 static IMAGE_HANDLE: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
52 
53 /// Get the [`Handle`] of the currently-executing image.
54 #[must_use]
image_handle() -> Handle55 pub fn image_handle() -> Handle {
56     let ptr = IMAGE_HANDLE.load(Ordering::Acquire);
57     // Safety: the image handle must be valid. We know it is, because it was set
58     // by `set_image_handle`, which has that same safety requirement.
59     unsafe { Handle::from_ptr(ptr) }.expect("set_image_handle has not been called")
60 }
61 
62 /// Update the global image [`Handle`].
63 ///
64 /// This is called automatically in the `main` entry point as part of
65 /// [`uefi::entry`]. It should not be called at any other point in time, unless
66 /// the executable does not use [`uefi::entry`], in which case it should be
67 /// called once before calling other boot services functions.
68 ///
69 /// # Safety
70 ///
71 /// This function should only be called as described above, and the
72 /// `image_handle` must be a valid image [`Handle`]. The safety guarantees of
73 /// [`open_protocol_exclusive`] rely on the global image handle being correct.
set_image_handle(image_handle: Handle)74 pub unsafe fn set_image_handle(image_handle: Handle) {
75     IMAGE_HANDLE.store(image_handle.as_ptr(), Ordering::Release);
76 }
77 
78 /// Return true if boot services are active, false otherwise.
are_boot_services_active() -> bool79 pub(crate) fn are_boot_services_active() -> bool {
80     let Some(st) = table::system_table_raw() else {
81         return false;
82     };
83 
84     // SAFETY: valid per requirements of `set_system_table`.
85     let st = unsafe { st.as_ref() };
86 
87     !st.boot_services.is_null()
88 }
89 
boot_services_raw_panicking() -> NonNull<uefi_raw::table::boot::BootServices>90 fn boot_services_raw_panicking() -> NonNull<uefi_raw::table::boot::BootServices> {
91     let st = table::system_table_raw_panicking();
92     // SAFETY: valid per requirements of `set_system_table`.
93     let st = unsafe { st.as_ref() };
94     NonNull::new(st.boot_services).expect("boot services are not active")
95 }
96 
97 /// Raises a task's priority level and returns a [`TplGuard`].
98 ///
99 /// The effect of calling `raise_tpl` with a `Tpl` that is below the current
100 /// one (which, sadly, cannot be queried) is undefined by the UEFI spec,
101 /// which also warns against remaining at high `Tpl`s for a long time.
102 ///
103 /// This function returns an RAII guard that will automatically restore the
104 /// original `Tpl` when dropped.
105 ///
106 /// # Safety
107 ///
108 /// Raising a task's priority level can affect other running tasks and
109 /// critical processes run by UEFI. The highest priority level is the
110 /// most dangerous, since it disables interrupts.
111 #[must_use]
raise_tpl(tpl: Tpl) -> TplGuard112 pub unsafe fn raise_tpl(tpl: Tpl) -> TplGuard {
113     let bt = boot_services_raw_panicking();
114     let bt = unsafe { bt.as_ref() };
115 
116     TplGuard {
117         old_tpl: (bt.raise_tpl)(tpl),
118     }
119 }
120 
121 /// Allocates memory pages from the system.
122 ///
123 /// UEFI OS loaders should allocate memory of the type `LoaderData`.
124 ///
125 /// # Errors
126 ///
127 /// * [`Status::OUT_OF_RESOURCES`]: allocation failed.
128 /// * [`Status::INVALID_PARAMETER`]: `mem_ty` is [`MemoryType::PERSISTENT_MEMORY`],
129 ///   [`MemoryType::UNACCEPTED`], or in the range [`MemoryType::MAX`]`..=0x6fff_ffff`.
130 /// * [`Status::NOT_FOUND`]: the requested pages could not be found.
allocate_pages(ty: AllocateType, mem_ty: MemoryType, count: usize) -> Result<NonNull<u8>>131 pub fn allocate_pages(ty: AllocateType, mem_ty: MemoryType, count: usize) -> Result<NonNull<u8>> {
132     let bt = boot_services_raw_panicking();
133     let bt = unsafe { bt.as_ref() };
134 
135     let (ty, mut addr) = match ty {
136         AllocateType::AnyPages => (0, 0),
137         AllocateType::MaxAddress(addr) => (1, addr),
138         AllocateType::Address(addr) => (2, addr),
139     };
140     let addr =
141         unsafe { (bt.allocate_pages)(ty, mem_ty, count, &mut addr) }.to_result_with_val(|| addr)?;
142     let ptr = addr as *mut u8;
143     Ok(NonNull::new(ptr).expect("allocate_pages must not return a null pointer if successful"))
144 }
145 
146 /// Frees memory pages allocated by [`allocate_pages`].
147 ///
148 /// # Safety
149 ///
150 /// The caller must ensure that no references into the allocation remain,
151 /// and that the memory at the allocation is not used after it is freed.
152 ///
153 /// # Errors
154 ///
155 /// * [`Status::NOT_FOUND`]: `ptr` was not allocated by [`allocate_pages`].
156 /// * [`Status::INVALID_PARAMETER`]: `ptr` is not page aligned or is otherwise invalid.
free_pages(ptr: NonNull<u8>, count: usize) -> Result157 pub unsafe fn free_pages(ptr: NonNull<u8>, count: usize) -> Result {
158     let bt = boot_services_raw_panicking();
159     let bt = unsafe { bt.as_ref() };
160 
161     let addr = ptr.as_ptr() as PhysicalAddress;
162     unsafe { (bt.free_pages)(addr, count) }.to_result()
163 }
164 
165 /// Allocates from a memory pool. The pointer will be 8-byte aligned.
166 ///
167 /// # Errors
168 ///
169 /// * [`Status::OUT_OF_RESOURCES`]: allocation failed.
170 /// * [`Status::INVALID_PARAMETER`]: `mem_ty` is [`MemoryType::PERSISTENT_MEMORY`],
171 ///   [`MemoryType::UNACCEPTED`], or in the range [`MemoryType::MAX`]`..=0x6fff_ffff`.
allocate_pool(mem_ty: MemoryType, size: usize) -> Result<NonNull<u8>>172 pub fn allocate_pool(mem_ty: MemoryType, size: usize) -> Result<NonNull<u8>> {
173     let bt = boot_services_raw_panicking();
174     let bt = unsafe { bt.as_ref() };
175 
176     let mut buffer = ptr::null_mut();
177     let ptr =
178         unsafe { (bt.allocate_pool)(mem_ty, size, &mut buffer) }.to_result_with_val(|| buffer)?;
179 
180     Ok(NonNull::new(ptr).expect("allocate_pool must not return a null pointer if successful"))
181 }
182 
183 /// Frees memory allocated by [`allocate_pool`].
184 ///
185 /// # Safety
186 ///
187 /// The caller must ensure that no references into the allocation remain,
188 /// and that the memory at the allocation is not used after it is freed.
189 ///
190 /// # Errors
191 ///
192 /// * [`Status::INVALID_PARAMETER`]: `ptr` is invalid.
free_pool(ptr: NonNull<u8>) -> Result193 pub unsafe fn free_pool(ptr: NonNull<u8>) -> Result {
194     let bt = boot_services_raw_panicking();
195     let bt = unsafe { bt.as_ref() };
196 
197     unsafe { (bt.free_pool)(ptr.as_ptr()) }.to_result()
198 }
199 
200 /// Queries the `get_memory_map` function of UEFI to retrieve the current
201 /// size of the map. Returns a [`MemoryMapMeta`].
202 ///
203 /// It is recommended to add a few more bytes for a subsequent allocation
204 /// for the memory map, as the memory map itself also needs heap memory,
205 /// and other allocations might occur before that call.
206 #[must_use]
memory_map_size() -> MemoryMapMeta207 pub(crate) fn memory_map_size() -> MemoryMapMeta {
208     let bt = boot_services_raw_panicking();
209     let bt = unsafe { bt.as_ref() };
210 
211     let mut map_size = 0;
212     let mut map_key = MemoryMapKey(0);
213     let mut desc_size = 0;
214     let mut desc_version = 0;
215 
216     let status = unsafe {
217         (bt.get_memory_map)(
218             &mut map_size,
219             ptr::null_mut(),
220             &mut map_key.0,
221             &mut desc_size,
222             &mut desc_version,
223         )
224     };
225     assert_eq!(status, Status::BUFFER_TOO_SMALL);
226 
227     assert_eq!(
228         map_size % desc_size,
229         0,
230         "Memory map must be a multiple of the reported descriptor size."
231     );
232 
233     let mmm = MemoryMapMeta {
234         desc_size,
235         map_size,
236         map_key,
237         desc_version,
238     };
239 
240     mmm.assert_sanity_checks();
241 
242     mmm
243 }
244 
245 /// Stores the current UEFI memory map in an UEFI-heap allocated buffer
246 /// and returns a [`MemoryMapOwned`].
247 ///
248 /// # Parameters
249 ///
250 /// - `mt`: The memory type for the backing memory on the UEFI heap.
251 ///   Usually, this is [`MemoryType::LOADER_DATA`]. You can also use a
252 ///   custom type.
253 ///
254 /// # Errors
255 ///
256 /// * [`Status::BUFFER_TOO_SMALL`]
257 /// * [`Status::INVALID_PARAMETER`]
memory_map(mt: MemoryType) -> Result<MemoryMapOwned>258 pub fn memory_map(mt: MemoryType) -> Result<MemoryMapOwned> {
259     let mut buffer = MemoryMapBackingMemory::new(mt)?;
260 
261     let meta = get_memory_map(buffer.as_mut_slice())?;
262 
263     Ok(MemoryMapOwned::from_initialized_mem(buffer, meta))
264 }
265 
266 /// Calls the underlying `GetMemoryMap` function of UEFI. On success,
267 /// the buffer is mutated and contains the map. The map might be shorter
268 /// than the buffer, which is reflected by the return value.
get_memory_map(buf: &mut [u8]) -> Result<MemoryMapMeta>269 pub(crate) fn get_memory_map(buf: &mut [u8]) -> Result<MemoryMapMeta> {
270     let bt = boot_services_raw_panicking();
271     let bt = unsafe { bt.as_ref() };
272 
273     let mut map_size = buf.len();
274     let map_buffer = buf.as_mut_ptr().cast::<MemoryDescriptor>();
275     let mut map_key = MemoryMapKey(0);
276     let mut desc_size = 0;
277     let mut desc_version = 0;
278 
279     assert_eq!(
280         (map_buffer as usize) % mem::align_of::<MemoryDescriptor>(),
281         0,
282         "Memory map buffers must be aligned like a MemoryDescriptor"
283     );
284 
285     unsafe {
286         (bt.get_memory_map)(
287             &mut map_size,
288             map_buffer,
289             &mut map_key.0,
290             &mut desc_size,
291             &mut desc_version,
292         )
293     }
294     .to_result_with_val(|| MemoryMapMeta {
295         map_size,
296         desc_size,
297         map_key,
298         desc_version,
299     })
300 }
301 
302 /// Creates an event.
303 ///
304 /// This function creates a new event of the specified type and returns it.
305 ///
306 /// Events are created in a "waiting" state, and may switch to a "signaled"
307 /// state. If the event type has flag `NotifySignal` set, this will result in
308 /// a callback for the event being immediately enqueued at the `notify_tpl`
309 /// priority level. If the event type has flag `NotifyWait`, the notification
310 /// will be delivered next time `wait_for_event` or `check_event` is called.
311 /// In both cases, a `notify_fn` callback must be specified.
312 ///
313 /// # Safety
314 ///
315 /// This function is unsafe because callbacks must handle exit from boot
316 /// services correctly.
317 ///
318 /// # Errors
319 ///
320 /// * [`Status::INVALID_PARAMETER`]: an invalid combination of parameters was provided.
321 /// * [`Status::OUT_OF_RESOURCES`]: the event could not be allocated.
create_event( event_ty: EventType, notify_tpl: Tpl, notify_fn: Option<EventNotifyFn>, notify_ctx: Option<NonNull<c_void>>, ) -> Result<Event>322 pub unsafe fn create_event(
323     event_ty: EventType,
324     notify_tpl: Tpl,
325     notify_fn: Option<EventNotifyFn>,
326     notify_ctx: Option<NonNull<c_void>>,
327 ) -> Result<Event> {
328     let bt = boot_services_raw_panicking();
329     let bt = unsafe { bt.as_ref() };
330 
331     let mut event = ptr::null_mut();
332 
333     // Safety: the argument types of the function pointers are defined
334     // differently, but are compatible and can be safely transmuted.
335     let notify_fn: Option<uefi_raw::table::boot::EventNotifyFn> = mem::transmute(notify_fn);
336 
337     let notify_ctx = opt_nonnull_to_ptr(notify_ctx);
338 
339     // Now we're ready to call UEFI
340     (bt.create_event)(event_ty, notify_tpl, notify_fn, notify_ctx, &mut event).to_result_with_val(
341         // OK to unwrap: event is non-null for Status::SUCCESS.
342         || Event::from_ptr(event).unwrap(),
343     )
344 }
345 
346 /// Creates an event in an event group.
347 ///
348 /// The event's notification function, context, and task priority are specified
349 /// by `notify_fn`, `notify_ctx`, and `notify_tpl`, respectively. The event will
350 /// be added to the group of events identified by `event_group`.
351 ///
352 /// If no group is specified by `event_group`, this function behaves as if the
353 /// same parameters had been passed to `create_event()`.
354 ///
355 /// Event groups are collections of events identified by a shared GUID where,
356 /// when one member event is signaled, all other events are signaled and their
357 /// individual notification actions are taken. All events are guaranteed to be
358 /// signaled before the first notification action is taken. All notification
359 /// functions will be executed in the order specified by their `Tpl`.
360 ///
361 /// An event can only be part of a single event group. An event may be removed
362 /// from an event group by calling [`close_event`].
363 ///
364 /// The [`EventType`] of an event uses the same values as `create_event()`, except that
365 /// `EventType::SIGNAL_EXIT_BOOT_SERVICES` and `EventType::SIGNAL_VIRTUAL_ADDRESS_CHANGE`
366 /// are not valid.
367 ///
368 /// For events of type `NOTIFY_SIGNAL` or `NOTIFY_WAIT`, `notify_fn` must be
369 /// `Some` and `notify_tpl` must be a valid task priority level. For other event
370 /// types these parameters are ignored.
371 ///
372 /// More than one event of type `EventType::TIMER` may be part of a single event
373 /// group. However, there is no mechanism for determining which of the timers
374 /// was signaled.
375 ///
376 /// This operation is only supported starting with UEFI 2.0; earlier versions
377 /// will fail with [`Status::UNSUPPORTED`].
378 ///
379 /// # Safety
380 ///
381 /// The caller must ensure they are passing a valid `Guid` as `event_group`, if applicable.
382 ///
383 /// # Errors
384 ///
385 /// * [`Status::INVALID_PARAMETER`]: an invalid combination of parameters was provided.
386 /// * [`Status::OUT_OF_RESOURCES`]: the event could not be allocated.
create_event_ex( event_type: EventType, notify_tpl: Tpl, notify_fn: Option<EventNotifyFn>, notify_ctx: Option<NonNull<c_void>>, event_group: Option<NonNull<Guid>>, ) -> Result<Event>387 pub unsafe fn create_event_ex(
388     event_type: EventType,
389     notify_tpl: Tpl,
390     notify_fn: Option<EventNotifyFn>,
391     notify_ctx: Option<NonNull<c_void>>,
392     event_group: Option<NonNull<Guid>>,
393 ) -> Result<Event> {
394     let bt = boot_services_raw_panicking();
395     let bt = unsafe { bt.as_ref() };
396 
397     if bt.header.revision < Revision::EFI_2_00 {
398         return Err(Status::UNSUPPORTED.into());
399     }
400 
401     let mut event = ptr::null_mut();
402 
403     // Safety: the argument types of the function pointers are defined
404     // differently, but are compatible and can be safely transmuted.
405     let notify_fn: Option<uefi_raw::table::boot::EventNotifyFn> = mem::transmute(notify_fn);
406 
407     (bt.create_event_ex)(
408         event_type,
409         notify_tpl,
410         notify_fn,
411         opt_nonnull_to_ptr(notify_ctx),
412         opt_nonnull_to_ptr(event_group),
413         &mut event,
414     )
415     .to_result_with_val(
416         // OK to unwrap: event is non-null for Status::SUCCESS.
417         || Event::from_ptr(event).unwrap(),
418     )
419 }
420 
421 /// Checks to see if an event is signaled, without blocking execution to wait for it.
422 ///
423 /// Returns `Ok(true)` if the event is in the signaled state or `Ok(false)`
424 /// if the event is not in the signaled state.
425 ///
426 /// # Errors
427 ///
428 /// Note: Instead of returning [`Status::NOT_READY`] as listed in the UEFI
429 /// Specification, this function will return `Ok(false)`.
430 ///
431 /// * [`Status::INVALID_PARAMETER`]: `event` is of type [`NOTIFY_SIGNAL`].
432 ///
433 /// [`NOTIFY_SIGNAL`]: EventType::NOTIFY_SIGNAL
check_event(event: Event) -> Result<bool>434 pub fn check_event(event: Event) -> Result<bool> {
435     let bt = boot_services_raw_panicking();
436     let bt = unsafe { bt.as_ref() };
437 
438     let status = unsafe { (bt.check_event)(event.as_ptr()) };
439     match status {
440         Status::SUCCESS => Ok(true),
441         Status::NOT_READY => Ok(false),
442         _ => Err(status.into()),
443     }
444 }
445 
446 /// Removes `event` from any event group to which it belongs and closes it.
447 ///
448 /// If `event` was registered with [`register_protocol_notify`], then the
449 /// corresponding registration will be removed. Calling this function within the
450 /// corresponding notify function is allowed.
451 ///
452 /// # Errors
453 ///
454 /// The specification does not list any errors, however implementations are
455 /// allowed to return an error if needed.
close_event(event: Event) -> Result456 pub fn close_event(event: Event) -> Result {
457     let bt = boot_services_raw_panicking();
458     let bt = unsafe { bt.as_ref() };
459 
460     unsafe { (bt.close_event)(event.as_ptr()) }.to_result()
461 }
462 
463 /// Sets the trigger for an event of type [`TIMER`].
464 ///
465 /// # Errors
466 ///
467 /// * [`Status::INVALID_PARAMETER`]: `event` is not valid.
468 ///
469 /// [`TIMER`]: EventType::TIMER
set_timer(event: &Event, trigger_time: TimerTrigger) -> Result470 pub fn set_timer(event: &Event, trigger_time: TimerTrigger) -> Result {
471     let bt = boot_services_raw_panicking();
472     let bt = unsafe { bt.as_ref() };
473 
474     let (ty, time) = match trigger_time {
475         TimerTrigger::Cancel => (0, 0),
476         TimerTrigger::Periodic(hundreds_ns) => (1, hundreds_ns),
477         TimerTrigger::Relative(hundreds_ns) => (2, hundreds_ns),
478     };
479     unsafe { (bt.set_timer)(event.as_ptr(), ty, time) }.to_result()
480 }
481 
482 /// Stops execution until an event is signaled.
483 ///
484 /// This function must be called at priority level [`Tpl::APPLICATION`].
485 ///
486 /// The input [`Event`] slice is repeatedly iterated from first to last until
487 /// an event is signaled or an error is detected. The following checks are
488 /// performed on each event:
489 ///
490 /// * If an event is of type [`NOTIFY_SIGNAL`], then a
491 ///   [`Status::INVALID_PARAMETER`] error is returned with the index of the
492 ///   event that caused the failure.
493 /// * If an event is in the signaled state, the signaled state is cleared
494 ///   and the index of the event that was signaled is returned.
495 /// * If an event is not in the signaled state but does have a notification
496 ///   function, the notification function is queued at the event's
497 ///   notification task priority level. If the execution of the event's
498 ///   notification function causes the event to be signaled, then the
499 ///   signaled state is cleared and the index of the event that was signaled
500 ///   is returned.
501 ///
502 /// To wait for a specified time, a timer event must be included in `events`.
503 ///
504 /// To check if an event is signaled without waiting, an already signaled
505 /// event can be used as the last event in the slice being checked, or the
506 /// [`check_event`] interface may be used.
507 ///
508 /// # Errors
509 ///
510 /// * [`Status::INVALID_PARAMETER`]: `events` is empty, or one of the events of
511 ///   of type [`NOTIFY_SIGNAL`].
512 /// * [`Status::UNSUPPORTED`]: the current TPL is not [`Tpl::APPLICATION`].
513 ///
514 /// [`NOTIFY_SIGNAL`]: EventType::NOTIFY_SIGNAL
wait_for_event(events: &mut [Event]) -> Result<usize, Option<usize>>515 pub fn wait_for_event(events: &mut [Event]) -> Result<usize, Option<usize>> {
516     let bt = boot_services_raw_panicking();
517     let bt = unsafe { bt.as_ref() };
518 
519     let number_of_events = events.len();
520     let events: *mut uefi_raw::Event = events.as_mut_ptr().cast();
521 
522     let mut index = 0;
523     unsafe { (bt.wait_for_event)(number_of_events, events, &mut index) }.to_result_with(
524         || index,
525         |s| {
526             if s == Status::INVALID_PARAMETER {
527                 Some(index)
528             } else {
529                 None
530             }
531         },
532     )
533 }
534 
535 /// Connect one or more drivers to a controller.
536 ///
537 /// Usually one disconnects and then reconnects certain drivers
538 /// to make them rescan some state that changed, e.g. reconnecting
539 /// a block handle after your app modified disk partitions.
540 ///
541 /// # Errors
542 ///
543 /// * [`Status::NOT_FOUND`]: there are no driver-binding protocol instances
544 ///   present in the system, or no drivers are connected to `controller`.
545 /// * [`Status::SECURITY_VIOLATION`]: the caller does not have permission to
546 ///   start drivers associated with `controller`.
connect_controller( controller: Handle, driver_image: Option<Handle>, remaining_device_path: Option<&DevicePath>, recursive: bool, ) -> Result547 pub fn connect_controller(
548     controller: Handle,
549     driver_image: Option<Handle>,
550     remaining_device_path: Option<&DevicePath>,
551     recursive: bool,
552 ) -> Result {
553     let bt = boot_services_raw_panicking();
554     let bt = unsafe { bt.as_ref() };
555 
556     unsafe {
557         (bt.connect_controller)(
558             controller.as_ptr(),
559             Handle::opt_to_ptr(driver_image),
560             remaining_device_path
561                 .map(|dp| dp.as_ffi_ptr())
562                 .unwrap_or(ptr::null())
563                 .cast(),
564             recursive,
565         )
566     }
567     .to_result_with_err(|_| ())
568 }
569 
570 /// Disconnect one or more drivers from a controller.
571 ///
572 /// See also [`connect_controller`].
573 ///
574 /// # Errors
575 ///
576 /// * [`Status::INVALID_PARAMETER`]: `driver_image` is set but does not manage
577 ///   `controller`, or does not support the driver binding protocol, or one of
578 ///   the handles is invalid.
579 /// * [`Status::OUT_OF_RESOURCES`]: not enough resources available to disconnect
580 ///   drivers.
581 /// * [`Status::DEVICE_ERROR`]: the controller could not be disconnected due to
582 ///   a device error.
disconnect_controller( controller: Handle, driver_image: Option<Handle>, child: Option<Handle>, ) -> Result583 pub fn disconnect_controller(
584     controller: Handle,
585     driver_image: Option<Handle>,
586     child: Option<Handle>,
587 ) -> Result {
588     let bt = boot_services_raw_panicking();
589     let bt = unsafe { bt.as_ref() };
590 
591     unsafe {
592         (bt.disconnect_controller)(
593             controller.as_ptr(),
594             Handle::opt_to_ptr(driver_image),
595             Handle::opt_to_ptr(child),
596         )
597     }
598     .to_result_with_err(|_| ())
599 }
600 
601 /// Installs a protocol interface on a device handle.
602 ///
603 /// When a protocol interface is installed, firmware will call all functions
604 /// that have registered to wait for that interface to be installed.
605 ///
606 /// If `handle` is `None`, a new handle will be created and returned.
607 ///
608 /// # Safety
609 ///
610 /// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`.
611 ///
612 /// # Errors
613 ///
614 /// * [`Status::OUT_OF_RESOURCES`]: failed to allocate a new handle.
615 /// * [`Status::INVALID_PARAMETER`]: this protocol is already installed on the handle.
install_protocol_interface( handle: Option<Handle>, protocol: &Guid, interface: *const c_void, ) -> Result<Handle>616 pub unsafe fn install_protocol_interface(
617     handle: Option<Handle>,
618     protocol: &Guid,
619     interface: *const c_void,
620 ) -> Result<Handle> {
621     let bt = boot_services_raw_panicking();
622     let bt = unsafe { bt.as_ref() };
623 
624     let mut handle = Handle::opt_to_ptr(handle);
625     ((bt.install_protocol_interface)(
626         &mut handle,
627         protocol,
628         InterfaceType::NATIVE_INTERFACE,
629         interface,
630     ))
631     .to_result_with_val(|| Handle::from_ptr(handle).unwrap())
632 }
633 
634 /// Reinstalls a protocol interface on a device handle. `old_interface` is replaced with `new_interface`.
635 /// These interfaces may be the same, in which case the registered protocol notifications occur for the handle
636 /// without replacing the interface.
637 ///
638 /// As with `install_protocol_interface`, any process that has registered to wait for the installation of
639 /// the interface is notified.
640 ///
641 /// # Safety
642 ///
643 /// The caller is responsible for ensuring that there are no references to the `old_interface` that is being
644 /// removed.
645 ///
646 /// # Errors
647 ///
648 /// * [`Status::NOT_FOUND`]: the old interface was not found on the handle.
649 /// * [`Status::ACCESS_DENIED`]: the old interface is still in use and cannot be uninstalled.
reinstall_protocol_interface( handle: Handle, protocol: &Guid, old_interface: *const c_void, new_interface: *const c_void, ) -> Result<()>650 pub unsafe fn reinstall_protocol_interface(
651     handle: Handle,
652     protocol: &Guid,
653     old_interface: *const c_void,
654     new_interface: *const c_void,
655 ) -> Result<()> {
656     let bt = boot_services_raw_panicking();
657     let bt = unsafe { bt.as_ref() };
658 
659     (bt.reinstall_protocol_interface)(handle.as_ptr(), protocol, old_interface, new_interface)
660         .to_result()
661 }
662 
663 /// Removes a protocol interface from a device handle.
664 ///
665 /// # Safety
666 ///
667 /// The caller is responsible for ensuring that there are no references to a protocol interface
668 /// that has been removed. Some protocols may not be able to be removed as there is no information
669 /// available regarding the references. This includes Console I/O, Block I/O, Disk I/o, and handles
670 /// to device protocols.
671 ///
672 /// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`.
673 ///
674 /// # Errors
675 ///
676 /// * [`Status::NOT_FOUND`]: the interface was not found on the handle.
677 /// * [`Status::ACCESS_DENIED`]: the interface is still in use and cannot be uninstalled.
uninstall_protocol_interface( handle: Handle, protocol: &Guid, interface: *const c_void, ) -> Result<()>678 pub unsafe fn uninstall_protocol_interface(
679     handle: Handle,
680     protocol: &Guid,
681     interface: *const c_void,
682 ) -> Result<()> {
683     let bt = boot_services_raw_panicking();
684     let bt = unsafe { bt.as_ref() };
685 
686     (bt.uninstall_protocol_interface)(handle.as_ptr(), protocol, interface).to_result()
687 }
688 
689 /// Registers `event` to be signaled whenever a protocol interface is registered for
690 /// `protocol` by [`install_protocol_interface`] or [`reinstall_protocol_interface`].
691 ///
692 /// If successful, a [`SearchType::ByRegisterNotify`] is returned. This can be
693 /// used with [`locate_handle`] or [`locate_handle_buffer`] to identify the
694 /// newly (re)installed handles that support `protocol`.
695 ///
696 /// Events can be unregistered from protocol interface notification by calling [`close_event`].
697 ///
698 /// # Errors
699 ///
700 /// * [`Status::OUT_OF_RESOURCES`]: the event could not be allocated.
register_protocol_notify( protocol: &'static Guid, event: &Event, ) -> Result<SearchType<'static>>701 pub fn register_protocol_notify(
702     protocol: &'static Guid,
703     event: &Event,
704 ) -> Result<SearchType<'static>> {
705     let bt = boot_services_raw_panicking();
706     let bt = unsafe { bt.as_ref() };
707 
708     let mut key = ptr::null();
709     unsafe { (bt.register_protocol_notify)(protocol, event.as_ptr(), &mut key) }.to_result_with_val(
710         || {
711             // OK to unwrap: key is non-null for Status::SUCCESS.
712             SearchType::ByRegisterNotify(ProtocolSearchKey(NonNull::new(key.cast_mut()).unwrap()))
713         },
714     )
715 }
716 
717 /// Get the list of protocol interface [`Guids`][Guid] that are installed
718 /// on a [`Handle`].
719 ///
720 /// # Errors
721 ///
722 /// * [`Status::INVALID_PARAMETER`]: `handle` is invalid.
723 /// * [`Status::OUT_OF_RESOURCES`]: out of memory.
protocols_per_handle(handle: Handle) -> Result<ProtocolsPerHandle>724 pub fn protocols_per_handle(handle: Handle) -> Result<ProtocolsPerHandle> {
725     let bt = boot_services_raw_panicking();
726     let bt = unsafe { bt.as_ref() };
727 
728     let mut protocols = ptr::null_mut();
729     let mut count = 0;
730 
731     unsafe { (bt.protocols_per_handle)(handle.as_ptr(), &mut protocols, &mut count) }
732         .to_result_with_val(|| ProtocolsPerHandle {
733             count,
734             protocols: NonNull::new(protocols)
735                 .expect("protocols_per_handle must not return a null pointer"),
736         })
737 }
738 
739 /// Locates the handle of a device on the device path that supports the specified protocol.
740 ///
741 /// The `device_path` is updated to point at the remaining part of the [`DevicePath`] after
742 /// the part that matched the protocol. For example, it can be used with a device path
743 /// that contains a file path to strip off the file system portion of the device path,
744 /// leaving the file path and handle to the file system driver needed to access the file.
745 ///
746 /// If the first node of `device_path` matches the protocol, the `device_path`
747 /// is advanced to the device path terminator node. If `device_path` is a
748 /// multi-instance device path, the function will operate on the first instance.
749 ///
750 /// # Errors
751 ///
752 /// * [`Status::NOT_FOUND`]: no matching handles.
locate_device_path<P: ProtocolPointer + ?Sized>( device_path: &mut &DevicePath, ) -> Result<Handle>753 pub fn locate_device_path<P: ProtocolPointer + ?Sized>(
754     device_path: &mut &DevicePath,
755 ) -> Result<Handle> {
756     let bt = boot_services_raw_panicking();
757     let bt = unsafe { bt.as_ref() };
758 
759     let mut handle = ptr::null_mut();
760     let mut device_path_ptr: *const uefi_raw::protocol::device_path::DevicePathProtocol =
761         device_path.as_ffi_ptr().cast();
762     unsafe {
763         (bt.locate_device_path)(&P::GUID, &mut device_path_ptr, &mut handle).to_result_with_val(
764             || {
765                 *device_path = DevicePath::from_ffi_ptr(device_path_ptr.cast());
766                 // OK to unwrap: handle is non-null for Status::SUCCESS.
767                 Handle::from_ptr(handle).unwrap()
768             },
769         )
770     }
771 }
772 
773 /// Enumerates all handles installed on the system which match a certain query.
774 ///
775 /// # Errors
776 ///
777 /// * [`Status::NOT_FOUND`]: no matching handles found.
778 /// * [`Status::BUFFER_TOO_SMALL`]: the buffer is not large enough. The required
779 ///   size (in number of handles, not bytes) will be returned in the error data.
locate_handle<'buf>( search_ty: SearchType, buffer: &'buf mut [MaybeUninit<Handle>], ) -> Result<&'buf [Handle], Option<usize>>780 pub fn locate_handle<'buf>(
781     search_ty: SearchType,
782     buffer: &'buf mut [MaybeUninit<Handle>],
783 ) -> Result<&'buf [Handle], Option<usize>> {
784     let bt = boot_services_raw_panicking();
785     let bt = unsafe { bt.as_ref() };
786 
787     // Obtain the needed data from the parameters.
788     let (ty, guid, key) = match search_ty {
789         SearchType::AllHandles => (0, ptr::null(), ptr::null()),
790         SearchType::ByRegisterNotify(registration) => {
791             (1, ptr::null(), registration.0.as_ptr().cast_const())
792         }
793         SearchType::ByProtocol(guid) => (2, guid as *const Guid, ptr::null()),
794     };
795 
796     let mut buffer_size = buffer.len() * mem::size_of::<Handle>();
797     let status =
798         unsafe { (bt.locate_handle)(ty, guid, key, &mut buffer_size, buffer.as_mut_ptr().cast()) };
799 
800     let num_handles = buffer_size / mem::size_of::<Handle>();
801 
802     match status {
803         Status::SUCCESS => {
804             let buffer = &buffer[..num_handles];
805             // SAFETY: the entries up to `num_handles` have been initialized.
806             let handles = unsafe { maybe_uninit_slice_assume_init_ref(buffer) };
807             Ok(handles)
808         }
809         Status::BUFFER_TOO_SMALL => Err(Error::new(status, Some(num_handles))),
810         _ => Err(Error::new(status, None)),
811     }
812 }
813 
814 /// Returns an array of handles that support the requested protocol in a
815 /// pool-allocated buffer.
816 ///
817 /// See [`SearchType`] for details of the available search operations.
818 ///
819 /// # Errors
820 ///
821 /// * [`Status::NOT_FOUND`]: no matching handles.
822 /// * [`Status::OUT_OF_RESOURCES`]: out of memory.
locate_handle_buffer(search_ty: SearchType) -> Result<HandleBuffer>823 pub fn locate_handle_buffer(search_ty: SearchType) -> Result<HandleBuffer> {
824     let bt = boot_services_raw_panicking();
825     let bt = unsafe { bt.as_ref() };
826 
827     let (ty, guid, key) = match search_ty {
828         SearchType::AllHandles => (0, ptr::null(), ptr::null()),
829         SearchType::ByRegisterNotify(registration) => {
830             (1, ptr::null(), registration.0.as_ptr().cast_const())
831         }
832         SearchType::ByProtocol(guid) => (2, guid as *const _, ptr::null()),
833     };
834 
835     let mut num_handles: usize = 0;
836     let mut buffer: *mut uefi_raw::Handle = ptr::null_mut();
837     unsafe { (bt.locate_handle_buffer)(ty, guid, key, &mut num_handles, &mut buffer) }
838         .to_result_with_val(|| HandleBuffer {
839             count: num_handles,
840             buffer: NonNull::new(buffer.cast())
841                 .expect("locate_handle_buffer must not return a null pointer"),
842         })
843 }
844 
845 /// Returns all the handles implementing a certain protocol.
846 ///
847 /// # Errors
848 ///
849 /// * [`Status::NOT_FOUND`]: no matching handles.
850 #[cfg(feature = "alloc")]
find_handles<P: ProtocolPointer + ?Sized>() -> Result<Vec<Handle>>851 pub fn find_handles<P: ProtocolPointer + ?Sized>() -> Result<Vec<Handle>> {
852     // Search by protocol.
853     let search_type = SearchType::from_proto::<P>();
854 
855     // Determine how much we need to allocate.
856     let num_handles = match locate_handle(search_type, &mut []) {
857         Err(err) => {
858             if err.status() == Status::BUFFER_TOO_SMALL {
859                 err.data().expect("error data is missing")
860             } else {
861                 return Err(err.to_err_without_payload());
862             }
863         }
864         // This should never happen: if no handles match the search then a
865         // `NOT_FOUND` error should be returned.
866         Ok(_) => panic!("locate_handle should not return success with empty buffer"),
867     };
868 
869     // Allocate a large enough buffer without pointless initialization.
870     let mut handles = Vec::with_capacity(num_handles);
871 
872     // Perform the search.
873     let num_handles = locate_handle(search_type, handles.spare_capacity_mut())
874         .discard_errdata()?
875         .len();
876 
877     // Mark the returned number of elements as initialized.
878     unsafe {
879         handles.set_len(num_handles);
880     }
881 
882     // Emit output, with warnings
883     Ok(handles)
884 }
885 
886 /// Find an arbitrary handle that supports a particular [`Protocol`]. Returns
887 /// [`NOT_FOUND`] if no handles support the protocol.
888 ///
889 /// This method is a convenient wrapper around [`locate_handle_buffer`] for
890 /// getting just one handle. This is useful when you don't care which handle the
891 /// protocol is opened on. For example, [`DevicePathToText`] isn't tied to a
892 /// particular device, so only a single handle is expected to exist.
893 ///
894 /// [`NOT_FOUND`]: Status::NOT_FOUND
895 /// [`DevicePathToText`]: uefi::proto::device_path::text::DevicePathToText
896 ///
897 /// # Example
898 ///
899 /// ```
900 /// use uefi::proto::device_path::text::DevicePathToText;
901 /// use uefi::{boot, Handle};
902 /// # use uefi::Result;
903 ///
904 /// # fn get_fake_val<T>() -> T { todo!() }
905 /// # fn test() -> Result {
906 /// # let image_handle: Handle = get_fake_val();
907 /// let handle = boot::get_handle_for_protocol::<DevicePathToText>()?;
908 /// let device_path_to_text = boot::open_protocol_exclusive::<DevicePathToText>(handle)?;
909 /// # Ok(())
910 /// # }
911 /// ```
912 ///
913 /// # Errors
914 ///
915 /// * [`Status::NOT_FOUND`]: no matching handle.
916 /// * [`Status::OUT_OF_RESOURCES`]: out of memory.
get_handle_for_protocol<P: ProtocolPointer + ?Sized>() -> Result<Handle>917 pub fn get_handle_for_protocol<P: ProtocolPointer + ?Sized>() -> Result<Handle> {
918     locate_handle_buffer(SearchType::ByProtocol(&P::GUID))?
919         .first()
920         .cloned()
921         .ok_or_else(|| Status::NOT_FOUND.into())
922 }
923 
924 /// Opens a protocol interface for a handle.
925 ///
926 /// See also [`open_protocol_exclusive`], which provides a safe subset of this
927 /// functionality.
928 ///
929 /// This function attempts to get the protocol implementation of a handle, based
930 /// on the [protocol GUID].
931 ///
932 /// See [`OpenProtocolParams`] and [`OpenProtocolAttributes`] for details of the
933 /// input parameters.
934 ///
935 /// If successful, a [`ScopedProtocol`] is returned that will automatically
936 /// close the protocol interface when dropped.
937 ///
938 /// [protocol GUID]: uefi::data_types::Identify::GUID
939 ///
940 /// # Safety
941 ///
942 /// This function is unsafe because it can be used to open a protocol in ways
943 /// that don't get tracked by the UEFI implementation. This could allow the
944 /// protocol to be removed from a handle, or for the handle to be deleted
945 /// entirely, while a reference to the protocol is still active. The caller is
946 /// responsible for ensuring that the handle and protocol remain valid until the
947 /// `ScopedProtocol` is dropped.
948 ///
949 /// # Errors
950 ///
951 /// * [`Status::INVALID_PARAMETER`]: an invalid combination of `params` and
952 ///   `attributes` was provided.
953 /// * [`Status::UNSUPPORTED`]: the handle does not support the protocol.
954 /// * [`Status::ACCESS_DENIED`] or [`Status::ALREADY_STARTED`]: the protocol is
955 ///   already open in a way that is incompatible with the new request.
open_protocol<P: ProtocolPointer + ?Sized>( params: OpenProtocolParams, attributes: OpenProtocolAttributes, ) -> Result<ScopedProtocol<P>>956 pub unsafe fn open_protocol<P: ProtocolPointer + ?Sized>(
957     params: OpenProtocolParams,
958     attributes: OpenProtocolAttributes,
959 ) -> Result<ScopedProtocol<P>> {
960     let bt = boot_services_raw_panicking();
961     let bt = unsafe { bt.as_ref() };
962 
963     let mut interface = ptr::null_mut();
964     (bt.open_protocol)(
965         params.handle.as_ptr(),
966         &P::GUID,
967         &mut interface,
968         params.agent.as_ptr(),
969         Handle::opt_to_ptr(params.controller),
970         attributes as u32,
971     )
972     .to_result_with_val(|| {
973         let interface = if interface.is_null() {
974             None
975         } else {
976             NonNull::new(P::mut_ptr_from_ffi(interface))
977         };
978         ScopedProtocol {
979             interface,
980             open_params: params,
981         }
982     })
983 }
984 
985 /// Opens a protocol interface for a handle in exclusive mode.
986 ///
987 /// If successful, a [`ScopedProtocol`] is returned that will automatically
988 /// close the protocol interface when dropped.
989 ///
990 /// # Errors
991 ///
992 /// * [`Status::UNSUPPORTED`]: the handle does not support the protocol.
993 /// * [`Status::ACCESS_DENIED`]: the protocol is already open in a way that is
994 ///   incompatible with the new request.
open_protocol_exclusive<P: ProtocolPointer + ?Sized>( handle: Handle, ) -> Result<ScopedProtocol<P>>995 pub fn open_protocol_exclusive<P: ProtocolPointer + ?Sized>(
996     handle: Handle,
997 ) -> Result<ScopedProtocol<P>> {
998     // Safety: opening in exclusive mode with the correct agent
999     // handle set ensures that the protocol cannot be modified or
1000     // removed while it is open, so this usage is safe.
1001     unsafe {
1002         open_protocol::<P>(
1003             OpenProtocolParams {
1004                 handle,
1005                 agent: image_handle(),
1006                 controller: None,
1007             },
1008             OpenProtocolAttributes::Exclusive,
1009         )
1010     }
1011 }
1012 
1013 /// Tests whether a handle supports a protocol.
1014 ///
1015 /// Returns `Ok(true)` if the handle supports the protocol, `Ok(false)` if not.
1016 ///
1017 /// # Errors
1018 ///
1019 /// * [`Status::INVALID_PARAMETER`]: one of the handles in `params` is invalid.
test_protocol<P: ProtocolPointer + ?Sized>(params: OpenProtocolParams) -> Result<bool>1020 pub fn test_protocol<P: ProtocolPointer + ?Sized>(params: OpenProtocolParams) -> Result<bool> {
1021     const TEST_PROTOCOL: u32 = 0x04;
1022 
1023     let bt = boot_services_raw_panicking();
1024     let bt = unsafe { bt.as_ref() };
1025 
1026     let mut interface = ptr::null_mut();
1027     let status = unsafe {
1028         (bt.open_protocol)(
1029             params.handle.as_ptr(),
1030             &P::GUID,
1031             &mut interface,
1032             params.agent.as_ptr(),
1033             Handle::opt_to_ptr(params.controller),
1034             TEST_PROTOCOL,
1035         )
1036     };
1037 
1038     match status {
1039         Status::SUCCESS => Ok(true),
1040         Status::UNSUPPORTED => Ok(false),
1041         _ => Err(Error::from(status)),
1042     }
1043 }
1044 
1045 /// Loads a UEFI image into memory and return a [`Handle`] to the image.
1046 ///
1047 /// There are two ways to load the image: by copying raw image data
1048 /// from a source buffer, or by loading the image via the
1049 /// [`SimpleFileSystem`] protocol. See [`LoadImageSource`] for more
1050 /// details of the `source` parameter.
1051 ///
1052 /// The `parent_image_handle` is used to initialize the
1053 /// `parent_handle` field of the [`LoadedImage`] protocol for the
1054 /// image.
1055 ///
1056 /// If the image is successfully loaded, a [`Handle`] supporting the
1057 /// [`LoadedImage`] and [`LoadedImageDevicePath`] protocols is returned. The
1058 /// image can be started with [`start_image`] and unloaded with
1059 /// [`unload_image`].
1060 ///
1061 /// # Errors
1062 ///
1063 /// * [`Status::INVALID_PARAMETER`]: `source` contains an invalid value.
1064 /// * [`Status::UNSUPPORTED`]: the image type is not supported.
1065 /// * [`Status::OUT_OF_RESOURCES`]: insufficient resources to load the image.
1066 /// * [`Status::LOAD_ERROR`]: the image is invalid.
1067 /// * [`Status::DEVICE_ERROR`]: failed to load image due to a read error.
1068 /// * [`Status::ACCESS_DENIED`]: failed to load image due to a security policy.
1069 /// * [`Status::SECURITY_VIOLATION`]: a security policy specifies that the image
1070 ///   should not be started.
load_image(parent_image_handle: Handle, source: LoadImageSource) -> Result<Handle>1071 pub fn load_image(parent_image_handle: Handle, source: LoadImageSource) -> Result<Handle> {
1072     let bt = boot_services_raw_panicking();
1073     let bt = unsafe { bt.as_ref() };
1074 
1075     let (boot_policy, device_path, source_buffer, source_size) = source.to_ffi_params();
1076 
1077     let mut image_handle = ptr::null_mut();
1078     unsafe {
1079         (bt.load_image)(
1080             boot_policy.into(),
1081             parent_image_handle.as_ptr(),
1082             device_path.cast(),
1083             source_buffer,
1084             source_size,
1085             &mut image_handle,
1086         )
1087         .to_result_with_val(
1088             // OK to unwrap: image handle is non-null for Status::SUCCESS.
1089             || Handle::from_ptr(image_handle).unwrap(),
1090         )
1091     }
1092 }
1093 
1094 /// Unloads a UEFI image.
1095 ///
1096 /// # Errors
1097 ///
1098 /// * [`Status::UNSUPPORTED`]: the image has been started, and does not support unload.
1099 /// * [`Status::INVALID_PARAMETER`]: `image_handle` is not valid.
unload_image(image_handle: Handle) -> Result1100 pub fn unload_image(image_handle: Handle) -> Result {
1101     let bt = boot_services_raw_panicking();
1102     let bt = unsafe { bt.as_ref() };
1103 
1104     unsafe { (bt.unload_image)(image_handle.as_ptr()) }.to_result()
1105 }
1106 
1107 /// Transfers control to a loaded image's entry point.
1108 ///
1109 /// # Errors
1110 ///
1111 /// * [`Status::INVALID_PARAMETER`]: `image_handle` is not valid, or the image
1112 ///   has already been initialized with `start_image`.
1113 /// * [`Status::SECURITY_VIOLATION`]: a security policy specifies that the image
1114 ///   should not be started.
start_image(image_handle: Handle) -> Result1115 pub fn start_image(image_handle: Handle) -> Result {
1116     let bt = boot_services_raw_panicking();
1117     let bt = unsafe { bt.as_ref() };
1118 
1119     // TODO: implement returning exit data to the caller.
1120     let mut exit_data_size: usize = 0;
1121     let mut exit_data: *mut u16 = ptr::null_mut();
1122 
1123     unsafe {
1124         (bt.start_image)(image_handle.as_ptr(), &mut exit_data_size, &mut exit_data).to_result()
1125     }
1126 }
1127 
1128 /// Exits the UEFI application and returns control to the UEFI component
1129 /// that started the UEFI application.
1130 ///
1131 /// # Safety
1132 ///
1133 /// The caller must ensure that resources owned by the application are properly
1134 /// cleaned up.
1135 ///
1136 /// Note that event callbacks installed by the application are not automatically
1137 /// uninstalled. If such a callback is invoked after exiting the application,
1138 /// the function's code may no longer be loaded in memory, leading to a crash or
1139 /// other unexpected behavior.
exit( image_handle: Handle, exit_status: Status, exit_data_size: usize, exit_data: *mut Char16, ) -> !1140 pub unsafe fn exit(
1141     image_handle: Handle,
1142     exit_status: Status,
1143     exit_data_size: usize,
1144     exit_data: *mut Char16,
1145 ) -> ! {
1146     let bt = boot_services_raw_panicking();
1147     let bt = unsafe { bt.as_ref() };
1148 
1149     (bt.exit)(
1150         image_handle.as_ptr(),
1151         exit_status,
1152         exit_data_size,
1153         exit_data.cast(),
1154     )
1155 }
1156 
1157 /// Get the current memory map and exit boot services.
get_memory_map_and_exit_boot_services(buf: &mut [u8]) -> Result<MemoryMapMeta>1158 unsafe fn get_memory_map_and_exit_boot_services(buf: &mut [u8]) -> Result<MemoryMapMeta> {
1159     let bt = boot_services_raw_panicking();
1160     let bt = unsafe { bt.as_ref() };
1161 
1162     // Get the memory map.
1163     let memory_map = get_memory_map(buf)?;
1164 
1165     // Try to exit boot services using the memory map key. Note that after
1166     // the first call to `exit_boot_services`, there are restrictions on
1167     // what boot services functions can be called. In UEFI 2.8 and earlier,
1168     // only `get_memory_map` and `exit_boot_services` are allowed. Starting
1169     // in UEFI 2.9 other memory allocation functions may also be called.
1170     (bt.exit_boot_services)(image_handle().as_ptr(), memory_map.map_key.0)
1171         .to_result_with_val(|| memory_map)
1172 }
1173 
1174 /// Exit UEFI boot services.
1175 ///
1176 /// After this function completes, UEFI hands over control of the hardware
1177 /// to the executing OS loader, which implies that the UEFI boot services
1178 /// are shut down and cannot be used anymore. Only UEFI configuration tables
1179 /// and run-time services can be used.
1180 ///
1181 /// The memory map at the time of exiting boot services returned. The map is
1182 /// backed by a pool allocation of the given `memory_type`. Since the boot
1183 /// services function to free that memory is no longer available after calling
1184 /// `exit_boot_services`, the allocation will not be freed on drop.
1185 ///
1186 /// Note that once the boot services are exited, associated loggers and
1187 /// allocators can't use the boot services anymore. For the corresponding
1188 /// abstractions provided by this crate (see the [`helpers`] module),
1189 /// invoking this function will automatically disable them. If the
1190 /// `global_allocator` feature is enabled, attempting to use the allocator
1191 /// after exiting boot services will panic.
1192 ///
1193 /// # Safety
1194 ///
1195 /// The caller is responsible for ensuring that no references to
1196 /// boot-services data remain. A non-exhaustive list of resources to check:
1197 ///
1198 /// * All protocols will be invalid after exiting boot services. This
1199 ///   includes the [`Output`] protocols attached to stdout/stderr. The
1200 ///   caller must ensure that no protocol references remain.
1201 /// * The pool allocator is not usable after exiting boot services. Types
1202 ///   such as [`PoolString`] which call [`free_pool`] on drop
1203 ///   must be cleaned up before calling `exit_boot_services`, or leaked to
1204 ///   avoid drop ever being called.
1205 /// * All data in the memory map marked as
1206 ///   [`MemoryType::BOOT_SERVICES_CODE`] and
1207 ///   [`MemoryType::BOOT_SERVICES_DATA`] will become free memory.
1208 ///
1209 /// # Errors
1210 ///
1211 /// This function will fail if it is unable to allocate memory for
1212 /// the memory map, if it fails to retrieve the memory map, or if
1213 /// exiting boot services fails (with up to one retry).
1214 ///
1215 /// All errors are treated as unrecoverable because the system is
1216 /// now in an undefined state. Rather than returning control to the
1217 /// caller, the system will be reset.
1218 ///
1219 /// [`helpers`]: crate::helpers
1220 /// [`Output`]: crate::proto::console::text::Output
1221 /// [`PoolString`]: crate::proto::device_path::text::PoolString
1222 #[must_use]
exit_boot_services(memory_type: MemoryType) -> MemoryMapOwned1223 pub unsafe fn exit_boot_services(memory_type: MemoryType) -> MemoryMapOwned {
1224     crate::helpers::exit();
1225 
1226     let mut buf = MemoryMapBackingMemory::new(memory_type).expect("Failed to allocate memory");
1227 
1228     // Calling `exit_boot_services` can fail if the memory map key is not
1229     // current. Retry a second time if that occurs. This matches the
1230     // behavior of the Linux kernel:
1231     // https://github.com/torvalds/linux/blob/e544a0743/drivers/firmware/efi/libstub/efi-stub-helper.c#L375
1232     let mut status = Status::ABORTED;
1233     for _ in 0..2 {
1234         match unsafe { get_memory_map_and_exit_boot_services(buf.as_mut_slice()) } {
1235             Ok(memory_map) => {
1236                 return MemoryMapOwned::from_initialized_mem(buf, memory_map);
1237             }
1238             Err(err) => {
1239                 log::error!("Error retrieving the memory map for exiting the boot services");
1240                 status = err.status()
1241             }
1242         }
1243     }
1244 
1245     // Failed to exit boot services.
1246     log::warn!("Resetting the machine");
1247     runtime::reset(ResetType::COLD, status, None);
1248 }
1249 
1250 /// Adds, updates, or removes a configuration table entry
1251 /// from the EFI System Table.
1252 ///
1253 /// # Safety
1254 ///
1255 /// When installing or updating a configuration table, the data pointed to by
1256 /// `table_ptr` must be a pool allocation of type
1257 /// [`RUNTIME_SERVICES_DATA`]. Once this table has been installed, the caller
1258 /// should not modify or free the data.
1259 ///
1260 /// [`RUNTIME_SERVICES_DATA`]: MemoryType::RUNTIME_SERVICES_DATA
1261 ///
1262 /// # Errors
1263 ///
1264 /// * [`Status::NOT_FOUND`]: tried to delete a nonexistent entry.
1265 /// * [`Status::OUT_OF_RESOURCES`]: out of memory.
install_configuration_table( guid_entry: &'static Guid, table_ptr: *const c_void, ) -> Result1266 pub unsafe fn install_configuration_table(
1267     guid_entry: &'static Guid,
1268     table_ptr: *const c_void,
1269 ) -> Result {
1270     let bt = boot_services_raw_panicking();
1271     let bt = unsafe { bt.as_ref() };
1272 
1273     (bt.install_configuration_table)(guid_entry, table_ptr).to_result()
1274 }
1275 
1276 /// Sets the watchdog timer.
1277 ///
1278 /// UEFI will start a 5-minute countdown after an UEFI image is loaded.  The
1279 /// image must either successfully load an OS and exit boot services in that
1280 /// time, or disable the watchdog.
1281 ///
1282 /// Otherwise, the firmware will log the event using the provided numeric
1283 /// code and data, then reset the system.
1284 ///
1285 /// This function allows you to change the watchdog timer's timeout to a
1286 /// certain amount of seconds or to disable the watchdog entirely. It also
1287 /// allows you to change what will be logged when the timer expires.
1288 ///
1289 /// The watchdog codes from 0 to 0xffff (65535) are reserved for internal
1290 /// firmware use. Higher values can be used freely by applications.
1291 ///
1292 /// If provided, the watchdog data must be a null-terminated string optionally
1293 /// followed by other binary data.
1294 ///
1295 /// # Errors
1296 ///
1297 /// * [`Status::INVALID_PARAMETER`]: `watchdog_code` is invalid.
1298 /// * [`Status::UNSUPPORTED`]: the system does not have a watchdog timer.
1299 /// * [`Status::DEVICE_ERROR`]: the watchdog timer could not be set due to a
1300 ///   hardware error.
set_watchdog_timer( timeout_in_seconds: usize, watchdog_code: u64, data: Option<&mut [u16]>, ) -> Result1301 pub fn set_watchdog_timer(
1302     timeout_in_seconds: usize,
1303     watchdog_code: u64,
1304     data: Option<&mut [u16]>,
1305 ) -> Result {
1306     let bt = boot_services_raw_panicking();
1307     let bt = unsafe { bt.as_ref() };
1308 
1309     let (data_len, data) = data
1310         .map(|d| {
1311             assert!(
1312                 d.contains(&0),
1313                 "Watchdog data must start with a null-terminated string"
1314             );
1315             (d.len(), d.as_mut_ptr())
1316         })
1317         .unwrap_or((0, ptr::null_mut()));
1318 
1319     unsafe { (bt.set_watchdog_timer)(timeout_in_seconds, watchdog_code, data_len, data) }
1320         .to_result()
1321 }
1322 
1323 /// Stalls execution for the given number of microseconds.
stall(microseconds: usize)1324 pub fn stall(microseconds: usize) {
1325     let bt = boot_services_raw_panicking();
1326     let bt = unsafe { bt.as_ref() };
1327 
1328     unsafe {
1329         // No error conditions are defined in the spec for this function, so
1330         // ignore the status.
1331         let _ = (bt.stall)(microseconds);
1332     }
1333 }
1334 
1335 /// Retrieves a [`SimpleFileSystem`] protocol associated with the device the given
1336 /// image was loaded from.
1337 ///
1338 /// # Errors
1339 ///
1340 /// This function can return errors from [`open_protocol_exclusive`] and
1341 /// [`locate_device_path`]. See those functions for more details.
1342 ///
1343 /// * [`Status::INVALID_PARAMETER`]
1344 /// * [`Status::UNSUPPORTED`]
1345 /// * [`Status::ACCESS_DENIED`]
1346 /// * [`Status::ALREADY_STARTED`]
1347 /// * [`Status::NOT_FOUND`]
get_image_file_system(image_handle: Handle) -> Result<ScopedProtocol<SimpleFileSystem>>1348 pub fn get_image_file_system(image_handle: Handle) -> Result<ScopedProtocol<SimpleFileSystem>> {
1349     let loaded_image = open_protocol_exclusive::<LoadedImage>(image_handle)?;
1350 
1351     let device_handle = loaded_image
1352         .device()
1353         .ok_or(Error::new(Status::UNSUPPORTED, ()))?;
1354     let device_path = open_protocol_exclusive::<DevicePath>(device_handle)?;
1355 
1356     let device_handle = locate_device_path::<SimpleFileSystem>(&mut &*device_path)?;
1357 
1358     open_protocol_exclusive(device_handle)
1359 }
1360 
1361 /// Protocol interface [`Guids`][Guid] that are installed on a [`Handle`] as
1362 /// returned by [`protocols_per_handle`].
1363 #[derive(Debug)]
1364 pub struct ProtocolsPerHandle {
1365     protocols: NonNull<*const Guid>,
1366     count: usize,
1367 }
1368 
1369 impl Drop for ProtocolsPerHandle {
drop(&mut self)1370     fn drop(&mut self) {
1371         let _ = unsafe { free_pool(self.protocols.cast::<u8>()) };
1372     }
1373 }
1374 
1375 impl Deref for ProtocolsPerHandle {
1376     type Target = [&'static Guid];
1377 
deref(&self) -> &Self::Target1378     fn deref(&self) -> &Self::Target {
1379         let ptr: *const &'static Guid = self.protocols.as_ptr().cast();
1380 
1381         // SAFETY:
1382         //
1383         // * The firmware is assumed to provide a correctly-aligned pointer and
1384         //   array length.
1385         // * The firmware is assumed to provide valid GUID pointers.
1386         // * Protocol GUIDs should be constants or statics, so a 'static
1387         //   lifetime (of the individual pointers, not the overall slice) can be
1388         //   assumed.
1389         unsafe { slice::from_raw_parts(ptr, self.count) }
1390     }
1391 }
1392 
1393 /// A buffer returned by [`locate_handle_buffer`] that contains an array of
1394 /// [`Handle`]s that support the requested protocol.
1395 #[derive(Debug, Eq, PartialEq)]
1396 pub struct HandleBuffer {
1397     count: usize,
1398     buffer: NonNull<Handle>,
1399 }
1400 
1401 impl Drop for HandleBuffer {
drop(&mut self)1402     fn drop(&mut self) {
1403         let _ = unsafe { free_pool(self.buffer.cast::<u8>()) };
1404     }
1405 }
1406 
1407 impl Deref for HandleBuffer {
1408     type Target = [Handle];
1409 
deref(&self) -> &Self::Target1410     fn deref(&self) -> &Self::Target {
1411         unsafe { slice::from_raw_parts(self.buffer.as_ptr(), self.count) }
1412     }
1413 }
1414 
1415 /// An open protocol interface. Automatically closes the protocol
1416 /// interface on drop.
1417 ///
1418 /// Most protocols have interface data associated with them. `ScopedProtocol`
1419 /// implements [`Deref`] and [`DerefMut`] to access this data. A few protocols
1420 /// (such as [`DevicePath`] and [`LoadedImageDevicePath`]) may be installed with
1421 /// null interface data, in which case [`Deref`] and [`DerefMut`] will
1422 /// panic. The [`get`] and [`get_mut`] methods may be used to access the
1423 /// optional interface data without panicking.
1424 ///
1425 /// [`DevicePath`]: crate::proto::device_path::DevicePath
1426 /// [`LoadedImageDevicePath`]: crate::proto::device_path::LoadedImageDevicePath
1427 /// [`get`]: ScopedProtocol::get
1428 /// [`get_mut`]: ScopedProtocol::get_mut
1429 #[derive(Debug)]
1430 pub struct ScopedProtocol<P: Protocol + ?Sized> {
1431     /// The protocol interface.
1432     interface: Option<NonNull<P>>,
1433     open_params: OpenProtocolParams,
1434 }
1435 
1436 impl<P: Protocol + ?Sized> Drop for ScopedProtocol<P> {
drop(&mut self)1437     fn drop(&mut self) {
1438         let bt = boot_services_raw_panicking();
1439         let bt = unsafe { bt.as_ref() };
1440 
1441         let status = unsafe {
1442             (bt.close_protocol)(
1443                 self.open_params.handle.as_ptr(),
1444                 &P::GUID,
1445                 self.open_params.agent.as_ptr(),
1446                 Handle::opt_to_ptr(self.open_params.controller),
1447             )
1448         };
1449         // All of the error cases for close_protocol boil down to
1450         // calling it with a different set of parameters than what was
1451         // passed to open_protocol. The public API prevents such errors,
1452         // and the error can't be propagated out of drop anyway, so just
1453         // assert success.
1454         assert_eq!(status, Status::SUCCESS);
1455     }
1456 }
1457 
1458 impl<P: Protocol + ?Sized> Deref for ScopedProtocol<P> {
1459     type Target = P;
1460 
1461     #[track_caller]
deref(&self) -> &Self::Target1462     fn deref(&self) -> &Self::Target {
1463         unsafe { self.interface.unwrap().as_ref() }
1464     }
1465 }
1466 
1467 impl<P: Protocol + ?Sized> DerefMut for ScopedProtocol<P> {
1468     #[track_caller]
deref_mut(&mut self) -> &mut Self::Target1469     fn deref_mut(&mut self) -> &mut Self::Target {
1470         unsafe { self.interface.unwrap().as_mut() }
1471     }
1472 }
1473 
1474 impl<P: Protocol + ?Sized> ScopedProtocol<P> {
1475     /// Get the protocol interface data, or `None` if the open protocol's
1476     /// interface is null.
1477     #[must_use]
get(&self) -> Option<&P>1478     pub fn get(&self) -> Option<&P> {
1479         self.interface.map(|p| unsafe { p.as_ref() })
1480     }
1481 
1482     /// Get the protocol interface data, or `None` if the open protocol's
1483     /// interface is null.
1484     #[must_use]
get_mut(&mut self) -> Option<&mut P>1485     pub fn get_mut(&mut self) -> Option<&mut P> {
1486         self.interface.map(|mut p| unsafe { p.as_mut() })
1487     }
1488 }
1489 
1490 /// RAII guard for task priority level changes.
1491 ///
1492 /// Will automatically restore the former task priority level when dropped.
1493 #[derive(Debug)]
1494 pub struct TplGuard {
1495     old_tpl: Tpl,
1496 }
1497 
1498 impl Drop for TplGuard {
drop(&mut self)1499     fn drop(&mut self) {
1500         let bt = boot_services_raw_panicking();
1501         let bt = unsafe { bt.as_ref() };
1502 
1503         unsafe {
1504             (bt.restore_tpl)(self.old_tpl);
1505         }
1506     }
1507 }
1508 
1509 // OpenProtocolAttributes is safe to model as a regular enum because it
1510 // is only used as an input. The attributes are bitflags, but all valid
1511 // combinations are listed in the spec and only ByDriver and Exclusive
1512 // can actually be combined.
1513 //
1514 // Some values intentionally excluded:
1515 //
1516 // ByHandleProtocol (0x01) excluded because it is only intended to be
1517 // used in an implementation of `HandleProtocol`.
1518 //
1519 // TestProtocol (0x04) excluded because it doesn't actually open the
1520 // protocol, just tests if it's present on the handle. Since that
1521 // changes the interface significantly, that's exposed as a separate
1522 // method: `test_protocol`.
1523 
1524 /// Attributes for [`open_protocol`].
1525 #[repr(u32)]
1526 #[derive(Debug)]
1527 pub enum OpenProtocolAttributes {
1528     /// Used by drivers to get a protocol interface for a handle. The
1529     /// driver will not be informed if the interface is uninstalled or
1530     /// reinstalled.
1531     GetProtocol = 0x02,
1532 
1533     /// Used by bus drivers to show that a protocol is being used by one
1534     /// of the child controllers of the bus.
1535     ByChildController = 0x08,
1536 
1537     /// Used by a driver to gain access to a protocol interface. When
1538     /// this mode is used, the driver's `Stop` function will be called
1539     /// if the protocol interface is reinstalled or uninstalled. Once a
1540     /// protocol interface is opened with this attribute, no other
1541     /// drivers will be allowed to open the same protocol interface with
1542     /// the `ByDriver` attribute.
1543     ByDriver = 0x10,
1544 
1545     /// Used by a driver to gain exclusive access to a protocol
1546     /// interface. If any other drivers have the protocol interface
1547     /// opened with an attribute of `ByDriver`, then an attempt will be
1548     /// made to remove them with `DisconnectController`.
1549     ByDriverExclusive = 0x30,
1550 
1551     /// Used by applications to gain exclusive access to a protocol
1552     /// interface. If any drivers have the protocol opened with an
1553     /// attribute of `ByDriver`, then an attempt will be made to remove
1554     /// them by calling the driver's `Stop` function.
1555     Exclusive = 0x20,
1556 }
1557 
1558 /// Parameters passed to [`open_protocol`].
1559 #[derive(Debug)]
1560 pub struct OpenProtocolParams {
1561     /// The handle for the protocol to open.
1562     pub handle: Handle,
1563 
1564     /// The handle of the calling agent. For drivers, this is the handle
1565     /// containing the `EFI_DRIVER_BINDING_PROTOCOL` instance. For
1566     /// applications, this is the image handle.
1567     pub agent: Handle,
1568 
1569     /// For drivers, this is the controller handle that requires the
1570     /// protocol interface. For applications this should be set to
1571     /// `None`.
1572     pub controller: Option<Handle>,
1573 }
1574 
1575 /// Used as a parameter of [`load_image`] to provide the image source.
1576 #[derive(Debug)]
1577 pub enum LoadImageSource<'a> {
1578     /// Load an image from a buffer. The data will be copied from the
1579     /// buffer, so the input reference doesn't need to remain valid
1580     /// after the image is loaded.
1581     FromBuffer {
1582         /// Raw image data.
1583         buffer: &'a [u8],
1584 
1585         /// If set, this path will be added as the file path of the
1586         /// loaded image. This is not required to load the image, but
1587         /// may be used by the image itself to load other resources
1588         /// relative to the image's path.
1589         file_path: Option<&'a DevicePath>,
1590     },
1591 
1592     /// Load an image via the [`SimpleFileSystem`] protocol. If there is
1593     /// no instance of that protocol associated with the path then the
1594     /// behavior depends on [`BootPolicy`]. If [`BootPolicy::BootSelection`],
1595     /// attempt to load via the [`LoadFile`] protocol. If
1596     /// [`BootPolicy::ExactMatch`], attempt to load via the [`LoadFile2`]
1597     /// protocol, then fall back to [`LoadFile`].
1598     ///
1599     /// [`LoadFile`]: crate::proto::media::load_file::LoadFile
1600     /// [`LoadFile2`]: crate::proto::media::load_file::LoadFile2
1601     FromDevicePath {
1602         /// The full device path from which to load the image.
1603         ///
1604         /// The provided path should be a full device path and not just the
1605         /// file path portion of it. So for example, it must be (the binary
1606         /// representation)
1607         /// `PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/\\EFI\\BOOT\\BOOTX64.EFI`
1608         /// and not just `\\EFI\\BOOT\\BOOTX64.EFI`.
1609         device_path: &'a DevicePath,
1610 
1611         /// The [`BootPolicy`] to use.
1612         boot_policy: BootPolicy,
1613     },
1614 }
1615 
1616 impl<'a> LoadImageSource<'a> {
1617     /// Returns the raw FFI parameters for `load_image`.
1618     #[must_use]
to_ffi_params( &self, ) -> ( BootPolicy, *const FfiDevicePath, *const u8, usize, )1619     pub(crate) fn to_ffi_params(
1620         &self,
1621     ) -> (
1622         BootPolicy,
1623         *const FfiDevicePath,
1624         *const u8, /* buffer */
1625         usize,     /* buffer length */
1626     ) {
1627         let boot_policy;
1628         let device_path;
1629         let source_buffer;
1630         let source_size;
1631         match self {
1632             LoadImageSource::FromBuffer { buffer, file_path } => {
1633                 // Boot policy is ignored when loading from source buffer.
1634                 boot_policy = BootPolicy::default();
1635 
1636                 device_path = file_path.map(|p| p.as_ffi_ptr()).unwrap_or(ptr::null());
1637                 source_buffer = buffer.as_ptr();
1638                 source_size = buffer.len();
1639             }
1640             LoadImageSource::FromDevicePath {
1641                 device_path: d_path,
1642                 boot_policy: b_policy,
1643             } => {
1644                 boot_policy = *b_policy;
1645                 device_path = d_path.as_ffi_ptr();
1646                 source_buffer = ptr::null();
1647                 source_size = 0;
1648             }
1649         };
1650         (boot_policy, device_path, source_buffer, source_size)
1651     }
1652 }
1653 
1654 /// Type of allocation to perform.
1655 #[derive(Debug, Copy, Clone)]
1656 pub enum AllocateType {
1657     /// Allocate any possible pages.
1658     AnyPages,
1659     /// Allocate pages at any address below the given address.
1660     MaxAddress(PhysicalAddress),
1661     /// Allocate pages at the specified address.
1662     Address(PhysicalAddress),
1663 }
1664 
1665 /// The type of handle search to perform.
1666 #[derive(Debug, Copy, Clone)]
1667 pub enum SearchType<'guid> {
1668     /// Return all handles present on the system.
1669     AllHandles,
1670     /// Returns all handles supporting a certain protocol, specified by its GUID.
1671     ///
1672     /// If the protocol implements the `Protocol` interface,
1673     /// you can use the `from_proto` function to construct a new `SearchType`.
1674     ByProtocol(&'guid Guid),
1675     /// Return all handles that implement a protocol when an interface for that protocol
1676     /// is (re)installed.
1677     ByRegisterNotify(ProtocolSearchKey),
1678 }
1679 
1680 impl<'guid> SearchType<'guid> {
1681     /// Constructs a new search type for a specified protocol.
1682     #[must_use]
from_proto<P: ProtocolPointer + ?Sized>() -> Self1683     pub const fn from_proto<P: ProtocolPointer + ?Sized>() -> Self {
1684         SearchType::ByProtocol(&P::GUID)
1685     }
1686 }
1687 
1688 /// Event notification callback type.
1689 pub type EventNotifyFn = unsafe extern "efiapi" fn(event: Event, context: Option<NonNull<c_void>>);
1690 
1691 /// Timer events manipulation.
1692 #[derive(Debug)]
1693 pub enum TimerTrigger {
1694     /// Cancel event's timer
1695     Cancel,
1696     /// The event is to be signaled periodically.
1697     /// Parameter is the period in 100ns units.
1698     /// Delay of 0 will be signalled on every timer tick.
1699     Periodic(u64),
1700     /// The event is to be signaled once in 100ns units.
1701     /// Parameter is the delay in 100ns units.
1702     /// Delay of 0 will be signalled on next timer tick.
1703     Relative(u64),
1704 }
1705 
1706 /// Opaque pointer returned by [`register_protocol_notify`] to be used
1707 /// with [`locate_handle`] via [`SearchType::ByRegisterNotify`].
1708 #[derive(Debug, Clone, Copy)]
1709 #[repr(transparent)]
1710 pub struct ProtocolSearchKey(pub(crate) NonNull<c_void>);
1711