Skip to content

Commit 23eb0e8

Browse files
authored
Merge pull request #1250 from rust-osdev/bishop-runtime-mod-vars
runtime: Add freestanding functions to get/set/delete UEFI variables
2 parents 9da5b55 + 3fe8b6b commit 23eb0e8

File tree

3 files changed

+209
-20
lines changed

3 files changed

+209
-20
lines changed

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

+59-18
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
use log::info;
2-
use uefi::guid;
32
use uefi::prelude::*;
43
use uefi::table::runtime::{VariableAttributes, VariableVendor};
4+
use uefi::{guid, runtime, CStr16, Error};
55

6-
fn test_variables(rt: &RuntimeServices) {
7-
let name = cstr16!("UefiRsTestVar");
8-
let test_value = b"TestValue";
9-
let test_attrs = VariableAttributes::BOOTSERVICE_ACCESS | VariableAttributes::RUNTIME_ACCESS;
6+
/// Test variable name.
7+
const NAME: &CStr16 = cstr16!("UefiRsTestVar");
8+
9+
/// Test variable vendor.
10+
const VENDOR: &VariableVendor = &VariableVendor(guid!("9baf21cf-e187-497e-ae77-5bd8b0e09703"));
11+
12+
/// Test variable value.
13+
const VALUE: &[u8] = b"TestValue";
1014

11-
// Arbitrary GUID generated for this test.
12-
let vendor = VariableVendor(guid!("9baf21cf-e187-497e-ae77-5bd8b0e09703"));
15+
/// Test variable attributes.
16+
const ATTRS: VariableAttributes =
17+
VariableAttributes::BOOTSERVICE_ACCESS.union(VariableAttributes::RUNTIME_ACCESS);
1318

19+
fn test_variables(rt: &RuntimeServices) {
1420
info!("Testing set_variable");
15-
rt.set_variable(name, &vendor, test_attrs, test_value)
21+
rt.set_variable(NAME, VENDOR, ATTRS, VALUE)
1622
.expect("failed to set variable");
1723

1824
info!("Testing get_variable_size");
1925
let size = rt
20-
.get_variable_size(name, &vendor)
26+
.get_variable_size(NAME, VENDOR)
2127
.expect("failed to get variable size");
22-
assert_eq!(size, test_value.len());
28+
assert_eq!(size, VALUE.len());
2329

2430
info!("Testing get_variable");
2531
let mut buf = [0u8; 9];
2632
let (data, attrs) = rt
27-
.get_variable(name, &vendor, &mut buf)
33+
.get_variable(NAME, VENDOR, &mut buf)
2834
.expect("failed to get variable");
29-
assert_eq!(data, test_value);
30-
assert_eq!(attrs, test_attrs);
35+
assert_eq!(data, VALUE);
36+
assert_eq!(attrs, ATTRS);
3137

3238
info!("Testing get_variable_boxed");
3339
let (data, attrs) = rt
34-
.get_variable_boxed(name, &vendor)
40+
.get_variable_boxed(NAME, VENDOR)
3541
.expect("failed to get variable");
36-
assert_eq!(&*data, test_value);
37-
assert_eq!(attrs, test_attrs);
42+
assert_eq!(&*data, VALUE);
43+
assert_eq!(attrs, ATTRS);
3844

3945
info!("Testing variable_keys");
4046
let variable_keys = rt.variable_keys().expect("failed to get variable keys");
@@ -46,10 +52,44 @@ fn test_variables(rt: &RuntimeServices) {
4652
}
4753

4854
info!("Testing delete_variable()");
49-
rt.delete_variable(name, &vendor)
55+
rt.delete_variable(NAME, VENDOR)
5056
.expect("failed to delete variable");
5157
assert_eq!(
52-
rt.get_variable(name, &vendor, &mut buf)
58+
rt.get_variable(NAME, VENDOR, &mut buf)
59+
.unwrap_err()
60+
.status(),
61+
Status::NOT_FOUND
62+
);
63+
}
64+
65+
/// Test the variable functions in `uefi::runtime`.
66+
fn test_variables_freestanding() {
67+
// Create the test variable.
68+
runtime::set_variable(NAME, VENDOR, ATTRS, VALUE).expect("failed to set variable");
69+
70+
// Test `get_variable` with too small of a buffer.
71+
let mut buf = [0u8; 0];
72+
assert_eq!(
73+
runtime::get_variable(NAME, VENDOR, &mut buf).unwrap_err(),
74+
Error::new(Status::BUFFER_TOO_SMALL, Some(9))
75+
);
76+
77+
// Test `get_variable`.
78+
let mut buf = [0u8; 9];
79+
let (data, attrs) =
80+
runtime::get_variable(NAME, VENDOR, &mut buf).expect("failed to get variable");
81+
assert_eq!(data, VALUE);
82+
assert_eq!(attrs, ATTRS);
83+
84+
// Test `get_variable_boxed`.
85+
let (data, attrs) = runtime::get_variable_boxed(NAME, VENDOR).expect("failed to get variable");
86+
assert_eq!(&*data, VALUE);
87+
assert_eq!(attrs, ATTRS);
88+
89+
// Delete the variable and verify it can no longer be read.
90+
runtime::delete_variable(NAME, VENDOR).expect("failed to delete variable");
91+
assert_eq!(
92+
runtime::get_variable(NAME, VENDOR, &mut buf)
5393
.unwrap_err()
5494
.status(),
5595
Status::NOT_FOUND
@@ -76,4 +116,5 @@ fn test_variable_info(rt: &RuntimeServices) {
76116
pub fn test(rt: &RuntimeServices) {
77117
test_variables(rt);
78118
test_variable_info(rt);
119+
test_variables_freestanding();
79120
}

uefi/src/data_types/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ pub trait Align {
137137
}
138138
}
139139

140+
impl Align for [u8] {
141+
fn alignment() -> usize {
142+
1
143+
}
144+
}
145+
140146
mod guid;
141147
pub use guid::{Guid, Identify};
142148

uefi/src/runtime.rs

+144-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@
55
//! functions after exiting boot services; see the "Calling Convention" section
66
//! of the UEFI specification for details.
77
8-
use crate::table::{self};
9-
use crate::{Result, StatusExt};
8+
use crate::{table, CStr16, Error, Result, Status, StatusExt};
109
use core::ptr::{self, NonNull};
1110

11+
#[cfg(feature = "alloc")]
12+
use {crate::mem::make_boxed, alloc::boxed::Box};
13+
14+
#[cfg(all(feature = "unstable", feature = "alloc"))]
15+
use alloc::alloc::Global;
16+
1217
pub use crate::table::runtime::{Daylight, Time, TimeCapabilities, TimeError, TimeParams};
18+
pub use uefi_raw::capsule::{CapsuleBlockDescriptor, CapsuleFlags, CapsuleHeader};
19+
pub use uefi_raw::table::runtime::{ResetType, VariableAttributes, VariableVendor};
1320

1421
fn runtime_services_raw_panicking() -> NonNull<uefi_raw::table::runtime::RuntimeServices> {
1522
let st = table::system_table_raw_panicking();
@@ -55,3 +62,138 @@ pub unsafe fn set_time(time: &Time) -> Result {
5562
let time: *const Time = time;
5663
(rt.set_time)(time.cast()).to_result()
5764
}
65+
66+
/// Gets the contents and attributes of a variable. The size of `buf` must be at
67+
/// least as big as the variable's size, although it can be larger.
68+
///
69+
/// On success, returns a tuple containing the variable's value (a slice of
70+
/// `buf`) and the variable's attributes.
71+
///
72+
/// # Errors
73+
///
74+
/// * [`Status::NOT_FOUND`]: variable was not found.
75+
/// * [`Status::BUFFER_TOO_SMALL`]: `buf` is not large enough. The required size
76+
/// will be returned in the error data.
77+
/// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
78+
/// * [`Status::SECURITY_VIOLATION`]: variable could not be read due to an
79+
/// authentication error.
80+
/// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
81+
/// after exiting boot services.
82+
pub fn get_variable<'buf>(
83+
name: &CStr16,
84+
vendor: &VariableVendor,
85+
buf: &'buf mut [u8],
86+
) -> Result<(&'buf mut [u8], VariableAttributes), Option<usize>> {
87+
let rt = runtime_services_raw_panicking();
88+
let rt = unsafe { rt.as_ref() };
89+
90+
let mut attributes = VariableAttributes::empty();
91+
let mut data_size = buf.len();
92+
let status = unsafe {
93+
(rt.get_variable)(
94+
name.as_ptr().cast(),
95+
&vendor.0,
96+
&mut attributes,
97+
&mut data_size,
98+
buf.as_mut_ptr(),
99+
)
100+
};
101+
102+
match status {
103+
Status::SUCCESS => Ok((&mut buf[..data_size], attributes)),
104+
Status::BUFFER_TOO_SMALL => Err(Error::new(status, Some(data_size))),
105+
_ => Err(Error::new(status, None)),
106+
}
107+
}
108+
109+
/// Gets the contents and attributes of a variable.
110+
///
111+
/// # Errors
112+
///
113+
/// * [`Status::NOT_FOUND`]: variable was not found.
114+
/// * [`Status::DEVICE_ERROR`]: variable could not be read due to a hardware error.
115+
/// * [`Status::SECURITY_VIOLATION`]: variable could not be read due to an
116+
/// authentication error.
117+
/// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
118+
/// after exiting boot services.
119+
#[cfg(feature = "alloc")]
120+
pub fn get_variable_boxed(
121+
name: &CStr16,
122+
vendor: &VariableVendor,
123+
) -> Result<(Box<[u8]>, VariableAttributes)> {
124+
let mut out_attr = VariableAttributes::empty();
125+
let get_var = |buf| {
126+
get_variable(name, vendor, buf).map(|(val, attr)| {
127+
// `make_boxed` expects only a DST value to be returned (`val` in
128+
// this case), so smuggle the `attr` value out via a separate
129+
// variable.
130+
out_attr = attr;
131+
val
132+
})
133+
};
134+
#[cfg(not(feature = "unstable"))]
135+
{
136+
make_boxed(get_var).map(|val| (val, out_attr))
137+
}
138+
#[cfg(feature = "unstable")]
139+
{
140+
make_boxed(get_var, Global).map(|val| (val, out_attr))
141+
}
142+
}
143+
144+
/// Sets the value of a variable. This can be used to create a new variable,
145+
/// update an existing variable, or (when the size of `data` is zero)
146+
/// delete a variable.
147+
///
148+
/// # Warnings
149+
///
150+
/// The [`Status::WARN_RESET_REQUIRED`] warning will be returned when using
151+
/// this function to transition the Secure Boot mode to setup mode or audit
152+
/// mode if the firmware requires a reboot for that operation.
153+
///
154+
/// # Errors
155+
///
156+
/// * [`Status::INVALID_PARAMETER`]: invalid attributes, name, or vendor.
157+
/// * [`Status::OUT_OF_RESOURCES`]: not enough storage is available to hold
158+
/// the variable.
159+
/// * [`Status::WRITE_PROTECTED`]: variable is read-only.
160+
/// * [`Status::SECURITY_VIOLATION`]: variable could not be written due to an
161+
/// authentication error.
162+
/// * [`Status::NOT_FOUND`]: attempted to update a non-existent variable.
163+
/// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
164+
/// after exiting boot services.
165+
pub fn set_variable(
166+
name: &CStr16,
167+
vendor: &VariableVendor,
168+
attributes: VariableAttributes,
169+
data: &[u8],
170+
) -> Result {
171+
let rt = runtime_services_raw_panicking();
172+
let rt = unsafe { rt.as_ref() };
173+
174+
unsafe {
175+
(rt.set_variable)(
176+
name.as_ptr().cast(),
177+
&vendor.0,
178+
attributes,
179+
data.len(),
180+
data.as_ptr(),
181+
)
182+
.to_result()
183+
}
184+
}
185+
186+
/// Deletes a UEFI variable.
187+
///
188+
/// # Errors
189+
///
190+
/// * [`Status::INVALID_PARAMETER`]: invalid name or vendor.
191+
/// * [`Status::WRITE_PROTECTED`]: variable is read-only.
192+
/// * [`Status::SECURITY_VIOLATION`]: variable could not be deleted due to an
193+
/// authentication error.
194+
/// * [`Status::NOT_FOUND`]: attempted to delete a non-existent variable.
195+
/// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
196+
/// after exiting boot services.
197+
pub fn delete_variable(name: &CStr16, vendor: &VariableVendor) -> Result {
198+
set_variable(name, vendor, VariableAttributes::empty(), &[])
199+
}

0 commit comments

Comments
 (0)