hypervisor: snapshot interrupt state for WHPX.

When snapshotting, the interrupt state (pending or otherwise) needs to
be saved from the VCPU. Every hypervisor has its own implementation
of how this is exposed to VMMs (KVM uses vcpu_events). This CL adds
an implementation for WHPX's VCPU.

BUG=b:270734340
TEST=builds (we can't test the full WHPX snapshot flow yet; need to
reach critical mass on implementation first).

Change-Id: Ic97ad15391f277967bd7b19a7ff6b55c57f3a00b
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4428057
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: Elie Kheirallah <khei@google.com>
Reviewed-by: Vaibhav Nagarnaik <vnagarnaik@google.com>
Commit-Queue: Noah Gold <nkgold@google.com>
This commit is contained in:
Noah Gold 2023-04-14 11:39:03 -07:00 committed by crosvm LUCI
parent f897fb4f54
commit 307b57809a
2 changed files with 86 additions and 5 deletions

View file

@ -5,6 +5,8 @@
use std::collections::HashMap;
use once_cell::sync::Lazy;
use serde::Deserialize;
use serde::Serialize;
use super::whpx_sys::*;
use crate::CpuIdEntry;
@ -587,6 +589,54 @@ impl From<&WhpxDebugRegs> for DebugRegs {
}
}
/// Registers that store pending interrupts and interrupt state.
#[derive(Default)]
pub(super) struct WhpxInterruptRegs {
register_values: [WHV_REGISTER_VALUE; 2],
}
#[derive(Serialize, Deserialize)]
pub(super) struct SerializedWhpxInterruptRegs {
pending_interruption: u64,
interrupt_state: u64,
}
impl WhpxInterruptRegs {
pub(super) fn get_register_names() -> &'static [WHV_REGISTER_NAME; 2] {
const REG_NAMES: [WHV_REGISTER_NAME; 2] = [
WHV_REGISTER_NAME_WHvRegisterPendingInterruption,
WHV_REGISTER_NAME_WHvRegisterInterruptState,
];
&REG_NAMES
}
pub(super) fn as_ptr(&self) -> *const WHV_REGISTER_VALUE {
self.register_values.as_ptr()
}
pub(super) fn as_mut_ptr(&mut self) -> *mut WHV_REGISTER_VALUE {
self.register_values.as_mut_ptr()
}
pub(super) fn into_serializable(self) -> SerializedWhpxInterruptRegs {
SerializedWhpxInterruptRegs {
// SAFETY: This register is a valid u64.
pending_interruption: unsafe { self.register_values[0].PendingInterruption.AsUINT64 },
// SAFETY: This register is a valid u64.
interrupt_state: unsafe { self.register_values[1].InterruptState.AsUINT64 },
}
}
pub(super) fn from_serializable(serialized_regs: SerializedWhpxInterruptRegs) -> Self {
let mut whpx_interrupt_regs: WhpxInterruptRegs = Default::default();
whpx_interrupt_regs.register_values[0]
.PendingInterruption
.AsUINT64 = serialized_regs.pending_interruption;
whpx_interrupt_regs.register_values[1]
.InterruptState
.AsUINT64 = serialized_regs.interrupt_state;
whpx_interrupt_regs
}
}
// list of MSR registers for whpx, and their actual ids
pub(super) const MSR_TSC: u32 = 0x00000010;
pub(super) const MSR_EFER: u32 = 0xc0000080;

View file

@ -1101,14 +1101,45 @@ impl VcpuX86_64 for WhpxVcpu {
})
}
// TODO: b/270734340 implement
fn get_interrupt_state(&self) -> Result<serde_json::Value> {
Err(Error::new(EOPNOTSUPP))
let mut whpx_interrupt_regs: WhpxInterruptRegs = Default::default();
let reg_names = WhpxInterruptRegs::get_register_names();
// SAFETY: we have enough space for all the registers & the memory lives for the duration
// of the FFI call.
check_whpx!(unsafe {
WHvGetVirtualProcessorRegisters(
self.vm_partition.partition,
self.index,
reg_names as *const WHV_REGISTER_NAME,
reg_names.len() as u32,
whpx_interrupt_regs.as_mut_ptr(),
)
})?;
serde_json::to_value(whpx_interrupt_regs.into_serializable()).map_err(|e| {
error!("failed to serialize interrupt state: {:?}", e);
Error::new(EIO)
})
}
// TODO: b/270734340 implement
fn set_interrupt_state(&self, _data: serde_json::Value) -> Result<()> {
Err(Error::new(EOPNOTSUPP))
fn set_interrupt_state(&self, data: serde_json::Value) -> Result<()> {
let whpx_interrupt_regs =
WhpxInterruptRegs::from_serializable(serde_json::from_value(data).map_err(|e| {
error!("failed to serialize interrupt state: {:?}", e);
Error::new(EIO)
})?);
let reg_names = WhpxInterruptRegs::get_register_names();
// SAFETY: we have enough space for all the registers & the memory lives for the duration
// of the FFI call.
check_whpx!(unsafe {
WHvSetVirtualProcessorRegisters(
self.vm_partition.partition,
self.index,
reg_names as *const WHV_REGISTER_NAME,
reg_names.len() as u32,
whpx_interrupt_regs.as_ptr(),
)
})
}
/// Gets the VCPU debug registers.