#![cfg_attr(not(any(test, feature = "fakes")), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(unsafe_op_in_unsafe_fn)]
pub mod gicv2;
pub mod gicv3;
#[cfg(any(test, feature = "fakes", target_arch = "aarch64", target_arch = "arm"))]
mod sysreg;
pub use safe_mmio::UniqueMmioPointer;
use safe_mmio::fields::ReadPureWrite;
use thiserror::Error;
use zerocopy::{FromZeros, Immutable, IntoBytes, KnownLayout};
#[cfg(all(target_arch = "aarch64", not(feature = "fakes")))]
use core::arch::asm;
use core::fmt::{self, Debug, Formatter};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Trigger {
Edge,
Level,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum InterruptGroup {
Group0,
Group1,
}
#[derive(
Copy, Clone, Eq, FromZeros, Immutable, IntoBytes, KnownLayout, Ord, PartialOrd, PartialEq,
)]
#[repr(transparent)]
pub struct IntId(u32);
impl IntId {
pub const SPECIAL_SECURE: Self = Self(1020);
pub const SPECIAL_NONSECURE: Self = Self(1021);
pub const SPECIAL_NMI: Self = Self(1022);
pub const SPECIAL_NONE: Self = Self(1023);
pub const MAX_SPI_COUNT: u32 = Self::SPECIAL_START - Self::SPI_START;
pub const SGI_COUNT: u32 = Self::PPI_START - Self::SGI_START;
pub const PPI_COUNT: u32 = Self::SPI_START - Self::PPI_START;
pub const MAX_EPPI_COUNT: u32 = Self::EPPI_END - Self::EPPI_START;
pub const MAX_ESPI_COUNT: u32 = Self::ESPI_END - Self::ESPI_START;
pub const MAX_LPI_COUNT: u32 = Self::LPI_END - Self::LPI_START;
const SGI_START: u32 = 0;
const PPI_START: u32 = 16;
const SPI_START: u32 = 32;
const SPECIAL_START: u32 = 1020;
const SPECIAL_END: u32 = 1024;
const EPPI_START: u32 = 1056;
const EPPI_END: u32 = 1120;
const ESPI_START: u32 = 4096;
const ESPI_END: u32 = 5120;
const LPI_START: u32 = 8192;
const LPI_END: u32 = 1 << 24;
pub const fn sgi(sgi: u32) -> Self {
assert!(sgi < Self::SGI_COUNT);
Self(Self::SGI_START + sgi)
}
pub const fn ppi(ppi: u32) -> Self {
assert!(ppi < Self::PPI_COUNT);
Self(Self::PPI_START + ppi)
}
pub const fn spi(spi: u32) -> Self {
assert!(spi < Self::MAX_SPI_COUNT);
Self(Self::SPI_START + spi)
}
pub const fn eppi(eppi: u32) -> Self {
assert!(eppi < Self::MAX_EPPI_COUNT);
Self(Self::EPPI_START + eppi)
}
pub const fn espi(espi: u32) -> Self {
assert!(espi < Self::MAX_ESPI_COUNT);
Self(Self::ESPI_START + espi)
}
pub const fn lpi(lpi: u32) -> Self {
assert!(lpi < Self::MAX_LPI_COUNT);
Self(Self::LPI_START + lpi)
}
pub const fn is_sgi(self) -> bool {
self.0 < Self::PPI_START
}
pub const fn is_ppi(self) -> bool {
Self::PPI_START <= self.0 && self.0 < Self::SPI_START
}
pub const fn is_eppi(self) -> bool {
Self::EPPI_START <= self.0 && self.0 < Self::EPPI_END
}
pub const fn is_spi(self) -> bool {
Self::SPI_START <= self.0 && self.0 < Self::SPECIAL_START
}
pub const fn is_espi(self) -> bool {
Self::ESPI_START <= self.0 && self.0 < Self::ESPI_END
}
pub const fn is_private(self) -> bool {
self.is_sgi() || self.is_ppi() || self.is_eppi()
}
pub const fn sgi_index(self) -> Option<u32> {
if self.is_sgi() {
Some(self.0 - Self::SGI_START)
} else {
None
}
}
pub const fn private_index(self) -> Option<usize> {
if self.is_sgi() || self.is_ppi() {
Some(self.0 as usize)
} else if self.is_eppi() {
Some((self.0 - IntId::EPPI_START + IntId::SGI_COUNT + IntId::PPI_COUNT) as usize)
} else {
None
}
}
pub const fn spi_index(self) -> Option<usize> {
if self.is_spi() {
Some((self.0 - Self::SPI_START) as usize)
} else {
None
}
}
pub const fn espi_index(self) -> Option<usize> {
if self.is_espi() {
Some((self.0 - Self::ESPI_START) as usize)
} else {
None
}
}
pub const fn raw_value(self) -> u32 {
self.0
}
pub fn private() -> impl Iterator<Item = IntId> {
let sgis = (0..Self::SGI_COUNT).map(Self::sgi);
let ppis = (0..Self::PPI_COUNT).map(Self::ppi);
sgis.chain(ppis)
}
pub fn spis() -> impl Iterator<Item = IntId> {
(0..Self::MAX_SPI_COUNT).map(Self::spi)
}
}
impl Debug for IntId {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.0 < Self::PPI_START {
write!(f, "SGI {}", self.0 - Self::SGI_START)
} else if self.0 < Self::SPI_START {
write!(f, "PPI {}", self.0 - Self::PPI_START)
} else if self.0 < Self::SPECIAL_START {
write!(f, "SPI {}", self.0 - Self::SPI_START)
} else if self.0 < Self::SPECIAL_END {
write!(f, "Special IntId {}", self.0)
} else if self.0 < Self::EPPI_START {
write!(f, "Reserved IntId {}", self.0)
} else if self.0 < Self::EPPI_END {
write!(f, "EPPI {}", self.0 - Self::EPPI_START)
} else if self.0 < Self::ESPI_START {
write!(f, "Reserved IntId {}", self.0)
} else if self.0 < Self::ESPI_END {
write!(f, "ESPI {}", self.0 - Self::ESPI_START)
} else if self.0 < Self::LPI_START {
write!(f, "Reserved IntId {}", self.0)
} else {
write!(f, "LPI {}", self.0 - Self::LPI_START)
}
}
}
impl From<IntId> for u32 {
fn from(intid: IntId) -> Self {
intid.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Error)]
#[error("Invalid (reserved) interrupt ID {0}")]
pub struct InvalidIntId(u32);
impl TryFrom<u32> for IntId {
type Error = InvalidIntId;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if (Self::SPECIAL_END..Self::EPPI_START).contains(&value)
|| (Self::EPPI_END..Self::ESPI_START).contains(&value)
|| (Self::ESPI_END..Self::LPI_START).contains(&value)
|| value >= Self::LPI_END
{
Err(InvalidIntId(value))
} else {
Ok(Self(value))
}
}
}
#[cfg(all(target_arch = "aarch64", not(feature = "fakes")))]
pub fn irq_disable() {
unsafe {
asm!("msr DAIFSet, #0xf", options(nomem, nostack));
}
}
#[cfg(any(test, feature = "fakes"))]
pub fn irq_disable() {}
#[cfg(all(target_arch = "aarch64", not(feature = "fakes")))]
pub fn irq_enable() {
unsafe {
asm!("msr DAIFClr, #0xf", options(nomem, nostack));
}
}
#[cfg(any(test, feature = "fakes"))]
pub fn irq_enable() {}
#[cfg(all(target_arch = "aarch64", not(feature = "fakes")))]
pub fn wfi() {
unsafe {
asm!("wfi", options(nomem, nostack));
}
}
#[cfg(any(test, feature = "fakes"))]
pub fn wfi() {
}
fn modify_bit<const N: usize>(
mut registers: UniqueMmioPointer<[ReadPureWrite<u32>; N]>,
nth: usize,
set_bit: bool,
) {
let reg_num: usize = nth / 32;
let bit_num: usize = nth % 32;
let bit_mask: u32 = 1 << bit_num;
let mut reg_ptr = registers.get(reg_num).unwrap();
reg_ptr.modify(|old_value| {
if set_bit {
old_value | bit_mask
} else {
old_value & !bit_mask
}
});
}
fn set_bit<const N: usize>(registers: UniqueMmioPointer<[ReadPureWrite<u32>; N]>, nth: usize) {
modify_bit(registers, nth, true);
}
fn clear_bit<const N: usize>(registers: UniqueMmioPointer<[ReadPureWrite<u32>; N]>, nth: usize) {
modify_bit(registers, nth, false);
}