diff --git a/uefi-test-runner/src/proto/shell.rs b/uefi-test-runner/src/proto/shell.rs index 493a22370..31f5eb783 100644 --- a/uefi-test-runner/src/proto/shell.rs +++ b/uefi-test-runner/src/proto/shell.rs @@ -1,13 +1,108 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use uefi::boot; +use uefi::boot::ScopedProtocol; use uefi::proto::shell::Shell; +use uefi::{boot, cstr16}; + +/// 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_current_dir(Some(fs_var), Some(dir_var)); + assert!(status.is_ok()); + + let cur_fs_str = shell + .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); + + // Changing current file system + let fs_var = cstr16!("fs1:"); + let dir_var = cstr16!("/"); + let status = shell.set_current_dir(Some(fs_var), Some(dir_var)); + assert!(status.is_ok()); + + let cur_fs_str = shell + .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:\\"); + 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_current_dir(Some(fs_var), Some(dir_var)); + assert!(status.is_ok()); + + let cur_fs_str = shell + .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"); + 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.current_dir(None).is_none()); + + // Setting the current working file system and current working directory + let dir_var = cstr16!("fs0:/"); + let status = shell.set_current_dir(None, Some(dir_var)); + assert!(status.is_ok()); + let cur_fs_str = shell + .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 + .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_current_dir(None, Some(dir_var)); + assert!(status.is_ok()); + let cur_fs_str = shell + .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 + .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_current_dir(Some(fs_var), Some(dir_var)); + assert!(status.is_ok()); + let cur_fs_str = shell + .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 + .current_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_current_dir(&shell); } diff --git a/uefi/src/proto/shell/mod.rs b/uefi/src/proto/shell/mod.rs index e7e0dd2f4..cb67f2a1e 100644 --- a/uefi/src/proto/shell/mod.rs +++ b/uefi/src/proto/shell/mod.rs @@ -2,12 +2,64 @@ //! EFI Shell Protocol v2.2 -use crate::proto::unsafe_protocol; +use uefi_macros::unsafe_protocol; -pub use uefi_raw::protocol::shell::ShellProtocol; +use core::ptr; + +use uefi_raw::protocol::shell::ShellProtocol; + +use crate::{CStr16, Char16, Result, StatusExt}; /// 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 + /// + /// # 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 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 + } 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_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() + } +}