|
1 | 1 | //! UEFI services available at runtime, even after the OS boots.
|
2 | 2 |
|
3 | 3 | use super::Header;
|
| 4 | +#[cfg(feature = "exts")] |
| 5 | +use crate::data_types::FromSliceWithNulError; |
4 | 6 | use crate::result::Error;
|
5 | 7 | use crate::table::boot::MemoryDescriptor;
|
6 | 8 | use crate::{CStr16, Char16, Guid, Result, Status};
|
| 9 | +#[cfg(feature = "exts")] |
| 10 | +use alloc_api::{vec, vec::Vec}; |
7 | 11 | use bitflags::bitflags;
|
8 |
| -use core::fmt; |
9 | 12 | use core::fmt::Formatter;
|
| 13 | +#[cfg(feature = "exts")] |
| 14 | +use core::mem; |
10 | 15 | use core::mem::MaybeUninit;
|
11 |
| -use core::ptr; |
| 16 | +use core::{fmt, ptr}; |
12 | 17 |
|
13 | 18 | /// Contains pointers to all of the runtime services.
|
14 | 19 | ///
|
@@ -151,6 +156,64 @@ impl RuntimeServices {
|
151 | 156 | }
|
152 | 157 | }
|
153 | 158 |
|
| 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 | + |
154 | 217 | /// Set the value of a variable. This can be used to create a new variable,
|
155 | 218 | /// update an existing variable, or (when the size of `data` is zero)
|
156 | 219 | /// delete a variable.
|
@@ -413,6 +476,45 @@ pub const GLOBAL_VARIABLE: Guid = Guid::from_values(
|
413 | 476 | [0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c],
|
414 | 477 | );
|
415 | 478 |
|
| 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 | + |
416 | 518 | /// The type of system reset.
|
417 | 519 | #[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
418 | 520 | #[repr(u32)]
|
|
0 commit comments