Skip to content

boot: Add freestanding version of protocols_per_handle #1305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion uefi-test-runner/src/proto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use uefi::prelude::*;
use uefi::proto::loaded_image::LoadedImage;
use uefi::{proto, Identify};
use uefi::{boot, proto, Identify};

pub fn test(st: &mut SystemTable<Boot>) {
info!("Testing various protocols");
Expand All @@ -10,6 +10,7 @@ pub fn test(st: &mut SystemTable<Boot>) {
let bt = st.boot_services();
find_protocol(bt);
test_protocols_per_handle(bt);
test_protocols_per_handle_freestanding();

debug::test(bt);
device_path::test(bt);
Expand Down Expand Up @@ -55,6 +56,13 @@ fn test_protocols_per_handle(bt: &BootServices) {
assert!(pph.iter().any(|guid| **guid == LoadedImage::GUID));
}

fn test_protocols_per_handle_freestanding() {
let pph = boot::protocols_per_handle(boot::image_handle()).unwrap();
info!("Image handle has {} protocols", pph.len());
// Check that one of the image's protocols is `LoadedImage`.
assert!(pph.iter().any(|guid| **guid == LoadedImage::GUID));
}

mod console;
mod debug;
mod device_path;
Expand Down
54 changes: 54 additions & 0 deletions uefi/src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,28 @@ pub unsafe fn uninstall_protocol_interface(
(bt.uninstall_protocol_interface)(handle.as_ptr(), protocol, interface).to_result()
}

/// Get the list of protocol interface [`Guids`][Guid] that are installed
/// on a [`Handle`].
///
/// # Errors
///
/// * [`Status::INVALID_PARAMETER`]: `handle` is invalid.
/// * [`Status::OUT_OF_RESOURCES`]: out of memory.
pub fn protocols_per_handle(handle: Handle) -> Result<ProtocolsPerHandle> {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };

let mut protocols = ptr::null_mut();
let mut count = 0;

unsafe { (bt.protocols_per_handle)(handle.as_ptr(), &mut protocols, &mut count) }
.to_result_with_val(|| ProtocolsPerHandle {
count,
protocols: NonNull::new(protocols)
.expect("protocols_per_handle must not return a null pointer"),
})
}

/// Returns an array of handles that support the requested protocol in a
/// pool-allocated buffer.
///
Expand Down Expand Up @@ -736,6 +758,38 @@ pub fn stall(microseconds: usize) {
}
}

/// Protocol interface [`Guids`][Guid] that are installed on a [`Handle`] as
/// returned by [`protocols_per_handle`].
#[derive(Debug)]
pub struct ProtocolsPerHandle {
protocols: NonNull<*const Guid>,
count: usize,
}

impl Drop for ProtocolsPerHandle {
fn drop(&mut self) {
let _ = unsafe { free_pool(self.protocols.cast::<u8>()) };
}
}

impl Deref for ProtocolsPerHandle {
type Target = [&'static Guid];

fn deref(&self) -> &Self::Target {
let ptr: *const &'static Guid = self.protocols.as_ptr().cast();

// SAFETY:
//
// * The firmware is assumed to provide a correctly-aligned pointer and
// array length.
// * The firmware is assumed to provide valid GUID pointers.
// * Protocol GUIDs should be constants or statics, so a 'static
// lifetime (of the individual pointers, not the overall slice) can be
// assumed.
unsafe { slice::from_raw_parts(ptr, self.count) }
}
}

/// A buffer returned by [`locate_handle_buffer`] that contains an array of
/// [`Handle`]s that support the requested protocol.
#[derive(Debug, Eq, PartialEq)]
Expand Down