Skip to main content

irq_framework/
types.rs

1use alloc::boxed::Box;
2
3/// An IRQ controller domain id.
4#[repr(transparent)]
5#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
6pub struct IrqDomainId(pub u16);
7
8/// Hardware interrupt line number within an IRQ domain.
9#[repr(transparent)]
10#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
11pub struct HwIrq(pub u32);
12
13/// A framework IRQ id, scoped by controller domain.
14#[repr(C)]
15#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
16pub struct IrqId {
17    /// IRQ controller domain.
18    pub domain: IrqDomainId,
19    /// Hardware interrupt line within the domain.
20    pub hwirq: HwIrq,
21}
22
23impl IrqId {
24    /// Creates an IRQ id from a domain and hardware line.
25    pub const fn new(domain: IrqDomainId, hwirq: HwIrq) -> Self {
26        Self { domain, hwirq }
27    }
28}
29
30/// CPU trap vector observed at the architecture trap boundary.
31#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
32pub struct TrapVector(pub usize);
33
34/// A firmware or controller interrupt source that can be resolved to [`IrqId`].
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum IrqSource {
37    /// ACPI Global System Interrupt.
38    AcpiGsi(u32),
39    /// ACPI Global System Interrupt with explicit routing metadata.
40    AcpiGsiRoute(AcpiGsiRoute),
41    /// Explicit controller-domain line.
42    ControllerLine {
43        /// IRQ controller domain.
44        domain: IrqDomainId,
45        /// Hardware interrupt line within the domain.
46        hwirq: HwIrq,
47    },
48}
49
50/// Controller interrupt trigger configuration.
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52pub enum IrqTrigger {
53    /// Edge-triggered interrupt.
54    Edge,
55    /// Level-triggered interrupt.
56    Level,
57}
58
59/// ACPI IRQ trigger configuration.
60#[derive(Clone, Copy, Debug, Eq, PartialEq)]
61pub enum AcpiIrqTrigger {
62    /// Edge-triggered interrupt.
63    Edge,
64    /// Level-triggered interrupt.
65    Level,
66}
67
68/// ACPI IRQ polarity configuration.
69#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub enum AcpiIrqPolarity {
71    /// Active-high interrupt.
72    ActiveHigh,
73    /// Active-low interrupt.
74    ActiveLow,
75}
76
77/// ACPI GSI controller kind.
78#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79pub enum AcpiGsiController {
80    /// I/O APIC controller.
81    IoApic,
82    /// LoongArch PCH-PIC controller.
83    PchPic,
84}
85
86/// Fully described ACPI GSI routing metadata.
87#[derive(Clone, Copy, Debug, Eq, PartialEq)]
88pub struct AcpiGsiRoute {
89    /// Global System Interrupt number.
90    pub gsi: u32,
91    /// CPU trap vector programmed by the platform controller, if known.
92    pub vector: usize,
93    /// Controller kind.
94    pub controller: AcpiGsiController,
95    /// ACPI controller id.
96    pub controller_id: u16,
97    /// Controller MMIO base address.
98    pub controller_address: u64,
99    /// Controller-local input line.
100    pub controller_input: u8,
101    /// Trigger configuration.
102    pub trigger: AcpiIrqTrigger,
103    /// Polarity configuration.
104    pub polarity: AcpiIrqPolarity,
105}
106
107/// A logical CPU id.
108#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
109pub struct CpuId(pub usize);
110
111/// A compact CPU mask for low-level IRQ affinity.
112#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
113pub struct CpuMask {
114    bits: u128,
115}
116
117impl CpuMask {
118    /// Creates an empty CPU mask.
119    pub const fn empty() -> Self {
120        Self { bits: 0 }
121    }
122
123    /// Creates a CPU mask containing a single CPU.
124    pub fn from_cpu(cpu: CpuId) -> Self {
125        let mut mask = Self::empty();
126        mask.insert(cpu);
127        mask
128    }
129
130    /// Creates a CPU mask containing CPUs in the range `0..cpu_count`.
131    pub fn first_n(cpu_count: usize) -> Self {
132        let mut mask = Self::empty();
133        let end = cpu_count.min(u128::BITS as usize);
134        for cpu in 0..end {
135            mask.insert(CpuId(cpu));
136        }
137        mask
138    }
139
140    /// Adds a CPU to this mask.
141    pub fn insert(&mut self, cpu: CpuId) {
142        if cpu.0 < u128::BITS as usize {
143            self.bits |= 1u128 << cpu.0;
144        }
145    }
146
147    /// Removes a CPU from this mask.
148    pub fn remove(&mut self, cpu: CpuId) {
149        if cpu.0 < u128::BITS as usize {
150            self.bits &= !(1u128 << cpu.0);
151        }
152    }
153
154    /// Returns whether the CPU is present in this mask.
155    pub const fn contains(self, cpu: CpuId) -> bool {
156        cpu.0 < u128::BITS as usize && (self.bits & (1u128 << cpu.0)) != 0
157    }
158
159    /// Returns whether no CPU is present in this mask.
160    pub const fn is_empty(self) -> bool {
161        self.bits == 0
162    }
163
164    /// Iterates over the CPUs in this mask.
165    pub fn iter(self) -> CpuMaskIter {
166        CpuMaskIter { bits: self.bits }
167    }
168}
169
170/// Iterator over [`CpuMask`].
171pub struct CpuMaskIter {
172    bits: u128,
173}
174
175impl Iterator for CpuMaskIter {
176    type Item = CpuId;
177
178    fn next(&mut self) -> Option<Self::Item> {
179        if self.bits == 0 {
180            return None;
181        }
182        let cpu = self.bits.trailing_zeros() as usize;
183        self.bits &= !(1u128 << cpu);
184        Some(CpuId(cpu))
185    }
186}
187
188/// IRQ registration scope.
189#[derive(Clone, Copy, Debug, Eq, PartialEq)]
190pub enum IrqScope {
191    /// The action is visible on every CPU.
192    Global,
193    /// The action is CPU-local and only visible to matching CPUs.
194    PerCpu {
195        /// Target CPUs.
196        cpus: CpuMask,
197    },
198}
199
200/// Hardware routing preference for an IRQ line.
201#[derive(Clone, Copy, Debug, Eq, PartialEq)]
202pub enum IrqAffinity {
203    /// The platform may route the line to any CPU.
204    Any,
205    /// Route the line to one fixed logical CPU.
206    Fixed(CpuId),
207}
208
209/// Execution contract for an IRQ action.
210#[derive(Clone, Copy, Debug, Eq, PartialEq)]
211pub enum IrqExecution {
212    /// The handler may run concurrently if the controller delivers it that way.
213    Concurrent,
214    /// The framework prevents nested/concurrent calls to this action.
215    NonReentrant,
216}
217
218/// Whether an IRQ line is exclusive or shared.
219#[derive(Clone, Copy, Debug, Eq, PartialEq)]
220pub enum ShareMode {
221    /// No other action can share the IRQ.
222    Exclusive,
223    /// Multiple actions can share the IRQ.
224    Shared,
225}
226
227/// Whether an IRQ action should be enabled after registration.
228#[derive(Clone, Copy, Debug, Eq, PartialEq)]
229pub enum AutoEnable {
230    /// Register the action but leave it disabled.
231    No,
232    /// Enable the action after registration.
233    Yes,
234}
235
236/// Return value from a raw IRQ handler.
237#[derive(Clone, Copy, Debug, Eq, PartialEq)]
238pub enum IrqReturn {
239    /// This action did not handle the IRQ.
240    Unhandled,
241    /// This action handled the IRQ.
242    Handled,
243    /// This action handled the IRQ and asks the OS adapter to wake deferred work.
244    Wake,
245}
246
247/// Aggregated dispatch result.
248#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
249pub struct IrqOutcome {
250    /// At least one action handled the IRQ.
251    pub handled: bool,
252    /// At least one action requested a wakeup.
253    pub wake: bool,
254    /// Number of handlers called by this dispatch.
255    pub called: usize,
256}
257
258/// IRQ status snapshot.
259#[derive(Clone, Copy, Debug, Eq, PartialEq)]
260pub struct IrqStatus {
261    /// Whether this action is enabled in the framework.
262    pub action_enabled: bool,
263    /// Whether the platform line is enabled.
264    pub line_enabled: bool,
265    /// Whether the platform reports the IRQ pending.
266    pub pending: bool,
267    /// Whether the platform reports the IRQ in service.
268    pub in_service: bool,
269    /// Number of in-flight dispatches for this descriptor.
270    pub in_flight: usize,
271    /// Whether this action is currently running.
272    pub action_running: bool,
273}
274
275/// IRQ framework errors.
276#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
277pub enum IrqError {
278    /// Invalid IRQ number.
279    #[error("invalid IRQ number")]
280    InvalidIrq,
281    /// Invalid CPU id.
282    #[error("invalid CPU id")]
283    InvalidCpu,
284    /// The target CPU is offline.
285    #[error("target CPU is offline")]
286    CpuOffline,
287    /// A synchronous IRQ operation timed out.
288    #[error("IRQ operation timed out")]
289    Timeout,
290    /// IRQ line/action sharing rules reject the operation.
291    #[error("IRQ line is busy")]
292    Busy,
293    /// Allocation failed.
294    #[error("IRQ allocation failed")]
295    NoMemory,
296    /// The requested descriptor or action does not exist.
297    #[error("IRQ descriptor or action was not found")]
298    NotFound,
299    /// This operation is not legal from IRQ context.
300    #[error("operation is not legal from IRQ context")]
301    InIrqContext,
302    /// The platform adapter does not support this operation.
303    #[error("IRQ operation is not supported")]
304    Unsupported,
305    /// The platform controller reported an error.
306    #[error("interrupt controller failed")]
307    Controller,
308}
309
310/// Context passed to IRQ handlers.
311#[derive(Clone, Copy, Debug, Eq, PartialEq)]
312pub struct IrqContext {
313    /// IRQ number being dispatched.
314    pub irq: IrqId,
315    /// CPU handling the IRQ.
316    pub cpu: CpuId,
317}
318
319/// Boxed IRQ handler ABI.
320pub type BoxedIrqHandler = Box<dyn FnMut(IrqContext) -> IrqReturn + Send + 'static>;
321
322/// Boxed IRQ handler ABI for callbacks that may run concurrently.
323pub type ConcurrentBoxedIrqHandler = Box<dyn Fn(IrqContext) -> IrqReturn + Send + Sync + 'static>;
324
325pub(crate) enum IrqHandler {
326    NonReentrant(BoxedIrqHandler),
327    Concurrent(ConcurrentBoxedIrqHandler),
328}
329
330/// External capabilities supplied by the OS/platform adapter.
331pub trait IrqOps {
332    /// Saved local IRQ state.
333    type LocalIrqState: Copy;
334
335    /// Returns the current CPU.
336    fn current_cpu(&self) -> CpuId;
337
338    /// Returns whether the CPU is online.
339    fn cpu_online(&self, cpu: CpuId) -> bool;
340
341    /// Returns whether the current execution context is an IRQ context.
342    fn in_irq_context(&self) -> bool;
343
344    /// Saves and disables local IRQs for metadata lock acquisition.
345    fn local_irq_save(&self) -> Self::LocalIrqState;
346
347    /// Restores local IRQ state saved by [`IrqOps::local_irq_save`].
348    fn local_irq_restore(&self, state: Self::LocalIrqState);
349
350    /// Runs a thunk synchronously on the target CPU.
351    fn run_on_cpu_sync(
352        &self,
353        cpu: CpuId,
354        f: unsafe fn(*mut ()),
355        arg: *mut (),
356    ) -> Result<(), IrqError>;
357
358    /// Routes a global IRQ line to the requested CPU affinity.
359    fn set_affinity(&self, _irq: IrqId, _affinity: IrqAffinity) -> Result<(), IrqError> {
360        Err(IrqError::Unsupported)
361    }
362
363    /// Enables or disables an IRQ line.
364    fn set_enabled(&self, irq: IrqId, cpu: Option<CpuId>, enabled: bool) -> Result<(), IrqError>;
365
366    /// Returns whether the IRQ line is enabled.
367    fn is_enabled(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError>;
368
369    /// Returns whether the IRQ line is pending.
370    fn is_pending(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError>;
371
372    /// Returns whether the IRQ line is in service.
373    fn is_in_service(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError>;
374
375    /// Relaxes a spin wait.
376    fn relax(&self);
377}
378
379/// Request parameters for an IRQ action.
380pub struct IrqRequest {
381    pub(crate) handler: Option<IrqHandler>,
382    pub(crate) scope: IrqScope,
383    pub(crate) affinity: IrqAffinity,
384    pub(crate) execution: IrqExecution,
385    pub(crate) share_mode: ShareMode,
386    pub(crate) auto_enable: AutoEnable,
387}
388
389impl IrqRequest {
390    /// Creates a new exclusive, global, auto-enabled IRQ request.
391    pub fn new(handler: impl FnMut(IrqContext) -> IrqReturn + Send + 'static) -> Self {
392        Self {
393            handler: Some(IrqHandler::NonReentrant(Box::new(handler))),
394            scope: IrqScope::Global,
395            affinity: IrqAffinity::Any,
396            execution: IrqExecution::NonReentrant,
397            share_mode: ShareMode::Exclusive,
398            auto_enable: AutoEnable::Yes,
399        }
400    }
401
402    /// Creates a new exclusive, global, auto-enabled concurrent IRQ request.
403    pub fn new_concurrent(
404        handler: impl Fn(IrqContext) -> IrqReturn + Send + Sync + 'static,
405    ) -> Self {
406        Self {
407            handler: Some(IrqHandler::Concurrent(Box::new(handler))),
408            scope: IrqScope::Global,
409            affinity: IrqAffinity::Any,
410            execution: IrqExecution::Concurrent,
411            share_mode: ShareMode::Exclusive,
412            auto_enable: AutoEnable::Yes,
413        }
414    }
415
416    pub(crate) fn supports_concurrent(&self) -> bool {
417        matches!(self.handler.as_ref(), Some(IrqHandler::Concurrent(_)))
418    }
419
420    /// Sets the IRQ scope.
421    pub fn scope(mut self, scope: IrqScope) -> Self {
422        self.scope = scope;
423        self
424    }
425
426    /// Sets the IRQ affinity.
427    pub fn affinity(mut self, affinity: IrqAffinity) -> Self {
428        self.affinity = affinity;
429        self
430    }
431
432    /// Sets the action execution contract.
433    pub fn execution(mut self, execution: IrqExecution) -> Self {
434        self.execution = execution;
435        self
436    }
437
438    /// Sets the sharing mode.
439    pub fn share_mode(mut self, share_mode: ShareMode) -> Self {
440        self.share_mode = share_mode;
441        self
442    }
443
444    /// Sets whether the action should be enabled after request.
445    pub fn auto_enable(mut self, auto_enable: AutoEnable) -> Self {
446        self.auto_enable = auto_enable;
447        self
448    }
449
450    /// Returns whether the action should be enabled after request.
451    pub const fn auto_enable_mode(&self) -> AutoEnable {
452        self.auto_enable
453    }
454}
455
456/// Token returned from request and used for later lifecycle operations.
457#[derive(Clone, Copy, Debug, Eq, PartialEq)]
458pub struct IrqHandle {
459    pub(crate) irq: IrqId,
460    pub(crate) id: u64,
461}
462
463impl IrqHandle {
464    /// Returns the IRQ number associated with this handle.
465    pub const fn irq(self) -> IrqId {
466        self.irq
467    }
468
469    /// Returns the framework-local action id.
470    pub const fn id(self) -> u64 {
471        self.id
472    }
473}