Skip to content

Commit 0efdadf

Browse files
committed
hcl_compat_uefi_nvram_storage: load from storage if no saved state (#1697)
Fix for a previous change that would have broken VMs that were serviced to a new version of OpenHCL with the UEFI NVRAM saved state. The new version would have skipped loading from storage since it was restoring, and the saved state would have contained an empty Vec. This change converts it to an Option<Vec<>> to distinguish between missing saved state and no NVRAM entries. If the saved state is missing, the HclCompatNvram will lazy load from storage like it did before. Original PR here: #1556 This is something that should have been caught by cross-version servicing tests, which don't exist currently. We should follow up to add that soon.
1 parent 2a4708a commit 0efdadf

File tree

5 files changed

+75
-65
lines changed

5 files changed

+75
-65
lines changed

openhcl/underhill_core/src/worker.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2074,7 +2074,6 @@ async fn new_underhill_vm(
20742074
Some(HclCompatNvramQuirks {
20752075
skip_corrupt_vars_with_missing_null_term: true,
20762076
}),
2077-
is_restoring,
20782077
)
20792078
.await?,
20802079
),

openvmm/hvlite_core/src/worker/dispatch.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1074,7 +1074,6 @@ impl InitializedVm {
10741074
.context("failed to instantiate UEFI NVRAM store")?,
10751075
),
10761076
None,
1077-
false,
10781077
)
10791078
.await?,
10801079
),

vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ pub struct HclCompatNvram<S> {
9494
// reduced allocator pressure
9595
#[cfg_attr(feature = "inspect", inspect(skip))] // internal bookkeeping - not worth inspecting
9696
nvram_buf: Vec<u8>,
97+
98+
// whether the NVRAM has been loaded, either from storage or saved state
99+
loaded: bool,
97100
}
98101

99102
/// "Quirks" to take into account when loading/storing nvram blob data.
@@ -121,9 +124,8 @@ impl<S: StorageBackend> HclCompatNvram<S> {
121124
pub async fn new(
122125
storage: S,
123126
quirks: Option<HclCompatNvramQuirks>,
124-
is_restoring: bool,
125127
) -> Result<Self, NvramStorageError> {
126-
let mut nvram = Self {
128+
let nvram = Self {
127129
quirks: quirks.unwrap_or(HclCompatNvramQuirks {
128130
skip_corrupt_vars_with_missing_null_term: false,
129131
}),
@@ -133,16 +135,14 @@ impl<S: StorageBackend> HclCompatNvram<S> {
133135
in_memory: in_memory::InMemoryNvram::new(),
134136

135137
nvram_buf: Vec::new(),
138+
139+
loaded: false,
136140
};
137-
if !is_restoring {
138-
nvram.load_from_storage().await?;
139-
}
140141
Ok(nvram)
141142
}
142143

143-
async fn load_from_storage(&mut self) -> Result<(), NvramStorageError> {
144-
tracing::info!("loading uefi nvram from storage");
145-
let res = self.load_from_storage_inner().await;
144+
async fn lazy_load_from_storage(&mut self) -> Result<(), NvramStorageError> {
145+
let res = self.lazy_load_from_storage_inner().await;
146146
if let Err(e) = &res {
147147
tracing::error!(CVM_ALLOWED, "storage contains corrupt nvram state");
148148
tracing::error!(
@@ -154,7 +154,13 @@ impl<S: StorageBackend> HclCompatNvram<S> {
154154
res
155155
}
156156

157-
async fn load_from_storage_inner(&mut self) -> Result<(), NvramStorageError> {
157+
async fn lazy_load_from_storage_inner(&mut self) -> Result<(), NvramStorageError> {
158+
if self.loaded {
159+
return Ok(());
160+
}
161+
162+
tracing::info!("loading uefi nvram from storage");
163+
158164
let nvram_buf = self
159165
.storage
160166
.restore()
@@ -293,6 +299,7 @@ impl<S: StorageBackend> HclCompatNvram<S> {
293299
));
294300
}
295301

302+
self.loaded = true;
296303
Ok(())
297304
}
298305

@@ -343,8 +350,11 @@ impl<S: StorageBackend> HclCompatNvram<S> {
343350

344351
/// Iterate over the NVRAM entries. This function asynchronously loads the
345352
/// NVRAM contents into memory from the backing storage if necessary.
346-
pub fn iter(&mut self) -> impl Iterator<Item = in_memory::VariableEntry<'_>> {
347-
self.in_memory.iter()
353+
pub async fn iter(
354+
&mut self,
355+
) -> Result<impl Iterator<Item = in_memory::VariableEntry<'_>>, NvramStorageError> {
356+
self.lazy_load_from_storage().await?;
357+
Ok(self.in_memory.iter())
348358
}
349359
}
350360

@@ -355,6 +365,8 @@ impl<S: StorageBackend> NvramStorage for HclCompatNvram<S> {
355365
name: &Ucs2LeSlice,
356366
vendor: Guid,
357367
) -> Result<Option<(u32, Vec<u8>, EFI_TIME)>, NvramStorageError> {
368+
self.lazy_load_from_storage().await?;
369+
358370
if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE {
359371
return Err(NvramStorageError::VariableNameTooLong);
360372
}
@@ -370,6 +382,8 @@ impl<S: StorageBackend> NvramStorage for HclCompatNvram<S> {
370382
data: Vec<u8>,
371383
timestamp: EFI_TIME,
372384
) -> Result<(), NvramStorageError> {
385+
self.lazy_load_from_storage().await?;
386+
373387
if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE {
374388
return Err(NvramStorageError::VariableNameTooLong);
375389
}
@@ -404,14 +418,15 @@ impl<S: StorageBackend> NvramStorage for HclCompatNvram<S> {
404418

405419
Ok(())
406420
}
407-
408421
async fn append_variable(
409422
&mut self,
410423
name: &Ucs2LeSlice,
411424
vendor: Guid,
412425
data: Vec<u8>,
413426
timestamp: EFI_TIME,
414427
) -> Result<bool, NvramStorageError> {
428+
self.lazy_load_from_storage().await?;
429+
415430
if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE {
416431
return Err(NvramStorageError::VariableNameTooLong);
417432
}
@@ -442,6 +457,8 @@ impl<S: StorageBackend> NvramStorage for HclCompatNvram<S> {
442457
name: &Ucs2LeSlice,
443458
vendor: Guid,
444459
) -> Result<bool, NvramStorageError> {
460+
self.lazy_load_from_storage().await?;
461+
445462
if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE {
446463
return Err(NvramStorageError::VariableNameTooLong);
447464
}
@@ -456,6 +473,8 @@ impl<S: StorageBackend> NvramStorage for HclCompatNvram<S> {
456473
&mut self,
457474
name_vendor: Option<(&Ucs2LeSlice, Guid)>,
458475
) -> Result<NextVariable, NvramStorageError> {
476+
self.lazy_load_from_storage().await?;
477+
459478
if let Some((name, _)) = name_vendor {
460479
if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE {
461480
return Err(NvramStorageError::VariableNameTooLong);
@@ -481,7 +500,11 @@ mod save_restore {
481500
}
482501

483502
fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
484-
self.in_memory.restore(state)
503+
if state.nvram.is_some() {
504+
self.in_memory.restore(state)?;
505+
self.loaded = true;
506+
}
507+
Ok(())
485508
}
486509
}
487510
}
@@ -516,36 +539,28 @@ mod test {
516539
#[async_test]
517540
async fn test_single_variable() {
518541
let mut storage = EphemeralStorageBackend::default();
519-
let mut nvram = HclCompatNvram::new(&mut storage, None, false)
520-
.await
521-
.unwrap();
542+
let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap();
522543
impl_agnostic_tests::test_single_variable(&mut nvram).await;
523544
}
524545

525546
#[async_test]
526547
async fn test_multiple_variable() {
527548
let mut storage = EphemeralStorageBackend::default();
528-
let mut nvram = HclCompatNvram::new(&mut storage, None, false)
529-
.await
530-
.unwrap();
549+
let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap();
531550
impl_agnostic_tests::test_multiple_variable(&mut nvram).await;
532551
}
533552

534553
#[async_test]
535554
async fn test_next() {
536555
let mut storage = EphemeralStorageBackend::default();
537-
let mut nvram = HclCompatNvram::new(&mut storage, None, false)
538-
.await
539-
.unwrap();
556+
let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap();
540557
impl_agnostic_tests::test_next(&mut nvram).await;
541558
}
542559

543560
#[async_test]
544561
async fn boundary_conditions() {
545562
let mut storage = EphemeralStorageBackend::default();
546-
let mut nvram = HclCompatNvram::new(&mut storage, None, false)
547-
.await
548-
.unwrap();
563+
let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap();
549564

550565
let vendor = Guid::new_random();
551566
let attr = 0x1234;
@@ -633,9 +648,7 @@ mod test {
633648
let data = vec![0x1, 0x2, 0x3, 0x4, 0x5];
634649
let timestamp = EFI_TIME::default();
635650

636-
let mut nvram = HclCompatNvram::new(&mut storage, None, false)
637-
.await
638-
.unwrap();
651+
let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap();
639652
nvram
640653
.set_variable(name1, vendor1, attr, data.clone(), timestamp)
641654
.await
@@ -652,9 +665,7 @@ mod test {
652665
drop(nvram);
653666

654667
// reload
655-
let mut nvram = HclCompatNvram::new(&mut storage, None, false)
656-
.await
657-
.unwrap();
668+
let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap();
658669

659670
let (result_attr, result_data, result_timestamp) =
660671
nvram.get_variable(name1, vendor1).await.unwrap().unwrap();

vm/devices/firmware/uefi_nvram_storage/src/in_memory.rs

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ mod save_restore {
216216
#[mesh(package = "firmware.in_memory_nvram")]
217217
pub struct SavedState {
218218
#[mesh(1)]
219-
pub nvram: Vec<NvramEntry>,
219+
pub nvram: Option<Vec<NvramEntry>>,
220220
}
221221
}
222222

@@ -225,39 +225,41 @@ mod save_restore {
225225

226226
fn save(&mut self) -> Result<Self::SavedState, SaveError> {
227227
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(),
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+
),
239240
})
240241
}
241242

242243
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<_, _>, _>>()?;
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+
}
261263
Ok(())
262264
}
263265
}

vm/vmgs/vmgstool/src/uefi_nvram.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ async fn dump_nvram(
144144
truncate: bool,
145145
) -> Result<(), Error> {
146146
let mut count = 0;
147-
for entry in nvram_storage.iter() {
147+
for entry in nvram_storage.iter().await? {
148148
let meta = NvramEntryMetadata {
149149
vendor: entry.vendor.to_string(),
150150
name: entry.name.to_string(),
@@ -354,7 +354,6 @@ async fn open_nvram(
354354
VmgsStorageBackend::new(vmgs, vmgs::FileId::BIOS_NVRAM, encrypted)
355355
.map_err(Error::VmgsStorageBackend)?,
356356
None,
357-
false,
358357
)
359358
.await?)
360359
}

0 commit comments

Comments
 (0)