hypervisor: handle VcpuExit::MemoryFault for AP boot page conversions

During SNP boot all guest RAM is initially marked
KVM_MEMORY_ATTRIBUTE_PRIVATE. Pages imported via SNP_LAUNCH_UPDATE are
properly accepted by the guest, but generic RAM pages (e.g. the AP
trampoline at GPA 0xD000) are not. When stage0 on the BSP starts
secondary vCPUs via x2APIC, the APs try to execute from the trampoline
page through the shared mapping while KVM still has it marked private,
causing a KVM_EXIT_MEMORY_FAULT (flags=KVM_MEMORY_EXIT_FLAG_PRIVATE)
that previously fell through to the catch-all error, killing the VM.

Handle VcpuExit::MemoryFault by toggling the page's memory attribute
between private and shared based on the exit flags, allowing the vCPU
to retry the access.

Signed-off-by: Ruben Hakobyan <hruben@meta.com>
This commit is contained in:
Ruben Hakobyan
2026-04-07 17:21:15 -07:00
committed by Rob Bradford
parent 4a0cfa02de
commit b5ddcdc74a

View File

@@ -2354,6 +2354,38 @@ impl cpu::Vcpu for KvmVcpu {
}
}
#[cfg(feature = "sev_snp")]
VcpuExit::MemoryFault { flags, gpa, size } => {
debug!("VcpuExit::MemoryFault: flags={flags:#x}, gpa={gpa:#x}, size={size:#x}");
const KVM_MEMORY_EXIT_FLAG_PRIVATE: u64 =
kvm_bindings::KVM_MEMORY_EXIT_FLAG_PRIVATE as u64;
if flags & !KVM_MEMORY_EXIT_FLAG_PRIVATE != 0 {
return Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
"VcpuExit::MemoryFault: unknown flags {flags:#x}"
)));
}
let attributes = if flags & KVM_MEMORY_EXIT_FLAG_PRIVATE != 0 {
KVM_MEMORY_ATTRIBUTE_PRIVATE as u64
} else {
// the only attribute available is private, o/w 0
// https://docs.kernel.org/virt/kvm/api.html#kvm-set-memory-attributes
0u64
};
self.vm_fd
.set_memory_attributes(kvm_memory_attributes {
address: gpa,
size,
attributes,
flags: 0,
})
.map(|_| cpu::VmExit::Ignore)
.map_err(|e| cpu::HypervisorCpuError::RunVcpu(e.into()))
}
r => Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
"Unexpected exit reason on vcpu run: {r:?}"
))),