From 2a4708a15d562b63b017003b38b51d3cd6458216 Mon Sep 17 00:00:00 2001 From: Trevor Jones Date: Wed, 25 Jun 2025 15:49:02 -0700 Subject: [PATCH 1/3] uefi_nvram_storage: save restore nvram contents (#1556) Implement save/restore for HclCompatNvram/NvramStorage/InMemoryNvram so that OpenHCL doesn't need to re-read from the VMGS file after a servicing or other save-restore operation. This will help avoid panics if the VMGS file is not accessible in certain VM teardown cases. --- Cargo.lock | 3 + openhcl/underhill_core/Cargo.toml | 2 +- openhcl/underhill_core/src/worker.rs | 24 +++--- openvmm/hvlite_core/Cargo.toml | 4 +- openvmm/hvlite_core/src/worker/dispatch.rs | 18 ++-- vm/devices/firmware/firmware_uefi/Cargo.toml | 2 +- vm/devices/firmware/firmware_uefi/src/lib.rs | 4 +- .../firmware_uefi/src/service/nvram/mod.rs | 11 ++- .../src/service/nvram/spec_services/mod.rs | 21 +++-- .../nvram/spec_services/nvram_services_ext.rs | 4 +- .../hcl_compat_uefi_nvram_storage/Cargo.toml | 2 + .../hcl_compat_uefi_nvram_storage/src/lib.rs | 85 ++++++++++++------- .../firmware/uefi_nvram_storage/Cargo.toml | 3 + .../uefi_nvram_storage/src/in_memory.rs | 78 +++++++++++++++++ .../firmware/uefi_nvram_storage/src/lib.rs | 41 +++++++-- vm/vmgs/vmgstool/src/uefi_nvram.rs | 17 ++-- vmm_core/vmotherboard/src/base_chipset.rs | 2 +- 17 files changed, 237 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 174e48d921..3237343f0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2799,6 +2799,7 @@ dependencies = [ "tracing", "ucs2 0.0.0", "uefi_nvram_storage", + "vmcore", "wchar", "zerocopy 0.8.24", ] @@ -7276,10 +7277,12 @@ dependencies = [ "async-trait", "guid", "inspect", + "mesh_protobuf", "pal_async", "thiserror 2.0.12", "ucs2 0.0.0", "uefi_specs", + "vmcore", "wchar", "zerocopy 0.8.24", ] diff --git a/openhcl/underhill_core/Cargo.toml b/openhcl/underhill_core/Cargo.toml index 06ad516633..8936b56092 100644 --- a/openhcl/underhill_core/Cargo.toml +++ b/openhcl/underhill_core/Cargo.toml @@ -56,7 +56,7 @@ hyperv_ic_resources.workspace = true hyperv_secure_boot_templates.workspace = true hyperv_uefi_custom_vars_json.workspace = true framebuffer.workspace = true -hcl_compat_uefi_nvram_storage = { workspace = true, features = ["inspect"] } +hcl_compat_uefi_nvram_storage = { workspace = true, features = ["inspect", "save_restore"] } get_helpers.workspace = true get_protocol.workspace = true guest_emulation_transport.workspace = true diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index 78341e6f20..17eb4e1a75 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -2064,16 +2064,20 @@ async fn new_underhill_vm( logger: Box::new(UnderhillLogger { get: get_client.clone(), }), - nvram_storage: Box::new(HclCompatNvram::new( - VmgsStorageBackendAdapter( - vmgs_client - .as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true) - .context("failed to instantiate UEFI NVRAM store")?, - ), - Some(HclCompatNvramQuirks { - skip_corrupt_vars_with_missing_null_term: true, - }), - )), + nvram_storage: Box::new( + HclCompatNvram::new( + VmgsStorageBackendAdapter( + vmgs_client + .as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true) + .context("failed to instantiate UEFI NVRAM store")?, + ), + Some(HclCompatNvramQuirks { + skip_corrupt_vars_with_missing_null_term: true, + }), + is_restoring, + ) + .await?, + ), generation_id_recv: get_client .take_generation_id_recv() .await diff --git a/openvmm/hvlite_core/Cargo.toml b/openvmm/hvlite_core/Cargo.toml index e80b283b44..75caf4e562 100644 --- a/openvmm/hvlite_core/Cargo.toml +++ b/openvmm/hvlite_core/Cargo.toml @@ -52,10 +52,10 @@ disk_backend.workspace = true firmware_pcat.workspace = true firmware_uefi_custom_vars.workspace = true firmware_uefi.workspace = true -uefi_nvram_storage.workspace = true +uefi_nvram_storage = { workspace = true, features = ["save_restore"] } framebuffer.workspace = true get_resources.workspace = true -hcl_compat_uefi_nvram_storage = { workspace = true, features = ["inspect"] } +hcl_compat_uefi_nvram_storage = { workspace = true, features = ["inspect", "save_restore"] } ide.workspace = true floppy.workspace = true input_core.workspace = true diff --git a/openvmm/hvlite_core/src/worker/dispatch.rs b/openvmm/hvlite_core/src/worker/dispatch.rs index ea2ae5472c..7545ee8107 100644 --- a/openvmm/hvlite_core/src/worker/dispatch.rs +++ b/openvmm/hvlite_core/src/worker/dispatch.rs @@ -1067,13 +1067,17 @@ impl InitializedVm { use vmm_core::emuplat::hcl_compat_uefi_nvram_storage::VmgsStorageBackendAdapter; match vmgs_client { - Some(vmgs) => Box::new(HclCompatNvram::new( - VmgsStorageBackendAdapter( - vmgs.as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true) - .context("failed to instantiate UEFI NVRAM store")?, - ), - None, - )), + Some(vmgs) => Box::new( + HclCompatNvram::new( + VmgsStorageBackendAdapter( + vmgs.as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true) + .context("failed to instantiate UEFI NVRAM store")?, + ), + None, + false, + ) + .await?, + ), None => Box::new(InMemoryNvram::new()), } }, diff --git a/vm/devices/firmware/firmware_uefi/Cargo.toml b/vm/devices/firmware/firmware_uefi/Cargo.toml index dca17b1d6d..9be74decd0 100644 --- a/vm/devices/firmware/firmware_uefi/Cargo.toml +++ b/vm/devices/firmware/firmware_uefi/Cargo.toml @@ -22,7 +22,7 @@ fuzzing = [] [dependencies] firmware_uefi_custom_vars.workspace = true -uefi_nvram_storage = { workspace = true, features = ["inspect"] } +uefi_nvram_storage = { workspace = true, features = ["inspect", "save_restore"] } uefi_specs.workspace = true uefi_nvram_specvars.workspace = true generation_id.workspace = true diff --git a/vm/devices/firmware/firmware_uefi/src/lib.rs b/vm/devices/firmware/firmware_uefi/src/lib.rs index 93529c4f31..82273ac8e5 100644 --- a/vm/devices/firmware/firmware_uefi/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi/src/lib.rs @@ -74,7 +74,7 @@ use std::convert::TryInto; use std::ops::RangeInclusive; use std::task::Context; use thiserror::Error; -use uefi_nvram_storage::InspectableNvramStorage; +use uefi_nvram_storage::VmmNvramStorage; use vmcore::device_state::ChangeDeviceState; use vmcore::vmtime::VmTimeSource; use watchdog_core::platform::WatchdogPlatform; @@ -129,7 +129,7 @@ pub struct UefiConfig { /// Various runtime objects used by the UEFI device + underlying services. pub struct UefiRuntimeDeps<'a> { pub gm: GuestMemory, - pub nvram_storage: Box, + pub nvram_storage: Box, pub logger: Box, pub vmtime: &'a VmTimeSource, pub watchdog_platform: Box, diff --git a/vm/devices/firmware/firmware_uefi/src/service/nvram/mod.rs b/vm/devices/firmware/firmware_uefi/src/service/nvram/mod.rs index a438b3af4a..d6e8b4ee99 100644 --- a/vm/devices/firmware/firmware_uefi/src/service/nvram/mod.rs +++ b/vm/devices/firmware/firmware_uefi/src/service/nvram/mod.rs @@ -24,7 +24,7 @@ use inspect::Inspect; use std::borrow::Cow; use std::fmt::Debug; use thiserror::Error; -use uefi_nvram_storage::InspectableNvramStorage; +use uefi_nvram_storage::VmmNvramStorage; use uefi_specs::uefi::common::EfiStatus; use uefi_specs::uefi::nvram::EfiVariableAttributes; use zerocopy::IntoBytes; @@ -67,12 +67,12 @@ pub struct NvramServices { // Sub-emulators #[inspect(flatten)] - services: NvramSpecServices>, + services: NvramSpecServices>, } impl NvramServices { pub async fn new( - nvram_storage: Box, + nvram_storage: Box, custom_vars: CustomVars, secure_boot_enabled: bool, vsm_config: Option>, @@ -622,15 +622,14 @@ mod save_restore { mod state { use crate::service::nvram::NvramSpecServices; use mesh::payload::Protobuf; - use uefi_nvram_storage::InspectableNvramStorage; + use uefi_nvram_storage::VmmNvramStorage; use vmcore::save_restore::SaveRestore; #[derive(Protobuf)] #[mesh(package = "firmware.uefi.nvram")] pub struct SavedState { #[mesh(1)] - pub services: - > as SaveRestore>::SavedState, + pub services: > as SaveRestore>::SavedState, } } diff --git a/vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/mod.rs b/vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/mod.rs index ac2459c7b2..58503a7c38 100644 --- a/vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/mod.rs +++ b/vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/mod.rs @@ -23,9 +23,9 @@ use ucs2::Ucs2LeSlice; use ucs2::Ucs2ParseError; use uefi_nvram_specvars::signature_list; use uefi_nvram_specvars::signature_list::ParseSignatureLists; -use uefi_nvram_storage::InspectableNvramStorage; use uefi_nvram_storage::NextVariable; use uefi_nvram_storage::NvramStorageError; +use uefi_nvram_storage::VmmNvramStorage; use uefi_specs::uefi::common::EfiStatus; use uefi_specs::uefi::nvram::EfiVariableAttributes; use uefi_specs::uefi::time::EFI_TIME; @@ -224,12 +224,12 @@ impl RuntimeState { /// `NvramError` type provides additional context as to what error occurred in /// OpenVMM (i.e: for logging purposes). #[derive(Debug, Inspect)] -pub struct NvramSpecServices { +pub struct NvramSpecServices { storage: S, runtime_state: RuntimeState, } -impl NvramSpecServices { +impl NvramSpecServices { /// Construct a new NvramServices instance from an existing storage backend. pub fn new(storage: S) -> NvramSpecServices { NvramSpecServices { @@ -1453,6 +1453,8 @@ mod save_restore { mod state { use mesh::payload::Protobuf; + use uefi_nvram_storage::in_memory::InMemoryNvram; + use vmcore::save_restore::SaveRestore; #[derive(Protobuf)] #[mesh(package = "firmware.uefi.nvram.spec")] @@ -1470,10 +1472,12 @@ mod save_restore { pub struct SavedState { #[mesh(1)] pub runtime_state: SavedRuntimeState, + #[mesh(2)] + pub storage: ::SavedState, } } - impl SaveRestore for NvramSpecServices { + impl SaveRestore for NvramSpecServices { type SavedState = state::SavedState; fn save(&mut self) -> Result { @@ -1483,17 +1487,22 @@ mod save_restore { RuntimeState::Boot => state::SavedRuntimeState::Boot, RuntimeState::Runtime => state::SavedRuntimeState::Runtime, }, + storage: self.storage.save()?, }) } fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> { - let state::SavedState { runtime_state } = state; + let state::SavedState { + runtime_state, + storage, + } = state; self.runtime_state = match runtime_state { state::SavedRuntimeState::PreBoot => RuntimeState::PreBoot, state::SavedRuntimeState::Boot => RuntimeState::Boot, state::SavedRuntimeState::Runtime => RuntimeState::Runtime, }; + self.storage.restore(storage)?; Ok(()) } @@ -1526,7 +1535,7 @@ mod test { } #[async_trait::async_trait] - impl NvramServicesTestExt for NvramSpecServices { + impl NvramServicesTestExt for NvramSpecServices { async fn set_test_var(&mut self, name: &[u8], attr: u32, data: &[u8]) -> NvramResult<()> { let vendor = Guid::default(); diff --git a/vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/nvram_services_ext.rs b/vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/nvram_services_ext.rs index 9ac393600a..422cbc5528 100644 --- a/vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/nvram_services_ext.rs +++ b/vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/nvram_services_ext.rs @@ -6,7 +6,7 @@ use super::NvramResult; use super::NvramSpecServices; use guid::Guid; use ucs2::Ucs2LeSlice; -use uefi_nvram_storage::InspectableNvramStorage; +use uefi_nvram_storage::VmmNvramStorage; use uefi_specs::uefi::common::EfiStatus; /// Extension trait around `NvramServices` that makes it easier to use the API @@ -56,7 +56,7 @@ pub trait NvramServicesExt { } #[async_trait::async_trait] -impl NvramServicesExt for NvramSpecServices { +impl NvramServicesExt for NvramSpecServices { async fn get_variable( &mut self, vendor: Guid, diff --git a/vm/devices/firmware/hcl_compat_uefi_nvram_storage/Cargo.toml b/vm/devices/firmware/hcl_compat_uefi_nvram_storage/Cargo.toml index 31f3e4814d..38ba42c6a3 100644 --- a/vm/devices/firmware/hcl_compat_uefi_nvram_storage/Cargo.toml +++ b/vm/devices/firmware/hcl_compat_uefi_nvram_storage/Cargo.toml @@ -10,9 +10,11 @@ rust-version.workspace = true default = [] inspect = ["dep:inspect", "uefi_nvram_storage/inspect"] +save_restore = [ "dep:vmcore", "uefi_nvram_storage/save_restore"] [dependencies] uefi_nvram_storage.workspace = true +vmcore = { workspace = true, optional = true } cvm_tracing.workspace = true guid.workspace = true diff --git a/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs b/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs index a4db5ba2b2..96bf6cfbe8 100644 --- a/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs +++ b/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs @@ -118,8 +118,12 @@ pub struct HclCompatNvramQuirks { impl HclCompatNvram { /// Create a new [`HclCompatNvram`] - pub fn new(storage: S, quirks: Option) -> Self { - Self { + pub async fn new( + storage: S, + quirks: Option, + is_restoring: bool, + ) -> Result { + let mut nvram = Self { quirks: quirks.unwrap_or(HclCompatNvramQuirks { skip_corrupt_vars_with_missing_null_term: false, }), @@ -129,11 +133,16 @@ impl HclCompatNvram { in_memory: in_memory::InMemoryNvram::new(), nvram_buf: Vec::new(), + }; + if !is_restoring { + nvram.load_from_storage().await?; } + Ok(nvram) } - async fn lazy_load_from_storage(&mut self) -> Result<(), NvramStorageError> { - let res = self.lazy_load_from_storage_inner().await; + async fn load_from_storage(&mut self) -> Result<(), NvramStorageError> { + tracing::info!("loading uefi nvram from storage"); + let res = self.load_from_storage_inner().await; if let Err(e) = &res { tracing::error!(CVM_ALLOWED, "storage contains corrupt nvram state"); tracing::error!( @@ -145,11 +154,7 @@ impl HclCompatNvram { res } - async fn lazy_load_from_storage_inner(&mut self) -> Result<(), NvramStorageError> { - if !self.nvram_buf.is_empty() { - return Ok(()); - } - + async fn load_from_storage_inner(&mut self) -> Result<(), NvramStorageError> { let nvram_buf = self .storage .restore() @@ -293,6 +298,7 @@ impl HclCompatNvram { /// Dump in-memory nvram to the underlying storage device. async fn flush_storage(&mut self) -> Result<(), NvramStorageError> { + tracing::info!("flushing uefi nvram to storage"); self.nvram_buf.clear(); for in_memory::VariableEntry { @@ -337,11 +343,8 @@ impl HclCompatNvram { /// Iterate over the NVRAM entries. This function asynchronously loads the /// NVRAM contents into memory from the backing storage if necessary. - pub async fn iter( - &mut self, - ) -> Result>, NvramStorageError> { - self.lazy_load_from_storage().await?; - Ok(self.in_memory.iter()) + pub fn iter(&mut self) -> impl Iterator> { + self.in_memory.iter() } } @@ -352,8 +355,6 @@ impl NvramStorage for HclCompatNvram { name: &Ucs2LeSlice, vendor: Guid, ) -> Result, EFI_TIME)>, NvramStorageError> { - self.lazy_load_from_storage().await?; - if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); } @@ -369,8 +370,6 @@ impl NvramStorage for HclCompatNvram { data: Vec, timestamp: EFI_TIME, ) -> Result<(), NvramStorageError> { - self.lazy_load_from_storage().await?; - if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); } @@ -413,8 +412,6 @@ impl NvramStorage for HclCompatNvram { data: Vec, timestamp: EFI_TIME, ) -> Result { - self.lazy_load_from_storage().await?; - if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); } @@ -445,8 +442,6 @@ impl NvramStorage for HclCompatNvram { name: &Ucs2LeSlice, vendor: Guid, ) -> Result { - self.lazy_load_from_storage().await?; - if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); } @@ -461,8 +456,6 @@ impl NvramStorage for HclCompatNvram { &mut self, name_vendor: Option<(&Ucs2LeSlice, Guid)>, ) -> Result { - self.lazy_load_from_storage().await?; - if let Some((name, _)) = name_vendor { if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); @@ -473,6 +466,26 @@ impl NvramStorage for HclCompatNvram { } } +#[cfg(feature = "save_restore")] +mod save_restore { + use super::*; + use vmcore::save_restore::RestoreError; + use vmcore::save_restore::SaveError; + use vmcore::save_restore::SaveRestore; + + impl SaveRestore for HclCompatNvram { + type SavedState = ::SavedState; + + fn save(&mut self) -> Result { + self.in_memory.save() + } + + fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> { + self.in_memory.restore(state) + } + } +} + #[cfg(test)] mod test { use super::storage_backend::StorageBackend; @@ -503,28 +516,36 @@ mod test { #[async_test] async fn test_single_variable() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None); + let mut nvram = HclCompatNvram::new(&mut storage, None, false) + .await + .unwrap(); impl_agnostic_tests::test_single_variable(&mut nvram).await; } #[async_test] async fn test_multiple_variable() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None); + let mut nvram = HclCompatNvram::new(&mut storage, None, false) + .await + .unwrap(); impl_agnostic_tests::test_multiple_variable(&mut nvram).await; } #[async_test] async fn test_next() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None); + let mut nvram = HclCompatNvram::new(&mut storage, None, false) + .await + .unwrap(); impl_agnostic_tests::test_next(&mut nvram).await; } #[async_test] async fn boundary_conditions() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None); + let mut nvram = HclCompatNvram::new(&mut storage, None, false) + .await + .unwrap(); let vendor = Guid::new_random(); let attr = 0x1234; @@ -612,7 +633,9 @@ mod test { let data = vec![0x1, 0x2, 0x3, 0x4, 0x5]; let timestamp = EFI_TIME::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None); + let mut nvram = HclCompatNvram::new(&mut storage, None, false) + .await + .unwrap(); nvram .set_variable(name1, vendor1, attr, data.clone(), timestamp) .await @@ -629,7 +652,9 @@ mod test { drop(nvram); // reload - let mut nvram = HclCompatNvram::new(&mut storage, None); + let mut nvram = HclCompatNvram::new(&mut storage, None, false) + .await + .unwrap(); let (result_attr, result_data, result_timestamp) = nvram.get_variable(name1, vendor1).await.unwrap().unwrap(); diff --git a/vm/devices/firmware/uefi_nvram_storage/Cargo.toml b/vm/devices/firmware/uefi_nvram_storage/Cargo.toml index e894d3be77..6bfcc45e55 100644 --- a/vm/devices/firmware/uefi_nvram_storage/Cargo.toml +++ b/vm/devices/firmware/uefi_nvram_storage/Cargo.toml @@ -10,12 +10,15 @@ rust-version.workspace = true default = [] inspect = ["dep:inspect", "uefi_specs/inspect"] +save_restore = ["inspect", "dep:mesh_protobuf", "dep:vmcore"] [dependencies] guid.workspace = true inspect = { workspace = true, optional = true } +mesh_protobuf = { workspace = true, optional = true } ucs2.workspace = true uefi_specs.workspace = true +vmcore = { workspace = true, optional = true } async-trait.workspace = true thiserror.workspace = true diff --git a/vm/devices/firmware/uefi_nvram_storage/src/in_memory.rs b/vm/devices/firmware/uefi_nvram_storage/src/in_memory.rs index 9819d2d130..410b4ec976 100644 --- a/vm/devices/firmware/uefi_nvram_storage/src/in_memory.rs +++ b/vm/devices/firmware/uefi_nvram_storage/src/in_memory.rs @@ -185,6 +185,84 @@ impl NvramStorage for InMemoryNvram { } } +#[cfg(feature = "save_restore")] +mod save_restore { + use super::*; + use vmcore::save_restore::RestoreError; + use vmcore::save_restore::SaveError; + use vmcore::save_restore::SaveRestore; + + mod state { + use guid::Guid; + use uefi_specs::uefi::time::EFI_TIME; + use vmcore::save_restore::SavedStateRoot; + + #[derive(mesh_protobuf::Protobuf)] + #[mesh(package = "nvram_entry")] + pub struct NvramEntry { + #[mesh(1)] + pub vendor: Guid, + #[mesh(2)] + pub name: Vec, + #[mesh(3)] + pub data: Vec, + #[mesh(4, encoding = "mesh_protobuf::encoding::ZeroCopyEncoding")] + pub timestamp: EFI_TIME, + #[mesh(5)] + pub attr: u32, + } + + #[derive(mesh_protobuf::Protobuf, SavedStateRoot)] + #[mesh(package = "firmware.in_memory_nvram")] + pub struct SavedState { + #[mesh(1)] + pub nvram: Vec, + } + } + + impl SaveRestore for InMemoryNvram { + type SavedState = state::SavedState; + + fn save(&mut self) -> Result { + Ok(state::SavedState { + nvram: self + .nvram + .iter() + .map(|(k, v)| state::NvramEntry { + vendor: k.vendor, + name: k.name.clone().into_inner(), + data: v.data.clone(), + timestamp: v.timestamp, + attr: v.attr, + }) + .collect(), + }) + } + + fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> { + let state::SavedState { nvram } = state; + self.nvram = nvram + .into_iter() + .map::, _>(|e| { + Ok(( + VariableKey { + vendor: e.vendor, + name: Ucs2LeVec::from_vec_with_nul(e.name) + .map_err(|e| RestoreError::InvalidSavedState(e.into()))?, + }, + Variable { + data: e.data, + timestamp: e.timestamp, + attr: e.attr, + }, + )) + }) + .collect::, _>>()?; + Ok(()) + } + } +} + /// A collection of test-implementation helpers that operate on a generic /// implementation of [`NvramStorage`] pub mod impl_agnostic_tests { diff --git a/vm/devices/firmware/uefi_nvram_storage/src/lib.rs b/vm/devices/firmware/uefi_nvram_storage/src/lib.rs index 857d301e85..36926d3020 100644 --- a/vm/devices/firmware/uefi_nvram_storage/src/lib.rs +++ b/vm/devices/firmware/uefi_nvram_storage/src/lib.rs @@ -11,8 +11,8 @@ pub use uefi_specs::uefi::time::EFI_TIME; pub mod in_memory; use guid::Guid; -#[cfg(feature = "inspect")] -pub use inspect_ext::InspectableNvramStorage; +#[cfg(feature = "save_restore")] +pub use save_restore::VmmNvramStorage; use std::fmt::Debug; use thiserror::Error; use ucs2::Ucs2LeSlice; @@ -169,18 +169,26 @@ impl NvramStorage for Box { } } -/// Defines a trait that combines NvramStorage and Inspect -#[cfg(feature = "inspect")] -mod inspect_ext { +/// Defines a trait that combines NvramStorage, Inspect, and SaveRestore +#[cfg(feature = "save_restore")] +mod save_restore { use super::*; use inspect::Inspect; + use vmcore::save_restore::SaveRestore; - /// Extends [`NvramStorage`] with a bound on [`Inspect`] - pub trait InspectableNvramStorage: NvramStorage + Inspect {} - impl InspectableNvramStorage for T {} + type NvramSavedState = ::SavedState; + + pub trait VmmNvramStorage: + NvramStorage + Inspect + SaveRestore + { + } + impl VmmNvramStorage for T where + T: NvramStorage + Inspect + SaveRestore + { + } #[async_trait::async_trait] - impl NvramStorage for Box { + impl NvramStorage for Box { async fn get_variable( &mut self, name: &Ucs2LeSlice, @@ -229,4 +237,19 @@ mod inspect_ext { (**self).next_variable(name_vendor).await } } + + impl SaveRestore for Box> { + type SavedState = NvramSavedState; + + fn save(&mut self) -> Result { + (**self).save() + } + + fn restore( + &mut self, + state: Self::SavedState, + ) -> Result<(), vmcore::save_restore::RestoreError> { + (**self).restore(state) + } + } } diff --git a/vm/vmgs/vmgstool/src/uefi_nvram.rs b/vm/vmgs/vmgstool/src/uefi_nvram.rs index bb869c4c72..5566a9e2bd 100644 --- a/vm/vmgs/vmgstool/src/uefi_nvram.rs +++ b/vm/vmgs/vmgstool/src/uefi_nvram.rs @@ -144,7 +144,7 @@ async fn dump_nvram( truncate: bool, ) -> Result<(), Error> { let mut count = 0; - for entry in nvram_storage.iter().await? { + for entry in nvram_storage.iter() { let meta = NvramEntryMetadata { vendor: entry.vendor.to_string(), name: entry.name.to_string(), @@ -343,17 +343,20 @@ async fn vmgs_file_open_nvram( let vmgs = vmgs_file_open(file_path, key_path, open_mode, false).await?; let encrypted = vmgs.is_encrypted(); - open_nvram(vmgs, encrypted) + open_nvram(vmgs, encrypted).await } -fn open_nvram(vmgs: Vmgs, encrypted: bool) -> Result, Error> { - let nvram_storage = HclCompatNvram::new( +async fn open_nvram( + vmgs: Vmgs, + encrypted: bool, +) -> Result, Error> { + Ok(HclCompatNvram::new( VmgsStorageBackend::new(vmgs, vmgs::FileId::BIOS_NVRAM, encrypted) .map_err(Error::VmgsStorageBackend)?, None, - ); - - Ok(nvram_storage) + false, + ) + .await?) } /// Delete all boot entries in the BIOS NVRAM VMGS file in an attempt to repair a VM that is failing to boot. diff --git a/vmm_core/vmotherboard/src/base_chipset.rs b/vmm_core/vmotherboard/src/base_chipset.rs index c21214cc91..4d3488d480 100644 --- a/vmm_core/vmotherboard/src/base_chipset.rs +++ b/vmm_core/vmotherboard/src/base_chipset.rs @@ -1318,7 +1318,7 @@ pub mod options { /// Interface to log UEFI BIOS events pub logger: Box, /// Interface for storing/retrieving UEFI NVRAM variables - pub nvram_storage: Box, + pub nvram_storage: Box, /// Channel to receive updated generation ID values pub generation_id_recv: mesh::Receiver<[u8; 16]>, /// Device-specific functions the platform must provide in order From 0efdadf79c34ee20b993d2815081f93f9d63cfa3 Mon Sep 17 00:00:00 2001 From: Trevor Jones Date: Thu, 17 Jul 2025 16:28:28 -0700 Subject: [PATCH 2/3] 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> 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: https://github.com/microsoft/openvmm/pull/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. --- openhcl/underhill_core/src/worker.rs | 1 - openvmm/hvlite_core/src/worker/dispatch.rs | 1 - .../hcl_compat_uefi_nvram_storage/src/lib.rs | 73 +++++++++++-------- .../uefi_nvram_storage/src/in_memory.rs | 62 ++++++++-------- vm/vmgs/vmgstool/src/uefi_nvram.rs | 3 +- 5 files changed, 75 insertions(+), 65 deletions(-) diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index 17eb4e1a75..dfe133a499 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -2074,7 +2074,6 @@ async fn new_underhill_vm( Some(HclCompatNvramQuirks { skip_corrupt_vars_with_missing_null_term: true, }), - is_restoring, ) .await?, ), diff --git a/openvmm/hvlite_core/src/worker/dispatch.rs b/openvmm/hvlite_core/src/worker/dispatch.rs index 7545ee8107..83d4f0b23a 100644 --- a/openvmm/hvlite_core/src/worker/dispatch.rs +++ b/openvmm/hvlite_core/src/worker/dispatch.rs @@ -1074,7 +1074,6 @@ impl InitializedVm { .context("failed to instantiate UEFI NVRAM store")?, ), None, - false, ) .await?, ), diff --git a/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs b/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs index 96bf6cfbe8..393ac2f88f 100644 --- a/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs +++ b/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs @@ -94,6 +94,9 @@ pub struct HclCompatNvram { // reduced allocator pressure #[cfg_attr(feature = "inspect", inspect(skip))] // internal bookkeeping - not worth inspecting nvram_buf: Vec, + + // whether the NVRAM has been loaded, either from storage or saved state + loaded: bool, } /// "Quirks" to take into account when loading/storing nvram blob data. @@ -121,9 +124,8 @@ impl HclCompatNvram { pub async fn new( storage: S, quirks: Option, - is_restoring: bool, ) -> Result { - let mut nvram = Self { + let nvram = Self { quirks: quirks.unwrap_or(HclCompatNvramQuirks { skip_corrupt_vars_with_missing_null_term: false, }), @@ -133,16 +135,14 @@ impl HclCompatNvram { in_memory: in_memory::InMemoryNvram::new(), nvram_buf: Vec::new(), + + loaded: false, }; - if !is_restoring { - nvram.load_from_storage().await?; - } Ok(nvram) } - async fn load_from_storage(&mut self) -> Result<(), NvramStorageError> { - tracing::info!("loading uefi nvram from storage"); - let res = self.load_from_storage_inner().await; + async fn lazy_load_from_storage(&mut self) -> Result<(), NvramStorageError> { + let res = self.lazy_load_from_storage_inner().await; if let Err(e) = &res { tracing::error!(CVM_ALLOWED, "storage contains corrupt nvram state"); tracing::error!( @@ -154,7 +154,13 @@ impl HclCompatNvram { res } - async fn load_from_storage_inner(&mut self) -> Result<(), NvramStorageError> { + async fn lazy_load_from_storage_inner(&mut self) -> Result<(), NvramStorageError> { + if self.loaded { + return Ok(()); + } + + tracing::info!("loading uefi nvram from storage"); + let nvram_buf = self .storage .restore() @@ -293,6 +299,7 @@ impl HclCompatNvram { )); } + self.loaded = true; Ok(()) } @@ -343,8 +350,11 @@ impl HclCompatNvram { /// Iterate over the NVRAM entries. This function asynchronously loads the /// NVRAM contents into memory from the backing storage if necessary. - pub fn iter(&mut self) -> impl Iterator> { - self.in_memory.iter() + pub async fn iter( + &mut self, + ) -> Result>, NvramStorageError> { + self.lazy_load_from_storage().await?; + Ok(self.in_memory.iter()) } } @@ -355,6 +365,8 @@ impl NvramStorage for HclCompatNvram { name: &Ucs2LeSlice, vendor: Guid, ) -> Result, EFI_TIME)>, NvramStorageError> { + self.lazy_load_from_storage().await?; + if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); } @@ -370,6 +382,8 @@ impl NvramStorage for HclCompatNvram { data: Vec, timestamp: EFI_TIME, ) -> Result<(), NvramStorageError> { + self.lazy_load_from_storage().await?; + if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); } @@ -404,7 +418,6 @@ impl NvramStorage for HclCompatNvram { Ok(()) } - async fn append_variable( &mut self, name: &Ucs2LeSlice, @@ -412,6 +425,8 @@ impl NvramStorage for HclCompatNvram { data: Vec, timestamp: EFI_TIME, ) -> Result { + self.lazy_load_from_storage().await?; + if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); } @@ -442,6 +457,8 @@ impl NvramStorage for HclCompatNvram { name: &Ucs2LeSlice, vendor: Guid, ) -> Result { + self.lazy_load_from_storage().await?; + if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); } @@ -456,6 +473,8 @@ impl NvramStorage for HclCompatNvram { &mut self, name_vendor: Option<(&Ucs2LeSlice, Guid)>, ) -> Result { + self.lazy_load_from_storage().await?; + if let Some((name, _)) = name_vendor { if name.as_bytes().len() > EFI_MAX_VARIABLE_NAME_SIZE { return Err(NvramStorageError::VariableNameTooLong); @@ -481,7 +500,11 @@ mod save_restore { } fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> { - self.in_memory.restore(state) + if state.nvram.is_some() { + self.in_memory.restore(state)?; + self.loaded = true; + } + Ok(()) } } } @@ -516,36 +539,28 @@ mod test { #[async_test] async fn test_single_variable() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None, false) - .await - .unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); impl_agnostic_tests::test_single_variable(&mut nvram).await; } #[async_test] async fn test_multiple_variable() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None, false) - .await - .unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); impl_agnostic_tests::test_multiple_variable(&mut nvram).await; } #[async_test] async fn test_next() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None, false) - .await - .unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); impl_agnostic_tests::test_next(&mut nvram).await; } #[async_test] async fn boundary_conditions() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None, false) - .await - .unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); let vendor = Guid::new_random(); let attr = 0x1234; @@ -633,9 +648,7 @@ mod test { let data = vec![0x1, 0x2, 0x3, 0x4, 0x5]; let timestamp = EFI_TIME::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None, false) - .await - .unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); nvram .set_variable(name1, vendor1, attr, data.clone(), timestamp) .await @@ -652,9 +665,7 @@ mod test { drop(nvram); // reload - let mut nvram = HclCompatNvram::new(&mut storage, None, false) - .await - .unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); let (result_attr, result_data, result_timestamp) = nvram.get_variable(name1, vendor1).await.unwrap().unwrap(); diff --git a/vm/devices/firmware/uefi_nvram_storage/src/in_memory.rs b/vm/devices/firmware/uefi_nvram_storage/src/in_memory.rs index 410b4ec976..a74b03b884 100644 --- a/vm/devices/firmware/uefi_nvram_storage/src/in_memory.rs +++ b/vm/devices/firmware/uefi_nvram_storage/src/in_memory.rs @@ -216,7 +216,7 @@ mod save_restore { #[mesh(package = "firmware.in_memory_nvram")] pub struct SavedState { #[mesh(1)] - pub nvram: Vec, + pub nvram: Option>, } } @@ -225,39 +225,41 @@ mod save_restore { fn save(&mut self) -> Result { Ok(state::SavedState { - nvram: self - .nvram - .iter() - .map(|(k, v)| state::NvramEntry { - vendor: k.vendor, - name: k.name.clone().into_inner(), - data: v.data.clone(), - timestamp: v.timestamp, - attr: v.attr, - }) - .collect(), + nvram: Some( + self.nvram + .iter() + .map(|(k, v)| state::NvramEntry { + vendor: k.vendor, + name: k.name.clone().into_inner(), + data: v.data.clone(), + timestamp: v.timestamp, + attr: v.attr, + }) + .collect(), + ), }) } fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> { - let state::SavedState { nvram } = state; - self.nvram = nvram - .into_iter() - .map::, _>(|e| { - Ok(( - VariableKey { - vendor: e.vendor, - name: Ucs2LeVec::from_vec_with_nul(e.name) - .map_err(|e| RestoreError::InvalidSavedState(e.into()))?, - }, - Variable { - data: e.data, - timestamp: e.timestamp, - attr: e.attr, - }, - )) - }) - .collect::, _>>()?; + if let state::SavedState { nvram: Some(nvram) } = state { + self.nvram = nvram + .into_iter() + .map::, _>(|e| { + Ok(( + VariableKey { + vendor: e.vendor, + name: Ucs2LeVec::from_vec_with_nul(e.name) + .map_err(|e| RestoreError::InvalidSavedState(e.into()))?, + }, + Variable { + data: e.data, + timestamp: e.timestamp, + attr: e.attr, + }, + )) + }) + .collect::, _>>()?; + } Ok(()) } } diff --git a/vm/vmgs/vmgstool/src/uefi_nvram.rs b/vm/vmgs/vmgstool/src/uefi_nvram.rs index 5566a9e2bd..dd43dc1466 100644 --- a/vm/vmgs/vmgstool/src/uefi_nvram.rs +++ b/vm/vmgs/vmgstool/src/uefi_nvram.rs @@ -144,7 +144,7 @@ async fn dump_nvram( truncate: bool, ) -> Result<(), Error> { let mut count = 0; - for entry in nvram_storage.iter() { + for entry in nvram_storage.iter().await? { let meta = NvramEntryMetadata { vendor: entry.vendor.to_string(), name: entry.name.to_string(), @@ -354,7 +354,6 @@ async fn open_nvram( VmgsStorageBackend::new(vmgs, vmgs::FileId::BIOS_NVRAM, encrypted) .map_err(Error::VmgsStorageBackend)?, None, - false, ) .await?) } From b2a9387c5854131af2dfc41861cd8d4fbfde9b72 Mon Sep 17 00:00:00 2001 From: Trevor Jones Date: Thu, 17 Jul 2025 16:48:05 -0700 Subject: [PATCH 3/3] remove unnecessary async --- openhcl/underhill_core/src/worker.rs | 23 ++++++++----------- openvmm/hvlite_core/src/worker/dispatch.rs | 17 ++++++-------- .../hcl_compat_uefi_nvram_storage/src/lib.rs | 23 ++++++++----------- vm/vmgs/vmgstool/src/uefi_nvram.rs | 14 +++++------ 4 files changed, 33 insertions(+), 44 deletions(-) diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index dfe133a499..78341e6f20 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -2064,19 +2064,16 @@ async fn new_underhill_vm( logger: Box::new(UnderhillLogger { get: get_client.clone(), }), - nvram_storage: Box::new( - HclCompatNvram::new( - VmgsStorageBackendAdapter( - vmgs_client - .as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true) - .context("failed to instantiate UEFI NVRAM store")?, - ), - Some(HclCompatNvramQuirks { - skip_corrupt_vars_with_missing_null_term: true, - }), - ) - .await?, - ), + nvram_storage: Box::new(HclCompatNvram::new( + VmgsStorageBackendAdapter( + vmgs_client + .as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true) + .context("failed to instantiate UEFI NVRAM store")?, + ), + Some(HclCompatNvramQuirks { + skip_corrupt_vars_with_missing_null_term: true, + }), + )), generation_id_recv: get_client .take_generation_id_recv() .await diff --git a/openvmm/hvlite_core/src/worker/dispatch.rs b/openvmm/hvlite_core/src/worker/dispatch.rs index 83d4f0b23a..ea2ae5472c 100644 --- a/openvmm/hvlite_core/src/worker/dispatch.rs +++ b/openvmm/hvlite_core/src/worker/dispatch.rs @@ -1067,16 +1067,13 @@ impl InitializedVm { use vmm_core::emuplat::hcl_compat_uefi_nvram_storage::VmgsStorageBackendAdapter; match vmgs_client { - Some(vmgs) => Box::new( - HclCompatNvram::new( - VmgsStorageBackendAdapter( - vmgs.as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true) - .context("failed to instantiate UEFI NVRAM store")?, - ), - None, - ) - .await?, - ), + Some(vmgs) => Box::new(HclCompatNvram::new( + VmgsStorageBackendAdapter( + vmgs.as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true) + .context("failed to instantiate UEFI NVRAM store")?, + ), + None, + )), None => Box::new(InMemoryNvram::new()), } }, diff --git a/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs b/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs index 393ac2f88f..ffbe479d96 100644 --- a/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs +++ b/vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs @@ -121,11 +121,8 @@ pub struct HclCompatNvramQuirks { impl HclCompatNvram { /// Create a new [`HclCompatNvram`] - pub async fn new( - storage: S, - quirks: Option, - ) -> Result { - let nvram = Self { + pub fn new(storage: S, quirks: Option) -> Self { + Self { quirks: quirks.unwrap_or(HclCompatNvramQuirks { skip_corrupt_vars_with_missing_null_term: false, }), @@ -137,8 +134,7 @@ impl HclCompatNvram { nvram_buf: Vec::new(), loaded: false, - }; - Ok(nvram) + } } async fn lazy_load_from_storage(&mut self) -> Result<(), NvramStorageError> { @@ -418,6 +414,7 @@ impl NvramStorage for HclCompatNvram { Ok(()) } + async fn append_variable( &mut self, name: &Ucs2LeSlice, @@ -539,28 +536,28 @@ mod test { #[async_test] async fn test_single_variable() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None); impl_agnostic_tests::test_single_variable(&mut nvram).await; } #[async_test] async fn test_multiple_variable() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None); impl_agnostic_tests::test_multiple_variable(&mut nvram).await; } #[async_test] async fn test_next() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None); impl_agnostic_tests::test_next(&mut nvram).await; } #[async_test] async fn boundary_conditions() { let mut storage = EphemeralStorageBackend::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None); let vendor = Guid::new_random(); let attr = 0x1234; @@ -648,7 +645,7 @@ mod test { let data = vec![0x1, 0x2, 0x3, 0x4, 0x5]; let timestamp = EFI_TIME::default(); - let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None); nvram .set_variable(name1, vendor1, attr, data.clone(), timestamp) .await @@ -665,7 +662,7 @@ mod test { drop(nvram); // reload - let mut nvram = HclCompatNvram::new(&mut storage, None).await.unwrap(); + let mut nvram = HclCompatNvram::new(&mut storage, None); let (result_attr, result_data, result_timestamp) = nvram.get_variable(name1, vendor1).await.unwrap().unwrap(); diff --git a/vm/vmgs/vmgstool/src/uefi_nvram.rs b/vm/vmgs/vmgstool/src/uefi_nvram.rs index dd43dc1466..bb869c4c72 100644 --- a/vm/vmgs/vmgstool/src/uefi_nvram.rs +++ b/vm/vmgs/vmgstool/src/uefi_nvram.rs @@ -343,19 +343,17 @@ async fn vmgs_file_open_nvram( let vmgs = vmgs_file_open(file_path, key_path, open_mode, false).await?; let encrypted = vmgs.is_encrypted(); - open_nvram(vmgs, encrypted).await + open_nvram(vmgs, encrypted) } -async fn open_nvram( - vmgs: Vmgs, - encrypted: bool, -) -> Result, Error> { - Ok(HclCompatNvram::new( +fn open_nvram(vmgs: Vmgs, encrypted: bool) -> Result, Error> { + let nvram_storage = HclCompatNvram::new( VmgsStorageBackend::new(vmgs, vmgs::FileId::BIOS_NVRAM, encrypted) .map_err(Error::VmgsStorageBackend)?, None, - ) - .await?) + ); + + Ok(nvram_storage) } /// Delete all boot entries in the BIOS NVRAM VMGS file in an attempt to repair a VM that is failing to boot.