Skip to content

Commit 12192a0

Browse files
Implement BootServices::protocols_per_handle (#253)
1 parent 3848aa1 commit 12192a0

File tree

3 files changed

+85
-5
lines changed

3 files changed

+85
-5
lines changed

src/table/boot.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use bitflags::bitflags;
1212
use core::cell::UnsafeCell;
1313
use core::ffi::c_void;
1414
use core::mem::{self, MaybeUninit};
15-
use core::ptr;
15+
use core::{ptr, slice};
1616

1717
/// Contains pointers to all of the boot services.
1818
#[repr(C)]
@@ -121,7 +121,11 @@ pub struct BootServices {
121121
open_protocol_information: usize,
122122

123123
// Library services
124-
protocols_per_handle: usize,
124+
protocols_per_handle: unsafe extern "efiapi" fn(
125+
handle: Handle,
126+
protocol_buffer: *mut *mut *const Guid,
127+
protocol_buffer_count: *mut usize,
128+
) -> Status,
125129
locate_handle_buffer: usize,
126130
locate_protocol: extern "efiapi" fn(
127131
proto: &Guid,
@@ -548,6 +552,36 @@ impl BootServices {
548552
unsafe { (self.set_watchdog_timer)(timeout, watchdog_code, data_len, data) }.into()
549553
}
550554

555+
/// Get the list of protocol interface [`Guids`][Guid] that are installed
556+
/// on a [`Handle`].
557+
pub fn protocols_per_handle(&self, handle: Handle) -> Result<ProtocolsPerHandle> {
558+
let mut protocols = ptr::null_mut();
559+
let mut count = 0;
560+
561+
let mut status = unsafe { (self.protocols_per_handle)(handle, &mut protocols, &mut count) };
562+
563+
if !status.is_error() {
564+
// Ensure that protocols isn't null, and that none of the GUIDs
565+
// returned are null.
566+
if protocols.is_null() {
567+
status = Status::OUT_OF_RESOURCES;
568+
} else {
569+
let protocols: &[*const Guid] = unsafe { slice::from_raw_parts(protocols, count) };
570+
if protocols.iter().any(|ptr| ptr.is_null()) {
571+
status = Status::OUT_OF_RESOURCES;
572+
}
573+
}
574+
}
575+
576+
status.into_with_val(|| {
577+
let protocols = unsafe { slice::from_raw_parts_mut(protocols as *mut &Guid, count) };
578+
ProtocolsPerHandle {
579+
boot_services: self,
580+
protocols,
581+
}
582+
})
583+
}
584+
551585
/// Returns a protocol implementation, if present on the system.
552586
///
553587
/// The caveats of `BootServices::handle_protocol()` also apply here.
@@ -933,3 +967,33 @@ pub enum TimerTrigger {
933967
/// Delay of 0 will be signalled on next timer tick.
934968
Relative(u64),
935969
}
970+
971+
/// Protocol interface [`Guids`][Guid] that are installed on a [`Handle`] as
972+
/// returned by [`BootServices::protocols_per_handle`].
973+
pub struct ProtocolsPerHandle<'a> {
974+
// The pointer returned by `protocols_per_handle` has to be free'd with
975+
// `free_pool`, so keep a reference to boot services for that purpose.
976+
boot_services: &'a BootServices,
977+
978+
// This is mutable so that it can later be free'd with `free_pool`. Users
979+
// should only get an immutable reference though, so the field is not
980+
// public.
981+
protocols: &'a mut [&'a Guid],
982+
}
983+
984+
impl<'a> Drop for ProtocolsPerHandle<'a> {
985+
fn drop(&mut self) {
986+
// Ignore the result, we can't do anything about an error here.
987+
let _ = self
988+
.boot_services
989+
.free_pool(self.protocols.as_mut_ptr() as *mut u8);
990+
}
991+
}
992+
993+
impl<'a> ProtocolsPerHandle<'a> {
994+
/// Get the protocol interface [`Guids`][Guid] that are installed on the
995+
/// [`Handle`].
996+
pub fn protocols(&self) -> &[&Guid] {
997+
self.protocols
998+
}
999+
}

uefi-test-runner/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn efi_main(image: Handle, mut st: SystemTable<Boot>) -> Status {
4444
boot::test(bt);
4545

4646
// Test all the supported protocols.
47-
proto::test(&mut st);
47+
proto::test(image, &mut st);
4848

4949
// TODO: runtime services work before boot services are exited, but we'd
5050
// probably want to test them after exit_boot_services. However,

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use uefi::prelude::*;
22

3-
use uefi::proto;
3+
use uefi::proto::loaded_image::LoadedImage;
4+
use uefi::{proto, Identify};
45

5-
pub fn test(st: &mut SystemTable<Boot>) {
6+
pub fn test(image: Handle, st: &mut SystemTable<Boot>) {
67
info!("Testing various protocols");
78

89
console::test(st);
910

1011
let bt = st.boot_services();
1112
find_protocol(bt);
13+
test_protocols_per_handle(image, bt);
1214

1315
debug::test(bt);
1416
media::test(bt);
@@ -36,6 +38,20 @@ fn find_protocol(bt: &BootServices) {
3638
);
3739
}
3840

41+
fn test_protocols_per_handle(image: Handle, bt: &BootServices) {
42+
let pph = bt
43+
.protocols_per_handle(image)
44+
.expect_success("Failed to get protocols for image handle");
45+
46+
info!("Image handle has {} protocols", pph.protocols().len());
47+
48+
// Check that one of the image's protocols is `LoadedImage`.
49+
assert!(pph
50+
.protocols()
51+
.iter()
52+
.any(|guid| **guid == LoadedImage::GUID));
53+
}
54+
3955
mod console;
4056
mod debug;
4157
mod media;

0 commit comments

Comments
 (0)