From c19482a76f7ccf2e243500f020322520ddf1ea79 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Tue, 28 Mar 2023 15:30:19 +0200 Subject: [PATCH 01/25] Fixes and improvements in GIC driver --- Cargo.lock | 1 + kernel/cpu/src/aarch64.rs | 5 +- kernel/gic/Cargo.toml | 1 + kernel/gic/src/gic/cpu_interface_gicv3.rs | 41 ++++++------ kernel/gic/src/gic/dist_interface.rs | 81 +++++++++++------------ kernel/gic/src/gic/mod.rs | 34 +++++++--- 6 files changed, 90 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2ecde4fc..1264c5502 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1335,6 +1335,7 @@ name = "gic" version = "0.1.0" dependencies = [ "bitflags", + "cpu", "log", "memory", "static_assertions", diff --git a/kernel/cpu/src/aarch64.rs b/kernel/cpu/src/aarch64.rs index 9b63aa8ea..28fc73029 100644 --- a/kernel/cpu/src/aarch64.rs +++ b/kernel/cpu/src/aarch64.rs @@ -52,7 +52,7 @@ impl MpidrValue { /// 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 { + pub fn affinity(self, level: u8) -> u64 { let shift = match level { 0 => 0, 1 => 8, @@ -60,7 +60,8 @@ impl MpidrValue { 3 => 32, _ => panic!("Valid affinity levels are 0, 1, 2, 3"), }; - (self.0 >> shift) as u8 + + self.0 >> shift } /// Create an `MpidrValue` from its four affinity numbers diff --git a/kernel/gic/Cargo.toml b/kernel/gic/Cargo.toml index b524c862e..b9276b07b 100644 --- a/kernel/gic/Cargo.toml +++ b/kernel/gic/Cargo.toml @@ -12,3 +12,4 @@ zerocopy = "0.5.0" log = "0.4.8" memory = { path = "../memory" } +cpu = { path = "../cpu" } diff --git a/kernel/gic/src/gic/cpu_interface_gicv3.rs b/kernel/gic/src/gic/cpu_interface_gicv3.rs index 3de6a1f7b..c0ecabeb0 100644 --- a/kernel/gic/src/gic/cpu_interface_gicv3.rs +++ b/kernel/gic/src/gic/cpu_interface_gicv3.rs @@ -8,17 +8,18 @@ //! - Generating software interrupts use core::arch::asm; -use super::TargetCpu; +use super::IpiTargetCpu; use super::Priority; use super::InterruptNumber; +use cpu::MpidrValue; -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 @@ -40,7 +41,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) } @@ -50,7 +51,7 @@ 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) }; } @@ -58,7 +59,7 @@ pub fn set_minimum_priority(priority: Priority) { /// 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) }; } @@ -67,8 +68,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. @@ -82,26 +83,26 @@ 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; + IpiTargetCpu::Specific(cpu) => { + let mpidr: MpidrValue = cpu.into(); // level 3 affinity is expected in cpu[24:31] // we want it in bits [48:55] - let aff3 = (cpu & 0xff000000) << 24; + let aff3 = mpidr.affinity(3) << 48; // level 2 affinity is expected in cpu[16:23] // we want it in bits [32:39] - let aff2 = cpu & 0xff0000 << 16; + let aff2 = mpidr.affinity(2) << 32; // level 1 affinity is expected in cpu[8:15] // we want it in bits [16:23] - let aff1 = cpu & 0xff00 << 8; + let aff1 = mpidr.affinity(1) << 16; // level 0 affinity is expected in cpu[0:7] // we want it as a GICv2-style target list - let aff0 = cpu & 0xff; + let aff0 = mpidr.affinity(0); let target_list = if aff0 >= 16 { panic!("[GIC driver] cannot send an IPI to a core with Aff0 >= 16"); } else { @@ -111,12 +112,12 @@ pub fn send_ipi(int_num: InterruptNumber, target: TargetCpu) { }, // 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) }; } \ No newline at end of file diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index 312b3e757..9a382af54 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -13,14 +13,17 @@ //! - Generating software interrupts (GICv2 style) use super::GicRegisters; -use super::U32BYTES; -use super::TargetCpu; +use super::{U32BYTES, U64BYTES}; +use super::SpiDestination; +use super::IpiTargetCpu; use super::InterruptNumber; use super::Enabled; use super::TargetList; +use cpu::MpidrValue; + mod offset { - use super::U32BYTES; + use super::{U32BYTES, U64BYTES}; pub const CTLR: usize = 0x000 / U32BYTES; pub const IGROUPR: usize = 0x080 / U32BYTES; pub const ISENABLER: usize = 0x100 / U32BYTES; @@ -28,7 +31,7 @@ mod offset { pub const ITARGETSR: usize = 0x800 / U32BYTES; pub const SGIR: usize = 0xf00 / U32BYTES; /// This one is on the 6th page - pub const P6IROUTER: usize = 0x100 / U32BYTES; + pub const P6IROUTER: usize = 0x100 / U64BYTES; } // enable group 0 @@ -48,7 +51,7 @@ const SGIR_TARGET_ALL_OTHER_PE: u32 = 1 << 24; // bit 31: SPI routing // 1 = any available PE // 0 = route to specific PE -const P6IROUTER_ANY_AVAILABLE_PE: u32 = 1 << 31; +const P6IROUTER_ANY_AVAILABLE_PE: u64 = 1 << 31; // const GROUP_0: u32 = 0; const GROUP_1: u32 = 1; @@ -56,12 +59,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 /// @@ -106,13 +103,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.bits as u32) << 16, }; let value: u32 = int_num | target_list | SGIR_NSATT_GRP1; @@ -139,35 +138,37 @@ impl super::ArmGic { /// 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 { + pub fn get_spi_target(&self, int: InterruptNumber) -> SpiDestination { 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; + return SpiDestination::AnyCpuAvailable; } for i in 0..8 { let target = 1 << i; if target & flags == target { - return TargetCpu::Specific(i); + return SpiDestination::Specific(MpidrValue::new(0, 0, 0, i).into()); } } let list = TargetList::from_bits_truncate(flags as u8); - TargetCpu::GICv2TargetList(list) + SpiDestination::GICv2TargetList(list) } else if let Self::V3(v3) = self { - let reg = v3.dist_extended.read_volatile(offset::P6IROUTER); + 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) + let aff3 = (reg >> 32) as u8; + let aff2 = (reg >> 16) as u8; + let aff1 = (reg >> 8) as u8; + let aff0 = (reg >> 0) as u8; + SpiDestination::Specific(MpidrValue::new(aff3, aff2, aff1, aff0).into()) } } else { // If we're on gicv2 then affinity routing is off @@ -181,38 +182,32 @@ impl super::ArmGic { /// 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) { + 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 => 0xff, + SpiDestination::GICv2TargetList(list) => list.bits 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) => { - // 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!"); }, }; - v3.dist_extended.write_volatile(offset::P6IROUTER, value); + v3.dist_extended.write_volatile_64(offset::P6IROUTER, value); } // If we're on gicv2 then affinity routing is off diff --git a/kernel/gic/src/gic/mod.rs b/kernel/gic/src/gic/mod.rs index 301e6b4d9..d7af922b8 100644 --- a/kernel/gic/src/gic/mod.rs +++ b/kernel/gic/src/gic/mod.rs @@ -3,6 +3,8 @@ use core::convert::AsMut; use memory::{PageTable, BorrowedMappedPages, Mutable, PhysicalAddress, PteFlags, allocate_pages, allocate_frames_at}; +use cpu::CpuId; + use static_assertions::const_assert_eq; use bitflags::bitflags; @@ -46,15 +48,11 @@ bitflags! { } } -pub enum TargetCpu { +/// Target of a shared-peripheral interrupt +pub enum SpiDestination { /// That interrupt must be handled by /// a specific PE in the system. - /// - /// - level 3 affinity is expected in bits [24:31] - /// - level 2 affinity is expected in bits [16:23] - /// - level 1 affinity is expected in bits [8:15] - /// - level 0 affinity is expected in bits [0:7] - Specific(u32), + Specific(CpuId), /// That interrupt can be handled by /// any PE that is not busy with another, /// more important task @@ -62,6 +60,18 @@ pub enum TargetCpu { GICv2TargetList(TargetList), } +/// Target of an inter-processor interrupt +pub enum IpiTargetCpu { + /// That interrupt must be handled by + /// a specific PE in the system. + Specific(CpuId), + /// All PEs will receive this interrupt + /// except the sender + AllOtherCpus, + GICv2TargetList(TargetList), +} + +const U64BYTES: usize = core::mem::size_of::(); const U32BYTES: usize = core::mem::size_of::(); const U32BITS: usize = u32::BITS as usize; @@ -80,6 +90,14 @@ impl GicRegisters { unsafe { (&mut self.inner[index] as *mut u32).write_volatile(value) } } + fn read_volatile_64(&self, index: usize) -> u64 { + unsafe { (self.inner.as_ptr() as *const u64).add(index).read_volatile() } + } + + fn write_volatile_64(&mut self, index: usize, value: u64) { + unsafe { (self.inner.as_mut_ptr() as *mut u64).add(index).write_volatile(value) } + } + // Reads one item of an array spanning across // multiple u32s. // @@ -242,7 +260,7 @@ impl ArmGic { /// also called software generated interrupt (SGI). /// /// note: on Aarch64, IPIs must have a number below 16 on ARMv8 - pub fn send_ipi(&mut self, int_num: InterruptNumber, target: TargetCpu) { + pub fn send_ipi(&mut self, int_num: InterruptNumber, target: IpiTargetCpu) { assert!(int_num < 16, "IPIs must have a number below 16 on ARMv8"); match self { From 9871460e0682485c5bef0a74dceb7f32ab03f349 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Tue, 28 Mar 2023 15:48:25 +0200 Subject: [PATCH 02/25] Dedicated Offset Types to prevent confusion between 32b & 64b registers --- kernel/gic/src/gic/cpu_interface_gicv2.rs | 13 ++++--- kernel/gic/src/gic/dist_interface.rs | 17 +++++---- kernel/gic/src/gic/mod.rs | 44 +++++++++++++++-------- kernel/gic/src/gic/redist_interface.rs | 11 +++--- 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/kernel/gic/src/gic/cpu_interface_gicv2.rs b/kernel/gic/src/gic/cpu_interface_gicv2.rs index 65049b635..264297e5f 100644 --- a/kernel/gic/src/gic/cpu_interface_gicv2.rs +++ b/kernel/gic/src/gic/cpu_interface_gicv2.rs @@ -7,17 +7,16 @@ //! - Sending End-Of-Interrupts signals use super::GicRegisters; -use super::U32BYTES; use super::Priority; use super::InterruptNumber; mod offset { - use super::U32BYTES; - pub const CTLR: usize = 0x00 / U32BYTES; - pub const PMR: usize = 0x04 / U32BYTES; - pub const IAR: usize = 0x0C / U32BYTES; - pub const RPR: usize = 0x14 / U32BYTES; - pub const EOIR: usize = 0x10 / U32BYTES; + use crate::Offset32; + pub(crate) const CTLR: Offset32 = Offset32::from_byte_offset(0x00); + pub(crate) const PMR: Offset32 = Offset32::from_byte_offset(0x04); + pub(crate) const IAR: Offset32 = Offset32::from_byte_offset(0x0C); + pub(crate) const RPR: Offset32 = Offset32::from_byte_offset(0x14); + pub(crate) const EOIR: Offset32 = Offset32::from_byte_offset(0x10); } // enable group 0 diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index 9a382af54..019bb7e45 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -13,7 +13,6 @@ //! - Generating software interrupts (GICv2 style) use super::GicRegisters; -use super::{U32BYTES, U64BYTES}; use super::SpiDestination; use super::IpiTargetCpu; use super::InterruptNumber; @@ -23,15 +22,15 @@ use super::TargetList; use cpu::MpidrValue; mod offset { - use super::{U32BYTES, U64BYTES}; - pub const CTLR: usize = 0x000 / U32BYTES; - pub const IGROUPR: usize = 0x080 / U32BYTES; - pub const ISENABLER: usize = 0x100 / U32BYTES; - pub const ICENABLER: usize = 0x180 / U32BYTES; - pub const ITARGETSR: usize = 0x800 / U32BYTES; - pub const SGIR: usize = 0xf00 / U32BYTES; + use crate::{Offset32, Offset64}; + pub(crate) const CTLR: Offset32 = Offset32::from_byte_offset(0x000); + pub(crate) const IGROUPR: Offset32 = Offset32::from_byte_offset(0x080); + pub(crate) const ISENABLER: Offset32 = Offset32::from_byte_offset(0x100); + pub(crate) const ICENABLER: Offset32 = Offset32::from_byte_offset(0x180); + pub(crate) const ITARGETSR: Offset32 = Offset32::from_byte_offset(0x800); + pub(crate) const SGIR: Offset32 = Offset32::from_byte_offset(0xf00); /// This one is on the 6th page - pub const P6IROUTER: usize = 0x100 / U64BYTES; + pub(crate) const P6IROUTER: Offset64 = Offset64::from_byte_offset(0x100); } // enable group 0 diff --git a/kernel/gic/src/gic/mod.rs b/kernel/gic/src/gic/mod.rs index d7af922b8..899e98076 100644 --- a/kernel/gic/src/gic/mod.rs +++ b/kernel/gic/src/gic/mod.rs @@ -71,10 +71,26 @@ pub enum IpiTargetCpu { GICv2TargetList(TargetList), } -const U64BYTES: usize = core::mem::size_of::(); -const U32BYTES: usize = core::mem::size_of::(); const U32BITS: usize = u32::BITS as usize; +#[derive(Copy, Clone)] +pub(crate) struct Offset32(usize); + +#[derive(Copy, Clone)] +pub(crate) struct Offset64(usize); + +impl Offset32 { + pub(crate) const fn from_byte_offset(byte_offset: usize) -> Self { + Self(byte_offset / core::mem::size_of::()) + } +} + +impl Offset64 { + pub(crate) const fn from_byte_offset(byte_offset: usize) -> Self { + Self(byte_offset / core::mem::size_of::()) + } +} + #[repr(C)] #[derive(zerocopy::FromBytes)] pub struct GicRegisters { @@ -82,20 +98,20 @@ pub struct GicRegisters { } impl GicRegisters { - fn read_volatile(&self, index: usize) -> u32 { - unsafe { (&self.inner[index] as *const u32).read_volatile() } + fn read_volatile(&self, offset: Offset32) -> u32 { + unsafe { (&self.inner[offset.0] as *const u32).read_volatile() } } - fn write_volatile(&mut self, index: usize, value: u32) { - unsafe { (&mut self.inner[index] as *mut u32).write_volatile(value) } + fn write_volatile(&mut self, offset: Offset32, value: u32) { + unsafe { (&mut self.inner[offset.0] as *mut u32).write_volatile(value) } } - fn read_volatile_64(&self, index: usize) -> u64 { - unsafe { (self.inner.as_ptr() as *const u64).add(index).read_volatile() } + fn read_volatile_64(&self, offset: Offset64) -> u64 { + unsafe { (self.inner.as_ptr() as *const u64).add(offset.0).read_volatile() } } - fn write_volatile_64(&mut self, index: usize, value: u64) { - unsafe { (self.inner.as_mut_ptr() as *mut u64).add(index).write_volatile(value) } + fn write_volatile_64(&mut self, offset: Offset64, value: u64) { + unsafe { (self.inner.as_mut_ptr() as *mut u64).add(offset.0).write_volatile(value) } } // Reads one item of an array spanning across @@ -108,12 +124,12 @@ impl GicRegisters { // - `int` is the index // - `offset` tells the beginning of the array // - `INTS_PER_U32` = how many array slots per u32 in this array - fn read_array_volatile(&self, offset: usize, int: InterruptNumber) -> u32 { + fn read_array_volatile(&self, offset: Offset32, int: InterruptNumber) -> u32 { let int = int as usize; let bits_per_int: usize = U32BITS / INTS_PER_U32; let mask: u32 = u32::MAX >> (U32BITS - bits_per_int); - let offset = offset + (int / INTS_PER_U32); + let offset = Offset32(offset.0 + (int / INTS_PER_U32)); let reg_index = int & (INTS_PER_U32 - 1); let shift = reg_index * bits_per_int; @@ -132,12 +148,12 @@ impl GicRegisters { // - `offset` tells the beginning of the array // - `INTS_PER_U32` = how many array slots per u32 in this array // - `value` is the value to write - fn write_array_volatile(&mut self, offset: usize, int: InterruptNumber, value: u32) { + fn write_array_volatile(&mut self, offset: Offset32, int: InterruptNumber, value: u32) { let int = int as usize; let bits_per_int: usize = U32BITS / INTS_PER_U32; let mask: u32 = u32::MAX >> (U32BITS - bits_per_int); - let offset = offset + (int / INTS_PER_U32); + let offset = Offset32(offset.0 + (int / INTS_PER_U32)); let reg_index = int & (INTS_PER_U32 - 1); let shift = reg_index * bits_per_int; diff --git a/kernel/gic/src/gic/redist_interface.rs b/kernel/gic/src/gic/redist_interface.rs index a5212fc7a..4e9eaca2f 100644 --- a/kernel/gic/src/gic/redist_interface.rs +++ b/kernel/gic/src/gic/redist_interface.rs @@ -9,16 +9,15 @@ //! - Enabling or disabling the forwarding of PPIs & SGIs based on their numbers use super::GicRegisters; -use super::U32BYTES; use super::InterruptNumber; use super::Enabled; mod offset { - use super::U32BYTES; - pub const RD_WAKER: usize = 0x14 / U32BYTES; - pub const IGROUPR: usize = 0x80 / U32BYTES; - pub const SGIPPI_ISENABLER: usize = 0x100 / U32BYTES; - pub const SGIPPI_ICENABLER: usize = 0x180 / U32BYTES; + use crate::Offset32; + pub(crate) const RD_WAKER: Offset32 = Offset32::from_byte_offset(0x14); + pub(crate) const IGROUPR: Offset32 = Offset32::from_byte_offset(0x80); + pub(crate) const SGIPPI_ISENABLER: Offset32 = Offset32::from_byte_offset(0x100); + pub(crate) const SGIPPI_ICENABLER: Offset32 = Offset32::from_byte_offset(0x180); } const RD_WAKER_PROCESSOR_SLEEP: u32 = 1 << 1; From 0e2d2bfe23ca5cb5e9656a12dbb8427124b61826 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Tue, 28 Mar 2023 15:48:45 +0200 Subject: [PATCH 03/25] Fix doc builds --- kernel/gic/src/gic/cpu_interface_gicv3.rs | 3 +-- kernel/gic/src/gic/dist_interface.rs | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/kernel/gic/src/gic/cpu_interface_gicv3.rs b/kernel/gic/src/gic/cpu_interface_gicv3.rs index c0ecabeb0..a3d53b350 100644 --- a/kernel/gic/src/gic/cpu_interface_gicv3.rs +++ b/kernel/gic/src/gic/cpu_interface_gicv3.rs @@ -11,7 +11,6 @@ use core::arch::asm; use super::IpiTargetCpu; use super::Priority; use super::InterruptNumber; -use cpu::MpidrValue; const SGIR_TARGET_ALL_OTHER_PE: u64 = 1 << 40; const IGRPEN_ENABLED: u64 = 1; @@ -86,7 +85,7 @@ pub fn acknowledge_interrupt() -> (InterruptNumber, Priority) { pub fn send_ipi(int_num: InterruptNumber, target: IpiTargetCpu) { let mut value = match target { IpiTargetCpu::Specific(cpu) => { - let mpidr: MpidrValue = cpu.into(); + let mpidr: cpu::MpidrValue = cpu.into(); // level 3 affinity is expected in cpu[24:31] // we want it in bits [48:55] diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index 019bb7e45..52c944358 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -19,8 +19,6 @@ 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); @@ -148,7 +146,7 @@ impl super::ArmGic { for i in 0..8 { let target = 1 << i; if target & flags == target { - return SpiDestination::Specific(MpidrValue::new(0, 0, 0, i).into()); + return SpiDestination::Specific(cpu::MpidrValue::new(0, 0, 0, i).into()); } } @@ -167,7 +165,7 @@ impl super::ArmGic { let aff2 = (reg >> 16) as u8; let aff1 = (reg >> 8) as u8; let aff0 = (reg >> 0) as u8; - SpiDestination::Specific(MpidrValue::new(aff3, aff2, aff1, aff0).into()) + SpiDestination::Specific(cpu::MpidrValue::new(aff3, aff2, aff1, aff0).into()) } } else { // If we're on gicv2 then affinity routing is off @@ -197,7 +195,7 @@ impl super::ArmGic { self.distributor_mut().write_array_volatile::<4>(offset::ITARGETSR, int, value); } else if let Self::V3(v3) = self { let value = match target { - SpiDestination::Specific(cpu) => MpidrValue::from(cpu).value(), + SpiDestination::Specific(cpu) => cpu::MpidrValue::from(cpu).value(), // bit 31: Interrupt Routing Mode // value of 1 to target any available cpu SpiDestination::AnyCpuAvailable => P6IROUTER_ANY_AVAILABLE_PE, From 76fca7d84ba56b091b7f24975c388edb3ee8ab0d Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Tue, 28 Mar 2023 16:34:54 +0200 Subject: [PATCH 04/25] Make secondary cores usable on aarch64 --- Cargo.lock | 1 + kernel/ap_start/src/lib.rs | 44 +++++++++++++------------ kernel/captain/src/lib.rs | 8 +++-- kernel/cpu/Cargo.toml | 1 + kernel/cpu/src/aarch64.rs | 49 ++++++++++++++++++++++------ kernel/cpu/src/lib.rs | 3 ++ kernel/interrupts/src/aarch64/mod.rs | 8 +++++ 7 files changed, 82 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2ecde4fc..36d5ac9f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,6 +610,7 @@ dependencies = [ "apic", "cortex-a", "derive_more", + "irq_safety", "tock-registers", ] diff --git a/kernel/ap_start/src/lib.rs b/kernel/ap_start/src/lib.rs index d218a2200..04d79c5a4 100644 --- a/kernel/ap_start/src/lib.rs +++ b/kernel/ap_start/src/lib.rs @@ -54,6 +54,10 @@ pub fn kstart_ap( processor_id, cpu_id, _stack_start, _stack_end, nmi_lint, nmi_flags ); + // As a cheap precaution + #[cfg(target_arch = "aarch64")] + irq_safety::disable_interrupts(); + // set a flag telling the BSP that this AP has entered Rust code AP_READY_FLAG.store(true, Ordering::SeqCst); @@ -66,14 +70,10 @@ pub fn kstart_ap( || panic!("BUG: kstart_ap(): couldn't get stack created for CPU {}", cpu_id) ); - #[cfg(target_arch = "aarch64")] { - log::info!("cpu {} is ready!", cpu_id); - loop {} - } - - // initialize interrupts (including TSS/GDT) for this AP let kernel_mmi_ref = get_kernel_mmi_ref().expect("kstart_ap(): kernel_mmi ref was None"); + #[cfg(target_arch = "x86_64")] { + // initialize interrupts (including TSS/GDT) for this AP let (double_fault_stack, privilege_stack) = { let mut kernel_mmi = kernel_mmi_ref.lock(); ( @@ -85,23 +85,27 @@ pub fn kstart_ap( }; let _idt = interrupts::init_ap(cpu_id, double_fault_stack.top_unusable(), privilege_stack.top_unusable()) .expect("kstart_ap(): failed to initialize interrupts!"); + + // Initialize this CPU's Local APIC such that we can use everything that depends on APIC IDs. + // This must be done before initializing task spawning, because that relies on the ability to + // enable/disable preemption, which is partially implemented by the Local APIC. + LocalApic::init( + &mut kernel_mmi_ref.lock().page_table, + processor_id, + Some(cpu_id.value()), + false, + nmi_lint, + nmi_flags, + ).unwrap(); } - #[cfg(target_arch = "aarch64")] - interrupts::init_ap(); + #[cfg(target_arch = "aarch64")] { + interrupts::init_ap(); - // Initialize this CPU's Local APIC such that we can use everything that depends on APIC IDs. - // This must be done before initializing task spawning, because that relies on the ability to - // enable/disable preemption, which is partially implemented by the Local APIC. - #[cfg(target_arch = "x86_64")] - LocalApic::init( - &mut kernel_mmi_ref.lock().page_table, - processor_id, - Some(cpu_id.value()), - false, - nmi_lint, - nmi_flags, - ).unwrap(); + // Register this CPU as online in the system + // This is the equivalent of `LocalApic::init` on aarch64 + cpu::register_cpu(false).unwrap(); + } // Now that the Local APIC has been initialized for this CPU, we can initialize the // task management subsystem and create the idle task for this CPU. diff --git a/kernel/captain/src/lib.rs b/kernel/captain/src/lib.rs index b274f636b..3145dda56 100644 --- a/kernel/captain/src/lib.rs +++ b/kernel/captain/src/lib.rs @@ -112,8 +112,12 @@ pub fn init( interrupts::init(double_fault_stack.top_unusable(), privilege_stack.top_unusable())? }; - #[cfg(target_arch = "aarch64")] - interrupts::init().unwrap(); + #[cfg(target_arch = "aarch64")] { + interrupts::init().unwrap(); + + // register BSP CpuId + cpu::register_cpu(true).unwrap(); + } // get BSP's CPU ID let bsp_id = cpu::bootstrap_cpu().ok_or("captain::init(): couldn't get ID of bootstrap CPU!")?; diff --git a/kernel/cpu/Cargo.toml b/kernel/cpu/Cargo.toml index 5819b641b..275ed2279 100644 --- a/kernel/cpu/Cargo.toml +++ b/kernel/cpu/Cargo.toml @@ -15,6 +15,7 @@ apic = { path = "../apic" } [target.'cfg(target_arch = "aarch64")'.dependencies] tock-registers = "0.7.0" cortex-a = "7.5.0" +irq_safety = { git = "https://github.com/theseus-os/irq_safety" } [lib] crate-type = ["rlib"] diff --git a/kernel/cpu/src/aarch64.rs b/kernel/cpu/src/aarch64.rs index 9b63aa8ea..0ae77a2e7 100644 --- a/kernel/cpu/src/aarch64.rs +++ b/kernel/cpu/src/aarch64.rs @@ -2,32 +2,61 @@ use cortex_a::registers::MPIDR_EL1; use tock_registers::interfaces::Readable; - +use derive_more::{Display, Binary, Octal, LowerHex, UpperHex}; +use irq_safety::RwLockIrqSafe; use core::fmt; +use alloc::vec::Vec; + use super::CpuId; -use derive_more::{Display, Binary, Octal, LowerHex, UpperHex}; + +// The vector of CpuIds for known and online CPU cores +static ONLINE_CORES: RwLockIrqSafe> = RwLockIrqSafe::new(Vec::new()); + +/// This must be called once for every CPU core of the system +/// that should be used for running tasks. +/// +/// The first CPU to register itself is called the BSP (bootstrap processor). +/// When it does so (from captain), it must set the `bootstrap` parameter +/// to true. Other cores must set it to false. +pub fn register_cpu(bootstrap: bool) -> Result<(), &'static str> { + let mut locked = ONLINE_CORES.write(); + + // the vector must be empty when the bootstrap + // processor registers itself. + if bootstrap == locked.is_empty() { + let cpu_id = current_cpu(); + + if locked.contains(&cpu_id) { + Err("Tried to register the same CpuId twice") + } else { + locked.push(cpu_id); + + Ok(()) + } + } else { + match bootstrap { + true => Err("Tried to register the BSP after another core: invalid"), + false => Err("Tried to register a secondary CPU before the BSP: invalid"), + } + } +} /// Returns the number of CPUs (SMP cores) that exist and /// are currently initialized on this system. pub fn cpu_count() -> u32 { - // The ARM port doesn't start secondary cores for the moment. - 1 + ONLINE_CORES.read().len() as u32 } /// Returns the ID of the bootstrap CPU (if known), which /// is the first CPU to run after system power-on. pub fn bootstrap_cpu() -> Option { - // The ARM port doesn't start secondary cores for the moment, - // so the current CPU can only be the "bootstrap" CPU. - Some(current_cpu()) + ONLINE_CORES.read().first().copied() } /// Returns true if the currently executing CPU is the bootstrap /// CPU, i.e., the first CPU to run after system power-on. pub fn is_bootstrap_cpu() -> bool { - // The ARM port doesn't start secondary cores for the moment, - // so the current CPU can only be the "bootstrap" CPU. - true + Some(current_cpu()) == bootstrap_cpu() } /// Returns the ID of the currently executing CPU. diff --git a/kernel/cpu/src/lib.rs b/kernel/cpu/src/lib.rs index ae0b0e631..61f6cb97b 100644 --- a/kernel/cpu/src/lib.rs +++ b/kernel/cpu/src/lib.rs @@ -10,6 +10,9 @@ #![no_std] +#[cfg(target_arch = "aarch64")] +extern crate alloc; + #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] mod arch; diff --git a/kernel/interrupts/src/aarch64/mod.rs b/kernel/interrupts/src/aarch64/mod.rs index cad9f8ae3..ec86a1dc5 100644 --- a/kernel/interrupts/src/aarch64/mod.rs +++ b/kernel/interrupts/src/aarch64/mod.rs @@ -119,8 +119,16 @@ fn set_vbar_el1() { } /// Sets `VBAR_EL1` to the start of the exception vector +/// and enables timer interrupts pub fn init_ap() { set_vbar_el1(); + + // Enable the CPU-local timer + let mut gic = GIC.lock(); + let gic = gic.as_mut().expect("GIC is uninitialized"); + gic.set_interrupt_state(CPU_LOCAL_TIMER_IRQ, true); + + enable_timer(true); } /// Please call this (only once) before using this crate. From dbf7bc9247f62108baf31c7439d32d6004d4a535 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Tue, 28 Mar 2023 16:48:11 +0200 Subject: [PATCH 05/25] Enable old-school TLB-shootdowns on aarch64 --- Cargo.lock | 5 ++- kernel/captain/Cargo.toml | 2 +- kernel/captain/src/lib.rs | 2 - kernel/interrupts/src/aarch64/mod.rs | 39 ++++++++++++++++- kernel/tlb_shootdown/Cargo.toml | 28 ++++++------ kernel/tlb_shootdown/src/lib.rs | 65 +++++++++++++++++----------- 6 files changed, 96 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46fc0a722..0165a8b7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4073,10 +4073,13 @@ name = "tlb_shootdown" version = "0.1.0" dependencies = [ "apic", + "cpu", + "interrupts", "irq_safety", "log", "memory", - "x86_64", + "memory_aarch64", + "memory_x86_64", ] [[package]] diff --git a/kernel/captain/Cargo.toml b/kernel/captain/Cargo.toml index 269f5823b..1614f9788 100644 --- a/kernel/captain/Cargo.toml +++ b/kernel/captain/Cargo.toml @@ -12,6 +12,7 @@ irq_safety = { git = "https://github.com/theseus-os/irq_safety" } dfqueue = { path = "../../libs/dfqueue", version = "0.1.0" } multicore_bringup = { path = "../multicore_bringup" } early_printer = { path = "../early_printer" } +tlb_shootdown = { path = "../tlb_shootdown" } kernel_config = { path = "../kernel_config" } interrupts = { path = "../interrupts" } scheduler = { path = "../scheduler" } @@ -29,7 +30,6 @@ logger_x86_64 = { path = "../logger_x86_64" } window_manager = { path = "../window_manager" } first_application = { path = "../first_application" } exceptions_full = { path = "../exceptions_full" } -tlb_shootdown = { path = "../tlb_shootdown" } multiple_heaps = { path = "../multiple_heaps" } tsc = { path = "../tsc" } acpi = { path = "../acpi" } diff --git a/kernel/captain/src/lib.rs b/kernel/captain/src/lib.rs index 3145dda56..03b635a9e 100644 --- a/kernel/captain/src/lib.rs +++ b/kernel/captain/src/lib.rs @@ -154,8 +154,6 @@ pub fn init( // Now that other CPUs are fully booted, init TLB shootdowns, // which rely on Local APICs to broadcast an IPI to all running CPUs. - // arch-gate: no multicore support on aarch64 at the moment - #[cfg(target_arch = "x86_64")] tlb_shootdown::init(); // Initialize the per-core heaps. diff --git a/kernel/interrupts/src/aarch64/mod.rs b/kernel/interrupts/src/aarch64/mod.rs index ec86a1dc5..ff42508f5 100644 --- a/kernel/interrupts/src/aarch64/mod.rs +++ b/kernel/interrupts/src/aarch64/mod.rs @@ -9,7 +9,7 @@ use tock_registers::interfaces::Readable; use tock_registers::registers::InMemoryRegister; use kernel_config::time::CONFIG_TIMESLICE_PERIOD_MICROSECONDS; -use gic::{qemu_virt_addrs, ArmGic, InterruptNumber, Version as GicVersion}; +use gic::{qemu_virt_addrs, ArmGic, InterruptNumber, IpiTargetCpu, Version as GicVersion}; use irq_safety::{RwLockIrqSafe, MutexIrqSafe}; use memory::get_kernel_mmi_ref; use log::{info, error}; @@ -29,6 +29,11 @@ static GIC: MutexIrqSafe> = MutexIrqSafe::new(None); // aarch64 manuals define the default timer IRQ number to be 30. pub const CPU_LOCAL_TIMER_IRQ: InterruptNumber = 30; +/// The IRQ/IPI number for TLB Shootdowns +/// +/// This is arbitrarily defined (range: 0..16) +pub const TLB_SHOOTDOWN_IPI: InterruptNumber = 2; + const MAX_IRQ_NUM: usize = 256; // Singleton which acts like an x86-style Interrupt Descriptor Table: @@ -191,6 +196,30 @@ pub fn init_timer(timer_tick_handler: HandlerFunc) -> Result<(), &'static str> { Ok(()) } +/// This function registers an interrupt handler for an inter-processor interrupt +/// and handles GIC configuration for that interrupt. +pub fn init_ipi(handler: HandlerFunc, irq_num: InterruptNumber) -> Result<(), &'static str> { + assert!(irq_num < 16, "Inter-processor interrupts must have a number in the range 0..16"); + + // register/deregister the handler for the timer IRQ. + if let Err(existing_handler) = register_interrupt(irq_num, handler) { + if handler as *const HandlerFunc != existing_handler { + return Err("A different interrupt handler has already been setup for the timer IRQ number"); + } + } + + // Route the IRQ to this core (implicit as irq_num < 32) & Enable the interrupt. + { + let mut gic = GIC.lock(); + let gic = gic.as_mut().ok_or("GIC is uninitialized")?; + + // enable routing of this interrupt + gic.set_interrupt_state(irq_num, true); + } + + Ok(()) +} + /// Disables the timer, schedules its next tick, and re-enables it pub fn schedule_next_timer_tick() { enable_timer(false); @@ -270,6 +299,14 @@ pub fn deregister_interrupt(irq_num: InterruptNumber, func: HandlerFunc) -> Resu } } +/// Broadcast an Inter-Processor Interrupt to all other +/// cores in the system +pub fn send_ipi_to_all_other_cpus(irq_num: InterruptNumber) { + let mut gic = GIC.lock(); + let gic = gic.as_mut().expect("GIC is uninitialized"); + gic.send_ipi(irq_num, IpiTargetCpu::AllOtherCpus); +} + /// Send an "end of interrupt" signal, notifying the interrupt chip that /// the given interrupt request `irq` has been serviced. pub fn eoi(irq_num: InterruptNumber) { diff --git a/kernel/tlb_shootdown/Cargo.toml b/kernel/tlb_shootdown/Cargo.toml index 7682a40f4..3728d1f53 100644 --- a/kernel/tlb_shootdown/Cargo.toml +++ b/kernel/tlb_shootdown/Cargo.toml @@ -3,23 +3,21 @@ authors = ["Kevin Boos "] name = "tlb_shootdown" description = "Routines for handling TLB shootdowns" version = "0.1.0" +edition = "2021" [dependencies] -x86_64 = "0.14.8" - - -[dependencies.log] -version = "0.4.8" - -[dependencies.irq_safety] -git = "https://github.com/theseus-os/irq_safety" - -[dependencies.memory] -path = "../memory" - -[dependencies.apic] -path = "../apic" - +log = "0.4.8" +irq_safety = { git = "https://github.com/theseus-os/irq_safety" } +memory = { path = "../memory" } +cpu = { path = "../cpu" } + +[target.'cfg(target_arch = "x86_64")'.dependencies] +memory_x86_64 = { path = "../memory_x86_64" } +apic = { path = "../apic" } + +[target.'cfg(target_arch = "aarch64")'.dependencies] +memory_aarch64 = { path = "../memory_aarch64" } +interrupts = { path = "../interrupts" } [lib] crate-type = ["rlib"] diff --git a/kernel/tlb_shootdown/src/lib.rs b/kernel/tlb_shootdown/src/lib.rs index 6ce1aa732..7bd2245c1 100644 --- a/kernel/tlb_shootdown/src/lib.rs +++ b/kernel/tlb_shootdown/src/lib.rs @@ -2,19 +2,17 @@ #![no_std] -// #[macro_use] extern crate log; -extern crate irq_safety; -extern crate memory; -extern crate apic; -extern crate x86_64; - - use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use irq_safety::{hold_interrupts, RwLockIrqSafe}; use memory::PageRange; -use apic::{LocalApic, get_my_apic, cpu_count, LapicIpiDestination}; +use cpu::cpu_count; use core::hint::spin_loop; +#[cfg(target_arch = "x86_64")] +use memory_x86_64::tlb_flush_virt_addr; + +#[cfg(target_arch = "aarch64")] +use memory_aarch64::tlb_flush_virt_addr; /// The number of remaining cores that still need to handle the current TLB shootdown IPI pub static TLB_SHOOTDOWN_IPI_COUNT: AtomicU32 = AtomicU32::new(0); @@ -28,20 +26,11 @@ pub static TLB_SHOOTDOWN_IPI_PAGES: RwLockIrqSafe> = RwLockIrq /// TODO: redesign this, it's weird and silly just to set one callback. pub fn init() { memory::set_broadcast_tlb_shootdown_cb(broadcast_tlb_shootdown); -} - -/// Broadcasts TLB shootdown IPI to all other AP cores. -/// Do not invoke this directly, but rather pass it as a callback to the memory subsystem, -/// which will invoke it as needed (on remap/unmap operations). -fn broadcast_tlb_shootdown(pages_to_invalidate: PageRange) { - if let Some(my_lapic) = get_my_apic() { - // log::info!("broadcast_tlb_shootdown(): AP {}, pages: {:?}", my_lapic.read().apic_id(), pages_to_invalidate); - send_tlb_shootdown_ipi(&mut my_lapic.write(), pages_to_invalidate); - } + #[cfg(target_arch = "aarch64")] + interrupts::init_ipi(tlb_shootdown_ipi_handler, interrupts::TLB_SHOOTDOWN_IPI).unwrap(); } - /// Handles a TLB shootdown ipi by flushing the `VirtualAddress`es /// covered by the given range of `pages_to_invalidate`. /// @@ -50,26 +39,32 @@ pub fn handle_tlb_shootdown_ipi(pages_to_invalidate: PageRange) { // log::trace!("handle_tlb_shootdown_ipi(): AP {}, pages: {:?}", apic::current_cpu(), pages_to_invalidate); for page in pages_to_invalidate { - x86_64::instructions::tlb::flush(x86_64::VirtAddr::new(page.start_address().value() as u64)); + tlb_flush_virt_addr(page.start_address()); } + TLB_SHOOTDOWN_IPI_COUNT.fetch_sub(1, Ordering::SeqCst); } +/// Broadcasts TLB shootdown IPI to all other AP cores. +/// +/// Do not invoke this directly, but rather pass it as a callback to the memory subsystem, +/// which will invoke it as needed (on remap/unmap operations). +/// /// Sends an IPI to all other cores (except me) to trigger /// a TLB flush of the given pages' virtual addresses. -pub fn send_tlb_shootdown_ipi(my_lapic: &mut LocalApic, pages_to_invalidate: PageRange) { +fn broadcast_tlb_shootdown(pages_to_invalidate: PageRange) { // skip sending IPIs if there are no other cores running let cpu_count = cpu_count(); if cpu_count <= 1 { return; } - // log::trace!("send_tlb_shootdown_ipi(): from AP {}, cpu_count: {}, {:?}", my_lapic.apic_id(), cpu_count, pages_to_invalidate); + // log::trace!("send_tlb_shootdown_ipi(): cpu_count: {}, {:?}", cpu_count, pages_to_invalidate); // interrupts must be disabled here, because this IPI sequence must be fully synchronous with other cores, // and we wouldn't want this core to be interrupted while coordinating IPI responses across multiple cores. - let _held_ints = hold_interrupts(); + let _held_ints = hold_interrupts(); // acquire lock // TODO: add timeout!! @@ -85,8 +80,15 @@ pub fn send_tlb_shootdown_ipi(my_lapic: &mut LocalApic, pages_to_invalidate: Pag *TLB_SHOOTDOWN_IPI_PAGES.write() = Some(pages_to_invalidate); TLB_SHOOTDOWN_IPI_COUNT.store(cpu_count - 1, Ordering::SeqCst); // -1 to exclude this core - // let's try to use NMI instead, since it will interrupt everyone forcibly and result in the fastest handling - my_lapic.send_nmi_ipi(LapicIpiDestination::AllButMe); // send IPI to all other cores but this one + #[cfg(target_arch = "x86_64")] { + let my_lapic = apic::get_my_apic().expect("If there are more than one CPU ready, this one should be registered"); + + // use NMI, since it will interrupt everyone forcibly and result in the fastest handling + my_lapic.write().send_nmi_ipi(apic::LapicIpiDestination::AllButMe); // send IPI to all other cores but this one + } + + #[cfg(target_arch = "aarch64")] + interrupts::send_ipi_to_all_other_cpus(interrupts::TLB_SHOOTDOWN_IPI); // wait for all other cores to handle this IPI // it must be a blocking, synchronous operation to ensure stale TLB entries don't cause problems @@ -101,3 +103,16 @@ pub fn send_tlb_shootdown_ipi(my_lapic: &mut LocalApic, pages_to_invalidate: Pag // release lock TLB_SHOOTDOWN_IPI_LOCK.store(false, Ordering::Release); } + +/// Interrupt Handler for TLB Shootdowns on aarch64 +#[cfg(target_arch = "aarch64")] +extern "C" fn tlb_shootdown_ipi_handler(_exc: &interrupts::ExceptionContext) -> interrupts::EoiBehaviour { + if let Some(pages_to_invalidate) = TLB_SHOOTDOWN_IPI_PAGES.read().clone() { + // trace!("nmi_handler (AP {})", cpu::current_cpu()); + handle_tlb_shootdown_ipi(pages_to_invalidate); + } else { + panic!("Unexpected TLB Shootdown IPI!"); + } + + interrupts::EoiBehaviour::CallerMustSignalEoi +} From 6286989d00b47866c9e11fa2e9c80e33f46503b4 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Tue, 28 Mar 2023 17:03:11 +0200 Subject: [PATCH 06/25] This doesn't depend on #991 actually; my bad This reverts commit 76fca7d84ba56b091b7f24975c388edb3ee8ab0d. --- Cargo.lock | 1 - kernel/ap_start/src/lib.rs | 44 ++++++++++++------------- kernel/captain/src/lib.rs | 8 ++--- kernel/cpu/Cargo.toml | 1 - kernel/cpu/src/aarch64.rs | 49 ++++++---------------------- kernel/cpu/src/lib.rs | 3 -- kernel/interrupts/src/aarch64/mod.rs | 8 ----- 7 files changed, 32 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0165a8b7b..5d7954db2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,7 +610,6 @@ dependencies = [ "apic", "cortex-a", "derive_more", - "irq_safety", "tock-registers", ] diff --git a/kernel/ap_start/src/lib.rs b/kernel/ap_start/src/lib.rs index 04d79c5a4..d218a2200 100644 --- a/kernel/ap_start/src/lib.rs +++ b/kernel/ap_start/src/lib.rs @@ -54,10 +54,6 @@ pub fn kstart_ap( processor_id, cpu_id, _stack_start, _stack_end, nmi_lint, nmi_flags ); - // As a cheap precaution - #[cfg(target_arch = "aarch64")] - irq_safety::disable_interrupts(); - // set a flag telling the BSP that this AP has entered Rust code AP_READY_FLAG.store(true, Ordering::SeqCst); @@ -70,10 +66,14 @@ pub fn kstart_ap( || panic!("BUG: kstart_ap(): couldn't get stack created for CPU {}", cpu_id) ); - let kernel_mmi_ref = get_kernel_mmi_ref().expect("kstart_ap(): kernel_mmi ref was None"); + #[cfg(target_arch = "aarch64")] { + log::info!("cpu {} is ready!", cpu_id); + loop {} + } + // initialize interrupts (including TSS/GDT) for this AP + let kernel_mmi_ref = get_kernel_mmi_ref().expect("kstart_ap(): kernel_mmi ref was None"); #[cfg(target_arch = "x86_64")] { - // initialize interrupts (including TSS/GDT) for this AP let (double_fault_stack, privilege_stack) = { let mut kernel_mmi = kernel_mmi_ref.lock(); ( @@ -85,27 +85,23 @@ pub fn kstart_ap( }; let _idt = interrupts::init_ap(cpu_id, double_fault_stack.top_unusable(), privilege_stack.top_unusable()) .expect("kstart_ap(): failed to initialize interrupts!"); - - // Initialize this CPU's Local APIC such that we can use everything that depends on APIC IDs. - // This must be done before initializing task spawning, because that relies on the ability to - // enable/disable preemption, which is partially implemented by the Local APIC. - LocalApic::init( - &mut kernel_mmi_ref.lock().page_table, - processor_id, - Some(cpu_id.value()), - false, - nmi_lint, - nmi_flags, - ).unwrap(); } - #[cfg(target_arch = "aarch64")] { - interrupts::init_ap(); + #[cfg(target_arch = "aarch64")] + interrupts::init_ap(); - // Register this CPU as online in the system - // This is the equivalent of `LocalApic::init` on aarch64 - cpu::register_cpu(false).unwrap(); - } + // Initialize this CPU's Local APIC such that we can use everything that depends on APIC IDs. + // This must be done before initializing task spawning, because that relies on the ability to + // enable/disable preemption, which is partially implemented by the Local APIC. + #[cfg(target_arch = "x86_64")] + LocalApic::init( + &mut kernel_mmi_ref.lock().page_table, + processor_id, + Some(cpu_id.value()), + false, + nmi_lint, + nmi_flags, + ).unwrap(); // Now that the Local APIC has been initialized for this CPU, we can initialize the // task management subsystem and create the idle task for this CPU. diff --git a/kernel/captain/src/lib.rs b/kernel/captain/src/lib.rs index 03b635a9e..f0999f317 100644 --- a/kernel/captain/src/lib.rs +++ b/kernel/captain/src/lib.rs @@ -112,12 +112,8 @@ pub fn init( interrupts::init(double_fault_stack.top_unusable(), privilege_stack.top_unusable())? }; - #[cfg(target_arch = "aarch64")] { - interrupts::init().unwrap(); - - // register BSP CpuId - cpu::register_cpu(true).unwrap(); - } + #[cfg(target_arch = "aarch64")] + interrupts::init().unwrap(); // get BSP's CPU ID let bsp_id = cpu::bootstrap_cpu().ok_or("captain::init(): couldn't get ID of bootstrap CPU!")?; diff --git a/kernel/cpu/Cargo.toml b/kernel/cpu/Cargo.toml index 275ed2279..5819b641b 100644 --- a/kernel/cpu/Cargo.toml +++ b/kernel/cpu/Cargo.toml @@ -15,7 +15,6 @@ apic = { path = "../apic" } [target.'cfg(target_arch = "aarch64")'.dependencies] tock-registers = "0.7.0" cortex-a = "7.5.0" -irq_safety = { git = "https://github.com/theseus-os/irq_safety" } [lib] crate-type = ["rlib"] diff --git a/kernel/cpu/src/aarch64.rs b/kernel/cpu/src/aarch64.rs index 0fae18ca9..28fc73029 100644 --- a/kernel/cpu/src/aarch64.rs +++ b/kernel/cpu/src/aarch64.rs @@ -2,61 +2,32 @@ use cortex_a::registers::MPIDR_EL1; use tock_registers::interfaces::Readable; -use derive_more::{Display, Binary, Octal, LowerHex, UpperHex}; -use irq_safety::RwLockIrqSafe; -use core::fmt; -use alloc::vec::Vec; +use core::fmt; use super::CpuId; - -// The vector of CpuIds for known and online CPU cores -static ONLINE_CORES: RwLockIrqSafe> = RwLockIrqSafe::new(Vec::new()); - -/// This must be called once for every CPU core of the system -/// that should be used for running tasks. -/// -/// The first CPU to register itself is called the BSP (bootstrap processor). -/// When it does so (from captain), it must set the `bootstrap` parameter -/// to true. Other cores must set it to false. -pub fn register_cpu(bootstrap: bool) -> Result<(), &'static str> { - let mut locked = ONLINE_CORES.write(); - - // the vector must be empty when the bootstrap - // processor registers itself. - if bootstrap == locked.is_empty() { - let cpu_id = current_cpu(); - - if locked.contains(&cpu_id) { - Err("Tried to register the same CpuId twice") - } else { - locked.push(cpu_id); - - Ok(()) - } - } else { - match bootstrap { - true => Err("Tried to register the BSP after another core: invalid"), - false => Err("Tried to register a secondary CPU before the BSP: invalid"), - } - } -} +use derive_more::{Display, Binary, Octal, LowerHex, UpperHex}; /// Returns the number of CPUs (SMP cores) that exist and /// are currently initialized on this system. pub fn cpu_count() -> u32 { - ONLINE_CORES.read().len() as u32 + // The ARM port doesn't start secondary cores for the moment. + 1 } /// Returns the ID of the bootstrap CPU (if known), which /// is the first CPU to run after system power-on. pub fn bootstrap_cpu() -> Option { - ONLINE_CORES.read().first().copied() + // The ARM port doesn't start secondary cores for the moment, + // so the current CPU can only be the "bootstrap" CPU. + Some(current_cpu()) } /// Returns true if the currently executing CPU is the bootstrap /// CPU, i.e., the first CPU to run after system power-on. pub fn is_bootstrap_cpu() -> bool { - Some(current_cpu()) == bootstrap_cpu() + // The ARM port doesn't start secondary cores for the moment, + // so the current CPU can only be the "bootstrap" CPU. + true } /// Returns the ID of the currently executing CPU. diff --git a/kernel/cpu/src/lib.rs b/kernel/cpu/src/lib.rs index 61f6cb97b..ae0b0e631 100644 --- a/kernel/cpu/src/lib.rs +++ b/kernel/cpu/src/lib.rs @@ -10,9 +10,6 @@ #![no_std] -#[cfg(target_arch = "aarch64")] -extern crate alloc; - #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] mod arch; diff --git a/kernel/interrupts/src/aarch64/mod.rs b/kernel/interrupts/src/aarch64/mod.rs index ff42508f5..e90292b0c 100644 --- a/kernel/interrupts/src/aarch64/mod.rs +++ b/kernel/interrupts/src/aarch64/mod.rs @@ -124,16 +124,8 @@ fn set_vbar_el1() { } /// Sets `VBAR_EL1` to the start of the exception vector -/// and enables timer interrupts pub fn init_ap() { set_vbar_el1(); - - // Enable the CPU-local timer - let mut gic = GIC.lock(); - let gic = gic.as_mut().expect("GIC is uninitialized"); - gic.set_interrupt_state(CPU_LOCAL_TIMER_IRQ, true); - - enable_timer(true); } /// Please call this (only once) before using this crate. From dc2fb330b84574bcf3247a8916ec89105ff8f580 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 29 Mar 2023 15:49:17 +0200 Subject: [PATCH 07/25] Improve GIC driver doc --- kernel/gic/src/gic/mod.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/kernel/gic/src/gic/mod.rs b/kernel/gic/src/gic/mod.rs index 899e98076..54bfb35b0 100644 --- a/kernel/gic/src/gic/mod.rs +++ b/kernel/gic/src/gic/mod.rs @@ -50,24 +50,22 @@ bitflags! { /// Target of a shared-peripheral interrupt pub enum SpiDestination { - /// That interrupt must be handled by - /// a specific PE in the system. + /// The interrupt must be delivered to a specific CPU. Specific(CpuId), - /// That interrupt can be handled by - /// any PE that is not busy with another, - /// more important task + /// That interrupt can be handled by any PE that is not busy with another, more + /// important task AnyCpuAvailable, + /// The interrupt will be delivered to all CPUs specified by the included target list GICv2TargetList(TargetList), } /// Target of an inter-processor interrupt pub enum IpiTargetCpu { - /// That interrupt must be handled by - /// a specific PE in the system. + /// The interrupt will be delivered to a specific CPU. Specific(CpuId), - /// All PEs will receive this interrupt - /// except the sender + /// The interrupt will be delivered to all CPUs except the sender. AllOtherCpus, + /// The interrupt will be delivered to all CPUs specified by the included target list GICv2TargetList(TargetList), } From 88ab729ee3db26430f51289ace9ccf30fce642e3 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 29 Mar 2023 15:58:06 +0200 Subject: [PATCH 08/25] Introduce AffinityShift for safer affinity level readings from an MpidrValue --- kernel/cpu/src/aarch64.rs | 27 +++++++++++++---------- kernel/gic/src/gic/cpu_interface_gicv3.rs | 20 +++++++---------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/kernel/cpu/src/aarch64.rs b/kernel/cpu/src/aarch64.rs index 28fc73029..fb5f3ea13 100644 --- a/kernel/cpu/src/aarch64.rs +++ b/kernel/cpu/src/aarch64.rs @@ -43,6 +43,19 @@ 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 { @@ -50,18 +63,8 @@ impl MpidrValue { } /// 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) -> u64 { - let shift = match level { - 0 => 0, - 1 => 8, - 2 => 16, - 3 => 32, - _ => panic!("Valid affinity levels are 0, 1, 2, 3"), - }; - - self.0 >> shift + pub fn affinity(self, level: AffinityShift) -> u64 { + (self.0 >> (level as u64)) & (u8::MAX as u64) } /// Create an `MpidrValue` from its four affinity numbers diff --git a/kernel/gic/src/gic/cpu_interface_gicv3.rs b/kernel/gic/src/gic/cpu_interface_gicv3.rs index a3d53b350..5e122718c 100644 --- a/kernel/gic/src/gic/cpu_interface_gicv3.rs +++ b/kernel/gic/src/gic/cpu_interface_gicv3.rs @@ -87,21 +87,17 @@ pub fn send_ipi(int_num: InterruptNumber, target: IpiTargetCpu) { IpiTargetCpu::Specific(cpu) => { let mpidr: cpu::MpidrValue = cpu.into(); - // level 3 affinity is expected in cpu[24:31] - // we want it in bits [48:55] - let aff3 = mpidr.affinity(3) << 48; + // level 3 affinity in bits [48:55] + let aff3 = mpidr.affinity(cpu::AffinityShift::LevelThree) << 48; - // level 2 affinity is expected in cpu[16:23] - // we want it in bits [32:39] - let aff2 = mpidr.affinity(2) << 32; + // level 2 affinity in bits [32:39] + let aff2 = mpidr.affinity(cpu::AffinityShift::LevelTwo) << 32; - // level 1 affinity is expected in cpu[8:15] - // we want it in bits [16:23] - let aff1 = mpidr.affinity(1) << 16; + // level 1 affinity in bits [16:23] + let aff1 = mpidr.affinity(cpu::AffinityShift::LevelOne) << 16; - // level 0 affinity is expected in cpu[0:7] - // we want it as a GICv2-style target list - let aff0 = mpidr.affinity(0); + // level 0 affinity as a GICv2-style target list + let aff0 = mpidr.affinity(cpu::AffinityShift::LevelZero); let target_list = if aff0 >= 16 { panic!("[GIC driver] cannot send an IPI to a core with Aff0 >= 16"); } else { From 6a9675acce02fbf5d63d5ec962ee563d251be077 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 29 Mar 2023 16:05:00 +0200 Subject: [PATCH 09/25] Rename interrupts::aarch64::init_ipi to setup_ipi_handler --- kernel/interrupts/src/aarch64/mod.rs | 2 +- kernel/tlb_shootdown/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/interrupts/src/aarch64/mod.rs b/kernel/interrupts/src/aarch64/mod.rs index e90292b0c..ac70805ed 100644 --- a/kernel/interrupts/src/aarch64/mod.rs +++ b/kernel/interrupts/src/aarch64/mod.rs @@ -190,7 +190,7 @@ pub fn init_timer(timer_tick_handler: HandlerFunc) -> Result<(), &'static str> { /// This function registers an interrupt handler for an inter-processor interrupt /// and handles GIC configuration for that interrupt. -pub fn init_ipi(handler: HandlerFunc, irq_num: InterruptNumber) -> Result<(), &'static str> { +pub fn setup_ipi_handler(handler: HandlerFunc, irq_num: InterruptNumber) -> Result<(), &'static str> { assert!(irq_num < 16, "Inter-processor interrupts must have a number in the range 0..16"); // register/deregister the handler for the timer IRQ. diff --git a/kernel/tlb_shootdown/src/lib.rs b/kernel/tlb_shootdown/src/lib.rs index 7bd2245c1..7ce332f88 100644 --- a/kernel/tlb_shootdown/src/lib.rs +++ b/kernel/tlb_shootdown/src/lib.rs @@ -28,7 +28,7 @@ pub fn init() { memory::set_broadcast_tlb_shootdown_cb(broadcast_tlb_shootdown); #[cfg(target_arch = "aarch64")] - interrupts::init_ipi(tlb_shootdown_ipi_handler, interrupts::TLB_SHOOTDOWN_IPI).unwrap(); + interrupts::setup_ipi_handler(tlb_shootdown_ipi_handler, interrupts::TLB_SHOOTDOWN_IPI).unwrap(); } /// Handles a TLB shootdown ipi by flushing the `VirtualAddress`es From 7db3d0773e77633d338135cb3b245188738e1947 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 29 Mar 2023 16:05:28 +0200 Subject: [PATCH 10/25] Restore CPU info in tlb_shootdown logging statement --- kernel/tlb_shootdown/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/tlb_shootdown/src/lib.rs b/kernel/tlb_shootdown/src/lib.rs index 7ce332f88..1691b839f 100644 --- a/kernel/tlb_shootdown/src/lib.rs +++ b/kernel/tlb_shootdown/src/lib.rs @@ -60,7 +60,9 @@ fn broadcast_tlb_shootdown(pages_to_invalidate: PageRange) { return; } - // log::trace!("send_tlb_shootdown_ipi(): cpu_count: {}, {:?}", cpu_count, pages_to_invalidate); + if false { + log::trace!("send_tlb_shootdown_ipi(): from CPU {:?}, cpu_count: {}, {:?}", cpu::current_cpu(), cpu_count, pages_to_invalidate); + } // interrupts must be disabled here, because this IPI sequence must be fully synchronous with other cores, // and we wouldn't want this core to be interrupted while coordinating IPI responses across multiple cores. From 22b467535efb12c0434e8febe91969936dc69aa3 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Thu, 30 Mar 2023 17:38:55 +0200 Subject: [PATCH 11/25] Better comment for interrupts::aarch64::TLB_SHOOTDOWN_IPI --- kernel/interrupts/src/aarch64/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/interrupts/src/aarch64/mod.rs b/kernel/interrupts/src/aarch64/mod.rs index ac70805ed..0f4a71bdc 100644 --- a/kernel/interrupts/src/aarch64/mod.rs +++ b/kernel/interrupts/src/aarch64/mod.rs @@ -31,7 +31,9 @@ pub const CPU_LOCAL_TIMER_IRQ: InterruptNumber = 30; /// The IRQ/IPI number for TLB Shootdowns /// -/// This is arbitrarily defined (range: 0..16) +/// Note: This is arbitrarily defined in the range 0..16, +/// which is reserved for IPIs (SGIs - for software generated +/// interrupts - in GIC terminology) pub const TLB_SHOOTDOWN_IPI: InterruptNumber = 2; const MAX_IRQ_NUM: usize = 256; From 263707cff1a079a12cd61102325632d02af7b03e Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Thu, 30 Mar 2023 17:41:08 +0200 Subject: [PATCH 12/25] Fix error messages in interrupts::aarch64 --- kernel/interrupts/src/aarch64/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/interrupts/src/aarch64/mod.rs b/kernel/interrupts/src/aarch64/mod.rs index 0f4a71bdc..b095941c7 100644 --- a/kernel/interrupts/src/aarch64/mod.rs +++ b/kernel/interrupts/src/aarch64/mod.rs @@ -33,7 +33,7 @@ pub const CPU_LOCAL_TIMER_IRQ: InterruptNumber = 30; /// /// Note: This is arbitrarily defined in the range 0..16, /// which is reserved for IPIs (SGIs - for software generated -/// interrupts - in GIC terminology) +/// interrupts - in GIC terminology). pub const TLB_SHOOTDOWN_IPI: InterruptNumber = 2; const MAX_IRQ_NUM: usize = 256; @@ -171,7 +171,7 @@ pub fn init() -> Result<(), &'static str> { /// This function registers an interrupt handler for the CPU-local /// timer and handles GIC configuration for the timer interrupt. pub fn init_timer(timer_tick_handler: HandlerFunc) -> Result<(), &'static str> { - // register/deregister the handler for the timer IRQ. + // register the handler for the timer IRQ. if let Err(existing_handler) = register_interrupt(CPU_LOCAL_TIMER_IRQ, timer_tick_handler) { if timer_tick_handler as *const HandlerFunc != existing_handler { return Err("A different interrupt handler has already been setup for the timer IRQ number"); @@ -195,10 +195,10 @@ pub fn init_timer(timer_tick_handler: HandlerFunc) -> Result<(), &'static str> { pub fn setup_ipi_handler(handler: HandlerFunc, irq_num: InterruptNumber) -> Result<(), &'static str> { assert!(irq_num < 16, "Inter-processor interrupts must have a number in the range 0..16"); - // register/deregister the handler for the timer IRQ. + // register the handler if let Err(existing_handler) = register_interrupt(irq_num, handler) { if handler as *const HandlerFunc != existing_handler { - return Err("A different interrupt handler has already been setup for the timer IRQ number"); + return Err("A different interrupt handler has already been setup for that IPI"); } } From 801f50ffbf5d1ee449375a28f39a701486509a0b Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Thu, 30 Mar 2023 17:42:09 +0200 Subject: [PATCH 13/25] Better panic message in tlb_shootdown --- kernel/tlb_shootdown/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/tlb_shootdown/src/lib.rs b/kernel/tlb_shootdown/src/lib.rs index 1691b839f..87ae397dc 100644 --- a/kernel/tlb_shootdown/src/lib.rs +++ b/kernel/tlb_shootdown/src/lib.rs @@ -83,7 +83,8 @@ fn broadcast_tlb_shootdown(pages_to_invalidate: PageRange) { TLB_SHOOTDOWN_IPI_COUNT.store(cpu_count - 1, Ordering::SeqCst); // -1 to exclude this core #[cfg(target_arch = "x86_64")] { - let my_lapic = apic::get_my_apic().expect("If there are more than one CPU ready, this one should be registered"); + let my_lapic = apic::get_my_apic() + .expect("BUG: broadcast_tlb_shootdown(): couldn't get LocalApic"); // use NMI, since it will interrupt everyone forcibly and result in the fastest handling my_lapic.write().send_nmi_ipi(apic::LapicIpiDestination::AllButMe); // send IPI to all other cores but this one From cecbfd80cf024ee327bd93c995ae678327edae25 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 5 Apr 2023 18:22:14 +0200 Subject: [PATCH 14/25] Introduce SpiDestination and IpiTargetCpu to replace TargetCpu in GIC driver --- kernel/arm_boards/src/mpidr.rs | 13 ++++ kernel/cpu/src/aarch64.rs | 4 +- kernel/gic/src/gic/cpu_interface_gicv3.rs | 56 +++++++------- kernel/gic/src/gic/dist_interface.rs | 91 ++++++++++------------- kernel/gic/src/gic/mod.rs | 31 ++++---- 5 files changed, 96 insertions(+), 99 deletions(-) diff --git a/kernel/arm_boards/src/mpidr.rs b/kernel/arm_boards/src/mpidr.rs index 958b1a67a..5fa26923b 100644 --- a/kernel/arm_boards/src/mpidr.rs +++ b/kernel/arm_boards/src/mpidr.rs @@ -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 { + for def_mpidr in crate::BOARD_CONFIG.cpu_ids { + if def_mpidr.value() == mpidr_value { + return Some(def_mpidr) + } + } + + None +} diff --git a/kernel/cpu/src/aarch64.rs b/kernel/cpu/src/aarch64.rs index 3fe5128a9..fe75f961d 100644 --- a/kernel/cpu/src/aarch64.rs +++ b/kernel/cpu/src/aarch64.rs @@ -80,7 +80,7 @@ impl MpidrValue { /// 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 { + pub fn affinity(self, level: u8) -> u64 { let shift = match level { 0 => 0, 1 => 8, @@ -88,7 +88,7 @@ impl MpidrValue { 3 => 32, _ => panic!("Valid affinity levels are 0, 1, 2, 3"), }; - (self.0 >> shift) as u8 + (self.0 >> shift) & 0xff } } diff --git a/kernel/gic/src/gic/cpu_interface_gicv3.rs b/kernel/gic/src/gic/cpu_interface_gicv3.rs index 3de6a1f7b..c34ba0b6d 100644 --- a/kernel/gic/src/gic/cpu_interface_gicv3.rs +++ b/kernel/gic/src/gic/cpu_interface_gicv3.rs @@ -8,11 +8,11 @@ //! - 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 SGIR_TARGET_ALL_OTHER_PE: u64 = 1 << 40; const IGRPEN_ENABLED: usize = 1; /// Enables routing of group 1 interrupts for the current CPU and configures @@ -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(3) << 48; + + // level 2 affinity in bits [32:39] + let aff2 = mpidr.affinity(2) << 32; + + // level 1 affinity in bits [16:23] + let aff1 = mpidr.affinity(1) << 16; + + // level 0 affinity as a GICv2-style target list + let aff0 = mpidr.affinity(0); + 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) }; } \ No newline at end of file diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index 158d72e51..c9fece47f 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -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); @@ -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. @@ -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.bits as u32) << 16, }; let value: u32 = int_num | target_list | SGIR_NSATT_GRP1; @@ -138,28 +138,25 @@ 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 { 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(list) } else if let Self::V3(v3) = self { let reg = v3.dist_extended.read_volatile_64(offset::P6IROUTER); @@ -167,53 +164,41 @@ impl super::ArmGic { // 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 => 0xff, + SpiDestination::GICv2TargetList(list) => list.bits 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!"); }, }; diff --git a/kernel/gic/src/gic/mod.rs b/kernel/gic/src/gic/mod.rs index f70fba2e3..00c75a86d 100644 --- a/kernel/gic/src/gic/mod.rs +++ b/kernel/gic/src/gic/mod.rs @@ -39,19 +39,24 @@ bitflags! { } } -pub enum TargetCpu { - /// That interrupt must be handled by - /// a specific PE in the system. - /// - /// - level 3 affinity is expected in bits [24:31] - /// - level 2 affinity is expected in bits [16:23] - /// - level 1 affinity is expected in bits [8:15] - /// - level 0 affinity is expected in bits [0:7] - Specific(u32), - /// That interrupt can be handled by - /// any PE that is not busy with another, - /// more important task +/// Target of a shared-peripheral interrupt +pub enum SpiDestination { + /// The interrupt must be delivered to a specific CPU. + Specific(CpuId), + /// That interrupt can be handled by any PE that is not busy with another, more + /// important task AnyCpuAvailable, + /// The interrupt will be delivered to all CPUs specified by the included target list + GICv2TargetList(TargetList), +} + +/// Target of an inter-processor interrupt +pub enum IpiTargetCpu { + /// The interrupt will be delivered to a specific CPU. + Specific(CpuId), + /// The interrupt will be delivered to all CPUs except the sender. + AllOtherCpus, + /// The interrupt will be delivered to all CPUs specified by the included target list GICv2TargetList(TargetList), } @@ -288,7 +293,7 @@ impl ArmGic { /// also called software generated interrupt (SGI). /// /// note: on Aarch64, IPIs must have a number below 16 on ARMv8 - pub fn send_ipi(&mut self, int_num: InterruptNumber, target: TargetCpu) { + pub fn send_ipi(&mut self, int_num: InterruptNumber, target: IpiTargetCpu) { assert!(int_num < 16, "IPIs must have a number below 16 on ARMv8"); match self { From ea212c694849d336d503f97d6a04ea775b2e16d8 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 5 Apr 2023 18:24:03 +0200 Subject: [PATCH 15/25] Change TargetList so that it can only contain valid `CpuId`s --- Cargo.lock | 1 - kernel/gic/Cargo.toml | 1 - kernel/gic/src/gic/dist_interface.rs | 7 ++-- kernel/gic/src/gic/mod.rs | 63 +++++++++++++++++++++------- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1020a0731..aea972dcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1346,7 +1346,6 @@ name = "gic" version = "0.1.0" dependencies = [ "arm_boards", - "bitflags", "cpu", "log", "memory", diff --git a/kernel/gic/Cargo.toml b/kernel/gic/Cargo.toml index 8a5e5202d..6b37e010c 100644 --- a/kernel/gic/Cargo.toml +++ b/kernel/gic/Cargo.toml @@ -7,7 +7,6 @@ name = "gic" [dependencies] static_assertions = "1.1.0" -bitflags = "1.1.0" zerocopy = "0.5.0" log = "0.4.8" diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index c9fece47f..b4394a521 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -116,7 +116,7 @@ pub fn send_ipi_gicv2(registers: &mut GicRegisters, int_num: u32, target: IpiTar let target_list = match target { IpiTargetCpu::Specific(cpu) => (1 << cpu.value()) << 16, IpiTargetCpu::AllOtherCpus => SGIR_TARGET_ALL_OTHER_PE, - IpiTargetCpu::GICv2TargetList(list) => (list.bits as u32) << 16, + IpiTargetCpu::GICv2TargetList(list) => (list.0 as u32) << 16, }; let value: u32 = int_num | target_list | SGIR_NSATT_GRP1; @@ -155,8 +155,7 @@ impl super::ArmGic { } } - let list = TargetList::from_bits_truncate(flags as u8); - SpiDestination::GICv2TargetList(list) + SpiDestination::GICv2TargetList(TargetList(flags as u8)) } else if let Self::V3(v3) = self { let reg = v3.dist_extended.read_volatile_64(offset::P6IROUTER); @@ -187,7 +186,7 @@ impl super::ArmGic { let value = match target { SpiDestination::Specific(cpu) => 1 << cpu.value(), SpiDestination::AnyCpuAvailable => 0xff, - SpiDestination::GICv2TargetList(list) => list.bits as u32, + SpiDestination::GICv2TargetList(list) => list.0 as u32, }; self.distributor_mut().write_array_volatile::<4>(offset::ITARGETSR, int, value); diff --git a/kernel/gic/src/gic/mod.rs b/kernel/gic/src/gic/mod.rs index 00c75a86d..e4dac3963 100644 --- a/kernel/gic/src/gic/mod.rs +++ b/kernel/gic/src/gic/mod.rs @@ -1,13 +1,13 @@ use core::convert::AsMut; -use cpu::CpuId; +use cpu::{CpuId, MpidrValue}; +use arm_boards::mpidr::find_mpidr; use memory::{ PageTable, BorrowedMappedPages, Mutable, PhysicalAddress, PteFlags, allocate_pages, allocate_frames_at }; use static_assertions::const_assert_eq; -use bitflags::bitflags; mod cpu_interface_gicv3; mod cpu_interface_gicv2; @@ -23,19 +23,52 @@ pub type InterruptNumber = u32; /// 8-bit unsigned integer pub type Priority = u8; -bitflags! { - /// The legacy (GICv2) way of specifying - /// multiple target CPUs - pub struct TargetList: u8 { - const CPU_0 = 1 << 0; - const CPU_1 = 1 << 1; - const CPU_2 = 1 << 2; - const CPU_3 = 1 << 3; - const CPU_4 = 1 << 4; - const CPU_5 = 1 << 5; - const CPU_6 = 1 << 6; - const CPU_7 = 1 << 7; - const ALL_CPUS = u8::MAX; +#[derive(Copy, Clone, Debug)] +pub struct TargetList(u8); + +impl TargetList { + pub fn new(list: &[CpuId]) -> Result { + let mut this = 0; + + for cpu_id in list { + let mpidr = MpidrValue::from(*cpu_id).value(); + if mpidr < 8 { + this |= 1 << mpidr; + } else { + return Err("CPU id is too big for GICv2 (should be < 8)"); + } + } + + Ok(Self(this)) + } + + pub fn iter(self) -> TargetListIterator { + TargetListIterator { + bitfield: self.0, + shift: 0, + } + } +} + +pub struct TargetListIterator { + bitfield: u8, + shift: u8, +} + +impl Iterator for TargetListIterator { + type Item = Option; + fn next(&mut self) -> Option { + while ((self.bitfield >> self.shift) & 1 == 0) && self.shift < 8 { + self.shift += 1; + } + + if self.shift < 8 { + let def_mpidr = find_mpidr(self.shift as u64); + self.shift += 1; + Some(def_mpidr.map(|m| m.into())) + } else { + None + } } } From ddf05f1d8050c8528bd96bae52a957575f9f106f Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 5 Apr 2023 18:26:37 +0200 Subject: [PATCH 16/25] Introduce AffinityShift for safer affinity level readings from an MpidrValue --- kernel/cpu/src/aarch64.rs | 24 ++++++++++++++--------- kernel/gic/src/gic/cpu_interface_gicv3.rs | 8 ++++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/kernel/cpu/src/aarch64.rs b/kernel/cpu/src/aarch64.rs index fe75f961d..cbda410ae 100644 --- a/kernel/cpu/src/aarch64.rs +++ b/kernel/cpu/src/aarch64.rs @@ -71,6 +71,19 @@ 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 { @@ -80,15 +93,8 @@ impl MpidrValue { /// 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) -> u64 { - let shift = match level { - 0 => 0, - 1 => 8, - 2 => 16, - 3 => 32, - _ => panic!("Valid affinity levels are 0, 1, 2, 3"), - }; - (self.0 >> shift) & 0xff + pub fn affinity(self, level: AffinityShift) -> u64 { + (self.0 >> (level as u64)) & (u8::MAX as u64) } } diff --git a/kernel/gic/src/gic/cpu_interface_gicv3.rs b/kernel/gic/src/gic/cpu_interface_gicv3.rs index c34ba0b6d..7639a3c3c 100644 --- a/kernel/gic/src/gic/cpu_interface_gicv3.rs +++ b/kernel/gic/src/gic/cpu_interface_gicv3.rs @@ -88,16 +88,16 @@ pub fn send_ipi(int_num: InterruptNumber, target: IpiTargetCpu) { let mpidr: cpu::MpidrValue = cpu.into(); // level 3 affinity in bits [48:55] - let aff3 = mpidr.affinity(3) << 48; + let aff3 = mpidr.affinity(cpu::AffinityShift::LevelThree) << 48; // level 2 affinity in bits [32:39] - let aff2 = mpidr.affinity(2) << 32; + let aff2 = mpidr.affinity(cpu::AffinityShift::LevelTwo) << 32; // level 1 affinity in bits [16:23] - let aff1 = mpidr.affinity(1) << 16; + let aff1 = mpidr.affinity(cpu::AffinityShift::LevelOne) << 16; // level 0 affinity as a GICv2-style target list - let aff0 = mpidr.affinity(0); + 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, From 25e20a7772f62c299f043766cbff8b0efe313e21 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 5 Apr 2023 18:29:23 +0200 Subject: [PATCH 17/25] Use u64 instead of usize --- kernel/gic/src/gic/cpu_interface_gicv3.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/gic/src/gic/cpu_interface_gicv3.rs b/kernel/gic/src/gic/cpu_interface_gicv3.rs index 7639a3c3c..b5ee7d399 100644 --- a/kernel/gic/src/gic/cpu_interface_gicv3.rs +++ b/kernel/gic/src/gic/cpu_interface_gicv3.rs @@ -13,12 +13,12 @@ use super::Priority; use super::InterruptNumber; const SGIR_TARGET_ALL_OTHER_PE: u64 = 1 << 40; -const IGRPEN_ENABLED: usize = 1; +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 @@ -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) } @@ -50,7 +50,7 @@ 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) }; } @@ -58,7 +58,7 @@ pub fn set_minimum_priority(priority: Priority) { /// 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) }; } @@ -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. From 2a9a8e20ed2a47a4d0849fc66a99918fa461416b Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 5 Apr 2023 18:43:18 +0200 Subject: [PATCH 18/25] Fix: replaced 0xff with a safer value in gic::ArmGic::set_spi_target --- kernel/gic/src/gic/dist_interface.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index b4394a521..170e407d5 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -19,8 +19,8 @@ use super::InterruptNumber; use super::Enabled; use super::TargetList; -use arm_boards::mpidr::find_mpidr; -use cpu::MpidrValue; +use arm_boards::{mpidr::find_mpidr, NUM_CPUS, BOARD_CONFIG}; +use cpu::{CpuId, MpidrValue}; mod offset { use crate::{Offset32, Offset64}; @@ -185,7 +185,12 @@ impl super::ArmGic { let value = match target { SpiDestination::Specific(cpu) => 1 << cpu.value(), - SpiDestination::AnyCpuAvailable => 0xff, + SpiDestination::AnyCpuAvailable => { + let cpu_ids: [CpuId; NUM_CPUS] = core::array::from_fn(|i| { + CpuId::from(BOARD_CONFIG.cpu_ids[i]) + }); + TargetList::new(&cpu_ids).unwrap().0 as u32 + }, SpiDestination::GICv2TargetList(list) => list.0 as u32, }; From 3e1b2af99377c18dd07c0c96b2c2904a96a261f6 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 5 Apr 2023 18:52:47 +0200 Subject: [PATCH 19/25] Introduced gic::TargetList::new_all_cpus & SpiDestination::canonicalize --- kernel/gic/src/gic/dist_interface.rs | 13 ++++--------- kernel/gic/src/gic/mod.rs | 27 +++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index 170e407d5..49490f4c6 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -19,8 +19,8 @@ use super::InterruptNumber; use super::Enabled; use super::TargetList; -use arm_boards::{mpidr::find_mpidr, NUM_CPUS, BOARD_CONFIG}; -use cpu::{CpuId, MpidrValue}; +use arm_boards::mpidr::find_mpidr; +use cpu::MpidrValue; mod offset { use crate::{Offset32, Offset64}; @@ -155,7 +155,7 @@ impl super::ArmGic { } } - SpiDestination::GICv2TargetList(TargetList(flags as u8)) + SpiDestination::GICv2TargetList(TargetList(flags as u8)).canonicalize() } else if let Self::V3(v3) = self { let reg = v3.dist_extended.read_volatile_64(offset::P6IROUTER); @@ -185,12 +185,7 @@ impl super::ArmGic { let value = match target { SpiDestination::Specific(cpu) => 1 << cpu.value(), - SpiDestination::AnyCpuAvailable => { - let cpu_ids: [CpuId; NUM_CPUS] = core::array::from_fn(|i| { - CpuId::from(BOARD_CONFIG.cpu_ids[i]) - }); - TargetList::new(&cpu_ids).unwrap().0 as u32 - }, + SpiDestination::AnyCpuAvailable => TargetList::new_all_cpus().0 as u32, SpiDestination::GICv2TargetList(list) => list.0 as u32, }; diff --git a/kernel/gic/src/gic/mod.rs b/kernel/gic/src/gic/mod.rs index e4dac3963..d22ce3139 100644 --- a/kernel/gic/src/gic/mod.rs +++ b/kernel/gic/src/gic/mod.rs @@ -1,7 +1,7 @@ use core::convert::AsMut; use cpu::{CpuId, MpidrValue}; -use arm_boards::mpidr::find_mpidr; +use arm_boards::{mpidr::find_mpidr, NUM_CPUS, BOARD_CONFIG}; use memory::{ PageTable, BorrowedMappedPages, Mutable, PhysicalAddress, PteFlags, allocate_pages, allocate_frames_at @@ -23,7 +23,7 @@ pub type InterruptNumber = u32; /// 8-bit unsigned integer pub type Priority = u8; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct TargetList(u8); impl TargetList { @@ -42,6 +42,14 @@ impl TargetList { Ok(Self(this)) } + pub fn new_all_cpus() -> Self { + let cpu_ids: [CpuId; NUM_CPUS] = core::array::from_fn(|i| { + CpuId::from(BOARD_CONFIG.cpu_ids[i]) + }); + + Self::new(&cpu_ids).unwrap() + } + pub fn iter(self) -> TargetListIterator { TargetListIterator { bitfield: self.0, @@ -93,6 +101,21 @@ pub enum IpiTargetCpu { GICv2TargetList(TargetList), } +impl SpiDestination { + /// When this is a GICv2TargetList, converts the list to + /// `AnyCpuAvailable` if the list contains all CPUs. + pub fn canonicalize(self) -> Self { + match self { + Self::Specific(cpu_id) => Self::Specific(cpu_id), + Self::AnyCpuAvailable => Self::AnyCpuAvailable, + Self::GICv2TargetList(list) => match TargetList::new_all_cpus() == list { + true => Self::AnyCpuAvailable, + false => Self::GICv2TargetList(list), + }, + } + } +} + const U32BITS: usize = u32::BITS as usize; #[derive(Copy, Clone)] From 157059169034a67c8b90ea1340a5224e1b50e6ec Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 5 Apr 2023 18:59:32 +0200 Subject: [PATCH 20/25] Fix gic::TargetList::new_all_cpus --- kernel/gic/src/gic/dist_interface.rs | 6 +++++- kernel/gic/src/gic/mod.rs | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index 49490f4c6..e75a52f2d 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -185,7 +185,11 @@ impl super::ArmGic { let value = match target { SpiDestination::Specific(cpu) => 1 << cpu.value(), - SpiDestination::AnyCpuAvailable => TargetList::new_all_cpus().0 as u32, + 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, }; diff --git a/kernel/gic/src/gic/mod.rs b/kernel/gic/src/gic/mod.rs index d22ce3139..929867f70 100644 --- a/kernel/gic/src/gic/mod.rs +++ b/kernel/gic/src/gic/mod.rs @@ -42,12 +42,13 @@ impl TargetList { Ok(Self(this)) } - pub fn new_all_cpus() -> Self { + /// Tries to create a `TargetList` from `arm_boards::BOARD_CONFIG.cpu_ids` + pub fn new_all_cpus() -> Result { let cpu_ids: [CpuId; NUM_CPUS] = core::array::from_fn(|i| { CpuId::from(BOARD_CONFIG.cpu_ids[i]) }); - Self::new(&cpu_ids).unwrap() + Self::new(&cpu_ids).map_err(|_| "Some CPUs in the system cannot be stored in a TargetList") } pub fn iter(self) -> TargetListIterator { @@ -108,7 +109,7 @@ impl SpiDestination { match self { Self::Specific(cpu_id) => Self::Specific(cpu_id), Self::AnyCpuAvailable => Self::AnyCpuAvailable, - Self::GICv2TargetList(list) => match TargetList::new_all_cpus() == list { + Self::GICv2TargetList(list) => match TargetList::new_all_cpus() == Ok(list) { true => Self::AnyCpuAvailable, false => Self::GICv2TargetList(list), }, From 1f6978e7c0eb3c393d491218b693648e0551bb9a Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 5 Apr 2023 19:06:33 +0200 Subject: [PATCH 21/25] Fix doc of MpidrValue::affinity --- kernel/cpu/src/aarch64.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/cpu/src/aarch64.rs b/kernel/cpu/src/aarch64.rs index cbda410ae..d93887165 100644 --- a/kernel/cpu/src/aarch64.rs +++ b/kernel/cpu/src/aarch64.rs @@ -91,8 +91,6 @@ impl MpidrValue { } /// 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: AffinityShift) -> u64 { (self.0 >> (level as u64)) & (u8::MAX as u64) } From d25eda3785c84118efe6bbbf193455f7922e4cfe Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Wed, 5 Apr 2023 19:17:57 +0200 Subject: [PATCH 22/25] Fix merge mistakes --- Cargo.lock | 5 +++- kernel/interrupts/src/aarch64/mod.rs | 45 ++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aea972dcb..42a4c4347 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4085,10 +4085,13 @@ name = "tlb_shootdown" version = "0.1.0" dependencies = [ "apic", + "cpu", + "interrupts", "irq_safety", "log", "memory", - "x86_64", + "memory_aarch64", + "memory_x86_64", ] [[package]] diff --git a/kernel/interrupts/src/aarch64/mod.rs b/kernel/interrupts/src/aarch64/mod.rs index 852664689..40ddd570c 100644 --- a/kernel/interrupts/src/aarch64/mod.rs +++ b/kernel/interrupts/src/aarch64/mod.rs @@ -9,8 +9,8 @@ use tock_registers::interfaces::Readable; use tock_registers::registers::InMemoryRegister; use kernel_config::time::CONFIG_TIMESLICE_PERIOD_MICROSECONDS; +use gic::{ArmGic, InterruptNumber, IpiTargetCpu, Version as GicVersion}; use arm_boards::{BOARD_CONFIG, InterruptControllerConfig}; -use gic::{ArmGic, InterruptNumber, Version as GicVersion}; use irq_safety::{RwLockIrqSafe, MutexIrqSafe}; use memory::get_kernel_mmi_ref; use log::{info, error}; @@ -30,6 +30,13 @@ static INTERRUPT_CONTROLLER: MutexIrqSafe> = MutexIrqSafe::new(No // aarch64 manuals define the default timer IRQ number to be 30. pub const CPU_LOCAL_TIMER_IRQ: InterruptNumber = 30; +/// The IRQ/IPI number for TLB Shootdowns +/// +/// Note: This is arbitrarily defined in the range 0..16, +/// which is reserved for IPIs (SGIs - for software generated +/// interrupts - in GIC terminology). +pub const TLB_SHOOTDOWN_IPI: InterruptNumber = 2; + const MAX_IRQ_NUM: usize = 256; // Singleton which acts like an x86-style Interrupt Descriptor Table: @@ -175,7 +182,7 @@ pub fn init() -> Result<(), &'static str> { } /// This function registers an interrupt handler for the CPU-local -/// timer and handles INTERRUPT_CONTROLLER configuration for the timer interrupt. +/// timer and handles interrupt controller configuration for the timer interrupt. pub fn init_timer(timer_tick_handler: HandlerFunc) -> Result<(), &'static str> { // register/deregister the handler for the timer IRQ. if let Err(existing_handler) = register_interrupt(CPU_LOCAL_TIMER_IRQ, timer_tick_handler) { @@ -196,6 +203,30 @@ pub fn init_timer(timer_tick_handler: HandlerFunc) -> Result<(), &'static str> { Ok(()) } +/// This function registers an interrupt handler for an inter-processor interrupt +/// and handles interrupt controller configuration for that interrupt. +pub fn setup_ipi_handler(handler: HandlerFunc, irq_num: InterruptNumber) -> Result<(), &'static str> { + assert!(irq_num < 16, "Inter-processor interrupts must have a number in the range 0..16"); + + // register the handler + if let Err(existing_handler) = register_interrupt(irq_num, handler) { + if handler as *const HandlerFunc != existing_handler { + return Err("A different interrupt handler has already been setup for that IPI"); + } + } + + // Route the IRQ to this core (implicit as irq_num < 32) & Enable the interrupt. + { + let mut int_ctrl = INTERRUPT_CONTROLLER.lock(); + let int_ctrl = int_ctrl.as_mut().ok_or("INTERRUPT_CONTROLLER is uninitialized")?; + + // enable routing of this interrupt + int_ctrl.set_interrupt_state(irq_num, true); + } + + Ok(()) +} + /// Disables the timer, schedules its next tick, and re-enables it pub fn schedule_next_timer_tick() { enable_timer(false); @@ -275,6 +306,14 @@ pub fn deregister_interrupt(irq_num: InterruptNumber, func: HandlerFunc) -> Resu } } +/// Broadcast an Inter-Processor Interrupt to all other +/// cores in the system +pub fn send_ipi_to_all_other_cpus(irq_num: InterruptNumber) { + let mut int_ctrl = INTERRUPT_CONTROLLER.lock(); + let int_ctrl = int_ctrl.as_mut().expect("INTERRUPT_CONTROLLER is uninitialized"); + int_ctrl.send_ipi(irq_num, IpiTargetCpu::AllOtherCpus); +} + /// Send an "end of interrupt" signal, notifying the interrupt chip that /// the given interrupt request `irq` has been serviced. pub fn eoi(irq_num: InterruptNumber) { @@ -395,7 +434,7 @@ extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) { extern "C" fn current_elx_irq(exc: &mut ExceptionContext) { // read IRQ num // read IRQ priority - // ackownledge IRQ to the INTERRUPT_CONTROLLER + // ackownledge IRQ to the interrupt controller let (irq_num, _priority) = { let mut int_ctrl = INTERRUPT_CONTROLLER.lock(); let int_ctrl = int_ctrl.as_mut().expect("INTERRUPT_CONTROLLER is uninitialized"); From a59ad9801d266ace27d0d3636d2c0266ce8658c9 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Thu, 6 Apr 2023 10:00:38 +0200 Subject: [PATCH 23/25] Take an iterator instead of a slice in gic::TargetList::new --- kernel/gic/src/gic/mod.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/kernel/gic/src/gic/mod.rs b/kernel/gic/src/gic/mod.rs index 929867f70..0e922f0f3 100644 --- a/kernel/gic/src/gic/mod.rs +++ b/kernel/gic/src/gic/mod.rs @@ -1,7 +1,7 @@ use core::convert::AsMut; use cpu::{CpuId, MpidrValue}; -use arm_boards::{mpidr::find_mpidr, NUM_CPUS, BOARD_CONFIG}; +use arm_boards::{mpidr::find_mpidr, BOARD_CONFIG}; use memory::{ PageTable, BorrowedMappedPages, Mutable, PhysicalAddress, PteFlags, allocate_pages, allocate_frames_at @@ -27,11 +27,11 @@ pub type Priority = u8; pub struct TargetList(u8); impl TargetList { - pub fn new(list: &[CpuId]) -> Result { + pub fn new>(list: T) -> Result { let mut this = 0; - for cpu_id in list { - let mpidr = MpidrValue::from(*cpu_id).value(); + for mpidr in list { + let mpidr = mpidr.value(); if mpidr < 8 { this |= 1 << mpidr; } else { @@ -44,11 +44,8 @@ impl TargetList { /// Tries to create a `TargetList` from `arm_boards::BOARD_CONFIG.cpu_ids` pub fn new_all_cpus() -> Result { - let cpu_ids: [CpuId; NUM_CPUS] = core::array::from_fn(|i| { - CpuId::from(BOARD_CONFIG.cpu_ids[i]) - }); - - Self::new(&cpu_ids).map_err(|_| "Some CPUs in the system cannot be stored in a TargetList") + let list = BOARD_CONFIG.cpu_ids.iter().map(|def_mpidr| (*def_mpidr).into()); + Self::new(list).map_err(|_| "Some CPUs in the system cannot be stored in a TargetList") } pub fn iter(self) -> TargetListIterator { From 6d0fb81a86e12ebee7b6316d87ef2baffe2fc761 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Thu, 6 Apr 2023 10:02:30 +0200 Subject: [PATCH 24/25] Move `Some()` to if body --- kernel/gic/src/gic/dist_interface.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index e75a52f2d..267ee9e6c 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -145,7 +145,7 @@ impl super::ArmGic { /// of that destination is not checked. pub fn get_spi_target(&self, int: InterruptNumber) -> Option { assert!(int >= 32, "interrupts number below 32 (SGIs & PPIs) don't have a target CPU"); - Some(if !self.affinity_routing() { + if !self.affinity_routing() { let flags = self.distributor().read_array_volatile::<4>(offset::ITARGETSR, int); for i in 0..8 { @@ -155,7 +155,7 @@ impl super::ArmGic { } } - SpiDestination::GICv2TargetList(TargetList(flags as u8)).canonicalize() + Some(SpiDestination::GICv2TargetList(TargetList(flags as u8)).canonicalize()) } else if let Self::V3(v3) = self { let reg = v3.dist_extended.read_volatile_64(offset::P6IROUTER); @@ -163,16 +163,16 @@ impl super::ArmGic { // value of 1 to target any available cpu // value of 0 to target a specific cpu if reg & P6IROUTER_ANY_AVAILABLE_PE > 0 { - SpiDestination::AnyCpuAvailable + Some(SpiDestination::AnyCpuAvailable) } else { let mpidr = reg & 0xff00ffffff; - SpiDestination::Specific(find_mpidr(mpidr)?.into()) + Some(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!() - }) + } } /// Sets the destination of an SPI. From 18fff53036aefe85e2c0a673ae488e4c62a05202 Mon Sep 17 00:00:00 2001 From: Nathan Royer Date: Thu, 6 Apr 2023 10:14:14 +0200 Subject: [PATCH 25/25] Replace arm_boards::find_mpidr with cpu::MpidrValue::try_from --- kernel/arm_boards/src/mpidr.rs | 13 ------------- kernel/cpu/src/aarch64.rs | 18 +++++++++++++++++- kernel/gic/src/gic/dist_interface.rs | 14 ++++++++------ kernel/gic/src/gic/mod.rs | 14 +++++++------- 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/kernel/arm_boards/src/mpidr.rs b/kernel/arm_boards/src/mpidr.rs index 5fa26923b..958b1a67a 100644 --- a/kernel/arm_boards/src/mpidr.rs +++ b/kernel/arm_boards/src/mpidr.rs @@ -23,16 +23,3 @@ 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 { - for def_mpidr in crate::BOARD_CONFIG.cpu_ids { - if def_mpidr.value() == mpidr_value { - return Some(def_mpidr) - } - } - - None -} diff --git a/kernel/cpu/src/aarch64.rs b/kernel/cpu/src/aarch64.rs index d93887165..f85abb548 100644 --- a/kernel/cpu/src/aarch64.rs +++ b/kernel/cpu/src/aarch64.rs @@ -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; @@ -128,6 +128,22 @@ impl From for CpuId { } } +impl TryFrom 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 { + 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, 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 diff --git a/kernel/gic/src/gic/dist_interface.rs b/kernel/gic/src/gic/dist_interface.rs index 267ee9e6c..adddb867f 100644 --- a/kernel/gic/src/gic/dist_interface.rs +++ b/kernel/gic/src/gic/dist_interface.rs @@ -19,7 +19,6 @@ use super::InterruptNumber; use super::Enabled; use super::TargetList; -use arm_boards::mpidr::find_mpidr; use cpu::MpidrValue; mod offset { @@ -143,7 +142,7 @@ impl super::ArmGic { /// /// Note: If the destination is a `GICv2TargetList`, the validity /// of that destination is not checked. - pub fn get_spi_target(&self, int: InterruptNumber) -> Option { + pub fn get_spi_target(&self, int: InterruptNumber) -> Result { 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); @@ -151,11 +150,13 @@ impl super::ArmGic { for i in 0..8 { let target = 1 << i; if target & flags == flags { - return Some(SpiDestination::Specific(find_mpidr(i)?.into())); + let mpidr = i; + let cpu_id = MpidrValue::try_from(mpidr)?.into(); + return Ok(SpiDestination::Specific(cpu_id)); } } - Some(SpiDestination::GICv2TargetList(TargetList(flags as u8)).canonicalize()) + 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); @@ -163,10 +164,11 @@ impl super::ArmGic { // value of 1 to target any available cpu // value of 0 to target a specific cpu if reg & P6IROUTER_ANY_AVAILABLE_PE > 0 { - Some(SpiDestination::AnyCpuAvailable) + Ok(SpiDestination::AnyCpuAvailable) } else { let mpidr = reg & 0xff00ffffff; - Some(SpiDestination::Specific(find_mpidr(mpidr)?.into())) + 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 diff --git a/kernel/gic/src/gic/mod.rs b/kernel/gic/src/gic/mod.rs index 0e922f0f3..f16493800 100644 --- a/kernel/gic/src/gic/mod.rs +++ b/kernel/gic/src/gic/mod.rs @@ -1,7 +1,7 @@ use core::convert::AsMut; use cpu::{CpuId, MpidrValue}; -use arm_boards::{mpidr::find_mpidr, BOARD_CONFIG}; +use arm_boards::{BOARD_CONFIG, NUM_CPUS}; use memory::{ PageTable, BorrowedMappedPages, Mutable, PhysicalAddress, PteFlags, allocate_pages, allocate_frames_at @@ -62,14 +62,14 @@ pub struct TargetListIterator { } impl Iterator for TargetListIterator { - type Item = Option; + type Item = Result; fn next(&mut self) -> Option { while ((self.bitfield >> self.shift) & 1 == 0) && self.shift < 8 { self.shift += 1; } if self.shift < 8 { - let def_mpidr = find_mpidr(self.shift as u64); + let def_mpidr = MpidrValue::try_from(self.shift as u64); self.shift += 1; Some(def_mpidr.map(|m| m.into())) } else { @@ -215,7 +215,7 @@ const_assert_eq!(core::mem::size_of::(), 0x1000); /// This is defined in `arm_boards::INTERRUPT_CONTROLLER_CONFIG`. fn get_current_cpu_redist_index() -> usize { let cpu_id = cpu::current_cpu(); - arm_boards::BOARD_CONFIG.cpu_ids.iter() + BOARD_CONFIG.cpu_ids.iter() .position(|mpidr| CpuId::from(*mpidr) == cpu_id) .expect("BUG: get_current_cpu_redist_index: unexpected CpuId for current CPU") } @@ -237,7 +237,7 @@ pub struct ArmGicV3 { pub affinity_routing: Enabled, pub distributor: BorrowedMappedPages, pub dist_extended: BorrowedMappedPages, - pub redistributors: [ArmGicV3RedistPages; arm_boards::NUM_CPUS], + pub redistributors: [ArmGicV3RedistPages; NUM_CPUS], } /// Arm Generic Interrupt Controller @@ -257,7 +257,7 @@ pub enum Version { }, InitV3 { dist: PhysicalAddress, - redist: [PhysicalAddress; arm_boards::NUM_CPUS], + redist: [PhysicalAddress; NUM_CPUS], } } @@ -300,7 +300,7 @@ impl ArmGic { mapped.into_borrowed_mut(0).map_err(|(_, e)| e)? }; - let redistributors: [ArmGicV3RedistPages; arm_boards::NUM_CPUS] = core::array::try_from_fn(|i| { + let redistributors: [ArmGicV3RedistPages; NUM_CPUS] = core::array::try_from_fn(|i| { let phys_addr = redist[i]; let mut redistributor: BorrowedMappedPages = {