Skip to content

Commit 058066f

Browse files
authored
Merge pull request #1321 from nicholasbishop/bishop-locate-handle
boot: Add freestanding locate_handle and find_handles
2 parents a7d321b + 15b7418 commit 058066f

File tree

2 files changed

+95
-3
lines changed

2 files changed

+95
-3
lines changed

uefi-test-runner/src/boot/mod.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ pub fn test(st: &SystemTable<Boot>) {
1515
info!("Testing boot services");
1616
memory::test(bt);
1717
misc::test(st);
18-
test_locate_handle_buffer(bt);
18+
test_locate_handles(bt);
1919
test_load_image(bt);
2020
}
2121

22-
fn test_locate_handle_buffer(bt: &BootServices) {
23-
info!("Testing the `locate_handle_buffer` function");
22+
fn test_locate_handles(bt: &BootServices) {
23+
info!("Testing the `locate_handle_buffer`/`find_handles` functions");
2424

2525
{
2626
// search all handles
@@ -51,6 +51,11 @@ fn test_locate_handle_buffer(bt: &BootServices) {
5151
*handles,
5252
*boot::locate_handle_buffer(SearchType::ByProtocol(&Output::GUID)).unwrap()
5353
);
54+
55+
// Compare with `boot::find_handles`. This implicitly tests
56+
// `boot::locate_handle` as well.
57+
let handles_vec = boot::find_handles::<Output>().unwrap();
58+
assert_eq!(*handles, handles_vec);
5459
}
5560
}
5661

uefi/src/boot.rs

+87
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@
44
55
use crate::data_types::PhysicalAddress;
66
use crate::mem::memory_map::{MemoryMapBackingMemory, MemoryMapKey, MemoryMapMeta, MemoryMapOwned};
7+
use crate::polyfill::maybe_uninit_slice_assume_init_ref;
78
use crate::proto::device_path::DevicePath;
89
use crate::proto::{Protocol, ProtocolPointer};
910
use crate::table::Revision;
1011
use crate::util::opt_nonnull_to_ptr;
1112
use core::ffi::c_void;
13+
use core::mem::MaybeUninit;
1214
use core::ops::{Deref, DerefMut};
1315
use core::ptr::{self, NonNull};
1416
use core::sync::atomic::{AtomicPtr, Ordering};
1517
use core::{mem, slice};
1618
use uefi::{table, Char16, Error, Event, Guid, Handle, Result, Status, StatusExt};
1719
use uefi_raw::table::boot::InterfaceType;
1820

21+
#[cfg(feature = "alloc")]
22+
use {alloc::vec::Vec, uefi::ResultExt};
23+
1924
#[cfg(doc)]
2025
use {
2126
crate::proto::device_path::LoadedImageDevicePath, crate::proto::loaded_image::LoadedImage,
@@ -667,6 +672,47 @@ pub fn locate_device_path<P: ProtocolPointer + ?Sized>(
667672
}
668673
}
669674

675+
/// Enumerates all handles installed on the system which match a certain query.
676+
///
677+
/// # Errors
678+
///
679+
/// * [`Status::NOT_FOUND`]: no matching handles found.
680+
/// * [`Status::BUFFER_TOO_SMALL`]: the buffer is not large enough. The required
681+
/// size (in number of handles, not bytes) will be returned in the error data.
682+
pub fn locate_handle<'buf>(
683+
search_ty: SearchType,
684+
buffer: &'buf mut [MaybeUninit<Handle>],
685+
) -> Result<&'buf [Handle], Option<usize>> {
686+
let bt = boot_services_raw_panicking();
687+
let bt = unsafe { bt.as_ref() };
688+
689+
// Obtain the needed data from the parameters.
690+
let (ty, guid, key) = match search_ty {
691+
SearchType::AllHandles => (0, ptr::null(), ptr::null()),
692+
SearchType::ByRegisterNotify(registration) => {
693+
(1, ptr::null(), registration.0.as_ptr().cast_const())
694+
}
695+
SearchType::ByProtocol(guid) => (2, guid as *const Guid, ptr::null()),
696+
};
697+
698+
let mut buffer_size = buffer.len() * mem::size_of::<Handle>();
699+
let status =
700+
unsafe { (bt.locate_handle)(ty, guid, key, &mut buffer_size, buffer.as_mut_ptr().cast()) };
701+
702+
let num_handles = buffer_size / mem::size_of::<Handle>();
703+
704+
match status {
705+
Status::SUCCESS => {
706+
let buffer = &buffer[..num_handles];
707+
// SAFETY: the entries up to `num_handles` have been initialized.
708+
let handles = unsafe { maybe_uninit_slice_assume_init_ref(buffer) };
709+
Ok(handles)
710+
}
711+
Status::BUFFER_TOO_SMALL => Err(Error::new(status, Some(num_handles))),
712+
_ => Err(Error::new(status, None)),
713+
}
714+
}
715+
670716
/// Returns an array of handles that support the requested protocol in a
671717
/// pool-allocated buffer.
672718
///
@@ -698,6 +744,47 @@ pub fn locate_handle_buffer(search_ty: SearchType) -> Result<HandleBuffer> {
698744
})
699745
}
700746

747+
/// Returns all the handles implementing a certain protocol.
748+
///
749+
/// # Errors
750+
///
751+
/// * [`Status::NOT_FOUND`]: no matching handles.
752+
#[cfg(feature = "alloc")]
753+
pub fn find_handles<P: ProtocolPointer + ?Sized>() -> Result<Vec<Handle>> {
754+
// Search by protocol.
755+
let search_type = SearchType::from_proto::<P>();
756+
757+
// Determine how much we need to allocate.
758+
let num_handles = match locate_handle(search_type, &mut []) {
759+
Err(err) => {
760+
if err.status() == Status::BUFFER_TOO_SMALL {
761+
err.data().expect("error data is missing")
762+
} else {
763+
return Err(err.to_err_without_payload());
764+
}
765+
}
766+
// This should never happen: if no handles match the search then a
767+
// `NOT_FOUND` error should be returned.
768+
Ok(_) => panic!("locate_handle should not return success with empty buffer"),
769+
};
770+
771+
// Allocate a large enough buffer without pointless initialization.
772+
let mut handles = Vec::with_capacity(num_handles);
773+
774+
// Perform the search.
775+
let num_handles = locate_handle(search_type, handles.spare_capacity_mut())
776+
.discard_errdata()?
777+
.len();
778+
779+
// Mark the returned number of elements as initialized.
780+
unsafe {
781+
handles.set_len(num_handles);
782+
}
783+
784+
// Emit output, with warnings
785+
Ok(handles)
786+
}
787+
701788
/// Find an arbitrary handle that supports a particular [`Protocol`]. Returns
702789
/// [`NOT_FOUND`] if no handles support the protocol.
703790
///

0 commit comments

Comments
 (0)