bootmgr_rs_core/system/
variable.rs

1//! UEFI variable storage helpers.
2//!
3//! These store a value into a UEFI variable in a custom vendor namespace.
4
5use alloc::vec::Vec;
6use tinyvec::TinyVec;
7use uefi::{
8    CStr16, Status, guid,
9    runtime::{self, VariableAttributes, VariableVendor},
10};
11
12use crate::{BootResult, error::BootError};
13
14/// The custom variable namespace for the boot manager.
15const BOOTMGR_GUID: uefi::Guid = guid!("23600d08-561e-4e68-a024-1d7d6e04ee4e");
16
17/// A trait for implementations of UEFI variable storage.
18///
19/// Usually this will use runtime services.
20trait UefiVariableStorage {
21    /// Get a variable given its name, a variable vendor, and a mutable byte slice.
22    fn get_variable<T: UefiVariable + 'static>(
23        name: &CStr16,
24        vendor: &VariableVendor,
25        buf: &mut [u8],
26    ) -> BootResult<T>;
27
28    /// Set a variable given its name, a variable vendor, variable attributes, and the chosen type.
29    fn set_variable<T: UefiVariable + 'static>(
30        name: &CStr16,
31        vendor: &VariableVendor,
32        attributes: VariableAttributes,
33        num: Option<T>,
34    ) -> BootResult<()>;
35}
36
37/// UEFI variable storage implementation with runtime services..
38struct RuntimeUefiVariableStorage;
39
40impl UefiVariableStorage for RuntimeUefiVariableStorage {
41    fn get_variable<T: UefiVariable>(
42        name: &CStr16,
43        vendor: &VariableVendor,
44        buf: &mut [u8],
45    ) -> BootResult<T> {
46        match runtime::get_variable(name, vendor, buf) {
47            Ok((var, _)) => Ok(T::from_bytes(var)),
48            Err(e) if e.status() == Status::NOT_FOUND => Ok(T::default()), // pretend that we got all zeroes if its not found
49            Err(e) => Err(BootError::Uefi(e.to_err_without_payload())),
50        }
51    }
52
53    fn set_variable<T: UefiVariable>(
54        name: &CStr16,
55        vendor: &VariableVendor,
56        attributes: VariableAttributes,
57        num: Option<T>,
58    ) -> BootResult<()> {
59        let num = num.map_or_else(|| Vec::with_capacity(0), UefiVariable::to_bytes);
60        Ok(runtime::set_variable(name, vendor, attributes, &num)?)
61    }
62}
63
64/// A value that can be stored in a UEFI variable.
65///
66/// This is essentially a type that can be converted into and from a vector of bytes. What byte ordering these bytes
67/// are in does not particularly matter, or how these bytes are encoded or decoded, as long as the method from
68/// [`UefiVariable`] is used instead of whatever type you have. It also has to be a set size.
69pub trait UefiVariable: Sized {
70    /// Convert `Self` to a vector of bytes.
71    fn to_bytes(self) -> Vec<u8>;
72
73    /// Convert a vector of bytes to `Self`.
74    fn from_bytes(bytes: &[u8]) -> Self;
75
76    /// Return 0, or an equivalent value.
77    fn default() -> Self;
78}
79
80impl UefiVariable for usize {
81    fn to_bytes(self) -> Vec<u8> {
82        self.to_le_bytes().to_vec()
83    }
84    fn from_bytes(bytes: &[u8]) -> Self {
85        let mut array = [0; size_of::<Self>()];
86        array.copy_from_slice(bytes);
87        Self::from_le_bytes(array)
88    }
89    fn default() -> Self {
90        0
91    }
92}
93
94impl UefiVariable for u64 {
95    fn to_bytes(self) -> Vec<u8> {
96        self.to_le_bytes().to_vec()
97    }
98    fn from_bytes(bytes: &[u8]) -> Self {
99        let mut array = [0; size_of::<Self>()];
100        array.copy_from_slice(bytes);
101        Self::from_le_bytes(array)
102    }
103    fn default() -> Self {
104        0
105    }
106}
107
108impl UefiVariable for u32 {
109    fn to_bytes(self) -> Vec<u8> {
110        self.to_le_bytes().to_vec()
111    }
112    fn from_bytes(bytes: &[u8]) -> Self {
113        let mut array = [0; size_of::<Self>()];
114        array.copy_from_slice(bytes);
115        Self::from_le_bytes(array)
116    }
117    fn default() -> Self {
118        0
119    }
120}
121
122impl UefiVariable for u16 {
123    fn to_bytes(self) -> Vec<u8> {
124        self.to_le_bytes().to_vec()
125    }
126    fn from_bytes(bytes: &[u8]) -> Self {
127        let mut array = [0; size_of::<Self>()];
128        array.copy_from_slice(bytes);
129        Self::from_le_bytes(array)
130    }
131    fn default() -> Self {
132        0
133    }
134}
135
136impl UefiVariable for u8 {
137    fn to_bytes(self) -> Vec<u8> {
138        self.to_le_bytes().to_vec()
139    }
140    fn from_bytes(bytes: &[u8]) -> Self {
141        let mut array = [0; size_of::<Self>()];
142        array.copy_from_slice(bytes);
143        Self::from_le_bytes(array)
144    }
145    fn default() -> Self {
146        0
147    }
148}
149
150/// Sets a UEFI variable to a [`UefiVariable`] given the name.
151///
152/// If None is specified for the vendor, then the variable will be searched for in a custom GUID space,
153/// not the global variables vendor space. In other words, unless you are storing your own variables,
154/// it may not be what you expect.
155///
156/// This custom namespace is accessible at GUID `23600d08-561e-4e68-a024-1d7d6e04ee4e`.
157///
158/// Passing None for num will result in the variable being deleted.
159///
160/// # Errors
161///
162/// May return an `Error` for many reasons, see [`runtime::set_variable`]
163pub fn set_variable<T: UefiVariable + 'static>(
164    name: &CStr16,
165    vendor: Option<VariableVendor>,
166    attrs: Option<VariableAttributes>,
167    num: Option<T>,
168) -> BootResult<()> {
169    let vendor = vendor.unwrap_or(runtime::VariableVendor(BOOTMGR_GUID));
170    let attrs = attrs.map_or_else(
171        || VariableAttributes::NON_VOLATILE | VariableAttributes::BOOTSERVICE_ACCESS,
172        |x| x,
173    );
174    RuntimeUefiVariableStorage::set_variable(name, &vendor, attrs, num)
175}
176
177/// Gets a UEFI variable of a [`UefiVariable`] given the name
178///
179/// If None is specified for the vendor, then the variable will be searched for in a custom GUID space,
180/// not the global variables vendor space. In other words, unless you are storing your own variables,
181/// it may not be what you expect.
182///
183/// This custom namespace is accessible at GUID `23600d08-561e-4e68-a024-1d7d6e04ee4e`.
184///
185/// If the variable was not found, a default value of `0` will be returned. This is more convenient to handle
186/// internally as its easier to not handle specially the case of the variable not being found.
187///
188/// # Errors
189///
190/// May return an `Error` for many reasons, see [`runtime::get_variable`]
191pub fn get_variable<T: UefiVariable + 'static>(
192    name: &CStr16,
193    vendor: Option<VariableVendor>,
194) -> BootResult<T> {
195    let mut buf: TinyVec<[_; size_of::<u64>()]> = TinyVec::with_capacity(size_of::<T>()); // have a max capacity of u64, the largest type
196    let vendor = vendor.unwrap_or(runtime::VariableVendor(BOOTMGR_GUID));
197    RuntimeUefiVariableStorage::get_variable(name, &vendor, &mut buf)
198}