uefi_nvram_storage/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Abstractions to support pluggable UEFI nvram storage backends (e.g: in memory, file backed, etc...)
5
6#![expect(missing_docs)]
7
8pub use uefi_specs::uefi::time::EFI_TIME;
9
10pub mod in_memory;
11
12use guid::Guid;
13#[cfg(feature = "inspect")]
14pub use inspect_ext::InspectableNvramStorage;
15use std::fmt::Debug;
16use thiserror::Error;
17use ucs2::Ucs2LeSlice;
18use ucs2::Ucs2LeVec;
19
20#[derive(Debug, Error)]
21pub enum NvramStorageError {
22    #[error("error deserializing nvram storage")]
23    Deserialize,
24    #[error("error loading data from Nvram storage")]
25    Load(#[source] Box<dyn std::error::Error + Send + Sync>),
26    #[error("error committing data to Nvram storage")]
27    Commit(#[source] Box<dyn std::error::Error + Send + Sync>),
28    #[error("nvram is out of space")]
29    OutOfSpace,
30    #[error("variable name too long")]
31    VariableNameTooLong,
32    #[error("variable data too long")]
33    VariableDataTooLong,
34}
35
36#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
37pub enum NextVariable {
38    InvalidKey,
39    EndOfList,
40    Exists {
41        name: Ucs2LeVec,
42        vendor: Guid,
43        attr: u32,
44    },
45}
46
47/// Abstraction over persistent nvram variable storage (e.g: in-memory,
48/// file-backed, vmgs-backed, etc.).
49///
50/// Implementors of this interface are **not required** to perform attribute
51/// validation, and should simply store/retrieve data.
52#[async_trait::async_trait]
53pub trait NvramStorage: Send + Sync {
54    /// Return the `attr` + `data` of the variable identified by `name` +
55    /// `vendor`.
56    async fn get_variable(
57        &mut self,
58        name: &Ucs2LeSlice,
59        vendor: Guid,
60    ) -> Result<Option<(u32, Vec<u8>, EFI_TIME)>, NvramStorageError>;
61
62    /// Set the value of variable identified by `name` + `vendor` to the
63    /// provided `attr` + `data`.
64    ///
65    /// This method will persist any modifications to a backing data store.
66    async fn set_variable(
67        &mut self,
68        name: &Ucs2LeSlice,
69        vendor: Guid,
70        attr: u32,
71        data: Vec<u8>,
72        timestamp: EFI_TIME,
73    ) -> Result<(), NvramStorageError>;
74
75    /// Append data to a variable identified by `name` + `vendor` from the Nvram
76    /// storage.
77    ///
78    /// Returns `true` if the variable was appended to, or `false` if it could
79    /// not be found.
80    ///
81    /// This method will persist any modifications to a backing data store.
82    async fn append_variable(
83        &mut self,
84        name: &Ucs2LeSlice,
85        vendor: Guid,
86        data: Vec<u8>,
87        timestamp: EFI_TIME,
88    ) -> Result<bool, NvramStorageError>;
89
90    /// Remove a variable identified by `name` + `vendor` from the Nvram
91    /// storage.
92    ///
93    /// Returns `true` if the variable was removed, or `false` if it could not
94    /// be found.
95    ///
96    /// This method will persist any modifications to a backing data store.
97    async fn remove_variable(
98        &mut self,
99        name: &Ucs2LeSlice,
100        vendor: Guid,
101    ) -> Result<bool, NvramStorageError>;
102
103    /// Return the variable key immediately after the variable identified by
104    /// `name` + `vendor`. If `name_vendor` is `None`, return the first
105    /// variable.
106    async fn next_variable(
107        &mut self,
108        name_vendor: Option<(&Ucs2LeSlice, Guid)>,
109    ) -> Result<NextVariable, NvramStorageError>;
110
111    /// Return `true` if the underlying store doesn't contain any vars
112    async fn is_empty(&mut self) -> Result<bool, NvramStorageError> {
113        Ok(matches!(
114            self.next_variable(None).await?,
115            NextVariable::EndOfList
116        ))
117    }
118}
119
120#[async_trait::async_trait]
121impl NvramStorage for Box<dyn NvramStorage> {
122    async fn get_variable(
123        &mut self,
124        name: &Ucs2LeSlice,
125        vendor: Guid,
126    ) -> Result<Option<(u32, Vec<u8>, EFI_TIME)>, NvramStorageError> {
127        (**self).get_variable(name, vendor).await
128    }
129
130    async fn set_variable(
131        &mut self,
132        name: &Ucs2LeSlice,
133        vendor: Guid,
134        attr: u32,
135        data: Vec<u8>,
136        timestamp: EFI_TIME,
137    ) -> Result<(), NvramStorageError> {
138        (**self)
139            .set_variable(name, vendor, attr, data, timestamp)
140            .await
141    }
142
143    async fn append_variable(
144        &mut self,
145        name: &Ucs2LeSlice,
146        vendor: Guid,
147        data: Vec<u8>,
148        timestamp: EFI_TIME,
149    ) -> Result<bool, NvramStorageError> {
150        (**self)
151            .append_variable(name, vendor, data, timestamp)
152            .await
153    }
154
155    async fn remove_variable(
156        &mut self,
157        name: &Ucs2LeSlice,
158        vendor: Guid,
159    ) -> Result<bool, NvramStorageError> {
160        (**self).remove_variable(name, vendor).await
161    }
162
163    async fn next_variable(
164        &mut self,
165        name_vendor: Option<(&Ucs2LeSlice, Guid)>,
166    ) -> Result<NextVariable, NvramStorageError> {
167        (**self).next_variable(name_vendor).await
168    }
169}
170
171/// Defines a trait that combines NvramStorage and Inspect
172#[cfg(feature = "inspect")]
173mod inspect_ext {
174    use super::*;
175    use inspect::Inspect;
176
177    /// Extends [`NvramStorage`] with a bound on [`Inspect`]
178    pub trait InspectableNvramStorage: NvramStorage + Inspect {}
179    impl<T: NvramStorage + Inspect> InspectableNvramStorage for T {}
180
181    #[async_trait::async_trait]
182    impl NvramStorage for Box<dyn InspectableNvramStorage> {
183        async fn get_variable(
184            &mut self,
185            name: &Ucs2LeSlice,
186            vendor: Guid,
187        ) -> Result<Option<(u32, Vec<u8>, EFI_TIME)>, NvramStorageError> {
188            (**self).get_variable(name, vendor).await
189        }
190
191        async fn set_variable(
192            &mut self,
193            name: &Ucs2LeSlice,
194            vendor: Guid,
195            attr: u32,
196            data: Vec<u8>,
197            timestamp: EFI_TIME,
198        ) -> Result<(), NvramStorageError> {
199            (**self)
200                .set_variable(name, vendor, attr, data, timestamp)
201                .await
202        }
203
204        async fn append_variable(
205            &mut self,
206            name: &Ucs2LeSlice,
207            vendor: Guid,
208            data: Vec<u8>,
209            timestamp: EFI_TIME,
210        ) -> Result<bool, NvramStorageError> {
211            (**self)
212                .append_variable(name, vendor, data, timestamp)
213                .await
214        }
215
216        async fn remove_variable(
217            &mut self,
218            name: &Ucs2LeSlice,
219            vendor: Guid,
220        ) -> Result<bool, NvramStorageError> {
221            (**self).remove_variable(name, vendor).await
222        }
223
224        async fn next_variable(
225            &mut self,
226            name_vendor: Option<(&Ucs2LeSlice, Guid)>,
227        ) -> Result<NextVariable, NvramStorageError> {
228            (**self).next_variable(name_vendor).await
229        }
230    }
231}