Skip to content

Commit dde824f

Browse files
committed
uefi: helpers: Introduce BorrowedDevicePath and DevicePathIterator
BorrowedDevicePath is used to represent device paths not owned by Rust, namely UEFI Shell Mappings. The lifetime of such paths is usually for the whole UEFI Shell life. Device path is internally a sequence of Device path nodes laid out contiguously in memory. The length field is used to determine the start of the next node. The end of Device path is marked by a special node with type 0x7F and Sub-Type 0xFF. Also provide backup debug implementations which print the device path structure instead of the text representation. Generic Device Path Structures | Mnemonic | Byte Offset | Byte Length | |---------------|-------------|-------------| | Type | 0 | 1 | | Sub-Type | 1 | 1 | | Length | 2 | 2 | | Specific data | 4 | n | Signed-off-by: Ayush Singh <[email protected]>
1 parent 0e6a11d commit dde824f

File tree

1 file changed

+194
-2
lines changed

1 file changed

+194
-2
lines changed

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

+194-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use r_efi::protocols::{device_path, device_path_to_text, shell};
1414

1515
use crate::ffi::{OsStr, OsString};
1616
use crate::io::{self, const_error};
17+
use crate::iter::Iterator;
18+
use crate::marker::PhantomData;
1719
use crate::mem::{MaybeUninit, size_of};
1820
use crate::os::uefi::env::boot_services;
1921
use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
@@ -214,6 +216,60 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
214216
Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found"))
215217
}
216218

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+
217273
/// Gets RuntimeServices.
218274
pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>> {
219275
let system_table: NonNull<r_efi::efi::SystemTable> =
@@ -278,6 +334,10 @@ impl OwnedDevicePath {
278334
pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
279335
self.0.as_ptr()
280336
}
337+
338+
pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> {
339+
BorrowedDevicePath::new(self.0)
340+
}
281341
}
282342

283343
impl Drop for OwnedDevicePath {
@@ -293,8 +353,140 @@ impl Drop for OwnedDevicePath {
293353

294354
impl crate::fmt::Debug for OwnedDevicePath {
295355
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
296-
let p = device_path_to_text(self.0).unwrap();
297-
p.fmt(f)
356+
self.borrow().fmt(f)
357+
}
358+
}
359+
360+
pub(crate) struct BorrowedDevicePath<'a> {
361+
protocol: NonNull<r_efi::protocols::device_path::Protocol>,
362+
phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
363+
}
364+
365+
impl<'a> BorrowedDevicePath<'a> {
366+
pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self {
367+
Self { protocol, phantom: PhantomData }
368+
}
369+
370+
pub(crate) const fn iter(&'a self) -> DevicePathIterator<'a> {
371+
DevicePathIterator::new(DevicePathNode::new(self.protocol))
372+
}
373+
374+
pub(crate) fn to_text(&self) -> io::Result<OsString> {
375+
device_path_to_text(self.protocol)
376+
}
377+
}
378+
379+
impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> {
380+
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
381+
match self.to_text() {
382+
Ok(p) => p.fmt(f),
383+
Err(_) => f.debug_list().entries(self.iter()).finish(),
384+
}
385+
}
386+
}
387+
388+
pub(crate) struct DevicePathIterator<'a>(Option<DevicePathNode<'a>>);
389+
390+
impl<'a> DevicePathIterator<'a> {
391+
const fn new(node: DevicePathNode<'a>) -> Self {
392+
if node.is_end() { Self(None) } else { Self(Some(node)) }
393+
}
394+
}
395+
396+
impl<'a> Iterator for DevicePathIterator<'a> {
397+
type Item = DevicePathNode<'a>;
398+
399+
fn next(&mut self) -> Option<Self::Item> {
400+
let cur_node = self.0?;
401+
402+
let next_node = unsafe { cur_node.next_node() };
403+
self.0 = if next_node.is_end() { None } else { Some(next_node) };
404+
405+
Some(cur_node)
406+
}
407+
}
408+
409+
#[derive(Copy, Clone)]
410+
pub(crate) struct DevicePathNode<'a> {
411+
protocol: NonNull<r_efi::protocols::device_path::Protocol>,
412+
phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
413+
}
414+
415+
impl<'a> DevicePathNode<'a> {
416+
pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self {
417+
Self { protocol, phantom: PhantomData }
418+
}
419+
420+
pub(crate) const fn length(&self) -> u16 {
421+
let len = unsafe { (*self.protocol.as_ptr()).length };
422+
u16::from_le_bytes(len)
423+
}
424+
425+
pub(crate) const fn node_type(&self) -> u8 {
426+
unsafe { (*self.protocol.as_ptr()).r#type }
427+
}
428+
429+
pub(crate) const fn sub_type(&self) -> u8 {
430+
unsafe { (*self.protocol.as_ptr()).sub_type }
431+
}
432+
433+
pub(crate) fn data(&self) -> &[u8] {
434+
let length: usize = self.length().into();
435+
436+
// Some nodes do not have any special data
437+
if length > 4 {
438+
let raw_ptr: *const u8 = self.protocol.as_ptr().cast();
439+
let data = unsafe { raw_ptr.add(4) };
440+
unsafe { crate::slice::from_raw_parts(data, length - 4) }
441+
} else {
442+
&[]
443+
}
444+
}
445+
446+
pub(crate) const fn is_end(&self) -> bool {
447+
self.node_type() == r_efi::protocols::device_path::TYPE_END
448+
&& self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_ENTIRE
449+
}
450+
451+
pub(crate) unsafe fn next_node(&self) -> Self {
452+
let node = unsafe {
453+
self.protocol
454+
.cast::<u8>()
455+
.add(self.length().into())
456+
.cast::<r_efi::protocols::device_path::Protocol>()
457+
};
458+
Self::new(node)
459+
}
460+
}
461+
462+
impl<'a> PartialEq for DevicePathNode<'a> {
463+
fn eq(&self, other: &Self) -> bool {
464+
let self_len = self.length();
465+
let other_len = other.length();
466+
467+
self_len == other_len
468+
&& unsafe {
469+
compiler_builtins::mem::memcmp(
470+
self.protocol.as_ptr().cast(),
471+
other.protocol.as_ptr().cast(),
472+
usize::from(self_len),
473+
) == 0
474+
}
475+
}
476+
}
477+
478+
impl<'a> crate::fmt::Debug for DevicePathNode<'a> {
479+
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
480+
match device_node_to_text(self.protocol) {
481+
Ok(p) => p.fmt(f),
482+
Err(_) => f
483+
.debug_struct("DevicePathNode")
484+
.field("type", &self.node_type())
485+
.field("sub_type", &self.sub_type())
486+
.field("length", &self.length())
487+
.field("specific_device_path_data", &self.data())
488+
.finish(),
489+
}
298490
}
299491
}
300492

0 commit comments

Comments
 (0)