From f7965e9a4b9aca2cc8446515c0e8d2d6ea063418 Mon Sep 17 00:00:00 2001 From: Ren Trieu Date: Sat, 31 May 2025 08:35:44 -0700 Subject: [PATCH 1/4] uefi: Implementing wrappers for EFI Shell env and cur_dir functions This commit implements wrappers for the following EFI Shell Protocol functions: set_cur_dir() and get_cur_dir(). --- uefi/src/proto/shell/mod.rs | 50 ++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/uefi/src/proto/shell/mod.rs b/uefi/src/proto/shell/mod.rs index e7e0dd2f4..a242b2963 100644 --- a/uefi/src/proto/shell/mod.rs +++ b/uefi/src/proto/shell/mod.rs @@ -2,12 +2,60 @@ //! EFI Shell Protocol v2.2 -use crate::proto::unsafe_protocol; +use uefi_macros::unsafe_protocol; +use uefi_raw::Status; + +use core::ptr; pub use uefi_raw::protocol::shell::ShellProtocol; +use crate::{CStr16, Char16}; + /// Shell Protocol #[derive(Debug)] #[repr(transparent)] #[unsafe_protocol(uefi_raw::protocol::shell::ShellProtocol::GUID)] pub struct Shell(uefi_raw::protocol::shell::ShellProtocol); + +impl Shell { + /// Returns the current directory on the specified device + /// + /// # Arguments + /// + /// * `file_system_mapping` - The file system mapping for which to get + /// the current directory + /// # Returns + /// + /// * `Some(cwd)` - CStr16 containing the current working directory + /// * `None` - Could not retrieve current directory + #[must_use] + pub fn get_cur_dir<'a>(&'a self, file_system_mapping: Option<&CStr16>) -> Option<&'a CStr16> { + let mapping_ptr: *const Char16 = file_system_mapping.map_or(ptr::null(), |x| (x.as_ptr())); + let cur_dir = unsafe { (self.0.get_cur_dir)(mapping_ptr.cast()) }; + if cur_dir.is_null() { + None + } else { + unsafe { Some(CStr16::from_ptr(cur_dir.cast())) } + } + } + + /// Changes the current directory on the specified device + /// + /// # Arguments + /// + /// * `file_system` - Pointer to the file system's mapped name. + /// * `directory` - Points to the directory on the device specified by + /// `file_system`. + /// # Returns + /// + /// * `Status::SUCCESS` The directory was successfully set + /// + /// # Errors + /// + /// * `Status::EFI_NOT_FOUND` The directory does not exist + pub fn set_cur_dir(&self, file_system: Option<&CStr16>, directory: Option<&CStr16>) -> Status { + let fs_ptr: *const Char16 = file_system.map_or(ptr::null(), |x| (x.as_ptr())); + let dir_ptr: *const Char16 = directory.map_or(ptr::null(), |x| (x.as_ptr())); + unsafe { (self.0.set_cur_dir)(fs_ptr.cast(), dir_ptr.cast()) } + } +} From 6d6586e413dc19353ea14918eb075a459c3a9b17 Mon Sep 17 00:00:00 2001 From: Ren Trieu Date: Tue, 12 Aug 2025 08:20:26 -0700 Subject: [PATCH 2/4] uefi-test-runner: Added tests for EFI Shell cur_dir functions This commit includes tests for the following EFI Shell Protocol functions: get_cur_dir() and set_cur_dir(). --- uefi-test-runner/src/proto/shell.rs | 100 +++++++++++++++++++++++++++- uefi/src/proto/shell/mod.rs | 14 ++-- 2 files changed, 106 insertions(+), 8 deletions(-) diff --git a/uefi-test-runner/src/proto/shell.rs b/uefi-test-runner/src/proto/shell.rs index 493a22370..937ad9a03 100644 --- a/uefi-test-runner/src/proto/shell.rs +++ b/uefi-test-runner/src/proto/shell.rs @@ -1,13 +1,109 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use uefi::boot; +use uefi::boot::ScopedProtocol; use uefi::proto::shell::Shell; +use uefi::{boot, cstr16}; +use uefi_raw::Status; + +/// Test `get_cur_dir()` and `set_cur_dir()` +pub fn test_cur_dir(shell: &ScopedProtocol) { + /* Test setting and getting current file system and current directory */ + let fs_var = cstr16!("fs0:"); + let dir_var = cstr16!("/"); + let status = shell.set_cur_dir(Some(fs_var), Some(dir_var)); + assert_eq!(status, Status::SUCCESS); + + let cur_fs_str = shell + .get_cur_dir(Some(fs_var)) + .expect("Could not get the current file system mapping"); + let expected_fs_str = cstr16!("FS0:\\"); + assert_eq!(cur_fs_str, expected_fs_str); + + // Changing current file system + let fs_var = cstr16!("fs1:"); + let dir_var = cstr16!("/"); + let status = shell.set_cur_dir(Some(fs_var), Some(dir_var)); + assert_eq!(status, Status::SUCCESS); + + let cur_fs_str = shell + .get_cur_dir(Some(fs_var)) + .expect("Could not get the current file system mapping"); + assert_ne!(cur_fs_str, expected_fs_str); + let expected_fs_str = cstr16!("FS1:\\"); + assert_eq!(cur_fs_str, expected_fs_str); + + // Changing current file system and current directory + let fs_var = cstr16!("fs0:"); + let dir_var = cstr16!("efi/"); + let status = shell.set_cur_dir(Some(fs_var), Some(dir_var)); + assert_eq!(status, Status::SUCCESS); + + let cur_fs_str = shell + .get_cur_dir(Some(fs_var)) + .expect("Could not get the current file system mapping"); + assert_ne!(cur_fs_str, expected_fs_str); + let expected_fs_str = cstr16!("FS0:\\efi"); + assert_eq!(cur_fs_str, expected_fs_str); + + /* Test current working directory cases */ + + // At this point, the current working file system has not been set + // So we expect a NULL output + assert!(shell.get_cur_dir(None).is_none()); + + // Setting the current working file system and current working directory + let dir_var = cstr16!("fs0:/"); + let status = shell.set_cur_dir(None, Some(dir_var)); + assert_eq!(status, Status::SUCCESS); + let cur_fs_str = shell + .get_cur_dir(Some(fs_var)) + .expect("Could not get the current file system mapping"); + let expected_fs_str = cstr16!("FS0:"); + assert_eq!(cur_fs_str, expected_fs_str); + + let cur_fs_str = shell + .get_cur_dir(None) + .expect("Could not get the current file system mapping"); + assert_eq!(cur_fs_str, expected_fs_str); + + // Changing current working directory + let dir_var = cstr16!("/efi"); + let status = shell.set_cur_dir(None, Some(dir_var)); + assert_eq!(status, Status::SUCCESS); + let cur_fs_str = shell + .get_cur_dir(Some(fs_var)) + .expect("Could not get the current file system mapping"); + let expected_fs_str = cstr16!("FS0:\\efi"); + assert_eq!(cur_fs_str, expected_fs_str); + let cur_fs_str = shell + .get_cur_dir(None) + .expect("Could not get the current file system mapping"); + assert_eq!(cur_fs_str, expected_fs_str); + + // Changing current directory in a non-current working file system + let fs_var = cstr16!("fs0:"); + let dir_var = cstr16!("efi/tools"); + let status = shell.set_cur_dir(Some(fs_var), Some(dir_var)); + assert_eq!(status, Status::SUCCESS); + let cur_fs_str = shell + .get_cur_dir(None) + .expect("Could not get the current file system mapping"); + assert_ne!(cur_fs_str, expected_fs_str); + + let expected_fs_str = cstr16!("FS0:\\efi\\tools"); + let cur_fs_str = shell + .get_cur_dir(Some(fs_var)) + .expect("Could not get the current file system mapping"); + assert_eq!(cur_fs_str, expected_fs_str); +} pub fn test() { info!("Running shell protocol tests"); let handle = boot::get_handle_for_protocol::().expect("No Shell handles"); - let mut _shell = + let shell = boot::open_protocol_exclusive::(handle).expect("Failed to open Shell protocol"); + + test_cur_dir(&shell); } diff --git a/uefi/src/proto/shell/mod.rs b/uefi/src/proto/shell/mod.rs index a242b2963..449a0e994 100644 --- a/uefi/src/proto/shell/mod.rs +++ b/uefi/src/proto/shell/mod.rs @@ -7,15 +7,15 @@ use uefi_raw::Status; use core::ptr; -pub use uefi_raw::protocol::shell::ShellProtocol; +use uefi_raw::protocol::shell::ShellProtocol; use crate::{CStr16, Char16}; /// Shell Protocol #[derive(Debug)] #[repr(transparent)] -#[unsafe_protocol(uefi_raw::protocol::shell::ShellProtocol::GUID)] -pub struct Shell(uefi_raw::protocol::shell::ShellProtocol); +#[unsafe_protocol(ShellProtocol::GUID)] +pub struct Shell(ShellProtocol); impl Shell { /// Returns the current directory on the specified device @@ -24,12 +24,13 @@ impl Shell { /// /// * `file_system_mapping` - The file system mapping for which to get /// the current directory + /// /// # Returns /// /// * `Some(cwd)` - CStr16 containing the current working directory /// * `None` - Could not retrieve current directory #[must_use] - pub fn get_cur_dir<'a>(&'a self, file_system_mapping: Option<&CStr16>) -> Option<&'a CStr16> { + pub fn get_cur_dir(&self, file_system_mapping: Option<&CStr16>) -> Option<&CStr16> { let mapping_ptr: *const Char16 = file_system_mapping.map_or(ptr::null(), |x| (x.as_ptr())); let cur_dir = unsafe { (self.0.get_cur_dir)(mapping_ptr.cast()) }; if cur_dir.is_null() { @@ -46,13 +47,14 @@ impl Shell { /// * `file_system` - Pointer to the file system's mapped name. /// * `directory` - Points to the directory on the device specified by /// `file_system`. + /// /// # Returns /// - /// * `Status::SUCCESS` The directory was successfully set + /// * `Status::SUCCESS` - The directory was successfully set /// /// # Errors /// - /// * `Status::EFI_NOT_FOUND` The directory does not exist + /// * `Status::EFI_NOT_FOUND` - The directory does not exist pub fn set_cur_dir(&self, file_system: Option<&CStr16>, directory: Option<&CStr16>) -> Status { let fs_ptr: *const Char16 = file_system.map_or(ptr::null(), |x| (x.as_ptr())); let dir_ptr: *const Char16 = directory.map_or(ptr::null(), |x| (x.as_ptr())); From 873e3821b1a1ee9aa72a3a178eaf09ad9e9c6b20 Mon Sep 17 00:00:00 2001 From: Ren Trieu Date: Tue, 12 Aug 2025 08:28:39 -0700 Subject: [PATCH 3/4] uefi: Revising function calls, names, and return types to better match standard convention --- uefi-test-runner/src/proto/shell.rs | 51 ++++++++++++++--------------- uefi/src/proto/shell/mod.rs | 20 ++++++----- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/uefi-test-runner/src/proto/shell.rs b/uefi-test-runner/src/proto/shell.rs index 937ad9a03..31f5eb783 100644 --- a/uefi-test-runner/src/proto/shell.rs +++ b/uefi-test-runner/src/proto/shell.rs @@ -3,18 +3,17 @@ use uefi::boot::ScopedProtocol; use uefi::proto::shell::Shell; use uefi::{boot, cstr16}; -use uefi_raw::Status; -/// Test `get_cur_dir()` and `set_cur_dir()` -pub fn test_cur_dir(shell: &ScopedProtocol) { +/// Test `current_dir()` and `set_current_dir()` +pub fn test_current_dir(shell: &ScopedProtocol) { /* Test setting and getting current file system and current directory */ let fs_var = cstr16!("fs0:"); let dir_var = cstr16!("/"); - let status = shell.set_cur_dir(Some(fs_var), Some(dir_var)); - assert_eq!(status, Status::SUCCESS); + let status = shell.set_current_dir(Some(fs_var), Some(dir_var)); + assert!(status.is_ok()); let cur_fs_str = shell - .get_cur_dir(Some(fs_var)) + .current_dir(Some(fs_var)) .expect("Could not get the current file system mapping"); let expected_fs_str = cstr16!("FS0:\\"); assert_eq!(cur_fs_str, expected_fs_str); @@ -22,11 +21,11 @@ pub fn test_cur_dir(shell: &ScopedProtocol) { // Changing current file system let fs_var = cstr16!("fs1:"); let dir_var = cstr16!("/"); - let status = shell.set_cur_dir(Some(fs_var), Some(dir_var)); - assert_eq!(status, Status::SUCCESS); + let status = shell.set_current_dir(Some(fs_var), Some(dir_var)); + assert!(status.is_ok()); let cur_fs_str = shell - .get_cur_dir(Some(fs_var)) + .current_dir(Some(fs_var)) .expect("Could not get the current file system mapping"); assert_ne!(cur_fs_str, expected_fs_str); let expected_fs_str = cstr16!("FS1:\\"); @@ -35,11 +34,11 @@ pub fn test_cur_dir(shell: &ScopedProtocol) { // Changing current file system and current directory let fs_var = cstr16!("fs0:"); let dir_var = cstr16!("efi/"); - let status = shell.set_cur_dir(Some(fs_var), Some(dir_var)); - assert_eq!(status, Status::SUCCESS); + let status = shell.set_current_dir(Some(fs_var), Some(dir_var)); + assert!(status.is_ok()); let cur_fs_str = shell - .get_cur_dir(Some(fs_var)) + .current_dir(Some(fs_var)) .expect("Could not get the current file system mapping"); assert_ne!(cur_fs_str, expected_fs_str); let expected_fs_str = cstr16!("FS0:\\efi"); @@ -49,50 +48,50 @@ pub fn test_cur_dir(shell: &ScopedProtocol) { // At this point, the current working file system has not been set // So we expect a NULL output - assert!(shell.get_cur_dir(None).is_none()); + assert!(shell.current_dir(None).is_none()); // Setting the current working file system and current working directory let dir_var = cstr16!("fs0:/"); - let status = shell.set_cur_dir(None, Some(dir_var)); - assert_eq!(status, Status::SUCCESS); + let status = shell.set_current_dir(None, Some(dir_var)); + assert!(status.is_ok()); let cur_fs_str = shell - .get_cur_dir(Some(fs_var)) + .current_dir(Some(fs_var)) .expect("Could not get the current file system mapping"); let expected_fs_str = cstr16!("FS0:"); assert_eq!(cur_fs_str, expected_fs_str); let cur_fs_str = shell - .get_cur_dir(None) + .current_dir(None) .expect("Could not get the current file system mapping"); assert_eq!(cur_fs_str, expected_fs_str); // Changing current working directory let dir_var = cstr16!("/efi"); - let status = shell.set_cur_dir(None, Some(dir_var)); - assert_eq!(status, Status::SUCCESS); + let status = shell.set_current_dir(None, Some(dir_var)); + assert!(status.is_ok()); let cur_fs_str = shell - .get_cur_dir(Some(fs_var)) + .current_dir(Some(fs_var)) .expect("Could not get the current file system mapping"); let expected_fs_str = cstr16!("FS0:\\efi"); assert_eq!(cur_fs_str, expected_fs_str); let cur_fs_str = shell - .get_cur_dir(None) + .current_dir(None) .expect("Could not get the current file system mapping"); assert_eq!(cur_fs_str, expected_fs_str); // Changing current directory in a non-current working file system let fs_var = cstr16!("fs0:"); let dir_var = cstr16!("efi/tools"); - let status = shell.set_cur_dir(Some(fs_var), Some(dir_var)); - assert_eq!(status, Status::SUCCESS); + let status = shell.set_current_dir(Some(fs_var), Some(dir_var)); + assert!(status.is_ok()); let cur_fs_str = shell - .get_cur_dir(None) + .current_dir(None) .expect("Could not get the current file system mapping"); assert_ne!(cur_fs_str, expected_fs_str); let expected_fs_str = cstr16!("FS0:\\efi\\tools"); let cur_fs_str = shell - .get_cur_dir(Some(fs_var)) + .current_dir(Some(fs_var)) .expect("Could not get the current file system mapping"); assert_eq!(cur_fs_str, expected_fs_str); } @@ -105,5 +104,5 @@ pub fn test() { let shell = boot::open_protocol_exclusive::(handle).expect("Failed to open Shell protocol"); - test_cur_dir(&shell); + test_current_dir(&shell); } diff --git a/uefi/src/proto/shell/mod.rs b/uefi/src/proto/shell/mod.rs index 449a0e994..cb67f2a1e 100644 --- a/uefi/src/proto/shell/mod.rs +++ b/uefi/src/proto/shell/mod.rs @@ -3,20 +3,18 @@ //! EFI Shell Protocol v2.2 use uefi_macros::unsafe_protocol; -use uefi_raw::Status; use core::ptr; use uefi_raw::protocol::shell::ShellProtocol; -use crate::{CStr16, Char16}; +use crate::{CStr16, Char16, Result, StatusExt}; /// Shell Protocol #[derive(Debug)] #[repr(transparent)] #[unsafe_protocol(ShellProtocol::GUID)] pub struct Shell(ShellProtocol); - impl Shell { /// Returns the current directory on the specified device /// @@ -30,8 +28,8 @@ impl Shell { /// * `Some(cwd)` - CStr16 containing the current working directory /// * `None` - Could not retrieve current directory #[must_use] - pub fn get_cur_dir(&self, file_system_mapping: Option<&CStr16>) -> Option<&CStr16> { - let mapping_ptr: *const Char16 = file_system_mapping.map_or(ptr::null(), |x| (x.as_ptr())); + pub fn current_dir(&self, file_system_mapping: Option<&CStr16>) -> Option<&CStr16> { + let mapping_ptr: *const Char16 = file_system_mapping.map_or(ptr::null(), CStr16::as_ptr); let cur_dir = unsafe { (self.0.get_cur_dir)(mapping_ptr.cast()) }; if cur_dir.is_null() { None @@ -55,9 +53,13 @@ impl Shell { /// # Errors /// /// * `Status::EFI_NOT_FOUND` - The directory does not exist - pub fn set_cur_dir(&self, file_system: Option<&CStr16>, directory: Option<&CStr16>) -> Status { - let fs_ptr: *const Char16 = file_system.map_or(ptr::null(), |x| (x.as_ptr())); - let dir_ptr: *const Char16 = directory.map_or(ptr::null(), |x| (x.as_ptr())); - unsafe { (self.0.set_cur_dir)(fs_ptr.cast(), dir_ptr.cast()) } + pub fn set_current_dir( + &self, + file_system: Option<&CStr16>, + directory: Option<&CStr16>, + ) -> Result { + let fs_ptr: *const Char16 = file_system.map_or(ptr::null(), |x| x.as_ptr()); + let dir_ptr: *const Char16 = directory.map_or(ptr::null(), |x| x.as_ptr()); + unsafe { (self.0.set_cur_dir)(fs_ptr.cast(), dir_ptr.cast()) }.to_result() } } From 18953be01cd97c92f228d95fc446d89d4c46ff4e Mon Sep 17 00:00:00 2001 From: Ren Trieu Date: Mon, 18 Aug 2025 08:55:59 -0700 Subject: [PATCH 4/4] uefi: Revising style for EFI Shell CurDir functions Co-authored-by: Nicholas Bishop --- uefi-test-runner/src/proto/shell.rs | 8 ++++++-- uefi/src/proto/shell/mod.rs | 32 +++++++++++------------------ 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/uefi-test-runner/src/proto/shell.rs b/uefi-test-runner/src/proto/shell.rs index 31f5eb783..9ce449007 100644 --- a/uefi-test-runner/src/proto/shell.rs +++ b/uefi-test-runner/src/proto/shell.rs @@ -2,7 +2,7 @@ use uefi::boot::ScopedProtocol; use uefi::proto::shell::Shell; -use uefi::{boot, cstr16}; +use uefi::{Error, Status, boot, cstr16}; /// Test `current_dir()` and `set_current_dir()` pub fn test_current_dir(shell: &ScopedProtocol) { @@ -48,7 +48,11 @@ pub fn test_current_dir(shell: &ScopedProtocol) { // At this point, the current working file system has not been set // So we expect a NULL output - assert!(shell.current_dir(None).is_none()); + assert!(shell.current_dir(None).is_err()); + assert_eq!( + shell.current_dir(None).err().unwrap(), + Error::new(Status::NOT_FOUND, ()) + ); // Setting the current working file system and current working directory let dir_var = cstr16!("fs0:/"); diff --git a/uefi/src/proto/shell/mod.rs b/uefi/src/proto/shell/mod.rs index cb67f2a1e..41940f340 100644 --- a/uefi/src/proto/shell/mod.rs +++ b/uefi/src/proto/shell/mod.rs @@ -2,39 +2,35 @@ //! EFI Shell Protocol v2.2 -use uefi_macros::unsafe_protocol; - +use crate::proto::unsafe_protocol; +use crate::{CStr16, Char16, Error, Result, Status, StatusExt}; use core::ptr; - use uefi_raw::protocol::shell::ShellProtocol; -use crate::{CStr16, Char16, Result, StatusExt}; - /// Shell Protocol #[derive(Debug)] #[repr(transparent)] #[unsafe_protocol(ShellProtocol::GUID)] pub struct Shell(ShellProtocol); + impl Shell { - /// Returns the current directory on the specified device + /// Returns the current directory on the specified device. /// /// # Arguments /// /// * `file_system_mapping` - The file system mapping for which to get /// the current directory /// - /// # Returns + /// # Errors /// - /// * `Some(cwd)` - CStr16 containing the current working directory - /// * `None` - Could not retrieve current directory - #[must_use] - pub fn current_dir(&self, file_system_mapping: Option<&CStr16>) -> Option<&CStr16> { + /// * [`Status::NOT_FOUND`] - Could not retrieve current directory + pub fn current_dir(&self, file_system_mapping: Option<&CStr16>) -> Result<&CStr16> { let mapping_ptr: *const Char16 = file_system_mapping.map_or(ptr::null(), CStr16::as_ptr); let cur_dir = unsafe { (self.0.get_cur_dir)(mapping_ptr.cast()) }; if cur_dir.is_null() { - None + Err(Error::new(Status::NOT_FOUND, ())) } else { - unsafe { Some(CStr16::from_ptr(cur_dir.cast())) } + unsafe { Ok(CStr16::from_ptr(cur_dir.cast())) } } } @@ -42,17 +38,13 @@ impl Shell { /// /// # Arguments /// - /// * `file_system` - Pointer to the file system's mapped name. - /// * `directory` - Points to the directory on the device specified by + /// * `file_system` - File system's mapped name. + /// * `directory` - Directory on the device specified by /// `file_system`. /// - /// # Returns - /// - /// * `Status::SUCCESS` - The directory was successfully set - /// /// # Errors /// - /// * `Status::EFI_NOT_FOUND` - The directory does not exist + /// * [`Status::NOT_FOUND`] - The directory does not exist pub fn set_current_dir( &self, file_system: Option<&CStr16>,