summary refs log tree commit diff
path: root/library/std/src/sys/pal
diff options
context:
space:
mode:
authorAyush Singh <ayush@beagleboard.org>2025-01-14 11:40:22 +0530
committerAyush Singh <ayush@beagleboard.org>2025-01-16 10:19:22 +0530
commitc1790b14bcd62df7bccdfa7d7fbe4533dfdcdc8c (patch)
tree0b8411c4a82206d30ccbe8f034f88dd34bbc7313 /library/std/src/sys/pal
parent35c2908177a17ca4e0acbc9013e42ee525ba155c (diff)
downloadrust-c1790b14bcd62df7bccdfa7d7fbe4533dfdcdc8c.tar.gz
rust-c1790b14bcd62df7bccdfa7d7fbe4533dfdcdc8c.zip
uefi: Implement path
UEFI paths can be of 4 types:
1. Absolute Shell Path: Uses shell mappings
2. Absolute Device Path: this is what we want
3: Relative root: path relative to the current root.
4: Relative

Absolute shell path can be identified with `:` and Absolute Device path
can be identified with `/`. Relative root path will start with `\`.

The algorithm is mostly taken from edk2 UEFI shell implementation and is
somewhat simple. Check for the path type in order.

For Absolute Shell path, use `EFI_SHELL->GetDevicePathFromMap` to
get a BorrowedDevicePath for the volume.

For Relative paths, we use the current working directory to construct
the new path.

BorrowedDevicePath abstraction is needed to interact with
`EFI_SHELL->GetDevicePathFromMap` which returns a Device Path Protocol
with the lifetime of UEFI shell.

Absolute Shell paths cannot exist if UEFI shell is missing.

Signed-off-by: Ayush Singh <ayush@beagleboard.org>
Diffstat (limited to 'library/std/src/sys/pal')
-rw-r--r--library/std/src/sys/pal/uefi/helpers.rs50
1 files changed, 49 insertions, 1 deletions
diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs
index 7504a0f7ad7..dccc137d6f5 100644
--- a/library/std/src/sys/pal/uefi/helpers.rs
+++ b/library/std/src/sys/pal/uefi/helpers.rs
@@ -14,10 +14,12 @@ use r_efi::protocols::{device_path, device_path_to_text, shell};
 
 use crate::ffi::{OsStr, OsString};
 use crate::io::{self, const_error};
+use crate::marker::PhantomData;
 use crate::mem::{MaybeUninit, size_of};
 use crate::os::uefi::env::boot_services;
 use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
 use crate::os::uefi::{self};
+use crate::path::Path;
 use crate::ptr::NonNull;
 use crate::slice;
 use crate::sync::atomic::{AtomicPtr, Ordering};
@@ -278,6 +280,10 @@ impl OwnedDevicePath {
     pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
         self.0.as_ptr()
     }
+
+    pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> {
+        BorrowedDevicePath::new(self.0)
+    }
 }
 
 impl Drop for OwnedDevicePath {
@@ -293,13 +299,37 @@ impl Drop for OwnedDevicePath {
 
 impl crate::fmt::Debug for OwnedDevicePath {
     fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
-        match device_path_to_text(self.0) {
+        match self.borrow().to_text() {
             Ok(p) => p.fmt(f),
             Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(),
         }
     }
 }
 
+pub(crate) struct BorrowedDevicePath<'a> {
+    protocol: NonNull<r_efi::protocols::device_path::Protocol>,
+    phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
+}
+
+impl<'a> BorrowedDevicePath<'a> {
+    pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self {
+        Self { protocol, phantom: PhantomData }
+    }
+
+    pub(crate) fn to_text(&self) -> io::Result<OsString> {
+        device_path_to_text(self.protocol)
+    }
+}
+
+impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> {
+    fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
+        match self.to_text() {
+            Ok(p) => p.fmt(f),
+            Err(_) => f.debug_struct("BorrowedDevicePath").finish_non_exhaustive(),
+        }
+    }
+}
+
 pub(crate) struct OwnedProtocol<T> {
     guid: r_efi::efi::Guid,
     handle: NonNull<crate::ffi::c_void>,
@@ -452,3 +482,21 @@ pub(crate) fn open_shell() -> Option<NonNull<shell::Protocol>> {
 
     None
 }
+
+/// Get device path protocol associated with shell mapping.
+///
+/// returns None in case no such mapping is exists
+pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result<BorrowedDevicePath<'static>> {
+    let shell =
+        open_shell().ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?;
+    let mut path = os_string_to_raw(map.as_os_str())
+        .ok_or(io::const_error!(io::ErrorKind::InvalidFilename, "Invalid UEFI shell mapping"))?;
+
+    // The Device Path Protocol pointer returned by UEFI shell is owned by the shell and is not
+    // freed throughout it's lifetime. So it has a 'static lifetime.
+    let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) };
+    let protocol = NonNull::new(protocol)
+        .ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?;
+
+    Ok(BorrowedDevicePath::new(protocol))
+}