Skip to content

uefi_nvram_storage: save restore nvram contents (#1556) #1675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2799,6 +2799,7 @@ dependencies = [
"tracing",
"ucs2 0.0.0",
"uefi_nvram_storage",
"vmcore",
"wchar",
"zerocopy 0.8.24",
]
Expand Down Expand Up @@ -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",
]
Expand Down
2 changes: 1 addition & 1 deletion openhcl/underhill_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions openvmm/hvlite_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion vm/devices/firmware/firmware_uefi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions vm/devices/firmware/firmware_uefi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<dyn InspectableNvramStorage>,
pub nvram_storage: Box<dyn VmmNvramStorage>,
pub logger: Box<dyn UefiLogger>,
pub vmtime: &'a VmTimeSource,
pub watchdog_platform: Box<dyn WatchdogPlatform>,
Expand Down
11 changes: 5 additions & 6 deletions vm/devices/firmware/firmware_uefi/src/service/nvram/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -67,12 +67,12 @@ pub struct NvramServices {

// Sub-emulators
#[inspect(flatten)]
services: NvramSpecServices<Box<dyn InspectableNvramStorage>>,
services: NvramSpecServices<Box<dyn VmmNvramStorage>>,
}

impl NvramServices {
pub async fn new(
nvram_storage: Box<dyn InspectableNvramStorage>,
nvram_storage: Box<dyn VmmNvramStorage>,
custom_vars: CustomVars,
secure_boot_enabled: bool,
vsm_config: Option<Box<dyn VsmConfig>>,
Expand Down Expand Up @@ -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:
<NvramSpecServices<Box<dyn InspectableNvramStorage>> as SaveRestore>::SavedState,
pub services: <NvramSpecServices<Box<dyn VmmNvramStorage>> as SaveRestore>::SavedState,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<S: InspectableNvramStorage> {
pub struct NvramSpecServices<S: VmmNvramStorage> {
storage: S,
runtime_state: RuntimeState,
}

impl<S: InspectableNvramStorage> NvramSpecServices<S> {
impl<S: VmmNvramStorage> NvramSpecServices<S> {
/// Construct a new NvramServices instance from an existing storage backend.
pub fn new(storage: S) -> NvramSpecServices<S> {
NvramSpecServices {
Expand Down Expand Up @@ -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")]
Expand All @@ -1470,10 +1472,12 @@ mod save_restore {
pub struct SavedState {
#[mesh(1)]
pub runtime_state: SavedRuntimeState,
#[mesh(2)]
pub storage: <InMemoryNvram as SaveRestore>::SavedState,
}
}

impl<S: InspectableNvramStorage> SaveRestore for NvramSpecServices<S> {
impl<S: VmmNvramStorage> SaveRestore for NvramSpecServices<S> {
type SavedState = state::SavedState;

fn save(&mut self) -> Result<Self::SavedState, SaveError> {
Expand All @@ -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(())
}
Expand Down Expand Up @@ -1526,7 +1535,7 @@ mod test {
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -56,7 +56,7 @@ pub trait NvramServicesExt {
}

#[async_trait::async_trait]
impl<S: InspectableNvramStorage> NvramServicesExt for NvramSpecServices<S> {
impl<S: VmmNvramStorage> NvramServicesExt for NvramSpecServices<S> {
async fn get_variable(
&mut self,
vendor: Guid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 34 additions & 1 deletion vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ pub struct HclCompatNvram<S> {
// reduced allocator pressure
#[cfg_attr(feature = "inspect", inspect(skip))] // internal bookkeeping - not worth inspecting
nvram_buf: Vec<u8>,

// 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.
Expand Down Expand Up @@ -129,6 +132,8 @@ impl<S: StorageBackend> HclCompatNvram<S> {
in_memory: in_memory::InMemoryNvram::new(),

nvram_buf: Vec::new(),

loaded: false,
}
}

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

async fn lazy_load_from_storage_inner(&mut self) -> Result<(), NvramStorageError> {
if !self.nvram_buf.is_empty() {
if self.loaded {
return Ok(());
}

tracing::info!("loading uefi nvram from storage");

let nvram_buf = self
.storage
.restore()
Expand Down Expand Up @@ -288,11 +295,13 @@ impl<S: StorageBackend> HclCompatNvram<S> {
));
}

self.loaded = true;
Ok(())
}

/// 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 {
Expand Down Expand Up @@ -473,6 +482,30 @@ impl<S: StorageBackend> NvramStorage for HclCompatNvram<S> {
}
}

#[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<S: StorageBackend> SaveRestore for HclCompatNvram<S> {
type SavedState = <in_memory::InMemoryNvram as SaveRestore>::SavedState;

fn save(&mut self) -> Result<Self::SavedState, SaveError> {
self.in_memory.save()
}

fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
if state.nvram.is_some() {
self.in_memory.restore(state)?;
self.loaded = true;
}
Ok(())
}
}
}

#[cfg(test)]
mod test {
use super::storage_backend::StorageBackend;
Expand Down
3 changes: 3 additions & 0 deletions vm/devices/firmware/uefi_nvram_storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading