Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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.

13 changes: 13 additions & 0 deletions kernel/arm_boards/src/mpidr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,16 @@ impl DefinedMpidrValue {
Self(aff3 | aff2 | aff1 | aff0)
}
}

/// Tries to find this MPIDR value among those known for this board.
///
/// Returns None if no CPU has this MPIDR value.
pub fn find_mpidr(mpidr_value: u64) -> Option<DefinedMpidrValue> {
for def_mpidr in crate::BOARD_CONFIG.cpu_ids {
if def_mpidr.value() == mpidr_value {
return Some(def_mpidr)
}
}

None
}
26 changes: 15 additions & 11 deletions kernel/cpu/src/aarch64.rs
Original file line number Diff line number Diff line change
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
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) };
}
96 changes: 42 additions & 54 deletions kernel/gic/src/gic/dist_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@
//! - 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 arm_boards::mpidr::find_mpidr;
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 +62,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 +108,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,82 +138,70 @@ 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) -> Option<SpiDestination> {
assert!(int >= 32, "interrupts number below 32 (SGIs & PPIs) don't have a target CPU");
if !self.affinity_routing() {
Some(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 {
return Some(SpiDestination::Specific(find_mpidr(i)?.into()));
}
}

let list = TargetList::from_bits_truncate(flags as u8);
TargetCpu::GICv2TargetList(list)
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
SpiDestination::AnyCpuAvailable
} else {
let aff3 = (reg >> 8) & 0xff000000;
let aff012 = reg & 0xffffff;
TargetCpu::Specific((aff3 | aff012) as u32)
let mpidr = reg & 0xff00ffffff;
SpiDestination::Specific(find_mpidr(mpidr)?.into())
}
} else {
// If we're on gicv2 then affinity routing is off
// so we landed in the first block
unreachable!()
}
})
}

/// 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