Skip to content

Commit cb50d4d

Browse files
committedMar 14, 2025
Auto merge of rust-lang#137424 - Ayush1325:uefi-path-node, r=nicholasbishop,cuviper
uefi: helpers: Add DevicePathNode abstractions - UEFI device path is a series of nodes layed out in a contiguous memory region. So it makes sense to use Iterator abstraction for modeling DevicePaths - This PR has been split off from rust-lang#135368 for easier review. The allow dead_code will be removed in rust-lang#135368 cc `@nicholasbishop`
2 parents f7b4354 + 61e550a commit cb50d4d

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed
 

‎library/std/src/sys/pal/uefi/helpers.rs

+179
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,60 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
216216
Err(io::const_error!(io::ErrorKind::NotFound, "no device path to text protocol found"))
217217
}
218218

219+
fn device_node_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> {
220+
fn node_to_text(
221+
protocol: NonNull<device_path_to_text::Protocol>,
222+
path: NonNull<device_path::Protocol>,
223+
) -> io::Result<OsString> {
224+
let path_ptr: *mut r_efi::efi::Char16 = unsafe {
225+
((*protocol.as_ptr()).convert_device_node_to_text)(
226+
path.as_ptr(),
227+
// DisplayOnly
228+
r_efi::efi::Boolean::FALSE,
229+
// AllowShortcuts
230+
r_efi::efi::Boolean::FALSE,
231+
)
232+
};
233+
234+
let path = os_string_from_raw(path_ptr)
235+
.ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?;
236+
237+
if let Some(boot_services) = crate::os::uefi::env::boot_services() {
238+
let boot_services: NonNull<r_efi::efi::BootServices> = boot_services.cast();
239+
unsafe {
240+
((*boot_services.as_ptr()).free_pool)(path_ptr.cast());
241+
}
242+
}
243+
244+
Ok(path)
245+
}
246+
247+
static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> =
248+
AtomicPtr::new(crate::ptr::null_mut());
249+
250+
if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) {
251+
if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>(
252+
handle,
253+
device_path_to_text::PROTOCOL_GUID,
254+
) {
255+
return node_to_text(protocol, path);
256+
}
257+
}
258+
259+
let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?;
260+
for handle in device_path_to_text_handles {
261+
if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>(
262+
handle,
263+
device_path_to_text::PROTOCOL_GUID,
264+
) {
265+
LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release);
266+
return node_to_text(protocol, path);
267+
}
268+
}
269+
270+
Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found"))
271+
}
272+
219273
/// Gets RuntimeServices.
220274
pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>> {
221275
let system_table: NonNull<r_efi::efi::SystemTable> =
@@ -319,6 +373,11 @@ impl<'a> BorrowedDevicePath<'a> {
319373
pub(crate) fn to_text(&self) -> io::Result<OsString> {
320374
device_path_to_text(self.protocol)
321375
}
376+
377+
#[expect(dead_code)]
378+
pub(crate) const fn iter(&'a self) -> DevicePathIterator<'a> {
379+
DevicePathIterator::new(DevicePathNode::new(self.protocol))
380+
}
322381
}
323382

324383
impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> {
@@ -330,6 +389,126 @@ impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> {
330389
}
331390
}
332391

392+
pub(crate) struct DevicePathIterator<'a>(Option<DevicePathNode<'a>>);
393+
394+
impl<'a> DevicePathIterator<'a> {
395+
const fn new(node: DevicePathNode<'a>) -> Self {
396+
if node.is_end() { Self(None) } else { Self(Some(node)) }
397+
}
398+
}
399+
400+
impl<'a> Iterator for DevicePathIterator<'a> {
401+
type Item = DevicePathNode<'a>;
402+
403+
fn next(&mut self) -> Option<Self::Item> {
404+
let cur_node = self.0?;
405+
406+
let next_node = unsafe { cur_node.next_node() };
407+
self.0 = if next_node.is_end() { None } else { Some(next_node) };
408+
409+
Some(cur_node)
410+
}
411+
}
412+
413+
#[derive(Copy, Clone)]
414+
pub(crate) struct DevicePathNode<'a> {
415+
protocol: NonNull<r_efi::protocols::device_path::Protocol>,
416+
phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
417+
}
418+
419+
impl<'a> DevicePathNode<'a> {
420+
pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self {
421+
Self { protocol, phantom: PhantomData }
422+
}
423+
424+
pub(crate) const fn length(&self) -> u16 {
425+
let len = unsafe { (*self.protocol.as_ptr()).length };
426+
u16::from_le_bytes(len)
427+
}
428+
429+
pub(crate) const fn node_type(&self) -> u8 {
430+
unsafe { (*self.protocol.as_ptr()).r#type }
431+
}
432+
433+
pub(crate) const fn sub_type(&self) -> u8 {
434+
unsafe { (*self.protocol.as_ptr()).sub_type }
435+
}
436+
437+
pub(crate) fn data(&self) -> &[u8] {
438+
let length: usize = self.length().into();
439+
440+
// Some nodes do not have any special data
441+
if length > 4 {
442+
let raw_ptr: *const u8 = self.protocol.as_ptr().cast();
443+
let data = unsafe { raw_ptr.add(4) };
444+
unsafe { crate::slice::from_raw_parts(data, length - 4) }
445+
} else {
446+
&[]
447+
}
448+
}
449+
450+
pub(crate) const fn is_end(&self) -> bool {
451+
self.node_type() == r_efi::protocols::device_path::TYPE_END
452+
&& self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_ENTIRE
453+
}
454+
455+
#[expect(dead_code)]
456+
pub(crate) const fn is_end_instance(&self) -> bool {
457+
self.node_type() == r_efi::protocols::device_path::TYPE_END
458+
&& self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_INSTANCE
459+
}
460+
461+
pub(crate) unsafe fn next_node(&self) -> Self {
462+
let node = unsafe {
463+
self.protocol
464+
.cast::<u8>()
465+
.add(self.length().into())
466+
.cast::<r_efi::protocols::device_path::Protocol>()
467+
};
468+
Self::new(node)
469+
}
470+
471+
#[expect(dead_code)]
472+
pub(crate) fn to_path(&'a self) -> BorrowedDevicePath<'a> {
473+
BorrowedDevicePath::new(self.protocol)
474+
}
475+
476+
pub(crate) fn to_text(&self) -> io::Result<OsString> {
477+
device_node_to_text(self.protocol)
478+
}
479+
}
480+
481+
impl<'a> PartialEq for DevicePathNode<'a> {
482+
fn eq(&self, other: &Self) -> bool {
483+
let self_len = self.length();
484+
let other_len = other.length();
485+
486+
self_len == other_len
487+
&& unsafe {
488+
compiler_builtins::mem::memcmp(
489+
self.protocol.as_ptr().cast(),
490+
other.protocol.as_ptr().cast(),
491+
usize::from(self_len),
492+
) == 0
493+
}
494+
}
495+
}
496+
497+
impl<'a> crate::fmt::Debug for DevicePathNode<'a> {
498+
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
499+
match self.to_text() {
500+
Ok(p) => p.fmt(f),
501+
Err(_) => f
502+
.debug_struct("DevicePathNode")
503+
.field("type", &self.node_type())
504+
.field("sub_type", &self.sub_type())
505+
.field("length", &self.length())
506+
.field("specific_device_path_data", &self.data())
507+
.finish(),
508+
}
509+
}
510+
}
511+
333512
pub(crate) struct OwnedProtocol<T> {
334513
guid: r_efi::efi::Guid,
335514
handle: NonNull<crate::ffi::c_void>,

0 commit comments

Comments
 (0)