Skip to content

Commit

Permalink
uefi: helpers: Introduce OwnedDevicePath
Browse files Browse the repository at this point in the history
This PR is split off from rust-lang#135368 to reduce noise.

No real functionality changes, just some quality of life improvements.

Introduce `device_path_to_text_raw` which creates a Box<[u16]> (UTF-16
string) from path instead of creating OsString. OsString internally is
stored as WTF-8, which means converting OsString to Box<[u16]> requires
allocation. This is not ideal for std::fs APIs where we need to perform
Device Path Protocol matching while opening a volume, and create a UEFI
UTF-16 string from the remaining path (which represents file path inside
a volume). This remaining path is never used on the Rust side, and thus
does not need to be converted to WTF-8 to be used. By introducing direct
conversion to Box<[u16]>, we shorten the conversions from
`EFI_DEVICE_PATH_PROTOCOL` -> WTF-8 -> UTF-16 to
`EFI_DEVICE_PATH_PROTOCOL` -> UTF-16 which is required in every file
open operation.

Also implement Debug for OwnedDevicePath for some quality of life
improvements.

Signed-off-by: Ayush Singh <ayush@beagleboard.org>
  • Loading branch information
Ayush1325 committed Jan 12, 2025
1 parent 12445e0 commit ada6723
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 20 deletions.
52 changes: 33 additions & 19 deletions library/std/src/sys/pal/uefi/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use r_efi::protocols::{device_path, device_path_to_text, shell};

use crate::ffi::{OsStr, OsString};
use crate::io::{self, const_error};
use crate::iter::Iterator;
use crate::mem::{MaybeUninit, size_of};
use crate::os::uefi::env::boot_services;
use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
Expand Down Expand Up @@ -160,11 +161,11 @@ pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> io::Result<NonNul
open_protocol(system_handle, protocol_guid)
}

pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> {
fn device_path_to_text_raw(path: NonNull<device_path::Protocol>) -> io::Result<Box<[u16]>> {
fn path_to_text(
protocol: NonNull<device_path_to_text::Protocol>,
path: NonNull<device_path::Protocol>,
) -> io::Result<OsString> {
) -> io::Result<Box<[u16]>> {
let path_ptr: *mut r_efi::efi::Char16 = unsafe {
((*protocol.as_ptr()).convert_device_path_to_text)(
path.as_ptr(),
Expand All @@ -175,17 +176,8 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
)
};

let path = os_string_from_raw(path_ptr)
.ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?;

if let Some(boot_services) = crate::os::uefi::env::boot_services() {
let boot_services: NonNull<r_efi::efi::BootServices> = boot_services.cast();
unsafe {
((*boot_services.as_ptr()).free_pool)(path_ptr.cast());
}
}

Ok(path)
owned_uefi_string_from_raw(path_ptr)
.ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))
}

static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> =
Expand Down Expand Up @@ -214,6 +206,11 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found"))
}

pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> {
let p = device_path_to_text_raw(path)?;
Ok(OsString::from_wide(&p))
}

/// Gets RuntimeServices.
pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>> {
let system_table: NonNull<r_efi::efi::SystemTable> =
Expand All @@ -222,14 +219,14 @@ pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>>
NonNull::new(runtime_services)
}

pub(crate) struct DevicePath(NonNull<r_efi::protocols::device_path::Protocol>);
pub(crate) struct OwnedDevicePath(pub(crate) NonNull<r_efi::protocols::device_path::Protocol>);

impl DevicePath {
impl OwnedDevicePath {
pub(crate) fn from_text(p: &OsStr) -> io::Result<Self> {
fn inner(
p: &OsStr,
protocol: NonNull<r_efi::protocols::device_path_from_text::Protocol>,
) -> io::Result<DevicePath> {
) -> io::Result<OwnedDevicePath> {
let path_vec = p.encode_wide().chain(Some(0)).collect::<Vec<u16>>();
if path_vec[..path_vec.len() - 1].contains(&0) {
return Err(const_error!(
Expand All @@ -242,7 +239,7 @@ impl DevicePath {
unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) };

NonNull::new(path)
.map(DevicePath)
.map(OwnedDevicePath)
.ok_or_else(|| const_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path"))
}

Expand Down Expand Up @@ -275,12 +272,12 @@ impl DevicePath {
))
}

pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
self.0.as_ptr()
}
}

impl Drop for DevicePath {
impl Drop for OwnedDevicePath {
fn drop(&mut self) {
if let Some(bt) = boot_services() {
let bt: NonNull<r_efi::efi::BootServices> = bt.cast();
Expand All @@ -291,6 +288,13 @@ impl Drop for DevicePath {
}
}

impl crate::fmt::Debug for OwnedDevicePath {
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
let p = device_path_to_text(self.0).unwrap();
p.fmt(f)
}
}

pub(crate) struct OwnedProtocol<T> {
guid: r_efi::efi::Guid,
handle: NonNull<crate::ffi::c_void>,
Expand Down Expand Up @@ -411,6 +415,16 @@ impl<T> Drop for OwnedTable<T> {
}
}

/// Create an Owned UEFI string from pointer to NULL terminated UTF-16 string.
/// Allows string allocations and conversions.
///
/// SAFETY: This function assumes that Rust has ownership over this string
fn owned_uefi_string_from_raw(ptr: *mut r_efi::efi::Char16) -> Option<Box<[r_efi::efi::Char16]>> {
let str_len = unsafe { WStrUnits::new(ptr)?.count() };
let str_slice = crate::ptr::slice_from_raw_parts_mut(ptr.cast(), str_len);
Some(unsafe { Box::from_raw(str_slice) })
}

/// Create OsString from a pointer to NULL terminated UTF-16 string
pub(crate) fn os_string_from_raw(ptr: *mut r_efi::efi::Char16) -> Option<OsString> {
let path_len = unsafe { WStrUnits::new(ptr)?.count() };
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/pal/uefi/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ mod uefi_command_internal {

impl Image {
pub fn load_image(p: &OsStr) -> io::Result<Self> {
let path = helpers::DevicePath::from_text(p)?;
let path = helpers::OwnedDevicePath::from_text(p)?;
let boot_services: NonNull<r_efi::efi::BootServices> = boot_services()
.ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))?
.cast();
Expand Down

0 comments on commit ada6723

Please sign in to comment.