Skip to content

Commit 013723b

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 af3d192 commit 013723b

File tree

1 file changed

+132
-2
lines changed

1 file changed

+132
-2
lines changed

uefi/src/runtime.rs

Lines changed: 132 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@
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::mem::make_boxed;
9+
use crate::{table, CStr16, Error, Result, Status, StatusExt};
1010
use core::ptr::{self, NonNull};
1111

12+
#[cfg(feature = "alloc")]
13+
use alloc::boxed::Box;
14+
1215
pub use crate::table::runtime::{Daylight, Time, TimeCapabilities, TimeError, TimeParams};
16+
pub use uefi_raw::capsule::{CapsuleBlockDescriptor, CapsuleFlags, CapsuleHeader};
17+
pub use uefi_raw::table::runtime::{ResetType, VariableAttributes, VariableVendor};
1318

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

0 commit comments

Comments
 (0)