blob: 4f7660f4feab032c807fbd55d94893cf12ac2f1e [file] [log] [blame]
// SPDX-FileCopyrightText: Copyright 2023-2024 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::{DerefMut, Range},
ptr::{addr_of, addr_of_mut},
};
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;
}
}
/// GIC Distributor register map
#[repr(C, align(4))]
pub struct GICDRegisters {
ctlr: u32, // 0x0000
typer: u32, // 0x0004
iidr: u32, // 0x0008
typer2: u32, // 0x000c
statusr: u32, // 0x0010
reserved_0014: [u32; 3], // 0x0014-0x001c
imdef0: [u32; 8], // 0x0020-0x003c
setspi_nsr: u32, // 0x0040
reserved_0044: u32, // 0x0044
clrspi_nsr: u32, // 0x0048
reserved_004c: u32, // 0x004c
setspi_sr: u32, // 0x0050
reserved_0054: u32, // 0x0054
clrspi_sr: u32, // 0x0058
reserved_005c: [u32; 9], // 0x005c-0x007c
igroupr: [u32; 32], // 0x0080-0x00fc
isenabler: [u32; 32], // 0x0100-0x017c
icenabler: [u32; 32], // 0x0180-0x01fc
ispendr: [u32; 32], // 0x0200-0x027c
icpendr: [u32; 32], // 0x0280-0x02fc
isactiver: [u32; 32], // 0x0300-0x037c
icactiver: [u32; 32], // 0x0380-0x03fc
ipriorityr: [u32; 256], // 0x0400-0x07f8
itargetsr: [u32; 256], // 0x0800-0x0bfc
icfgr: [u32; 64], // 0x0c00-0x0cfc
igrpmodr: [u32; 32], // 0x0d00-0x0d7c
reserved_0d80: [u32; 32], // 0x0d80-0x0dfc
nsacr: [u32; 64], // 0x0e00-0x0efc
sgir: u32, // 0x0f00
reserved_0f04: [u32; 3], // 0x0f04-0x0f0c
cpendsgir: [u32; 4], // 0x0f10-0x0f1c
spendsgir: [u32; 4], // 0x0f20-0x0f2c
reserved_0f30: [u32; 20], // 0x0f30-0x0f7c
inmir: [u32; 32], // 0x0f80-0x0ffc
igroupr_e: [u32; 32], // 0x1000-0x107c
reserved_1080: [u32; 96], // 0x1080-0x1fc
isenabler_e: [u32; 32], // 0x1200-0x127c
reserved_1280: [u32; 96], // 0x1280-0x13fc
icenabler_e: [u32; 32], // 0x1400-0x147c
reserved_1480: [u32; 96], // 0x1480-0x15fc
ispendr_e: [u32; 32], // 0x1600-0x167c
reserved_1680: [u32; 96], // 0x1680-0x17fc
icpendr_e: [u32; 32], // 0x1800-0x187c
reserved_1880: [u32; 96], // 0x1880-0x19fc
isactive_e: [u32; 32], // 0x1a00-0x1a7c
reserved_1a80: [u32; 96], // 0x1a80-0x1bfc
icactive_e: [u32; 32], // 0x1c00-0x1c7c
reserved_1c80: [u32; 224], // 0x1c80-0x1ffc
ipriorityr_e: [u32; 256], // 0x2000-0x23fc
reserved_2400: [u32; 768], // 0x2400-0x2ffc
icfgr_e: [u32; 64], // 0x3000-0x30fc
reserved_3100: [u32; 192], // 0x3100-0x33fc
igrpmodr_e: [u32; 32], // 0x3400-0x347c
reserved_3480: [u32; 96], // 0x3480-0x35fc
nsacr_e: [u32; 32], // 0x3600-0x367c
reserved_3680: [u32; 256], // 0x3680-0x3afc
inmir_e: [u32; 64], // 0x3b00-0x3bfc
reserved_3b80: [u32; 2400], // 0x3b80-0x60fc
irouter: [u32; 1975], // 0x6100-0x7fd8
reserved_7fdc: [u32; 9], // 0x7fdc-0x7ffc
irouter_e: [u32; 2048], // 0x8000-0x9ffc
reserved_a000: [u32; 6132], // 0xa000-0xffcc
id_registers: [u32; 12], // 0xffd0-0xfffc
}
/// # 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: DerefMut<Target = GICDRegisters>,
{
gicd: R,
}
impl<R> GicDistributor<R>
where
R: DerefMut<Target = GICDRegisters>,
{
const MAX_IT: usize = 32;
/// Create GIC Distributor instance
pub fn new(mut gicd: R) -> Self {
// SAFETY: gicd can be dereferenced as a valid GIC Distributor register block
unsafe {
for n in 0..Self::MAX_IT {
addr_of_mut!(gicd.icenabler[n]).write_volatile(0xffffffff);
addr_of_mut!(gicd.icpendr[n]).write_volatile(0xffffffff);
addr_of_mut!(gicd.igroupr[n]).write_volatile(0xffffffff);
}
}
Self { gicd }
}
/// Enable Group 0 interrupts
pub fn enable_group0(&mut self) {
// SAFETY: self.gicd can be dereferenced as a valid GIC Distributor register block
unsafe {
let value = addr_of!(self.gicd.ctlr).read_volatile();
addr_of_mut!(self.gicd.ctlr)
.write_volatile(value | DistributorControlRegister::EnableGrp0.bits());
}
}
/// Enable Secure Group 1 interrupts
pub fn enable_secure_group1(&mut self) {
// SAFETY: self.gicd can be dereferenced as a valid GIC Distributor register block
unsafe {
let value = addr_of!(self.gicd.ctlr).read_volatile();
addr_of_mut!(self.gicd.ctlr)
.write_volatile(value | DistributorControlRegister::EnableGrp1S.bits());
}
}
/// Enable Non-secure Group 1 interrupts
pub fn enable_non_secure_group1(&mut self) {
// SAFETY: self.gicd can be dereferenced as a valid GIC Distributor register block
unsafe {
let value = addr_of!(self.gicd.ctlr).read_volatile();
addr_of_mut!(self.gicd.ctlr)
.write_volatile(value | DistributorControlRegister::EnableGrp1NS.bits());
}
}
/// 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 = 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);
// SAFETY: self.gicd can be dereferenced as a valid GIC Distributor register block
let mut modify_igroupr = |index, mask| unsafe {
let ptr: *mut u32 = addr_of_mut!(self.gicd.igroupr[index]);
ptr.write_volatile(f(ptr.read_volatile(), 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<R> Sync for GicDistributor<R> where R: DerefMut<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::<&mut GICDRegisters>::create_mask(0..0)
);
assert_eq!(
0x0000_0001,
GicDistributor::<&mut GICDRegisters>::create_mask(0..1)
);
assert_eq!(
0x8000_0000,
GicDistributor::<&mut GICDRegisters>::create_mask(31..32)
);
assert_eq!(
0x0000_00ff,
GicDistributor::<&mut GICDRegisters>::create_mask(0..8)
);
assert_eq!(
0xffff_ffff,
GicDistributor::<&mut GICDRegisters>::create_mask(0..32)
);
}
#[test]
fn test_gicd() {
extern crate alloc;
use alloc::vec::Vec;
let area: [u32; 0x4000] = [0; 0x4000];
let mut registers: GICDRegisters = unsafe { core::mem::transmute(area) };
let mut gic_distributor = GicDistributor::new(&mut registers);
gic_distributor.select_group0_range(8..16).unwrap();
let result: Vec<u32> = registers.igroupr.iter().map(|r| *r).collect();
let mut expected = [0xffff_ffffu32; 32];
expected[0] = 0xffff_00ff;
assert_eq!(result.as_slice(), &expected);
let mut gic_distributor = GicDistributor::new(&mut registers);
gic_distributor.select_group0_range(8..40).unwrap();
let result: Vec<u32> = registers.igroupr.iter().map(|r| *r).collect();
let mut expected = [0xffff_ffffu32; 32];
expected[0] = 0x0000_00ff;
expected[1] = 0xffff_ff00;
assert_eq!(result.as_slice(), &expected);
let mut gic_distributor = GicDistributor::new(&mut registers);
gic_distributor.select_group0_range(8..72).unwrap();
let result: Vec<u32> = registers.igroupr.iter().map(|r| *r).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 mut gic_distributor = GicDistributor::new(&mut registers);
gic_distributor.select_group0_range(8..64).unwrap();
let result: Vec<u32> = registers.igroupr.iter().map(|r| *r).collect();
let mut expected = [0xffff_ffffu32; 32];
expected[0] = 0x0000_00ff;
expected[1] = 0x0000_0000;
assert_eq!(result.as_slice(), &expected);
let mut gic_distributor = GicDistributor::new(&mut registers);
gic_distributor.select_group0_range(1..1023).unwrap();
let result: Vec<u32> = registers.igroupr.iter().map(|r| *r).collect();
let mut expected = [0x0000_0000u32; 32];
expected[0] = 0x0000_0001;
expected[31] = 0x8000_0000;
assert_eq!(result.as_slice(), &expected);
let mut gic_distributor = GicDistributor::new(&mut registers);
gic_distributor.select_group0_range(0..1024).unwrap();
let result: Vec<u32> = registers.igroupr.iter().map(|r| *r).collect();
let expected = [0x0000_0000u32; 32];
assert_eq!(result.as_slice(), &expected);
let mut gic_distributor = GicDistributor::new(&mut registers);
assert!(gic_distributor.select_group0_range(0..2048).is_err());
let result: Vec<u32> = registers.igroupr.iter().map(|r| *r).collect();
let expected = [0xffff_ffffu32; 32];
assert_eq!(result.as_slice(), &expected);
}