Skip to content

Commit 4036241

Browse files
committed
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 <[email protected]>
1 parent 35c2908 commit 4036241

File tree

3 files changed

+122
-5
lines changed

3 files changed

+122
-5
lines changed

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

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ 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::marker::PhantomData;
1718
use crate::mem::{MaybeUninit, size_of};
1819
use crate::os::uefi::env::boot_services;
1920
use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
2021
use crate::os::uefi::{self};
22+
use crate::path::Path;
2123
use crate::ptr::NonNull;
2224
use crate::slice;
2325
use crate::sync::atomic::{AtomicPtr, Ordering};
@@ -278,6 +280,10 @@ impl OwnedDevicePath {
278280
pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
279281
self.0.as_ptr()
280282
}
283+
284+
pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> {
285+
BorrowedDevicePath::new(self.0)
286+
}
281287
}
282288

283289
impl Drop for OwnedDevicePath {
@@ -293,13 +299,37 @@ impl Drop for OwnedDevicePath {
293299

294300
impl crate::fmt::Debug for OwnedDevicePath {
295301
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
296-
match device_path_to_text(self.0) {
302+
match self.borrow().to_text() {
297303
Ok(p) => p.fmt(f),
298304
Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(),
299305
}
300306
}
301307
}
302308

309+
pub(crate) struct BorrowedDevicePath<'a> {
310+
protocol: NonNull<r_efi::protocols::device_path::Protocol>,
311+
phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
312+
}
313+
314+
impl<'a> BorrowedDevicePath<'a> {
315+
pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self {
316+
Self { protocol, phantom: PhantomData }
317+
}
318+
319+
pub(crate) fn to_text(&self) -> io::Result<OsString> {
320+
device_path_to_text(self.protocol)
321+
}
322+
}
323+
324+
impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> {
325+
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
326+
match self.to_text() {
327+
Ok(p) => p.fmt(f),
328+
Err(_) => f.debug_struct("BorrowedDevicePath").finish_non_exhaustive(),
329+
}
330+
}
331+
}
332+
303333
pub(crate) struct OwnedProtocol<T> {
304334
guid: r_efi::efi::Guid,
305335
handle: NonNull<crate::ffi::c_void>,
@@ -452,3 +482,19 @@ pub(crate) fn open_shell() -> Option<NonNull<shell::Protocol>> {
452482

453483
None
454484
}
485+
486+
/// Get device path protocol associated with shell mapping.
487+
///
488+
/// returns None in case no such mapping is exists
489+
pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result<BorrowedDevicePath<'static>> {
490+
let shell =
491+
open_shell().ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?;
492+
let mut path = os_string_to_raw(map.as_os_str())
493+
.ok_or(io::const_error!(io::ErrorKind::InvalidFilename, "Invalid UEFI shell mapping"))?;
494+
495+
let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) };
496+
let protocol = NonNull::new(protocol)
497+
.ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?;
498+
499+
Ok(BorrowedDevicePath::new(protocol))
500+
}

library/std/src/sys/path/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ cfg_if::cfg_if! {
55
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
66
mod sgx;
77
pub use sgx::*;
8-
} else if #[cfg(any(
9-
target_os = "uefi",
10-
target_os = "solid_asp3",
11-
))] {
8+
} else if #[cfg(target_os = "solid_asp3")] {
129
mod unsupported_backslash;
1310
pub use unsupported_backslash::*;
11+
} else if #[cfg(target_os = "uefi")] {
12+
mod uefi;
13+
pub use uefi::*;
1414
} else {
1515
mod unix;
1616
pub use unix::*;

library/std/src/sys/path/uefi.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#![forbid(unsafe_op_in_unsafe_fn)]
2+
use crate::ffi::OsStr;
3+
use crate::io;
4+
use crate::path::{Path, PathBuf, Prefix};
5+
use crate::sys::{helpers, unsupported_err};
6+
7+
const FORWARD_SLASH: u8 = b'/';
8+
const COLON: u8 = b':';
9+
10+
#[inline]
11+
pub fn is_sep_byte(b: u8) -> bool {
12+
b == b'\\'
13+
}
14+
15+
#[inline]
16+
pub fn is_verbatim_sep(b: u8) -> bool {
17+
b == b'\\'
18+
}
19+
20+
pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
21+
None
22+
}
23+
24+
pub const MAIN_SEP_STR: &str = "\\";
25+
pub const MAIN_SEP: char = '\\';
26+
27+
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
28+
// Absolute Shell Path
29+
if path.as_os_str().as_encoded_bytes().contains(&COLON) {
30+
let mut path_components = path.components();
31+
// Since path is not empty, it has at least one Component
32+
let prefix = path_components.next().unwrap();
33+
34+
let dev_path = helpers::get_device_path_from_map(prefix.as_ref())?;
35+
let mut dev_path_text = dev_path.to_text().map_err(|_| unsupported_err())?;
36+
37+
// UEFI Shell does not seem to end device path with `/`
38+
if *dev_path_text.as_encoded_bytes().last().unwrap() != FORWARD_SLASH {
39+
dev_path_text.push("/");
40+
}
41+
42+
let mut ans = PathBuf::from(dev_path_text);
43+
ans.push(path_components);
44+
45+
return Ok(ans);
46+
}
47+
48+
// Absolute Device Path
49+
if path.as_os_str().as_encoded_bytes().contains(&FORWARD_SLASH) {
50+
return Ok(path.to_path_buf());
51+
}
52+
53+
// cur_dir() always returns something
54+
let cur_dir = crate::env::current_dir().unwrap();
55+
let mut path_components = path.components();
56+
57+
// Relative Root
58+
if path_components.next().unwrap() == crate::path::Component::RootDir {
59+
let mut ans = PathBuf::new();
60+
ans.push(cur_dir.components().next().unwrap());
61+
ans.push(path_components);
62+
return absolute(&ans);
63+
}
64+
65+
absolute(&cur_dir.join(path))
66+
}
67+
68+
pub(crate) fn is_absolute(path: &Path) -> bool {
69+
let temp = path.as_os_str().as_encoded_bytes();
70+
temp.contains(&COLON) || temp.contains(&FORWARD_SLASH)
71+
}

0 commit comments

Comments
 (0)