Skip to content

Commit 6e70d4f

Browse files
committed
uefi: Modifying Vars to return environment variable values in addition to names
1 parent 6852357 commit 6e70d4f

File tree

2 files changed

+75
-161
lines changed

2 files changed

+75
-161
lines changed

uefi-test-runner/src/proto/shell.rs

Lines changed: 7 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ use uefi::proto::shell::Shell;
55
use uefi::{boot, cstr16};
66

77
/// Test `var()`, `vars()`, and `set_var()`
8-
pub fn test_env(shell: &ScopedProtocol<Shell>) {
8+
pub fn test_var(shell: &ScopedProtocol<Shell>) {
99
/* Test retrieving list of environment variable names */
1010
let mut cur_env_vec = shell.vars();
11-
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("path"),);
11+
assert_eq!(cur_env_vec.next().unwrap().0, cstr16!("path"));
1212
// check pre-defined shell variables; see UEFI Shell spec
13-
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("nonesting"),);
13+
assert_eq!(cur_env_vec.next().unwrap().0, cstr16!("nonesting"));
1414
let cur_env_vec = shell.vars();
1515
let default_len = cur_env_vec.count();
1616

@@ -27,15 +27,15 @@ pub fn test_env(shell: &ScopedProtocol<Shell>) {
2727
assert_eq!(cur_env_str, test_val);
2828

2929
let mut found_var = false;
30-
for env_var in cur_env_vec {
30+
for (env_var, _) in cur_env_vec {
3131
if env_var == test_var {
3232
found_var = true;
3333
}
3434
}
3535
assert!(!found_var);
3636
let cur_env_vec = shell.vars();
3737
let mut found_var = false;
38-
for env_var in cur_env_vec {
38+
for (env_var, _) in cur_env_vec {
3939
if env_var == test_var {
4040
found_var = true;
4141
}
@@ -53,7 +53,7 @@ pub fn test_env(shell: &ScopedProtocol<Shell>) {
5353

5454
let cur_env_vec = shell.vars();
5555
let mut found_var = false;
56-
for env_var in cur_env_vec {
56+
for (env_var, _) in cur_env_vec {
5757
if env_var == test_var {
5858
found_var = true;
5959
}
@@ -63,98 +63,6 @@ pub fn test_env(shell: &ScopedProtocol<Shell>) {
6363
assert_eq!(cur_env_vec.count(), default_len);
6464
}
6565

66-
/// Test `current_dir()` and `set_current_dir()`
67-
pub fn test_cur_dir(shell: &ScopedProtocol<Shell>) {
68-
/* Test setting and getting current file system and current directory */
69-
let fs_var = cstr16!("fs0:");
70-
let dir_var = cstr16!("/");
71-
let status = shell.set_current_dir(Some(fs_var), Some(dir_var));
72-
assert!(status.is_ok());
73-
74-
let cur_fs_str = shell
75-
.current_dir(Some(fs_var))
76-
.expect("Could not get the current file system mapping");
77-
let expected_fs_str = cstr16!("FS0:\\");
78-
assert_eq!(cur_fs_str, expected_fs_str);
79-
80-
// Changing current file system
81-
let fs_var = cstr16!("fs1:");
82-
let dir_var = cstr16!("/");
83-
let status = shell.set_current_dir(Some(fs_var), Some(dir_var));
84-
assert!(status.is_ok());
85-
86-
let cur_fs_str = shell
87-
.current_dir(Some(fs_var))
88-
.expect("Could not get the current file system mapping");
89-
assert_ne!(cur_fs_str, expected_fs_str);
90-
let expected_fs_str = cstr16!("FS1:\\");
91-
assert_eq!(cur_fs_str, expected_fs_str);
92-
93-
// Changing current file system and current directory
94-
let fs_var = cstr16!("fs0:");
95-
let dir_var = cstr16!("efi/");
96-
let status = shell.set_current_dir(Some(fs_var), Some(dir_var));
97-
assert!(status.is_ok());
98-
99-
let cur_fs_str = shell
100-
.current_dir(Some(fs_var))
101-
.expect("Could not get the current file system mapping");
102-
assert_ne!(cur_fs_str, expected_fs_str);
103-
let expected_fs_str = cstr16!("FS0:\\efi");
104-
assert_eq!(cur_fs_str, expected_fs_str);
105-
106-
/* Test current working directory cases */
107-
108-
// At this point, the current working file system has not been set
109-
// So we expect a NULL output
110-
assert!(shell.current_dir(None).is_none());
111-
112-
// Setting the current working file system and current working directory
113-
let dir_var = cstr16!("fs0:/");
114-
let status = shell.set_current_dir(None, Some(dir_var));
115-
assert!(status.is_ok());
116-
let cur_fs_str = shell
117-
.current_dir(Some(fs_var))
118-
.expect("Could not get the current file system mapping");
119-
let expected_fs_str = cstr16!("FS0:");
120-
assert_eq!(cur_fs_str, expected_fs_str);
121-
122-
let cur_fs_str = shell
123-
.current_dir(None)
124-
.expect("Could not get the current file system mapping");
125-
assert_eq!(cur_fs_str, expected_fs_str);
126-
127-
// Changing current working directory
128-
let dir_var = cstr16!("/efi");
129-
let status = shell.set_current_dir(None, Some(dir_var));
130-
assert!(status.is_ok());
131-
let cur_fs_str = shell
132-
.current_dir(Some(fs_var))
133-
.expect("Could not get the current file system mapping");
134-
let expected_fs_str = cstr16!("FS0:\\efi");
135-
assert_eq!(cur_fs_str, expected_fs_str);
136-
let cur_fs_str = shell
137-
.current_dir(None)
138-
.expect("Could not get the current file system mapping");
139-
assert_eq!(cur_fs_str, expected_fs_str);
140-
141-
// Changing current directory in a non-current working file system
142-
let fs_var = cstr16!("fs0:");
143-
let dir_var = cstr16!("efi/tools");
144-
let status = shell.set_current_dir(Some(fs_var), Some(dir_var));
145-
assert!(status.is_ok());
146-
let cur_fs_str = shell
147-
.current_dir(None)
148-
.expect("Could not get the current file system mapping");
149-
assert_ne!(cur_fs_str, expected_fs_str);
150-
151-
let expected_fs_str = cstr16!("FS0:\\efi\\tools");
152-
let cur_fs_str = shell
153-
.current_dir(Some(fs_var))
154-
.expect("Could not get the current file system mapping");
155-
assert_eq!(cur_fs_str, expected_fs_str);
156-
}
157-
15866
pub fn test() {
15967
info!("Running shell protocol tests");
16068

@@ -163,6 +71,5 @@ pub fn test() {
16371
let shell =
16472
boot::open_protocol_exclusive::<Shell>(handle).expect("Failed to open Shell protocol");
16573

166-
test_env(&shell);
167-
test_cur_dir(&shell);
74+
test_var(&shell);
16875
}

uefi/src/proto/shell/mod.rs

Lines changed: 68 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,45 @@ use crate::{CStr16, Char16, Result, StatusExt};
1717
#[unsafe_protocol(ShellProtocol::GUID)]
1818
pub struct Shell(ShellProtocol);
1919

20+
/// Trait for implementing the var function
21+
pub trait ShellVar {
22+
/// Gets the value of the specified environment variable
23+
fn var(&self, name: &CStr16) -> Option<&CStr16>;
24+
}
25+
2026
/// Iterator over the names of environmental variables obtained from the Shell protocol.
2127
#[derive(Debug)]
22-
pub struct Vars<'a> {
28+
pub struct Vars<'a, T: ShellVar> {
2329
/// Char16 containing names of environment variables
24-
inner: *const Char16,
30+
names: *const Char16,
31+
/// Reference to Shell Protocol
32+
protocol: *const T,
2533
/// Placeholder to attach a lifetime to `Vars`
2634
placeholder: PhantomData<&'a CStr16>,
2735
}
2836

29-
impl<'a> Iterator for Vars<'a> {
30-
type Item = &'a CStr16;
37+
impl<'a, T: ShellVar + 'a> Iterator for Vars<'a, T> {
38+
type Item = (&'a CStr16, Option<&'a CStr16>);
3139
// We iterate a list of NUL terminated CStr16s.
3240
// The list is terminated with a double NUL.
3341
fn next(&mut self) -> Option<Self::Item> {
34-
let s = unsafe { CStr16::from_ptr(self.inner) };
42+
let s = unsafe { CStr16::from_ptr(self.names) };
3543
if s.is_empty() {
3644
None
3745
} else {
38-
self.inner = unsafe { self.inner.add(s.num_chars() + 1) };
39-
Some(s)
46+
self.names = unsafe { self.names.add(s.num_chars() + 1) };
47+
Some((s, unsafe { self.protocol.as_ref().unwrap().var(s) }))
4048
}
4149
}
4250
}
4351

52+
impl ShellVar for Shell {
53+
/// Gets the value of the specified environment variable
54+
fn var(&self, name: &CStr16) -> Option<&CStr16> {
55+
self.var(name)
56+
}
57+
}
58+
4459
impl Shell {
4560
/// Gets the value of the specified environment variable
4661
///
@@ -67,10 +82,11 @@ impl Shell {
6782

6883
/// Gets an iterator over the names of all environment variables
6984
#[must_use]
70-
pub fn vars(&self) -> Vars<'_> {
85+
pub fn vars(&self) -> Vars<'_, Self> {
7186
let env_ptr = unsafe { (self.0.get_env)(ptr::null()) };
7287
Vars {
73-
inner: env_ptr.cast::<Char16>(),
88+
names: env_ptr.cast::<Char16>(),
89+
protocol: self,
7490
placeholder: PhantomData,
7591
}
7692
}
@@ -92,61 +108,38 @@ impl Shell {
92108
let value_ptr: *const Char16 = value.as_ptr();
93109
unsafe { (self.0.set_env)(name_ptr.cast(), value_ptr.cast(), volatile) }.to_result()
94110
}
95-
96-
/// Returns the current directory on the specified device
97-
///
98-
/// # Arguments
99-
///
100-
/// * `file_system_mapping` - The file system mapping for which to get
101-
/// the current directory
102-
///
103-
/// # Returns
104-
///
105-
/// * `Some(cwd)` - CStr16 containing the current working directory
106-
/// * `None` - Could not retrieve current directory
107-
#[must_use]
108-
pub fn current_dir(&self, file_system_mapping: Option<&CStr16>) -> Option<&CStr16> {
109-
let mapping_ptr: *const Char16 = file_system_mapping.map_or(ptr::null(), CStr16::as_ptr);
110-
let cur_dir = unsafe { (self.0.get_cur_dir)(mapping_ptr.cast()) };
111-
if cur_dir.is_null() {
112-
None
113-
} else {
114-
unsafe { Some(CStr16::from_ptr(cur_dir.cast())) }
115-
}
116-
}
117-
118-
/// Changes the current directory on the specified device
119-
///
120-
/// # Arguments
121-
///
122-
/// * `file_system` - Pointer to the file system's mapped name.
123-
/// * `directory` - Points to the directory on the device specified by
124-
/// `file_system`.
125-
///
126-
/// # Returns
127-
///
128-
/// * `Status::SUCCESS` - The directory was successfully set
129-
///
130-
/// # Errors
131-
///
132-
/// * `Status::EFI_NOT_FOUND` - The directory does not exist
133-
pub fn set_current_dir(
134-
&self,
135-
file_system: Option<&CStr16>,
136-
directory: Option<&CStr16>,
137-
) -> Result {
138-
let fs_ptr: *const Char16 = file_system.map_or(ptr::null(), |x| (x.as_ptr()));
139-
let dir_ptr: *const Char16 = directory.map_or(ptr::null(), |x| (x.as_ptr()));
140-
unsafe { (self.0.set_cur_dir)(fs_ptr.cast(), dir_ptr.cast()) }.to_result()
141-
}
142111
}
143112

144113
#[cfg(test)]
145114
mod tests {
146115
use super::*;
116+
use alloc::collections::BTreeMap;
147117
use alloc::vec::Vec;
148118
use uefi::cstr16;
149119

120+
struct ShellMock<'a> {
121+
inner: BTreeMap<&'a CStr16, &'a CStr16>,
122+
}
123+
124+
impl<'a> ShellMock<'a> {
125+
fn new(names: Vec<&'a CStr16>, values: Vec<&'a CStr16>) -> ShellMock<'a> {
126+
let mut inner_map = BTreeMap::new();
127+
for (name, val) in names.iter().zip(values.iter()) {
128+
inner_map.insert(*name, *val);
129+
}
130+
ShellMock { inner: inner_map }
131+
}
132+
}
133+
impl<'a> ShellVar for ShellMock<'a> {
134+
fn var(&self, name: &CStr16) -> Option<&CStr16> {
135+
if let Some(val) = self.inner.get(name) {
136+
Some(*val)
137+
} else {
138+
None
139+
}
140+
}
141+
}
142+
150143
/// Testing Vars struct
151144
#[test]
152145
fn test_vars() {
@@ -155,9 +148,11 @@ mod tests {
155148
vars_mock.push(0);
156149
vars_mock.push(0);
157150
let mut vars = Vars {
158-
inner: vars_mock.as_ptr().cast(),
151+
names: vars_mock.as_ptr().cast(),
152+
protocol: &ShellMock::new(Vec::new(), Vec::new()),
159153
placeholder: PhantomData,
160154
};
155+
161156
assert!(vars.next().is_none());
162157

163158
// One environment variable in Vars
@@ -168,10 +163,14 @@ mod tests {
168163
vars_mock.push(0);
169164
vars_mock.push(0);
170165
let vars = Vars {
171-
inner: vars_mock.as_ptr().cast(),
166+
names: vars_mock.as_ptr().cast(),
167+
protocol: &ShellMock::new(Vec::from([cstr16!("foo")]), Vec::from([cstr16!("value")])),
172168
placeholder: PhantomData,
173169
};
174-
assert_eq!(vars.collect::<Vec<_>>(), Vec::from([cstr16!("foo")]));
170+
assert_eq!(
171+
vars.collect::<Vec<_>>(),
172+
Vec::from([(cstr16!("foo"), Some(cstr16!("value")))])
173+
);
175174

176175
// Multiple environment variables in Vars
177176
let mut vars_mock = Vec::<u16>::new();
@@ -192,12 +191,20 @@ mod tests {
192191
vars_mock.push(0);
193192

194193
let vars = Vars {
195-
inner: vars_mock.as_ptr().cast(),
194+
names: vars_mock.as_ptr().cast(),
195+
protocol: &ShellMock::new(
196+
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")]),
197+
Vec::from([cstr16!("value"), cstr16!("one"), cstr16!("two")]),
198+
),
196199
placeholder: PhantomData,
197200
};
198201
assert_eq!(
199202
vars.collect::<Vec<_>>(),
200-
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")])
203+
Vec::from([
204+
(cstr16!("foo1"), Some(cstr16!("value"))),
205+
(cstr16!("bar"), Some(cstr16!("one"))),
206+
(cstr16!("baz2"), Some(cstr16!("two")))
207+
])
201208
);
202209
}
203210
}

0 commit comments

Comments
 (0)