1use crate::EFI_TIME;
8use crate::NextVariable;
9use crate::NvramStorage;
10use crate::NvramStorageError;
11use guid::Guid;
12use std::collections::BTreeMap;
13use std::fmt::Display;
14use ucs2::Ucs2LeSlice;
15use ucs2::Ucs2LeVec;
16
17#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
18struct VariableKey {
19 vendor: Guid,
20 name: Ucs2LeVec,
21}
22
23impl Display for VariableKey {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{}-{}", self.vendor, self.name)
26 }
27}
28
29#[derive(Clone, Debug)]
30#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
31struct Variable {
32 data: Vec<u8>,
33 timestamp: EFI_TIME,
34 #[cfg_attr(feature = "inspect", inspect(hex))]
35 attr: u32,
36}
37
38#[derive(Debug)]
40#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
41pub struct InMemoryNvram {
42 #[cfg_attr(feature = "inspect", inspect(iter_by_key))]
43 nvram: BTreeMap<VariableKey, Variable>,
44}
45
46pub struct VariableEntry<'a> {
47 pub vendor: Guid,
48 pub name: &'a Ucs2LeSlice,
49 pub data: &'a [u8],
50 pub timestamp: EFI_TIME,
51 pub attr: u32,
52}
53
54impl InMemoryNvram {
55 pub fn new() -> Self {
56 Self {
57 nvram: Default::default(),
58 }
59 }
60
61 pub fn iter(&self) -> impl Iterator<Item = VariableEntry<'_>> {
62 self.nvram.iter().map(|(k, v)| VariableEntry {
63 vendor: k.vendor,
64 name: k.name.as_ref(),
65 data: v.data.as_slice(),
66 timestamp: v.timestamp,
67 attr: v.attr,
68 })
69 }
70
71 pub fn clear(&mut self) {
72 self.nvram.clear()
73 }
74}
75
76#[async_trait::async_trait]
77impl NvramStorage for InMemoryNvram {
78 async fn get_variable(
79 &mut self,
80 name: &Ucs2LeSlice,
81 vendor: Guid,
82 ) -> Result<Option<(u32, Vec<u8>, EFI_TIME)>, NvramStorageError> {
83 Ok(self
84 .nvram
85 .get(&VariableKey {
86 vendor,
87 name: name.to_ucs2_le_vec(),
88 })
89 .map(|v| (v.attr, v.data.clone(), v.timestamp)))
90 }
91
92 async fn set_variable(
93 &mut self,
94 name: &Ucs2LeSlice,
95 vendor: Guid,
96 attr: u32,
97 data: Vec<u8>,
98 timestamp: EFI_TIME,
99 ) -> Result<(), NvramStorageError> {
100 self.nvram.insert(
101 VariableKey {
102 vendor,
103 name: name.to_ucs2_le_vec(),
104 },
105 Variable {
106 data,
107 timestamp,
108 attr,
109 },
110 );
111 Ok(())
112 }
113
114 async fn append_variable(
115 &mut self,
116 name: &Ucs2LeSlice,
117 vendor: Guid,
118 data: Vec<u8>,
119 timestamp: EFI_TIME,
120 ) -> Result<bool, NvramStorageError> {
121 match self.nvram.get_mut(&VariableKey {
122 vendor,
123 name: name.to_ucs2_le_vec(),
124 }) {
125 Some(val) => {
126 val.data.extend_from_slice(&data);
127 val.timestamp = timestamp;
128 Ok(true)
129 }
130 None => Ok(false),
131 }
132 }
133
134 async fn remove_variable(
135 &mut self,
136 name: &Ucs2LeSlice,
137 vendor: Guid,
138 ) -> Result<bool, NvramStorageError> {
139 Ok(self
140 .nvram
141 .remove(&VariableKey {
142 vendor,
143 name: name.to_ucs2_le_vec(),
144 })
145 .is_some())
146 }
147
148 async fn next_variable(
149 &mut self,
150 name_vendor: Option<(&Ucs2LeSlice, Guid)>,
151 ) -> Result<NextVariable, NvramStorageError> {
152 let key = &name_vendor.map(|(name, vendor)| VariableKey {
153 vendor,
154 name: name.to_ucs2_le_vec(),
155 });
156
157 if let Some(key) = key {
158 let mut range = self.nvram.range(key..);
159 if let Some((found_key, _)) = range.next() {
160 if found_key == key {
161 Ok(match range.next() {
162 Some(v) => NextVariable::Exists {
163 name: v.0.name.clone(),
164 vendor: v.0.vendor,
165 attr: v.1.attr,
166 },
167 None => NextVariable::EndOfList,
168 })
169 } else {
170 Ok(NextVariable::InvalidKey)
171 }
172 } else {
173 Ok(NextVariable::EndOfList)
174 }
175 } else {
176 Ok(match self.nvram.iter().next() {
177 Some(v) => NextVariable::Exists {
178 name: v.0.name.clone(),
179 vendor: v.0.vendor,
180 attr: v.1.attr,
181 },
182 None => NextVariable::EndOfList,
183 })
184 }
185 }
186}
187
188#[cfg(feature = "save_restore")]
189mod save_restore {
190 use super::*;
191 use vmcore::save_restore::RestoreError;
192 use vmcore::save_restore::SaveError;
193 use vmcore::save_restore::SaveRestore;
194
195 mod state {
196 use guid::Guid;
197 use uefi_specs::uefi::time::EFI_TIME;
198 use vmcore::save_restore::SavedStateRoot;
199
200 #[derive(mesh_protobuf::Protobuf)]
201 #[mesh(package = "nvram_entry")]
202 pub struct NvramEntry {
203 #[mesh(1)]
204 pub vendor: Guid,
205 #[mesh(2)]
206 pub name: Vec<u8>,
207 #[mesh(3)]
208 pub data: Vec<u8>,
209 #[mesh(4, encoding = "mesh_protobuf::encoding::ZeroCopyEncoding")]
210 pub timestamp: EFI_TIME,
211 #[mesh(5)]
212 pub attr: u32,
213 }
214
215 #[derive(mesh_protobuf::Protobuf, SavedStateRoot)]
216 #[mesh(package = "firmware.in_memory_nvram")]
217 pub struct SavedState {
218 #[mesh(1)]
219 pub nvram: Option<Vec<NvramEntry>>,
220 }
221 }
222
223 impl SaveRestore for InMemoryNvram {
224 type SavedState = state::SavedState;
225
226 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
227 Ok(state::SavedState {
228 nvram: Some(
229 self.nvram
230 .iter()
231 .map(|(k, v)| state::NvramEntry {
232 vendor: k.vendor,
233 name: k.name.clone().into_inner(),
234 data: v.data.clone(),
235 timestamp: v.timestamp,
236 attr: v.attr,
237 })
238 .collect(),
239 ),
240 })
241 }
242
243 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
244 if let state::SavedState { nvram: Some(nvram) } = state {
245 self.nvram = nvram
246 .into_iter()
247 .map::<Result<(VariableKey, Variable), RestoreError>, _>(|e| {
248 Ok((
249 VariableKey {
250 vendor: e.vendor,
251 name: Ucs2LeVec::from_vec_with_nul(e.name)
252 .map_err(|e| RestoreError::InvalidSavedState(e.into()))?,
253 },
254 Variable {
255 data: e.data,
256 timestamp: e.timestamp,
257 attr: e.attr,
258 },
259 ))
260 })
261 .collect::<Result<BTreeMap<_, _>, _>>()?;
262 }
263 Ok(())
264 }
265 }
266}
267
268pub mod impl_agnostic_tests {
271 use crate::EFI_TIME;
272 use crate::NextVariable;
273 use crate::NvramStorage;
274 use guid::Guid;
275 use ucs2::Ucs2LeSlice;
276 use wchar::wchz;
277 use zerocopy::FromZeros;
278 use zerocopy::IntoBytes;
279
280 pub async fn test_single_variable(nvram: &mut dyn NvramStorage) {
281 let vendor = Guid::new_random();
282 let name = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var1").as_bytes()).unwrap();
283 let attr = 0x1234;
284 let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
285 let data1 = vec![0xa, 0xb, 0xc];
286 let timestamp = EFI_TIME::new_zeroed();
287
288 nvram
289 .set_variable(name, vendor, attr, data.clone(), timestamp)
290 .await
291 .unwrap();
292
293 let (result_attr, result_data, result_timestamp) =
294 nvram.get_variable(name, vendor).await.unwrap().unwrap();
295 assert_eq!(result_attr, attr);
296 assert_eq!(result_data, data);
297 assert_eq!(result_timestamp, timestamp);
298
299 let result = nvram.next_variable(Some((name, vendor))).await.unwrap();
300 assert!(matches!(result, NextVariable::EndOfList));
301
302 nvram
304 .set_variable(name, vendor, attr, data1.clone(), timestamp)
305 .await
306 .unwrap();
307
308 let (result_attr, result_data, result_timestamp) =
309 nvram.get_variable(name, vendor).await.unwrap().unwrap();
310 assert_eq!(result_attr, attr);
311 assert_eq!(result_data, data1);
312 assert_eq!(result_timestamp, timestamp);
313
314 nvram.remove_variable(name, vendor).await.unwrap();
315
316 let result = nvram.get_variable(name, vendor).await.unwrap();
318 assert!(result.is_none());
319 }
320
321 pub async fn test_next(nvram: &mut dyn NvramStorage) {
322 let vendor1 = Guid::new_random();
323 let name1 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var1").as_bytes()).unwrap();
324 let vendor2 = Guid::new_random();
325 let name2 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var2").as_bytes()).unwrap();
326 let vendor3 = Guid::new_random();
327 let name3 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var3").as_bytes()).unwrap();
328 let attr = 0x1234;
329 let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
330 let timestamp = EFI_TIME::new_zeroed();
331
332 nvram
333 .set_variable(name1, vendor1, attr, data.clone(), timestamp)
334 .await
335 .unwrap();
336 nvram
337 .set_variable(name2, vendor2, attr, data.clone(), timestamp)
338 .await
339 .unwrap();
340 nvram
341 .set_variable(name3, vendor3, attr, data, timestamp)
342 .await
343 .unwrap();
344
345 let mut expected = {
346 let mut s = std::collections::BTreeSet::new();
347
348 s.insert(NextVariable::Exists {
349 name: name1.to_owned(),
350 vendor: vendor1,
351 attr,
352 });
353 s.insert(NextVariable::Exists {
354 name: name2.to_owned(),
355 vendor: vendor2,
356 attr,
357 });
358 s.insert(NextVariable::Exists {
359 name: name3.to_owned(),
360 vendor: vendor3,
361 attr,
362 });
363
364 s
365 };
366
367 let mut owned_key;
368 let mut key = None;
369 loop {
370 let var = nvram.next_variable(key).await.unwrap();
371 match &var {
372 NextVariable::InvalidKey => panic!(),
373 NextVariable::EndOfList => break,
374 NextVariable::Exists {
375 name,
376 vendor,
377 attr: _,
378 } => owned_key = Some((name.clone(), *vendor)),
379 };
380
381 key = owned_key
382 .as_ref()
383 .map(|(name, vendor)| (name.as_ref(), *vendor));
384
385 let removed = expected.remove(&var);
386 assert!(removed);
387 }
388
389 assert!(expected.is_empty());
390
391 let var1 = nvram.next_variable(None).await.unwrap();
394 let var2 = nvram.next_variable(None).await.unwrap();
395 assert_eq!(var1, var2);
396
397 let key = match nvram.next_variable(None).await.unwrap() {
398 NextVariable::Exists {
399 name,
400 vendor,
401 attr: _,
402 } => Some((name, vendor)),
403 _ => panic!(),
404 };
405 let key = key.as_ref().map(|(name, vendor)| (name.as_ref(), *vendor));
406
407 let var1 = nvram.next_variable(key).await.unwrap();
408 let var2 = nvram.next_variable(key).await.unwrap();
409 assert_eq!(var1, var2);
410 }
411
412 pub async fn test_multiple_variable(nvram: &mut dyn NvramStorage) {
413 let vendor1 = Guid::new_random();
414 let name1 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var1").as_bytes()).unwrap();
415 let vendor2 = Guid::new_random();
416 let name2 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var2").as_bytes()).unwrap();
417 let vendor3 = Guid::new_random();
418 let name3 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var3").as_bytes()).unwrap();
419 let attr = 0x1234;
420 let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
421 let timestamp = EFI_TIME::new_zeroed();
422
423 nvram
425 .set_variable(name1, vendor1, attr, data.clone(), timestamp)
426 .await
427 .unwrap();
428 nvram
429 .set_variable(name2, vendor2, attr, data.clone(), timestamp)
430 .await
431 .unwrap();
432 nvram
433 .set_variable(name3, vendor3, attr, data.clone(), timestamp)
434 .await
435 .unwrap();
436
437 let (result_attr, result_data, result_timestamp) =
438 nvram.get_variable(name1, vendor1).await.unwrap().unwrap();
439 assert_eq!(result_attr, attr);
440 assert_eq!(result_data, data);
441 assert_eq!(result_timestamp, timestamp);
442
443 let (result_attr, result_data, result_timestamp) =
444 nvram.get_variable(name2, vendor2).await.unwrap().unwrap();
445 assert_eq!(result_attr, attr);
446 assert_eq!(result_data, data);
447 assert_eq!(result_timestamp, timestamp);
448
449 let (result_attr, result_data, result_timestamp) =
450 nvram.get_variable(name3, vendor3).await.unwrap().unwrap();
451 assert_eq!(result_attr, attr);
452 assert_eq!(result_data, data);
453 assert_eq!(result_timestamp, timestamp);
454
455 let appended = nvram
457 .append_variable(name1, vendor1, vec![6, 7, 8], timestamp)
458 .await
459 .unwrap();
460 assert!(appended);
461
462 let (result_attr, result_data, result_timestamp) =
463 nvram.get_variable(name1, vendor1).await.unwrap().unwrap();
464 assert_eq!(result_attr, attr);
465 assert_eq!(result_data, (1..=8).collect::<Vec<u8>>());
466 assert_eq!(result_timestamp, timestamp);
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::impl_agnostic_tests;
473 use super::*;
474 use pal_async::async_test;
475
476 #[async_test]
477 async fn nvram_trait_single_variable() {
478 let mut nvram = InMemoryNvram::new();
479 impl_agnostic_tests::test_single_variable(&mut nvram).await;
480 }
481
482 #[async_test]
483 async fn nvram_trait_next() {
484 let mut nvram = InMemoryNvram::new();
485 impl_agnostic_tests::test_next(&mut nvram).await;
486 }
487
488 #[async_test]
489 async fn nvram_trait_multiple_variable() {
490 let mut nvram = InMemoryNvram::new();
491 impl_agnostic_tests::test_multiple_variable(&mut nvram).await;
492 }
493}