Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 32 additions & 12 deletions kernel/cpu/src/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use derive_more::{Display, Binary, Octal, LowerHex, UpperHex};
use irq_safety::RwLockIrqSafe;
use core::fmt;
use alloc::vec::Vec;
use arm_boards::mpidr::DefinedMpidrValue;
use arm_boards::{mpidr::DefinedMpidrValue, BOARD_CONFIG};

use super::CpuId;

Expand Down Expand Up @@ -71,24 +71,28 @@ pub fn current_cpu() -> CpuId {
#[repr(transparent)]
pub struct MpidrValue(u64);

/// Affinity Levels and corresponding bit ranges
///
/// The associated integers are the locations (N..(N+8))
/// of the corresponding bits in an [`MpidrValue`].
#[derive(Copy, Clone, Debug)]
#[repr(u64)]
pub enum AffinityShift {
LevelZero = 0,
LevelOne = 8,
LevelTwo = 16,
LevelThree = 32,
}

impl MpidrValue {
/// Returns the inner raw value read from the `MPIDR_EL1` register.
pub fn value(self) -> u64 {
self.0
}

/// Reads an affinity `level` from this `MpidrValue`.
///
/// Panics if the given affinity level is not 0, 1, 2, or 3.
pub fn affinity(self, level: u8) -> u8 {
let shift = match level {
0 => 0,
1 => 8,
2 => 16,
3 => 32,
_ => panic!("Valid affinity levels are 0, 1, 2, 3"),
};
(self.0 >> shift) as u8
pub fn affinity(self, level: AffinityShift) -> u64 {
(self.0 >> (level as u64)) & (u8::MAX as u64)
}
}

Expand Down Expand Up @@ -124,6 +128,22 @@ impl From<MpidrValue> for CpuId {
}
}

impl TryFrom<u64> for MpidrValue {
type Error = &'static str;

/// Tries to find this MPIDR value in those defined by
/// `arm_boards::cpu_ids`. Fails if No CPU has this MPIDR value.
fn try_from(mpidr_value: u64) -> Result<Self, Self::Error> {
for def_mpidr in BOARD_CONFIG.cpu_ids {
if def_mpidr.value() == mpidr_value {
return Ok(def_mpidr.into())
}
}

Err("No CPU has this MPIDR value")
}
}

/// An equivalent to Option<CpuId>, which internally encodes None as
/// `u32::MAX`, which is an invalid CpuId (bits [4:7] of affinity level
/// 0 must always be cleared). This guarantees that it compiles down to
Expand Down
1 change: 0 additions & 1 deletion kernel/gic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ name = "gic"

[dependencies]
static_assertions = "1.1.0"
bitflags = "1.1.0"
zerocopy = "0.5.0"
log = "0.4.8"

Expand Down
70 changes: 32 additions & 38 deletions kernel/gic/src/gic/cpu_interface_gicv3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
//! - Generating software interrupts

use core::arch::asm;
use super::TargetCpu;
use super::IpiTargetCpu;
use super::Priority;
use super::InterruptNumber;

const SGIR_TARGET_ALL_OTHER_PE: usize = 1 << 40;
const IGRPEN_ENABLED: usize = 1;
const SGIR_TARGET_ALL_OTHER_PE: u64 = 1 << 40;
const IGRPEN_ENABLED: u64 = 1;

/// Enables routing of group 1 interrupts for the current CPU and configures
/// the end-of-interrupt mode
pub fn init() {
let mut icc_ctlr: usize;
let mut icc_ctlr: u64;

unsafe { asm!("mrs {}, ICC_CTLR_EL1", out(reg) icc_ctlr) };
// clear bit 1 (EOIMode) so that eoi signals both
Expand All @@ -40,7 +40,7 @@ pub fn init() {
/// until this CPU or another one is ready to handle
/// them
pub fn get_minimum_priority() -> Priority {
let mut reg_value: usize;
let mut reg_value: u64;
unsafe { asm!("mrs {}, ICC_PMR_EL1", out(reg) reg_value) };
u8::MAX - (reg_value as u8)
}
Expand All @@ -50,15 +50,15 @@ pub fn get_minimum_priority() -> Priority {
/// until this CPU or another one is ready to handle
/// them
pub fn set_minimum_priority(priority: Priority) {
let reg_value = (u8::MAX - priority) as usize;
let reg_value = (u8::MAX - priority) as u64;
unsafe { asm!("msr ICC_PMR_EL1, {}", in(reg) reg_value) };
}

/// Signals to the controller that the currently processed interrupt has
/// been fully handled, by zeroing the current priority level of this CPU.
/// This implies that the CPU is ready to process interrupts again.
pub fn end_of_interrupt(int: InterruptNumber) {
let reg_value = int as usize;
let reg_value = int as u64;
unsafe { asm!("msr ICC_EOIR1_EL1, {}", in(reg) reg_value) };
}

Expand All @@ -67,8 +67,8 @@ pub fn end_of_interrupt(int: InterruptNumber) {
/// the requested interrupt is being handled by
/// this CPU.
pub fn acknowledge_interrupt() -> (InterruptNumber, Priority) {
let int_num: usize;
let priority: usize;
let int_num: u64;
let priority: u64;

// Reading the interrupt number has the side effect
// of acknowledging the interrupt.
Expand All @@ -82,41 +82,35 @@ pub fn acknowledge_interrupt() -> (InterruptNumber, Priority) {
(int_num as InterruptNumber, priority as u8)
}

pub fn send_ipi(int_num: InterruptNumber, target: TargetCpu) {
pub fn send_ipi(int_num: InterruptNumber, target: IpiTargetCpu) {
let mut value = match target {
TargetCpu::Specific(cpu) => {
let cpu = cpu as usize;

// level 3 affinity is expected in cpu[24:31]
// we want it in bits [48:55]
let aff3 = (cpu & 0xff000000) << 24;

// level 2 affinity is expected in cpu[16:23]
// we want it in bits [32:39]
let aff2 = cpu & 0xff0000 << 16;

// level 1 affinity is expected in cpu[8:15]
// we want it in bits [16:23]
let aff1 = cpu & 0xff00 << 8;

// level 0 affinity is expected in cpu[0:7]
// we want it as a GICv2-style target list
let aff0 = cpu & 0xff;
let target_list = if aff0 >= 16 {
panic!("[GIC driver] cannot send an IPI to a core with Aff0 >= 16");
} else {
1 << aff0
IpiTargetCpu::Specific(cpu) => {
let mpidr: cpu::MpidrValue = cpu.into();

// level 3 affinity in bits [48:55]
let aff3 = mpidr.affinity(cpu::AffinityShift::LevelThree) << 48;

// level 2 affinity in bits [32:39]
let aff2 = mpidr.affinity(cpu::AffinityShift::LevelTwo) << 32;

// level 1 affinity in bits [16:23]
let aff1 = mpidr.affinity(cpu::AffinityShift::LevelOne) << 16;

// level 0 affinity as a GICv2-style target list
let aff0 = mpidr.affinity(cpu::AffinityShift::LevelZero);
let target_list = match aff0 >= 16 {
true => panic!("[GIC driver] cannot send an IPI to a core with Aff0 >= 16"),
false => 1 << aff0,
};

aff3 | aff2 | aff1 | target_list
},
// bit 31: Interrupt Routing Mode
// value of 1 to target any available cpu
TargetCpu::AnyCpuAvailable => SGIR_TARGET_ALL_OTHER_PE,
TargetCpu::GICv2TargetList(_) => {
panic!("Cannot use TargetCpu::GICv2TargetList with GICv3!");
IpiTargetCpu::AllOtherCpus => SGIR_TARGET_ALL_OTHER_PE,
IpiTargetCpu::GICv2TargetList(_) => {
panic!("Cannot use IpiTargetCpu::GICv2TargetList with GICv3!");
},
};

value |= (int_num as usize) << 24;
value |= (int_num as u64) << 24;
unsafe { asm!("msr ICC_SGI1R_EL1, {}", in(reg) value) };
}
94 changes: 42 additions & 52 deletions kernel/gic/src/gic/dist_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
//! - Generating software interrupts (GICv2 style)

use super::GicRegisters;
use super::TargetCpu;
use super::IpiTargetCpu;
use super::SpiDestination;
use super::InterruptNumber;
use super::Enabled;
use super::TargetList;

use cpu::MpidrValue;

mod offset {
use crate::{Offset32, Offset64};
pub(crate) const CTLR: Offset32 = Offset32::from_byte_offset(0x000);
Expand Down Expand Up @@ -58,12 +61,6 @@ const GROUP_1: u32 = 1;
// bit 15: which interrupt group to target
const SGIR_NSATT_GRP1: u32 = 1 << 15;

fn assert_cpu_bounds(target: &TargetCpu) {
if let TargetCpu::Specific(cpu) = target {
assert!(*cpu < 8, "affinity routing is disabled; cannot target a CPU with id >= 8");
}
}

/// Initializes the distributor by enabling forwarding
/// of group 1 interrupts and allowing the GIC to pick
/// a core that is asleep for "1 of N" interrupts.
Expand Down Expand Up @@ -110,13 +107,15 @@ pub fn enable_spi(registers: &mut GicRegisters, int: InterruptNumber, enabled: E
///
/// legacy / GICv2 method
/// int_num must be less than 16
pub fn send_ipi_gicv2(registers: &mut GicRegisters, int_num: u32, target: TargetCpu) {
assert_cpu_bounds(&target);
pub fn send_ipi_gicv2(registers: &mut GicRegisters, int_num: u32, target: IpiTargetCpu) {
if let IpiTargetCpu::Specific(cpu) = &target {
assert!(cpu.value() < 8, "affinity routing is disabled; cannot target a CPU with id >= 8");
}

let target_list = match target {
TargetCpu::Specific(cpu) => (1 << cpu) << 16,
TargetCpu::AnyCpuAvailable => SGIR_TARGET_ALL_OTHER_PE,
TargetCpu::GICv2TargetList(list) => (list.bits as u32) << 16,
IpiTargetCpu::Specific(cpu) => (1 << cpu.value()) << 16,
IpiTargetCpu::AllOtherCpus => SGIR_TARGET_ALL_OTHER_PE,
IpiTargetCpu::GICv2TargetList(list) => (list.0 as u32) << 16,
};

let value: u32 = int_num | target_list | SGIR_NSATT_GRP1;
Expand All @@ -138,40 +137,38 @@ impl super::ArmGic {
}
}

/// The GIC can be configured to route
/// Shared-Peripheral Interrupts (SPI) either
/// to a specific CPU or to any PE that is ready
/// to process them, i.e. not handling another
/// higher-priority interrupt.
pub fn get_spi_target(&self, int: InterruptNumber) -> TargetCpu {
/// Returns the destination of an SPI if it's valid, i.e. if it
/// points to existing CPU(s).
///
/// Note: If the destination is a `GICv2TargetList`, the validity
/// of that destination is not checked.
pub fn get_spi_target(&self, int: InterruptNumber) -> Result<SpiDestination, &'static str> {
assert!(int >= 32, "interrupts number below 32 (SGIs & PPIs) don't have a target CPU");
if !self.affinity_routing() {
let flags = self.distributor().read_array_volatile::<4>(offset::ITARGETSR, int);
if flags == 0xff {
return TargetCpu::AnyCpuAvailable;
}

for i in 0..8 {
let target = 1 << i;
if target & flags == target {
return TargetCpu::Specific(i);
if target & flags == flags {
let mpidr = i;
let cpu_id = MpidrValue::try_from(mpidr)?.into();
return Ok(SpiDestination::Specific(cpu_id));
}
}

let list = TargetList::from_bits_truncate(flags as u8);
TargetCpu::GICv2TargetList(list)
Ok(SpiDestination::GICv2TargetList(TargetList(flags as u8)).canonicalize())
} else if let Self::V3(v3) = self {
let reg = v3.dist_extended.read_volatile_64(offset::P6IROUTER);

// bit 31: Interrupt Routing Mode
// value of 1 to target any available cpu
// value of 0 to target a specific cpu
if reg & P6IROUTER_ANY_AVAILABLE_PE > 0 {
TargetCpu::AnyCpuAvailable
Ok(SpiDestination::AnyCpuAvailable)
} else {
let aff3 = (reg >> 8) & 0xff000000;
let aff012 = reg & 0xffffff;
TargetCpu::Specific((aff3 | aff012) as u32)
let mpidr = reg & 0xff00ffffff;
let cpu_id = MpidrValue::try_from(mpidr)?.into();
return Ok(SpiDestination::Specific(cpu_id));
}
} else {
// If we're on gicv2 then affinity routing is off
Expand All @@ -180,40 +177,33 @@ impl super::ArmGic {
}
}

/// The GIC can be configured to route
/// Shared-Peripheral Interrupts (SPI) either
/// to a specific CPU or to any PE that is ready
/// to process them, i.e. not handling another
/// higher-priority interrupt.
pub fn set_spi_target(&mut self, int: InterruptNumber, target: TargetCpu) {
/// Sets the destination of an SPI.
pub fn set_spi_target(&mut self, int: InterruptNumber, target: SpiDestination) {
assert!(int >= 32, "interrupts number below 32 (SGIs & PPIs) don't have a target CPU");
if !self.affinity_routing() {
assert_cpu_bounds(&target);
if let SpiDestination::Specific(cpu) = &target {
assert!(cpu.value() < 8, "affinity routing is disabled; cannot target a CPU with id >= 8");
}

let value = match target {
TargetCpu::Specific(cpu) => 1 << cpu,
TargetCpu::AnyCpuAvailable => 0xff,
TargetCpu::GICv2TargetList(list) => list.bits as u32,
SpiDestination::Specific(cpu) => 1 << cpu.value(),
SpiDestination::AnyCpuAvailable => {
let list = TargetList::new_all_cpus()
.expect("This is invalid: CpuId > 8 AND GICv2 interrupt controller");
list.0 as u32
},
SpiDestination::GICv2TargetList(list) => list.0 as u32,
};

self.distributor_mut().write_array_volatile::<4>(offset::ITARGETSR, int, value);
} else if let Self::V3(v3) = self {
let value = match target {
TargetCpu::Specific(cpu) => {
let cpu = cpu as u64;
// shift aff3 8 bits to the left
let aff3 = (cpu & 0xff000000) << 8;
// keep aff 0, 1 & 2 where they are
let aff012 = cpu & 0xffffff;
// leave bit 31 clear to indicate
// a specific target
aff3 | aff012
},
SpiDestination::Specific(cpu) => MpidrValue::from(cpu).value(),
// bit 31: Interrupt Routing Mode
// value of 1 to target any available cpu
TargetCpu::AnyCpuAvailable => P6IROUTER_ANY_AVAILABLE_PE,
TargetCpu::GICv2TargetList(_) => {
panic!("Cannot use TargetCpu::GICv2TargetList with GICv3!");
SpiDestination::AnyCpuAvailable => P6IROUTER_ANY_AVAILABLE_PE,
SpiDestination::GICv2TargetList(_) => {
panic!("Cannot use SpiDestination::GICv2TargetList with GICv3!");
},
};

Expand Down
Loading