diff --git a/arch/Cargo.toml b/arch/Cargo.toml index 848320ac4..4cde2ae3c 100644 --- a/arch/Cargo.toml +++ b/arch/Cargo.toml @@ -13,7 +13,11 @@ byteorder = "1.3.4" hypervisor = { path = "../hypervisor" } libc = "0.2.77" log = "0.4.11" +serde = {version = ">=1.0.27", features = ["rc"] } +serde_derive = ">=1.0.27" +serde_json = ">=1.0.9" vm-memory = { version = "0.2.1", features = ["backend-mmap"] } +vm-migration = { path = "../vm-migration" } acpi_tables = { path = "../acpi_tables", optional = true } arch_gen = { path = "../arch_gen" } diff --git a/arch/src/aarch64/gic/gicv3.rs b/arch/src/aarch64/gic/gicv3.rs index 280070a93..4f1a9af80 100644 --- a/arch/src/aarch64/gic/gicv3.rs +++ b/arch/src/aarch64/gic/gicv3.rs @@ -2,13 +2,45 @@ // SPDX-License-Identifier: Apache-2.0 pub mod kvm { - use crate::aarch64::gic::kvm::KvmGICDevice; - use crate::aarch64::gic::{Error, GICDevice}; + 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::kvm::{save_pending_tables, KvmGICDevice}; + use crate::aarch64::gic::redist_regs::{get_redist_regs, set_redist_regs}; + use crate::aarch64::gic::GICDevice; use crate::layout; + use anyhow::anyhow; use hypervisor::kvm::kvm_bindings; use std::convert::TryInto; use std::sync::Arc; use std::{boxed::Box, result}; + use vm_migration::{ + Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable, + Transportable, + }; + + /// Errors thrown while saving/restoring the GICv3. + #[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), + } + type Result = result::Result; pub struct KvmGICv3 { @@ -25,6 +57,15 @@ pub mod kvm { vcpu_count: u64, } + #[derive(Serialize, Deserialize)] + pub struct Gicv3State { + dist: Vec, + rdist: Vec, + icc: Vec, + // special register that enables interrupts and affinity routing + gicd_ctlr: u32, + } + impl KvmGICv3 { // Unfortunately bindgen omits defines that are based on other defines. // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. @@ -54,6 +95,48 @@ pub mod kvm { pub fn get_redists_size(vcpu_count: u64) -> u64 { vcpu_count * KvmGICv3::KVM_VGIC_V3_REDIST_SIZE } + + /// Save the state of GIC. + fn state(&self, gicr_typers: &[u64]) -> Result { + // Flush redistributors pending tables to guest RAM. + save_pending_tables(&self.device()).map_err(Error::SavePendingTables)?; + + 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)?; + + Ok(Gicv3State { + dist: dist_state, + rdist: rdist_state, + icc: icc_state, + gicd_ctlr, + }) + } + + /// Restore the state of GIC. + fn set_state(&mut self, gicr_typers: &[u64], state: &Gicv3State) -> 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)?; + + Ok(()) + } } impl GICDevice for KvmGICv3 { @@ -107,7 +190,7 @@ pub mod kvm { fn init_device_attributes( _vm: &Arc, gic_device: &dyn GICDevice, - ) -> Result<()> { + ) -> crate::aarch64::gic::Result<()> { /* Setting up the distributor attribute. We are placing the GIC below 1GB so we need to substract the size of the distributor. */ @@ -133,4 +216,55 @@ pub mod kvm { Ok(()) } } + + const GIC_V3_SNAPSHOT_ID: &str = "gic-v3"; + impl Snapshottable for KvmGICv3 { + fn id(&self) -> String { + GIC_V3_SNAPSHOT_ID.to_string() + } + + fn snapshot(&mut self) -> std::result::Result { + let gicr_typers = self.gicr_typers.clone(); + let snapshot = serde_json::to_vec(&self.state(&gicr_typers).unwrap()) + .map_err(|e| MigratableError::Snapshot(e.into()))?; + + let mut gic_v3_snapshot = Snapshot::new(self.id().as_str()); + gic_v3_snapshot.add_data_section(SnapshotDataSection { + id: format!("{}-section", self.id()), + snapshot, + }); + + Ok(gic_v3_snapshot) + } + + fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> { + if let Some(gic_v3_section) = snapshot + .snapshot_data + .get(&format!("{}-section", self.id())) + { + let gic_v3_state = match serde_json::from_slice(&gic_v3_section.snapshot) { + Ok(state) => state, + Err(error) => { + return Err(MigratableError::Restore(anyhow!( + "Could not deserialize GICv3 {}", + error + ))) + } + }; + + let gicr_typers = self.gicr_typers.clone(); + return self.set_state(&gicr_typers, &gic_v3_state).map_err(|e| { + MigratableError::Restore(anyhow!("Could not restore GICv3 state {:?}", e)) + }); + } + + Err(MigratableError::Restore(anyhow!( + "Could not find GICv3 snapshot section" + ))) + } + } + + impl Pausable for KvmGICv3 {} + impl Transportable for KvmGICv3 {} + impl Migratable for KvmGICv3 {} } diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 2c6f7a417..486b68488 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -14,6 +14,7 @@ clippy::cast_ptr_alignment )] +extern crate anyhow; extern crate byteorder; extern crate hypervisor; extern crate libc; @@ -23,7 +24,13 @@ extern crate log; extern crate acpi_tables; extern crate arch_gen; extern crate linux_loader; +extern crate serde; extern crate vm_memory; +extern crate vm_migration; +#[cfg(target_arch = "aarch64")] +#[macro_use] +extern crate serde_derive; +extern crate serde_json; use std::fmt; use std::result;