hypervisor, vmm: Add KVM SEV_{INIT2, SNP_LAUNCH_START} support

Introduce the SevFd abstraction that wraps /dev/sev and implements the
KVM_SEV_INIT2 and KVM_SEV_SNP_LAUNCH_START ioctls for SEV-SNP VM
initialization on KVM.

Key changes:
- Add sev.rs with KvmSevInit and KvmSevSnpLaunchStart ioctl structs
  matching the kernel layout (linux/arch/x86/include/uapi/asm/kvm.h)
- Implement KVM_SEV_INIT2 and KVM_SEV_SNP_LAUNCH_START ioctls
- Set KVM_MEMORY_ATTRIBUTE_PRIVATE on newly created memory regions
  when guest_memfd is supported
- Widen SevSnpPageAccessProxy cfg gates from mshv-only to all
  sev_snp-enabled builds
- Make sev_snp_init a required trait method (remove default impl)
- Include KVM_SEV_SNP_LAUNCH_START in the seccomp allowlist
- Parse VMSA SEV features from IGVM and include them in the
  KVM_SEV_INIT2 ioctl

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: Rob Bradford <rbradford@meta.com>
Signed-off-by: Rob Bradford <rbradford@meta.com>
Signed-off-by: Ruben Hakobyan <hruben@meta.com>
This commit is contained in:
Ruben Hakobyan
2026-04-07 14:23:13 -07:00
committed by Rob Bradford
parent 425609a8b5
commit 2e004521e0
12 changed files with 225 additions and 25 deletions

View File

@@ -140,6 +140,9 @@ use crate::kvm::x86_64::XsaveStateError;
#[cfg(target_arch = "x86_64")]
ioctl_io_nr!(KVM_NMI, kvm_bindings::KVMIO, 0x9a);
#[cfg(feature = "sev_snp")]
use kvm_bindings::{KVM_MEMORY_ATTRIBUTE_PRIVATE, KVM_X86_SNP_VM, kvm_memory_attributes};
#[cfg(feature = "tdx")]
const KVM_EXIT_TDX: u32 = 50;
#[cfg(feature = "tdx")]
@@ -498,9 +501,11 @@ struct KvmDirtyLogSlot {
/// Wrapper over KVM VM ioctls.
pub struct KvmVm {
fd: VmFd,
fd: Arc<VmFd>,
#[cfg(target_arch = "x86_64")]
msrs: Vec<MsrEntry>,
#[cfg(all(feature = "sev_snp", target_arch = "x86_64"))]
sev_fd: Option<x86_64::sev::SevFd>,
dirty_log_slots: RwLock<HashMap<u32, KvmDirtyLogSlot>>,
guest_memfds: Option<RwLock<HashMap<u32, OwnedFd>>>,
}
@@ -621,6 +626,15 @@ impl KvmVm {
/// let vm = hypervisor.create_vm(HypervisorVmConfig::default()).expect("new VM fd creation failed");
/// ```
impl vm::Vm for KvmVm {
#[cfg(all(feature = "sev_snp", target_arch = "x86_64"))]
fn sev_snp_init(&self, guest_policy: igvm_defs::SnpPolicy) -> vm::Result<()> {
self.sev_fd
.as_ref()
.unwrap()
.launch_start(&self.fd, guest_policy)
.map_err(|e| vm::HypervisorVmError::InitializeSevSnp(e.into()))
}
#[cfg(target_arch = "x86_64")]
///
/// Sets the address of the one-page region in the VM's address space.
@@ -938,6 +952,18 @@ impl vm::Vm for KvmVm {
self.set_user_memory_region(region)
.map_err(|e| vm::HypervisorVmError::CreateUserMemory(e.into()))?;
}
#[cfg(feature = "sev_snp")]
if self.guest_memfds.is_some() {
self.fd
.set_memory_attributes(kvm_memory_attributes {
address: region.guest_phys_addr,
size: region.memory_size,
attributes: KVM_MEMORY_ATTRIBUTE_PRIVATE as u64,
flags: 0,
})
.map_err(|e| vm::HypervisorVmError::CreateUserMemory(e.into()))?;
}
Ok(())
}
@@ -1383,15 +1409,17 @@ impl hypervisor::Hypervisor for KvmHypervisor {
}
#[cfg(target_arch = "x86_64")]
cfg_if::cfg_if! {
if #[cfg(feature = "tdx")] {
if _config.tdx_enabled {
vm_type = KVM_X86_SW_PROTECTED_VM.into();
} else {
vm_type = KVM_X86_DEFAULT_VM.into();
}
} else {
vm_type = KVM_X86_DEFAULT_VM.into();
{
vm_type = KVM_X86_DEFAULT_VM.into();
#[cfg(feature = "sev_snp")]
if _config.sev_snp_enabled {
vm_type = KVM_X86_SNP_VM.into();
}
#[cfg(feature = "tdx")]
if _config.tdx_enabled {
vm_type = KVM_X86_SW_PROTECTED_VM.into();
}
}
@@ -1433,10 +1461,35 @@ impl hypervisor::Hypervisor for KvmHypervisor {
guest_memfds = Some(RwLock::new(HashMap::new()));
}
#[cfg(feature = "sev_snp")]
let sev_fd = {
let sev_snp_enabled = vm_type == KVM_X86_SNP_VM as u64;
if sev_snp_enabled {
let mask = self.kvm.check_extension_int(crate::kvm::Cap::ExitHypercall);
let cap = kvm_bindings::kvm_enable_cap {
cap: kvm_bindings::KVM_CAP_EXIT_HYPERCALL,
args: [mask as _, 0, 0, 0],
..Default::default()
};
fd.enable_cap(&cap)
.map_err(|e| hypervisor::HypervisorError::VmCreate(e.into()))?;
let sev_dev = x86_64::sev::SevFd::new("/dev/sev")
.map_err(|e| hypervisor::HypervisorError::SevSnpCapabilities(e.into()))?;
sev_dev
.init2(&fd, _config.vmsa_features)
.map_err(|e| hypervisor::HypervisorError::VmCreate(e.into()))?;
Some(sev_dev)
} else {
None
}
};
Ok(Arc::new(KvmVm {
fd,
fd: Arc::new(fd),
msrs,
dirty_log_slots: RwLock::new(HashMap::new()),
#[cfg(feature = "sev_snp")]
sev_fd,
guest_memfds,
}))
}
@@ -1444,7 +1497,7 @@ impl hypervisor::Hypervisor for KvmHypervisor {
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
{
Ok(Arc::new(KvmVm {
fd,
fd: Arc::new(fd),
dirty_log_slots: RwLock::new(HashMap::new()),
guest_memfds: None,
}))