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