Add Arm Generic Interrupt Controller driver
Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: Ib65219f887448b2998a8c6a645408b69c64e6784
diff --git a/Cargo.lock b/Cargo.lock
index 47a16db..a8dccba 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,5 +3,30 @@
version = 3
[[package]]
-name = "gic"
+name = "arm-gic"
version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "volatile-register",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "vcell"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
+
+[[package]]
+name = "volatile-register"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
+dependencies = [
+ "vcell",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 0193630..1a359e3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,10 +2,20 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
[package]
-name = "gic"
+name = "arm-gic"
version = "0.1.0"
+authors = [
+ "Balint Dobszay <balint.dobszay@arm.com>",
+ "Imre Kis <imre.kis@arm.com>",
+]
+description = "Arm Generic Interrupt Controller driver"
edition = "2021"
+license = "MIT OR Apache-2.0"
+repository = "https://git.trustedfirmware.org/rust-spmc/rust-spmc.git"
+keywords = ["arm", "gic", "interrupt", "driver"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+bitflags = "2.6"
+volatile-register = "0.2"
diff --git a/src/lib.rs b/src/lib.rs
index b6af872..fa3e661 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,17 +1,358 @@
-// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
// SPDX-License-Identifier: MIT OR Apache-2.0
-pub fn add(left: usize, right: usize) -> usize {
- left + right
+//! # Arm Generic Interrupt Controller driver
+//!
+//! The driver implementation is based on [Arm Generic Interrupt Controller Architecture
+//! Specification GIC architecture version 3 and version 4](https://developer.arm.com/documentation/ihi0069/hb/).
+
+#![no_std]
+
+use bitflags::bitflags;
+use core::ops::{Deref, Range};
+use volatile_register::{RO, RW, WO};
+
+bitflags! {
+ // Distributor Control Register
+ #[repr(transparent)]
+ #[derive(Copy, Clone)]
+ struct DistributorControlRegister : u32 {
+ /// Register Write Pending
+ const RWP = 1 << 31;
+ /// Enable 1 of N Wakeup Functionality
+ const E1NWF = 1 << 7;
+ /// Disable Security
+ const DS = 1 << 6;
+ /// Affinity Routing Enable, Non-secure state
+ const ARE_NS = 1 << 5;
+ /// Affinity Routing Enable, Secure state
+ const ARE_S = 1 << 4;
+ /// Enable Secure Group 1 interrupts
+ const EnableGrp1S = 1 << 2;
+ /// Enable Non-secure Group 1 interrupts
+ const EnableGrp1NS = 1 << 1;
+ /// Enable Group 0 interrupts
+ const EnableGrp0 = 1 << 0;
+ }
}
-#[cfg(test)]
-mod tests {
- use super::*;
+/// GIC Distributor register map
+#[repr(C, align(4))]
+pub struct GICDRegisters {
+ ctlr: RW<u32>, // 0x0000
+ typer: RO<u32>, // 0x0004
+ iidr: RO<u32>, // 0x0008
+ typer2: RO<u32>, // 0x000c
+ statusr: RW<u32>, // 0x0010
+ reserved_0014: [u32; 3], // 0x0014-0x001c
+ imdef0: [u32; 8], // 0x0020-0x003c
+ setspi_nsr: WO<u32>, // 0x0040
+ reserved_0044: u32, // 0x0044
+ clrspi_nsr: WO<u32>, // 0x0048
+ reserved_004c: u32, // 0x004c
+ setspi_sr: WO<u32>, // 0x0050
+ reserved_0054: u32, // 0x0054
+ clrspi_sr: WO<u32>, // 0x0058
+ reserved_005c: [u32; 9], // 0x005c-0x007c
+ igroupr: [RW<u32>; 32], // 0x0080-0x00fc
+ isenabler: [RW<u32>; 32], // 0x0100-0x017c
+ icenabler: [RW<u32>; 32], // 0x0180-0x01fc
+ ispendr: [RW<u32>; 32], // 0x0200-0x027c
+ icpendr: [RW<u32>; 32], // 0x0280-0x02fc
+ isactiver: [RW<u32>; 32], // 0x0300-0x037c
+ icactiver: [RW<u32>; 32], // 0x0380-0x03fc
+ ipriorityr: [RW<u32>; 256], // 0x0400-0x07f8
+ itargetsr: [RW<u32>; 256], // 0x0800-0x0bfc
+ icfgr: [RW<u32>; 64], // 0x0c00-0x0cfc
+ igrpmodr: [RW<u32>; 32], // 0x0d00-0x0d7c
+ reserved_0d80: [u32; 32], // 0x0d80-0x0dfc
+ nsacr: [RW<u32>; 64], // 0x0e00-0x0efc
+ sgir: WO<u32>, // 0x0f00
+ reserved_0f04: [u32; 3], // 0x0f04-0x0f0c
+ cpendsgir: [RW<u32>; 4], // 0x0f10-0x0f1c
+ spendsgir: [RW<u32>; 4], // 0x0f20-0x0f2c
+ reserved_0f30: [u32; 20], // 0x0f30-0x0f7c
+ inmir: [RW<u32>; 32], // 0x0f80-0x0ffc
+ igroupr_e: [RW<u32>; 32], // 0x1000-0x107c
+ reserved_1080: [u32; 96], // 0x1080-0x1fc
+ isenabler_e: [RW<u32>; 32], // 0x1200-0x127c
+ reserved_1280: [u32; 96], // 0x1280-0x13fc
+ icenabler_e: [RW<u32>; 32], // 0x1400-0x147c
+ reserved_1480: [u32; 96], // 0x1480-0x15fc
+ ispendr_e: [RW<u32>; 32], // 0x1600-0x167c
+ reserved_1680: [u32; 96], // 0x1680-0x17fc
+ icpendr_e: [RW<u32>; 32], // 0x1800-0x187c
+ reserved_1880: [u32; 96], // 0x1880-0x19fc
+ isactive_e: [RW<u32>; 32], // 0x1a00-0x1a7c
+ reserved_1a80: [u32; 96], // 0x1a80-0x1bfc
+ icactive_e: [RW<u32>; 32], // 0x1c00-0x1c7c
+ reserved_1c80: [u32; 224], // 0x1c80-0x1ffc
+ ipriorityr_e: [RW<u32>; 256], // 0x2000-0x23fc
+ reserved_2400: [u32; 768], // 0x2400-0x2ffc
+ icfgr_e: [RW<u32>; 64], // 0x3000-0x30fc
+ reserved_3100: [u32; 192], // 0x3100-0x33fc
+ igrpmodr_e: [RW<u32>; 32], // 0x3400-0x347c
+ reserved_3480: [u32; 96], // 0x3480-0x35fc
+ nsacr_e: [RW<u32>; 32], // 0x3600-0x367c
+ reserved_3680: [u32; 256], // 0x3680-0x3afc
+ inmir_e: [RW<u32>; 64], // 0x3b00-0x3bfc
+ reserved_3b80: [u32; 2400], // 0x3b80-0x60fc
+ irouter: [RW<u32>; 1975], // 0x6100-0x7fd8
+ reserved_7fdc: [u32; 9], // 0x7fdc-0x7ffc
+ irouter_e: [RW<u32>; 2048], // 0x8000-0x9ffc
+ reserved_a000: [u32; 6132], // 0xa000-0xffcc
+ id_registers: [RW<u32>; 12], // 0xffd0-0xfffc
+}
- #[test]
- fn it_works() {
- let result = add(2, 2);
- assert_eq!(result, 4);
+/// # GIC Distributor error type
+#[derive(Debug)]
+pub enum GicDistributorError {
+ InvalidParameter,
+}
+
+/// # GIC Distributor
+///
+/// The Distributor performs interrupt prioritization and distribution of SPIs and SGIs to the
+/// Redistributors and CPU interfaces that are connected to the PEs in the system.
+pub struct GicDistributor<R>
+where
+ R: Deref<Target = GICDRegisters>,
+{
+ gicd: R,
+}
+
+impl<R> GicDistributor<R>
+where
+ R: Deref<Target = GICDRegisters>,
+{
+ const MAX_IT: usize = 32;
+
+ /// Create GIC Distributor instance
+ pub fn new(gicd: R) -> Self {
+ unsafe {
+ for n in 0..Self::MAX_IT {
+ gicd.icenabler[n].write(0xffffffff);
+ gicd.icpendr[n].write(0xffffffff);
+ gicd.igroupr[n].write(0xffffffff);
+ }
+ }
+
+ Self { gicd }
}
+
+ /// Enable Group 0 interrupts
+ pub fn enable_group0(&self) {
+ unsafe {
+ self.gicd
+ .ctlr
+ .modify(|v| v | DistributorControlRegister::EnableGrp0.bits());
+ }
+ }
+
+ /// Enable Secure Group 1 interrupts
+ pub fn enable_secure_group1(&self) {
+ unsafe {
+ self.gicd
+ .ctlr
+ .modify(|v| v | DistributorControlRegister::EnableGrp1S.bits());
+ }
+ }
+
+ /// Enable Non-secure Group 1 interrupts
+ pub fn enable_non_secure_group1(&self) {
+ unsafe {
+ self.gicd
+ .ctlr
+ .modify(|v| v | DistributorControlRegister::EnableGrp1NS.bits());
+ }
+ }
+
+ /// Select a range of interrupts for group 0
+ pub fn select_group0_range(&self, range: Range<usize>) -> Result<(), GicDistributorError> {
+ self.modify_group_range(range, |v, mask| v & !mask)
+ }
+
+ /// Select a range of interrupts for group 1
+ pub fn select_group1_range(&self, range: Range<usize>) -> Result<(), GicDistributorError> {
+ self.modify_group_range(range, |v, mask| v | mask)
+ }
+
+ fn modify_group_range<F>(&self, range: Range<usize>, f: F) -> Result<(), GicDistributorError>
+ where
+ F: Fn(u32, u32) -> u32,
+ {
+ let max_it_index: usize = self.gicd.igroupr.len() * 32;
+
+ if range.start > max_it_index || range.end > max_it_index {
+ return Err(GicDistributorError::InvalidParameter);
+ }
+
+ let (start_reg, start_bit) = Self::index_to_reg_and_bit(range.start);
+ let (end_reg, end_bit) = Self::index_to_reg_and_bit(range.end);
+
+ if start_reg == end_reg {
+ let mask = Self::create_mask(start_bit..end_bit);
+ unsafe {
+ self.gicd.igroupr[start_reg].modify(|v| f(v, mask));
+ }
+ } else {
+ let start_mask = Self::create_mask(start_bit..32);
+ unsafe {
+ self.gicd.igroupr[start_reg].modify(|v| f(v, start_mask));
+ }
+
+ for reg_index in (start_reg + 1)..end_reg {
+ unsafe {
+ self.gicd.igroupr[reg_index].modify(|v| f(v, 0xffff_ffff));
+ }
+ }
+
+ if end_reg < Self::MAX_IT {
+ let end_mask = Self::create_mask(0..end_bit);
+ unsafe {
+ self.gicd.igroupr[end_reg].modify(|v| f(v, end_mask));
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ fn index_to_reg_and_bit(index: usize) -> (usize, usize) {
+ (index >> 5, index & 0x1f)
+ }
+
+ fn create_mask(range: Range<usize>) -> u32 {
+ if range.start <= 31 && range.end > 0 {
+ (0xffffffff << range.start) & (0xffffffff >> (32 - range.end))
+ } else {
+ 0
+ }
+ }
+}
+
+/// # GIC CPU Interface
+///
+/// The GIC architecture supports a CPU interface that provides a register interface to a PE in
+/// the system.
+#[cfg(target_arch = "aarch64")]
+pub struct GicCpuInterface;
+
+#[cfg(target_arch = "aarch64")]
+impl GicCpuInterface {
+ const IGRPEN1_ENABLE: u64 = 0x1;
+
+ /// Provides an interrupt priority filter. Only interrupts with a higher priority than the value
+ /// in this register are signaled to the PE.
+ pub fn set_interrupt_priority_mask(mask: u8) {
+ unsafe {
+ core::arch::asm!("msr ICC_PMR_EL1, {pmr}", pmr = in(reg) mask as u64);
+ }
+ }
+
+ /// Enable Group 1 interrupts for current security state
+ pub fn enable_group1() {
+ unsafe {
+ core::arch::asm!("msr ICC_IGRPEN1_EL1, {igrpen1}", igrpen1 = in(reg)Self::IGRPEN1_ENABLE);
+ }
+ }
+
+ /// Disable Group 1 interrupts for current security state
+ pub fn disable_group1() {
+ unsafe {
+ core::arch::asm!("msr ICC_IGRPEN1_EL1, {igrpen1}", igrpen1 = in(reg)0u64);
+ }
+ }
+}
+
+unsafe impl<R> Sync for GicDistributor<R> where R: Deref<Target = GICDRegisters> {}
+
+#[test]
+fn test_gicd_size() {
+ assert_eq!(core::mem::size_of::<GICDRegisters>(), 0x1_0000);
+}
+
+#[test]
+fn test_bitmask() {
+ assert_eq!(
+ 0x0000_0000,
+ GicDistributor::<&GICDRegisters>::create_mask(0..0)
+ );
+ assert_eq!(
+ 0x0000_0001,
+ GicDistributor::<&GICDRegisters>::create_mask(0..1)
+ );
+ assert_eq!(
+ 0x8000_0000,
+ GicDistributor::<&GICDRegisters>::create_mask(31..32)
+ );
+ assert_eq!(
+ 0x0000_00ff,
+ GicDistributor::<&GICDRegisters>::create_mask(0..8)
+ );
+ assert_eq!(
+ 0xffff_ffff,
+ GicDistributor::<&GICDRegisters>::create_mask(0..32)
+ );
+}
+
+#[test]
+fn test_gicd() {
+ extern crate alloc;
+ use alloc::vec::Vec;
+
+ let area: [u32; 0x4000] = [0; 0x4000];
+ let registers: GICDRegisters = unsafe { core::mem::transmute(area) };
+
+ let gic_distributor = GicDistributor::new(®isters);
+ gic_distributor.select_group0_range(8..16).unwrap();
+ let result: Vec<u32> = registers.igroupr.iter().map(|r| r.read()).collect();
+ let mut expected = [0xffff_ffffu32; 32];
+ expected[0] = 0xffff_00ff;
+ assert_eq!(result.as_slice(), &expected);
+
+ let gic_distributor = GicDistributor::new(®isters);
+ gic_distributor.select_group0_range(8..40).unwrap();
+ let result: Vec<u32> = registers.igroupr.iter().map(|r| r.read()).collect();
+ let mut expected = [0xffff_ffffu32; 32];
+ expected[0] = 0x0000_00ff;
+ expected[1] = 0xffff_ff00;
+ assert_eq!(result.as_slice(), &expected);
+
+ let gic_distributor = GicDistributor::new(®isters);
+ gic_distributor.select_group0_range(8..72).unwrap();
+ let result: Vec<u32> = registers.igroupr.iter().map(|r| r.read()).collect();
+ let mut expected = [0xffff_ffffu32; 32];
+ expected[0] = 0x0000_00ff;
+ expected[1] = 0x0000_0000;
+ expected[2] = 0xffff_ff00;
+ assert_eq!(result.as_slice(), &expected);
+
+ let gic_distributor = GicDistributor::new(®isters);
+ gic_distributor.select_group0_range(8..64).unwrap();
+ let result: Vec<u32> = registers.igroupr.iter().map(|r| r.read()).collect();
+ let mut expected = [0xffff_ffffu32; 32];
+ expected[0] = 0x0000_00ff;
+ expected[1] = 0x0000_0000;
+ assert_eq!(result.as_slice(), &expected);
+
+ let gic_distributor = GicDistributor::new(®isters);
+ gic_distributor.select_group0_range(1..1023).unwrap();
+ let result: Vec<u32> = registers.igroupr.iter().map(|r| r.read()).collect();
+ let mut expected = [0x0000_0000u32; 32];
+ expected[0] = 0x0000_0001;
+ expected[31] = 0x8000_0000;
+ assert_eq!(result.as_slice(), &expected);
+
+ let gic_distributor = GicDistributor::new(®isters);
+ gic_distributor.select_group0_range(0..1024).unwrap();
+ let result: Vec<u32> = registers.igroupr.iter().map(|r| r.read()).collect();
+ let expected = [0x0000_0000u32; 32];
+ assert_eq!(result.as_slice(), &expected);
+
+ let gic_distributor = GicDistributor::new(®isters);
+ assert!(gic_distributor.select_group0_range(0..2048).is_err());
+ let result: Vec<u32> = registers.igroupr.iter().map(|r| r.read()).collect();
+ let expected = [0xffff_ffffu32; 32];
+ assert_eq!(result.as_slice(), &expected);
}