Skip to content

Commit 9745299

Browse files
runtime: Add variable key functions
The interface is slightly different than in `uefi/src/table/runtime.rs`, where we just have a function to get all keys at once in a `Vec<VariableKey>`. For the new implementation, two interfaces are provided: `get_next_variable_key` is a low-level interface that does not require the `alloc` feature; it's a simple wrapper around `GetNextVariableName`. `variable_keys` is a high-level interface that returns an iterator. (Following the conventions of Rust's std lib, the iterator type is named `VariableKeys`.) This requires `alloc`.
1 parent 80a8923 commit 9745299

File tree

1 file changed

+147
-1
lines changed

1 file changed

+147
-1
lines changed

uefi/src/runtime.rs

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
//! of the UEFI specification for details.
77
88
use crate::{table, CStr16, Error, Result, Status, StatusExt};
9+
use core::mem;
910
use core::ptr::{self, NonNull};
1011

1112
#[cfg(feature = "alloc")]
12-
use {crate::mem::make_boxed, alloc::boxed::Box};
13+
use {
14+
crate::mem::make_boxed, crate::Guid, alloc::borrow::ToOwned, alloc::boxed::Box, alloc::vec::Vec,
15+
};
1316

1417
#[cfg(all(feature = "unstable", feature = "alloc"))]
1518
use alloc::alloc::Global;
@@ -18,6 +21,9 @@ pub use crate::table::runtime::{Daylight, Time, TimeCapabilities, TimeError, Tim
1821
pub use uefi_raw::capsule::{CapsuleBlockDescriptor, CapsuleFlags, CapsuleHeader};
1922
pub use uefi_raw::table::runtime::{ResetType, VariableAttributes, VariableVendor};
2023

24+
#[cfg(feature = "alloc")]
25+
pub use crate::table::runtime::VariableKey;
26+
2127
fn runtime_services_raw_panicking() -> NonNull<uefi_raw::table::runtime::RuntimeServices> {
2228
let st = table::system_table_raw_panicking();
2329
// SAFETY: valid per requirements of `set_system_table`.
@@ -141,6 +147,146 @@ pub fn get_variable_boxed(
141147
}
142148
}
143149

150+
/// Gets each variable key (name and vendor) one at a time.
151+
///
152+
/// This is used to iterate over variable keys. See [`variable_keys`] for a more
153+
/// convenient interface that requires the `alloc` feature.
154+
///
155+
/// To get the first variable key, `name` must be initialized to start with a
156+
/// null character. The `vendor` value is arbitrary. On success, the first
157+
/// variable's name and vendor will be written out to `name` and `vendor`. Keep
158+
/// calling `get_next_variable_key` with the same `name` and `vendor` references
159+
/// to get the remaining variable keys.
160+
///
161+
/// All variable names should be valid strings, but this may not be enforced by
162+
/// firmware. To convert to a string, truncate at the first null and call
163+
/// [`CStr16::from_u16_with_nul`].
164+
///
165+
/// # Errors
166+
///
167+
/// * [`Status::NOT_FOUND`]: indicates end of iteration, the last variable keys
168+
/// was retrieved by the previous call to `get_next_variable_key`.
169+
/// * [`Status::BUFFER_TOO_SMALL`]: `name` is not large enough. The required
170+
/// size (in `u16` characters, not bytes) will be returned in the error data.
171+
/// * [`Status::INVALID_PARAMETER`]: `name` does not contain a null character, or
172+
/// the `name` and `vendor` are not an existing variable.
173+
/// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
174+
/// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
175+
/// after exiting boot services.
176+
pub fn get_next_variable_key(
177+
name: &mut [u16],
178+
vendor: &mut VariableVendor,
179+
) -> Result<(), Option<usize>> {
180+
let rt = runtime_services_raw_panicking();
181+
let rt = unsafe { rt.as_ref() };
182+
183+
let mut name_size_in_bytes = mem::size_of_val(name);
184+
185+
let status = unsafe {
186+
(rt.get_next_variable_name)(&mut name_size_in_bytes, name.as_mut_ptr(), &mut vendor.0)
187+
};
188+
match status {
189+
Status::SUCCESS => Ok(()),
190+
Status::BUFFER_TOO_SMALL => Err(Error::new(
191+
status,
192+
Some(name_size_in_bytes / mem::size_of::<u16>()),
193+
)),
194+
_ => Err(Error::new(status, None)),
195+
}
196+
}
197+
198+
/// Get an iterator over all UEFI variables.
199+
///
200+
/// See [`VariableKeys`] for details.
201+
#[cfg(feature = "alloc")]
202+
#[must_use]
203+
pub fn variable_keys() -> VariableKeys {
204+
VariableKeys::new()
205+
}
206+
207+
/// Iterator over all UEFI variables.
208+
///
209+
/// Each iteration yields a `Result<`[`VariableKey`]`>`. Error values:
210+
///
211+
/// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
212+
/// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
213+
/// after exiting boot services.
214+
#[cfg(feature = "alloc")]
215+
#[derive(Debug)]
216+
pub struct VariableKeys {
217+
name: Vec<u16>,
218+
vendor: VariableVendor,
219+
is_done: bool,
220+
}
221+
222+
#[cfg(feature = "alloc")]
223+
impl VariableKeys {
224+
fn new() -> Self {
225+
// Create a the name buffer with a reasonable default capacity, and
226+
// initialize it to an empty null-terminated string.
227+
let mut name = Vec::with_capacity(32);
228+
name.push(0);
229+
230+
Self {
231+
// Give the name buffer a reasonable default capacity.
232+
name,
233+
// The initial vendor GUID is arbitrary.
234+
vendor: VariableVendor(Guid::default()),
235+
is_done: false,
236+
}
237+
}
238+
}
239+
240+
#[cfg(feature = "alloc")]
241+
impl Iterator for VariableKeys {
242+
type Item = Result<VariableKey>;
243+
244+
fn next(&mut self) -> Option<Result<VariableKey>> {
245+
if self.is_done {
246+
return None;
247+
}
248+
249+
let mut result = get_next_variable_key(&mut self.name, &mut self.vendor);
250+
251+
// If the name buffer was too small, resize it to be big enough and call
252+
// `get_next_variable_key` again.
253+
if let Err(err) = &result {
254+
if let Some(required_size) = err.data() {
255+
self.name.resize(*required_size, 0u16);
256+
result = get_next_variable_key(&mut self.name, &mut self.vendor);
257+
}
258+
}
259+
260+
match result {
261+
Ok(()) => {
262+
// Copy the name buffer, truncated after the first null
263+
// character (if one is present).
264+
let name = if let Some(nul_pos) = self.name.iter().position(|c| *c == 0) {
265+
self.name[..=nul_pos].to_owned()
266+
} else {
267+
self.name.clone()
268+
};
269+
Some(Ok(VariableKey {
270+
name,
271+
vendor: self.vendor,
272+
}))
273+
}
274+
Err(err) => {
275+
if err.status() == Status::NOT_FOUND {
276+
// This status indicates the end of the list. The final variable
277+
// has already been yielded at this point, so return `None`.
278+
self.is_done = true;
279+
None
280+
} else {
281+
// Return the error and end iteration.
282+
self.is_done = true;
283+
Some(Err(err.to_err_without_payload()))
284+
}
285+
}
286+
}
287+
}
288+
}
289+
144290
/// Sets the value of a variable. This can be used to create a new variable,
145291
/// update an existing variable, or (when the size of `data` is zero)
146292
/// delete a variable.

0 commit comments

Comments
 (0)