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: 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: self
229 .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 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
243 let state::SavedState { nvram } = state;
244 self.nvram = nvram
245 .into_iter()
246 .map::<Result<(VariableKey, Variable), RestoreError>, _>(|e| {
247 Ok((
248 VariableKey {
249 vendor: e.vendor,
250 name: Ucs2LeVec::from_vec_with_nul(e.name)
251 .map_err(|e| RestoreError::InvalidSavedState(e.into()))?,
252 },
253 Variable {
254 data: e.data,
255 timestamp: e.timestamp,
256 attr: e.attr,
257 },
258 ))
259 })
260 .collect::<Result<BTreeMap<_, _>, _>>()?;
261 Ok(())
262 }
263 }
264}
265
266pub mod impl_agnostic_tests {
269 use crate::EFI_TIME;
270 use crate::NextVariable;
271 use crate::NvramStorage;
272 use guid::Guid;
273 use ucs2::Ucs2LeSlice;
274 use wchar::wchz;
275 use zerocopy::FromZeros;
276 use zerocopy::IntoBytes;
277
278 pub async fn test_single_variable(nvram: &mut dyn NvramStorage) {
279 let vendor = Guid::new_random();
280 let name = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var1").as_bytes()).unwrap();
281 let attr = 0x1234;
282 let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
283 let data1 = vec![0xa, 0xb, 0xc];
284 let timestamp = EFI_TIME::new_zeroed();
285
286 nvram
287 .set_variable(name, vendor, attr, data.clone(), timestamp)
288 .await
289 .unwrap();
290
291 let (result_attr, result_data, result_timestamp) =
292 nvram.get_variable(name, vendor).await.unwrap().unwrap();
293 assert_eq!(result_attr, attr);
294 assert_eq!(result_data, data);
295 assert_eq!(result_timestamp, timestamp);
296
297 let result = nvram.next_variable(Some((name, vendor))).await.unwrap();
298 assert!(matches!(result, NextVariable::EndOfList));
299
300 nvram
302 .set_variable(name, vendor, attr, data1.clone(), timestamp)
303 .await
304 .unwrap();
305
306 let (result_attr, result_data, result_timestamp) =
307 nvram.get_variable(name, vendor).await.unwrap().unwrap();
308 assert_eq!(result_attr, attr);
309 assert_eq!(result_data, data1);
310 assert_eq!(result_timestamp, timestamp);
311
312 nvram.remove_variable(name, vendor).await.unwrap();
313
314 let result = nvram.get_variable(name, vendor).await.unwrap();
316 assert!(result.is_none());
317 }
318
319 pub async fn test_next(nvram: &mut dyn NvramStorage) {
320 let vendor1 = Guid::new_random();
321 let name1 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var1").as_bytes()).unwrap();
322 let vendor2 = Guid::new_random();
323 let name2 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var2").as_bytes()).unwrap();
324 let vendor3 = Guid::new_random();
325 let name3 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var3").as_bytes()).unwrap();
326 let attr = 0x1234;
327 let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
328 let timestamp = EFI_TIME::new_zeroed();
329
330 nvram
331 .set_variable(name1, vendor1, attr, data.clone(), timestamp)
332 .await
333 .unwrap();
334 nvram
335 .set_variable(name2, vendor2, attr, data.clone(), timestamp)
336 .await
337 .unwrap();
338 nvram
339 .set_variable(name3, vendor3, attr, data, timestamp)
340 .await
341 .unwrap();
342
343 let mut expected = {
344 let mut s = std::collections::BTreeSet::new();
345
346 s.insert(NextVariable::Exists {
347 name: name1.to_owned(),
348 vendor: vendor1,
349 attr,
350 });
351 s.insert(NextVariable::Exists {
352 name: name2.to_owned(),
353 vendor: vendor2,
354 attr,
355 });
356 s.insert(NextVariable::Exists {
357 name: name3.to_owned(),
358 vendor: vendor3,
359 attr,
360 });
361
362 s
363 };
364
365 let mut owned_key;
366 let mut key = None;
367 loop {
368 let var = nvram.next_variable(key).await.unwrap();
369 match &var {
370 NextVariable::InvalidKey => panic!(),
371 NextVariable::EndOfList => break,
372 NextVariable::Exists {
373 name,
374 vendor,
375 attr: _,
376 } => owned_key = Some((name.clone(), *vendor)),
377 };
378
379 key = owned_key
380 .as_ref()
381 .map(|(name, vendor)| (name.as_ref(), *vendor));
382
383 let removed = expected.remove(&var);
384 assert!(removed);
385 }
386
387 assert!(expected.is_empty());
388
389 let var1 = nvram.next_variable(None).await.unwrap();
392 let var2 = nvram.next_variable(None).await.unwrap();
393 assert_eq!(var1, var2);
394
395 let key = match nvram.next_variable(None).await.unwrap() {
396 NextVariable::Exists {
397 name,
398 vendor,
399 attr: _,
400 } => Some((name, vendor)),
401 _ => panic!(),
402 };
403 let key = key.as_ref().map(|(name, vendor)| (name.as_ref(), *vendor));
404
405 let var1 = nvram.next_variable(key).await.unwrap();
406 let var2 = nvram.next_variable(key).await.unwrap();
407 assert_eq!(var1, var2);
408 }
409
410 pub async fn test_multiple_variable(nvram: &mut dyn NvramStorage) {
411 let vendor1 = Guid::new_random();
412 let name1 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var1").as_bytes()).unwrap();
413 let vendor2 = Guid::new_random();
414 let name2 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var2").as_bytes()).unwrap();
415 let vendor3 = Guid::new_random();
416 let name3 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var3").as_bytes()).unwrap();
417 let attr = 0x1234;
418 let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
419 let timestamp = EFI_TIME::new_zeroed();
420
421 nvram
423 .set_variable(name1, vendor1, attr, data.clone(), timestamp)
424 .await
425 .unwrap();
426 nvram
427 .set_variable(name2, vendor2, attr, data.clone(), timestamp)
428 .await
429 .unwrap();
430 nvram
431 .set_variable(name3, vendor3, attr, data.clone(), timestamp)
432 .await
433 .unwrap();
434
435 let (result_attr, result_data, result_timestamp) =
436 nvram.get_variable(name1, vendor1).await.unwrap().unwrap();
437 assert_eq!(result_attr, attr);
438 assert_eq!(result_data, data);
439 assert_eq!(result_timestamp, timestamp);
440
441 let (result_attr, result_data, result_timestamp) =
442 nvram.get_variable(name2, vendor2).await.unwrap().unwrap();
443 assert_eq!(result_attr, attr);
444 assert_eq!(result_data, data);
445 assert_eq!(result_timestamp, timestamp);
446
447 let (result_attr, result_data, result_timestamp) =
448 nvram.get_variable(name3, vendor3).await.unwrap().unwrap();
449 assert_eq!(result_attr, attr);
450 assert_eq!(result_data, data);
451 assert_eq!(result_timestamp, timestamp);
452
453 let appended = nvram
455 .append_variable(name1, vendor1, vec![6, 7, 8], timestamp)
456 .await
457 .unwrap();
458 assert!(appended);
459
460 let (result_attr, result_data, result_timestamp) =
461 nvram.get_variable(name1, vendor1).await.unwrap().unwrap();
462 assert_eq!(result_attr, attr);
463 assert_eq!(result_data, (1..=8).collect::<Vec<u8>>());
464 assert_eq!(result_timestamp, timestamp);
465 }
466}
467
468#[cfg(test)]
469mod tests {
470 use super::impl_agnostic_tests;
471 use super::*;
472 use pal_async::async_test;
473
474 #[async_test]
475 async fn nvram_trait_single_variable() {
476 let mut nvram = InMemoryNvram::new();
477 impl_agnostic_tests::test_single_variable(&mut nvram).await;
478 }
479
480 #[async_test]
481 async fn nvram_trait_next() {
482 let mut nvram = InMemoryNvram::new();
483 impl_agnostic_tests::test_next(&mut nvram).await;
484 }
485
486 #[async_test]
487 async fn nvram_trait_multiple_variable() {
488 let mut nvram = InMemoryNvram::new();
489 impl_agnostic_tests::test_multiple_variable(&mut nvram).await;
490 }
491}