Skip to content

Commit f8ac1c4

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 5ceb623 commit f8ac1c4

2 files changed

Lines changed: 73 additions & 14 deletions

File tree

‎library/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
ifletSome(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.ptras*mutu8,self.layout)};
421413
}
422414
}
415+
416+
/// Create OsString from a pointer to NULL terminated UTF-16 string
417+
pub(crate)fnos_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)fnos_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+
}

‎library/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-
usesuper::{RawOsError, helpers,unsupported};
4+
usesuper::{RawOsError, helpers,unsupported_err};
55
usecrate::error::ErrorasStdError;
66
usecrate::ffi::{OsStr,OsString};
77
usecrate::marker::PhantomData;
@@ -125,11 +125,32 @@ pub fn error_string(errno: RawOsError) -> String {
125125
}
126126

127127
pubfngetcwd() -> 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+
letmut 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-
pubfnchdir(_:&path::Path) -> io::Result<()>{
132-
unsupported()
146+
pubfnchdir(p:&path::Path) -> io::Result<()>{
147+
let shell = uefi_shell::open_shell().ok_or(unsupported_err())?;
148+
149+
letmut 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
pubstructSplitPaths<'a>(!,PhantomData<&'a()>);
@@ -239,3 +260,37 @@ pub fn exit(code: i32) -> ! {
239260
pubfngetpid() -> u32{
240261
panic!("no pids on this platform")
241262
}
263+
264+
mod uefi_shell {
265+
use r_efi::protocols::shell;
266+
267+
usesuper::super::helpers;
268+
usecrate::ptr::NonNull;
269+
usecrate::sync::atomic::{AtomicPtr,Ordering};
270+
271+
pubfnopen_shell() -> Option<NonNull<shell::Protocol>>{
272+
staticLAST_VALID_HANDLE:AtomicPtr<crate::ffi::c_void> =
273+
AtomicPtr::new(crate::ptr::null_mut());
274+
275+
ifletSome(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)){
276+
ifletOk(protocol) = helpers::open_protocol::<shell::Protocol>(
277+
handle,
278+
r_efi::protocols::shell::PROTOCOL_GUID,
279+
){
280+
returnSome(protocol);
281+
}
282+
}
283+
284+
let handles = helpers::locate_handles(shell::PROTOCOL_GUID).ok()?;
285+
for handle in handles {
286+
ifletOk(protocol) =
287+
helpers::open_protocol::<shell::Protocol>(handle, shell::PROTOCOL_GUID)
288+
{
289+
LAST_VALID_HANDLE.store(handle.as_ptr(),Ordering::Release);
290+
returnSome(protocol);
291+
}
292+
}
293+
294+
None
295+
}
296+
}

0 commit comments

Comments
 (0)