diff --git a/uefi-test-runner/src/proto/mod.rs b/uefi-test-runner/src/proto/mod.rs index d40a11453..6348aac73 100644 --- a/uefi-test-runner/src/proto/mod.rs +++ b/uefi-test-runner/src/proto/mod.rs @@ -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) { info!("Testing various protocols"); @@ -10,6 +10,7 @@ pub fn test(st: &mut SystemTable) { 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); @@ -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; diff --git a/uefi/src/boot.rs b/uefi/src/boot.rs index d86d635a3..6aa841cee 100644 --- a/uefi/src/boot.rs +++ b/uefi/src/boot.rs @@ -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 { + 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. /// @@ -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::()) }; + } +} + +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)]