From 8976f49338e44ef96506b2b89b02748a23d07ca5 Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Fri, 17 Jul 2026 19:26:53 +0000 Subject: [PATCH 01/11] Plumbing base secureboot template --- openhcl/underhill_core/src/worker.rs | 2 + openvmm/openvmm_core/src/worker/dispatch.rs | 3 + openvmm/openvmm_defs/src/config.rs | 1 + openvmm/openvmm_entry/src/lib.rs | 11 ++- openvmm/openvmm_entry/src/ttrpc/mod.rs | 1 + petri/src/vm/openvmm/construct.rs | 57 ++++++++------- .../firmware/firmware_uefi/src/resolver.rs | 73 ++++++++++++++++++- .../firmware_uefi_resources/src/lib.rs | 1 + .../hcl_compat_uefi_nvram_storage/src/lib.rs | 36 +++++++++ vmm_core/vm_manifest_builder/src/lib.rs | 2 + 10 files changed, 156 insertions(+), 31 deletions(-) diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index 192f0bbf40..2863d6a9fb 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -2562,6 +2562,7 @@ async fn new_underhill_vm( } } }; + let base_secure_boot_template_vars = base_vars.clone(); // check if vmgs includes custom UEFI JSON let custom_uefi_json_data = if let Some(vmgs_client) = vmgs_client.as_ref() { @@ -2599,6 +2600,7 @@ async fn new_underhill_vm( }; let config = firmware_uefi_resources::UefiConfig { + base_secure_boot_template_vars, custom_uefi_vars, secure_boot: dps.general.secure_boot_enabled, initial_generation_id, diff --git a/openvmm/openvmm_core/src/worker/dispatch.rs b/openvmm/openvmm_core/src/worker/dispatch.rs index e95aefdb51..41a4d170d6 100644 --- a/openvmm/openvmm_core/src/worker/dispatch.rs +++ b/openvmm/openvmm_core/src/worker/dispatch.rs @@ -212,6 +212,7 @@ impl Manifest { vpci_resources: config.vpci_resources, vmgs: config.vmgs, secure_boot_enabled: config.secure_boot_enabled, + base_secure_boot_template_vars: config.base_secure_boot_template_vars, custom_uefi_vars: config.custom_uefi_vars, firmware_event_send: config.firmware_event_send, debugger_rpc: config.debugger_rpc, @@ -263,6 +264,7 @@ pub struct Manifest { vpci_resources: Vec, vmgs: Option, secure_boot_enabled: bool, + base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, custom_uefi_vars: firmware_uefi_custom_vars::CustomVars, firmware_event_send: Option>, debugger_rpc: Option>, @@ -3941,6 +3943,7 @@ impl LoadedVm { vpci_resources: vec![], // TODO vmgs: None, // TODO secure_boot_enabled: false, // TODO + base_secure_boot_template_vars: Default::default(), // TODO custom_uefi_vars: Default::default(), // TODO firmware_event_send: self.inner.firmware_event_send, debugger_rpc: None, // TODO diff --git a/openvmm/openvmm_defs/src/config.rs b/openvmm/openvmm_defs/src/config.rs index b44781b5c9..a06e7e4da1 100644 --- a/openvmm/openvmm_defs/src/config.rs +++ b/openvmm/openvmm_defs/src/config.rs @@ -48,6 +48,7 @@ pub struct Config { pub vpci_resources: Vec, pub vmgs: Option, pub secure_boot_enabled: bool, + pub base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, pub custom_uefi_vars: firmware_uefi_custom_vars::CustomVars, // TODO: move FirmwareEvent somewhere not GED-specific. pub firmware_event_send: Option>, diff --git a/openvmm/openvmm_entry/src/lib.rs b/openvmm/openvmm_entry/src/lib.rs index 7a796b989f..7502c5e392 100644 --- a/openvmm/openvmm_entry/src/lib.rs +++ b/openvmm/openvmm_entry/src/lib.rs @@ -1158,7 +1158,7 @@ async fn vm_config_from_command_line( ); } - let custom_uefi_vars = { + let (base_secure_boot_template_vars, custom_uefi_vars) = { use firmware_uefi_custom_vars::CustomVars; // load base vars from specified template, or use an empty set of base @@ -1180,6 +1180,7 @@ async fn vm_config_from_command_line( }, None => CustomVars::default(), }; + let base_secure_boot_template_vars = base_vars.clone(); // TODO: fallback to VMGS read if no command line flag was given @@ -1189,13 +1190,15 @@ async fn vm_config_from_command_line( }; // obtain the final custom uefi vars by applying the delta onto the base vars - match custom_uefi_json_data { + let custom_uefi_vars = match custom_uefi_json_data { Some(data) => { let delta = hyperv_uefi_custom_vars_json::load_delta_from_json(&data)?; base_vars.apply_delta(delta)? } None => base_vars, - } + }; + + (base_secure_boot_template_vars, custom_uefi_vars) }; let efi_diagnostics_log_level = match opt.efi_diagnostics_log_level.unwrap_or_default() { @@ -1219,6 +1222,7 @@ async fn vm_config_from_command_line( }; chipset = chipset.with_uefi(vm_manifest_builder::UefiManifest::new( arch, + base_secure_boot_template_vars.clone(), custom_uefi_vars.clone(), opt.secure_boot, log_level, @@ -2009,6 +2013,7 @@ async fn vm_config_from_command_line( vpci_resources, vmgs, secure_boot_enabled: opt.secure_boot, + base_secure_boot_template_vars, custom_uefi_vars, firmware_event_send: None, debugger_rpc: None, diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index d3655d8259..88ef5178f1 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -665,6 +665,7 @@ impl VmService { vpci_resources: vec![], vmgs: None, secure_boot_enabled: false, + base_secure_boot_template_vars: Default::default(), custom_uefi_vars: Default::default(), firmware_event_send: None, debugger_rpc: None, diff --git a/petri/src/vm/openvmm/construct.rs b/petri/src/vm/openvmm/construct.rs index b7d72232c4..74f12d9424 100644 --- a/petri/src/vm/openvmm/construct.rs +++ b/petri/src/vm/openvmm/construct.rs @@ -369,7 +369,7 @@ impl PetriVmConfigOpenVmm { // OpenhclUefi uses BaseChipsetType::HclHost, so it does not need this. if matches!(firmware, Firmware::Uefi { .. }) { let uefi_cfg = firmware.uefi_config(); - let custom_uefi_vars = + let base_secure_boot_template_vars = uefi_cfg.map_or_else(Default::default, |c| match (arch, c.secure_boot_template) { (MachineArch::X86_64, Some(SecureBootTemplate::MicrosoftWindows)) => { hyperv_secure_boot_templates::x64::microsoft_windows() @@ -387,6 +387,7 @@ impl PetriVmConfigOpenVmm { ) => hyperv_secure_boot_templates::aarch64::microsoft_uefi_ca(), (_, None) => Default::default(), }); + let custom_uefi_vars = base_secure_boot_template_vars.clone(); let secure_boot = uefi_cfg.is_some_and(|c| c.secure_boot_enabled); let log_level = match uefi_cfg .map(|c| c.efi_diagnostics_log_level) @@ -409,6 +410,7 @@ impl PetriVmConfigOpenVmm { MachineArch::X86_64 => vm_manifest_builder::MachineArch::X86_64, MachineArch::Aarch64 => vm_manifest_builder::MachineArch::Aarch64, }, + base_secure_boot_template_vars, custom_uefi_vars, secure_boot, log_level, @@ -505,31 +507,33 @@ impl PetriVmConfigOpenVmm { } }; - let (secure_boot_enabled, custom_uefi_vars) = firmware.uefi_config().map_or_else( - || (false, Default::default()), - |c| { - ( - c.secure_boot_enabled, - match (arch, c.secure_boot_template) { - (MachineArch::X86_64, Some(SecureBootTemplate::MicrosoftWindows)) => { - hyperv_secure_boot_templates::x64::microsoft_windows() - } - ( - MachineArch::X86_64, - Some(SecureBootTemplate::MicrosoftUefiCertificateAuthority), - ) => hyperv_secure_boot_templates::x64::microsoft_uefi_ca(), - (MachineArch::Aarch64, Some(SecureBootTemplate::MicrosoftWindows)) => { - hyperv_secure_boot_templates::aarch64::microsoft_windows() - } - ( - MachineArch::Aarch64, - Some(SecureBootTemplate::MicrosoftUefiCertificateAuthority), - ) => hyperv_secure_boot_templates::aarch64::microsoft_uefi_ca(), - (_, None) => Default::default(), - }, - ) - }, - ); + let (secure_boot_enabled, base_secure_boot_template_vars) = + firmware.uefi_config().map_or_else( + || (false, Default::default()), + |c| { + ( + c.secure_boot_enabled, + match (arch, c.secure_boot_template) { + (MachineArch::X86_64, Some(SecureBootTemplate::MicrosoftWindows)) => { + hyperv_secure_boot_templates::x64::microsoft_windows() + } + ( + MachineArch::X86_64, + Some(SecureBootTemplate::MicrosoftUefiCertificateAuthority), + ) => hyperv_secure_boot_templates::x64::microsoft_uefi_ca(), + (MachineArch::Aarch64, Some(SecureBootTemplate::MicrosoftWindows)) => { + hyperv_secure_boot_templates::aarch64::microsoft_windows() + } + ( + MachineArch::Aarch64, + Some(SecureBootTemplate::MicrosoftUefiCertificateAuthority), + ) => hyperv_secure_boot_templates::aarch64::microsoft_uefi_ca(), + (_, None) => Default::default(), + }, + ) + }, + ); + let custom_uefi_vars = base_secure_boot_template_vars.clone(); let vmgs = if firmware.is_openhcl() { None @@ -632,6 +636,7 @@ impl PetriVmConfigOpenVmm { vga_firmware, secure_boot_enabled, + base_secure_boot_template_vars, custom_uefi_vars, vmgs, diff --git a/vm/devices/firmware/firmware_uefi/src/resolver.rs b/vm/devices/firmware/firmware_uefi/src/resolver.rs index 65044dd2e6..3e96eccdca 100644 --- a/vm/devices/firmware/firmware_uefi/src/resolver.rs +++ b/vm/devices/firmware/firmware_uefi/src/resolver.rs @@ -17,8 +17,12 @@ use firmware_uefi_resources::UefiDeviceHandle; use firmware_uefi_resources::UefiLoggerHandleKind; use firmware_uefi_resources::UefiVsmConfigHandleKind; use firmware_uefi_resources::UefiWatchdogPlatformHandleKind; +use hcl_compat_uefi_nvram_storage::BaseSecureBootTemplateVariables; use hcl_compat_uefi_nvram_storage::HclCompatNvram; +use std::borrow::Cow; use thiserror::Error; +use uefi_nvram_specvars::signature_list::SignatureData; +use uefi_nvram_specvars::signature_list::SignatureList; use vm_resource::AsyncResolveResource; use vm_resource::ResolveError; use vm_resource::ResourceResolver; @@ -57,6 +61,62 @@ pub enum ResolveUefiDeviceError { Init(#[from] crate::UefiInitError), } +fn base_secure_boot_template_variables( + custom_vars: &firmware_uefi_custom_vars::CustomVars, +) -> Option { + let signatures = custom_vars.signatures.as_ref()?; + + let mut pk = Vec::new(); + extend_signature_var(std::iter::once(&signatures.pk), &mut pk); + + let mut kek = Vec::new(); + extend_signature_var(&signatures.kek, &mut kek); + + let mut db = Vec::new(); + extend_signature_var(&signatures.db, &mut db); + + let mut dbx = Vec::new(); + extend_signature_var(&signatures.dbx, &mut dbx); + + Some(BaseSecureBootTemplateVariables::new(pk, kek, db, dbx)) +} + +fn extend_signature_var<'a>( + signatures: impl IntoIterator, + data: &mut Vec, +) { + use firmware_uefi_custom_vars::Signature; + use uefi_specs::hyperv::nvram::vars::MSFT_SECURE_BOOT_PRODUCTION_GUID; + + for signature in signatures { + match signature { + Signature::X509(certs) => { + for cert in certs { + SignatureList::X509(SignatureData::new_x509( + MSFT_SECURE_BOOT_PRODUCTION_GUID, + Cow::Borrowed(cert.0.as_slice()), + )) + .extend_as_spec_signature_list(data); + } + } + Signature::Sha256(digests) => { + SignatureList::Sha256( + digests + .iter() + .map(|digest| { + SignatureData::new_sha256( + MSFT_SECURE_BOOT_PRODUCTION_GUID, + Cow::Borrowed(&digest.0), + ) + }) + .collect(), + ) + .extend_as_spec_signature_list(data); + } + } + } +} + // The ACPI GPE0 line to use for generation ID. This must match the value in // the DSDT. const GPE0_LINE_GENERATION_ID: u32 = 0; @@ -133,12 +193,21 @@ impl AsyncResolveResource for UefiDev } }; - let nvram_storage = Box::new(HclCompatNvram::new( + let nvram_storage = HclCompatNvram::new( vmm_core::emuplat::hcl_compat_uefi_nvram_storage::VmgsStorageBackendAdapter( nvram_storage, ), storage_quirks, - )); + ); + let nvram_storage = if config.secure_boot { + match base_secure_boot_template_variables(&config.base_secure_boot_template_vars) { + Some(template) => nvram_storage.with_base_secure_boot_template_variables(template), + None => nvram_storage, + } + } else { + nvram_storage + }; + let nvram_storage = Box::new(nvram_storage); let gm = input.encrypted_guest_memory.clone(); let runtime_deps = UefiRuntimeDeps { diff --git a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs index 077b862372..6aea9785d1 100644 --- a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs @@ -153,6 +153,7 @@ pub fn debug_level_to_string(debug_level: u32) -> Cow<'static, str> { /// Static configuration for the UEFI device. #[derive(Clone, Protobuf)] pub struct UefiConfig { + pub base_secure_boot_template_vars: CustomVars, pub custom_uefi_vars: CustomVars, pub secure_boot: bool, pub initial_generation_id: [u8; 16], 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 d2d5d4f004..4b0b932f0f 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 @@ -43,6 +43,26 @@ const EFI_MAX_VARIABLE_DATA_SIZE: usize = 32 * 1024; const INITIAL_NVRAM_SIZE: usize = 32768; const MAXIMUM_NVRAM_SIZE: usize = INITIAL_NVRAM_SIZE * 4; +/// Base Secure Boot template variable contents expected to be present in loaded NVRAM. +#[derive(Clone, Debug)] +pub struct BaseSecureBootTemplateVariables { + pk: Vec, + kek: Vec, + db: Vec, + dbx: Vec, +} + +impl BaseSecureBootTemplateVariables { + /// Create a new base Secure Boot template variable set. + pub fn new(pk: Vec, kek: Vec, db: Vec, dbx: Vec) -> Self { + Self { pk, kek, db, dbx } + } + + fn is_empty(&self) -> bool { + self.pk.is_empty() && self.kek.is_empty() && self.db.is_empty() && self.dbx.is_empty() + } +} + mod format { use super::*; use open_enum::open_enum; @@ -98,6 +118,9 @@ pub struct HclCompatNvram { // whether the NVRAM has been loaded, either from storage or saved state loaded: bool, + + #[cfg_attr(feature = "inspect", inspect(skip))] + base_secure_boot_template_variables: Option, } impl HclCompatNvram { @@ -115,7 +138,20 @@ impl HclCompatNvram { nvram_buf: Vec::new(), loaded: false, + + base_secure_boot_template_variables: None, + } + } + + /// Store the base Secure Boot template variables for later observation. + pub fn with_base_secure_boot_template_variables( + mut self, + template: BaseSecureBootTemplateVariables, + ) -> Self { + if !template.is_empty() { + self.base_secure_boot_template_variables = Some(template); } + self } async fn lazy_load_from_storage(&mut self) -> Result<(), NvramStorageError> { diff --git a/vmm_core/vm_manifest_builder/src/lib.rs b/vmm_core/vm_manifest_builder/src/lib.rs index 9fcf7dff34..cdf3db7b32 100644 --- a/vmm_core/vm_manifest_builder/src/lib.rs +++ b/vmm_core/vm_manifest_builder/src/lib.rs @@ -113,6 +113,7 @@ impl UefiManifest { /// [`SystemTimeClockHandle`]: chipset_resources::cmos_rtc_time_source::SystemTimeClockHandle pub fn new( arch: MachineArch, + base_secure_boot_template_vars: CustomVars, custom_uefi_vars: CustomVars, secure_boot: bool, diagnostics_log_level: LogLevel, @@ -124,6 +125,7 @@ impl UefiManifest { getrandom::fill(&mut initial_generation_id).expect("rng failure"); Self { config: UefiConfig { + base_secure_boot_template_vars, custom_uefi_vars, secure_boot, initial_generation_id, From 724caaf6a64191f36a6f5460cbaa7e33e39559cf Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Fri, 17 Jul 2026 20:03:34 +0000 Subject: [PATCH 02/11] Report base secure boot template presence --- Cargo.lock | 2 + .../hcl_compat_uefi_nvram_storage/Cargo.toml | 2 + .../hcl_compat_uefi_nvram_storage/src/lib.rs | 207 +++++++++++++++++- 3 files changed, 210 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index dbd70f195f..6cc72d04a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3237,7 +3237,9 @@ dependencies = [ "thiserror 2.0.16", "tracing", "ucs2 0.0.0", + "uefi_nvram_specvars", "uefi_nvram_storage", + "uefi_specs", "vmcore", "wchar", "zerocopy", 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 1c6caf7d00..963b80ebc6 100644 --- a/vm/devices/firmware/hcl_compat_uefi_nvram_storage/Cargo.toml +++ b/vm/devices/firmware/hcl_compat_uefi_nvram_storage/Cargo.toml @@ -14,7 +14,9 @@ save_restore = [ "dep:vmcore", "uefi_nvram_storage/save_restore"] [dependencies] hcl_compat_uefi_nvram_resources.workspace = true +uefi_nvram_specvars.workspace = true uefi_nvram_storage.workspace = true +uefi_specs.workspace = true vmcore = { workspace = true, optional = true } cvm_tracing.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 4b0b932f0f..23c841fb07 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 @@ -22,14 +22,23 @@ use cvm_tracing::CVM_ALLOWED; use cvm_tracing::CVM_CONFIDENTIAL; use guid::Guid; use hcl_compat_uefi_nvram_resources::HclCompatNvramQuirks; -use std::fmt::Debug; +use std::collections::BTreeSet; use storage_backend::StorageBackend; use ucs2::Ucs2LeSlice; +use uefi_nvram_specvars::signature_list::ParseError as SignatureListParseError; +use uefi_nvram_specvars::signature_list::ParseSignatureList; +use uefi_nvram_specvars::signature_list::ParseSignatureLists; use uefi_nvram_storage::EFI_TIME; use uefi_nvram_storage::NextVariable; use uefi_nvram_storage::NvramStorage; use uefi_nvram_storage::NvramStorageError; use uefi_nvram_storage::in_memory; +use uefi_specs::uefi::nvram::EFI_VARIABLE_AUTHENTICATION_2; +use uefi_specs::uefi::nvram::signature_list::EFI_SIGNATURE_DATA; +use uefi_specs::uefi::nvram::vars; +use uefi_specs::uefi::signing::EFI_CERT_TYPE_PKCS7_GUID; +use uefi_specs::uefi::signing::WIN_CERT_TYPE_EFI_GUID; +use uefi_specs::uefi::signing::WIN_CERTIFICATE_UEFI_GUID; use zerocopy::FromBytes; use zerocopy::Immutable; use zerocopy::IntoBytes; @@ -42,6 +51,9 @@ const EFI_MAX_VARIABLE_DATA_SIZE: usize = 32 * 1024; // TODO: how big required for secure boot with db/dbx? const INITIAL_NVRAM_SIZE: usize = 32768; const MAXIMUM_NVRAM_SIZE: usize = INITIAL_NVRAM_SIZE * 4; +const WIN_CERT_REVISION_2_0: u16 = 0x0200; + +type SignatureSet = BTreeSet<(EFI_SIGNATURE_DATA, Vec)>; /// Base Secure Boot template variable contents expected to be present in loaded NVRAM. #[derive(Clone, Debug)] @@ -58,6 +70,7 @@ impl BaseSecureBootTemplateVariables { Self { pk, kek, db, dbx } } + /// Return whether there are no base Secure Boot template variables to track. fn is_empty(&self) -> bool { self.pk.is_empty() && self.kek.is_empty() && self.db.is_empty() && self.dbx.is_empty() } @@ -178,6 +191,7 @@ impl HclCompatNvram { .await .map_err(|e| NvramStorageError::Load(e.into()))? .unwrap_or_default(); + let loaded_existing_state = !nvram_buf.is_empty(); if nvram_buf.len() > MAXIMUM_NVRAM_SIZE { return Err(NvramStorageError::Load( @@ -311,9 +325,118 @@ impl HclCompatNvram { } self.loaded = true; + if loaded_existing_state { + self.report_secure_boot_base_template_presence(); + } Ok(()) } + /// Report whether each base Secure Boot template variable is present in loaded NVRAM. + fn report_secure_boot_base_template_presence(&self) { + let Some(template) = &self.base_secure_boot_template_variables else { + tracing::warn!( + CVM_ALLOWED, + "no base secure boot template variables to check" + ); + return; + }; + + for (variable, (vendor, name), base_template_variable) in [ + ("PK", vars::PK(), template.pk.as_slice()), + ("KEK", vars::KEK(), template.kek.as_slice()), + ("db", vars::DB(), template.db.as_slice()), + ("dbx", vars::DBX(), template.dbx.as_slice()), + ] { + // Load the variable from NVRAM. + let loaded_variable = match self + .in_memory + .iter() + .find(|entry| entry.vendor == vendor && entry.name.as_bytes() == name.as_bytes()) + .map(|entry| entry.data) + { + Some(loaded_variable) if !loaded_variable.is_empty() => loaded_variable, + loaded_variable => { + tracing::warn!( + CVM_ALLOWED, + variable, + loaded_variable_bytes = loaded_variable.map_or(0, |data| data.len()), + "base secure boot template variable is missing in NVRAM" + ); + continue; + } + }; + + // Parse the loaded variable into a set of signatures. + let loaded_variable_bytes = loaded_variable.len(); + let loaded_signatures = match collect_signature_set(loaded_variable) { + Ok(signatures) => signatures, + Err(error) => { + tracing::warn!( + CVM_CONFIDENTIAL, + variable, + error = &error as &dyn std::error::Error, + "failed to parse loaded secure boot variable" + ); + continue; + } + }; + let loaded_entries = loaded_signatures.len(); + + // Parse the base template variable into a set of signatures. + let base_template_bytes = base_template_variable.len(); + let base_template_signatures = match collect_signature_set(base_template_variable) { + Ok(signatures) if !signatures.is_empty() => signatures, + Ok(_) => { + tracing::warn!( + CVM_ALLOWED, + variable, + "base secure boot template variable contains no signatures" + ); + continue; + } + Err(error) => { + tracing::warn!( + CVM_CONFIDENTIAL, + variable, + error = &error as &dyn std::error::Error, + "failed to parse base secure boot template variable" + ); + continue; + } + }; + let base_template_entries = base_template_signatures.len(); + + // Count how many base template signatures are missing from the loaded variable. + let missing_entries = base_template_signatures + .difference(&loaded_signatures) + .count(); + + if missing_entries == 0 { + tracing::info!( + CVM_ALLOWED, + variable, + base_template_entries, + loaded_entries, + missing_entries, + base_template_bytes, + loaded_variable_bytes, + "base secure boot template variable is present" + ); + } else { + tracing::warn!( + CVM_ALLOWED, + variable, + base_template_entries, + loaded_entries, + missing_entries, + base_template_bytes, + loaded_variable_bytes, + "base secure boot template variable is missing" + ); + } + } + } + /// Dump in-memory nvram to the underlying storage device. async fn flush_storage(&mut self) -> Result<(), NvramStorageError> { self.nvram_buf.clear(); @@ -496,6 +619,52 @@ impl NvramStorage for HclCompatNvram { } } +/// Parse a serialized Secure Boot variable into a comparable set of signatures. +fn collect_signature_set(data: &[u8]) -> Result { + let mut signatures = BTreeSet::new(); + + for list in ParseSignatureLists::new(signature_list_payload(data)) { + match list? { + ParseSignatureList::X509(certs) => { + for cert in certs { + let cert = cert?; + signatures.insert((cert.header, cert.data.0.as_ref().to_vec())); + } + } + ParseSignatureList::Sha256(digests) => { + for digest in digests { + let digest = digest?; + signatures.insert((digest.header, digest.data.0.as_ref().to_vec())); + } + } + } + } + + Ok(signatures) +} + +/// Return the `EFI_SIGNATURE_LIST` payload, skipping a valid auth header if present. +fn signature_list_payload(data: &[u8]) -> &[u8] { + let Ok((auth, _)) = EFI_VARIABLE_AUTHENTICATION_2::read_from_prefix(data) else { + return data; + }; + + if auth.auth_info.header.revision != WIN_CERT_REVISION_2_0 + || auth.auth_info.header.certificate_type != WIN_CERT_TYPE_EFI_GUID + || auth.auth_info.cert_type != EFI_CERT_TYPE_PKCS7_GUID + { + return data; + } + + let cert_len = auth.auth_info.header.length as usize; + if cert_len < size_of::() { + return data; + } + + let auth_len = size_of_val(&auth.timestamp) + cert_len; + data.get(auth_len..).unwrap_or(data) +} + #[cfg(feature = "save_restore")] mod save_restore { use super::*; @@ -526,10 +695,46 @@ mod test { use super::storage_backend::StorageBackendError; use super::*; use pal_async::async_test; + use std::borrow::Cow; use ucs2::Ucs2LeVec; + use uefi_nvram_specvars::signature_list::SignatureData; + use uefi_nvram_specvars::signature_list::SignatureList; use uefi_nvram_storage::in_memory::impl_agnostic_tests; use wchar::wchz; + const TEST_OWNER: Guid = Guid { + data1: 1, + data2: 0, + data3: 0, + data4: [0; 8], + }; + + fn x509_variable(certs: &[&'static [u8]]) -> Vec { + let mut data = Vec::new(); + for cert in certs { + SignatureList::X509(SignatureData::new_x509(TEST_OWNER, Cow::Borrowed(*cert))) + .extend_as_spec_signature_list(&mut data); + } + data + } + + fn signature_set(data: &[u8]) -> SignatureSet { + collect_signature_set(data).unwrap() + } + + #[test] + fn base_secure_boot_template_variable_counts_missing_entries() { + let base_data = x509_variable(&[b"cert1", b"cert2"]); + let loaded_data = x509_variable(&[b"cert1", b"cert3"]); + let base = signature_set(&base_data); + let loaded = signature_set(&loaded_data); + let missing_entries = base.difference(&loaded).count(); + + assert_eq!(base.len(), 2); + assert_eq!(loaded.len(), 2); + assert_eq!(missing_entries, 1); + } + /// An ephemeral implementation of [`StorageBackend`] backed by an in-memory /// buffer. Useful for tests, stateless VM scenarios. #[derive(Default)] From e1ce9b5d0154f9c1803aa15318ebd49286c553c1 Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Fri, 17 Jul 2026 21:20:53 +0000 Subject: [PATCH 03/11] Invoke on template injection --- .../firmware/firmware_uefi/src/service/nvram/mod.rs | 8 +++++++- .../src/service/nvram/spec_services/mod.rs | 5 +++++ .../firmware/hcl_compat_uefi_nvram_storage/src/lib.rs | 4 ++++ vm/devices/firmware/uefi_nvram_storage/src/lib.rs | 11 +++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) 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 f088d4e10b..279a675397 100644 --- a/vm/devices/firmware/firmware_uefi/src/service/nvram/mod.rs +++ b/vm/devices/firmware/firmware_uefi/src/service/nvram/mod.rs @@ -84,7 +84,9 @@ impl NvramServices { }; if !is_restoring { - nvram.inject_vars_on_first_boot(custom_vars).await?; + nvram + .inject_vars_on_first_boot(custom_vars, secure_boot_enabled) + .await?; nvram.inject_hyperv_vars().await?; nvram.setup_secure_boot(secure_boot_enabled).await?; } @@ -104,6 +106,7 @@ impl NvramServices { async fn inject_vars_on_first_boot( &mut self, custom_vars: CustomVars, + secure_boot_enabled: bool, ) -> Result<(), NvramSetupError> { // "First boot" is marked by having no variables in nvram storage if !self @@ -160,6 +163,9 @@ impl NvramServices { } self.inject_custom_vars(custom_vars).await?; + if secure_boot_enabled { + self.services.after_custom_vars_injected(); + } Ok(()) } 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 2fe5df92b6..0cf1ec09ae 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 @@ -242,6 +242,11 @@ impl NvramSpecServices { self.storage.is_empty().await } + /// Called after custom UEFI variables are injected on first boot. + pub fn after_custom_vars_injected(&self) { + self.storage.after_custom_vars_injected() + } + /// Update "SetupMode" based on the current value of "PK" /// /// From UEFI spec section 32.3 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 23c841fb07..62ceb4dda6 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 @@ -617,6 +617,10 @@ impl NvramStorage for HclCompatNvram { self.in_memory.next_variable(name_vendor).await } + + fn after_custom_vars_injected(&self) { + HclCompatNvram::report_secure_boot_base_template_presence(self) + } } /// Parse a serialized Secure Boot variable into a comparable set of signatures. diff --git a/vm/devices/firmware/uefi_nvram_storage/src/lib.rs b/vm/devices/firmware/uefi_nvram_storage/src/lib.rs index 36926d3020..305a88239f 100644 --- a/vm/devices/firmware/uefi_nvram_storage/src/lib.rs +++ b/vm/devices/firmware/uefi_nvram_storage/src/lib.rs @@ -116,6 +116,9 @@ pub trait NvramStorage: Send + Sync { NextVariable::EndOfList )) } + + /// Called after custom UEFI variables are injected on first boot. + fn after_custom_vars_injected(&self) {} } #[async_trait::async_trait] @@ -167,6 +170,10 @@ impl NvramStorage for Box { ) -> Result { (**self).next_variable(name_vendor).await } + + fn after_custom_vars_injected(&self) { + (**self).after_custom_vars_injected() + } } /// Defines a trait that combines NvramStorage, Inspect, and SaveRestore @@ -236,6 +243,10 @@ mod save_restore { ) -> Result { (**self).next_variable(name_vendor).await } + + fn after_custom_vars_injected(&self) { + (**self).after_custom_vars_injected() + } } impl SaveRestore for Box> { From 211eae1199f622650d5eeb93a60230f17ab61163 Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Tue, 21 Jul 2026 20:55:13 +0000 Subject: [PATCH 04/11] Address Copilot review feedback --- openvmm/openvmm_core/src/worker/dispatch.rs | 2 +- openvmm/openvmm_defs/src/config.rs | 2 +- .../firmware/firmware_uefi_resources/src/lib.rs | 2 +- .../hcl_compat_uefi_nvram_storage/src/lib.rs | 15 +++++++++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/openvmm/openvmm_core/src/worker/dispatch.rs b/openvmm/openvmm_core/src/worker/dispatch.rs index 41a4d170d6..15e33fd2ce 100644 --- a/openvmm/openvmm_core/src/worker/dispatch.rs +++ b/openvmm/openvmm_core/src/worker/dispatch.rs @@ -264,7 +264,6 @@ pub struct Manifest { vpci_resources: Vec, vmgs: Option, secure_boot_enabled: bool, - base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, custom_uefi_vars: firmware_uefi_custom_vars::CustomVars, firmware_event_send: Option>, debugger_rpc: Option>, @@ -277,6 +276,7 @@ pub struct Manifest { rtc_delta_milliseconds: i64, automatic_guest_reset: bool, efi_diagnostics_log_level: LogLevel, + base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, } #[derive(Protobuf, SavedStateRoot)] diff --git a/openvmm/openvmm_defs/src/config.rs b/openvmm/openvmm_defs/src/config.rs index a06e7e4da1..ab7c3aa836 100644 --- a/openvmm/openvmm_defs/src/config.rs +++ b/openvmm/openvmm_defs/src/config.rs @@ -48,7 +48,6 @@ pub struct Config { pub vpci_resources: Vec, pub vmgs: Option, pub secure_boot_enabled: bool, - pub base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, pub custom_uefi_vars: firmware_uefi_custom_vars::CustomVars, // TODO: move FirmwareEvent somewhere not GED-specific. pub firmware_event_send: Option>, @@ -66,6 +65,7 @@ pub struct Config { /// allow the guest to reset without notifying the client pub automatic_guest_reset: bool, pub efi_diagnostics_log_level: EfiDiagnosticsLogLevelType, + pub base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, } pub const DEFAULT_GIC_DISTRIBUTOR_BASE: u64 = 0xFFFF_0000; diff --git a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs index 6aea9785d1..ca0881bf5e 100644 --- a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs @@ -153,7 +153,6 @@ pub fn debug_level_to_string(debug_level: u32) -> Cow<'static, str> { /// Static configuration for the UEFI device. #[derive(Clone, Protobuf)] pub struct UefiConfig { - pub base_secure_boot_template_vars: CustomVars, pub custom_uefi_vars: CustomVars, pub secure_boot: bool, pub initial_generation_id: [u8; 16], @@ -161,6 +160,7 @@ pub struct UefiConfig { pub command_set: UefiCommandSet, pub diagnostics_log_level: LogLevel, pub diagnostics_rate_limit: Option, + pub base_secure_boot_template_vars: CustomVars, } /// Resource kind for the platform-provided UEFI logger. 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 62ceb4dda6..45cabe6d05 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 @@ -18,6 +18,8 @@ pub mod storage_backend; +use core::mem::size_of; +use core::mem::size_of_val; use cvm_tracing::CVM_ALLOWED; use cvm_tracing::CVM_CONFIDENTIAL; use guid::Guid; @@ -334,10 +336,6 @@ impl HclCompatNvram { /// Report whether each base Secure Boot template variable is present in loaded NVRAM. fn report_secure_boot_base_template_presence(&self) { let Some(template) = &self.base_secure_boot_template_variables else { - tracing::warn!( - CVM_ALLOWED, - "no base secure boot template variables to check" - ); return; }; @@ -739,6 +737,15 @@ mod test { assert_eq!(missing_entries, 1); } + #[test] + fn secure_boot_template_variable_skips_auth_header() { + let data = x509_variable(&[b"cert1"]); + let mut authenticated_data = EFI_VARIABLE_AUTHENTICATION_2::DUMMY.as_bytes().to_vec(); + authenticated_data.extend_from_slice(&data); + + assert_eq!(signature_set(&authenticated_data), signature_set(&data)); + } + /// An ephemeral implementation of [`StorageBackend`] backed by an in-memory /// buffer. Useful for tests, stateless VM scenarios. #[derive(Default)] From bae6357d35bab0a2e281f920236e48d4c987c4c8 Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Tue, 21 Jul 2026 22:21:35 +0000 Subject: [PATCH 05/11] Attach revision to secure boot templates --- openhcl/underhill_core/src/worker.rs | 6 +++++ openvmm/openvmm_core/src/worker/dispatch.rs | 3 +++ openvmm/openvmm_defs/src/config.rs | 1 + openvmm/openvmm_entry/src/lib.rs | 5 ++++ openvmm/openvmm_entry/src/ttrpc/mod.rs | 1 + petri/src/vm/openvmm/construct.rs | 9 +++++++ .../firmware/firmware_uefi/src/resolver.rs | 14 +++++++++-- .../firmware_uefi_resources/src/lib.rs | 1 + .../hcl_compat_uefi_nvram_storage/src/lib.rs | 25 +++++++++++++++++-- .../hyperv_secure_boot_templates/src/lib.rs | 5 ++++ vmm_core/vm_manifest_builder/src/lib.rs | 2 ++ 11 files changed, 68 insertions(+), 4 deletions(-) diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index 2863d6a9fb..833e77e4a0 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -2541,6 +2541,11 @@ async fn new_underhill_vm( use guest_emulation_transport::api::platform_settings::SecureBootTemplateType; // map the GET's template enum onto the hardcoded secureboot template type + let base_secure_boot_template_revision = (!matches!( + dps.general.secure_boot_template, + SecureBootTemplateType::None + )) + .then(|| hyperv_secure_boot_templates::BASELINE_REVISION.to_string()); let base_vars = match dps.general.secure_boot_template { SecureBootTemplateType::None => CustomVars::default(), SecureBootTemplateType::MicrosoftWindows => { @@ -2621,6 +2626,7 @@ async fn new_underhill_vm( } }, diagnostics_rate_limit: env_cfg.efi_diagnostics_rate_limit, + base_secure_boot_template_revision, }; // Register the platform resolvers used by the resource-model UEFI diff --git a/openvmm/openvmm_core/src/worker/dispatch.rs b/openvmm/openvmm_core/src/worker/dispatch.rs index 15e33fd2ce..63c0c932e6 100644 --- a/openvmm/openvmm_core/src/worker/dispatch.rs +++ b/openvmm/openvmm_core/src/worker/dispatch.rs @@ -213,6 +213,7 @@ impl Manifest { vmgs: config.vmgs, secure_boot_enabled: config.secure_boot_enabled, base_secure_boot_template_vars: config.base_secure_boot_template_vars, + base_secure_boot_template_revision: config.base_secure_boot_template_revision, custom_uefi_vars: config.custom_uefi_vars, firmware_event_send: config.firmware_event_send, debugger_rpc: config.debugger_rpc, @@ -277,6 +278,7 @@ pub struct Manifest { automatic_guest_reset: bool, efi_diagnostics_log_level: LogLevel, base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, + base_secure_boot_template_revision: Option, } #[derive(Protobuf, SavedStateRoot)] @@ -3944,6 +3946,7 @@ impl LoadedVm { vmgs: None, // TODO secure_boot_enabled: false, // TODO base_secure_boot_template_vars: Default::default(), // TODO + base_secure_boot_template_revision: None, // TODO custom_uefi_vars: Default::default(), // TODO firmware_event_send: self.inner.firmware_event_send, debugger_rpc: None, // TODO diff --git a/openvmm/openvmm_defs/src/config.rs b/openvmm/openvmm_defs/src/config.rs index ab7c3aa836..c978e9d824 100644 --- a/openvmm/openvmm_defs/src/config.rs +++ b/openvmm/openvmm_defs/src/config.rs @@ -66,6 +66,7 @@ pub struct Config { pub automatic_guest_reset: bool, pub efi_diagnostics_log_level: EfiDiagnosticsLogLevelType, pub base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, + pub base_secure_boot_template_revision: Option, } pub const DEFAULT_GIC_DISTRIBUTOR_BASE: u64 = 0xFFFF_0000; diff --git a/openvmm/openvmm_entry/src/lib.rs b/openvmm/openvmm_entry/src/lib.rs index 7502c5e392..39536d7d89 100644 --- a/openvmm/openvmm_entry/src/lib.rs +++ b/openvmm/openvmm_entry/src/lib.rs @@ -1158,6 +1158,9 @@ async fn vm_config_from_command_line( ); } + let base_secure_boot_template_revision = opt + .secure_boot_template + .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_string()); let (base_secure_boot_template_vars, custom_uefi_vars) = { use firmware_uefi_custom_vars::CustomVars; @@ -1223,6 +1226,7 @@ async fn vm_config_from_command_line( chipset = chipset.with_uefi(vm_manifest_builder::UefiManifest::new( arch, base_secure_boot_template_vars.clone(), + base_secure_boot_template_revision.clone(), custom_uefi_vars.clone(), opt.secure_boot, log_level, @@ -2014,6 +2018,7 @@ async fn vm_config_from_command_line( vmgs, secure_boot_enabled: opt.secure_boot, base_secure_boot_template_vars, + base_secure_boot_template_revision, custom_uefi_vars, firmware_event_send: None, debugger_rpc: None, diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index 88ef5178f1..612162c658 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -666,6 +666,7 @@ impl VmService { vmgs: None, secure_boot_enabled: false, base_secure_boot_template_vars: Default::default(), + base_secure_boot_template_revision: None, custom_uefi_vars: Default::default(), firmware_event_send: None, debugger_rpc: None, diff --git a/petri/src/vm/openvmm/construct.rs b/petri/src/vm/openvmm/construct.rs index 74f12d9424..a7ca622920 100644 --- a/petri/src/vm/openvmm/construct.rs +++ b/petri/src/vm/openvmm/construct.rs @@ -369,6 +369,9 @@ impl PetriVmConfigOpenVmm { // OpenhclUefi uses BaseChipsetType::HclHost, so it does not need this. if matches!(firmware, Firmware::Uefi { .. }) { let uefi_cfg = firmware.uefi_config(); + let base_secure_boot_template_revision = uefi_cfg + .and_then(|c| c.secure_boot_template) + .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_string()); let base_secure_boot_template_vars = uefi_cfg.map_or_else(Default::default, |c| match (arch, c.secure_boot_template) { (MachineArch::X86_64, Some(SecureBootTemplate::MicrosoftWindows)) => { @@ -411,6 +414,7 @@ impl PetriVmConfigOpenVmm { MachineArch::Aarch64 => vm_manifest_builder::MachineArch::Aarch64, }, base_secure_boot_template_vars, + base_secure_boot_template_revision, custom_uefi_vars, secure_boot, log_level, @@ -533,6 +537,10 @@ impl PetriVmConfigOpenVmm { ) }, ); + let base_secure_boot_template_revision = firmware + .uefi_config() + .and_then(|c| c.secure_boot_template) + .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_string()); let custom_uefi_vars = base_secure_boot_template_vars.clone(); let vmgs = if firmware.is_openhcl() { @@ -637,6 +645,7 @@ impl PetriVmConfigOpenVmm { secure_boot_enabled, base_secure_boot_template_vars, + base_secure_boot_template_revision, custom_uefi_vars, vmgs, diff --git a/vm/devices/firmware/firmware_uefi/src/resolver.rs b/vm/devices/firmware/firmware_uefi/src/resolver.rs index 3e96eccdca..5c461292c3 100644 --- a/vm/devices/firmware/firmware_uefi/src/resolver.rs +++ b/vm/devices/firmware/firmware_uefi/src/resolver.rs @@ -63,6 +63,7 @@ pub enum ResolveUefiDeviceError { fn base_secure_boot_template_variables( custom_vars: &firmware_uefi_custom_vars::CustomVars, + baseline_revision: Option<&str>, ) -> Option { let signatures = custom_vars.signatures.as_ref()?; @@ -78,7 +79,13 @@ fn base_secure_boot_template_variables( let mut dbx = Vec::new(); extend_signature_var(&signatures.dbx, &mut dbx); - Some(BaseSecureBootTemplateVariables::new(pk, kek, db, dbx)) + Some(BaseSecureBootTemplateVariables::new( + baseline_revision.map(str::to_owned), + pk, + kek, + db, + dbx, + )) } fn extend_signature_var<'a>( @@ -200,7 +207,10 @@ impl AsyncResolveResource for UefiDev storage_quirks, ); let nvram_storage = if config.secure_boot { - match base_secure_boot_template_variables(&config.base_secure_boot_template_vars) { + match base_secure_boot_template_variables( + &config.base_secure_boot_template_vars, + config.base_secure_boot_template_revision.as_deref(), + ) { Some(template) => nvram_storage.with_base_secure_boot_template_variables(template), None => nvram_storage, } diff --git a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs index ca0881bf5e..e9583b2c50 100644 --- a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs @@ -161,6 +161,7 @@ pub struct UefiConfig { pub diagnostics_log_level: LogLevel, pub diagnostics_rate_limit: Option, pub base_secure_boot_template_vars: CustomVars, + pub base_secure_boot_template_revision: Option, } /// Resource kind for the platform-provided UEFI logger. 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 45cabe6d05..d8451d2ed2 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 @@ -60,6 +60,7 @@ type SignatureSet = BTreeSet<(EFI_SIGNATURE_DATA, Vec)>; /// Base Secure Boot template variable contents expected to be present in loaded NVRAM. #[derive(Clone, Debug)] pub struct BaseSecureBootTemplateVariables { + baseline_revision: Option, pk: Vec, kek: Vec, db: Vec, @@ -68,8 +69,20 @@ pub struct BaseSecureBootTemplateVariables { impl BaseSecureBootTemplateVariables { /// Create a new base Secure Boot template variable set. - pub fn new(pk: Vec, kek: Vec, db: Vec, dbx: Vec) -> Self { - Self { pk, kek, db, dbx } + pub fn new( + baseline_revision: Option, + pk: Vec, + kek: Vec, + db: Vec, + dbx: Vec, + ) -> Self { + Self { + baseline_revision, + pk, + kek, + db, + dbx, + } } /// Return whether there are no base Secure Boot template variables to track. @@ -339,6 +352,14 @@ impl HclCompatNvram { return; }; + if let Some(baseline_revision) = &template.baseline_revision { + tracing::info!( + CVM_ALLOWED, + baseline_revision, + "secure boot base template baseline configured" + ); + } + for (variable, (vendor, name), base_template_variable) in [ ("PK", vars::PK(), template.pk.as_slice()), ("KEK", vars::KEK(), template.kek.as_slice()), diff --git a/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs b/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs index 3926eaec0c..2528e93838 100644 --- a/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs +++ b/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs @@ -11,6 +11,11 @@ //! gates! Unused templates should be stripped from the final binary by the //! linker. +/// Revision of the Secure Boot baseline represented by the built-in templates. +/// Update this value when the built-in templates are updated to a new baseline. +/// TODO: Should we change our baseline identifier? +pub const BASELINE_REVISION: &str = "July 2026"; + macro_rules! include_templates { ( $(($fn_name:ident, $path:literal),)* diff --git a/vmm_core/vm_manifest_builder/src/lib.rs b/vmm_core/vm_manifest_builder/src/lib.rs index cdf3db7b32..4cee3475eb 100644 --- a/vmm_core/vm_manifest_builder/src/lib.rs +++ b/vmm_core/vm_manifest_builder/src/lib.rs @@ -114,6 +114,7 @@ impl UefiManifest { pub fn new( arch: MachineArch, base_secure_boot_template_vars: CustomVars, + base_secure_boot_template_revision: Option, custom_uefi_vars: CustomVars, secure_boot: bool, diagnostics_log_level: LogLevel, @@ -136,6 +137,7 @@ impl UefiManifest { }, diagnostics_log_level, diagnostics_rate_limit, + base_secure_boot_template_revision, }, storage_quirks, generation_id_recv: mesh::channel().1, From bf7c75655eb62cf53792ed9e2a08d71a36355103 Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Tue, 21 Jul 2026 22:40:00 +0000 Subject: [PATCH 06/11] Document PK baseline semantics --- vm/devices/firmware/hcl_compat_uefi_nvram_storage/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) 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 d8451d2ed2..884aaf9527 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 @@ -360,6 +360,8 @@ impl HclCompatNvram { ); } + // TODO: Determine whether custom keys can be appended to PK before + // requiring an exact PK match instead of baseline set membership. for (variable, (vendor, name), base_template_variable) in [ ("PK", vars::PK(), template.pk.as_slice()), ("KEK", vars::KEK(), template.kek.as_slice()), From fbc278eda7efb596b84e4bc113ed16d4323658c8 Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Tue, 21 Jul 2026 23:29:13 +0000 Subject: [PATCH 07/11] Implement customization mode --- openhcl/underhill_core/src/worker.rs | 12 ++- openvmm/openvmm_core/src/worker/dispatch.rs | 4 + openvmm/openvmm_defs/src/config.rs | 2 + openvmm/openvmm_entry/src/lib.rs | 17 ++- openvmm/openvmm_entry/src/ttrpc/mod.rs | 1 + petri/src/vm/openvmm/construct.rs | 2 + .../firmware/firmware_uefi/src/resolver.rs | 51 ++++++++- .../firmware_uefi_custom_vars/src/delta.rs | 100 ++++++++++++++++++ .../firmware_uefi_resources/src/lib.rs | 2 + vmm_core/vm_manifest_builder/src/lib.rs | 3 + 10 files changed, 179 insertions(+), 15 deletions(-) diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index 833e77e4a0..a3705e77fd 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -2583,15 +2583,16 @@ async fn new_underhill_vm( // obtain the final custom uefi vars by applying the delta onto // the base vars - let custom_uefi_vars = match custom_uefi_json_data { + let (secure_boot_customization, custom_uefi_vars) = match custom_uefi_json_data { Some(data) => { - let res = (|| -> Result { + let res = (|| -> Result<_, anyhow::Error> { let delta = hyperv_uefi_custom_vars_json::load_delta_from_json(&data)?; - Ok(base_vars.apply_delta(delta)?) + let customization = delta.secure_boot_customization(); + Ok((Some(customization), base_vars.apply_delta(delta)?)) })(); match res { - Ok(vars) => vars, + Ok(result) => result, Err(e) => { tracing::error!(CVM_ALLOWED, "Failed to load custom UEFI vars"); get_client @@ -2601,7 +2602,7 @@ async fn new_underhill_vm( } } } - None => base_vars, + None => (None, base_vars), }; let config = firmware_uefi_resources::UefiConfig { @@ -2627,6 +2628,7 @@ async fn new_underhill_vm( }, diagnostics_rate_limit: env_cfg.efi_diagnostics_rate_limit, base_secure_boot_template_revision, + secure_boot_customization, }; // Register the platform resolvers used by the resource-model UEFI diff --git a/openvmm/openvmm_core/src/worker/dispatch.rs b/openvmm/openvmm_core/src/worker/dispatch.rs index 63c0c932e6..ed6f415edb 100644 --- a/openvmm/openvmm_core/src/worker/dispatch.rs +++ b/openvmm/openvmm_core/src/worker/dispatch.rs @@ -35,6 +35,7 @@ use cxl_spec::spec::CXL_COMPONENT_REGISTERS_SIZE_BYTES; use debug_ptr::DebugPtr; use disk_backend::Disk; use disk_backend::resolve::ResolveDiskParameters; +use firmware_uefi_custom_vars::delta::SecureBootCustomization; use firmware_uefi_resources::LogLevel; use floppy_resources::FloppyDiskConfig; use futures::FutureExt; @@ -214,6 +215,7 @@ impl Manifest { secure_boot_enabled: config.secure_boot_enabled, base_secure_boot_template_vars: config.base_secure_boot_template_vars, base_secure_boot_template_revision: config.base_secure_boot_template_revision, + secure_boot_customization: config.secure_boot_customization, custom_uefi_vars: config.custom_uefi_vars, firmware_event_send: config.firmware_event_send, debugger_rpc: config.debugger_rpc, @@ -279,6 +281,7 @@ pub struct Manifest { efi_diagnostics_log_level: LogLevel, base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, base_secure_boot_template_revision: Option, + secure_boot_customization: Option, } #[derive(Protobuf, SavedStateRoot)] @@ -3947,6 +3950,7 @@ impl LoadedVm { secure_boot_enabled: false, // TODO base_secure_boot_template_vars: Default::default(), // TODO base_secure_boot_template_revision: None, // TODO + secure_boot_customization: None, // TODO custom_uefi_vars: Default::default(), // TODO firmware_event_send: self.inner.firmware_event_send, debugger_rpc: None, // TODO diff --git a/openvmm/openvmm_defs/src/config.rs b/openvmm/openvmm_defs/src/config.rs index c978e9d824..e5590f1686 100644 --- a/openvmm/openvmm_defs/src/config.rs +++ b/openvmm/openvmm_defs/src/config.rs @@ -3,6 +3,7 @@ //! Configuration for the VM worker. +use firmware_uefi_custom_vars::delta::SecureBootCustomization; use guid::Guid; use input_core::InputData; use memory_range::MemoryRange; @@ -67,6 +68,7 @@ pub struct Config { pub efi_diagnostics_log_level: EfiDiagnosticsLogLevelType, pub base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, pub base_secure_boot_template_revision: Option, + pub secure_boot_customization: Option, } pub const DEFAULT_GIC_DISTRIBUTOR_BASE: u64 = 0xFFFF_0000; diff --git a/openvmm/openvmm_entry/src/lib.rs b/openvmm/openvmm_entry/src/lib.rs index 39536d7d89..ed23274611 100644 --- a/openvmm/openvmm_entry/src/lib.rs +++ b/openvmm/openvmm_entry/src/lib.rs @@ -1161,7 +1161,7 @@ async fn vm_config_from_command_line( let base_secure_boot_template_revision = opt .secure_boot_template .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_string()); - let (base_secure_boot_template_vars, custom_uefi_vars) = { + let (base_secure_boot_template_vars, secure_boot_customization, custom_uefi_vars) = { use firmware_uefi_custom_vars::CustomVars; // load base vars from specified template, or use an empty set of base @@ -1193,15 +1193,20 @@ async fn vm_config_from_command_line( }; // obtain the final custom uefi vars by applying the delta onto the base vars - let custom_uefi_vars = match custom_uefi_json_data { + let (secure_boot_customization, custom_uefi_vars) = match custom_uefi_json_data { Some(data) => { let delta = hyperv_uefi_custom_vars_json::load_delta_from_json(&data)?; - base_vars.apply_delta(delta)? + let customization = delta.secure_boot_customization(); + (Some(customization), base_vars.apply_delta(delta)?) } - None => base_vars, + None => (None, base_vars), }; - (base_secure_boot_template_vars, custom_uefi_vars) + ( + base_secure_boot_template_vars, + secure_boot_customization, + custom_uefi_vars, + ) }; let efi_diagnostics_log_level = match opt.efi_diagnostics_log_level.unwrap_or_default() { @@ -1227,6 +1232,7 @@ async fn vm_config_from_command_line( arch, base_secure_boot_template_vars.clone(), base_secure_boot_template_revision.clone(), + secure_boot_customization, custom_uefi_vars.clone(), opt.secure_boot, log_level, @@ -2019,6 +2025,7 @@ async fn vm_config_from_command_line( secure_boot_enabled: opt.secure_boot, base_secure_boot_template_vars, base_secure_boot_template_revision, + secure_boot_customization, custom_uefi_vars, firmware_event_send: None, debugger_rpc: None, diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index 612162c658..34325cc30f 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -667,6 +667,7 @@ impl VmService { secure_boot_enabled: false, base_secure_boot_template_vars: Default::default(), base_secure_boot_template_revision: None, + secure_boot_customization: None, custom_uefi_vars: Default::default(), firmware_event_send: None, debugger_rpc: None, diff --git a/petri/src/vm/openvmm/construct.rs b/petri/src/vm/openvmm/construct.rs index a7ca622920..2004415a91 100644 --- a/petri/src/vm/openvmm/construct.rs +++ b/petri/src/vm/openvmm/construct.rs @@ -415,6 +415,7 @@ impl PetriVmConfigOpenVmm { }, base_secure_boot_template_vars, base_secure_boot_template_revision, + None, custom_uefi_vars, secure_boot, log_level, @@ -646,6 +647,7 @@ impl PetriVmConfigOpenVmm { secure_boot_enabled, base_secure_boot_template_vars, base_secure_boot_template_revision, + secure_boot_customization: None, custom_uefi_vars, vmgs, diff --git a/vm/devices/firmware/firmware_uefi/src/resolver.rs b/vm/devices/firmware/firmware_uefi/src/resolver.rs index 5c461292c3..1d2712521c 100644 --- a/vm/devices/firmware/firmware_uefi/src/resolver.rs +++ b/vm/devices/firmware/firmware_uefi/src/resolver.rs @@ -11,6 +11,7 @@ use chipset_device_resources::IRQ_LINE_SET; use chipset_device_resources::ResolveChipsetDeviceHandleParams; use chipset_device_resources::ResolvedChipsetDevice; use chipset_resources::CmosRtcTimeSourceHandleKind; +use firmware_uefi_custom_vars::delta::SecureBootCustomization; use firmware_uefi_resources::ResolvedUefiWatchdogPlatform; use firmware_uefi_resources::UefiCommandSet; use firmware_uefi_resources::UefiDeviceHandle; @@ -207,12 +208,52 @@ impl AsyncResolveResource for UefiDev storage_quirks, ); let nvram_storage = if config.secure_boot { - match base_secure_boot_template_variables( - &config.base_secure_boot_template_vars, - config.base_secure_boot_template_revision.as_deref(), + // log secure boot customization mode + let customization_mode = config + .secure_boot_customization + .map(SecureBootCustomization::as_str) + .unwrap_or("none"); + tracing::info!( + custom_uefi_config_present = config.secure_boot_customization.is_some(), + customization_mode, + "secure boot custom UEFI configuration" + ); + + if matches!( + config.secure_boot_customization, + Some(SecureBootCustomization::FullReplace) ) { - Some(template) => nvram_storage.with_base_secure_boot_template_variables(template), - None => nvram_storage, + // Full replacement skips the baseline evaluation + tracing::info!( + baseline_configured = + config.base_secure_boot_template_vars.signatures.is_some(), + baseline_revision = config + .base_secure_boot_template_revision + .as_deref() + .unwrap_or("none"), + customization_mode, + "secure boot baseline evaluation skipped" + ); + nvram_storage + } else { + // Append, PartialReplace, or None: evaluate the baseline and apply it to the NVRAM storage + match base_secure_boot_template_variables( + &config.base_secure_boot_template_vars, + config.base_secure_boot_template_revision.as_deref(), + ) { + // Base template selected, evaluate even if customization is Append or PartialReplace + Some(template) => { + nvram_storage.with_base_secure_boot_template_variables(template) + } + // No base template selected, log that the baseline is not configured + None => { + tracing::info!( + baseline_configured = false, + "secure boot base template is empty" + ); + nvram_storage + } + } } } else { nvram_storage diff --git a/vm/devices/firmware/firmware_uefi_custom_vars/src/delta.rs b/vm/devices/firmware/firmware_uefi_custom_vars/src/delta.rs index 6bc20b6a41..21204627e3 100644 --- a/vm/devices/firmware/firmware_uefi_custom_vars/src/delta.rs +++ b/vm/devices/firmware/firmware_uefi_custom_vars/src/delta.rs @@ -6,6 +6,29 @@ use super::CustomVar; use super::Signature; +use mesh_protobuf::Protobuf; + +/// How custom Secure Boot signature variables modify a selected base template. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Protobuf)] +pub enum SecureBootCustomization { + /// Custom entries are appended to the base template. + Append, + /// Some base template variables are retained and others are replaced. + PartialReplace, + /// PK, KEK, db, and dbx are all explicitly replaced. + FullReplace, +} + +impl SecureBootCustomization { + /// Return the stable telemetry name for this customization mode. + pub fn as_str(self) -> &'static str { + match self { + Self::Append => "append", + Self::PartialReplace => "partial-replace", + Self::FullReplace => "full-replace", + } + } +} /// Collection of custom UEFI nvram variables. #[derive(Debug)] @@ -16,6 +39,24 @@ pub struct CustomVarsDelta { pub custom_vars: Vec<(String, CustomVar)>, } +impl CustomVarsDelta { + /// Classify how this delta modifies Secure Boot signature variables. + pub fn secure_boot_customization(&self) -> SecureBootCustomization { + match &self.signatures { + SignaturesDelta::Append(_) => SecureBootCustomization::Append, + SignaturesDelta::Replace(signatures) + if matches!(signatures.pk, SignatureDelta::Sig(_)) + && matches!(signatures.kek, SignatureDeltaVec::Sigs(_)) + && matches!(signatures.db, SignatureDeltaVec::Sigs(_)) + && matches!(signatures.dbx, SignatureDeltaVec::Sigs(_)) => + { + SecureBootCustomization::FullReplace + } + SignaturesDelta::Replace(_) => SecureBootCustomization::PartialReplace, + } + } +} + #[derive(Debug)] pub enum SignaturesDelta { /// Vars should append onto underlying template @@ -65,3 +106,62 @@ pub enum SignatureDeltaVec { /// It shouldn't be used in the hardcoded templates Default, } + +#[cfg(test)] +mod tests { + use super::*; + + fn signature() -> Signature { + Signature::Sha256(Vec::new()) + } + + #[test] + fn classifies_secure_boot_customization() { + let append = CustomVarsDelta { + signatures: SignaturesDelta::Append(SignaturesAppend { + kek: None, + db: None, + dbx: None, + moklist: None, + moklistx: None, + }), + custom_vars: Vec::new(), + }; + assert_eq!( + append.secure_boot_customization(), + SecureBootCustomization::Append + ); + + let partial_replace = CustomVarsDelta { + signatures: SignaturesDelta::Replace(SignaturesReplace { + pk: SignatureDelta::Sig(signature()), + kek: SignatureDeltaVec::Default, + db: SignatureDeltaVec::Sigs(Vec::new()), + dbx: SignatureDeltaVec::Sigs(Vec::new()), + moklist: None, + moklistx: None, + }), + custom_vars: Vec::new(), + }; + assert_eq!( + partial_replace.secure_boot_customization(), + SecureBootCustomization::PartialReplace + ); + + let full_replace = CustomVarsDelta { + signatures: SignaturesDelta::Replace(SignaturesReplace { + pk: SignatureDelta::Sig(signature()), + kek: SignatureDeltaVec::Sigs(Vec::new()), + db: SignatureDeltaVec::Sigs(Vec::new()), + dbx: SignatureDeltaVec::Sigs(Vec::new()), + moklist: None, + moklistx: None, + }), + custom_vars: Vec::new(), + }; + assert_eq!( + full_replace.secure_boot_customization(), + SecureBootCustomization::FullReplace + ); + } +} diff --git a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs index e9583b2c50..a0a041fe3a 100644 --- a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs @@ -16,6 +16,7 @@ pub use hcl_compat_uefi_nvram_resources::HclCompatNvramQuirks; use chipset_resources::CmosRtcTimeSourceHandleKind; use firmware_uefi_custom_vars::CustomVars; +use firmware_uefi_custom_vars::delta::SecureBootCustomization; use inspect::Inspect; use mesh::MeshPayload; use mesh_protobuf::Protobuf; @@ -162,6 +163,7 @@ pub struct UefiConfig { pub diagnostics_rate_limit: Option, pub base_secure_boot_template_vars: CustomVars, pub base_secure_boot_template_revision: Option, + pub secure_boot_customization: Option, } /// Resource kind for the platform-provided UEFI logger. diff --git a/vmm_core/vm_manifest_builder/src/lib.rs b/vmm_core/vm_manifest_builder/src/lib.rs index 4cee3475eb..5c8a734ef5 100644 --- a/vmm_core/vm_manifest_builder/src/lib.rs +++ b/vmm_core/vm_manifest_builder/src/lib.rs @@ -39,6 +39,7 @@ use chipset_resources::pm::HyperVPowerManagementDeviceHandle; use chipset_resources::pm::PIIX4_PM_BDF; use chipset_resources::pm::Piix4PowerManagementDeviceHandle; use firmware_uefi_custom_vars::CustomVars; +use firmware_uefi_custom_vars::delta::SecureBootCustomization; use firmware_uefi_resources::HclCompatNvramQuirks; use firmware_uefi_resources::LogLevel; use firmware_uefi_resources::UefiCommandSet; @@ -115,6 +116,7 @@ impl UefiManifest { arch: MachineArch, base_secure_boot_template_vars: CustomVars, base_secure_boot_template_revision: Option, + secure_boot_customization: Option, custom_uefi_vars: CustomVars, secure_boot: bool, diagnostics_log_level: LogLevel, @@ -138,6 +140,7 @@ impl UefiManifest { diagnostics_log_level, diagnostics_rate_limit, base_secure_boot_template_revision, + secure_boot_customization, }, storage_quirks, generation_id_recv: mesh::channel().1, From 63506a10a6dac974345390c0e1a60a71d9ebf19e Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Wed, 22 Jul 2026 20:14:34 +0000 Subject: [PATCH 08/11] Trim --- openhcl/underhill_core/src/worker.rs | 18 +--- openvmm/openvmm_core/src/worker/dispatch.rs | 7 -- openvmm/openvmm_defs/src/config.rs | 3 - openvmm/openvmm_entry/src/lib.rs | 22 +--- openvmm/openvmm_entry/src/ttrpc/mod.rs | 2 - petri/src/vm/openvmm/construct.rs | 11 -- .../firmware/firmware_uefi/src/resolver.rs | 63 ++--------- .../firmware_uefi_custom_vars/src/delta.rs | 100 ------------------ .../firmware_uefi_custom_vars/src/lib.rs | 62 +++++++++++ .../firmware_uefi_resources/src/lib.rs | 3 - .../hcl_compat_uefi_nvram_storage/src/lib.rs | 25 +---- .../hyperv_secure_boot_templates/src/lib.rs | 9 +- .../hyperv_uefi_custom_vars_json/src/lib.rs | 6 +- vmm_core/vm_manifest_builder/src/lib.rs | 5 - 14 files changed, 95 insertions(+), 241 deletions(-) diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index a3705e77fd..2863d6a9fb 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -2541,11 +2541,6 @@ async fn new_underhill_vm( use guest_emulation_transport::api::platform_settings::SecureBootTemplateType; // map the GET's template enum onto the hardcoded secureboot template type - let base_secure_boot_template_revision = (!matches!( - dps.general.secure_boot_template, - SecureBootTemplateType::None - )) - .then(|| hyperv_secure_boot_templates::BASELINE_REVISION.to_string()); let base_vars = match dps.general.secure_boot_template { SecureBootTemplateType::None => CustomVars::default(), SecureBootTemplateType::MicrosoftWindows => { @@ -2583,16 +2578,15 @@ async fn new_underhill_vm( // obtain the final custom uefi vars by applying the delta onto // the base vars - let (secure_boot_customization, custom_uefi_vars) = match custom_uefi_json_data { + let custom_uefi_vars = match custom_uefi_json_data { Some(data) => { - let res = (|| -> Result<_, anyhow::Error> { + let res = (|| -> Result { let delta = hyperv_uefi_custom_vars_json::load_delta_from_json(&data)?; - let customization = delta.secure_boot_customization(); - Ok((Some(customization), base_vars.apply_delta(delta)?)) + Ok(base_vars.apply_delta(delta)?) })(); match res { - Ok(result) => result, + Ok(vars) => vars, Err(e) => { tracing::error!(CVM_ALLOWED, "Failed to load custom UEFI vars"); get_client @@ -2602,7 +2596,7 @@ async fn new_underhill_vm( } } } - None => (None, base_vars), + None => base_vars, }; let config = firmware_uefi_resources::UefiConfig { @@ -2627,8 +2621,6 @@ async fn new_underhill_vm( } }, diagnostics_rate_limit: env_cfg.efi_diagnostics_rate_limit, - base_secure_boot_template_revision, - secure_boot_customization, }; // Register the platform resolvers used by the resource-model UEFI diff --git a/openvmm/openvmm_core/src/worker/dispatch.rs b/openvmm/openvmm_core/src/worker/dispatch.rs index ed6f415edb..15e33fd2ce 100644 --- a/openvmm/openvmm_core/src/worker/dispatch.rs +++ b/openvmm/openvmm_core/src/worker/dispatch.rs @@ -35,7 +35,6 @@ use cxl_spec::spec::CXL_COMPONENT_REGISTERS_SIZE_BYTES; use debug_ptr::DebugPtr; use disk_backend::Disk; use disk_backend::resolve::ResolveDiskParameters; -use firmware_uefi_custom_vars::delta::SecureBootCustomization; use firmware_uefi_resources::LogLevel; use floppy_resources::FloppyDiskConfig; use futures::FutureExt; @@ -214,8 +213,6 @@ impl Manifest { vmgs: config.vmgs, secure_boot_enabled: config.secure_boot_enabled, base_secure_boot_template_vars: config.base_secure_boot_template_vars, - base_secure_boot_template_revision: config.base_secure_boot_template_revision, - secure_boot_customization: config.secure_boot_customization, custom_uefi_vars: config.custom_uefi_vars, firmware_event_send: config.firmware_event_send, debugger_rpc: config.debugger_rpc, @@ -280,8 +277,6 @@ pub struct Manifest { automatic_guest_reset: bool, efi_diagnostics_log_level: LogLevel, base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, - base_secure_boot_template_revision: Option, - secure_boot_customization: Option, } #[derive(Protobuf, SavedStateRoot)] @@ -3949,8 +3944,6 @@ impl LoadedVm { vmgs: None, // TODO secure_boot_enabled: false, // TODO base_secure_boot_template_vars: Default::default(), // TODO - base_secure_boot_template_revision: None, // TODO - secure_boot_customization: None, // TODO custom_uefi_vars: Default::default(), // TODO firmware_event_send: self.inner.firmware_event_send, debugger_rpc: None, // TODO diff --git a/openvmm/openvmm_defs/src/config.rs b/openvmm/openvmm_defs/src/config.rs index e5590f1686..ab7c3aa836 100644 --- a/openvmm/openvmm_defs/src/config.rs +++ b/openvmm/openvmm_defs/src/config.rs @@ -3,7 +3,6 @@ //! Configuration for the VM worker. -use firmware_uefi_custom_vars::delta::SecureBootCustomization; use guid::Guid; use input_core::InputData; use memory_range::MemoryRange; @@ -67,8 +66,6 @@ pub struct Config { pub automatic_guest_reset: bool, pub efi_diagnostics_log_level: EfiDiagnosticsLogLevelType, pub base_secure_boot_template_vars: firmware_uefi_custom_vars::CustomVars, - pub base_secure_boot_template_revision: Option, - pub secure_boot_customization: Option, } pub const DEFAULT_GIC_DISTRIBUTOR_BASE: u64 = 0xFFFF_0000; diff --git a/openvmm/openvmm_entry/src/lib.rs b/openvmm/openvmm_entry/src/lib.rs index ed23274611..7502c5e392 100644 --- a/openvmm/openvmm_entry/src/lib.rs +++ b/openvmm/openvmm_entry/src/lib.rs @@ -1158,10 +1158,7 @@ async fn vm_config_from_command_line( ); } - let base_secure_boot_template_revision = opt - .secure_boot_template - .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_string()); - let (base_secure_boot_template_vars, secure_boot_customization, custom_uefi_vars) = { + let (base_secure_boot_template_vars, custom_uefi_vars) = { use firmware_uefi_custom_vars::CustomVars; // load base vars from specified template, or use an empty set of base @@ -1193,20 +1190,15 @@ async fn vm_config_from_command_line( }; // obtain the final custom uefi vars by applying the delta onto the base vars - let (secure_boot_customization, custom_uefi_vars) = match custom_uefi_json_data { + let custom_uefi_vars = match custom_uefi_json_data { Some(data) => { let delta = hyperv_uefi_custom_vars_json::load_delta_from_json(&data)?; - let customization = delta.secure_boot_customization(); - (Some(customization), base_vars.apply_delta(delta)?) + base_vars.apply_delta(delta)? } - None => (None, base_vars), + None => base_vars, }; - ( - base_secure_boot_template_vars, - secure_boot_customization, - custom_uefi_vars, - ) + (base_secure_boot_template_vars, custom_uefi_vars) }; let efi_diagnostics_log_level = match opt.efi_diagnostics_log_level.unwrap_or_default() { @@ -1231,8 +1223,6 @@ async fn vm_config_from_command_line( chipset = chipset.with_uefi(vm_manifest_builder::UefiManifest::new( arch, base_secure_boot_template_vars.clone(), - base_secure_boot_template_revision.clone(), - secure_boot_customization, custom_uefi_vars.clone(), opt.secure_boot, log_level, @@ -2024,8 +2014,6 @@ async fn vm_config_from_command_line( vmgs, secure_boot_enabled: opt.secure_boot, base_secure_boot_template_vars, - base_secure_boot_template_revision, - secure_boot_customization, custom_uefi_vars, firmware_event_send: None, debugger_rpc: None, diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index 34325cc30f..88ef5178f1 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -666,8 +666,6 @@ impl VmService { vmgs: None, secure_boot_enabled: false, base_secure_boot_template_vars: Default::default(), - base_secure_boot_template_revision: None, - secure_boot_customization: None, custom_uefi_vars: Default::default(), firmware_event_send: None, debugger_rpc: None, diff --git a/petri/src/vm/openvmm/construct.rs b/petri/src/vm/openvmm/construct.rs index 2004415a91..74f12d9424 100644 --- a/petri/src/vm/openvmm/construct.rs +++ b/petri/src/vm/openvmm/construct.rs @@ -369,9 +369,6 @@ impl PetriVmConfigOpenVmm { // OpenhclUefi uses BaseChipsetType::HclHost, so it does not need this. if matches!(firmware, Firmware::Uefi { .. }) { let uefi_cfg = firmware.uefi_config(); - let base_secure_boot_template_revision = uefi_cfg - .and_then(|c| c.secure_boot_template) - .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_string()); let base_secure_boot_template_vars = uefi_cfg.map_or_else(Default::default, |c| match (arch, c.secure_boot_template) { (MachineArch::X86_64, Some(SecureBootTemplate::MicrosoftWindows)) => { @@ -414,8 +411,6 @@ impl PetriVmConfigOpenVmm { MachineArch::Aarch64 => vm_manifest_builder::MachineArch::Aarch64, }, base_secure_boot_template_vars, - base_secure_boot_template_revision, - None, custom_uefi_vars, secure_boot, log_level, @@ -538,10 +533,6 @@ impl PetriVmConfigOpenVmm { ) }, ); - let base_secure_boot_template_revision = firmware - .uefi_config() - .and_then(|c| c.secure_boot_template) - .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_string()); let custom_uefi_vars = base_secure_boot_template_vars.clone(); let vmgs = if firmware.is_openhcl() { @@ -646,8 +637,6 @@ impl PetriVmConfigOpenVmm { secure_boot_enabled, base_secure_boot_template_vars, - base_secure_boot_template_revision, - secure_boot_customization: None, custom_uefi_vars, vmgs, diff --git a/vm/devices/firmware/firmware_uefi/src/resolver.rs b/vm/devices/firmware/firmware_uefi/src/resolver.rs index 1d2712521c..31292dc69b 100644 --- a/vm/devices/firmware/firmware_uefi/src/resolver.rs +++ b/vm/devices/firmware/firmware_uefi/src/resolver.rs @@ -11,7 +11,6 @@ use chipset_device_resources::IRQ_LINE_SET; use chipset_device_resources::ResolveChipsetDeviceHandleParams; use chipset_device_resources::ResolvedChipsetDevice; use chipset_resources::CmosRtcTimeSourceHandleKind; -use firmware_uefi_custom_vars::delta::SecureBootCustomization; use firmware_uefi_resources::ResolvedUefiWatchdogPlatform; use firmware_uefi_resources::UefiCommandSet; use firmware_uefi_resources::UefiDeviceHandle; @@ -64,7 +63,6 @@ pub enum ResolveUefiDeviceError { fn base_secure_boot_template_variables( custom_vars: &firmware_uefi_custom_vars::CustomVars, - baseline_revision: Option<&str>, ) -> Option { let signatures = custom_vars.signatures.as_ref()?; @@ -80,13 +78,7 @@ fn base_secure_boot_template_variables( let mut dbx = Vec::new(); extend_signature_var(&signatures.dbx, &mut dbx); - Some(BaseSecureBootTemplateVariables::new( - baseline_revision.map(str::to_owned), - pk, - kek, - db, - dbx, - )) + Some(BaseSecureBootTemplateVariables::new(pk, kek, db, dbx)) } fn extend_signature_var<'a>( @@ -208,52 +200,19 @@ impl AsyncResolveResource for UefiDev storage_quirks, ); let nvram_storage = if config.secure_boot { - // log secure boot customization mode - let customization_mode = config - .secure_boot_customization - .map(SecureBootCustomization::as_str) - .unwrap_or("none"); tracing::info!( - custom_uefi_config_present = config.secure_boot_customization.is_some(), - customization_mode, - "secure boot custom UEFI configuration" + baseline_configured = config.base_secure_boot_template_vars.signatures.is_some(), + baseline_revision = config + .base_secure_boot_template_vars + .baseline_revision() + .unwrap_or("none"), + custom_uefi_config_present = config.custom_uefi_vars.custom_uefi_config_present(), + "secure boot configuration" ); - if matches!( - config.secure_boot_customization, - Some(SecureBootCustomization::FullReplace) - ) { - // Full replacement skips the baseline evaluation - tracing::info!( - baseline_configured = - config.base_secure_boot_template_vars.signatures.is_some(), - baseline_revision = config - .base_secure_boot_template_revision - .as_deref() - .unwrap_or("none"), - customization_mode, - "secure boot baseline evaluation skipped" - ); - nvram_storage - } else { - // Append, PartialReplace, or None: evaluate the baseline and apply it to the NVRAM storage - match base_secure_boot_template_variables( - &config.base_secure_boot_template_vars, - config.base_secure_boot_template_revision.as_deref(), - ) { - // Base template selected, evaluate even if customization is Append or PartialReplace - Some(template) => { - nvram_storage.with_base_secure_boot_template_variables(template) - } - // No base template selected, log that the baseline is not configured - None => { - tracing::info!( - baseline_configured = false, - "secure boot base template is empty" - ); - nvram_storage - } - } + match base_secure_boot_template_variables(&config.base_secure_boot_template_vars) { + Some(template) => nvram_storage.with_base_secure_boot_template_variables(template), + None => nvram_storage, } } else { nvram_storage diff --git a/vm/devices/firmware/firmware_uefi_custom_vars/src/delta.rs b/vm/devices/firmware/firmware_uefi_custom_vars/src/delta.rs index 21204627e3..6bc20b6a41 100644 --- a/vm/devices/firmware/firmware_uefi_custom_vars/src/delta.rs +++ b/vm/devices/firmware/firmware_uefi_custom_vars/src/delta.rs @@ -6,29 +6,6 @@ use super::CustomVar; use super::Signature; -use mesh_protobuf::Protobuf; - -/// How custom Secure Boot signature variables modify a selected base template. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Protobuf)] -pub enum SecureBootCustomization { - /// Custom entries are appended to the base template. - Append, - /// Some base template variables are retained and others are replaced. - PartialReplace, - /// PK, KEK, db, and dbx are all explicitly replaced. - FullReplace, -} - -impl SecureBootCustomization { - /// Return the stable telemetry name for this customization mode. - pub fn as_str(self) -> &'static str { - match self { - Self::Append => "append", - Self::PartialReplace => "partial-replace", - Self::FullReplace => "full-replace", - } - } -} /// Collection of custom UEFI nvram variables. #[derive(Debug)] @@ -39,24 +16,6 @@ pub struct CustomVarsDelta { pub custom_vars: Vec<(String, CustomVar)>, } -impl CustomVarsDelta { - /// Classify how this delta modifies Secure Boot signature variables. - pub fn secure_boot_customization(&self) -> SecureBootCustomization { - match &self.signatures { - SignaturesDelta::Append(_) => SecureBootCustomization::Append, - SignaturesDelta::Replace(signatures) - if matches!(signatures.pk, SignatureDelta::Sig(_)) - && matches!(signatures.kek, SignatureDeltaVec::Sigs(_)) - && matches!(signatures.db, SignatureDeltaVec::Sigs(_)) - && matches!(signatures.dbx, SignatureDeltaVec::Sigs(_)) => - { - SecureBootCustomization::FullReplace - } - SignaturesDelta::Replace(_) => SecureBootCustomization::PartialReplace, - } - } -} - #[derive(Debug)] pub enum SignaturesDelta { /// Vars should append onto underlying template @@ -106,62 +65,3 @@ pub enum SignatureDeltaVec { /// It shouldn't be used in the hardcoded templates Default, } - -#[cfg(test)] -mod tests { - use super::*; - - fn signature() -> Signature { - Signature::Sha256(Vec::new()) - } - - #[test] - fn classifies_secure_boot_customization() { - let append = CustomVarsDelta { - signatures: SignaturesDelta::Append(SignaturesAppend { - kek: None, - db: None, - dbx: None, - moklist: None, - moklistx: None, - }), - custom_vars: Vec::new(), - }; - assert_eq!( - append.secure_boot_customization(), - SecureBootCustomization::Append - ); - - let partial_replace = CustomVarsDelta { - signatures: SignaturesDelta::Replace(SignaturesReplace { - pk: SignatureDelta::Sig(signature()), - kek: SignatureDeltaVec::Default, - db: SignatureDeltaVec::Sigs(Vec::new()), - dbx: SignatureDeltaVec::Sigs(Vec::new()), - moklist: None, - moklistx: None, - }), - custom_vars: Vec::new(), - }; - assert_eq!( - partial_replace.secure_boot_customization(), - SecureBootCustomization::PartialReplace - ); - - let full_replace = CustomVarsDelta { - signatures: SignaturesDelta::Replace(SignaturesReplace { - pk: SignatureDelta::Sig(signature()), - kek: SignatureDeltaVec::Sigs(Vec::new()), - db: SignatureDeltaVec::Sigs(Vec::new()), - dbx: SignatureDeltaVec::Sigs(Vec::new()), - moklist: None, - moklistx: None, - }), - custom_vars: Vec::new(), - }; - assert_eq!( - full_replace.secure_boot_customization(), - SecureBootCustomization::FullReplace - ); - } -} diff --git a/vm/devices/firmware/firmware_uefi_custom_vars/src/lib.rs b/vm/devices/firmware/firmware_uefi_custom_vars/src/lib.rs index 9af59ad2a6..acda7591fd 100644 --- a/vm/devices/firmware/firmware_uefi_custom_vars/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi_custom_vars/src/lib.rs @@ -21,6 +21,8 @@ pub struct CustomVars { pub signatures: Option, /// Any additional custom vars pub custom_vars: Vec<(String, CustomVar)>, + baseline_revision: Option, + custom_uefi_config_present: bool, } #[derive(Debug, Clone, Protobuf)] @@ -82,6 +84,34 @@ impl CustomVars { CustomVars::default() } + /// Create a set of custom variables from signatures and additional variables. + pub fn from_parts( + signatures: Option, + custom_vars: Vec<(String, CustomVar)>, + ) -> Self { + Self { + signatures, + custom_vars, + ..Default::default() + } + } + + /// Associate these variables with the built-in baseline revision they represent. + pub fn with_baseline_revision(mut self, revision: impl Into) -> Self { + self.baseline_revision = Some(revision.into()); + self + } + + /// Return the built-in baseline revision associated with these variables. + pub fn baseline_revision(&self) -> Option<&str> { + self.baseline_revision.as_deref() + } + + /// Return whether customer-provided custom UEFI configuration was applied. + pub fn custom_uefi_config_present(&self) -> bool { + self.custom_uefi_config_present + } + /// Apply a delta on-top of an existing set of CustomVars. pub fn apply_delta(self, delta: delta::CustomVarsDelta) -> Result { use delta::SignatureDelta; @@ -90,6 +120,7 @@ impl CustomVars { use delta::SignaturesDelta; use delta::SignaturesReplace; + let baseline_revision = self.baseline_revision; let signatures = match (self.signatures, delta.signatures) { (None, SignaturesDelta::Append(..)) => return Err(ApplyDeltaError::AppendWithoutBase), ( @@ -251,6 +282,37 @@ impl CustomVars { Ok(CustomVars { signatures: Some(signatures), custom_vars, + baseline_revision, + custom_uefi_config_present: true, }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn apply_delta_preserves_template_provenance() { + let signature = || Signature::Sha256(Vec::new()); + let delta = delta::CustomVarsDelta { + signatures: delta::SignaturesDelta::Replace(delta::SignaturesReplace { + pk: delta::SignatureDelta::Sig(signature()), + kek: delta::SignatureDeltaVec::Sigs(Vec::new()), + db: delta::SignatureDeltaVec::Sigs(Vec::new()), + dbx: delta::SignatureDeltaVec::Sigs(Vec::new()), + moklist: None, + moklistx: None, + }), + custom_vars: Vec::new(), + }; + + let vars = CustomVars::new() + .with_baseline_revision("July 2026") + .apply_delta(delta) + .unwrap(); + + assert_eq!(vars.baseline_revision(), Some("July 2026")); + assert!(vars.custom_uefi_config_present()); + } +} diff --git a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs index a0a041fe3a..ca0881bf5e 100644 --- a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs @@ -16,7 +16,6 @@ pub use hcl_compat_uefi_nvram_resources::HclCompatNvramQuirks; use chipset_resources::CmosRtcTimeSourceHandleKind; use firmware_uefi_custom_vars::CustomVars; -use firmware_uefi_custom_vars::delta::SecureBootCustomization; use inspect::Inspect; use mesh::MeshPayload; use mesh_protobuf::Protobuf; @@ -162,8 +161,6 @@ pub struct UefiConfig { pub diagnostics_log_level: LogLevel, pub diagnostics_rate_limit: Option, pub base_secure_boot_template_vars: CustomVars, - pub base_secure_boot_template_revision: Option, - pub secure_boot_customization: Option, } /// Resource kind for the platform-provided UEFI logger. 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 884aaf9527..9ad38d4703 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 @@ -60,7 +60,6 @@ type SignatureSet = BTreeSet<(EFI_SIGNATURE_DATA, Vec)>; /// Base Secure Boot template variable contents expected to be present in loaded NVRAM. #[derive(Clone, Debug)] pub struct BaseSecureBootTemplateVariables { - baseline_revision: Option, pk: Vec, kek: Vec, db: Vec, @@ -69,20 +68,8 @@ pub struct BaseSecureBootTemplateVariables { impl BaseSecureBootTemplateVariables { /// Create a new base Secure Boot template variable set. - pub fn new( - baseline_revision: Option, - pk: Vec, - kek: Vec, - db: Vec, - dbx: Vec, - ) -> Self { - Self { - baseline_revision, - pk, - kek, - db, - dbx, - } + pub fn new(pk: Vec, kek: Vec, db: Vec, dbx: Vec) -> Self { + Self { pk, kek, db, dbx } } /// Return whether there are no base Secure Boot template variables to track. @@ -352,14 +339,6 @@ impl HclCompatNvram { return; }; - if let Some(baseline_revision) = &template.baseline_revision { - tracing::info!( - CVM_ALLOWED, - baseline_revision, - "secure boot base template baseline configured" - ); - } - // TODO: Determine whether custom keys can be appended to PK before // requiring an exact PK match instead of baseline set membership. for (variable, (vendor, name), base_template_variable) in [ diff --git a/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs b/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs index 2528e93838..7cfb06a5f6 100644 --- a/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs +++ b/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs @@ -32,7 +32,9 @@ macro_rules! include_templates { // in the final bin (given that much of the parsing + validation // code is shared between both templates and user custom uefi // JSON files), it may result in a nice .rodata size decrease. - hyperv_uefi_custom_vars_json::load_template_from_json(include_bytes!(concat!(env!("OUT_DIR"), "/", $path))).unwrap() + hyperv_uefi_custom_vars_json::load_template_from_json(include_bytes!(concat!(env!("OUT_DIR"), "/", $path))) + .unwrap() + .with_baseline_revision($crate::BASELINE_REVISION) } )* @@ -41,7 +43,10 @@ macro_rules! include_templates { $( #[test] fn $fn_name() { - super::$fn_name(); + assert_eq!( + super::$fn_name().baseline_revision(), + Some($crate::BASELINE_REVISION) + ); } )* } diff --git a/vm/devices/firmware/hyperv_uefi_custom_vars_json/src/lib.rs b/vm/devices/firmware/hyperv_uefi_custom_vars_json/src/lib.rs index e9ee6a8f6c..c97cea07c0 100644 --- a/vm/devices/firmware/hyperv_uefi_custom_vars_json/src/lib.rs +++ b/vm/devices/firmware/hyperv_uefi_custom_vars_json/src/lib.rs @@ -68,8 +68,8 @@ pub fn load_template_from_json( custom_vars, } = load_delta_from_json(data)?; - Ok(CustomVars { - signatures: Some(match signatures { + Ok(CustomVars::from_parts( + Some(match signatures { SignaturesDelta::Append(_) => panic!("hardcoded templates cannot use append"), SignaturesDelta::Replace(signatures) => Signatures { pk: deny_default(signatures.pk)?, @@ -89,7 +89,7 @@ pub fn load_template_from_json( }, }), custom_vars, - }) + )) } /// Parse a [`CustomVarsDelta`](firmware_uefi_custom_vars::delta::CustomVarsDelta) from a user diff --git a/vmm_core/vm_manifest_builder/src/lib.rs b/vmm_core/vm_manifest_builder/src/lib.rs index 5c8a734ef5..cdf3db7b32 100644 --- a/vmm_core/vm_manifest_builder/src/lib.rs +++ b/vmm_core/vm_manifest_builder/src/lib.rs @@ -39,7 +39,6 @@ use chipset_resources::pm::HyperVPowerManagementDeviceHandle; use chipset_resources::pm::PIIX4_PM_BDF; use chipset_resources::pm::Piix4PowerManagementDeviceHandle; use firmware_uefi_custom_vars::CustomVars; -use firmware_uefi_custom_vars::delta::SecureBootCustomization; use firmware_uefi_resources::HclCompatNvramQuirks; use firmware_uefi_resources::LogLevel; use firmware_uefi_resources::UefiCommandSet; @@ -115,8 +114,6 @@ impl UefiManifest { pub fn new( arch: MachineArch, base_secure_boot_template_vars: CustomVars, - base_secure_boot_template_revision: Option, - secure_boot_customization: Option, custom_uefi_vars: CustomVars, secure_boot: bool, diagnostics_log_level: LogLevel, @@ -139,8 +136,6 @@ impl UefiManifest { }, diagnostics_log_level, diagnostics_rate_limit, - base_secure_boot_template_revision, - secure_boot_customization, }, storage_quirks, generation_id_recv: mesh::channel().1, From 4a3042149f3d74dc28dc0c220151bfc12612732a Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Wed, 22 Jul 2026 20:44:45 +0000 Subject: [PATCH 09/11] feedback --- .../src/service/nvram/spec_services/mod.rs | 3 +- .../hcl_compat_uefi_nvram_storage/src/lib.rs | 52 +++++++++++++++---- .../firmware/uefi_nvram_storage/src/lib.rs | 3 +- 3 files changed, 46 insertions(+), 12 deletions(-) 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 0cf1ec09ae..f9a5a413b7 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 @@ -242,7 +242,8 @@ impl NvramSpecServices { self.storage.is_empty().await } - /// Called after custom UEFI variables are injected on first boot. + /// Called after custom UEFI variables are injected on first boot, + /// when secure boot is enabled. pub fn after_custom_vars_injected(&self) { self.storage.after_custom_vars_injected() } 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 9ad38d4703..d163584ba1 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 @@ -36,7 +36,6 @@ use uefi_nvram_storage::NvramStorage; use uefi_nvram_storage::NvramStorageError; use uefi_nvram_storage::in_memory; use uefi_specs::uefi::nvram::EFI_VARIABLE_AUTHENTICATION_2; -use uefi_specs::uefi::nvram::signature_list::EFI_SIGNATURE_DATA; use uefi_specs::uefi::nvram::vars; use uefi_specs::uefi::signing::EFI_CERT_TYPE_PKCS7_GUID; use uefi_specs::uefi::signing::WIN_CERT_TYPE_EFI_GUID; @@ -55,7 +54,14 @@ const INITIAL_NVRAM_SIZE: usize = 32768; const MAXIMUM_NVRAM_SIZE: usize = INITIAL_NVRAM_SIZE * 4; const WIN_CERT_REVISION_2_0: u16 = 0x0200; -type SignatureSet = BTreeSet<(EFI_SIGNATURE_DATA, Vec)>; +/// Signature identity for baseline telemetry; `SignatureOwner` is intentionally ignored. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +enum SignatureValue { + X509(Vec), + Sha256(Vec), +} + +type SignatureSet = BTreeSet; /// Base Secure Boot template variable contents expected to be present in loaded NVRAM. #[derive(Clone, Debug)] @@ -390,6 +396,7 @@ impl HclCompatNvram { tracing::warn!( CVM_ALLOWED, variable, + base_template_bytes, "base secure boot template variable contains no signatures" ); continue; @@ -632,13 +639,13 @@ fn collect_signature_set(data: &[u8]) -> Result { for cert in certs { let cert = cert?; - signatures.insert((cert.header, cert.data.0.as_ref().to_vec())); + signatures.insert(SignatureValue::X509(cert.data.0.as_ref().to_vec())); } } ParseSignatureList::Sha256(digests) => { for digest in digests { let digest = digest?; - signatures.insert((digest.header, digest.data.0.as_ref().to_vec())); + signatures.insert(SignatureValue::Sha256(digest.data.0.as_ref().to_vec())); } } } @@ -665,7 +672,9 @@ fn signature_list_payload(data: &[u8]) -> &[u8] { return data; } - let auth_len = size_of_val(&auth.timestamp) + cert_len; + let Some(auth_len) = size_of_val(&auth.timestamp).checked_add(cert_len) else { + return data; + }; data.get(auth_len..).unwrap_or(data) } @@ -713,10 +722,10 @@ mod test { data4: [0; 8], }; - fn x509_variable(certs: &[&'static [u8]]) -> Vec { + fn x509_variable(owner: Guid, certs: &[&'static [u8]]) -> Vec { let mut data = Vec::new(); for cert in certs { - SignatureList::X509(SignatureData::new_x509(TEST_OWNER, Cow::Borrowed(*cert))) + SignatureList::X509(SignatureData::new_x509(owner, Cow::Borrowed(*cert))) .extend_as_spec_signature_list(&mut data); } data @@ -726,10 +735,15 @@ mod test { collect_signature_set(data).unwrap() } + #[test] + fn empty_secure_boot_variable_contains_no_signatures() { + assert!(signature_set(&[]).is_empty()); + } + #[test] fn base_secure_boot_template_variable_counts_missing_entries() { - let base_data = x509_variable(&[b"cert1", b"cert2"]); - let loaded_data = x509_variable(&[b"cert1", b"cert3"]); + let base_data = x509_variable(TEST_OWNER, &[b"cert1", b"cert2"]); + let loaded_data = x509_variable(TEST_OWNER, &[b"cert1", b"cert3"]); let base = signature_set(&base_data); let loaded = signature_set(&loaded_data); let missing_entries = base.difference(&loaded).count(); @@ -739,15 +753,33 @@ mod test { assert_eq!(missing_entries, 1); } + #[test] + fn secure_boot_template_comparison_ignores_signature_owner() { + let baseline = signature_set(&x509_variable(TEST_OWNER, &[b"cert1"])); + let loaded = signature_set(&x509_variable(Guid::new_random(), &[b"cert1"])); + + assert_eq!(baseline, loaded); + } + #[test] fn secure_boot_template_variable_skips_auth_header() { - let data = x509_variable(&[b"cert1"]); + let data = x509_variable(TEST_OWNER, &[b"cert1"]); let mut authenticated_data = EFI_VARIABLE_AUTHENTICATION_2::DUMMY.as_bytes().to_vec(); authenticated_data.extend_from_slice(&data); assert_eq!(signature_set(&authenticated_data), signature_set(&data)); } + #[test] + fn secure_boot_template_variable_rejects_invalid_auth_header_length() { + let mut data = EFI_VARIABLE_AUTHENTICATION_2::DUMMY.as_bytes().to_vec(); + let length_offset = size_of_val(&EFI_VARIABLE_AUTHENTICATION_2::DUMMY.timestamp); + data[length_offset..length_offset + size_of::()] + .copy_from_slice(&u32::MAX.to_ne_bytes()); + + assert_eq!(signature_list_payload(&data), data); + } + /// An ephemeral implementation of [`StorageBackend`] backed by an in-memory /// buffer. Useful for tests, stateless VM scenarios. #[derive(Default)] diff --git a/vm/devices/firmware/uefi_nvram_storage/src/lib.rs b/vm/devices/firmware/uefi_nvram_storage/src/lib.rs index 305a88239f..69cdde79de 100644 --- a/vm/devices/firmware/uefi_nvram_storage/src/lib.rs +++ b/vm/devices/firmware/uefi_nvram_storage/src/lib.rs @@ -117,7 +117,8 @@ pub trait NvramStorage: Send + Sync { )) } - /// Called after custom UEFI variables are injected on first boot. + /// Called after custom UEFI variables are injected on first boot, + /// when secure boot is enabled. fn after_custom_vars_injected(&self) {} } From 73815aed0421a0a454b079bcecdfaa66c7f320f3 Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Wed, 22 Jul 2026 23:22:58 +0000 Subject: [PATCH 10/11] Feedback again --- openhcl/underhill_core/src/worker.rs | 24 +++++-- openvmm/openvmm_entry/src/lib.rs | 20 +++++- petri/src/vm/openvmm/construct.rs | 5 ++ .../firmware/firmware_uefi/src/resolver.rs | 6 +- .../firmware_uefi_custom_vars/src/lib.rs | 62 ------------------- .../firmware_uefi_resources/src/lib.rs | 2 + .../hcl_compat_uefi_nvram_storage/src/lib.rs | 21 ++++--- .../hyperv_secure_boot_templates/src/lib.rs | 6 +- .../hyperv_uefi_custom_vars_json/src/lib.rs | 6 +- vmm_core/vm_manifest_builder/src/lib.rs | 4 ++ 10 files changed, 68 insertions(+), 88 deletions(-) diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index 2863d6a9fb..aaa3518040 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -2541,25 +2541,34 @@ async fn new_underhill_vm( use guest_emulation_transport::api::platform_settings::SecureBootTemplateType; // map the GET's template enum onto the hardcoded secureboot template type - let base_vars = match dps.general.secure_boot_template { - SecureBootTemplateType::None => CustomVars::default(), + let (base_vars, base_secure_boot_template_revision) = match dps.general.secure_boot_template + { + SecureBootTemplateType::None => (CustomVars::default(), None), SecureBootTemplateType::MicrosoftWindows => { - if cfg!(guest_arch = "x86_64") { + let vars = if cfg!(guest_arch = "x86_64") { hyperv_secure_boot_templates::x64::microsoft_windows() } else if cfg!(guest_arch = "aarch64") { hyperv_secure_boot_templates::aarch64::microsoft_windows() } else { anyhow::bail!("no secure boot template for current guest_arch") - } + }; + ( + vars, + Some(hyperv_secure_boot_templates::BASELINE_REVISION.to_owned()), + ) } SecureBootTemplateType::MicrosoftUefiCertificateAuthority => { - if cfg!(guest_arch = "x86_64") { + let vars = if cfg!(guest_arch = "x86_64") { hyperv_secure_boot_templates::x64::microsoft_uefi_ca() } else if cfg!(guest_arch = "aarch64") { hyperv_secure_boot_templates::aarch64::microsoft_uefi_ca() } else { anyhow::bail!("no secure boot template for current guest_arch") - } + }; + ( + vars, + Some(hyperv_secure_boot_templates::BASELINE_REVISION.to_owned()), + ) } }; let base_secure_boot_template_vars = base_vars.clone(); @@ -2575,6 +2584,7 @@ async fn new_underhill_vm( } else { None }; + let custom_uefi_config_present = custom_uefi_json_data.is_some(); // obtain the final custom uefi vars by applying the delta onto // the base vars @@ -2602,6 +2612,8 @@ async fn new_underhill_vm( let config = firmware_uefi_resources::UefiConfig { base_secure_boot_template_vars, custom_uefi_vars, + base_secure_boot_template_revision, + custom_uefi_config_present, secure_boot: dps.general.secure_boot_enabled, initial_generation_id, use_mmio: cfg!(not(guest_arch = "x86_64")), diff --git a/openvmm/openvmm_entry/src/lib.rs b/openvmm/openvmm_entry/src/lib.rs index 4e1d20ccd1..297cc5b3b8 100644 --- a/openvmm/openvmm_entry/src/lib.rs +++ b/openvmm/openvmm_entry/src/lib.rs @@ -1177,7 +1177,12 @@ async fn vm_config_from_command_line( ); } - let (base_secure_boot_template_vars, custom_uefi_vars) = { + let ( + base_secure_boot_template_vars, + custom_uefi_vars, + base_secure_boot_template_revision, + custom_uefi_config_present, + ) = { use firmware_uefi_custom_vars::CustomVars; // load base vars from specified template, or use an empty set of base @@ -1200,6 +1205,9 @@ async fn vm_config_from_command_line( None => CustomVars::default(), }; let base_secure_boot_template_vars = base_vars.clone(); + let base_secure_boot_template_revision = opt + .secure_boot_template + .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_owned()); // TODO: fallback to VMGS read if no command line flag was given @@ -1207,6 +1215,7 @@ async fn vm_config_from_command_line( Some(file) => Some(fs_err::read(file).context("opening custom uefi json file")?), None => None, }; + let custom_uefi_config_present = custom_uefi_json_data.is_some(); // obtain the final custom uefi vars by applying the delta onto the base vars let custom_uefi_vars = match custom_uefi_json_data { @@ -1217,7 +1226,12 @@ async fn vm_config_from_command_line( None => base_vars, }; - (base_secure_boot_template_vars, custom_uefi_vars) + ( + base_secure_boot_template_vars, + custom_uefi_vars, + base_secure_boot_template_revision, + custom_uefi_config_present, + ) }; if opt.uefi { @@ -1235,6 +1249,8 @@ async fn vm_config_from_command_line( arch, base_secure_boot_template_vars, custom_uefi_vars, + base_secure_boot_template_revision, + custom_uefi_config_present, opt.secure_boot, log_level, None, diff --git a/petri/src/vm/openvmm/construct.rs b/petri/src/vm/openvmm/construct.rs index 7ab5fd8e17..c0a099af20 100644 --- a/petri/src/vm/openvmm/construct.rs +++ b/petri/src/vm/openvmm/construct.rs @@ -412,6 +412,9 @@ impl PetriVmConfigOpenVmm { (_, None) => Default::default(), }); let custom_uefi_vars = base_secure_boot_template_vars.clone(); + let base_secure_boot_template_revision = uefi_cfg + .and_then(|c| c.secure_boot_template) + .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_owned()); let secure_boot = uefi_cfg.is_some_and(|c| c.secure_boot_enabled); let log_level = match uefi_cfg .map(|c| c.efi_diagnostics_log_level) @@ -436,6 +439,8 @@ impl PetriVmConfigOpenVmm { }, base_secure_boot_template_vars, custom_uefi_vars, + base_secure_boot_template_revision, + false, secure_boot, log_level, diagnostics_rate_limit, diff --git a/vm/devices/firmware/firmware_uefi/src/resolver.rs b/vm/devices/firmware/firmware_uefi/src/resolver.rs index 31292dc69b..f9e3907975 100644 --- a/vm/devices/firmware/firmware_uefi/src/resolver.rs +++ b/vm/devices/firmware/firmware_uefi/src/resolver.rs @@ -203,10 +203,10 @@ impl AsyncResolveResource for UefiDev tracing::info!( baseline_configured = config.base_secure_boot_template_vars.signatures.is_some(), baseline_revision = config - .base_secure_boot_template_vars - .baseline_revision() + .base_secure_boot_template_revision + .as_deref() .unwrap_or("none"), - custom_uefi_config_present = config.custom_uefi_vars.custom_uefi_config_present(), + custom_uefi_config_present = config.custom_uefi_config_present, "secure boot configuration" ); diff --git a/vm/devices/firmware/firmware_uefi_custom_vars/src/lib.rs b/vm/devices/firmware/firmware_uefi_custom_vars/src/lib.rs index acda7591fd..9af59ad2a6 100644 --- a/vm/devices/firmware/firmware_uefi_custom_vars/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi_custom_vars/src/lib.rs @@ -21,8 +21,6 @@ pub struct CustomVars { pub signatures: Option, /// Any additional custom vars pub custom_vars: Vec<(String, CustomVar)>, - baseline_revision: Option, - custom_uefi_config_present: bool, } #[derive(Debug, Clone, Protobuf)] @@ -84,34 +82,6 @@ impl CustomVars { CustomVars::default() } - /// Create a set of custom variables from signatures and additional variables. - pub fn from_parts( - signatures: Option, - custom_vars: Vec<(String, CustomVar)>, - ) -> Self { - Self { - signatures, - custom_vars, - ..Default::default() - } - } - - /// Associate these variables with the built-in baseline revision they represent. - pub fn with_baseline_revision(mut self, revision: impl Into) -> Self { - self.baseline_revision = Some(revision.into()); - self - } - - /// Return the built-in baseline revision associated with these variables. - pub fn baseline_revision(&self) -> Option<&str> { - self.baseline_revision.as_deref() - } - - /// Return whether customer-provided custom UEFI configuration was applied. - pub fn custom_uefi_config_present(&self) -> bool { - self.custom_uefi_config_present - } - /// Apply a delta on-top of an existing set of CustomVars. pub fn apply_delta(self, delta: delta::CustomVarsDelta) -> Result { use delta::SignatureDelta; @@ -120,7 +90,6 @@ impl CustomVars { use delta::SignaturesDelta; use delta::SignaturesReplace; - let baseline_revision = self.baseline_revision; let signatures = match (self.signatures, delta.signatures) { (None, SignaturesDelta::Append(..)) => return Err(ApplyDeltaError::AppendWithoutBase), ( @@ -282,37 +251,6 @@ impl CustomVars { Ok(CustomVars { signatures: Some(signatures), custom_vars, - baseline_revision, - custom_uefi_config_present: true, }) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn apply_delta_preserves_template_provenance() { - let signature = || Signature::Sha256(Vec::new()); - let delta = delta::CustomVarsDelta { - signatures: delta::SignaturesDelta::Replace(delta::SignaturesReplace { - pk: delta::SignatureDelta::Sig(signature()), - kek: delta::SignatureDeltaVec::Sigs(Vec::new()), - db: delta::SignatureDeltaVec::Sigs(Vec::new()), - dbx: delta::SignatureDeltaVec::Sigs(Vec::new()), - moklist: None, - moklistx: None, - }), - custom_vars: Vec::new(), - }; - - let vars = CustomVars::new() - .with_baseline_revision("July 2026") - .apply_delta(delta) - .unwrap(); - - assert_eq!(vars.baseline_revision(), Some("July 2026")); - assert!(vars.custom_uefi_config_present()); - } -} diff --git a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs index ca0881bf5e..6438abcbc5 100644 --- a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs @@ -161,6 +161,8 @@ pub struct UefiConfig { pub diagnostics_log_level: LogLevel, pub diagnostics_rate_limit: Option, pub base_secure_boot_template_vars: CustomVars, + pub base_secure_boot_template_revision: Option, + pub custom_uefi_config_present: bool, } /// Resource kind for the platform-provided UEFI logger. 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 d163584ba1..fd7020e1da 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 @@ -36,6 +36,7 @@ use uefi_nvram_storage::NvramStorage; use uefi_nvram_storage::NvramStorageError; use uefi_nvram_storage::in_memory; use uefi_specs::uefi::nvram::EFI_VARIABLE_AUTHENTICATION_2; +use uefi_specs::uefi::nvram::signature_list::EFI_SIGNATURE_DATA; use uefi_specs::uefi::nvram::vars; use uefi_specs::uefi::signing::EFI_CERT_TYPE_PKCS7_GUID; use uefi_specs::uefi::signing::WIN_CERT_TYPE_EFI_GUID; @@ -54,11 +55,11 @@ const INITIAL_NVRAM_SIZE: usize = 32768; const MAXIMUM_NVRAM_SIZE: usize = INITIAL_NVRAM_SIZE * 4; const WIN_CERT_REVISION_2_0: u16 = 0x0200; -/// Signature identity for baseline telemetry; `SignatureOwner` is intentionally ignored. +/// Secure Boot signature entry tracked by baseline telemetry. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] enum SignatureValue { - X509(Vec), - Sha256(Vec), + X509(EFI_SIGNATURE_DATA, Vec), + Sha256(EFI_SIGNATURE_DATA, Vec), } type SignatureSet = BTreeSet; @@ -639,13 +640,19 @@ fn collect_signature_set(data: &[u8]) -> Result { for cert in certs { let cert = cert?; - signatures.insert(SignatureValue::X509(cert.data.0.as_ref().to_vec())); + signatures.insert(SignatureValue::X509( + cert.header, + cert.data.0.as_ref().to_vec(), + )); } } ParseSignatureList::Sha256(digests) => { for digest in digests { let digest = digest?; - signatures.insert(SignatureValue::Sha256(digest.data.0.as_ref().to_vec())); + signatures.insert(SignatureValue::Sha256( + digest.header, + digest.data.0.as_ref().to_vec(), + )); } } } @@ -754,11 +761,11 @@ mod test { } #[test] - fn secure_boot_template_comparison_ignores_signature_owner() { + fn secure_boot_template_comparison_includes_signature_owner() { let baseline = signature_set(&x509_variable(TEST_OWNER, &[b"cert1"])); let loaded = signature_set(&x509_variable(Guid::new_random(), &[b"cert1"])); - assert_eq!(baseline, loaded); + assert_ne!(baseline, loaded); } #[test] diff --git a/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs b/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs index 7cfb06a5f6..0cce71edbf 100644 --- a/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs +++ b/vm/devices/firmware/hyperv_secure_boot_templates/src/lib.rs @@ -34,7 +34,6 @@ macro_rules! include_templates { // JSON files), it may result in a nice .rodata size decrease. hyperv_uefi_custom_vars_json::load_template_from_json(include_bytes!(concat!(env!("OUT_DIR"), "/", $path))) .unwrap() - .with_baseline_revision($crate::BASELINE_REVISION) } )* @@ -43,10 +42,7 @@ macro_rules! include_templates { $( #[test] fn $fn_name() { - assert_eq!( - super::$fn_name().baseline_revision(), - Some($crate::BASELINE_REVISION) - ); + assert!(super::$fn_name().signatures.is_some()); } )* } diff --git a/vm/devices/firmware/hyperv_uefi_custom_vars_json/src/lib.rs b/vm/devices/firmware/hyperv_uefi_custom_vars_json/src/lib.rs index c97cea07c0..e9ee6a8f6c 100644 --- a/vm/devices/firmware/hyperv_uefi_custom_vars_json/src/lib.rs +++ b/vm/devices/firmware/hyperv_uefi_custom_vars_json/src/lib.rs @@ -68,8 +68,8 @@ pub fn load_template_from_json( custom_vars, } = load_delta_from_json(data)?; - Ok(CustomVars::from_parts( - Some(match signatures { + Ok(CustomVars { + signatures: Some(match signatures { SignaturesDelta::Append(_) => panic!("hardcoded templates cannot use append"), SignaturesDelta::Replace(signatures) => Signatures { pk: deny_default(signatures.pk)?, @@ -89,7 +89,7 @@ pub fn load_template_from_json( }, }), custom_vars, - )) + }) } /// Parse a [`CustomVarsDelta`](firmware_uefi_custom_vars::delta::CustomVarsDelta) from a user diff --git a/vmm_core/vm_manifest_builder/src/lib.rs b/vmm_core/vm_manifest_builder/src/lib.rs index cdf3db7b32..5e2b81128d 100644 --- a/vmm_core/vm_manifest_builder/src/lib.rs +++ b/vmm_core/vm_manifest_builder/src/lib.rs @@ -115,6 +115,8 @@ impl UefiManifest { arch: MachineArch, base_secure_boot_template_vars: CustomVars, custom_uefi_vars: CustomVars, + base_secure_boot_template_revision: Option, + custom_uefi_config_present: bool, secure_boot: bool, diagnostics_log_level: LogLevel, diagnostics_rate_limit: Option, @@ -127,6 +129,8 @@ impl UefiManifest { config: UefiConfig { base_secure_boot_template_vars, custom_uefi_vars, + base_secure_boot_template_revision, + custom_uefi_config_present, secure_boot, initial_generation_id, use_mmio: !matches!(arch, MachineArch::X86_64), From 183bf8c3310668e32cac4a6e9a7cdaccaf760a5e Mon Sep 17 00:00:00 2001 From: maheeraeron Date: Wed, 22 Jul 2026 23:34:29 +0000 Subject: [PATCH 11/11] Remove baseline plumbing --- Cargo.lock | 1 + openhcl/underhill_core/src/worker.rs | 22 +++++-------------- openvmm/openvmm_entry/src/lib.rs | 12 +--------- petri/src/vm/openvmm/construct.rs | 4 ---- vm/devices/firmware/firmware_uefi/Cargo.toml | 1 + .../firmware/firmware_uefi/src/resolver.rs | 12 +++++----- .../firmware_uefi_resources/src/lib.rs | 1 - vmm_core/vm_manifest_builder/src/lib.rs | 2 -- 8 files changed, 16 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b8ce6798e..1c573acf72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2084,6 +2084,7 @@ dependencies = [ "guestmem", "guid", "hcl_compat_uefi_nvram_storage", + "hyperv_secure_boot_templates", "inspect", "jiff", "local_clock", diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index aaa3518040..726f9c6046 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -2541,34 +2541,25 @@ async fn new_underhill_vm( use guest_emulation_transport::api::platform_settings::SecureBootTemplateType; // map the GET's template enum onto the hardcoded secureboot template type - let (base_vars, base_secure_boot_template_revision) = match dps.general.secure_boot_template - { - SecureBootTemplateType::None => (CustomVars::default(), None), + let base_vars = match dps.general.secure_boot_template { + SecureBootTemplateType::None => CustomVars::default(), SecureBootTemplateType::MicrosoftWindows => { - let vars = if cfg!(guest_arch = "x86_64") { + if cfg!(guest_arch = "x86_64") { hyperv_secure_boot_templates::x64::microsoft_windows() } else if cfg!(guest_arch = "aarch64") { hyperv_secure_boot_templates::aarch64::microsoft_windows() } else { anyhow::bail!("no secure boot template for current guest_arch") - }; - ( - vars, - Some(hyperv_secure_boot_templates::BASELINE_REVISION.to_owned()), - ) + } } SecureBootTemplateType::MicrosoftUefiCertificateAuthority => { - let vars = if cfg!(guest_arch = "x86_64") { + if cfg!(guest_arch = "x86_64") { hyperv_secure_boot_templates::x64::microsoft_uefi_ca() } else if cfg!(guest_arch = "aarch64") { hyperv_secure_boot_templates::aarch64::microsoft_uefi_ca() } else { anyhow::bail!("no secure boot template for current guest_arch") - }; - ( - vars, - Some(hyperv_secure_boot_templates::BASELINE_REVISION.to_owned()), - ) + } } }; let base_secure_boot_template_vars = base_vars.clone(); @@ -2612,7 +2603,6 @@ async fn new_underhill_vm( let config = firmware_uefi_resources::UefiConfig { base_secure_boot_template_vars, custom_uefi_vars, - base_secure_boot_template_revision, custom_uefi_config_present, secure_boot: dps.general.secure_boot_enabled, initial_generation_id, diff --git a/openvmm/openvmm_entry/src/lib.rs b/openvmm/openvmm_entry/src/lib.rs index 297cc5b3b8..b9c17d3d72 100644 --- a/openvmm/openvmm_entry/src/lib.rs +++ b/openvmm/openvmm_entry/src/lib.rs @@ -1177,12 +1177,7 @@ async fn vm_config_from_command_line( ); } - let ( - base_secure_boot_template_vars, - custom_uefi_vars, - base_secure_boot_template_revision, - custom_uefi_config_present, - ) = { + let (base_secure_boot_template_vars, custom_uefi_vars, custom_uefi_config_present) = { use firmware_uefi_custom_vars::CustomVars; // load base vars from specified template, or use an empty set of base @@ -1205,9 +1200,6 @@ async fn vm_config_from_command_line( None => CustomVars::default(), }; let base_secure_boot_template_vars = base_vars.clone(); - let base_secure_boot_template_revision = opt - .secure_boot_template - .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_owned()); // TODO: fallback to VMGS read if no command line flag was given @@ -1229,7 +1221,6 @@ async fn vm_config_from_command_line( ( base_secure_boot_template_vars, custom_uefi_vars, - base_secure_boot_template_revision, custom_uefi_config_present, ) }; @@ -1249,7 +1240,6 @@ async fn vm_config_from_command_line( arch, base_secure_boot_template_vars, custom_uefi_vars, - base_secure_boot_template_revision, custom_uefi_config_present, opt.secure_boot, log_level, diff --git a/petri/src/vm/openvmm/construct.rs b/petri/src/vm/openvmm/construct.rs index c0a099af20..a515b47673 100644 --- a/petri/src/vm/openvmm/construct.rs +++ b/petri/src/vm/openvmm/construct.rs @@ -412,9 +412,6 @@ impl PetriVmConfigOpenVmm { (_, None) => Default::default(), }); let custom_uefi_vars = base_secure_boot_template_vars.clone(); - let base_secure_boot_template_revision = uefi_cfg - .and_then(|c| c.secure_boot_template) - .map(|_| hyperv_secure_boot_templates::BASELINE_REVISION.to_owned()); let secure_boot = uefi_cfg.is_some_and(|c| c.secure_boot_enabled); let log_level = match uefi_cfg .map(|c| c.efi_diagnostics_log_level) @@ -439,7 +436,6 @@ impl PetriVmConfigOpenVmm { }, base_secure_boot_template_vars, custom_uefi_vars, - base_secure_boot_template_revision, false, secure_boot, log_level, diff --git a/vm/devices/firmware/firmware_uefi/Cargo.toml b/vm/devices/firmware/firmware_uefi/Cargo.toml index ada0e567e0..fa749fe804 100644 --- a/vm/devices/firmware/firmware_uefi/Cargo.toml +++ b/vm/devices/firmware/firmware_uefi/Cargo.toml @@ -14,6 +14,7 @@ fuzzing = [] firmware_uefi_custom_vars.workspace = true firmware_uefi_resources.workspace = true hcl_compat_uefi_nvram_storage = { workspace = true, features = ["inspect", "save_restore"] } +hyperv_secure_boot_templates.workspace = true uefi_nvram_storage = { workspace = true, features = ["inspect", "save_restore"] } uefi_specs.workspace = true uefi_nvram_specvars.workspace = true diff --git a/vm/devices/firmware/firmware_uefi/src/resolver.rs b/vm/devices/firmware/firmware_uefi/src/resolver.rs index f9e3907975..f465769273 100644 --- a/vm/devices/firmware/firmware_uefi/src/resolver.rs +++ b/vm/devices/firmware/firmware_uefi/src/resolver.rs @@ -200,12 +200,14 @@ impl AsyncResolveResource for UefiDev storage_quirks, ); let nvram_storage = if config.secure_boot { + let baseline_configured = config.base_secure_boot_template_vars.signatures.is_some(); tracing::info!( - baseline_configured = config.base_secure_boot_template_vars.signatures.is_some(), - baseline_revision = config - .base_secure_boot_template_revision - .as_deref() - .unwrap_or("none"), + baseline_configured, + baseline_revision = if baseline_configured { + hyperv_secure_boot_templates::BASELINE_REVISION + } else { + "none" + }, custom_uefi_config_present = config.custom_uefi_config_present, "secure boot configuration" ); diff --git a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs index 6438abcbc5..7eae16c4a9 100644 --- a/vm/devices/firmware/firmware_uefi_resources/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi_resources/src/lib.rs @@ -161,7 +161,6 @@ pub struct UefiConfig { pub diagnostics_log_level: LogLevel, pub diagnostics_rate_limit: Option, pub base_secure_boot_template_vars: CustomVars, - pub base_secure_boot_template_revision: Option, pub custom_uefi_config_present: bool, } diff --git a/vmm_core/vm_manifest_builder/src/lib.rs b/vmm_core/vm_manifest_builder/src/lib.rs index 5e2b81128d..2f14d4876f 100644 --- a/vmm_core/vm_manifest_builder/src/lib.rs +++ b/vmm_core/vm_manifest_builder/src/lib.rs @@ -115,7 +115,6 @@ impl UefiManifest { arch: MachineArch, base_secure_boot_template_vars: CustomVars, custom_uefi_vars: CustomVars, - base_secure_boot_template_revision: Option, custom_uefi_config_present: bool, secure_boot: bool, diagnostics_log_level: LogLevel, @@ -129,7 +128,6 @@ impl UefiManifest { config: UefiConfig { base_secure_boot_template_vars, custom_uefi_vars, - base_secure_boot_template_revision, custom_uefi_config_present, secure_boot, initial_generation_id,