From 9f1247fe60d96bc4c89308f64d65258af1037b82 Mon Sep 17 00:00:00 2001 From: Ruben Hakobyan Date: Wed, 29 Apr 2026 16:55:16 -0700 Subject: [PATCH] devices: fw_cfg: Don't modify kernel header for KVM SEV-SNP guests For KVM SEV-SNP guests, the VMM should not modify the kernel boot header before sending it via fw_cfg. The guest firmware is expected to set fields like type_of_loader itself. For upcoming measured boot logic for SEV-SNP, modifying `type_of_loader` causes the kernel hash computed by the VMM to diverge from the hash that `sev-snp-measure` (and the guest firmware) compute, resulting in a launch measurement mismatch. This matches QEMU's behavior, which skips kernel header modifications for confidential guests so the data sent via fw_cfg matches the original kernel file provided by the user. Signed-off-by: Ruben Hakobyan --- devices/src/legacy/fw_cfg.rs | 36 +++++++++++++++++++++++++++--------- vmm/src/vm.rs | 24 +++++++++++++++++++++++- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/devices/src/legacy/fw_cfg.rs b/devices/src/legacy/fw_cfg.rs index 876ceaaf3..c5200e5f5 100644 --- a/devices/src/legacy/fw_cfg.rs +++ b/devices/src/legacy/fw_cfg.rs @@ -441,12 +441,17 @@ impl FwCfg { initramfs: Option, cmdline: Option, fw_cfg_item_list: Option>, + #[cfg(target_arch = "x86_64")] kvm_sev_snp_enabled: bool, ) -> Result<()> { if let Some(mem_size) = mem_size { self.add_e820(mem_size)?; } if let Some(kernel) = kernel { - self.add_kernel_data(&kernel)?; + self.add_kernel_data( + &kernel, + #[cfg(target_arch = "x86_64")] + kvm_sev_snp_enabled, + )?; } if let Some(cmdline) = cmdline { self.add_kernel_cmdline(cmdline); @@ -631,24 +636,37 @@ impl FwCfg { } } - pub fn add_kernel_data(&mut self, file: &File) -> Result<()> { + pub fn add_kernel_data( + &mut self, + file: &File, + #[cfg(target_arch = "x86_64")] kvm_sev_snp_enabled: bool, + ) -> Result<()> { let mut buffer = vec![0u8; size_of::()]; file.read_exact_at(&mut buffer, 0)?; let bp = boot_params::from_mut_slice(&mut buffer).unwrap(); #[cfg(target_arch = "x86_64")] { - // must set to 4 for backwards compatibility - // https://docs.kernel.org/arch/x86/boot.html#the-real-mode-kernel-header - if bp.hdr.setup_sects == 0 { - bp.hdr.setup_sects = 4; + // For SEV-SNP guests on KVM, don't modify the kernel header so the + // bytes sent via fw_cfg match what the VMM hashes for the launch digest. + // The guest firmware handles these fields itself. + if !kvm_sev_snp_enabled { + if bp.hdr.setup_sects == 0 { + bp.hdr.setup_sects = 4; + } + bp.hdr.type_of_loader = 0xff; } - // wildcard boot loader type - bp.hdr.type_of_loader = 0xff; } #[cfg(target_arch = "aarch64")] let kernel_start = bp.text_offset; #[cfg(target_arch = "x86_64")] - let kernel_start = (bp.hdr.setup_sects as usize + 1) * 512; + let kernel_start = { + let sects = if bp.hdr.setup_sects == 0 { + 4 + } else { + bp.hdr.setup_sects + }; + (sects as usize + 1) * 512 + }; #[cfg(target_arch = "x86_64")] if kernel_start <= buffer.len() { diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 3d5315b19..d479ad722 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -1139,6 +1139,7 @@ impl Vm { fw_cfg_config: &FwCfgConfig, device_manager: &Arc>, config: &Arc>, + #[cfg(target_arch = "x86_64")] kvm_sev_snp_enabled: bool, ) -> Result<()> { let mut e820_option: Option = None; if fw_cfg_config.e820 { @@ -1222,6 +1223,8 @@ impl Vm { initramfs_option, cmdline_option, fw_cfg_item_list_option, + #[cfg(target_arch = "x86_64")] + kvm_sev_snp_enabled, ) .map_err(Error::ErrorPopulatingFwCfg)?; Ok(()) @@ -2739,7 +2742,26 @@ impl Vm { .map(|p| p.fw_cfg_config.clone()) .unwrap_or_default() .ok_or(Error::VmMissingConfig)?; - Self::populate_fw_cfg(&fw_cfg_config, &self.device_manager, &self.config)?; + #[cfg(target_arch = "x86_64")] + let kvm_sev_snp_enabled = { + #[cfg(feature = "sev_snp")] + { + self.config.lock().unwrap().is_sev_snp_enabled() + && self.hypervisor.hypervisor_type() == hypervisor::HypervisorType::Kvm + } + #[cfg(not(feature = "sev_snp"))] + { + false + } + }; + + Self::populate_fw_cfg( + &fw_cfg_config, + &self.device_manager, + &self.config, + #[cfg(target_arch = "x86_64")] + kvm_sev_snp_enabled, + )?; if fw_cfg_config.acpi_tables { let tpm_enabled = self.config.lock().unwrap().tpm.is_some();