Skip to content

Commit 3b92996

Browse files
committed
uefi: Implement getcwd and chdir
- Using EFI Shell Protocol. These functions do not make much sense unless a shell is present. - Return the exe dir in case shell protocol is missing. Signed-off-by: Ayush Singh <ayush@beagleboard.org>
1 parent d11e388 commit 3b92996

File tree

2 files changed

+73
-14
lines changed

2 files changed

+73
-14
lines changed

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,8 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
177177
)
178178
};
179179

180-
// SAFETY: `convert_device_path_to_text` returns a pointer to a null-terminated UTF-16
181-
// string, and that string cannot be deallocated prior to dropping the `WStrUnits`, so
182-
// it's safe for `WStrUnits` to use.
183-
let path_len = unsafe {
184-
WStrUnits::new(path_ptr)
185-
.ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?
186-
.count()
187-
};
188-
189-
let path = OsString::from_wide(unsafe { slice::from_raw_parts(path_ptr.cast(), path_len) });
180+
let path = os_string_from_raw(path_ptr)
181+
.ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?;
190182

191183
if let Some(boot_services) = crate::os::uefi::env::boot_services() {
192184
let boot_services: NonNull<r_efi::efi::BootServices> = boot_services.cast();
@@ -420,3 +412,15 @@ impl<T> Drop for OwnedTable<T> {
420412
unsafe { crate::alloc::dealloc(self.ptr as *mut u8, self.layout) };
421413
}
422414
}
415+
416+
/// Create OsString from a pointer to NULL terminated UTF-16 string
417+
pub(crate) fn os_string_from_raw(ptr: *mut r_efi::efi::Char16) -> Option<OsString> {
418+
let path_len = unsafe { WStrUnits::new(ptr)?.count() };
419+
Some(OsString::from_wide(unsafe { slice::from_raw_parts(ptr.cast(), path_len) }))
420+
}
421+
422+
/// Create NULL terminated UTF-16 string
423+
pub(crate) fn os_string_to_raw(s: &OsStr) -> Option<Box<[r_efi::efi::Char16]>> {
424+
let temp = s.encode_wide().chain(Some(0)).collect::<Box<[r_efi::efi::Char16]>>();
425+
if temp[..temp.len() - 1].contains(&0) { None } else { Some(temp) }
426+
}

std/src/sys/pal/uefi/os.rs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use r_efi::efi::Status;
22
use r_efi::efi::protocols::{device_path, loaded_image_device_path};
33

4-
use super::{RawOsError, helpers, unsupported};
4+
use super::{RawOsError, helpers, unsupported_err};
55
use crate::error::Error as StdError;
66
use crate::ffi::{OsStr, OsString};
77
use crate::marker::PhantomData;
@@ -125,11 +125,32 @@ pub fn error_string(errno: RawOsError) -> String {
125125
}
126126

127127
pub fn getcwd() -> io::Result<PathBuf> {
128-
unsupported()
128+
match uefi_shell::open_shell() {
129+
Some(shell) => {
130+
// SAFETY: path_ptr is managed by UEFI shell and should not be deallocated
131+
let path_ptr = unsafe { ((*shell.as_ptr()).get_cur_dir)(crate::ptr::null_mut()) };
132+
helpers::os_string_from_raw(path_ptr)
133+
.map(PathBuf::from)
134+
.ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))
135+
}
136+
None => {
137+
let mut t = current_exe()?;
138+
// SAFETY: This should never fail since the disk prefix will be present even for root
139+
// executables
140+
assert!(t.pop());
141+
Ok(t)
142+
}
143+
}
129144
}
130145

131-
pub fn chdir(_: &path::Path) -> io::Result<()> {
132-
unsupported()
146+
pub fn chdir(p: &path::Path) -> io::Result<()> {
147+
let shell = uefi_shell::open_shell().ok_or(unsupported_err())?;
148+
149+
let mut p = helpers::os_string_to_raw(p.as_os_str())
150+
.ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?;
151+
152+
let r = unsafe { ((*shell.as_ptr()).set_cur_dir)(crate::ptr::null_mut(), p.as_mut_ptr()) };
153+
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
133154
}
134155

135156
pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
@@ -239,3 +260,37 @@ pub fn exit(code: i32) -> ! {
239260
pub fn getpid() -> u32 {
240261
panic!("no pids on this platform")
241262
}
263+
264+
mod uefi_shell {
265+
use r_efi::protocols::shell;
266+
267+
use super::super::helpers;
268+
use crate::ptr::NonNull;
269+
use crate::sync::atomic::{AtomicPtr, Ordering};
270+
271+
pub fn open_shell() -> Option<NonNull<shell::Protocol>> {
272+
static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> =
273+
AtomicPtr::new(crate::ptr::null_mut());
274+
275+
if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) {
276+
if let Ok(protocol) = helpers::open_protocol::<shell::Protocol>(
277+
handle,
278+
r_efi::protocols::shell::PROTOCOL_GUID,
279+
) {
280+
return Some(protocol);
281+
}
282+
}
283+
284+
let handles = helpers::locate_handles(shell::PROTOCOL_GUID).ok()?;
285+
for handle in handles {
286+
if let Ok(protocol) =
287+
helpers::open_protocol::<shell::Protocol>(handle, shell::PROTOCOL_GUID)
288+
{
289+
LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release);
290+
return Some(protocol);
291+
}
292+
}
293+
294+
None
295+
}
296+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy