mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
hypervisor: Add GHCB CPUID, MSR and TERM_REQ handlers
When booting an SEV-SNP guest VM using IGVM with -pvalidate_opt 1 (lazy
page acceptance), the guest kernel's #VC exception handler may issue
VMGEXIT with SVM_EXIT_CPUID (0x72) or SVM_EXIT_MSR (0x7c) exit codes
via the GHCB page protocol. The hypervisor had no handlers for these
exit codes, causing the guest's #VC handler to fail and trigger
sev_es_terminate(), which sends GHCB_MSR_TERM_REQ (0x100). The
hypervisor then panicked on the unhandled 0x100 operation.
Add the following handlers to the GHCB VMGEXIT processing:
- SVM_EXIT_CPUID (0x72): Read function/index/xcr0/xss from the GHCB
page and return CPUID results via get_cpuid_values().
- SVM_EXIT_MSR (0x7c): Handle MSR read (RDMSR) and write (WRMSR)
requests from the guest via the GHCB page protocol.
- GHCB_MSR_TERM_REQ (0x100): Decode reason_set and reason_val from the
GHCB MSR and return an error instead of panicking, allowing graceful
error propagation.
Testing:
Reproducer (on Azure DC16as_cc_v5, /dev/mshv):
cloud-hypervisor --cpus boot=1,nested=off --memory size=512M \
--disk path=osdisk.img path=cloudinit \
--net "tap=,mac=12:34:56:78:90:06,ip=192.168.6.1,mask=255.255.255.128" \
--serial null --console pty \
--api-socket /tmp/ch.sock \
--igvm /igvm_files/linux-ttyS0.bin \
--host-data <hash> --platform sev_snp=on -v
Before fix:
thread 'vcpu0' panicked at hypervisor/src/mshv/mod.rs:1207:30:
Unsupported VMGEXIT operation: 100
After fix: VM boots successfully to login prompt with no panics.
All virtio devices (console, rng, disks) activate normally.
No regression risk for non-SEV-SNP: all new code is within the
HVMSG_X64_SEV_VMGEXIT_INTERCEPT handler which is only reached for
SEV-SNP guests.
Signed-off-by: Souradeep Chakrabarti <schakrabarti@microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
ff20f18364
commit
7832401816
@@ -13,6 +13,8 @@ use anyhow::anyhow;
|
|||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
#[cfg(feature = "sev_snp")]
|
#[cfg(feature = "sev_snp")]
|
||||||
|
use log::error;
|
||||||
|
#[cfg(feature = "sev_snp")]
|
||||||
use log::info;
|
use log::info;
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use mshv_bindings::*;
|
use mshv_bindings::*;
|
||||||
@@ -85,6 +87,12 @@ use crate::{CpuState, IoEventAddress, IrqRoutingEntry, MpState};
|
|||||||
|
|
||||||
pub const PAGE_SHIFT: usize = 12;
|
pub const PAGE_SHIFT: usize = 12;
|
||||||
|
|
||||||
|
// SVM exit codes not yet defined in mshv-bindings (AMD APM Vol 2, Table 15-7)
|
||||||
|
#[cfg(feature = "sev_snp")]
|
||||||
|
const SVM_EXITCODE_CPUID: u32 = 0x72;
|
||||||
|
#[cfg(feature = "sev_snp")]
|
||||||
|
const SVM_EXITCODE_MSR: u32 = 0x7c;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
impl From<MshvClockData> for ClockData {
|
impl From<MshvClockData> for ClockData {
|
||||||
fn from(d: MshvClockData) -> Self {
|
fn from(d: MshvClockData) -> Self {
|
||||||
@@ -1199,11 +1207,104 @@ impl cpu::Vcpu for MshvVcpu {
|
|||||||
// Clear the SW_EXIT_INFO1 register to indicate no error
|
// Clear the SW_EXIT_INFO1 register to indicate no error
|
||||||
self.clear_swexit_info1()?;
|
self.clear_swexit_info1()?;
|
||||||
}
|
}
|
||||||
|
SVM_EXITCODE_CPUID => {
|
||||||
|
// SAFETY: Accessing fields from the mapped GHCB page
|
||||||
|
let cpuid_fn = unsafe { (*ghcb).rax } as u32;
|
||||||
|
// SAFETY: Accessing fields from the mapped GHCB page
|
||||||
|
let cpuid_idx = unsafe { (*ghcb).rcx } as u32;
|
||||||
|
// SAFETY: Accessing fields from the mapped GHCB page
|
||||||
|
let xcr0 = unsafe { (*ghcb).xfem };
|
||||||
|
// SAFETY: Accessing fields from the mapped GHCB page
|
||||||
|
let xss = unsafe { (*ghcb).xss };
|
||||||
|
debug!("GHCB CPUID: fn=0x{cpuid_fn:x} idx=0x{cpuid_idx:x}");
|
||||||
|
|
||||||
|
let cpuid_result = self
|
||||||
|
.fd
|
||||||
|
.get_cpuid_values(cpuid_fn, cpuid_idx, xcr0, xss)
|
||||||
|
.unwrap_or([0u32; 4]);
|
||||||
|
|
||||||
|
set_svm_field_u64_ptr!(ghcb, rax, cpuid_result[0] as u64);
|
||||||
|
set_svm_field_u64_ptr!(ghcb, rbx, cpuid_result[1] as u64);
|
||||||
|
set_svm_field_u64_ptr!(ghcb, rcx, cpuid_result[2] as u64);
|
||||||
|
set_svm_field_u64_ptr!(ghcb, rdx, cpuid_result[3] as u64);
|
||||||
|
|
||||||
|
self.clear_swexit_info1()?;
|
||||||
|
}
|
||||||
|
SVM_EXITCODE_MSR => {
|
||||||
|
let exit_info1 =
|
||||||
|
info.__bindgen_anon_2.__bindgen_anon_1.sw_exit_info1;
|
||||||
|
// SAFETY: Accessing fields from the mapped GHCB page
|
||||||
|
let msr_index = unsafe { (*ghcb).rcx } as u32;
|
||||||
|
let is_write = exit_info1 & 1 != 0;
|
||||||
|
|
||||||
|
if is_write {
|
||||||
|
// SAFETY: Accessing fields from the mapped GHCB page
|
||||||
|
let msr_lo = unsafe { (*ghcb).rax } as u32;
|
||||||
|
// SAFETY: Accessing fields from the mapped GHCB page
|
||||||
|
let msr_hi = unsafe { (*ghcb).rdx } as u32;
|
||||||
|
let msr_val = ((msr_hi as u64) << 32) | (msr_lo as u64);
|
||||||
|
debug!(
|
||||||
|
"GHCB MSR WRITE: index=0x{msr_index:x} val=0x{msr_val:x}"
|
||||||
|
);
|
||||||
|
let entry = msr_entry {
|
||||||
|
index: msr_index,
|
||||||
|
data: msr_val,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let msr_entries = MsrEntries::from_entries(&[entry])
|
||||||
|
.map_err(|e| {
|
||||||
|
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||||
|
})?;
|
||||||
|
self.fd.set_msrs(&msr_entries).map_err(|e| {
|
||||||
|
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
let entry = msr_entry {
|
||||||
|
index: msr_index,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut msr_entries = MsrEntries::from_entries(&[entry])
|
||||||
|
.map_err(|e| {
|
||||||
|
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||||
|
})?;
|
||||||
|
self.fd.get_msrs(&mut msr_entries).map_err(|e| {
|
||||||
|
cpu::HypervisorCpuError::RunVcpu(e.into())
|
||||||
|
})?;
|
||||||
|
let msr_slice = msr_entries.as_slice();
|
||||||
|
if msr_slice.is_empty() {
|
||||||
|
return Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
|
||||||
|
"get_msrs returned no entries for index 0x{msr_index:x}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let msr_val = msr_slice[0].data;
|
||||||
|
debug!(
|
||||||
|
"GHCB MSR READ: index=0x{msr_index:x} val=0x{msr_val:x}"
|
||||||
|
);
|
||||||
|
set_svm_field_u64_ptr!(ghcb, rax, msr_val & 0xFFFFFFFF);
|
||||||
|
set_svm_field_u64_ptr!(ghcb, rdx, msr_val >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clear_swexit_info1()?;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("GHCB_INFO_NORMAL: Unhandled exit code: {exit_code:0x}")
|
panic!("GHCB_INFO_NORMAL: Unhandled exit code: {exit_code:0x}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GHCB_INFO_SHUTDOWN_REQUEST => {
|
||||||
|
let ghcb_msr_val = { info.ghcb_msr };
|
||||||
|
let reason_set = (ghcb_msr_val >> 12) & 0xf;
|
||||||
|
let reason_val = (ghcb_msr_val >> 16) & 0xff;
|
||||||
|
error!(
|
||||||
|
"GHCB_MSR_TERM_REQ: Guest terminated! \
|
||||||
|
ghcb_msr=0x{ghcb_msr_val:x}, \
|
||||||
|
reason_set={reason_set}, reason_val={reason_val}"
|
||||||
|
);
|
||||||
|
return Err(cpu::HypervisorCpuError::RunVcpu(anyhow!(
|
||||||
|
"Guest requested termination via GHCB_MSR_TERM_REQ \
|
||||||
|
(reason_set={reason_set}, reason_val={reason_val})"
|
||||||
|
)));
|
||||||
|
}
|
||||||
_ => panic!("Unsupported VMGEXIT operation: {ghcb_op:0x}"),
|
_ => panic!("Unsupported VMGEXIT operation: {ghcb_op:0x}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user