Skip to content

Commit ed6e9b7

Browse files
authored
uefi_nvram_storage: save restore nvram contents (#1556) (#1675)
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 4cd692a commit ed6e9b7

File tree

14 files changed

+183
-31
lines changed

14 files changed

+183
-31
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
]
@@ -7275,10 +7276,12 @@ dependencies = [
72757276
"async-trait",
72767277
"guid",
72777278
"inspect",
7279+
"mesh_protobuf",
72787280
"pal_async",
72797281
"thiserror 2.0.12",
72807282
"ucs2 0.0.0",
72817283
"uefi_specs",
7284+
"vmcore",
72827285
"wchar",
72837286
"zerocopy 0.8.24",
72847287
]

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

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

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
@@ -76,7 +76,7 @@ use std::ops::RangeInclusive;
7676
use std::task::Context;
7777
use std::task::Poll;
7878
use thiserror::Error;
79-
use uefi_nvram_storage::InspectableNvramStorage;
79+
use uefi_nvram_storage::VmmNvramStorage;
8080
use vmcore::device_state::ChangeDeviceState;
8181
use vmcore::vmtime::VmTimeSource;
8282
use watchdog_core::platform::WatchdogPlatform;
@@ -131,7 +131,7 @@ pub struct UefiConfig {
131131
/// Various runtime objects used by the UEFI device + underlying services.
132132
pub struct UefiRuntimeDeps<'a> {
133133
pub gm: GuestMemory,
134-
pub nvram_storage: Box<dyn InspectableNvramStorage>,
134+
pub nvram_storage: Box<dyn VmmNvramStorage>,
135135
pub logger: Box<dyn UefiLogger>,
136136
pub vmtime: &'a VmTimeSource,
137137
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,

vm/devices/firmware/hcl_compat_uefi_nvram_storage/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ rust-version.workspace = true
1010
default = []
1111

1212
inspect = ["dep:inspect", "uefi_nvram_storage/inspect"]
13+
save_restore = [ "dep:vmcore", "uefi_nvram_storage/save_restore"]
1314

1415
[dependencies]
1516
uefi_nvram_storage.workspace = true
17+
vmcore = { workspace = true, optional = true }
1618

1719
cvm_tracing.workspace = true
1820
guid.workspace = true

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

Lines changed: 34 additions & 1 deletion
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.
@@ -129,6 +132,8 @@ impl<S: StorageBackend> HclCompatNvram<S> {
129132
in_memory: in_memory::InMemoryNvram::new(),
130133

131134
nvram_buf: Vec::new(),
135+
136+
loaded: false,
132137
}
133138
}
134139

@@ -146,10 +151,12 @@ impl<S: StorageBackend> HclCompatNvram<S> {
146151
}
147152

148153
async fn lazy_load_from_storage_inner(&mut self) -> Result<(), NvramStorageError> {
149-
if !self.nvram_buf.is_empty() {
154+
if self.loaded {
150155
return Ok(());
151156
}
152157

158+
tracing::info!("loading uefi nvram from storage");
159+
153160
let nvram_buf = self
154161
.storage
155162
.restore()
@@ -288,11 +295,13 @@ impl<S: StorageBackend> HclCompatNvram<S> {
288295
));
289296
}
290297

298+
self.loaded = true;
291299
Ok(())
292300
}
293301

294302
/// Dump in-memory nvram to the underlying storage device.
295303
async fn flush_storage(&mut self) -> Result<(), NvramStorageError> {
304+
tracing::info!("flushing uefi nvram to storage");
296305
self.nvram_buf.clear();
297306

298307
for in_memory::VariableEntry {
@@ -473,6 +482,30 @@ impl<S: StorageBackend> NvramStorage for HclCompatNvram<S> {
473482
}
474483
}
475484

485+
#[cfg(feature = "save_restore")]
486+
mod save_restore {
487+
use super::*;
488+
use vmcore::save_restore::RestoreError;
489+
use vmcore::save_restore::SaveError;
490+
use vmcore::save_restore::SaveRestore;
491+
492+
impl<S: StorageBackend> SaveRestore for HclCompatNvram<S> {
493+
type SavedState = <in_memory::InMemoryNvram as SaveRestore>::SavedState;
494+
495+
fn save(&mut self) -> Result<Self::SavedState, SaveError> {
496+
self.in_memory.save()
497+
}
498+
499+
fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
500+
if state.nvram.is_some() {
501+
self.in_memory.restore(state)?;
502+
self.loaded = true;
503+
}
504+
Ok(())
505+
}
506+
}
507+
}
508+
476509
#[cfg(test)]
477510
mod test {
478511
use super::storage_backend::StorageBackend;

0 commit comments

Comments
 (0)