vmm: add KVM SEV-SNP support to IGVM loader

Adapt the IGVM loader to work with both MSHV and KVM backends, which
differ in page type constants, CPUID page layout, and VMSA handling.

Abstract page types into a PageTypeConfig struct populated at runtime
from the detected hypervisor, replacing hardcoded mshv_bindings constants.

Apply the VMSA register state to each vCPU via setup_sev_snp_regs(),
translating SevSelector attributes to KVM segment format using a bitfield
decoder.

KVM's SNP launch path sanitizes certain CPUID bits that could lead to
an insecure guest. If the VMM sets these bits, KVM rejects the CPUID
page import on the first attempt, requiring a retry with the
firmware-corrected values.

Pre-clear the known problematic bits before import to avoid the
reject-and-retry cycle:

- Leaf 0x1, ECX bit 24: TSC_DEADLINE (filtered by KVM)
- Leaf 0x7, EBX bit 1: SGX (filtered by KVM)
- Leaf 0x7, EDX: clear entirely (contains speculative features)
- Leaf 0x80000008, EBX bit 25: filtered by KVM
- Leaf 0x80000021, ECX: clear entirely

This keeps the CPUID page stable across launch updates and avoids
noisy error logs from the retry path.

Co-authored-by: Keith Adler <kadler@cloudflare.com>
Signed-off-by: Keith Adler <kadler@cloudflare.com>
Co-authored-by: Alex Orozco <aorozco@google.com>
Signed-off-by: Alex Orozco <aorozco@google.com>
Co-authored-by: Dylan Reid <dgreid@fb.com>
Signed-off-by: Dylan Reid <dgreid@fb.com>
Signed-off-by: Ruben Hakobyan <hruben@meta.com>
This commit is contained in:
Ruben Hakobyan
2026-04-07 17:30:24 -07:00
committed by Rob Bradford
parent b5ddcdc74a
commit 75ed2c9f90
5 changed files with 435 additions and 60 deletions
+22 -6
View File
@@ -352,6 +352,9 @@ pub enum ValidationError {
#[cfg(feature = "sev_snp")]
#[error("Invalid host data format")]
InvalidHostData,
#[cfg(all(feature = "sev_snp", feature = "igvm"))]
#[error("SEV-SNP requires an IGVM payload (--payload igvm=<path>)")]
SevSnpRequiresIgvm,
/// Restore expects all net ids that have fds
#[error("Net id {0} is associated with FDs and is required")]
RestoreMissingRequiredNetId(String),
@@ -2823,12 +2826,25 @@ impl VmConfig {
#[cfg(feature = "sev_snp")]
{
let host_data_opt = &self.payload.as_ref().unwrap().host_data;
if let Some(host_data) = host_data_opt
&& host_data.len() != 64
{
return Err(ValidationError::InvalidHostData);
let sev_snp_enabled = self.platform.as_ref().is_some_and(|p| p.sev_snp);
if sev_snp_enabled {
let host_data_opt = &self.payload.as_ref().unwrap().host_data;
if let Some(host_data) = host_data_opt
&& host_data.len() != 64
{
return Err(ValidationError::InvalidHostData);
}
// KVM SEV-SNP requires an IGVM payload to initialise the VMSA.
// Without IGVM the vCPU register state is undefined and VM entry fails.
#[cfg(feature = "igvm")]
if self
.payload
.as_ref()
.and_then(|p| p.igvm.as_ref())
.is_none()
{
return Err(ValidationError::SevSnpRequiresIgvm);
}
}
}
// The 'conflict' check is introduced in commit 24438e0390d3