arch: AArch64: implement save/restore for GICv3

This commit implements the save/restore for GICv3.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
This commit is contained in:
Henry Wang 2020-09-04 09:52:05 +08:00 committed by Rob Bradford
parent 7ddcad1d8b
commit 39c9583b48
3 changed files with 148 additions and 3 deletions

View File

@ -13,7 +13,11 @@ byteorder = "1.3.4"
hypervisor = { path = "../hypervisor" } hypervisor = { path = "../hypervisor" }
libc = "0.2.77" libc = "0.2.77"
log = "0.4.11" 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-memory = { version = "0.2.1", features = ["backend-mmap"] }
vm-migration = { path = "../vm-migration" }
acpi_tables = { path = "../acpi_tables", optional = true } acpi_tables = { path = "../acpi_tables", optional = true }
arch_gen = { path = "../arch_gen" } arch_gen = { path = "../arch_gen" }

View File

@ -2,13 +2,45 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub mod kvm { pub mod kvm {
use crate::aarch64::gic::kvm::KvmGICDevice; use crate::aarch64::gic::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
use crate::aarch64::gic::{Error, GICDevice}; 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 crate::layout;
use anyhow::anyhow;
use hypervisor::kvm::kvm_bindings; use hypervisor::kvm::kvm_bindings;
use std::convert::TryInto; use std::convert::TryInto;
use std::sync::Arc; use std::sync::Arc;
use std::{boxed::Box, result}; 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<T> = result::Result<T, Error>; type Result<T> = result::Result<T, Error>;
pub struct KvmGICv3 { pub struct KvmGICv3 {
@ -25,6 +57,15 @@ pub mod kvm {
vcpu_count: u64, vcpu_count: u64,
} }
#[derive(Serialize, Deserialize)]
pub struct Gicv3State {
dist: Vec<u32>,
rdist: Vec<u32>,
icc: Vec<u32>,
// special register that enables interrupts and affinity routing
gicd_ctlr: u32,
}
impl KvmGICv3 { impl KvmGICv3 {
// Unfortunately bindgen omits defines that are based on other defines. // Unfortunately bindgen omits defines that are based on other defines.
// See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel. // 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 { pub fn get_redists_size(vcpu_count: u64) -> u64 {
vcpu_count * KvmGICv3::KVM_VGIC_V3_REDIST_SIZE vcpu_count * KvmGICv3::KVM_VGIC_V3_REDIST_SIZE
} }
/// Save the state of GIC.
fn state(&self, gicr_typers: &[u64]) -> Result<Gicv3State> {
// 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 { impl GICDevice for KvmGICv3 {
@ -107,7 +190,7 @@ pub mod kvm {
fn init_device_attributes( fn init_device_attributes(
_vm: &Arc<dyn hypervisor::Vm>, _vm: &Arc<dyn hypervisor::Vm>,
gic_device: &dyn GICDevice, gic_device: &dyn GICDevice,
) -> Result<()> { ) -> crate::aarch64::gic::Result<()> {
/* Setting up the distributor attribute. /* Setting up the distributor attribute.
We are placing the GIC below 1GB so we need to substract the size of the distributor. 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(()) 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<Snapshot, MigratableError> {
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 {}
} }

View File

@ -14,6 +14,7 @@
clippy::cast_ptr_alignment clippy::cast_ptr_alignment
)] )]
extern crate anyhow;
extern crate byteorder; extern crate byteorder;
extern crate hypervisor; extern crate hypervisor;
extern crate libc; extern crate libc;
@ -23,7 +24,13 @@ extern crate log;
extern crate acpi_tables; extern crate acpi_tables;
extern crate arch_gen; extern crate arch_gen;
extern crate linux_loader; extern crate linux_loader;
extern crate serde;
extern crate vm_memory; 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::fmt;
use std::result; use std::result;