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