Skip to content

Commit 22df5c6

Browse files
committed
uefi: Modifying Vars to return environment variable values in addition to names
1 parent 595cab2 commit 22df5c6

File tree

2 files changed

+75
-21
lines changed

2 files changed

+75
-21
lines changed

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,12 @@ pub fn test_current_dir(shell: &ScopedProtocol<Shell>) {
101101
}
102102

103103
/// Test `var()`, `vars()`, and `set_var()`
104-
pub fn test_env(shell: &ScopedProtocol<Shell>) {
104+
pub fn test_var(shell: &ScopedProtocol<Shell>) {
105105
/* Test retrieving list of environment variable names */
106106
let mut cur_env_vec = shell.vars();
107-
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("path"),);
107+
assert_eq!(cur_env_vec.next().unwrap().0, cstr16!("path"));
108108
// check pre-defined shell variables; see UEFI Shell spec
109-
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("nonesting"),);
109+
assert_eq!(cur_env_vec.next().unwrap().0, cstr16!("nonesting"));
110110
let cur_env_vec = shell.vars();
111111
let default_len = cur_env_vec.count();
112112

@@ -123,15 +123,15 @@ pub fn test_env(shell: &ScopedProtocol<Shell>) {
123123
assert_eq!(cur_env_str, test_val);
124124

125125
let mut found_var = false;
126-
for env_var in cur_env_vec {
126+
for (env_var, _) in cur_env_vec {
127127
if env_var == test_var {
128128
found_var = true;
129129
}
130130
}
131131
assert!(!found_var);
132132
let cur_env_vec = shell.vars();
133133
let mut found_var = false;
134-
for env_var in cur_env_vec {
134+
for (env_var, _) in cur_env_vec {
135135
if env_var == test_var {
136136
found_var = true;
137137
}
@@ -149,7 +149,7 @@ pub fn test_env(shell: &ScopedProtocol<Shell>) {
149149

150150
let cur_env_vec = shell.vars();
151151
let mut found_var = false;
152-
for env_var in cur_env_vec {
152+
for (env_var, _) in cur_env_vec {
153153
if env_var == test_var {
154154
found_var = true;
155155
}
@@ -168,5 +168,5 @@ pub fn test() {
168168
boot::open_protocol_exclusive::<Shell>(handle).expect("Failed to open Shell protocol");
169169

170170
test_current_dir(&shell);
171-
test_env(&shell);
171+
test_var(&shell);
172172
}

uefi/src/proto/shell/mod.rs

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,45 @@ use uefi_raw::protocol::shell::ShellProtocol;
1515
#[unsafe_protocol(ShellProtocol::GUID)]
1616
pub struct Shell(ShellProtocol);
1717

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

27-
impl<'a> Iterator for Vars<'a> {
28-
type Item = &'a CStr16;
35+
impl<'a, T: ShellVar + 'a> Iterator for Vars<'a, T> {
36+
type Item = (&'a CStr16, Option<&'a CStr16>);
2937
// We iterate a list of NUL terminated CStr16s.
3038
// The list is terminated with a double NUL.
3139
fn next(&mut self) -> Option<Self::Item> {
32-
let s = unsafe { CStr16::from_ptr(self.inner) };
40+
let s = unsafe { CStr16::from_ptr(self.names) };
3341
if s.is_empty() {
3442
None
3543
} else {
36-
self.inner = unsafe { self.inner.add(s.num_chars() + 1) };
37-
Some(s)
44+
self.names = unsafe { self.names.add(s.num_chars() + 1) };
45+
Some((s, unsafe { self.protocol.as_ref().unwrap().var(s) }))
3846
}
3947
}
4048
}
4149

50+
impl ShellVar for Shell {
51+
/// Gets the value of the specified environment variable
52+
fn var(&self, name: &CStr16) -> Option<&CStr16> {
53+
self.var(name)
54+
}
55+
}
56+
4257
impl Shell {
4358
/// Returns the current directory on the specified device.
4459
///
@@ -106,10 +121,11 @@ impl Shell {
106121

107122
/// Gets an iterator over the names of all environment variables
108123
#[must_use]
109-
pub fn vars(&self) -> Vars<'_> {
124+
pub fn vars(&self) -> Vars<'_, Self> {
110125
let env_ptr = unsafe { (self.0.get_env)(ptr::null()) };
111126
Vars {
112-
inner: env_ptr.cast::<Char16>(),
127+
names: env_ptr.cast::<Char16>(),
128+
protocol: self,
113129
placeholder: PhantomData,
114130
}
115131
}
@@ -136,9 +152,33 @@ impl Shell {
136152
#[cfg(test)]
137153
mod tests {
138154
use super::*;
155+
use alloc::collections::BTreeMap;
139156
use alloc::vec::Vec;
140157
use uefi::cstr16;
141158

159+
struct ShellMock<'a> {
160+
inner: BTreeMap<&'a CStr16, &'a CStr16>,
161+
}
162+
163+
impl<'a> ShellMock<'a> {
164+
fn new(names: Vec<&'a CStr16>, values: Vec<&'a CStr16>) -> ShellMock<'a> {
165+
let mut inner_map = BTreeMap::new();
166+
for (name, val) in names.iter().zip(values.iter()) {
167+
inner_map.insert(*name, *val);
168+
}
169+
ShellMock { inner: inner_map }
170+
}
171+
}
172+
impl<'a> ShellVar for ShellMock<'a> {
173+
fn var(&self, name: &CStr16) -> Option<&CStr16> {
174+
if let Some(val) = self.inner.get(name) {
175+
Some(*val)
176+
} else {
177+
None
178+
}
179+
}
180+
}
181+
142182
/// Testing Vars struct
143183
#[test]
144184
fn test_vars() {
@@ -147,9 +187,11 @@ mod tests {
147187
vars_mock.push(0);
148188
vars_mock.push(0);
149189
let mut vars = Vars {
150-
inner: vars_mock.as_ptr().cast(),
190+
names: vars_mock.as_ptr().cast(),
191+
protocol: &ShellMock::new(Vec::new(), Vec::new()),
151192
placeholder: PhantomData,
152193
};
194+
153195
assert!(vars.next().is_none());
154196

155197
// One environment variable in Vars
@@ -160,10 +202,14 @@ mod tests {
160202
vars_mock.push(0);
161203
vars_mock.push(0);
162204
let vars = Vars {
163-
inner: vars_mock.as_ptr().cast(),
205+
names: vars_mock.as_ptr().cast(),
206+
protocol: &ShellMock::new(Vec::from([cstr16!("foo")]), Vec::from([cstr16!("value")])),
164207
placeholder: PhantomData,
165208
};
166-
assert_eq!(vars.collect::<Vec<_>>(), Vec::from([cstr16!("foo")]));
209+
assert_eq!(
210+
vars.collect::<Vec<_>>(),
211+
Vec::from([(cstr16!("foo"), Some(cstr16!("value")))])
212+
);
167213

168214
// Multiple environment variables in Vars
169215
let mut vars_mock = Vec::<u16>::new();
@@ -184,12 +230,20 @@ mod tests {
184230
vars_mock.push(0);
185231

186232
let vars = Vars {
187-
inner: vars_mock.as_ptr().cast(),
233+
names: vars_mock.as_ptr().cast(),
234+
protocol: &ShellMock::new(
235+
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")]),
236+
Vec::from([cstr16!("value"), cstr16!("one"), cstr16!("two")]),
237+
),
188238
placeholder: PhantomData,
189239
};
190240
assert_eq!(
191241
vars.collect::<Vec<_>>(),
192-
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")])
242+
Vec::from([
243+
(cstr16!("foo1"), Some(cstr16!("value"))),
244+
(cstr16!("bar"), Some(cstr16!("one"))),
245+
(cstr16!("baz2"), Some(cstr16!("two")))
246+
])
193247
);
194248
}
195249
}

0 commit comments

Comments
 (0)