use crate::{
IntId, Trigger, clear_bit,
gicv3::{
GicError, Group, HIGHEST_NS_PRIORITY, SecureIntGroup, register_count,
registers::{GicrCtlr, GicrIidr, GicrPwrr, GicrSgi, GicrTyper, Sgi, Waker},
set_regs,
},
set_bit,
};
use core::{hint::spin_loop, marker::PhantomData, ops::Range, ptr::NonNull, stringify};
use safe_mmio::{UniqueMmioPointer, field, field_shared, fields::ReadPureWrite};
use zerocopy::{transmute_mut, transmute_ref};
macro_rules! save_reg {
($context:ident, $regs:expr, $reg:ident) => {
$context.$reg = field_shared!($regs, $reg).read();
};
($context:ident, $regs:ident, $reg:ident, $index:ident) => {
$context.$reg[$index] = field_shared!($regs, $reg).get($index).unwrap().read();
};
}
macro_rules! restore_reg {
($context:ident, $regs:expr, $reg:ident) => {
field!($regs, $reg).write($context.$reg);
};
($context:ident, $regs:ident, $reg:ident, $index:ident) => {
field!($regs, $reg)
.get($index)
.unwrap()
.write($context.$reg[$index]);
};
}
macro_rules! save_regs {
($context:expr, $regs:expr, $reg:ident, $int_count:expr, $bits_per_int:expr) => {
let reg_count = register_count($int_count, $bits_per_int, &$context[0]);
for i in 0..reg_count {
$context[i] = 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) => {
let reg_count = register_count($int_count, $bits_per_int, &$context[0]);
for i in 0..reg_count {
field!($regs, $reg).get(i).unwrap().write($context[i]);
}
};
}
pub struct GicRedistributorIterator<'a> {
pointer: Option<NonNull<GicrSgi>>,
gic_v4: bool,
phantom: PhantomData<&'a GicrSgi>,
}
impl GicRedistributorIterator<'_> {
pub unsafe fn new(base: NonNull<GicrSgi>, gic_v4: bool) -> Self {
Self {
pointer: Some(base),
gic_v4,
phantom: PhantomData,
}
}
}
impl<'a> Iterator for GicRedistributorIterator<'a> {
type Item = GicRedistributor<'a>;
fn next(&mut self) -> Option<Self::Item> {
let pointer = self.pointer?;
let redist = unsafe { UniqueMmioPointer::new(pointer) };
let gicr = field_shared!(redist, gicr);
let typer = field_shared!(gicr, typer).read();
self.pointer = if !typer.last_redistributor() {
let redistributor_size = if self.gic_v4 && typer.virtual_lpis_supported() {
2
} else {
1
};
unsafe { Some(pointer.add(redistributor_size)) }
} else {
None
};
Some(GicRedistributor::new(redist))
}
}
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_INTERRUPT: 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::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::index($n..($n + $bits))])
}
define_context_registers!(@step $n + $bits ; $( $rest ),*);
};
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GicRedistributorContext<const IREG_COUNT: usize> {
propbaser: u64,
pendbaser: u64,
ctlr: GicrCtlr,
nsacr: u32,
iregs: [u32; IREG_COUNT],
}
impl GicRedistributorContext<0> {
pub const fn ireg_count(ppi_count: usize) -> usize {
assert!(
ppi_count.is_multiple_of(32),
"ppi_count must be multiple of 32"
);
(ppi_count * GicRedistributorContext::<0>::BITS_PER_INTERRUPT).div_ceil(32)
}
}
impl<const IREG_COUNT: usize> GicRedistributorContext<IREG_COUNT> {
const PPI_COUNT: usize = (IREG_COUNT * 32).div_ceil(Self::BITS_PER_INTERRUPT);
pub const fn new() -> Self {
Self {
propbaser: 0,
pendbaser: 0,
ctlr: GicrCtlr::empty(),
nsacr: 0,
iregs: [0; IREG_COUNT],
}
}
define_context_registers![
(igroupr, igroupr_mut, u32, Sgi::IGROUPR_BITS),
(isenabler, isenabler_mut, u32, Sgi::ISENABLER_BITS),
(ispendr, ispendr_mut, u32, Sgi::ISPENDR_BITS),
(isactiver, isactiver_mut, u32, Sgi::ISACTIVER_BITS),
(igrpmodr, igrpmodr_mut, u32, Sgi::IGRPMODR_BITS),
(icfgr, icfgr_mut, u32, Sgi::ICFGR_BITS),
(ipriorityr, ipriorityr_mut, u8, Sgi::IPRIORITY_BITS)
];
const fn index(bits: Range<usize>) -> Range<usize> {
const {
assert!(
IREG_COUNT.is_multiple_of(Self::BITS_PER_INTERRUPT),
"IREG_COUNT must be multiple of BITS_PER_INTERRUPT"
)
};
(bits.start * Self::PPI_COUNT / 32)..(bits.end * Self::PPI_COUNT / 32)
}
}
impl<const IREG_COUNT: usize> Default for GicRedistributorContext<IREG_COUNT> {
fn default() -> Self {
Self::new()
}
}
pub struct GicRedistributor<'a> {
regs: UniqueMmioPointer<'a, GicrSgi>,
}
impl<'a> GicRedistributor<'a> {
pub fn new(regs: UniqueMmioPointer<'a, GicrSgi>) -> Self {
Self { regs }
}
pub fn configure_default_settings(&mut self) {
let ppi_count = self.ppi_count();
let mut sgi = field!(self.regs, sgi);
set_regs(
field!(sgi, icenabler),
0,
ppi_count,
Sgi::ICENABLER_BITS,
0xffff_ffffu32,
);
self.wait_for_pending_write();
let mut sgi = field!(self.regs, sgi);
set_regs(
field!(sgi, igroupr),
0,
ppi_count,
Sgi::IGROUPR_BITS,
0xffff_ffffu32,
);
let mut ipriorityr_bytes = field!(sgi, ipriorityr);
let ipriority_words = unsafe {
UniqueMmioPointer::new(
ipriorityr_bytes
.ptr_nonnull()
.cast::<[ReadPureWrite<u32>; 96 / 4]>(),
)
};
let ipriority_word_value = u32::from_le_bytes([
HIGHEST_NS_PRIORITY,
HIGHEST_NS_PRIORITY,
HIGHEST_NS_PRIORITY,
HIGHEST_NS_PRIORITY,
]);
set_regs(
ipriority_words,
0,
ppi_count / 4,
Sgi::IPRIORITY_BITS * 4,
ipriority_word_value,
);
set_regs(
field!(sgi, icfgr),
IntId::PPI_START as usize,
ppi_count - IntId::PPI_START as usize,
Sgi::ICFGR_BITS,
0x0000_0000u32,
);
}
pub fn set_interrupt_priority(&mut self, intid: IntId, priority: u8) -> Result<(), GicError> {
let index = Self::private_index(intid)?;
let mut sgi = field!(self.regs, sgi);
field!(sgi, ipriorityr).get(index).unwrap().write(priority);
Ok(())
}
pub fn set_trigger(&mut self, intid: IntId, trigger: Trigger) -> Result<(), GicError> {
let index = Self::private_index(intid)?;
const INT_PER_REGS: usize = 32 / Sgi::ICFGR_BITS;
let reg_index = index / INT_PER_REGS;
let bit = 1 << (((index % INT_PER_REGS) * Sgi::ICFGR_BITS) + 1);
let mut sgi = field!(self.regs, sgi);
let mut icfgr = field!(sgi, icfgr);
let mut register = icfgr.get(reg_index).unwrap();
register.modify(|v| match trigger {
Trigger::Edge => v | bit,
Trigger::Level => v & !bit,
});
Ok(())
}
pub fn set_group(&mut self, intid: IntId, group: Group) -> Result<(), GicError> {
let index = Self::private_index(intid)?;
let mut sgi = field!(self.regs, sgi);
if let Group::Secure(sg) = group {
clear_bit(field!(sgi, igroupr), index);
let igrpmodr = field!(sgi, igrpmodr);
match sg {
SecureIntGroup::Group1S => set_bit(igrpmodr, index),
SecureIntGroup::Group0 => clear_bit(igrpmodr, index),
}
} else {
set_bit(field!(sgi, igroupr), index);
clear_bit(field!(sgi, igrpmodr), index);
}
Ok(())
}
pub fn enable_interrupt(&mut self, intid: IntId, enable: bool) -> Result<(), GicError> {
let index = Self::private_index(intid)?;
let mut sgi = field!(self.regs, sgi);
if enable {
set_bit(field!(sgi, isenabler), index);
} else {
set_bit(field!(sgi, icenabler), index);
}
Ok(())
}
pub fn enable_all_interrupts(&mut self, enable: bool) {
let ppi_count = self.ppi_count();
let mut sgi = field!(self.regs, sgi);
let mut regs = if enable {
field!(sgi, isenabler)
} else {
field!(sgi, icenabler)
};
assert_eq!(Sgi::ISENABLER_BITS, Sgi::ICENABLER_BITS);
let bits = 0xffff_ffff;
for i in 0..register_count(ppi_count, Sgi::ISENABLER_BITS, &bits) {
regs.get(i).unwrap().write(bits);
}
}
pub fn restore<const N: usize>(
&mut self,
context: &GicRedistributorContext<N>,
) -> Result<(), GicError> {
let ppi_count = Self::min_count(self.ppi_count(), GicRedistributorContext::<N>::PPI_COUNT)?;
let mut sgi = field!(self.regs, sgi);
set_regs(
field!(sgi, icenabler),
0,
ppi_count,
Sgi::ISENABLER_BITS,
0xffff_ffffu32,
);
self.wait_for_pending_write();
let mut gicr = field!(self.regs, gicr);
field!(gicr, ctlr).write(context.ctlr - GicrCtlr::EnableLPIs);
self.wait_for_pending_write();
let mut gicr = field!(self.regs, gicr);
restore_reg!(context, gicr, propbaser);
restore_reg!(context, gicr, pendbaser);
let mut sgi = field!(self.regs, sgi);
restore_regs!(
context.igroupr(),
sgi,
igroupr,
ppi_count,
Sgi::IGROUPR_BITS
);
restore_regs!(
context.igrpmodr(),
sgi,
igrpmodr,
ppi_count,
Sgi::IGRPMODR_BITS
);
restore_regs!(
context.ipriorityr(),
sgi,
ipriorityr,
ppi_count,
Sgi::IPRIORITY_BITS
);
restore_regs!(context.icfgr(), sgi, icfgr, ppi_count, Sgi::ICFGR_BITS);
restore_reg!(context, sgi, nsacr);
restore_regs!(
context.ispendr(),
sgi,
ispendr,
ppi_count,
Sgi::ISPENDR_BITS
);
restore_regs!(
context.isactiver(),
sgi,
isactiver,
ppi_count,
Sgi::ISACTIVER_BITS
);
self.wait_for_upstream_pending_write();
let mut sgi = field!(self.regs, sgi);
restore_regs!(
context.isenabler(),
sgi,
isenabler,
ppi_count,
Sgi::ISENABLER_BITS
);
let mut gicr = field!(self.regs, gicr);
restore_reg!(context, gicr, ctlr);
self.wait_for_pending_write();
Ok(())
}
pub fn save<const N: usize>(
&self,
context: &mut GicRedistributorContext<N>,
) -> Result<(), GicError> {
self.wait_for_pending_write();
let gicr = field_shared!(self.regs, gicr);
save_reg!(context, gicr, propbaser);
save_reg!(context, gicr, pendbaser);
save_reg!(context, gicr, ctlr);
let ppi_count = Self::min_count(self.ppi_count(), GicRedistributorContext::<N>::PPI_COUNT)?;
let sgi = field_shared!(self.regs, sgi);
save_reg!(context, sgi, nsacr);
save_regs!(
context.igroupr_mut(),
sgi,
igroupr,
ppi_count,
Sgi::IGROUPR_BITS
);
save_regs!(
context.isenabler_mut(),
sgi,
isenabler,
ppi_count,
Sgi::ISENABLER_BITS
);
save_regs!(
context.ispendr_mut(),
sgi,
ispendr,
ppi_count,
Sgi::ISPENDR_BITS
);
save_regs!(
context.isactiver_mut(),
sgi,
isactiver,
ppi_count,
Sgi::ISACTIVER_BITS
);
save_regs!(
context.igrpmodr_mut(),
sgi,
igrpmodr,
ppi_count,
Sgi::IGRPMODR_BITS
);
save_regs!(context.icfgr_mut(), sgi, icfgr, ppi_count, Sgi::ICFGR_BITS);
save_regs!(
context.ipriorityr_mut(),
sgi,
ipriorityr,
ppi_count,
Sgi::IPRIORITY_BITS
);
Ok(())
}
pub fn power_on(&mut self) {
if self.is_gic600_700() {
self.gic600_700_power_on();
}
}
pub fn power_off(&mut self) {
if self.is_gic600_700() {
self.gic600_700_power_off();
}
}
fn wait_until_group_not_in_transit(&self) {
let gicr = field_shared!(self.regs, gicr);
let pwrr = field_shared!(gicr, pwrr).read();
while pwrr.contains(GicrPwrr::RedistributorGroupPowerDown)
!= pwrr.contains(GicrPwrr::RedistributorGroupPoweredOff)
{
spin_loop();
}
}
fn is_gic600_700(&self) -> bool {
let gicr = field_shared!(self.regs, gicr);
let iidr = field_shared!(gicr, iidr).read();
iidr.model_id() == GicrIidr::MODEL_ID_ARM_GIC_600
|| iidr.model_id() == GicrIidr::MODEL_ID_ARM_GIC_600AE
|| iidr.model_id() == GicrIidr::MODEL_ID_ARM_GIC_700
}
fn gic600_700_power_on(&mut self) {
loop {
self.wait_until_group_not_in_transit();
let mut gicr = field!(self.regs, gicr);
let mut pwrr = field!(gicr, pwrr);
pwrr.write(GicrPwrr::empty());
if !pwrr.read().contains(GicrPwrr::RedistributorPowerDown) {
break;
}
}
}
fn gic600_700_power_off(&mut self) {
self.wait_until_group_not_in_transit();
let mut gicr = field!(self.regs, gicr);
let mut pwrr = field!(gicr, pwrr);
pwrr.write(GicrPwrr::RedistributorPowerDown);
if pwrr.read().contains(GicrPwrr::RedistributorGroupPowerDown) {
self.wait_until_group_not_in_transit();
}
}
pub fn mark_core_awake(&mut self) -> Result<(), GicError> {
let mut gicr = field!(self.regs, gicr);
let mut waker = field!(gicr, waker);
let mut gicr_waker = waker.read();
if !gicr_waker.contains(Waker::CHILDREN_ASLEEP) {
return Err(GicError::AlreadyAwake);
}
gicr_waker -= Waker::PROCESSOR_SLEEP;
waker.write(gicr_waker);
while waker.read().contains(Waker::CHILDREN_ASLEEP) {
spin_loop();
}
Ok(())
}
pub fn mark_core_asleep(&mut self) -> Result<(), GicError> {
let mut gicr = field!(self.regs, gicr);
let mut waker = field!(gicr, waker);
let mut gicr_waker = waker.read();
if gicr_waker.contains(Waker::CHILDREN_ASLEEP) {
return Err(GicError::AlreadyAsleep);
}
gicr_waker |= Waker::PROCESSOR_SLEEP;
waker.write(gicr_waker);
while !waker.read().contains(Waker::CHILDREN_ASLEEP) {
spin_loop();
}
Ok(())
}
pub fn wait_for_pending_write(&self) {
self.wait_ctlr(GicrCtlr::RWP);
}
pub fn wait_for_upstream_pending_write(&self) {
self.wait_ctlr(GicrCtlr::UWP);
}
pub fn typer(&self) -> GicrTyper {
let gicr = field_shared!(self.regs, gicr);
field_shared!(gicr, typer).read()
}
fn wait_ctlr(&self, flag: GicrCtlr) {
let gicr = field_shared!(self.regs, gicr);
let ctlr = field_shared!(gicr, ctlr);
while ctlr.read().contains(flag) {
spin_loop();
}
}
fn ppi_count(&self) -> usize {
let gicr = field_shared!(self.regs, gicr);
let typer = field_shared!(gicr, typer).read();
let ppi_num = typer.max_eppi_count();
ppi_num as usize + 32
}
fn private_index(intid: IntId) -> Result<usize, GicError> {
match intid.private_index() {
Some(index) => Ok(index),
None => Err(GicError::InvalidGicrIntid(intid)),
}
}
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 = 2 * 64 * 1024 / 4;
pub fn new() -> Self {
Self {
regs: [0u32; Self::REG_COUNT],
}
}
}
struct FakeRedistributor {
regs: FakeRegisters,
}
impl FakeRedistributor {
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<'_, GicrSgi> {
UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
}
pub fn redistributor_for_test(&mut self) -> GicRedistributor<'_> {
GicRedistributor::new(self.get())
}
}
#[derive(Debug)]
struct TestCase<T> {
intid: IntId,
offset: usize,
expected_value: u32,
param: T,
}
impl<T> TestCase<T> {
pub const fn new(intid: IntId, offset: usize, expected_value: u32, param: T) -> Self {
Self {
intid,
offset,
expected_value,
param,
}
}
}
#[test]
fn configure_default_settings() {
let mut regs = FakeRedistributor::new();
regs.regs_write(0x0_008, 1 << 27);
let mut redistributor = regs.redistributor_for_test();
redistributor.configure_default_settings();
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0080));
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0084));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0088));
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0180));
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0184));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0188));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0400));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0404));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0408));
assert_eq!(0x8080_8080, regs.reg_read(0x1_040c));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0410));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0414));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0418));
assert_eq!(0x8080_8080, regs.reg_read(0x1_041c));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0420));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0424));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0428));
assert_eq!(0x8080_8080, regs.reg_read(0x1_042c));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0430));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0434));
assert_eq!(0x8080_8080, regs.reg_read(0x1_0438));
assert_eq!(0x8080_8080, regs.reg_read(0x1_043c));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0440));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0444));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0448));
assert_eq!(0x0000_0000, regs.reg_read(0x1_044c));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0450));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0454));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0458));
assert_eq!(0x0000_0000, regs.reg_read(0x1_045c));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0c00));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0c04));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0c08));
}
#[test]
fn set_interrupt_priority() {
let tests = [
TestCase::new(IntId::sgi(0), 0x1_0400, 0x0000_00ab, 0xab),
TestCase::new(IntId::sgi(15), 0x1_040c, 0xcd00_0000, 0xcd),
TestCase::new(IntId::ppi(0), 0x1_0410, 0x0000_0012, 0x12),
TestCase::new(IntId::ppi(15), 0x1_041c, 0x3400_0000, 0x34),
TestCase::new(IntId::eppi(0), 0x1_0420, 0x0000_0056, 0x56),
TestCase::new(IntId::eppi(63), 0x1_045c, 0x7800_0000, 0x78),
];
let mut regs = FakeRedistributor::new();
for test in tests {
let mut redistributor = regs.redistributor_for_test();
assert_eq!(
Ok(()),
redistributor.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.redistributor_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::InvalidGicrIntid(IntId::spi(0))),
redistributor.set_interrupt_priority(IntId::spi(0), 0x12)
);
assert_eq!(0x1200_abcd, regs.reg_read(0x1_0400));
}
#[test]
fn set_trigger() {
let tests = [
TestCase::new(IntId::sgi(0), 0x1_0c00, 0x0000_0002, Trigger::Edge),
TestCase::new(IntId::sgi(15), 0x1_0c00, 0x8000_0000, Trigger::Edge),
TestCase::new(IntId::ppi(0), 0x1_0c04, 0x0000_0002, Trigger::Edge),
TestCase::new(IntId::ppi(15), 0x1_0c04, 0x8000_0000, Trigger::Edge),
TestCase::new(IntId::eppi(0), 0x1_0c08, 0x0000_0002, Trigger::Edge),
TestCase::new(IntId::eppi(63), 0x1_0c14, 0x8000_0000, Trigger::Edge),
];
let mut regs = FakeRedistributor::new();
for test in tests {
let mut redistributor = regs.redistributor_for_test();
assert_eq!(Ok(()), redistributor.set_trigger(test.intid, test.param));
assert_eq!(
test.expected_value,
regs.reg_read(test.offset),
"test case: {test:x?}",
);
regs.clear();
}
let mut redistributor = regs.redistributor_for_test();
assert_eq!(
Ok(()),
redistributor.set_trigger(IntId::sgi(0), Trigger::Edge)
);
assert_eq!(
Ok(()),
redistributor.set_trigger(IntId::sgi(1), Trigger::Edge)
);
assert_eq!(
Ok(()),
redistributor.set_trigger(IntId::sgi(3), Trigger::Edge)
);
assert_eq!(
Ok(()),
redistributor.set_trigger(IntId::sgi(3), Trigger::Level)
);
assert_eq!(
Err(GicError::InvalidGicrIntid(IntId::spi(0))),
redistributor.set_trigger(IntId::spi(0), Trigger::Level)
);
assert_eq!(0x0000_000a, regs.reg_read(0x1_0c00));
}
#[test]
fn set_group() {
let tests = [
TestCase::new(IntId::sgi(0), 0, 1, Group::Group1NS),
TestCase::new(IntId::sgi(15), 15, 0, Group::Secure(SecureIntGroup::Group0)),
TestCase::new(IntId::ppi(0), 16, 2, Group::Secure(SecureIntGroup::Group1S)),
TestCase::new(IntId::ppi(15), 31, 1, Group::Group1NS),
TestCase::new(IntId::eppi(0), 32, 0, Group::Secure(SecureIntGroup::Group0)),
TestCase::new(
IntId::eppi(63),
95,
2,
Group::Secure(SecureIntGroup::Group1S),
),
];
let mut regs = FakeRedistributor::new();
for test in tests {
let mut redistributor = regs.redistributor_for_test();
assert_eq!(Ok(()), redistributor.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);
assert_eq!(
expected_igroupr,
regs.reg_read(0x1_0080 + test.offset / 32 * 4),
"test case: {test:x?}",
);
assert_eq!(
exptected_igrpmodr,
regs.reg_read(0x1_0d00 + test.offset / 32 * 4),
"test case: {test:x?}",
);
regs.clear();
}
let mut redistributor = regs.redistributor_for_test();
assert_eq!(
Ok(()),
redistributor.set_group(IntId::sgi(0), Group::Group1NS)
);
assert_eq!(
Ok(()),
redistributor.set_group(IntId::sgi(1), Group::Secure(SecureIntGroup::Group0))
);
assert_eq!(
Ok(()),
redistributor.set_group(IntId::sgi(3), Group::Secure(SecureIntGroup::Group1S))
);
assert_eq!(
Ok(()),
redistributor.set_group(IntId::sgi(3), Group::Group1NS)
);
assert_eq!(
Err(GicError::InvalidGicrIntid(IntId::spi(0))),
redistributor.set_group(IntId::spi(0), Group::Group1NS)
);
assert_eq!(0x0000_0009, regs.reg_read(0x1_0080));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0d00));
}
#[test]
fn enable_interrupt() {
let tests = [
TestCase::new(IntId::sgi(0), 0x1_0100, 0x0000_0001, true),
TestCase::new(IntId::sgi(15), 0x1_0180, 0x0000_8000, false),
TestCase::new(IntId::ppi(0), 0x1_0100, 0x0001_0000, true),
TestCase::new(IntId::ppi(15), 0x1_0180, 0x8000_0000, false),
TestCase::new(IntId::eppi(0), 0x1_0104, 0x0000_0001, true),
TestCase::new(IntId::eppi(63), 0x1_0188, 0x8000_0000, false),
];
let mut regs = FakeRedistributor::new();
for test in tests {
let mut redistributor = regs.redistributor_for_test();
assert_eq!(
Ok(()),
redistributor.enable_interrupt(test.intid, test.param)
);
assert_eq!(
test.expected_value,
regs.reg_read(test.offset),
"test case: {test:x?}",
);
regs.clear();
}
let mut redistributor = regs.redistributor_for_test();
assert_eq!(Ok(()), redistributor.enable_interrupt(IntId::sgi(0), true));
assert_eq!(Ok(()), redistributor.enable_interrupt(IntId::sgi(1), true));
assert_eq!(Ok(()), redistributor.enable_interrupt(IntId::sgi(3), true));
assert_eq!(Ok(()), redistributor.enable_interrupt(IntId::sgi(3), false));
assert_eq!(
Err(GicError::InvalidGicrIntid(IntId::spi(0))),
redistributor.enable_interrupt(IntId::spi(0), false)
);
assert_eq!(0x0000_000b, regs.reg_read(0x1_0100));
assert_eq!(0x0000_0008, regs.reg_read(0x1_0180));
}
#[test]
fn enable_all_interrupts() {
let mut regs = FakeRedistributor::new();
{
let mut redistributor = regs.redistributor_for_test();
redistributor.enable_all_interrupts(true);
}
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0100));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0104));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0108));
regs.clear();
regs.regs_write(0x0_008, 1 << 27);
{
let mut redistributor = regs.redistributor_for_test();
redistributor.enable_all_interrupts(false);
}
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0180));
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0184));
assert_eq!(0x0000_0000, regs.reg_read(0x1_0188));
regs.clear();
regs.regs_write(0x0_008, 2 << 27);
{
let mut redistributor = regs.redistributor_for_test();
redistributor.enable_all_interrupts(true);
}
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0100));
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0104));
assert_eq!(0xffff_ffff, regs.reg_read(0x1_0108));
}
#[test]
fn context_size() {
assert_eq!(
(24usize + 32 * 15 / 8).next_multiple_of(size_of::<usize>()),
size_of::<GicRedistributorContext::<{ GicRedistributorContext::ireg_count(32) }>>()
);
assert_eq!(
(24usize + 64 * 15 / 8).next_multiple_of(size_of::<usize>()),
size_of::<GicRedistributorContext::<{ GicRedistributorContext::ireg_count(64) }>>()
);
assert_eq!(
(24usize + 96 * 15 / 8).next_multiple_of(size_of::<usize>()),
size_of::<GicRedistributorContext::<{ GicRedistributorContext::ireg_count(96) }>>()
);
}
#[test]
fn save_restore() {
let mut context =
GicRedistributorContext::<{ GicRedistributorContext::ireg_count(32) }>::new();
let saved_offsets = [
0x0_0000..=0x0_0000, 0x0_0070..=0x0_0070, 0x0_0078..=0x0_0078, 0x1_0080..=0x1_0080, 0x1_0100..=0x1_0100, 0x1_0200..=0x1_0200, 0x1_0300..=0x1_0300, 0x1_0400..=0x1_041c, 0x1_0c00..=0x1_0c04, 0x1_0d00..=0x1_0d00, 0x1_0e00..=0x1_0e00, ];
fn generate_value(offset: usize) -> u32 {
(offset * 2 + 1) as u32
}
let mut regs = FakeRedistributor::new();
for offset_range in &saved_offsets {
for offset in offset_range.clone().step_by(4) {
regs.regs_write(offset, generate_value(offset));
}
}
{
let redistributor = regs.redistributor_for_test();
assert_eq!(Ok(()), redistributor.save(&mut context));
}
regs.clear();
{
let mut redistributor = regs.redistributor_for_test();
assert_eq!(Ok(()), redistributor.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 =
GicRedistributorContext::<{ GicRedistributorContext::ireg_count(96) }>::new();
let saved_offsets = [
0x0_0000..=0x0_0000, 0x0_0070..=0x0_0070, 0x0_0078..=0x0_0078, 0x1_0080..=0x1_0088, 0x1_0100..=0x1_0108, 0x1_0200..=0x1_0208, 0x1_0300..=0x1_0308, 0x1_0400..=0x1_045c, 0x1_0c00..=0x1_0c14, 0x1_0d00..=0x1_0d08, 0x1_0e00..=0x1_0e00, ];
fn generate_value(offset: usize) -> u32 {
(offset * 2 + 1) as u32
}
let mut regs = FakeRedistributor::new();
regs.regs_write(0x0_008, 2 << 27);
for offset_range in &saved_offsets {
for offset in offset_range.clone().step_by(4) {
regs.regs_write(offset, generate_value(offset));
}
}
{
let redistributor = regs.redistributor_for_test();
assert_eq!(Ok(()), redistributor.save(&mut context));
}
regs.clear();
regs.regs_write(0x0_008, 2 << 27);
{
let mut redistributor = regs.redistributor_for_test();
assert_eq!(Ok(()), redistributor.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 mark_core_awake() {
let mut regs = FakeRedistributor::new();
let mut redistributor = regs.redistributor_for_test();
assert_eq!(Err(GicError::AlreadyAwake), redistributor.mark_core_awake());
}
#[test]
fn mark_core_asleep() {
let mut regs = FakeRedistributor::new();
regs.regs_write(0x14, 0x0000_0004);
let mut redistributor = regs.redistributor_for_test();
assert_eq!(
Err(GicError::AlreadyAsleep),
redistributor.mark_core_asleep()
);
}
#[test]
fn wait_for_pending_write() {
let mut regs = FakeRedistributor::new();
let redistributor = regs.redistributor_for_test();
redistributor.wait_for_pending_write();
}
#[test]
fn wait_for_upstream_pending_write() {
let mut regs = FakeRedistributor::new();
let redistributor = regs.redistributor_for_test();
redistributor.wait_for_upstream_pending_write();
}
#[test]
fn typer() {
let mut regs = FakeRedistributor::new();
regs.regs_write(0x0_0008, 0xabcd_0123);
let redistributor = regs.redistributor_for_test();
let typer = redistributor.typer();
assert_eq!(0xcd01, typer.processor_number());
}
}