use crate::{
IntId, Trigger, clear_bit,
gicv3::{
GicError, Group, HIGHEST_NS_PRIORITY, SecureIntGroup, register_count,
registers::{Gicd, GicdCtlr, Typer},
set_regs,
},
set_bit,
};
use core::{hint::spin_loop, ops::Range};
use safe_mmio::{UniqueMmioPointer, field, field_shared, fields::ReadPureWrite};
use zerocopy::{transmute_mut, transmute_ref};
macro_rules! select_regs {
($regs:expr, $reg:ident, $reg_e:ident, $intid:expr) => {
if $intid.0 < IntId::SPECIAL_START {
Ok((field!($regs, $reg), $intid.0 as usize))
} else if ($intid.is_espi()) {
Ok((
field!($regs, $reg_e),
($intid.0 - IntId::ESPI_START) as usize,
))
} else {
Err(GicError::InvalidGicdIntid($intid))
}
};
}
macro_rules! save_regs {
($context:expr, $regs:expr, $reg:ident, $int_count:expr, $bits_per_int:expr) => {
save_regs!(
$context,
$regs,
$reg,
$int_count,
$bits_per_int,
IntId::SPI_START as usize
)
};
($context:expr, $regs:expr, $reg:ident, $int_count:expr, $bits_per_int:expr, $start_offset:expr) => {
let context_typed = if false { $context[0] } else { 0 };
let reg_start = register_count($start_offset, $bits_per_int, &context_typed);
let reg_end = register_count($start_offset + $int_count, $bits_per_int, &context_typed);
for i in reg_start..reg_end {
$context[i - reg_start] = field_shared!($regs, $reg).get(i).unwrap().read();
}
};
}
macro_rules! restore_regs {
($context:expr, $regs:expr, $reg:ident, $int_count:expr, $bits_per_int:expr) => {
restore_regs!(
$context,
$regs,
$reg,
$int_count,
$bits_per_int,
IntId::SPI_START as usize
);
};
($context:expr, $regs:expr, $reg:ident, $int_count:expr, $bits_per_int:expr, $start_offset:expr) => {
let context_typed = if false { $context[0] } else { 0 };
let reg_start = register_count($start_offset, $bits_per_int, &context_typed);
let reg_end = register_count($start_offset + $int_count, $bits_per_int, &context_typed);
for i in reg_start..reg_end {
field!($regs, $reg)
.get(i)
.unwrap()
.write($context[i - reg_start]);
}
};
}
macro_rules! define_context_registers {
($( ( $reg:ident, $reg_mut:ident, $type:ty, $bits:expr ) ),* $(,,)?) => {
define_context_registers!(@step 0 ; $( ( $reg, $reg_mut, $type, $bits ) ),* );
};
( @step $n:expr ; ) => {
const BITS_PER_SPI: usize = $n;
};
( @step $n:expr ;
( $reg:ident, $reg_mut:ident, $type:ty, $bits:expr ) $(, $rest:tt)*
) => {
#[doc = "Autogenerated function to access "]
#[doc = stringify!($reg)]
#[doc = " registers."]
pub fn $reg(&self) -> &[$type] {
transmute_ref!(&self.iregs[Self::spi_reg_index($n..($n + $bits))])
}
#[doc = "Autogenerated function to mutable access "]
#[doc = stringify!($reg)]
#[doc = " registers."]
pub fn $reg_mut(&mut self) -> &mut [$type] {
transmute_mut!(&mut self.iregs[Self::spi_reg_index($n..($n + $bits))])
}
define_context_registers!(@step $n + $bits ; $( $rest ),*);
};
}
macro_rules! define_context_extended_registers {
($( ( $reg:ident, $reg_mut:ident, $type:ty, $bits:expr ) ),* $(,,)?) => {
define_context_extended_registers!(@step 0 ; $( ( $reg, $reg_mut, $type, $bits ) ),* );
};
( @step $n:expr ; ) => {
const BITS_PER_ESPI: usize = $n;
};
( @step $n:expr ;
( $reg:ident, $reg_mut:ident, $type:ty, $bits:expr ) $(, $rest:tt)*
) => {
#[doc = "Autogenerated function to access "]
#[doc = stringify!($reg)]
#[doc = " registers."]
pub fn $reg(&self) -> &[$type] {
transmute_ref!(&self.iregs_e[Self::espi_reg_index($n..($n + $bits))])
}
#[doc = "Autogenerated function to mutable access "]
#[doc = stringify!($reg)]
#[doc = " registers."]
pub fn $reg_mut(&mut self) -> &mut [$type] {
transmute_mut!(&mut self.iregs_e[Self::espi_reg_index($n..($n + $bits))])
}
define_context_extended_registers!(@step $n + $bits ; $( $rest ),*);
};
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GicDistributorContext<const IREG_COUNT: usize, const IREG_E_COUNT: usize> {
ctlr: GicdCtlr,
iregs: [u64; IREG_COUNT],
iregs_e: [u64; IREG_E_COUNT],
}
impl GicDistributorContext<0, 0> {
pub const fn ireg_count(spi_count: usize) -> usize {
let max_spi_index = spi_count + 32 + 4;
assert!(
(max_spi_index).is_multiple_of(32),
"max_spi_index must be multiple of 32"
);
(max_spi_index * GicDistributorContext::<0, 0>::BITS_PER_SPI).div_ceil(64)
}
pub const fn ireg_e_count(espi_count: usize) -> usize {
assert!(
espi_count.is_multiple_of(32),
"espi_count must be multiple of 32"
);
(espi_count * GicDistributorContext::<0, 0>::BITS_PER_ESPI).div_ceil(64)
}
}
impl<const IREG_COUNT: usize, const IREG_E_COUNT: usize>
GicDistributorContext<IREG_COUNT, IREG_E_COUNT>
{
const SPI_COUNT: usize = (IREG_COUNT * 64).div_ceil(Self::BITS_PER_SPI);
const ESPI_COUNT: usize = (IREG_E_COUNT * 64).div_ceil(Self::BITS_PER_ESPI);
pub const fn new() -> Self {
Self {
ctlr: GicdCtlr::empty(),
iregs: [0; IREG_COUNT],
iregs_e: [0; IREG_E_COUNT],
}
}
define_context_registers![
(irouter, irouter_mut, u64, Gicd::IROUTER_BITS),
(igroupr, igroupr_mut, u32, Gicd::IGROUPR_BITS),
(isenabler, isenabler_mut, u32, Gicd::ISENABLER_BITS),
(ispendr, ispendr_mut, u32, Gicd::ISPENDR_BITS),
(isactiver, isactiver_mut, u32, Gicd::ISACTIVER_BITS),
(icfgr, icfgr_mut, u32, Gicd::ICFGR_BITS),
(igrpmodr, igrpmodr_mut, u32, Gicd::IGRPMODR_BITS),
(nsacr, nsacr_mut, u32, Gicd::NSACR_BITS),
(ipriorityr, ipriorityr_mut, u8, Gicd::IPRIORITY_BITS)
];
define_context_extended_registers![
(irouter_e, irouter_e_mut, u64, Gicd::IROUTER_BITS),
(igroupr_e, igroupr_e_mut, u32, Gicd::IGROUPR_BITS),
(isenabler_e, isenabler_e_mut, u32, Gicd::ISENABLER_BITS),
(ispendr_e, ispendr_e_mut, u32, Gicd::ISPENDR_BITS),
(isactiver_e, isactiver_e_mut, u32, Gicd::ISACTIVER_BITS),
(icfgr_e, icfgr_e_mut, u32, Gicd::ICFGR_BITS),
(igrpmodr_e, igrpmodr_e_mut, u32, Gicd::IGRPMODR_BITS),
(nsacr_e, nsacr_e_mut, u32, Gicd::NSACR_BITS),
(ipriorityr_e, ipriorityr_e_mut, u8, Gicd::IPRIORITY_BITS)
];
const fn spi_reg_index(bits: Range<usize>) -> Range<usize> {
const {
assert!(
IREG_COUNT.is_multiple_of(Self::BITS_PER_SPI),
"IREG_COUNT must be multiple of BITS_PER_SPI"
)
};
((bits.start * Self::SPI_COUNT).div_ceil(64))..((bits.end * Self::SPI_COUNT).div_ceil(64))
}
const fn espi_reg_index(bits: Range<usize>) -> Range<usize> {
const {
assert!(
IREG_E_COUNT.is_multiple_of(Self::BITS_PER_ESPI),
"IREG_E_COUNT must be multiple of BITS_PER_ESPI"
)
};
((bits.start * Self::ESPI_COUNT).div_ceil(64))..((bits.end * Self::ESPI_COUNT).div_ceil(64))
}
}
impl<const IREG_COUNT: usize, const IREG_E_COUNT: usize> Default
for GicDistributorContext<IREG_COUNT, IREG_E_COUNT>
{
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct GicDistributor<'a> {
regs: UniqueMmioPointer<'a, Gicd>,
}
impl<'a> GicDistributor<'a> {
pub fn new(regs: UniqueMmioPointer<'a, Gicd>) -> Self {
Self { regs }
}
pub fn configure_default_settings(&mut self) {
const SPI_START: usize = IntId::SPI_START as usize;
const G1NS_ALL: u32 = 0xffff_ffff;
const LEVEL_SENSITIVE_ALL: u32 = 0x0000_0000;
self.enable_affinity_routing_secure(true);
self.enable_group1_non_secure(true);
let spi_count = self.spi_count();
let espi_count = self.espi_count();
set_regs(
field!(self.regs, igroupr),
SPI_START,
spi_count,
Gicd::IGROUPR_BITS,
G1NS_ALL,
);
set_regs(
field!(self.regs, igroupr_e),
0,
espi_count,
Gicd::IGROUPR_BITS,
G1NS_ALL,
);
let ipriority_word_value = u32::from_le_bytes([
HIGHEST_NS_PRIORITY,
HIGHEST_NS_PRIORITY,
HIGHEST_NS_PRIORITY,
HIGHEST_NS_PRIORITY,
]);
let mut ipriorityr_bytes = field!(self.regs, ipriorityr);
let ipriority_words = unsafe {
UniqueMmioPointer::new(
ipriorityr_bytes
.ptr_nonnull()
.cast::<[ReadPureWrite<u32>; 1024 / 4]>(),
)
};
set_regs(
ipriority_words,
SPI_START / 4,
spi_count / 4,
Gicd::IPRIORITY_BITS * 4,
ipriority_word_value,
);
let mut ipriorityr_e_bytes = field!(self.regs, ipriorityr_e);
let ipriority_e_words = unsafe {
UniqueMmioPointer::new(
ipriorityr_e_bytes
.ptr_nonnull()
.cast::<[ReadPureWrite<u32>; 1024 / 4]>(),
)
};
set_regs(
ipriority_e_words,
0,
espi_count / 4,
Gicd::IPRIORITY_BITS * 4,
ipriority_word_value,
);
set_regs(
field!(self.regs, icfgr),
SPI_START,
spi_count,
Gicd::ICFGR_BITS,
LEVEL_SENSITIVE_ALL,
);
set_regs(
field!(self.regs, icfgr_e),
0,
espi_count,
Gicd::ICFGR_BITS,
LEVEL_SENSITIVE_ALL,
);
self.wait_for_pending_write();
}
pub fn typer(&self) -> Typer {
field_shared!(self.regs, typer).read()
}
pub fn enable_affinity_routing_non_secure(&mut self, enable: bool) {
self.modify_control(GicdCtlr::ARE_NS, enable);
}
pub fn enable_affinity_routing_secure(&mut self, enable: bool) {
self.modify_control(GicdCtlr::ARE_S, enable);
}
pub fn enable_group1_secure(&mut self, enable: bool) {
self.modify_control(GicdCtlr::EnableGrp1S, enable);
}
pub fn enable_group1_non_secure(&mut self, enable: bool) {
self.modify_control(GicdCtlr::EnableGrp1NS, enable);
}
pub fn enable_group0(&mut self, enable: bool) {
self.modify_control(GicdCtlr::EnableGrp0, enable);
}
pub fn modify_control(&mut self, bits: GicdCtlr, enable: bool) {
field!(self.regs, ctlr).modify_mut(|v| v.set(bits, enable));
self.wait_for_pending_write();
}
pub fn set_interrupt_priority(&mut self, intid: IntId, priority: u8) -> Result<(), GicError> {
let (mut registers, index) = select_regs!(self.regs, ipriorityr, ipriorityr_e, intid)?;
registers.get(index).unwrap().write(priority);
Ok(())
}
pub fn set_trigger(&mut self, intid: IntId, trigger: Trigger) -> Result<(), GicError> {
let (registers, index) = select_regs!(self.regs, icfgr, icfgr_e, intid)?;
let bit_index = index * Gicd::ICFGR_BITS + 1;
match trigger {
Trigger::Edge => set_bit(registers, bit_index),
Trigger::Level => clear_bit(registers, bit_index),
};
Ok(())
}
pub fn set_routing(&mut self, intid: IntId, mpidr: Option<u64>) -> Result<(), GicError> {
const INTERRUPT_ROUTING_MODE: u64 = 1 << 31;
const IROUTER_MASK: u64 = 0x000000FF_80FFFFFF;
let irouter = mpidr
.map(|mpidr| mpidr & IROUTER_MASK)
.unwrap_or(INTERRUPT_ROUTING_MODE);
if let Some(spi_index) = intid.spi_index() {
field!(self.regs, irouter)
.get(spi_index)
.unwrap()
.write(irouter);
Ok(())
} else if let Some(espi_index) = intid.espi_index() {
field!(self.regs, irouter_e)
.get(espi_index)
.unwrap()
.write(irouter);
Ok(())
} else {
Err(GicError::InvalidGicdIntid(intid))
}
}
pub fn set_group(&mut self, intid: IntId, group: Group) -> Result<(), GicError> {
if let Group::Secure(sg) = group {
let (igroupr, index) = select_regs!(self.regs, igroupr, igroupr_e, intid)?;
clear_bit(igroupr, index);
let (igrpmodr, index) = select_regs!(self.regs, igrpmodr, igrpmodr_e, intid)?;
match sg {
SecureIntGroup::Group1S => set_bit(igrpmodr, index),
SecureIntGroup::Group0 => clear_bit(igrpmodr, index),
}
} else {
let (igroupr, index) = select_regs!(self.regs, igroupr, igroupr_e, intid)?;
set_bit(igroupr, index);
let (igrpmodr, index) = select_regs!(self.regs, igrpmodr, igrpmodr_e, intid)?;
clear_bit(igrpmodr, index);
}
Ok(())
}
pub fn enable_interrupt(&mut self, intid: IntId, enable: bool) -> Result<(), GicError> {
if enable {
let (registers, index) = select_regs!(self.regs, isenabler, isenabler_e, intid)?;
set_bit(registers, index);
} else {
let (registers, index) = select_regs!(self.regs, icenabler, icenabler_e, intid)?;
set_bit(registers, index);
}
Ok(())
}
pub fn enable_all_interrupts(&mut self, enable: bool) {
const SPI_START: usize = IntId::SPI_START as usize;
const ALL: u32 = 0xffff_ffff;
let spi_count = self.spi_count();
let espi_count = self.espi_count();
if enable {
set_regs(
field!(self.regs, isenabler),
SPI_START,
spi_count,
Gicd::ISENABLER_BITS,
ALL,
);
set_regs(
field!(self.regs, isenabler_e),
0,
espi_count,
Gicd::ISENABLER_BITS,
ALL,
);
} else {
set_regs(
field!(self.regs, icenabler),
SPI_START,
spi_count,
Gicd::ICENABLER_BITS,
ALL,
);
set_regs(
field!(self.regs, icenabler_e),
0,
espi_count,
Gicd::ICENABLER_BITS,
ALL,
);
}
}
pub fn restore<const IREG_COUNT: usize, const IREG_E_COUNT: usize>(
&mut self,
context: &GicDistributorContext<IREG_COUNT, IREG_E_COUNT>,
) -> Result<(), GicError> {
self.modify_control(
GicdCtlr::EnableGrp0 | GicdCtlr::EnableGrp1S | GicdCtlr::EnableGrp1NS,
false,
);
self.modify_control(GicdCtlr::ARE_S | GicdCtlr::ARE_NS, true);
let spi_count = Self::min_count(
self.spi_count(),
GicDistributorContext::<IREG_COUNT, IREG_E_COUNT>::SPI_COUNT,
)?;
let espi_count = Self::min_count(
self.espi_count(),
GicDistributorContext::<IREG_COUNT, IREG_E_COUNT>::ESPI_COUNT,
)?;
restore_regs!(
context.igroupr(),
self.regs,
igroupr,
spi_count,
Gicd::IGROUPR_BITS
);
restore_regs!(
context.igroupr_e(),
self.regs,
igroupr_e,
espi_count,
Gicd::IGROUPR_BITS,
0
);
restore_regs!(
context.ipriorityr(),
self.regs,
ipriorityr,
spi_count,
Gicd::IPRIORITY_BITS
);
restore_regs!(
context.ipriorityr_e(),
self.regs,
ipriorityr_e,
espi_count,
Gicd::IPRIORITY_BITS,
0
);
restore_regs!(
context.icfgr(),
self.regs,
icfgr,
spi_count,
Gicd::ICFGR_BITS
);
restore_regs!(
context.icfgr_e(),
self.regs,
icfgr_e,
espi_count,
Gicd::ICFGR_BITS,
0
);
restore_regs!(
context.igrpmodr(),
self.regs,
igrpmodr,
spi_count,
Gicd::IGRPMODR_BITS
);
restore_regs!(
context.igrpmodr_e(),
self.regs,
igrpmodr_e,
espi_count,
Gicd::IGRPMODR_BITS,
0
);
restore_regs!(
context.nsacr(),
self.regs,
nsacr,
spi_count,
Gicd::NSACR_BITS
);
restore_regs!(
context.nsacr_e(),
self.regs,
nsacr_e,
espi_count,
Gicd::NSACR_BITS,
0
);
restore_regs!(
context.irouter(),
self.regs,
irouter,
spi_count,
Gicd::IROUTER_BITS,
0
);
restore_regs!(
context.irouter_e(),
self.regs,
irouter_e,
espi_count,
Gicd::IROUTER_BITS,
0
);
restore_regs!(
context.isenabler(),
self.regs,
isenabler,
spi_count,
Gicd::ISENABLER_BITS
);
restore_regs!(
context.isenabler_e(),
self.regs,
isenabler_e,
espi_count,
Gicd::ISENABLER_BITS,
0
);
restore_regs!(
context.ispendr(),
self.regs,
ispendr,
spi_count,
Gicd::ISPENDR_BITS
);
restore_regs!(
context.ispendr_e(),
self.regs,
ispendr_e,
espi_count,
Gicd::ISPENDR_BITS,
0
);
restore_regs!(
context.isactiver(),
self.regs,
isactiver,
spi_count,
Gicd::ISACTIVER_BITS
);
restore_regs!(
context.isactiver_e(),
self.regs,
isactiver_e,
espi_count,
Gicd::ISACTIVER_BITS,
0
);
field!(self.regs, ctlr).write(context.ctlr);
self.wait_for_pending_write();
Ok(())
}
pub fn save<const IREG_COUNT: usize, const IREG_E_COUNT: usize>(
&self,
context: &mut GicDistributorContext<IREG_COUNT, IREG_E_COUNT>,
) -> Result<(), GicError> {
let spi_count = Self::min_count(
self.spi_count(),
GicDistributorContext::<IREG_COUNT, IREG_E_COUNT>::SPI_COUNT,
)?;
let espi_count = Self::min_count(
self.espi_count(),
GicDistributorContext::<IREG_COUNT, IREG_E_COUNT>::ESPI_COUNT,
)?;
self.wait_for_pending_write();
context.ctlr = field_shared!(self.regs, ctlr).read();
save_regs!(
context.igroupr_mut(),
self.regs,
igroupr,
spi_count,
Gicd::IGROUPR_BITS
);
save_regs!(
context.igroupr_e_mut(),
self.regs,
igroupr_e,
espi_count,
Gicd::IGROUPR_BITS,
0
);
save_regs!(
context.isenabler_mut(),
self.regs,
isenabler,
spi_count,
Gicd::ISENABLER_BITS
);
save_regs!(
context.isenabler_e_mut(),
self.regs,
isenabler_e,
espi_count,
Gicd::ISENABLER_BITS,
0
);
save_regs!(
context.ispendr_mut(),
self.regs,
ispendr,
spi_count,
Gicd::ISPENDR_BITS
);
save_regs!(
context.ispendr_e_mut(),
self.regs,
ispendr_e,
espi_count,
Gicd::ISPENDR_BITS,
0
);
save_regs!(
context.isactiver_mut(),
self.regs,
isactiver,
spi_count,
Gicd::ISACTIVER_BITS
);
save_regs!(
context.isactiver_e_mut(),
self.regs,
isactiver_e,
espi_count,
Gicd::ISACTIVER_BITS,
0
);
save_regs!(
context.ipriorityr_mut(),
self.regs,
ipriorityr,
spi_count,
Gicd::IPRIORITY_BITS
);
save_regs!(
context.ipriorityr_e_mut(),
self.regs,
ipriorityr_e,
espi_count,
Gicd::IPRIORITY_BITS,
0
);
save_regs!(
context.icfgr_mut(),
self.regs,
icfgr,
spi_count,
Gicd::ICFGR_BITS
);
save_regs!(
context.icfgr_e_mut(),
self.regs,
icfgr_e,
espi_count,
Gicd::ICFGR_BITS,
0
);
save_regs!(
context.igrpmodr_mut(),
self.regs,
igrpmodr,
spi_count,
Gicd::IGRPMODR_BITS
);
save_regs!(
context.igrpmodr_e_mut(),
self.regs,
igrpmodr_e,
espi_count,
Gicd::IGRPMODR_BITS,
0
);
save_regs!(
context.nsacr_mut(),
self.regs,
nsacr,
spi_count,
Gicd::NSACR_BITS
);
save_regs!(
context.nsacr_e_mut(),
self.regs,
nsacr_e,
espi_count,
Gicd::NSACR_BITS,
0
);
save_regs!(
context.irouter_mut(),
self.regs,
irouter,
spi_count,
Gicd::IROUTER_BITS,
0
);
save_regs!(
context.irouter_e_mut(),
self.regs,
irouter_e,
espi_count,
Gicd::IROUTER_BITS,
0
);
Ok(())
}
pub fn wait_for_pending_write(&self) {
let ctlr = field_shared!(self.regs, ctlr);
while ctlr.read().contains(GicdCtlr::RWP) {
spin_loop();
}
}
fn spi_count(&self) -> usize {
let typer = field_shared!(self.regs, typer).read();
typer.num_spis() as usize
}
fn espi_count(&self) -> usize {
let typer = field_shared!(self.regs, typer).read();
typer.num_espis() as usize
}
fn min_count(implemented: usize, stored: usize) -> Result<usize, GicError> {
if implemented <= stored {
Ok(implemented)
} else {
Err(GicError::InvalidContextSize(implemented, stored))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, transmute_mut};
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(8))]
struct FakeRegisters {
regs: [u32; Self::REG_COUNT],
}
impl FakeRegisters {
const REG_COUNT: usize = 64 * 1024 / 4;
pub fn new() -> Self {
Self {
regs: [0u32; Self::REG_COUNT],
}
}
}
struct FakeDistributor {
regs: FakeRegisters,
}
impl FakeDistributor {
pub fn new() -> Self {
Self {
regs: FakeRegisters::new(),
}
}
pub fn clear(&mut self) {
self.regs.regs.fill(0);
}
pub fn regs_write(&mut self, offset: usize, value: u32) {
self.regs.regs[offset / 4] = value;
}
pub fn reg_read(&self, offset: usize) -> u32 {
self.regs.regs[offset / 4]
}
fn get(&mut self) -> UniqueMmioPointer<'_, Gicd> {
UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
}
pub fn distributor_for_test(&mut self) -> GicDistributor<'_> {
GicDistributor::new(self.get())
}
}
#[derive(Debug)]
struct TestCase<V, T> {
intid: IntId,
offset: usize,
expected_value: V,
param: T,
}
impl<V, T> TestCase<V, T> {
pub const fn new(intid: IntId, offset: usize, expected_value: V, param: T) -> Self {
Self {
intid,
offset,
expected_value,
param,
}
}
}
#[test]
fn configure_default_settings() {
let mut regs = FakeDistributor::new();
let mut distributor = regs.distributor_for_test();
distributor.configure_default_settings();
}
#[test]
fn typer() {
let mut regs = FakeDistributor::new();
regs.regs_write(0x0004, 1 << 8);
let distributor = regs.distributor_for_test();
let typer = distributor.typer();
assert!(typer.espi_supported());
}
#[test]
fn control() {
let mut regs = FakeDistributor::new();
macro_rules! test_control {
($regs:expr, $func:ident, $enable:literal, $expected:expr) => {
let mut distributor = $regs.distributor_for_test();
distributor.$func($enable);
assert_eq!($expected, $regs.reg_read(0x0000));
};
}
test_control!(regs, enable_affinity_routing_non_secure, true, 0x20);
test_control!(regs, enable_affinity_routing_secure, true, 0x30);
test_control!(regs, enable_group1_secure, true, 0x34);
test_control!(regs, enable_group1_non_secure, true, 0x36);
test_control!(regs, enable_group0, true, 0x37);
test_control!(regs, enable_affinity_routing_non_secure, false, 0x17);
test_control!(regs, enable_affinity_routing_secure, false, 0x07);
test_control!(regs, enable_group1_secure, false, 0x03);
test_control!(regs, enable_group1_non_secure, false, 0x01);
test_control!(regs, enable_group0, false, 0x00);
{
let mut distributor = regs.distributor_for_test();
distributor.modify_control(
GicdCtlr::ARE_NS | GicdCtlr::EnableGrp1NS | GicdCtlr::EnableGrp0,
true,
);
}
assert_eq!(0x23, regs.reg_read(0x0000));
}
#[test]
fn set_interrupt_priority() {
let tests = [
TestCase::new(IntId::sgi(0), 0x0400, 0x0000_00ab, 0xab),
TestCase::new(IntId::sgi(15), 0x040c, 0xcd00_0000, 0xcd),
TestCase::new(IntId::ppi(0), 0x0410, 0x0000_0012, 0x12),
TestCase::new(IntId::ppi(15), 0x041c, 0x3400_0000, 0x34),
TestCase::new(IntId::espi(0), 0x2000, 0x0000_0056, 0x56),
TestCase::new(IntId::espi(1023), 0x23fc, 0x7800_0000, 0x78),
];
let mut regs = FakeDistributor::new();
for test in tests {
let mut distributor = regs.distributor_for_test();
assert_eq!(
Ok(()),
distributor.set_interrupt_priority(test.intid, test.param)
);
assert_eq!(
test.expected_value,
regs.reg_read(test.offset),
"test case: {test:x?}",
);
regs.clear();
}
let mut redistributor = regs.distributor_for_test();
assert_eq!(
Ok(()),
redistributor.set_interrupt_priority(IntId::sgi(0), 0xcd)
);
assert_eq!(
Ok(()),
redistributor.set_interrupt_priority(IntId::sgi(1), 0xab)
);
assert_eq!(
Ok(()),
redistributor.set_interrupt_priority(IntId::sgi(3), 0x12)
);
assert_eq!(
Err(GicError::InvalidGicdIntid(IntId::SPECIAL_NONSECURE)),
redistributor.set_interrupt_priority(IntId::SPECIAL_NONSECURE, 0x12)
);
assert_eq!(0x1200_abcd, regs.reg_read(0x0400));
}
#[test]
fn set_trigger() {
let tests = [
TestCase::new(IntId::sgi(0), 0x0c00, 0x0000_0002, Trigger::Edge),
TestCase::new(IntId::sgi(15), 0x0c00, 0x8000_0000, Trigger::Edge),
TestCase::new(IntId::ppi(0), 0x0c04, 0x0000_0002, Trigger::Edge),
TestCase::new(IntId::ppi(15), 0x0c04, 0x8000_0000, Trigger::Edge),
TestCase::new(IntId::espi(0), 0x3000, 0x0000_0002, Trigger::Edge),
TestCase::new(IntId::espi(1023), 0x30fc, 0x8000_0000, Trigger::Edge),
];
let mut regs = FakeDistributor::new();
for test in tests {
let mut distributor = regs.distributor_for_test();
assert_eq!(Ok(()), distributor.set_trigger(test.intid, test.param));
assert_eq!(
test.expected_value,
regs.reg_read(test.offset),
"test case: {test:x?}",
);
regs.clear();
}
let mut distributor = regs.distributor_for_test();
assert_eq!(
Ok(()),
distributor.set_trigger(IntId::sgi(0), Trigger::Edge)
);
assert_eq!(
Ok(()),
distributor.set_trigger(IntId::sgi(1), Trigger::Edge)
);
assert_eq!(
Ok(()),
distributor.set_trigger(IntId::sgi(3), Trigger::Edge)
);
assert_eq!(
Ok(()),
distributor.set_trigger(IntId::sgi(3), Trigger::Level)
);
assert_eq!(
Err(GicError::InvalidGicdIntid(IntId::SPECIAL_NONSECURE)),
distributor.set_trigger(IntId::SPECIAL_NONSECURE, Trigger::Level)
);
assert_eq!(0x0000_000a, regs.reg_read(0x0c00));
}
#[test]
fn set_routing() {
let mpidr = 0x0000_00ab_00cd_ef12u64;
let tests = [
TestCase::new(IntId::spi(0), 0x6100, mpidr, Some(mpidr)),
TestCase::new(IntId::spi(987), 0x7FD8, mpidr, Some(mpidr)),
TestCase::new(IntId::espi(0), 0x8000, mpidr, Some(mpidr)),
TestCase::new(IntId::espi(1023), 0x9ff8, 0x0000_0000_8000_0000, None),
];
let mut regs = FakeDistributor::new();
for test in tests {
let mut distributor = regs.distributor_for_test();
assert_eq!(Ok(()), distributor.set_routing(test.intid, test.param));
let lo = regs.reg_read(test.offset);
let hi = regs.reg_read(test.offset + 4);
let actual = (u64::from(hi) << 32) | u64::from(lo);
assert_eq!(test.expected_value, actual, "test case: {test:x?}",);
regs.clear();
}
let invalid_int_ids = [
IntId::sgi(0),
IntId::sgi(15),
IntId::ppi(0),
IntId::ppi(15),
IntId::SPECIAL_NONSECURE,
];
let mut distributor = regs.distributor_for_test();
for int_id in invalid_int_ids {
assert_eq!(
Err(GicError::InvalidGicdIntid(int_id)),
distributor.set_routing(int_id, None)
);
}
}
#[test]
fn set_group() {
let tests = [
TestCase::new(IntId::sgi(0), 0, 0b01, Group::Group1NS),
TestCase::new(
IntId::sgi(15),
15,
0b00,
Group::Secure(SecureIntGroup::Group0),
),
TestCase::new(
IntId::ppi(0),
16,
0b10,
Group::Secure(SecureIntGroup::Group1S),
),
TestCase::new(
IntId::ppi(15),
31,
0b00,
Group::Secure(SecureIntGroup::Group0),
),
TestCase::new(IntId::espi(0), 32, 0b01, Group::Group1NS),
TestCase::new(
IntId::espi(1023),
1055,
0b10,
Group::Secure(SecureIntGroup::Group1S),
),
];
let mut regs = FakeDistributor::new();
for test in tests {
let mut distributor = regs.distributor_for_test();
assert_eq!(Ok(()), distributor.set_group(test.intid, test.param));
let expected_igroupr = (test.expected_value & 1) << (test.offset % 32);
let exptected_igrpmodr = ((test.expected_value & 2) >> 1) << (test.offset % 32);
let (igroupr_index, igrpmodr_index) = if test.offset < 32 {
(0x0080 + test.offset / 32 * 4, 0x0d00 + test.offset / 32 * 4)
} else {
(
0x1000 + (test.offset - 32) / 32 * 4,
0x3400 + (test.offset - 32) / 32 * 4,
)
};
assert_eq!(
expected_igroupr,
regs.reg_read(igroupr_index),
"test case: {test:x?}",
);
assert_eq!(
exptected_igrpmodr,
regs.reg_read(igrpmodr_index),
"test case: {test:x?}",
);
regs.clear();
}
let mut distributor = regs.distributor_for_test();
assert_eq!(
Ok(()),
distributor.set_group(IntId::sgi(0), Group::Group1NS)
);
assert_eq!(
Ok(()),
distributor.set_group(IntId::sgi(1), Group::Secure(SecureIntGroup::Group0))
);
assert_eq!(
Ok(()),
distributor.set_group(IntId::sgi(3), Group::Secure(SecureIntGroup::Group1S))
);
assert_eq!(
Ok(()),
distributor.set_group(IntId::sgi(3), Group::Group1NS)
);
assert_eq!(
Err(GicError::InvalidGicdIntid(IntId::SPECIAL_NONSECURE)),
distributor.set_group(IntId::SPECIAL_NONSECURE, Group::Group1NS)
);
assert_eq!(0x0000_0009, regs.reg_read(0x0080));
assert_eq!(0x0000_0000, regs.reg_read(0x0d00));
}
#[test]
fn enable_interrupt() {
let tests = [
TestCase::new(IntId::sgi(0), 0x0100, 0x0000_0001, true),
TestCase::new(IntId::sgi(15), 0x0180, 0x0000_8000, false),
TestCase::new(IntId::ppi(0), 0x0100, 0x0001_0000, true),
TestCase::new(IntId::ppi(15), 0x0180, 0x8000_0000, false),
TestCase::new(IntId::espi(0), 0x1200, 0x0000_0001, true),
TestCase::new(IntId::espi(1023), 0x147c, 0x8000_0000, false),
];
let mut regs = FakeDistributor::new();
for test in tests {
let mut distributor = regs.distributor_for_test();
assert_eq!(Ok(()), distributor.enable_interrupt(test.intid, test.param));
assert_eq!(
test.expected_value,
regs.reg_read(test.offset),
"test case: {test:x?}",
);
regs.clear();
}
let mut distributor = regs.distributor_for_test();
assert_eq!(Ok(()), distributor.enable_interrupt(IntId::sgi(0), true));
assert_eq!(Ok(()), distributor.enable_interrupt(IntId::sgi(1), true));
assert_eq!(Ok(()), distributor.enable_interrupt(IntId::sgi(3), true));
assert_eq!(Ok(()), distributor.enable_interrupt(IntId::sgi(3), false));
assert_eq!(
Err(GicError::InvalidGicdIntid(IntId::SPECIAL_NONSECURE)),
distributor.enable_interrupt(IntId::SPECIAL_NONSECURE, false)
);
assert_eq!(0x0000_000b, regs.reg_read(0x0100));
assert_eq!(0x0000_0008, regs.reg_read(0x0180));
}
#[test]
fn enable_all_interrupts() {
let mut regs = FakeDistributor::new();
regs.regs_write(0x0004, 0x0000_0001);
{
let mut distributor = regs.distributor_for_test();
distributor.enable_all_interrupts(true);
assert_eq!(0x0000_0000, regs.reg_read(0x0100));
assert_eq!(0xffff_ffff, regs.reg_read(0x0104));
assert_eq!(0x0000_0000, regs.reg_read(0x0108));
assert_eq!(0x0000_0000, regs.reg_read(0x010c));
assert_eq!(0x0000_0000, regs.reg_read(0x0110));
regs.clear();
}
regs.regs_write(0x0004, 0x0000_0002);
{
let mut distributor = regs.distributor_for_test();
distributor.enable_all_interrupts(false);
assert_eq!(0x0000_0000, regs.reg_read(0x0180));
assert_eq!(0xffff_ffff, regs.reg_read(0x0184));
assert_eq!(0xffff_ffff, regs.reg_read(0x0188));
assert_eq!(0x0000_0000, regs.reg_read(0x010c));
assert_eq!(0x0000_0000, regs.reg_read(0x0110));
regs.clear();
}
regs.regs_write(0x0004, 0xf800_011f);
{
let mut distributor = regs.distributor_for_test();
distributor.enable_all_interrupts(true);
for i in 1..32 {
let offset = i * 4;
assert_eq!(
0xffff_ffff,
regs.reg_read(0x0100 + offset),
"index {offset:x}"
);
}
for i in 0..32 {
let offset = i * 4;
assert_eq!(
0xffff_ffff,
regs.reg_read(0x1200 + offset),
"index {offset:x}"
);
}
}
}
#[test]
fn context_size() {
assert_eq!(
(4usize + 512 * 81 / 8).next_multiple_of(size_of::<usize>()),
size_of::<GicDistributorContext::<{ GicDistributorContext::ireg_count(476) }, 0>>()
);
assert_eq!(
(4usize + 512 * 81 / 8 + 512 * 81 / 8).next_multiple_of(size_of::<usize>()),
size_of::<
GicDistributorContext::<
{ GicDistributorContext::ireg_count(476) },
{ GicDistributorContext::ireg_e_count(512) },
>,
>()
);
assert_eq!(
(4usize + 1024 * 81 / 8 + 1024 * 81 / 8).next_multiple_of(size_of::<usize>()),
size_of::<
GicDistributorContext::<
{ GicDistributorContext::ireg_count(988) },
{ GicDistributorContext::ireg_e_count(1024) },
>,
>()
);
}
#[test]
fn save_restore() {
let mut context = GicDistributorContext::<
{ GicDistributorContext::ireg_count(988) },
{ GicDistributorContext::ireg_e_count(0) },
>::new();
let saved_offsets = [
0x0000..=0x0000, 0x0084..=0x00fc, 0x0104..=0x017c, 0x0204..=0x027c, 0x0304..=0x037c, 0x0420..=0x07f8, 0x0c08..=0x0cfc, 0x0d04..=0x0d7c, 0x0e08..=0x0efc, 0x6100..=0x7fd8, ];
fn generate_value(offset: usize) -> u32 {
(offset * 2 + 1) as u32
}
let mut regs = FakeDistributor::new();
regs.regs_write(0x0004, 0x0000_001f);
for offset_range in &saved_offsets {
for offset in offset_range.clone().step_by(4) {
regs.regs_write(offset, generate_value(offset));
}
}
{
let distributor = regs.distributor_for_test();
assert_eq!(Ok(()), distributor.save(&mut context));
}
regs.clear();
regs.regs_write(0x0004, 0x0000_001f);
{
let mut distributor = regs.distributor_for_test();
assert_eq!(Ok(()), distributor.restore(&context));
}
for offset_range in saved_offsets {
for offset in offset_range.step_by(4) {
assert_eq!(
generate_value(offset),
regs.reg_read(offset),
"offset {offset:#x}",
);
}
}
}
#[test]
fn save_restore_extended() {
let mut context = GicDistributorContext::<
{ GicDistributorContext::ireg_count(988) },
{ GicDistributorContext::ireg_e_count(1024) },
>::new();
let saved_offsets = [
0x0000..=0x0000, 0x0084..=0x00fc, 0x0104..=0x017c, 0x0204..=0x027c, 0x0304..=0x037c, 0x0420..=0x07f8, 0x0c08..=0x0cfc, 0x0d04..=0x0d7c, 0x0e08..=0x0efc, 0x6100..=0x7fd8, 0x1000..=0x107c, 0x1200..=0x127c, 0x1600..=0x167c, 0x1a00..=0x1a7c, 0x2000..=0x23fc, 0x3000..=0x30fc, 0x3400..=0x347c, 0x3600..=0x36fc, 0x8000..=0x9ffc, ];
fn generate_value(offset: usize) -> u32 {
(offset * 2 + 1) as u32
}
let mut regs = FakeDistributor::new();
regs.regs_write(0x0004, 0xf800_011f);
for offset_range in &saved_offsets {
for offset in offset_range.clone().step_by(4) {
regs.regs_write(offset, generate_value(offset));
}
}
{
let distributor = regs.distributor_for_test();
assert_eq!(Ok(()), distributor.save(&mut context));
}
regs.clear();
regs.regs_write(0x0004, 0xf800_011f);
{
let mut distributor = regs.distributor_for_test();
assert_eq!(Ok(()), distributor.restore(&context));
}
for offset_range in saved_offsets {
for offset in offset_range.step_by(4) {
assert_eq!(
generate_value(offset),
regs.reg_read(offset),
"offset {offset:#x}",
);
}
}
}
}