Skip to content

Commit 55c99ad

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 55c99ad

File tree

1 file changed

+144
-2
lines changed

1 file changed

+144
-2
lines changed

uefi/src/runtime.rs

Lines changed: 144 additions & 2 deletions
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)