blob: b7567c53081ade98e628fb7df95ba2102d05b820 [file] [log] [blame]
// SPDX-FileCopyrightText: Copyright 2023-2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
// SPDX-License-Identifier: MIT OR Apache-2.0
//! # 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::Range;
use safe_mmio::{
field,
fields::{ReadOnly, ReadPure, ReadPureWrite, ReadWrite, WriteOnly},
UniqueMmioPointer,
};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
struct DistributorControlRegister(u32);
bitflags! {
// Distributor Control Register
impl 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;
}
}
/// GIC Distributor register map
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct GICDRegisters {
/// 0x0000
ctlr: ReadPureWrite<DistributorControlRegister>,
/// 0x0004
typer: ReadPure<u32>,
/// 0x0008
iidr: ReadPure<u32>,
/// 0x000c
typer2: ReadPure<u32>,
/// 0x0010
statusr: ReadPureWrite<u32>,
/// 0x0014-0x001c
reserved_0014: [u32; 3],
/// 0x0020-0x003c
imdef0: [ReadWrite<u32>; 8],
/// 0x0040
setspi_nsr: WriteOnly<u32>,
/// 0x0044
reserved_0044: u32,
/// 0x0048
clrspi_nsr: WriteOnly<u32>,
/// 0x004c
reserved_004c: u32,
/// 0x0050
setspi_sr: WriteOnly<u32>,
/// 0x0054
reserved_0054: u32,
/// 0x0058
clrspi_sr: WriteOnly<u32>,
/// 0x005c-0x007c
reserved_005c: [u32; 9],
/// 0x0080-0x00fc
igroupr: [ReadPureWrite<u32>; 32],
/// 0x0100-0x017c
isenabler: [ReadPureWrite<u32>; 32],
/// 0x0180-0x01fc
icenabler: [ReadPureWrite<u32>; 32],
// 0x0200-0x027c
ispendr: [ReadPureWrite<u32>; 32],
/// 0x0280-0x02fc
icpendr: [ReadPureWrite<u32>; 32],
/// 0x0300-0x037c
isactiver: [ReadPureWrite<u32>; 32],
/// 0x0380-0x03fc
icactiver: [ReadPureWrite<u32>; 32],
/// 0x0400-0x07f8
ipriorityr: [ReadPureWrite<u32>; 256],
/// 0x0800-0x0bfc
itargetsr: [ReadOnly<u32>; 256],
/// 0x0c00-0x0cfc
icfgr: [ReadPureWrite<u32>; 64],
/// 0x0d00-0x0d7c
igrpmodr: [ReadPureWrite<u32>; 32],
/// 0x0d80-0x0dfc
reserved_0d80: [u32; 32],
/// 0x0e00-0x0efc
nsacr: [ReadPureWrite<u32>; 64],
/// 0x0f00
sgir: WriteOnly<u32>,
/// 0x0f04-0x0f0c
reserved_0f04: [u32; 3],
/// 0x0f10-0x0f1c
cpendsgir: [ReadPureWrite<u32>; 4],
/// 0x0f20-0x0f2c
spendsgir: [ReadPureWrite<u32>; 4],
/// 0x0f30-0x0f7c
reserved_0f30: [u32; 20],
/// 0x0f80-0x0ffc
inmir: [ReadPureWrite<u32>; 32],
/// 0x1000-0x107c
igroupr_e: [ReadPureWrite<u32>; 32],
/// 0x1080-0x1fc
reserved_1080: [u32; 96],
/// 0x1200-0x127c
isenabler_e: [ReadPureWrite<u32>; 32],
/// 0x1280-0x13fc
reserved_1280: [u32; 96],
/// 0x1400-0x147c
icenabler_e: [ReadPureWrite<u32>; 32],
/// 0x1480-0x15fc
reserved_1480: [u32; 96],
/// 0x1600-0x167c
ispendr_e: [ReadPureWrite<u32>; 32],
/// 0x1680-0x17fc
reserved_1680: [u32; 96],
/// 0x1800-0x187c
icpendr_e: [ReadPureWrite<u32>; 32],
/// 0x1880-0x19fc
reserved_1880: [u32; 96],
/// 0x1a00-0x1a7c
isactive_e: [ReadPureWrite<u32>; 32],
/// 0x1a80-0x1bfc
reserved_1a80: [u32; 96],
/// 0x1c00-0x1c7c
icactive_e: [ReadPureWrite<u32>; 32],
/// 0x1c80-0x1ffc
reserved_1c80: [u32; 224],
/// 0x2000-0x23fc
ipriorityr_e: [ReadPureWrite<u32>; 256],
/// 0x2400-0x2ffc
reserved_2400: [u32; 768],
/// 0x3000-0x30fc
icfgr_e: [ReadPureWrite<u32>; 64],
/// 0x3100-0x33fc
reserved_3100: [u32; 192],
/// 0x3400-0x347c
igrpmodr_e: [ReadPureWrite<u32>; 32],
/// 0x3480-0x35fc
reserved_3480: [u32; 96],
/// 0x3600-0x367c
nsacr_e: [ReadPureWrite<u32>; 32],
/// 0x3680-0x3afc
reserved_3680: [u32; 256],
/// 0x3b00-0x3bfc
inmir_e: [ReadPureWrite<u32>; 64],
/// 0x3b80-0x60fc
reserved_3b80: [u32; 2400],
/// 0x6100-0x7fd8
irouter: [ReadPureWrite<u32>; 1975],
/// 0x7fdc-0x7ffc
reserved_7fdc: [u32; 9],
/// 0x8000-0x9ffc
irouter_e: [ReadPureWrite<u32>; 2048],
/// 0xa000-0xffcc
reserved_a000: [u32; 6132],
/// 0xffd0-0xfffc
id_registers: [ReadOnly<u32>; 12],
}
/// # 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<'a> {
gicd: UniqueMmioPointer<'a, GICDRegisters>,
}
impl<'a> GicDistributor<'a> {
const MAX_IT: usize = 32;
/// Create GIC Distributor instance
pub fn new(mut gicd: UniqueMmioPointer<'a, GICDRegisters>) -> Self {
for n in 0..Self::MAX_IT {
field!(gicd, icenabler).split()[n].write(0xffff_ffff);
field!(gicd, icpendr).split()[n].write(0xffff_ffff);
field!(gicd, igroupr).split()[n].write(0xffff_ffff);
}
Self { gicd }
}
/// Enable Group 0 interrupts
pub fn enable_group0(&mut self) {
let value = field!(self.gicd, ctlr).read();
field!(self.gicd, ctlr).write(value | DistributorControlRegister::EnableGrp0);
}
/// Enable Secure Group 1 interrupts
pub fn enable_secure_group1(&mut self) {
let value = field!(self.gicd, ctlr).read();
field!(self.gicd, ctlr).write(value | DistributorControlRegister::EnableGrp1S);
}
/// Enable Non-secure Group 1 interrupts
pub fn enable_non_secure_group1(&mut self) {
let value = field!(self.gicd, ctlr).read();
field!(self.gicd, ctlr).write(value | DistributorControlRegister::EnableGrp1NS);
}
/// Select a range of interrupts for group 0
pub fn select_group0_range(&mut 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(&mut self, range: Range<usize>) -> Result<(), GicDistributorError> {
self.modify_group_range(range, |v, mask| v | mask)
}
fn modify_group_range<F>(
&mut self,
range: Range<usize>,
f: F,
) -> Result<(), GicDistributorError>
where
F: Fn(u32, u32) -> u32,
{
let max_it_index: usize = field!(self.gicd, igroupr).split().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);
let mut modify_igroupr = |index: usize, mask| {
let value = field!(self.gicd, igroupr).split()[index].read();
field!(self.gicd, igroupr).split()[index].write(f(value, mask));
};
if start_reg == end_reg {
let mask = Self::create_mask(start_bit..end_bit);
modify_igroupr(start_reg, mask);
} else {
let start_mask = Self::create_mask(start_bit..32);
modify_igroupr(start_reg, start_mask);
for reg_index in (start_reg + 1)..end_reg {
modify_igroupr(reg_index, 0xffff_ffff);
}
if end_reg < Self::MAX_IT {
let end_mask = Self::create_mask(0..end_bit);
modify_igroupr(end_reg, 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) {
// SAFETY: The ICC_PMR_EL1 register is available if FEAT_GICv3 is supported.
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() {
// SAFETY: The ICC_IGRPEN1_EL1 register is available if FEAT_GICv3 is supported.
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() {
// SAFETY: The ICC_IGRPEN1_EL1 register is available if FEAT_GICv3 is supported.
unsafe {
core::arch::asm!("msr ICC_IGRPEN1_EL1, {igrpen1}", igrpen1 = in(reg)0u64);
}
}
}
// SAFETY: It is safe to access GicDistributor from any core/thread.
unsafe impl Sync for GicDistributor<'_> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gicd_size() {
assert_eq!(core::mem::size_of::<GICDRegisters>(), 0x1_0000);
}
#[test]
fn test_bitmask() {
assert_eq!(0x0000_0000, GicDistributor::create_mask(0..0));
assert_eq!(0x0000_0001, GicDistributor::create_mask(0..1));
assert_eq!(0x8000_0000, GicDistributor::create_mask(31..32));
assert_eq!(0x0000_00ff, GicDistributor::create_mask(0..8));
assert_eq!(0xffff_ffff, GicDistributor::create_mask(0..32));
}
fn check_igroup(expected: &[u32], mut registers: UniqueMmioPointer<GICDRegisters>) {
for (index, expected) in expected.iter().enumerate() {
let actual = field!(registers, igroupr).split()[index].read();
assert_eq!(*expected, actual);
}
}
#[test]
fn test_gicd() {
let area: [u32; 0x4000] = [0; 0x4000];
let mut registers: GICDRegisters = unsafe { core::mem::transmute(area) };
{
let mut gic_distributor = GicDistributor::new((&mut registers).into());
gic_distributor.select_group0_range(8..16).unwrap();
}
let mut expected = [0xffff_ffffu32; 32];
expected[0] = 0xffff_00ff;
check_igroup(&expected, (&mut registers).into());
let mut gic_distributor = GicDistributor::new((&mut registers).into());
gic_distributor.select_group0_range(8..40).unwrap();
let mut expected = [0xffff_ffffu32; 32];
expected[0] = 0x0000_00ff;
expected[1] = 0xffff_ff00;
check_igroup(&expected, (&mut registers).into());
let mut gic_distributor = GicDistributor::new((&mut registers).into());
gic_distributor.select_group0_range(8..72).unwrap();
let mut expected = [0xffff_ffffu32; 32];
expected[0] = 0x0000_00ff;
expected[1] = 0x0000_0000;
expected[2] = 0xffff_ff00;
check_igroup(&expected, (&mut registers).into());
let mut gic_distributor = GicDistributor::new((&mut registers).into());
gic_distributor.select_group0_range(8..64).unwrap();
let mut expected = [0xffff_ffffu32; 32];
expected[0] = 0x0000_00ff;
expected[1] = 0x0000_0000;
check_igroup(&expected, (&mut registers).into());
let mut gic_distributor = GicDistributor::new((&mut registers).into());
gic_distributor.select_group0_range(1..1023).unwrap();
let mut expected = [0x0000_0000u32; 32];
expected[0] = 0x0000_0001;
expected[31] = 0x8000_0000;
check_igroup(&expected, (&mut registers).into());
let mut gic_distributor = GicDistributor::new((&mut registers).into());
gic_distributor.select_group0_range(0..1024).unwrap();
let expected = [0x0000_0000u32; 32];
check_igroup(&expected, (&mut registers).into());
let mut gic_distributor = GicDistributor::new((&mut registers).into());
assert!(gic_distributor.select_group0_range(0..2048).is_err());
let expected = [0xffff_ffffu32; 32];
check_igroup(&expected, (&mut registers).into());
}
}