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
188pub mod impl_agnostic_tests {
191 use crate::EFI_TIME;
192 use crate::NextVariable;
193 use crate::NvramStorage;
194 use guid::Guid;
195 use ucs2::Ucs2LeSlice;
196 use wchar::wchz;
197 use zerocopy::FromZeros;
198 use zerocopy::IntoBytes;
199
200 pub async fn test_single_variable(nvram: &mut dyn NvramStorage) {
201 let vendor = Guid::new_random();
202 let name = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var1").as_bytes()).unwrap();
203 let attr = 0x1234;
204 let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
205 let data1 = vec![0xa, 0xb, 0xc];
206 let timestamp = EFI_TIME::new_zeroed();
207
208 nvram
209 .set_variable(name, vendor, attr, data.clone(), timestamp)
210 .await
211 .unwrap();
212
213 let (result_attr, result_data, result_timestamp) =
214 nvram.get_variable(name, vendor).await.unwrap().unwrap();
215 assert_eq!(result_attr, attr);
216 assert_eq!(result_data, data);
217 assert_eq!(result_timestamp, timestamp);
218
219 let result = nvram.next_variable(Some((name, vendor))).await.unwrap();
220 assert!(matches!(result, NextVariable::EndOfList));
221
222 nvram
224 .set_variable(name, vendor, attr, data1.clone(), timestamp)
225 .await
226 .unwrap();
227
228 let (result_attr, result_data, result_timestamp) =
229 nvram.get_variable(name, vendor).await.unwrap().unwrap();
230 assert_eq!(result_attr, attr);
231 assert_eq!(result_data, data1);
232 assert_eq!(result_timestamp, timestamp);
233
234 nvram.remove_variable(name, vendor).await.unwrap();
235
236 let result = nvram.get_variable(name, vendor).await.unwrap();
238 assert!(result.is_none());
239 }
240
241 pub async fn test_next(nvram: &mut dyn NvramStorage) {
242 let vendor1 = Guid::new_random();
243 let name1 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var1").as_bytes()).unwrap();
244 let vendor2 = Guid::new_random();
245 let name2 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var2").as_bytes()).unwrap();
246 let vendor3 = Guid::new_random();
247 let name3 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var3").as_bytes()).unwrap();
248 let attr = 0x1234;
249 let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
250 let timestamp = EFI_TIME::new_zeroed();
251
252 nvram
253 .set_variable(name1, vendor1, attr, data.clone(), timestamp)
254 .await
255 .unwrap();
256 nvram
257 .set_variable(name2, vendor2, attr, data.clone(), timestamp)
258 .await
259 .unwrap();
260 nvram
261 .set_variable(name3, vendor3, attr, data, timestamp)
262 .await
263 .unwrap();
264
265 let mut expected = {
266 let mut s = std::collections::BTreeSet::new();
267
268 s.insert(NextVariable::Exists {
269 name: name1.to_owned(),
270 vendor: vendor1,
271 attr,
272 });
273 s.insert(NextVariable::Exists {
274 name: name2.to_owned(),
275 vendor: vendor2,
276 attr,
277 });
278 s.insert(NextVariable::Exists {
279 name: name3.to_owned(),
280 vendor: vendor3,
281 attr,
282 });
283
284 s
285 };
286
287 let mut owned_key;
288 let mut key = None;
289 loop {
290 let var = nvram.next_variable(key).await.unwrap();
291 match &var {
292 NextVariable::InvalidKey => panic!(),
293 NextVariable::EndOfList => break,
294 NextVariable::Exists {
295 name,
296 vendor,
297 attr: _,
298 } => owned_key = Some((name.clone(), *vendor)),
299 };
300
301 key = owned_key
302 .as_ref()
303 .map(|(name, vendor)| (name.as_ref(), *vendor));
304
305 let removed = expected.remove(&var);
306 assert!(removed);
307 }
308
309 assert!(expected.is_empty());
310
311 let var1 = nvram.next_variable(None).await.unwrap();
314 let var2 = nvram.next_variable(None).await.unwrap();
315 assert_eq!(var1, var2);
316
317 let key = match nvram.next_variable(None).await.unwrap() {
318 NextVariable::Exists {
319 name,
320 vendor,
321 attr: _,
322 } => Some((name, vendor)),
323 _ => panic!(),
324 };
325 let key = key.as_ref().map(|(name, vendor)| (name.as_ref(), *vendor));
326
327 let var1 = nvram.next_variable(key).await.unwrap();
328 let var2 = nvram.next_variable(key).await.unwrap();
329 assert_eq!(var1, var2);
330 }
331
332 pub async fn test_multiple_variable(nvram: &mut dyn NvramStorage) {
333 let vendor1 = Guid::new_random();
334 let name1 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var1").as_bytes()).unwrap();
335 let vendor2 = Guid::new_random();
336 let name2 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var2").as_bytes()).unwrap();
337 let vendor3 = Guid::new_random();
338 let name3 = Ucs2LeSlice::from_slice_with_nul(wchz!(u16, "var3").as_bytes()).unwrap();
339 let attr = 0x1234;
340 let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
341 let timestamp = EFI_TIME::new_zeroed();
342
343 nvram
345 .set_variable(name1, vendor1, attr, data.clone(), timestamp)
346 .await
347 .unwrap();
348 nvram
349 .set_variable(name2, vendor2, attr, data.clone(), timestamp)
350 .await
351 .unwrap();
352 nvram
353 .set_variable(name3, vendor3, attr, data.clone(), timestamp)
354 .await
355 .unwrap();
356
357 let (result_attr, result_data, result_timestamp) =
358 nvram.get_variable(name1, vendor1).await.unwrap().unwrap();
359 assert_eq!(result_attr, attr);
360 assert_eq!(result_data, data);
361 assert_eq!(result_timestamp, timestamp);
362
363 let (result_attr, result_data, result_timestamp) =
364 nvram.get_variable(name2, vendor2).await.unwrap().unwrap();
365 assert_eq!(result_attr, attr);
366 assert_eq!(result_data, data);
367 assert_eq!(result_timestamp, timestamp);
368
369 let (result_attr, result_data, result_timestamp) =
370 nvram.get_variable(name3, vendor3).await.unwrap().unwrap();
371 assert_eq!(result_attr, attr);
372 assert_eq!(result_data, data);
373 assert_eq!(result_timestamp, timestamp);
374
375 let appended = nvram
377 .append_variable(name1, vendor1, vec![6, 7, 8], timestamp)
378 .await
379 .unwrap();
380 assert!(appended);
381
382 let (result_attr, result_data, result_timestamp) =
383 nvram.get_variable(name1, vendor1).await.unwrap().unwrap();
384 assert_eq!(result_attr, attr);
385 assert_eq!(result_data, (1..=8).collect::<Vec<u8>>());
386 assert_eq!(result_timestamp, timestamp);
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::impl_agnostic_tests;
393 use super::*;
394 use pal_async::async_test;
395
396 #[async_test]
397 async fn nvram_trait_single_variable() {
398 let mut nvram = InMemoryNvram::new();
399 impl_agnostic_tests::test_single_variable(&mut nvram).await;
400 }
401
402 #[async_test]
403 async fn nvram_trait_next() {
404 let mut nvram = InMemoryNvram::new();
405 impl_agnostic_tests::test_next(&mut nvram).await;
406 }
407
408 #[async_test]
409 async fn nvram_trait_multiple_variable() {
410 let mut nvram = InMemoryNvram::new();
411 impl_agnostic_tests::test_multiple_variable(&mut nvram).await;
412 }
413}