use alloc::boxed::Box;
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct IrqDomainId(pub u16);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct HwIrq(pub u32);
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct IrqId {
pub domain: IrqDomainId,
pub hwirq: HwIrq,
}
impl IrqId {
pub const fn new(domain: IrqDomainId, hwirq: HwIrq) -> Self {
Self { domain, hwirq }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TrapVector(pub usize);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IrqSource {
AcpiGsi(u32),
AcpiGsiRoute(AcpiGsiRoute),
ControllerLine {
domain: IrqDomainId,
hwirq: HwIrq,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IrqTrigger {
Edge,
Level,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AcpiIrqTrigger {
Edge,
Level,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AcpiIrqPolarity {
ActiveHigh,
ActiveLow,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AcpiGsiController {
IoApic,
PchPic,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AcpiGsiRoute {
pub gsi: u32,
pub vector: usize,
pub controller: AcpiGsiController,
pub controller_id: u16,
pub controller_address: u64,
pub controller_input: u8,
pub trigger: AcpiIrqTrigger,
pub polarity: AcpiIrqPolarity,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct CpuId(pub usize);
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CpuMask {
bits: u128,
}
impl CpuMask {
pub const fn empty() -> Self {
Self { bits: 0 }
}
pub fn from_cpu(cpu: CpuId) -> Self {
let mut mask = Self::empty();
mask.insert(cpu);
mask
}
pub fn first_n(cpu_count: usize) -> Self {
let mut mask = Self::empty();
let end = cpu_count.min(u128::BITS as usize);
for cpu in 0..end {
mask.insert(CpuId(cpu));
}
mask
}
pub fn insert(&mut self, cpu: CpuId) {
if cpu.0 < u128::BITS as usize {
self.bits |= 1u128 << cpu.0;
}
}
pub fn remove(&mut self, cpu: CpuId) {
if cpu.0 < u128::BITS as usize {
self.bits &= !(1u128 << cpu.0);
}
}
pub const fn contains(self, cpu: CpuId) -> bool {
cpu.0 < u128::BITS as usize && (self.bits & (1u128 << cpu.0)) != 0
}
pub const fn is_empty(self) -> bool {
self.bits == 0
}
pub fn iter(self) -> CpuMaskIter {
CpuMaskIter { bits: self.bits }
}
}
pub struct CpuMaskIter {
bits: u128,
}
impl Iterator for CpuMaskIter {
type Item = CpuId;
fn next(&mut self) -> Option<Self::Item> {
if self.bits == 0 {
return None;
}
let cpu = self.bits.trailing_zeros() as usize;
self.bits &= !(1u128 << cpu);
Some(CpuId(cpu))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IrqScope {
Global,
PerCpu {
cpus: CpuMask,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IrqAffinity {
Any,
Fixed(CpuId),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IrqExecution {
Concurrent,
NonReentrant,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ShareMode {
Exclusive,
Shared,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AutoEnable {
No,
Yes,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IrqReturn {
Unhandled,
Handled,
Wake,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct IrqOutcome {
pub handled: bool,
pub wake: bool,
pub called: usize,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct IrqStatus {
pub action_enabled: bool,
pub line_enabled: bool,
pub pending: bool,
pub in_service: bool,
pub in_flight: usize,
pub action_running: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
pub enum IrqError {
#[error("invalid IRQ number")]
InvalidIrq,
#[error("invalid CPU id")]
InvalidCpu,
#[error("target CPU is offline")]
CpuOffline,
#[error("IRQ operation timed out")]
Timeout,
#[error("IRQ line is busy")]
Busy,
#[error("IRQ allocation failed")]
NoMemory,
#[error("IRQ descriptor or action was not found")]
NotFound,
#[error("operation is not legal from IRQ context")]
InIrqContext,
#[error("IRQ operation is not supported")]
Unsupported,
#[error("interrupt controller failed")]
Controller,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct IrqContext {
pub irq: IrqId,
pub cpu: CpuId,
}
pub type BoxedIrqHandler = Box<dyn FnMut(IrqContext) -> IrqReturn + Send + 'static>;
pub type ConcurrentBoxedIrqHandler = Box<dyn Fn(IrqContext) -> IrqReturn + Send + Sync + 'static>;
pub(crate) enum IrqHandler {
NonReentrant(BoxedIrqHandler),
Concurrent(ConcurrentBoxedIrqHandler),
}
pub trait IrqOps {
type LocalIrqState: Copy;
fn current_cpu(&self) -> CpuId;
fn cpu_online(&self, cpu: CpuId) -> bool;
fn in_irq_context(&self) -> bool;
fn local_irq_save(&self) -> Self::LocalIrqState;
fn local_irq_restore(&self, state: Self::LocalIrqState);
fn run_on_cpu_sync(
&self,
cpu: CpuId,
f: unsafe fn(*mut ()),
arg: *mut (),
) -> Result<(), IrqError>;
fn set_affinity(&self, _irq: IrqId, _affinity: IrqAffinity) -> Result<(), IrqError> {
Err(IrqError::Unsupported)
}
fn set_enabled(&self, irq: IrqId, cpu: Option<CpuId>, enabled: bool) -> Result<(), IrqError>;
fn is_enabled(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError>;
fn is_pending(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError>;
fn is_in_service(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError>;
fn relax(&self);
}
pub struct IrqRequest {
pub(crate) handler: Option<IrqHandler>,
pub(crate) scope: IrqScope,
pub(crate) affinity: IrqAffinity,
pub(crate) execution: IrqExecution,
pub(crate) share_mode: ShareMode,
pub(crate) auto_enable: AutoEnable,
}
impl IrqRequest {
pub fn new(handler: impl FnMut(IrqContext) -> IrqReturn + Send + 'static) -> Self {
Self {
handler: Some(IrqHandler::NonReentrant(Box::new(handler))),
scope: IrqScope::Global,
affinity: IrqAffinity::Any,
execution: IrqExecution::NonReentrant,
share_mode: ShareMode::Exclusive,
auto_enable: AutoEnable::Yes,
}
}
pub fn new_concurrent(
handler: impl Fn(IrqContext) -> IrqReturn + Send + Sync + 'static,
) -> Self {
Self {
handler: Some(IrqHandler::Concurrent(Box::new(handler))),
scope: IrqScope::Global,
affinity: IrqAffinity::Any,
execution: IrqExecution::Concurrent,
share_mode: ShareMode::Exclusive,
auto_enable: AutoEnable::Yes,
}
}
pub(crate) fn supports_concurrent(&self) -> bool {
matches!(self.handler.as_ref(), Some(IrqHandler::Concurrent(_)))
}
pub fn scope(mut self, scope: IrqScope) -> Self {
self.scope = scope;
self
}
pub fn affinity(mut self, affinity: IrqAffinity) -> Self {
self.affinity = affinity;
self
}
pub fn execution(mut self, execution: IrqExecution) -> Self {
self.execution = execution;
self
}
pub fn share_mode(mut self, share_mode: ShareMode) -> Self {
self.share_mode = share_mode;
self
}
pub fn auto_enable(mut self, auto_enable: AutoEnable) -> Self {
self.auto_enable = auto_enable;
self
}
pub const fn auto_enable_mode(&self) -> AutoEnable {
self.auto_enable
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct IrqHandle {
pub(crate) irq: IrqId,
pub(crate) id: u64,
}
impl IrqHandle {
pub const fn irq(self) -> IrqId {
self.irq
}
pub const fn id(self) -> u64 {
self.id
}
}