diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e00bd479..59ed0ec73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Added `core::error::Error` implementations to all error types. - `SystemTable::exit_boot_services` now takes one param `memory_type` to ensure the memory type of memory map. +- Added the `ShellParams` protocol ### Removed - `BootServices::memmove` and `BootServices::set_mem` have been removed, use diff --git a/uefi-raw/src/protocol/mod.rs b/uefi-raw/src/protocol/mod.rs index 5f8bce94f..396ef53ed 100644 --- a/uefi-raw/src/protocol/mod.rs +++ b/uefi-raw/src/protocol/mod.rs @@ -12,3 +12,4 @@ pub mod driver; pub mod loaded_image; pub mod memory_protection; pub mod rng; +pub mod shell_params; diff --git a/uefi-raw/src/protocol/shell_params.rs b/uefi-raw/src/protocol/shell_params.rs new file mode 100644 index 000000000..a47137a33 --- /dev/null +++ b/uefi-raw/src/protocol/shell_params.rs @@ -0,0 +1,23 @@ +use crate::{guid, Char16, Guid}; +use core::ffi::c_void; + +pub type ShellFileHandle = *const c_void; + +#[derive(Debug)] +#[repr(C)] +pub struct ShellParametersProtocol { + /// Pointer to a list of arguments. + pub argv: *const *const Char16, + /// Number of arguments. + pub argc: usize, + /// Handle of the standard input. + pub std_in: ShellFileHandle, + /// Handle of the standard output. + pub std_out: ShellFileHandle, + /// Handle of the standard error output. + pub std_err: ShellFileHandle, +} + +impl ShellParametersProtocol { + pub const GUID: Guid = guid!("752f3136-4e16-4fdc-a22a-e5f46812f4ca"); +} diff --git a/uefi-test-runner/examples/shell_params.rs b/uefi-test-runner/examples/shell_params.rs new file mode 100644 index 000000000..1491fdf83 --- /dev/null +++ b/uefi-test-runner/examples/shell_params.rs @@ -0,0 +1,53 @@ +// ANCHOR: all +// ANCHOR: features +#![no_main] +#![no_std] +// ANCHOR_END: features + +use log::error; +// ANCHOR: use +use uefi::{prelude::*, proto::shell_params::ShellParameters}; +use uefi_services::println; + +extern crate alloc; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +// ANCHOR_END: use + +// ANCHOR: entry +#[entry] +fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { + // ANCHOR_END: entry + // ANCHOR: services + uefi_services::init(&mut system_table).unwrap(); + let boot_services = system_table.boot_services(); + // ANCHOR_END: services + + // ANCHOR: params + let shell_params = + boot_services.open_protocol_exclusive::(image_handle); + let shell_params = match shell_params { + Ok(s) => s, + Err(e) => { + error!("Failed to get ShellParameters protocol"); + return e.status(); + } + }; + + // Get as Vec of String, only with alloc feature + let args: Vec = + shell_params.args().map(|x| x.to_string()).collect(); + println!("Args: {:?}", args); + + // Or without allocating, get a slice of the pointers + println!("Num args: {}", args.len()); + if shell_params.args_len() > 1 { + println!("First real arg: '{}'", args[1]); + } + // ANCHOR_END: params + + // ANCHOR: return + Status::SUCCESS +} +// ANCHOR_END: return +// ANCHOR_END: all diff --git a/uefi-test-runner/src/bin/shell_launcher.rs b/uefi-test-runner/src/bin/shell_launcher.rs index 0309f7377..f7b89bdb7 100644 --- a/uefi-test-runner/src/bin/shell_launcher.rs +++ b/uefi-test-runner/src/bin/shell_launcher.rs @@ -69,7 +69,7 @@ fn efi_main(image: Handle, mut st: SystemTable) -> Status { let mut shell_loaded_image = boot_services .open_protocol_exclusive::(shell_image_handle) .expect("failed to open LoadedImage protocol"); - let load_options = cstr16!(r"shell.efi test_runner.efi"); + let load_options = cstr16!(r"shell.efi test_runner.efi arg1 arg2"); unsafe { shell_loaded_image.set_load_options( load_options.as_ptr().cast(), diff --git a/uefi-test-runner/src/proto/mod.rs b/uefi-test-runner/src/proto/mod.rs index ce7575e55..b232e3cf9 100644 --- a/uefi-test-runner/src/proto/mod.rs +++ b/uefi-test-runner/src/proto/mod.rs @@ -20,6 +20,7 @@ pub fn test(image: Handle, st: &mut SystemTable) { network::test(bt); pi::test(bt); rng::test(bt); + shell_params::test(bt); string::test(bt); #[cfg(any( @@ -63,6 +64,7 @@ mod media; mod network; mod pi; mod rng; +mod shell_params; #[cfg(any( target_arch = "x86", target_arch = "x86_64", diff --git a/uefi-test-runner/src/proto/shell_params.rs b/uefi-test-runner/src/proto/shell_params.rs new file mode 100644 index 000000000..935e9660b --- /dev/null +++ b/uefi-test-runner/src/proto/shell_params.rs @@ -0,0 +1,25 @@ +use uefi::proto::shell_params::ShellParameters; +use uefi::table::boot::BootServices; + +use alloc::string::ToString; +use alloc::vec::Vec; + +pub fn test(bt: &BootServices) { + info!("Running loaded image protocol test"); + + let image = bt + .get_handle_for_protocol::() + .expect("No ShellParameters handles"); + let shell_params = bt + .open_protocol_exclusive::(image) + .expect("Failed to open ShellParameters protocol"); + + assert_eq!(shell_params.args_len(), 4); + assert_eq!( + shell_params + .args() + .map(|x| x.to_string()) + .collect::>(), + &["shell.efi", "test_runner.efi", "arg1", "arg2"] + ); +} diff --git a/uefi/src/proto/mod.rs b/uefi/src/proto/mod.rs index 71d7530dd..6f2663944 100644 --- a/uefi/src/proto/mod.rs +++ b/uefi/src/proto/mod.rs @@ -76,6 +76,7 @@ pub mod network; pub mod pi; pub mod rng; pub mod security; +pub mod shell_params; pub mod shim; pub mod string; pub mod tcg; diff --git a/uefi/src/proto/shell_params.rs b/uefi/src/proto/shell_params.rs new file mode 100644 index 000000000..e1f084f8f --- /dev/null +++ b/uefi/src/proto/shell_params.rs @@ -0,0 +1,40 @@ +//! `ShellParams` protocol + +use crate::proto::unsafe_protocol; +use crate::{data_types, Char16}; +use core::slice::from_raw_parts; +use uefi_raw::protocol::shell_params::ShellParametersProtocol; + +use crate::CStr16; + +/// The ShellParameters protocol. +#[derive(Debug)] +#[repr(transparent)] +#[unsafe_protocol(ShellParametersProtocol::GUID)] +pub struct ShellParameters(ShellParametersProtocol); + +impl ShellParameters { + /// Get the number of shell parameter arguments + #[must_use] + pub fn args_len(&self) -> usize { + self.0.argc + } + + /// Get an iterator of the shell parameter arguments + pub fn args(&self) -> impl Iterator { + self.args_slice() + .iter() + .map(|x| unsafe { CStr16::from_ptr(*x) }) + } + + /// Get a slice of the args, as Char16 pointers + #[must_use] + fn args_slice(&self) -> &[*const Char16] { + unsafe { + from_raw_parts( + self.0.argv.cast::<*const data_types::chars::Char16>(), + self.0.argc, + ) + } + } +}