Skip to content

Commit 2a4708a

Browse files
committed
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.
1 parent 4cbc184 commit 2a4708a

File tree

17 files changed

+237
-84
lines changed

17 files changed

+237
-84
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,6 +2799,7 @@ dependencies = [
27992799
"tracing",
28002800
"ucs2 0.0.0",
28012801
"uefi_nvram_storage",
2802+
"vmcore",
28022803
"wchar",
28032804
"zerocopy 0.8.24",
28042805
]
@@ -7276,10 +7277,12 @@ dependencies = [
72767277
"async-trait",
72777278
"guid",
72787279
"inspect",
7280+
"mesh_protobuf",
72797281
"pal_async",
72807282
"thiserror 2.0.12",
72817283
"ucs2 0.0.0",
72827284
"uefi_specs",
7285+
"vmcore",
72837286
"wchar",
72847287
"zerocopy 0.8.24",
72857288
]

openhcl/underhill_core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ hyperv_ic_resources.workspace = true
5656
hyperv_secure_boot_templates.workspace = true
5757
hyperv_uefi_custom_vars_json.workspace = true
5858
framebuffer.workspace = true
59-
hcl_compat_uefi_nvram_storage = { workspace = true, features = ["inspect"] }
59+
hcl_compat_uefi_nvram_storage = { workspace = true, features = ["inspect", "save_restore"] }
6060
get_helpers.workspace = true
6161
get_protocol.workspace = true
6262
guest_emulation_transport.workspace = true

openhcl/underhill_core/src/worker.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,16 +2064,20 @@ async fn new_underhill_vm(
20642064
logger: Box::new(UnderhillLogger {
20652065
get: get_client.clone(),
20662066
}),
2067-
nvram_storage: Box::new(HclCompatNvram::new(
2068-
VmgsStorageBackendAdapter(
2069-
vmgs_client
2070-
.as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true)
2071-
.context("failed to instantiate UEFI NVRAM store")?,
2072-
),
2073-
Some(HclCompatNvramQuirks {
2074-
skip_corrupt_vars_with_missing_null_term: true,
2075-
}),
2076-
)),
2067+
nvram_storage: Box::new(
2068+
HclCompatNvram::new(
2069+
VmgsStorageBackendAdapter(
2070+
vmgs_client
2071+
.as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true)
2072+
.context("failed to instantiate UEFI NVRAM store")?,
2073+
),
2074+
Some(HclCompatNvramQuirks {
2075+
skip_corrupt_vars_with_missing_null_term: true,
2076+
}),
2077+
is_restoring,
2078+
)
2079+
.await?,
2080+
),
20772081
generation_id_recv: get_client
20782082
.take_generation_id_recv()
20792083
.await

openvmm/hvlite_core/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ disk_backend.workspace = true
5252
firmware_pcat.workspace = true
5353
firmware_uefi_custom_vars.workspace = true
5454
firmware_uefi.workspace = true
55-
uefi_nvram_storage.workspace = true
55+
uefi_nvram_storage = { workspace = true, features = ["save_restore"] }
5656
framebuffer.workspace = true
5757
get_resources.workspace = true
58-
hcl_compat_uefi_nvram_storage = { workspace = true, features = ["inspect"] }
58+
hcl_compat_uefi_nvram_storage = { workspace = true, features = ["inspect", "save_restore"] }
5959
ide.workspace = true
6060
floppy.workspace = true
6161
input_core.workspace = true

openvmm/hvlite_core/src/worker/dispatch.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,13 +1067,17 @@ impl InitializedVm {
10671067
use vmm_core::emuplat::hcl_compat_uefi_nvram_storage::VmgsStorageBackendAdapter;
10681068

10691069
match vmgs_client {
1070-
Some(vmgs) => Box::new(HclCompatNvram::new(
1071-
VmgsStorageBackendAdapter(
1072-
vmgs.as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true)
1073-
.context("failed to instantiate UEFI NVRAM store")?,
1074-
),
1075-
None,
1076-
)),
1070+
Some(vmgs) => Box::new(
1071+
HclCompatNvram::new(
1072+
VmgsStorageBackendAdapter(
1073+
vmgs.as_non_volatile_store(vmgs::FileId::BIOS_NVRAM, true)
1074+
.context("failed to instantiate UEFI NVRAM store")?,
1075+
),
1076+
None,
1077+
false,
1078+
)
1079+
.await?,
1080+
),
10771081
None => Box::new(InMemoryNvram::new()),
10781082
}
10791083
},

vm/devices/firmware/firmware_uefi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fuzzing = []
2222

2323
[dependencies]
2424
firmware_uefi_custom_vars.workspace = true
25-
uefi_nvram_storage = { workspace = true, features = ["inspect"] }
25+
uefi_nvram_storage = { workspace = true, features = ["inspect", "save_restore"] }
2626
uefi_specs.workspace = true
2727
uefi_nvram_specvars.workspace = true
2828
generation_id.workspace = true

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ use std::convert::TryInto;
7474
use std::ops::RangeInclusive;
7575
use std::task::Context;
7676
use thiserror::Error;
77-
use uefi_nvram_storage::InspectableNvramStorage;
77+
use uefi_nvram_storage::VmmNvramStorage;
7878
use vmcore::device_state::ChangeDeviceState;
7979
use vmcore::vmtime::VmTimeSource;
8080
use watchdog_core::platform::WatchdogPlatform;
@@ -129,7 +129,7 @@ pub struct UefiConfig {
129129
/// Various runtime objects used by the UEFI device + underlying services.
130130
pub struct UefiRuntimeDeps<'a> {
131131
pub gm: GuestMemory,
132-
pub nvram_storage: Box<dyn InspectableNvramStorage>,
132+
pub nvram_storage: Box<dyn VmmNvramStorage>,
133133
pub logger: Box<dyn UefiLogger>,
134134
pub vmtime: &'a VmTimeSource,
135135
pub watchdog_platform: Box<dyn WatchdogPlatform>,

vm/devices/firmware/firmware_uefi/src/service/nvram/mod.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use inspect::Inspect;
2424
use std::borrow::Cow;
2525
use std::fmt::Debug;
2626
use thiserror::Error;
27-
use uefi_nvram_storage::InspectableNvramStorage;
27+
use uefi_nvram_storage::VmmNvramStorage;
2828
use uefi_specs::uefi::common::EfiStatus;
2929
use uefi_specs::uefi::nvram::EfiVariableAttributes;
3030
use zerocopy::IntoBytes;
@@ -67,12 +67,12 @@ pub struct NvramServices {
6767

6868
// Sub-emulators
6969
#[inspect(flatten)]
70-
services: NvramSpecServices<Box<dyn InspectableNvramStorage>>,
70+
services: NvramSpecServices<Box<dyn VmmNvramStorage>>,
7171
}
7272

7373
impl NvramServices {
7474
pub async fn new(
75-
nvram_storage: Box<dyn InspectableNvramStorage>,
75+
nvram_storage: Box<dyn VmmNvramStorage>,
7676
custom_vars: CustomVars,
7777
secure_boot_enabled: bool,
7878
vsm_config: Option<Box<dyn VsmConfig>>,
@@ -622,15 +622,14 @@ mod save_restore {
622622
mod state {
623623
use crate::service::nvram::NvramSpecServices;
624624
use mesh::payload::Protobuf;
625-
use uefi_nvram_storage::InspectableNvramStorage;
625+
use uefi_nvram_storage::VmmNvramStorage;
626626
use vmcore::save_restore::SaveRestore;
627627

628628
#[derive(Protobuf)]
629629
#[mesh(package = "firmware.uefi.nvram")]
630630
pub struct SavedState {
631631
#[mesh(1)]
632-
pub services:
633-
<NvramSpecServices<Box<dyn InspectableNvramStorage>> as SaveRestore>::SavedState,
632+
pub services: <NvramSpecServices<Box<dyn VmmNvramStorage>> as SaveRestore>::SavedState,
634633
}
635634
}
636635

vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/mod.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ use ucs2::Ucs2LeSlice;
2323
use ucs2::Ucs2ParseError;
2424
use uefi_nvram_specvars::signature_list;
2525
use uefi_nvram_specvars::signature_list::ParseSignatureLists;
26-
use uefi_nvram_storage::InspectableNvramStorage;
2726
use uefi_nvram_storage::NextVariable;
2827
use uefi_nvram_storage::NvramStorageError;
28+
use uefi_nvram_storage::VmmNvramStorage;
2929
use uefi_specs::uefi::common::EfiStatus;
3030
use uefi_specs::uefi::nvram::EfiVariableAttributes;
3131
use uefi_specs::uefi::time::EFI_TIME;
@@ -224,12 +224,12 @@ impl RuntimeState {
224224
/// `NvramError` type provides additional context as to what error occurred in
225225
/// OpenVMM (i.e: for logging purposes).
226226
#[derive(Debug, Inspect)]
227-
pub struct NvramSpecServices<S: InspectableNvramStorage> {
227+
pub struct NvramSpecServices<S: VmmNvramStorage> {
228228
storage: S,
229229
runtime_state: RuntimeState,
230230
}
231231

232-
impl<S: InspectableNvramStorage> NvramSpecServices<S> {
232+
impl<S: VmmNvramStorage> NvramSpecServices<S> {
233233
/// Construct a new NvramServices instance from an existing storage backend.
234234
pub fn new(storage: S) -> NvramSpecServices<S> {
235235
NvramSpecServices {
@@ -1453,6 +1453,8 @@ mod save_restore {
14531453

14541454
mod state {
14551455
use mesh::payload::Protobuf;
1456+
use uefi_nvram_storage::in_memory::InMemoryNvram;
1457+
use vmcore::save_restore::SaveRestore;
14561458

14571459
#[derive(Protobuf)]
14581460
#[mesh(package = "firmware.uefi.nvram.spec")]
@@ -1470,10 +1472,12 @@ mod save_restore {
14701472
pub struct SavedState {
14711473
#[mesh(1)]
14721474
pub runtime_state: SavedRuntimeState,
1475+
#[mesh(2)]
1476+
pub storage: <InMemoryNvram as SaveRestore>::SavedState,
14731477
}
14741478
}
14751479

1476-
impl<S: InspectableNvramStorage> SaveRestore for NvramSpecServices<S> {
1480+
impl<S: VmmNvramStorage> SaveRestore for NvramSpecServices<S> {
14771481
type SavedState = state::SavedState;
14781482

14791483
fn save(&mut self) -> Result<Self::SavedState, SaveError> {
@@ -1483,17 +1487,22 @@ mod save_restore {
14831487
RuntimeState::Boot => state::SavedRuntimeState::Boot,
14841488
RuntimeState::Runtime => state::SavedRuntimeState::Runtime,
14851489
},
1490+
storage: self.storage.save()?,
14861491
})
14871492
}
14881493

14891494
fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
1490-
let state::SavedState { runtime_state } = state;
1495+
let state::SavedState {
1496+
runtime_state,
1497+
storage,
1498+
} = state;
14911499

14921500
self.runtime_state = match runtime_state {
14931501
state::SavedRuntimeState::PreBoot => RuntimeState::PreBoot,
14941502
state::SavedRuntimeState::Boot => RuntimeState::Boot,
14951503
state::SavedRuntimeState::Runtime => RuntimeState::Runtime,
14961504
};
1505+
self.storage.restore(storage)?;
14971506

14981507
Ok(())
14991508
}
@@ -1526,7 +1535,7 @@ mod test {
15261535
}
15271536

15281537
#[async_trait::async_trait]
1529-
impl<S: InspectableNvramStorage> NvramServicesTestExt for NvramSpecServices<S> {
1538+
impl<S: VmmNvramStorage> NvramServicesTestExt for NvramSpecServices<S> {
15301539
async fn set_test_var(&mut self, name: &[u8], attr: u32, data: &[u8]) -> NvramResult<()> {
15311540
let vendor = Guid::default();
15321541

vm/devices/firmware/firmware_uefi/src/service/nvram/spec_services/nvram_services_ext.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use super::NvramResult;
66
use super::NvramSpecServices;
77
use guid::Guid;
88
use ucs2::Ucs2LeSlice;
9-
use uefi_nvram_storage::InspectableNvramStorage;
9+
use uefi_nvram_storage::VmmNvramStorage;
1010
use uefi_specs::uefi::common::EfiStatus;
1111

1212
/// Extension trait around `NvramServices` that makes it easier to use the API
@@ -56,7 +56,7 @@ pub trait NvramServicesExt {
5656
}
5757

5858
#[async_trait::async_trait]
59-
impl<S: InspectableNvramStorage> NvramServicesExt for NvramSpecServices<S> {
59+
impl<S: VmmNvramStorage> NvramServicesExt for NvramSpecServices<S> {
6060
async fn get_variable(
6161
&mut self,
6262
vendor: Guid,

0 commit comments

Comments
 (0)