Skip to content

Commit 506f10e

Browse files
committed
uefi: handle: add protocol-based helpers: component_name() and device_path()
In practice, UEFI applications frequently need to extract additional metadata from handles for logging, debugging, and user-facing selection. The Device Path and Component Name 2 protocols provide helpful information, but accessing them repeatedly requires non-trivial boilerplate. This change introduces convenience helpers on Handle to streamline common queries and improve ergonomics when working with these protocols.
1 parent 7cbc1ff commit 506f10e

2 files changed

Lines changed: 85 additions & 0 deletions

File tree

uefi/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
this may print `PciRoot(0x0)/Pci(0x6,0x0)/MAC(525400000001,0x1)`.
1717
`ScopedProtocol` only implements `Display` if the underlying protocol also
1818
implements `Display`.
19+
- Added `Handle::component_name()` and `Handle::device_path()` to simplify the
20+
common use-case of querying more information about a handle.
1921

2022
## Changed
2123
- export all `text::{input, output}::*` types

uefi/src/data_types/mod.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,23 @@ pub use strs::{str_num_latin1_chars, str_to_latin1};
3030
pub use uefi_raw::{PhysicalAddress, VirtualAddress};
3131
pub use unaligned_slice::UnalignedSlice;
3232

33+
use crate::Result;
34+
use crate::boot::{self, OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol};
35+
use crate::proto::device_path::DevicePath;
36+
use crate::proto::driver::ComponentName2;
3337
use core::ffi::c_void;
3438
use core::ptr::{self, NonNull};
39+
#[cfg(doc)]
40+
use uefi_raw::Status;
3541

3642
/// Opaque handle to an UEFI entity (protocol, image...), guaranteed to be non-null.
3743
///
3844
/// If you need to have a nullable handle (for a custom UEFI FFI for example) use `Option<Handle>`.
45+
///
46+
/// A handle offers some convenient methods to better explore the context of
47+
/// it, such as:
48+
/// - [`Handle::component_name`]
49+
/// - [`Handle::device_path`]
3950
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
4051
#[repr(transparent)]
4152
pub struct Handle(NonNull<c_void>);
@@ -85,6 +96,78 @@ impl Handle {
8596
pub(crate) fn opt_to_ptr(handle: Option<Self>) -> *mut c_void {
8697
handle.map(|h| h.0.as_ptr()).unwrap_or(ptr::null_mut())
8798
}
99+
100+
// some convenient protocol helpers
101+
102+
/// Opens the underlying [`ComponentName2`], if it exists.
103+
///
104+
/// # Example
105+
/// ```rust,no_run
106+
/// # use uefi::Handle;
107+
/// # let handle = unsafe { Handle::from_ptr(123 as *mut _).unwrap() };
108+
/// let cn2_prot = handle
109+
/// .component_name()
110+
/// .expect("should have component name (v2) protocol");
111+
/// log::info!(
112+
/// "driver: {}, controller: {}",
113+
/// cn2_prot.driver_name("en").unwrap(),
114+
/// cn2_prot.controller_name(handle, None, "en").unwrap()
115+
/// );
116+
/// ```
117+
///
118+
/// # Errors
119+
///
120+
/// * [`Status::INVALID_PARAMETER`]: an invalid combination of `params` and
121+
/// `attributes` was provided.
122+
/// * [`Status::UNSUPPORTED`]: the handle does not support the protocol.
123+
/// * [`Status::ACCESS_DENIED`] or [`Status::ALREADY_STARTED`]: the protocol is
124+
/// already open in a way that is incompatible with the new request.
125+
pub fn component_name(&self) -> Result<ScopedProtocol<ComponentName2>> {
126+
// SAFETY: The protocol is only used for reading data.
127+
unsafe {
128+
boot::open_protocol::<ComponentName2>(
129+
OpenProtocolParams {
130+
handle: *self,
131+
agent: boot::image_handle(),
132+
controller: None,
133+
},
134+
OpenProtocolAttributes::GetProtocol,
135+
)
136+
}
137+
}
138+
139+
/// Opens the underlying [`DevicePath`] protocol, if it exists.
140+
///
141+
/// # Example
142+
/// ```rust,no_run
143+
/// # use uefi::Handle;
144+
/// # let handle = unsafe { Handle::from_ptr(123 as *mut _).unwrap() };
145+
/// let device_path = handle
146+
/// .device_path()
147+
/// .expect("should have device path");
148+
/// log::info!("device path: {device_path}");
149+
/// ```
150+
///
151+
/// # Errors
152+
///
153+
/// * [`Status::INVALID_PARAMETER`]: an invalid combination of `params` and
154+
/// `attributes` was provided.
155+
/// * [`Status::UNSUPPORTED`]: the handle does not support the protocol.
156+
/// * [`Status::ACCESS_DENIED`] or [`Status::ALREADY_STARTED`]: the protocol is
157+
/// already open in a way that is incompatible with the new request.
158+
pub fn device_path(&self) -> Result<ScopedProtocol<DevicePath>> {
159+
// SAFETY: The protocol is only used for reading data.
160+
unsafe {
161+
boot::open_protocol::<DevicePath>(
162+
OpenProtocolParams {
163+
handle: *self,
164+
agent: boot::image_handle(),
165+
controller: None,
166+
},
167+
OpenProtocolAttributes::GetProtocol,
168+
)
169+
}
170+
}
88171
}
89172

90173
/// Handle to an event structure, guaranteed to be non-null.

0 commit comments

Comments
 (0)