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