diff --git a/arch/src/aarch64/gic/gicv3_its.rs b/arch/src/aarch64/gic/gicv3_its.rs deleted file mode 100644 index 28c7bef39..000000000 --- a/arch/src/aarch64/gic/gicv3_its.rs +++ /dev/null @@ -1,555 +0,0 @@ -// Copyright 2021 Arm Limited (or its affiliates). All rights reserved. -// SPDX-License-Identifier: Apache-2.0 -// -// This file implements the GicV3 device with ITS (Virtual Interrupt Translation Service). - -pub mod kvm { - use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr}; - use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs}; - use crate::aarch64::gic::redist_regs::{ - construct_gicr_typers, get_redist_regs, set_redist_regs, - }; - - use crate::aarch64::gic::kvm::{save_pending_tables, KvmGicDevice}; - use crate::aarch64::gic::GicDevice; - use crate::layout; - use anyhow::anyhow; - use hypervisor::kvm::kvm_bindings; - use hypervisor::CpuState; - use serde::{Deserialize, Serialize}; - use std::any::Any; - use std::convert::TryInto; - use std::sync::Arc; - use std::{boxed::Box, result}; - use vm_memory::Address; - use vm_migration::{ - Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, - }; - - const GITS_CTLR: u32 = 0x0000; - const GITS_IIDR: u32 = 0x0004; - const GITS_CBASER: u32 = 0x0080; - const GITS_CWRITER: u32 = 0x0088; - const GITS_CREADR: u32 = 0x0090; - const GITS_BASER: u32 = 0x0100; - - /// Errors thrown while saving/restoring the GICv3ITS. - #[derive(Debug)] - pub enum Error { - /// Error in saving RDIST pending tables into guest RAM. - SavePendingTables(crate::aarch64::gic::Error), - /// Error in saving GIC distributor registers. - SaveDistributorRegisters(crate::aarch64::gic::Error), - /// Error in restoring GIC distributor registers. - RestoreDistributorRegisters(crate::aarch64::gic::Error), - /// Error in saving GIC distributor control registers. - SaveDistributorCtrlRegisters(crate::aarch64::gic::Error), - /// Error in restoring GIC distributor control registers. - RestoreDistributorCtrlRegisters(crate::aarch64::gic::Error), - /// Error in saving GIC redistributor registers. - SaveRedistributorRegisters(crate::aarch64::gic::Error), - /// Error in restoring GIC redistributor registers. - RestoreRedistributorRegisters(crate::aarch64::gic::Error), - /// Error in saving GIC CPU interface registers. - SaveIccRegisters(crate::aarch64::gic::Error), - /// Error in restoring GIC CPU interface registers. - RestoreIccRegisters(crate::aarch64::gic::Error), - /// Error in saving GICv3ITS IIDR register. - SaveITSIIDR(crate::aarch64::gic::Error), - /// Error in restoring GICv3ITS IIDR register. - RestoreITSIIDR(crate::aarch64::gic::Error), - /// Error in saving GICv3ITS CBASER register. - SaveITSCBASER(crate::aarch64::gic::Error), - /// Error in restoring GICv3ITS CBASER register. - RestoreITSCBASER(crate::aarch64::gic::Error), - /// Error in saving GICv3ITS CREADR register. - SaveITSCREADR(crate::aarch64::gic::Error), - /// Error in restoring GICv3ITS CREADR register. - RestoreITSCREADR(crate::aarch64::gic::Error), - /// Error in saving GICv3ITS CWRITER register. - SaveITSCWRITER(crate::aarch64::gic::Error), - /// Error in restoring GICv3ITS CWRITER register. - RestoreITSCWRITER(crate::aarch64::gic::Error), - /// Error in saving GICv3ITS BASER register. - SaveITSBASER(crate::aarch64::gic::Error), - /// Error in restoring GICv3ITS BASER register. - RestoreITSBASER(crate::aarch64::gic::Error), - /// Error in saving GICv3ITS CTLR register. - SaveITSCTLR(crate::aarch64::gic::Error), - /// Error in restoring GICv3ITS CTLR register. - RestoreITSCTLR(crate::aarch64::gic::Error), - /// Error in saving GICv3ITS restore tables. - SaveITSTables(crate::aarch64::gic::Error), - /// Error in restoring GICv3ITS restore tables. - RestoreITSTables(crate::aarch64::gic::Error), - } - - type Result = result::Result; - - /// Access an ITS device attribute. - /// - /// This is a helper function to get/set the ITS device attribute depending - /// the bool parameter `set` provided. - pub fn gicv3_its_attr_access( - its_device: &Arc, - group: u32, - attr: u32, - val: &u64, - set: bool, - ) -> crate::aarch64::gic::Result<()> { - let mut gicv3_its_attr = kvm_bindings::kvm_device_attr { - group, - attr: attr as u64, - addr: val as *const u64 as u64, - flags: 0, - }; - if set { - its_device - .set_device_attr(&gicv3_its_attr) - .map_err(crate::aarch64::gic::Error::SetDeviceAttribute)?; - } else { - its_device - .get_device_attr(&mut gicv3_its_attr) - .map_err(crate::aarch64::gic::Error::GetDeviceAttribute)?; - } - Ok(()) - } - - /// Function that saves/restores ITS tables into guest RAM. - /// - /// The tables get flushed to guest RAM whenever the VM gets stopped. - pub fn gicv3_its_tables_access( - its_device: &Arc, - save: bool, - ) -> crate::aarch64::gic::Result<()> { - let attr = if save { - u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES) - } else { - u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES) - }; - - let init_gic_attr = kvm_bindings::kvm_device_attr { - group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - attr, - addr: 0, - flags: 0, - }; - its_device - .set_device_attr(&init_gic_attr) - .map_err(crate::aarch64::gic::Error::SetDeviceAttribute) - } - - pub struct KvmGicV3Its { - /// The hypervisor agnostic device for the GicV3 - device: Arc, - - /// The hypervisor agnostic device for the Its device - its_device: Option>, - - /// Vector holding values of GICR_TYPER for each vCPU - gicr_typers: Vec, - - /// GIC device properties, to be used for setting up the fdt entry - gic_properties: [u64; 4], - - /// MSI device properties, to be used for setting up the fdt entry - msi_properties: [u64; 2], - - /// Number of CPUs handled by the device - vcpu_count: u64, - } - - #[derive(Clone, Default, Serialize, Deserialize)] - pub struct Gicv3ItsState { - dist: Vec, - rdist: Vec, - icc: Vec, - // special register that enables interrupts and affinity routing - gicd_ctlr: u32, - its_ctlr: u64, - its_iidr: u64, - its_cbaser: u64, - its_cwriter: u64, - its_creadr: u64, - its_baser: [u64; 8], - } - - impl KvmGicV3Its { - // Device trees specific constants - pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9; - - /// Get the address of the GIC distributor. - pub fn get_dist_addr() -> u64 { - layout::GIC_V3_DIST_START.raw_value() - } - - /// Get the size of the GIC distributor. - pub fn get_dist_size() -> u64 { - layout::GIC_V3_DIST_SIZE - } - - /// Get the address of the GIC redistributors. - pub fn get_redists_addr(vcpu_count: u64) -> u64 { - KvmGicV3Its::get_dist_addr() - KvmGicV3Its::get_redists_size(vcpu_count) - } - - /// Get the size of the GIC redistributors. - pub fn get_redists_size(vcpu_count: u64) -> u64 { - vcpu_count * layout::GIC_V3_REDIST_SIZE - } - - fn get_msi_size() -> u64 { - layout::GIC_V3_ITS_SIZE - } - - fn get_msi_addr(vcpu_count: u64) -> u64 { - KvmGicV3Its::get_redists_addr(vcpu_count) - KvmGicV3Its::get_msi_size() - } - - /// Save the state of GICv3ITS. - fn state(&self, gicr_typers: &[u64]) -> Result { - let gicd_ctlr = - read_ctlr(self.device()).map_err(Error::SaveDistributorCtrlRegisters)?; - - let dist_state = - get_dist_regs(self.device()).map_err(Error::SaveDistributorRegisters)?; - - let rdist_state = get_redist_regs(self.device(), gicr_typers) - .map_err(Error::SaveRedistributorRegisters)?; - - let icc_state = - get_icc_regs(self.device(), gicr_typers).map_err(Error::SaveIccRegisters)?; - - let its_baser_state: [u64; 8] = [0; 8]; - for i in 0..8 { - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_BASER + i * 8, - &its_baser_state[i as usize], - false, - ) - .map_err(Error::SaveITSBASER)?; - } - - let its_ctlr_state: u64 = 0; - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_CTLR, - &its_ctlr_state, - false, - ) - .map_err(Error::SaveITSCTLR)?; - - let its_cbaser_state: u64 = 0; - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_CBASER, - &its_cbaser_state, - false, - ) - .map_err(Error::SaveITSCBASER)?; - - let its_creadr_state: u64 = 0; - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_CREADR, - &its_creadr_state, - false, - ) - .map_err(Error::SaveITSCREADR)?; - - let its_cwriter_state: u64 = 0; - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_CWRITER, - &its_cwriter_state, - false, - ) - .map_err(Error::SaveITSCWRITER)?; - - let its_iidr_state: u64 = 0; - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_IIDR, - &its_iidr_state, - false, - ) - .map_err(Error::SaveITSIIDR)?; - - Ok(Gicv3ItsState { - dist: dist_state, - rdist: rdist_state, - icc: icc_state, - gicd_ctlr, - its_ctlr: its_ctlr_state, - its_iidr: its_iidr_state, - its_cbaser: its_cbaser_state, - its_cwriter: its_cwriter_state, - its_creadr: its_creadr_state, - its_baser: its_baser_state, - }) - } - - /// Restore the state of GICv3ITS. - fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3ItsState) -> Result<()> { - write_ctlr(self.device(), state.gicd_ctlr) - .map_err(Error::RestoreDistributorCtrlRegisters)?; - - set_dist_regs(self.device(), &state.dist) - .map_err(Error::RestoreDistributorRegisters)?; - - set_redist_regs(self.device(), gicr_typers, &state.rdist) - .map_err(Error::RestoreRedistributorRegisters)?; - - set_icc_regs(self.device(), gicr_typers, &state.icc) - .map_err(Error::RestoreIccRegisters)?; - - //Restore GICv3ITS registers - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_IIDR, - &state.its_iidr, - true, - ) - .map_err(Error::RestoreITSIIDR)?; - - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_CBASER, - &state.its_cbaser, - true, - ) - .map_err(Error::RestoreITSCBASER)?; - - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_CREADR, - &state.its_creadr, - true, - ) - .map_err(Error::RestoreITSCREADR)?; - - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_CWRITER, - &state.its_cwriter, - true, - ) - .map_err(Error::RestoreITSCWRITER)?; - - for i in 0..8 { - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_BASER + i * 8, - &state.its_baser[i as usize], - true, - ) - .map_err(Error::RestoreITSBASER)?; - } - - // Restore ITS tables - gicv3_its_tables_access(self.its_device().unwrap(), false) - .map_err(Error::RestoreITSTables)?; - - gicv3_its_attr_access( - self.its_device().unwrap(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, - GITS_CTLR, - &state.its_ctlr, - true, - ) - .map_err(Error::RestoreITSCTLR)?; - - Ok(()) - } - } - - impl GicDevice for KvmGicV3Its { - fn device(&self) -> &Arc { - &self.device - } - - fn its_device(&self) -> Option<&Arc> { - self.its_device.as_ref() - } - - fn fdt_compatibility(&self) -> &str { - "arm,gic-v3" - } - - fn msi_compatible(&self) -> bool { - true - } - - fn msi_compatibility(&self) -> &str { - "arm,gic-v3-its" - } - - fn fdt_maint_irq(&self) -> u32 { - KvmGicV3Its::ARCH_GIC_V3_MAINT_IRQ - } - - fn msi_properties(&self) -> &[u64] { - &self.msi_properties - } - - fn device_properties(&self) -> &[u64] { - &self.gic_properties - } - - fn vcpu_count(&self) -> u64 { - self.vcpu_count - } - - fn set_its_device(&mut self, its_device: Option>) { - self.its_device = its_device; - } - - fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) { - let gicr_typers = construct_gicr_typers(vcpu_states); - self.gicr_typers = gicr_typers; - } - - fn as_any_concrete_mut(&mut self) -> &mut dyn Any { - self - } - } - - impl KvmGicDevice for KvmGicV3Its { - fn version() -> u32 { - kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3 - } - - fn create_device( - device: Arc, - vcpu_count: u64, - ) -> Box { - Box::new(KvmGicV3Its { - device, - its_device: None, - gicr_typers: vec![0; vcpu_count.try_into().unwrap()], - gic_properties: [ - KvmGicV3Its::get_dist_addr(), - KvmGicV3Its::get_dist_size(), - KvmGicV3Its::get_redists_addr(vcpu_count), - KvmGicV3Its::get_redists_size(vcpu_count), - ], - msi_properties: [ - KvmGicV3Its::get_msi_addr(vcpu_count), - KvmGicV3Its::get_msi_size(), - ], - vcpu_count, - }) - } - - fn init_device_attributes( - vm: &Arc, - gic_device: &mut dyn GicDevice, - ) -> crate::aarch64::gic::Result<()> { - // GicV3 part attributes - /* Setting up the distributor attribute. - We are placing the GIC below 1GB so we need to substract the size of the distributor. - */ - Self::set_device_attribute( - gic_device.device(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST), - &KvmGicV3Its::get_dist_addr() as *const u64 as u64, - 0, - )?; - - /* Setting up the redistributors' attribute. - We are calculating here the start of the redistributors address. We have one per CPU. - */ - Self::set_device_attribute( - gic_device.device(), - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST), - &KvmGicV3Its::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64, - 0, - )?; - - // ITS part attributes - let mut its_device = kvm_bindings::kvm_create_device { - type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS, - fd: 0, - flags: 0, - }; - - let its_fd = vm - .create_device(&mut its_device) - .map_err(crate::aarch64::gic::Error::CreateGic)?; - - Self::set_device_attribute( - &its_fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, - u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE), - &KvmGicV3Its::get_msi_addr(gic_device.vcpu_count()) as *const u64 as u64, - 0, - )?; - - Self::set_device_attribute( - &its_fd, - kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, - u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), - 0, - 0, - )?; - - gic_device.set_its_device(Some(its_fd)); - - Ok(()) - } - } - - pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its"; - impl Snapshottable for KvmGicV3Its { - fn id(&self) -> String { - GIC_V3_ITS_SNAPSHOT_ID.to_string() - } - - fn snapshot(&mut self) -> std::result::Result { - let gicr_typers = self.gicr_typers.clone(); - Snapshot::new_from_state(&self.id(), &self.state(&gicr_typers).unwrap()) - } - - fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> { - let gicr_typers = self.gicr_typers.clone(); - self.set_state(&gicr_typers, &snapshot.to_state(&self.id())?) - .map_err(|e| { - MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e)) - }) - } - } - - impl Pausable for KvmGicV3Its { - fn pause(&mut self) -> std::result::Result<(), MigratableError> { - // Flush redistributors pending tables to guest RAM. - save_pending_tables(self.device()).map_err(|e| { - MigratableError::Pause(anyhow!( - "Could not save GICv3ITS GIC pending tables {:?}", - e - )) - })?; - // Flush ITS tables to guest RAM. - gicv3_its_tables_access(self.its_device().unwrap(), true).map_err(|e| { - MigratableError::Pause(anyhow!("Could not save GICv3ITS ITS tables {:?}", e)) - })?; - - Ok(()) - } - } - impl Transportable for KvmGicV3Its {} - impl Migratable for KvmGicV3Its {} -} diff --git a/arch/src/aarch64/gic/mod.rs b/arch/src/aarch64/gic/mod.rs index cb4d3988c..f1517feaa 100644 --- a/arch/src/aarch64/gic/mod.rs +++ b/arch/src/aarch64/gic/mod.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 pub mod dist_regs; -pub mod gicv3_its; pub mod icc_regs; pub mod redist_regs; @@ -73,30 +72,388 @@ pub trait GicDevice: Send { } pub mod kvm { - use super::GicDevice; use super::Result; - use crate::aarch64::gic::gicv3_its::kvm::KvmGicV3Its; + use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr}; + use crate::aarch64::gic::icc_regs::{get_icc_regs, set_icc_regs}; + use crate::aarch64::gic::redist_regs::{ + construct_gicr_typers, get_redist_regs, set_redist_regs, + }; + use crate::aarch64::gic::GicDevice; use crate::layout; + use anyhow::anyhow; use hypervisor::kvm::kvm_bindings; + use hypervisor::CpuState; + use serde::{Deserialize, Serialize}; + use std::any::Any; use std::boxed::Box; + use std::convert::TryInto; use std::sync::Arc; + use vm_memory::Address; + use vm_migration::{ + Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, + }; + + const GITS_CTLR: u32 = 0x0000; + const GITS_IIDR: u32 = 0x0004; + const GITS_CBASER: u32 = 0x0080; + const GITS_CWRITER: u32 = 0x0088; + const GITS_CREADR: u32 = 0x0090; + const GITS_BASER: u32 = 0x0100; + + /// Access an ITS device attribute. + /// + /// This is a helper function to get/set the ITS device attribute depending + /// the bool parameter `set` provided. + pub fn gicv3_its_attr_access( + its_device: &Arc, + group: u32, + attr: u32, + val: &u64, + set: bool, + ) -> crate::aarch64::gic::Result<()> { + let mut gicv3_its_attr = kvm_bindings::kvm_device_attr { + group, + attr: attr as u64, + addr: val as *const u64 as u64, + flags: 0, + }; + if set { + its_device + .set_device_attr(&gicv3_its_attr) + .map_err(crate::aarch64::gic::Error::SetDeviceAttribute)?; + } else { + its_device + .get_device_attr(&mut gicv3_its_attr) + .map_err(crate::aarch64::gic::Error::GetDeviceAttribute)?; + } + Ok(()) + } + + /// Function that saves/restores ITS tables into guest RAM. + /// + /// The tables get flushed to guest RAM whenever the VM gets stopped. + pub fn gicv3_its_tables_access( + its_device: &Arc, + save: bool, + ) -> crate::aarch64::gic::Result<()> { + let attr = if save { + u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES) + } else { + u64::from(kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES) + }; + + let init_gic_attr = kvm_bindings::kvm_device_attr { + group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + attr, + addr: 0, + flags: 0, + }; + its_device + .set_device_attr(&init_gic_attr) + .map_err(crate::aarch64::gic::Error::SetDeviceAttribute) + } + + pub struct KvmGicV3Its { + /// The hypervisor agnostic device for the GicV3 + device: Arc, + + /// The hypervisor agnostic device for the Its device + its_device: Option>, + + /// Vector holding values of GICR_TYPER for each vCPU + gicr_typers: Vec, + + /// GIC device properties, to be used for setting up the fdt entry + gic_properties: [u64; 4], + + /// MSI device properties, to be used for setting up the fdt entry + msi_properties: [u64; 2], + + /// Number of CPUs handled by the device + vcpu_count: u64, + } + + #[derive(Clone, Default, Serialize, Deserialize)] + pub struct Gicv3ItsState { + dist: Vec, + rdist: Vec, + icc: Vec, + // special register that enables interrupts and affinity routing + gicd_ctlr: u32, + its_ctlr: u64, + its_iidr: u64, + its_cbaser: u64, + its_cwriter: u64, + its_creadr: u64, + its_baser: [u64; 8], + } + + impl KvmGicV3Its { + /// Device trees specific constants + pub const ARCH_GIC_V3_MAINT_IRQ: u32 = 9; + + /// Get the address of the GIC distributor. + pub fn get_dist_addr() -> u64 { + layout::GIC_V3_DIST_START.raw_value() + } + + /// Get the size of the GIC distributor. + pub fn get_dist_size() -> u64 { + layout::GIC_V3_DIST_SIZE + } + + /// Get the address of the GIC redistributors. + pub fn get_redists_addr(vcpu_count: u64) -> u64 { + KvmGicV3Its::get_dist_addr() - KvmGicV3Its::get_redists_size(vcpu_count) + } + + /// Get the size of the GIC redistributors. + pub fn get_redists_size(vcpu_count: u64) -> u64 { + vcpu_count * layout::GIC_V3_REDIST_SIZE + } + + fn get_msi_size() -> u64 { + layout::GIC_V3_ITS_SIZE + } + + fn get_msi_addr(vcpu_count: u64) -> u64 { + KvmGicV3Its::get_redists_addr(vcpu_count) - KvmGicV3Its::get_msi_size() + } + + /// Save the state of GICv3ITS. + fn state(&self, gicr_typers: &[u64]) -> Result { + let gicd_ctlr = read_ctlr(self.device())?; + + let dist_state = get_dist_regs(self.device())?; + + let rdist_state = get_redist_regs(self.device(), gicr_typers)?; + + let icc_state = get_icc_regs(self.device(), gicr_typers)?; + + let its_baser_state: [u64; 8] = [0; 8]; + for i in 0..8 { + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_BASER + i * 8, + &its_baser_state[i as usize], + false, + )?; + } + + let its_ctlr_state: u64 = 0; + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CTLR, + &its_ctlr_state, + false, + )?; + + let its_cbaser_state: u64 = 0; + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CBASER, + &its_cbaser_state, + false, + )?; + + let its_creadr_state: u64 = 0; + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CREADR, + &its_creadr_state, + false, + )?; + + let its_cwriter_state: u64 = 0; + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CWRITER, + &its_cwriter_state, + false, + )?; + + let its_iidr_state: u64 = 0; + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_IIDR, + &its_iidr_state, + false, + )?; + + Ok(Gicv3ItsState { + dist: dist_state, + rdist: rdist_state, + icc: icc_state, + gicd_ctlr, + its_ctlr: its_ctlr_state, + its_iidr: its_iidr_state, + its_cbaser: its_cbaser_state, + its_cwriter: its_cwriter_state, + its_creadr: its_creadr_state, + its_baser: its_baser_state, + }) + } + + /// Restore the state of GICv3ITS. + fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3ItsState) -> Result<()> { + write_ctlr(self.device(), state.gicd_ctlr)?; + + set_dist_regs(self.device(), &state.dist)?; + + set_redist_regs(self.device(), gicr_typers, &state.rdist)?; + + set_icc_regs(self.device(), gicr_typers, &state.icc)?; + + //Restore GICv3ITS registers + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_IIDR, + &state.its_iidr, + true, + )?; + + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CBASER, + &state.its_cbaser, + true, + )?; + + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CREADR, + &state.its_creadr, + true, + )?; + + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CWRITER, + &state.its_cwriter, + true, + )?; + + for i in 0..8 { + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_BASER + i * 8, + &state.its_baser[i as usize], + true, + )?; + } + + // Restore ITS tables + gicv3_its_tables_access(self.its_device().unwrap(), false)?; + + gicv3_its_attr_access( + self.its_device().unwrap(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CTLR, + &state.its_ctlr, + true, + )?; + + Ok(()) + } - /// Trait for GIC devices. - pub trait KvmGicDevice: Send + Sync + GicDevice { /// Returns the GIC version of the device - fn version() -> u32; + fn version() -> u32 { + kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3 + } /// Create the GIC device object fn create_device( device: Arc, vcpu_count: u64, - ) -> Box; + ) -> Box { + Box::new(KvmGicV3Its { + device, + its_device: None, + gicr_typers: vec![0; vcpu_count.try_into().unwrap()], + gic_properties: [ + KvmGicV3Its::get_dist_addr(), + KvmGicV3Its::get_dist_size(), + KvmGicV3Its::get_redists_addr(vcpu_count), + KvmGicV3Its::get_redists_size(vcpu_count), + ], + msi_properties: [ + KvmGicV3Its::get_msi_addr(vcpu_count), + KvmGicV3Its::get_msi_size(), + ], + vcpu_count, + }) + } /// Setup the device-specific attributes fn init_device_attributes( vm: &Arc, gic_device: &mut dyn GicDevice, - ) -> Result<()>; + ) -> crate::aarch64::gic::Result<()> { + // GicV3 part attributes + /* Setting up the distributor attribute. + We are placing the GIC below 1GB so we need to substract the size of the distributor. + */ + Self::set_device_attribute( + gic_device.device(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_DIST), + &KvmGicV3Its::get_dist_addr() as *const u64 as u64, + 0, + )?; + + /* Setting up the redistributors' attribute. + We are calculating here the start of the redistributors address. We have one per CPU. + */ + Self::set_device_attribute( + gic_device.device(), + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_V3_ADDR_TYPE_REDIST), + &KvmGicV3Its::get_redists_addr(gic_device.vcpu_count()) as *const u64 as u64, + 0, + )?; + + // ITS part attributes + let mut its_device = kvm_bindings::kvm_create_device { + type_: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_ITS, + fd: 0, + flags: 0, + }; + + let its_fd = vm + .create_device(&mut its_device) + .map_err(crate::aarch64::gic::Error::CreateGic)?; + + Self::set_device_attribute( + &its_fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ADDR, + u64::from(kvm_bindings::KVM_VGIC_ITS_ADDR_TYPE), + &KvmGicV3Its::get_msi_addr(gic_device.vcpu_count()) as *const u64 as u64, + 0, + )?; + + Self::set_device_attribute( + &its_fd, + kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL, + u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_CTRL_INIT), + 0, + 0, + )?; + + gic_device.set_its_device(Some(its_fd)); + + Ok(()) + } /// Initialize a GIC device fn init_device(vm: &Arc) -> Result> { @@ -131,27 +488,6 @@ pub mod kvm { Ok(()) } - /// Get a GIC device attribute - fn get_device_attribute( - device: &Arc, - group: u32, - attr: u64, - addr: u64, - flags: u32, - ) -> Result<()> { - let mut attr = kvm_bindings::kvm_device_attr { - flags, - group, - attr, - addr, - }; - device - .get_device_attr(&mut attr) - .map_err(super::Error::GetDeviceAttribute)?; - - Ok(()) - } - /// Finalize the setup of a GIC device fn finalize_device(gic_device: &dyn GicDevice) -> Result<()> { /* We need to tell the kernel how many irqs to support with this vgic. @@ -216,4 +552,95 @@ pub mod kvm { gic.set_device_attr(&init_gic_attr) .map_err(super::Error::SetDeviceAttribute) } + + impl GicDevice for KvmGicV3Its { + fn device(&self) -> &Arc { + &self.device + } + + fn its_device(&self) -> Option<&Arc> { + self.its_device.as_ref() + } + + fn fdt_compatibility(&self) -> &str { + "arm,gic-v3" + } + + fn msi_compatible(&self) -> bool { + true + } + + fn msi_compatibility(&self) -> &str { + "arm,gic-v3-its" + } + + fn fdt_maint_irq(&self) -> u32 { + KvmGicV3Its::ARCH_GIC_V3_MAINT_IRQ + } + + fn msi_properties(&self) -> &[u64] { + &self.msi_properties + } + + fn device_properties(&self) -> &[u64] { + &self.gic_properties + } + + fn vcpu_count(&self) -> u64 { + self.vcpu_count + } + + fn set_its_device(&mut self, its_device: Option>) { + self.its_device = its_device; + } + + fn set_gicr_typers(&mut self, vcpu_states: &[CpuState]) { + let gicr_typers = construct_gicr_typers(vcpu_states); + self.gicr_typers = gicr_typers; + } + + fn as_any_concrete_mut(&mut self) -> &mut dyn Any { + self + } + } + + pub const GIC_V3_ITS_SNAPSHOT_ID: &str = "gic-v3-its"; + impl Snapshottable for KvmGicV3Its { + fn id(&self) -> String { + GIC_V3_ITS_SNAPSHOT_ID.to_string() + } + + fn snapshot(&mut self) -> std::result::Result { + let gicr_typers = self.gicr_typers.clone(); + Snapshot::new_from_state(&self.id(), &self.state(&gicr_typers).unwrap()) + } + + fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> { + let gicr_typers = self.gicr_typers.clone(); + self.set_state(&gicr_typers, &snapshot.to_state(&self.id())?) + .map_err(|e| { + MigratableError::Restore(anyhow!("Could not restore GICv3ITS state {:?}", e)) + }) + } + } + + impl Pausable for KvmGicV3Its { + fn pause(&mut self) -> std::result::Result<(), MigratableError> { + // Flush redistributors pending tables to guest RAM. + save_pending_tables(self.device()).map_err(|e| { + MigratableError::Pause(anyhow!( + "Could not save GICv3ITS GIC pending tables {:?}", + e + )) + })?; + // Flush ITS tables to guest RAM. + gicv3_its_tables_access(self.its_device().unwrap(), true).map_err(|e| { + MigratableError::Pause(anyhow!("Could not save GICv3ITS ITS tables {:?}", e)) + })?; + + Ok(()) + } + } + impl Transportable for KvmGicV3Its {} + impl Migratable for KvmGicV3Its {} } diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index d781cfda8..aa35d8295 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -30,7 +30,7 @@ use crate::{device_node, DEVICE_MANAGER_SNAPSHOT_ID}; use acpi_tables::{aml, aml::Aml}; use anyhow::anyhow; #[cfg(target_arch = "aarch64")] -use arch::aarch64::gic::gicv3_its::kvm::KvmGicV3Its; +use arch::aarch64::gic::kvm::KvmGicV3Its; use arch::layout; #[cfg(target_arch = "x86_64")] use arch::layout::{APIC_START, IOAPIC_SIZE, IOAPIC_START}; diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 8b6f4f271..122ae5dc8 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -32,9 +32,9 @@ use crate::{ }; use anyhow::anyhow; #[cfg(target_arch = "aarch64")] -use arch::aarch64::gic::gicv3_its::kvm::{KvmGicV3Its, GIC_V3_ITS_SNAPSHOT_ID}; -#[cfg(target_arch = "aarch64")] use arch::aarch64::gic::kvm::create_gic; +#[cfg(target_arch = "aarch64")] +use arch::aarch64::gic::kvm::{KvmGicV3Its, GIC_V3_ITS_SNAPSHOT_ID}; use arch::get_host_cpu_phys_bits; #[cfg(target_arch = "x86_64")] use arch::layout::{KVM_IDENTITY_MAP_START, KVM_TSS_START};