Skip to content

Commit 0afaf99

Browse files
runtime: Add freestanding functions to get/set/delete UEFI variables
The new version of `get_variable` returns the required size in the error data if the input buffer is too small. This allows `get_variable_boxed` to use `make_boxed`, and also makes `get_variable_size` unnecessary. Also added more info about errors to the docstrings. (Note: `variable_keys` function to come in a later commit.)
1 parent 5d4a575 commit 0afaf99

File tree

1 file changed

+141
-2
lines changed

1 file changed

+141
-2
lines changed

uefi/src/runtime.rs

+141-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,135 @@ 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 mut get_var = |buf| {
126+
get_variable(name, vendor, buf).map(|(val, attr)| {
127+
out_attr = attr;
128+
val
129+
})
130+
};
131+
#[cfg(not(feature = "unstable"))]
132+
{
133+
make_boxed(|buf| get_var(buf)).map(|val| (val, out_attr))
134+
}
135+
#[cfg(feature = "unstable")]
136+
{
137+
make_boxed(|buf| get_var(buf), Global).map(|val| (val, out_attr))
138+
}
139+
}
140+
141+
/// Sets the value of a variable. This can be used to create a new variable,
142+
/// update an existing variable, or (when the size of `data` is zero)
143+
/// delete a variable.
144+
///
145+
/// # Warnings
146+
///
147+
/// The [`Status::WARN_RESET_REQUIRED`] warning will be returned when using
148+
/// this function to transition the Secure Boot mode to setup mode or audit
149+
/// mode if the firmware requires a reboot for that operation.
150+
///
151+
/// # Errors
152+
///
153+
/// * [`Status::INVALID_PARAMETER`]: invalid attributes, name, or vendor.
154+
/// * [`Status::OUT_OF_RESOURCES`]: not enough storage is available to hold
155+
/// the variable.
156+
/// * [`Status::WRITE_PROTECTED`]: variable is read-only.
157+
/// * [`Status::SECURITY_VIOLATION`]: variable could not be written due to an
158+
/// authentication error.
159+
/// * [`Status::NOT_FOUND`]: attempted to update a non-existent variable.
160+
/// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
161+
/// after exiting boot services.
162+
pub fn set_variable(
163+
name: &CStr16,
164+
vendor: &VariableVendor,
165+
attributes: VariableAttributes,
166+
data: &[u8],
167+
) -> Result {
168+
let rt = runtime_services_raw_panicking();
169+
let rt = unsafe { rt.as_ref() };
170+
171+
unsafe {
172+
(rt.set_variable)(
173+
name.as_ptr().cast(),
174+
&vendor.0,
175+
attributes,
176+
data.len(),
177+
data.as_ptr(),
178+
)
179+
.to_result()
180+
}
181+
}
182+
183+
/// Deletes a UEFI variable.
184+
///
185+
/// # Errors
186+
///
187+
/// * [`Status::INVALID_PARAMETER`]: invalid name or vendor.
188+
/// * [`Status::WRITE_PROTECTED`]: variable is read-only.
189+
/// * [`Status::SECURITY_VIOLATION`]: variable could not be deleted due to an
190+
/// authentication error.
191+
/// * [`Status::NOT_FOUND`]: attempted to delete a non-existent variable.
192+
/// * [`Status::UNSUPPORTED`]: this platform does not support variable storage
193+
/// after exiting boot services.
194+
pub fn delete_variable(name: &CStr16, vendor: &VariableVendor) -> Result {
195+
set_variable(name, vendor, VariableAttributes::empty(), &[])
196+
}

0 commit comments

Comments
 (0)