hypervisor: Avoid leaking KVM GIC state into common GIC state

KVM supports GICv3-ITS emulation and the current GicState is modelled
around the KVM implementation. We should refactor this to accomodate
other hypervisor requirements. For example, MSHV only support GICv2M
emulation for guests for delivering MSI interrupts instead of GICv3-ITS.

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
This commit is contained in:
Jinank Jain 2025-03-27 13:52:26 +00:00
parent 3d88996e5b
commit 6bb33601d0
7 changed files with 88 additions and 22 deletions

1
Cargo.lock generated
View file

@ -984,6 +984,7 @@ dependencies = [
"mshv-bindings",
"mshv-ioctls",
"serde",
"serde_json",
"serde_with",
"thiserror 2.0.6",
"vfio-ioctls",

View file

@ -9,8 +9,8 @@ use std::sync::{Arc, Mutex};
use anyhow::anyhow;
use arch::layout;
use hypervisor::arch::aarch64::gic::{Vgic, VgicConfig};
use hypervisor::{CpuState, GicState};
use hypervisor::arch::aarch64::gic::{GicState, Vgic, VgicConfig};
use hypervisor::CpuState;
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
LegacyIrqSourceConfig, MsiIrqGroupConfig,

View file

@ -30,6 +30,7 @@ mshv-bindings = { workspace = true, features = [
], optional = true }
mshv-ioctls = { workspace = true, optional = true }
serde = { version = "1.0.208", features = ["derive", "rc"] }
serde_json = "1.0.120"
serde_with = { version = "3.9.0", default-features = false, features = [
"macros",
] }

View file

@ -5,9 +5,12 @@
use std::any::Any;
use std::result;
use serde::de::Error as SerdeError;
use serde::{Deserialize, Serialize};
use serde_json;
use thiserror::Error;
use crate::{CpuState, GicState, HypervisorDeviceError, HypervisorVmError};
use crate::{CpuState, HypervisorDeviceError, HypervisorVmError};
/// Errors thrown while setting up the VGIC.
#[derive(Debug, Error)]
@ -36,6 +39,50 @@ pub struct VgicConfig {
pub nr_irqs: u32,
}
#[derive(Clone, Serialize)]
pub enum GicState {
#[cfg(feature = "kvm")]
Kvm(crate::kvm::aarch64::gic::Gicv3ItsState),
}
impl<'de> Deserialize<'de> for GicState {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
// GicStateDefaultDeserialize is a helper enum that mirrors GicState but also derives the Deserialize trait.
// This enables backward-compatible deserialization of GicState, facilitating live-upgrade scenarios.
#[derive(Deserialize)]
pub enum GicStateDefaultDeserialize {
#[cfg(feature = "kvm")]
Kvm(crate::kvm::aarch64::gic::Gicv3ItsState),
}
const {
assert!(
std::mem::size_of::<GicStateDefaultDeserialize>()
== std::mem::size_of::<GicState>()
)
};
let value: serde_json::Value = Deserialize::deserialize(deserializer)?;
#[cfg(feature = "kvm")]
if let Ok(gicv3_its_state) =
crate::kvm::aarch64::gic::Gicv3ItsState::deserialize(value.clone())
{
return Ok(GicState::Kvm(gicv3_its_state));
}
if let Ok(gic_state_de) = GicStateDefaultDeserialize::deserialize(value.clone()) {
return match gic_state_de {
#[cfg(feature = "kvm")]
GicStateDefaultDeserialize::Kvm(state) => Ok(GicState::Kvm(state)),
};
}
Err(SerdeError::custom("Failed to deserialize GicState"))
}
}
/// Hypervisor agnostic interface for a virtualized GIC
pub trait Vgic: Send + Sync {
/// Returns the fdt compatibility property of the device

View file

@ -14,7 +14,7 @@ use kvm_ioctls::DeviceFd;
use redist_regs::{construct_gicr_typers, get_redist_regs, set_redist_regs};
use serde::{Deserialize, Serialize};
use crate::arch::aarch64::gic::{Error, Result, Vgic, VgicConfig};
use crate::arch::aarch64::gic::{Error, GicState, Result, Vgic, VgicConfig};
use crate::device::HypervisorDeviceError;
use crate::kvm::KvmVm;
use crate::{CpuState, Vm};
@ -125,6 +125,20 @@ pub struct Gicv3ItsState {
its_baser: [u64; 8],
}
impl From<GicState> for Gicv3ItsState {
fn from(state: GicState) -> Self {
match state {
GicState::Kvm(state) => state,
}
}
}
impl From<Gicv3ItsState> for GicState {
fn from(state: Gicv3ItsState) -> Self {
GicState::Kvm(state)
}
}
impl KvmGicV3Its {
/// Device trees specific constants
pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9;
@ -316,7 +330,7 @@ impl Vgic for KvmGicV3Its {
}
/// Save the state of GICv3ITS.
fn state(&self) -> Result<Gicv3ItsState> {
fn state(&self) -> Result<GicState> {
let gicr_typers = self.gicr_typers.clone();
let gicd_ctlr = read_ctlr(&self.device)?;
@ -366,7 +380,7 @@ impl Vgic for KvmGicV3Its {
GITS_IIDR,
)?;
Ok(Gicv3ItsState {
let gic_state: GicState = Gicv3ItsState {
dist: dist_state,
rdist: rdist_state,
icc: icc_state,
@ -377,48 +391,53 @@ impl Vgic for KvmGicV3Its {
its_cwriter: its_cwriter_state,
its_creadr: its_creadr_state,
its_baser: its_baser_state,
})
}
.into();
Ok(gic_state)
}
/// Restore the state of GICv3ITS.
fn set_state(&mut self, state: &Gicv3ItsState) -> Result<()> {
fn set_state(&mut self, state: &GicState) -> Result<()> {
let kvm_state: Gicv3ItsState = state.clone().into();
let gicr_typers = self.gicr_typers.clone();
write_ctlr(&self.device, state.gicd_ctlr)?;
write_ctlr(&self.device, kvm_state.gicd_ctlr)?;
set_dist_regs(&self.device, &state.dist)?;
set_dist_regs(&self.device, &kvm_state.dist)?;
set_redist_regs(&self.device, &gicr_typers, &state.rdist)?;
set_redist_regs(&self.device, &gicr_typers, &kvm_state.rdist)?;
set_icc_regs(&self.device, &gicr_typers, &state.icc)?;
set_icc_regs(&self.device, &gicr_typers, &kvm_state.icc)?;
//Restore GICv3ITS registers
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_IIDR,
state.its_iidr,
kvm_state.its_iidr,
)?;
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CBASER,
state.its_cbaser,
kvm_state.its_cbaser,
)?;
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CREADR,
state.its_creadr,
kvm_state.its_creadr,
)?;
gicv3_its_attr_set(
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CWRITER,
state.its_cwriter,
kvm_state.its_cwriter,
)?;
for i in 0..8 {
@ -426,7 +445,7 @@ impl Vgic for KvmGicV3Its {
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_BASER + i * 8,
state.its_baser[i as usize],
kvm_state.its_baser[i as usize],
)?;
}
@ -437,7 +456,7 @@ impl Vgic for KvmGicV3Its {
self.its_device.as_ref().unwrap(),
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
GITS_CTLR,
state.its_ctlr,
kvm_state.its_ctlr,
)
}

View file

@ -29,9 +29,7 @@ use vmm_sys_util::eventfd::EventFd;
#[cfg(target_arch = "aarch64")]
use crate::aarch64::gic::KvmGicV3Its;
#[cfg(target_arch = "aarch64")]
pub use crate::aarch64::{
check_required_kvm_extensions, gic::Gicv3ItsState as GicState, is_system_register, VcpuKvmState,
};
pub use crate::aarch64::{check_required_kvm_extensions, is_system_register, VcpuKvmState};
#[cfg(target_arch = "aarch64")]
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
#[cfg(target_arch = "riscv64")]

View file

@ -59,7 +59,7 @@ pub use cpu::CpuVendor;
pub use cpu::{HypervisorCpuError, Vcpu, VmExit};
pub use device::HypervisorDeviceError;
#[cfg(all(feature = "kvm", target_arch = "aarch64"))]
pub use kvm::{aarch64, GicState};
pub use kvm::aarch64;
#[cfg(all(feature = "kvm", target_arch = "riscv64"))]
pub use kvm::{riscv64, AiaState};
pub use vm::{