mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-08-21 15:31:15 +00:00
arch: AArch64: Porting GIC icc_regs implementation
This commit ports the implementation of GIC ICC registers from Firecracker. Signed-off-by: Henry Wang <Henry.Wang@arm.com>
This commit is contained in:
parent
bfde6977c8
commit
09d53aad11
196
arch/src/aarch64/gic/icc_regs.rs
Normal file
196
arch/src/aarch64/gic/icc_regs.rs
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
// 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_CPU_SYSREGS, KVM_REG_ARM64_SYSREG_CRM_MASK,
|
||||||
|
KVM_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_SHIFT,
|
||||||
|
KVM_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP1_MASK,
|
||||||
|
KVM_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_SHIFT,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
const ICC_CTLR_EL1_PRIBITS_SHIFT: u32 = 8;
|
||||||
|
const ICC_CTLR_EL1_PRIBITS_MASK: u32 = 7 << ICC_CTLR_EL1_PRIBITS_SHIFT;
|
||||||
|
|
||||||
|
macro_rules! arm64_vgic_sys_reg {
|
||||||
|
($name: tt, $op0: tt, $op1: tt, $crn: tt, $crm: tt, $op2: expr) => {
|
||||||
|
const $name: u64 = ((($op0 as u64) << KVM_REG_ARM64_SYSREG_OP0_SHIFT)
|
||||||
|
& KVM_REG_ARM64_SYSREG_OP0_MASK as u64)
|
||||||
|
| ((($op1 as u64) << KVM_REG_ARM64_SYSREG_OP1_SHIFT)
|
||||||
|
& KVM_REG_ARM64_SYSREG_OP1_MASK as u64)
|
||||||
|
| ((($crn as u64) << KVM_REG_ARM64_SYSREG_CRN_SHIFT)
|
||||||
|
& KVM_REG_ARM64_SYSREG_CRN_MASK as u64)
|
||||||
|
| ((($crm as u64) << KVM_REG_ARM64_SYSREG_CRM_SHIFT)
|
||||||
|
& KVM_REG_ARM64_SYSREG_CRM_MASK as u64)
|
||||||
|
| ((($op2 as u64) << KVM_REG_ARM64_SYSREG_OP2_SHIFT)
|
||||||
|
& KVM_REG_ARM64_SYSREG_OP2_MASK as u64);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! SYS_ICC_AP0Rn_EL1 {
|
||||||
|
($name: tt, $n: tt) => {
|
||||||
|
arm64_vgic_sys_reg!($name, 3, 0, 12, 8, (4 | $n));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! SYS_ICC_AP1Rn_EL1 {
|
||||||
|
($name: tt, $n: tt) => {
|
||||||
|
arm64_vgic_sys_reg!($name, 3, 0, 12, 9, $n);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
arm64_vgic_sys_reg!(SYS_ICC_SRE_EL1, 3, 0, 12, 12, 5);
|
||||||
|
arm64_vgic_sys_reg!(SYS_ICC_CTLR_EL1, 3, 0, 12, 12, 4);
|
||||||
|
arm64_vgic_sys_reg!(SYS_ICC_IGRPEN0_EL1, 3, 0, 12, 12, 6);
|
||||||
|
arm64_vgic_sys_reg!(SYS_ICC_IGRPEN1_EL1, 3, 0, 12, 12, 7);
|
||||||
|
arm64_vgic_sys_reg!(SYS_ICC_PMR_EL1, 3, 0, 4, 6, 0);
|
||||||
|
arm64_vgic_sys_reg!(SYS_ICC_BPR0_EL1, 3, 0, 12, 8, 3);
|
||||||
|
arm64_vgic_sys_reg!(SYS_ICC_BPR1_EL1, 3, 0, 12, 12, 3);
|
||||||
|
SYS_ICC_AP0Rn_EL1!(SYS_ICC_AP0R0_EL1, 0);
|
||||||
|
SYS_ICC_AP0Rn_EL1!(SYS_ICC_AP0R1_EL1, 1);
|
||||||
|
SYS_ICC_AP0Rn_EL1!(SYS_ICC_AP0R2_EL1, 2);
|
||||||
|
SYS_ICC_AP0Rn_EL1!(SYS_ICC_AP0R3_EL1, 3);
|
||||||
|
SYS_ICC_AP1Rn_EL1!(SYS_ICC_AP1R0_EL1, 0);
|
||||||
|
SYS_ICC_AP1Rn_EL1!(SYS_ICC_AP1R1_EL1, 1);
|
||||||
|
SYS_ICC_AP1Rn_EL1!(SYS_ICC_AP1R2_EL1, 2);
|
||||||
|
SYS_ICC_AP1Rn_EL1!(SYS_ICC_AP1R3_EL1, 3);
|
||||||
|
|
||||||
|
static VGIC_ICC_REGS: &'static [u64] = &[
|
||||||
|
SYS_ICC_SRE_EL1,
|
||||||
|
SYS_ICC_CTLR_EL1,
|
||||||
|
SYS_ICC_IGRPEN0_EL1,
|
||||||
|
SYS_ICC_IGRPEN1_EL1,
|
||||||
|
SYS_ICC_PMR_EL1,
|
||||||
|
SYS_ICC_BPR0_EL1,
|
||||||
|
SYS_ICC_BPR1_EL1,
|
||||||
|
SYS_ICC_AP0R0_EL1,
|
||||||
|
SYS_ICC_AP0R1_EL1,
|
||||||
|
SYS_ICC_AP0R2_EL1,
|
||||||
|
SYS_ICC_AP0R3_EL1,
|
||||||
|
SYS_ICC_AP1R0_EL1,
|
||||||
|
SYS_ICC_AP1R1_EL1,
|
||||||
|
SYS_ICC_AP1R2_EL1,
|
||||||
|
SYS_ICC_AP1R3_EL1,
|
||||||
|
];
|
||||||
|
|
||||||
|
fn icc_attr_access(
|
||||||
|
gic: &Arc<dyn hypervisor::Device>,
|
||||||
|
offset: u64,
|
||||||
|
typer: u64,
|
||||||
|
val: &u32,
|
||||||
|
set: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut gic_icc_attr = kvm_device_attr {
|
||||||
|
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||||
|
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // 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_icc_attr)
|
||||||
|
.map_err(Error::SetDeviceAttribute)?;
|
||||||
|
} else {
|
||||||
|
gic.get_device_attr(&mut gic_icc_attr)
|
||||||
|
.map_err(Error::GetDeviceAttribute)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get ICC registers.
|
||||||
|
pub fn get_icc_regs(gic: &Arc<dyn hypervisor::Device>, gicr_typer: &[u64]) -> Result<Vec<u32>> {
|
||||||
|
let mut state: Vec<u32> = Vec::new();
|
||||||
|
// We need this for the ICC_AP<m>R<n>_EL1 registers.
|
||||||
|
let mut num_priority_bits = 0;
|
||||||
|
|
||||||
|
for ix in gicr_typer {
|
||||||
|
let i = *ix;
|
||||||
|
for icc_offset in VGIC_ICC_REGS {
|
||||||
|
let val = 0;
|
||||||
|
if *icc_offset == SYS_ICC_CTLR_EL1 {
|
||||||
|
// calculate priority bits by reading the ctrl_el1 register.
|
||||||
|
icc_attr_access(gic, *icc_offset, i, &val, false)?;
|
||||||
|
// The priority bits are found in the ICC_CTLR_EL1 register (bits from 10:8).
|
||||||
|
// See page 194 from https://static.docs.arm.com/ihi0069/c/IHI0069C_gic_
|
||||||
|
// architecture_specification.pdf.
|
||||||
|
// Citation:
|
||||||
|
// "Priority bits. Read-only and writes are ignored. The number of priority bits
|
||||||
|
// implemented, minus one."
|
||||||
|
num_priority_bits =
|
||||||
|
((val & ICC_CTLR_EL1_PRIBITS_MASK) >> ICC_CTLR_EL1_PRIBITS_SHIFT) + 1;
|
||||||
|
state.push(val);
|
||||||
|
}
|
||||||
|
// As per ARMv8 documentation: https://static.docs.arm.com/ihi0069/c/IHI0069C_
|
||||||
|
// gic_architecture_specification.pdf
|
||||||
|
// page 178,
|
||||||
|
// ICC_AP0R1_EL1 is only implemented in implementations that support 6 or more bits of
|
||||||
|
// priority.
|
||||||
|
// ICC_AP0R2_EL1 and ICC_AP0R3_EL1 are only implemented in implementations that support
|
||||||
|
// 7 bits of priority.
|
||||||
|
else if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
|
||||||
|
if num_priority_bits >= 6 {
|
||||||
|
icc_attr_access(gic, *icc_offset, i, &val, false)?;
|
||||||
|
state.push(val);
|
||||||
|
}
|
||||||
|
} else if *icc_offset == SYS_ICC_AP0R2_EL1
|
||||||
|
|| *icc_offset == SYS_ICC_AP0R3_EL1
|
||||||
|
|| *icc_offset == SYS_ICC_AP1R2_EL1
|
||||||
|
|| *icc_offset == SYS_ICC_AP1R3_EL1
|
||||||
|
{
|
||||||
|
if num_priority_bits == 7 {
|
||||||
|
icc_attr_access(gic, *icc_offset, i, &val, false)?;
|
||||||
|
state.push(val);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
icc_attr_access(gic, *icc_offset, i, &val, false)?;
|
||||||
|
state.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set ICC registers.
|
||||||
|
pub fn set_icc_regs(
|
||||||
|
gic: &Arc<dyn hypervisor::Device>,
|
||||||
|
gicr_typer: &[u64],
|
||||||
|
state: &[u32],
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut num_priority_bits = 0;
|
||||||
|
let mut idx = 0;
|
||||||
|
for ix in gicr_typer {
|
||||||
|
let i = *ix;
|
||||||
|
for icc_offset in VGIC_ICC_REGS {
|
||||||
|
if *icc_offset == SYS_ICC_CTLR_EL1 {
|
||||||
|
let ctrl_el1 = state[idx];
|
||||||
|
num_priority_bits =
|
||||||
|
((ctrl_el1 & ICC_CTLR_EL1_PRIBITS_MASK) >> ICC_CTLR_EL1_PRIBITS_SHIFT) + 1;
|
||||||
|
}
|
||||||
|
if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
|
||||||
|
if num_priority_bits >= 6 {
|
||||||
|
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if *icc_offset == SYS_ICC_AP0R2_EL1
|
||||||
|
|| *icc_offset == SYS_ICC_AP0R3_EL1
|
||||||
|
|| *icc_offset == SYS_ICC_AP1R2_EL1
|
||||||
|
|| *icc_offset == SYS_ICC_AP1R3_EL1
|
||||||
|
{
|
||||||
|
if num_priority_bits == 7 {
|
||||||
|
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -5,9 +5,11 @@ pub mod dist_regs;
|
|||||||
mod gicv2;
|
mod gicv2;
|
||||||
mod gicv3;
|
mod gicv3;
|
||||||
mod gicv3_its;
|
mod gicv3_its;
|
||||||
|
pub mod icc_regs;
|
||||||
pub mod redist_regs;
|
pub mod redist_regs;
|
||||||
|
|
||||||
pub use self::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
pub use self::dist_regs::{get_dist_regs, read_ctlr, set_dist_regs, write_ctlr};
|
||||||
|
pub use self::icc_regs::{get_icc_regs, set_icc_regs};
|
||||||
pub use self::redist_regs::{get_redist_regs, set_redist_regs};
|
pub use self::redist_regs::{get_redist_regs, set_redist_regs};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
Loading…
Reference in New Issue
Block a user