Skip to content

Commit 45c38a1

Browse files
Add method to get variable names (#254)
1 parent ce3b344 commit 45c38a1

File tree

5 files changed

+119
-5
lines changed

5 files changed

+119
-5
lines changed

src/data_types/guid.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use core::fmt;
88
///
99
/// The `Display` formatter prints GUIDs in the canonical format defined by
1010
/// RFC 4122, which is also used by UEFI.
11-
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
11+
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
1212
#[repr(C)]
1313
pub struct Guid {
1414
/// The low field of the timestamp.

src/data_types/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ pub use self::chars::{Char16, Char8};
5050
mod enums;
5151

5252
mod strs;
53-
pub use self::strs::{CStr16, CStr8};
53+
pub use self::strs::{CStr16, CStr8, FromSliceWithNulError};

src/data_types/strs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use core::iter::Iterator;
55
use core::result::Result;
66
use core::slice;
77

8-
/// Errors which can occur during checked [uN] -> CStrN conversions
8+
/// Errors which can occur during checked `[uN]` -> `CStrN` conversions
9+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
910
pub enum FromSliceWithNulError {
1011
/// An invalid character was encountered before the end of the slice
1112
InvalidChar(usize),

src/table/runtime.rs

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
//! UEFI services available at runtime, even after the OS boots.
22
33
use super::Header;
4+
#[cfg(feature = "exts")]
5+
use crate::data_types::FromSliceWithNulError;
46
use crate::result::Error;
57
use crate::table::boot::MemoryDescriptor;
68
use crate::{CStr16, Char16, Guid, Result, Status};
9+
#[cfg(feature = "exts")]
10+
use alloc_api::{vec, vec::Vec};
711
use bitflags::bitflags;
8-
use core::fmt;
912
use core::fmt::Formatter;
13+
#[cfg(feature = "exts")]
14+
use core::mem;
1015
use core::mem::MaybeUninit;
11-
use core::ptr;
16+
use core::{fmt, ptr};
1217

1318
/// Contains pointers to all of the runtime services.
1419
///
@@ -151,6 +156,64 @@ impl RuntimeServices {
151156
}
152157
}
153158

159+
/// Get the names and vendor GUIDs of all currently-set variables.
160+
#[cfg(feature = "exts")]
161+
pub fn variable_keys(&self) -> Result<Vec<VariableKey>> {
162+
let mut all_variables = Vec::new();
163+
164+
// The initial value of name must start with a null character. Start
165+
// out with a reasonable size that likely won't need to be increased.
166+
let mut name = vec![0u16; 32];
167+
// The initial value of vendor is ignored.
168+
let mut vendor = Guid::default();
169+
170+
let mut status;
171+
loop {
172+
let mut name_size_in_bytes = name.len() * mem::size_of::<u16>();
173+
status = unsafe {
174+
(self.get_next_variable_name)(
175+
&mut name_size_in_bytes,
176+
name.as_mut_ptr(),
177+
&mut vendor,
178+
)
179+
};
180+
181+
match status {
182+
Status::SUCCESS => {
183+
// CStr16::from_u16_with_nul does not allow interior nulls,
184+
// so make the copy exactly the right size.
185+
let name = if let Some(nul_pos) = name.iter().position(|c| *c == 0) {
186+
name[..=nul_pos].to_vec()
187+
} else {
188+
status = Status::ABORTED;
189+
break;
190+
};
191+
192+
all_variables.push(VariableKey { name, vendor });
193+
}
194+
Status::BUFFER_TOO_SMALL => {
195+
// The name buffer passed in was too small, resize it to be
196+
// big enough for the next variable name.
197+
name.resize(name_size_in_bytes / 2, 0);
198+
}
199+
Status::NOT_FOUND => {
200+
// This status indicates the end of the list. The final
201+
// variable has already been received at this point, so
202+
// no new variable should be added to the output.
203+
status = Status::SUCCESS;
204+
break;
205+
}
206+
_ => {
207+
// For anything else, an error has occurred so break out of
208+
// the loop and return it.
209+
break;
210+
}
211+
}
212+
}
213+
214+
status.into_with_val(|| all_variables)
215+
}
216+
154217
/// Set the value of a variable. This can be used to create a new variable,
155218
/// update an existing variable, or (when the size of `data` is zero)
156219
/// delete a variable.
@@ -413,6 +476,45 @@ pub const GLOBAL_VARIABLE: Guid = Guid::from_values(
413476
[0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c],
414477
);
415478

479+
/// Unique key for a variable.
480+
#[cfg(feature = "exts")]
481+
#[derive(Debug)]
482+
pub struct VariableKey {
483+
name: Vec<u16>,
484+
/// Unique identifier for the vendor.
485+
pub vendor: Guid,
486+
}
487+
488+
#[cfg(feature = "exts")]
489+
impl VariableKey {
490+
/// Name of the variable.
491+
pub fn name(&self) -> core::result::Result<&CStr16, FromSliceWithNulError> {
492+
CStr16::from_u16_with_nul(&self.name)
493+
}
494+
}
495+
496+
#[cfg(feature = "exts")]
497+
impl fmt::Display for VariableKey {
498+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
499+
write!(f, "VariableKey {{ name: ")?;
500+
501+
match self.name() {
502+
Ok(name) => write!(f, "\"{}\"", name)?,
503+
Err(err) => write!(f, "Err({:?})", err)?,
504+
}
505+
506+
write!(f, ", vendor: ")?;
507+
508+
if self.vendor == GLOBAL_VARIABLE {
509+
write!(f, "GLOBAL_VARIABLE")?;
510+
} else {
511+
write!(f, "{}", self.vendor)?;
512+
}
513+
514+
write!(f, " }}")
515+
}
516+
}
517+
416518
/// The type of system reset.
417519
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
418520
#[repr(u32)]

uefi-test-runner/src/runtime/vars.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ fn test_variables(rt: &RuntimeServices) {
5252
.expect_success("failed to get variable");
5353
assert_eq!(data, test_value);
5454
assert_eq!(attrs, test_attrs);
55+
56+
info!("Testing variable_keys");
57+
let variable_keys = rt
58+
.variable_keys()
59+
.expect_success("failed to get variable keys");
60+
info!("Found {} variables", variable_keys.len());
61+
// There are likely a bunch of variables, only print out the first one
62+
// during the test to avoid spamming the log.
63+
if let Some(key) = variable_keys.first() {
64+
info!("First variable: {}", key);
65+
}
5566
}
5667

5768
pub fn test(rt: &RuntimeServices) {

0 commit comments

Comments
 (0)