diff --git a/arch/src/aarch64/gic/mod.rs b/arch/src/aarch64/gic/mod.rs index a718c6254..39af6c71c 100644 --- a/arch/src/aarch64/gic/mod.rs +++ b/arch/src/aarch64/gic/mod.rs @@ -5,8 +5,10 @@ pub mod dist_regs; mod gicv2; mod gicv3; mod gicv3_its; +pub mod redist_regs; pub use self::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr}; +pub use self::redist_regs::{get_redist_regs, set_redist_regs}; use std::result; use std::sync::Arc; diff --git a/arch/src/aarch64/gic/redist_regs.rs b/arch/src/aarch64/gic/redist_regs.rs new file mode 100644 index 000000000..c7d7a27e8 --- /dev/null +++ b/arch/src/aarch64/gic/redist_regs.rs @@ -0,0 +1,179 @@ +// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use super::{Error, Result}; +use hypervisor::kvm::kvm_bindings::{kvm_device_attr, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS}; +use std::sync::Arc; + +// Relevant redistributor registers that we want to save/restore. +const GICR_CTLR: u32 = 0x0000; +const GICR_STATUSR: u32 = 0x0010; +const GICR_WAKER: u32 = 0x0014; +const GICR_PROPBASER: u32 = 0x0070; +const GICR_PENDBASER: u32 = 0x0078; + +/* SGI and PPI Redistributor registers, offsets from RD_base */ +/* + * Redistributor frame offsets from RD_base which is actually SZ_ + */ +const GICR_SGI_OFFSET: u32 = 0x0001_0000; +const GICR_IGROUPR0: u32 = GICR_SGI_OFFSET + 0x0080; +const GICR_ICENABLER0: u32 = GICR_SGI_OFFSET + 0x0180; +const GICR_ISENABLER0: u32 = GICR_SGI_OFFSET + 0x0100; +const GICR_ISPENDR0: u32 = GICR_SGI_OFFSET + 0x0200; +const GICR_ICPENDR0: u32 = GICR_SGI_OFFSET + 0x0280; +const GICR_ISACTIVER0: u32 = GICR_SGI_OFFSET + 0x0300; +const GICR_ICACTIVER0: u32 = GICR_SGI_OFFSET + 0x0380; +const GICR_IPRIORITYR0: u32 = GICR_SGI_OFFSET + 0x0400; +const GICR_ICFGR0: u32 = GICR_SGI_OFFSET + 0x0C00; + +const KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT: u32 = 32; +const KVM_DEV_ARM_VGIC_V3_MPIDR_MASK: u64 = 0xffffffff << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT as u64; + +/// This is how we represent the registers of a distributor. +/// It is relrvant their offset from the base address of the +/// distributor. +/// Each register has a different number +/// of bits_per_irq and is therefore variable length. +/// First 32 interrupts (0-32) are private to each CPU (SGIs and PPIs). +/// and so we save the first irq to identify between the type of the interrupt +/// that the respective register deals with. +struct RdistReg { + /// Offset from distributor address. + base: u32, + /// Length of the register. + length: u8, +} + +// All or at least the registers we are interested in are 32 bit, so +// we use a constant for size(u32). +const REG_SIZE: u8 = 4; + +// Creates a vgic redistributor register. +macro_rules! VGIC_RDIST_REG { + ($base:expr, $len:expr) => { + RdistReg { + base: $base, + length: $len, + } + }; +} + +// List with relevant distributor registers that we will be restoring. +static VGIC_RDIST_REGS: &'static [RdistReg] = &[ + VGIC_RDIST_REG!(GICR_CTLR, 4), + VGIC_RDIST_REG!(GICR_STATUSR, 4), + VGIC_RDIST_REG!(GICR_WAKER, 4), + VGIC_RDIST_REG!(GICR_PROPBASER, 8), + VGIC_RDIST_REG!(GICR_PENDBASER, 8), +]; + +// List with relevant distributor registers that we will be restoring. +static VGIC_SGI_REGS: &'static [RdistReg] = &[ + VGIC_RDIST_REG!(GICR_IGROUPR0, 4), + VGIC_RDIST_REG!(GICR_ICENABLER0, 4), + VGIC_RDIST_REG!(GICR_ISENABLER0, 4), + VGIC_RDIST_REG!(GICR_ICFGR0, 8), + VGIC_RDIST_REG!(GICR_ICPENDR0, 4), + VGIC_RDIST_REG!(GICR_ISPENDR0, 4), + VGIC_RDIST_REG!(GICR_ICACTIVER0, 4), + VGIC_RDIST_REG!(GICR_ISACTIVER0, 4), + VGIC_RDIST_REG!(GICR_IPRIORITYR0, 32), +]; + +fn redist_attr_access( + gic: &Arc, + offset: u32, + typer: u64, + val: &u32, + set: bool, +) -> Result<()> { + let mut gic_dist_attr = kvm_device_attr { + group: KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, + attr: (typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | (offset as u64), // this needs the mpidr + addr: val as *const u32 as u64, + flags: 0, + }; + if set { + #[allow(clippy::unnecessary_mut_passed)] + gic.set_device_attr(&mut gic_dist_attr) + .map_err(Error::SetDeviceAttribute)?; + } else { + gic.get_device_attr(&mut gic_dist_attr) + .map_err(Error::GetDeviceAttribute)?; + } + Ok(()) +} + +fn access_redists_aux( + gic: &Arc, + gicr_typer: &[u64], + state: &mut Vec, + reg_list: &'static [RdistReg], + idx: &mut usize, + set: bool, +) -> Result<()> { + for i in gicr_typer { + for rdreg in reg_list { + let mut base = rdreg.base; + let end = base + rdreg.length as u32; + + while base < end { + let mut val = 0; + if set { + val = state[*idx]; + redist_attr_access(gic, base, *i, &val, true)?; + *idx += 1; + } else { + redist_attr_access(gic, base, *i, &val, false)?; + state.push(val); + } + base += REG_SIZE as u32; + } + } + } + Ok(()) +} + +/// Get redistributor registers. +pub fn get_redist_regs(gic: &Arc, gicr_typer: &[u64]) -> Result> { + let mut state = Vec::new(); + let mut idx: usize = 0; + access_redists_aux( + gic, + &gicr_typer, + &mut state, + VGIC_RDIST_REGS, + &mut idx, + false, + )?; + + access_redists_aux(gic, &gicr_typer, &mut state, VGIC_SGI_REGS, &mut idx, false)?; + Ok(state) +} + +/// Set redistributor registers. +pub fn set_redist_regs( + gic: &Arc, + gicr_typer: &[u64], + state: &[u32], +) -> Result<()> { + let mut idx: usize = 0; + let mut mut_state = state.to_owned(); + access_redists_aux( + gic, + gicr_typer, + &mut mut_state, + VGIC_RDIST_REGS, + &mut idx, + true, + )?; + access_redists_aux( + gic, + gicr_typer, + &mut mut_state, + VGIC_SGI_REGS, + &mut idx, + true, + ) +}