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)]
277pub enum IrqError {
278    /// Invalid IRQ number.
279    InvalidIrq,
280    /// Invalid CPU id.
281    InvalidCpu,
282    /// The target CPU is offline.
283    CpuOffline,
284    /// A synchronous IRQ operation timed out.
285    Timeout,
286    /// IRQ line/action sharing rules reject the operation.
287    Busy,
288    /// Allocation failed.
289    NoMemory,
290    /// The requested descriptor or action does not exist.
291    NotFound,
292    /// This operation is not legal from IRQ context.
293    InIrqContext,
294    /// The platform adapter does not support this operation.
295    Unsupported,
296    /// The platform controller reported an error.
297    Controller,
298}
299
300/// Context passed to IRQ handlers.
301#[derive(Clone, Copy, Debug, Eq, PartialEq)]
302pub struct IrqContext {
303    /// IRQ number being dispatched.
304    pub irq: IrqId,
305    /// CPU handling the IRQ.
306    pub cpu: CpuId,
307}
308
309/// Boxed IRQ handler ABI.
310pub type BoxedIrqHandler = Box<dyn FnMut(IrqContext) -> IrqReturn + Send + 'static>;
311
312/// Boxed IRQ handler ABI for callbacks that may run concurrently.
313pub type ConcurrentBoxedIrqHandler = Box<dyn Fn(IrqContext) -> IrqReturn + Send + Sync + 'static>;
314
315pub(crate) enum IrqHandler {
316    NonReentrant(BoxedIrqHandler),
317    Concurrent(ConcurrentBoxedIrqHandler),
318}
319
320/// External capabilities supplied by the OS/platform adapter.
321pub trait IrqOps {
322    /// Saved local IRQ state.
323    type LocalIrqState: Copy;
324
325    /// Returns the current CPU.
326    fn current_cpu(&self) -> CpuId;
327
328    /// Returns whether the CPU is online.
329    fn cpu_online(&self, cpu: CpuId) -> bool;
330
331    /// Returns whether the current execution context is an IRQ context.
332    fn in_irq_context(&self) -> bool;
333
334    /// Saves and disables local IRQs for metadata lock acquisition.
335    fn local_irq_save(&self) -> Self::LocalIrqState;
336
337    /// Restores local IRQ state saved by [`IrqOps::local_irq_save`].
338    fn local_irq_restore(&self, state: Self::LocalIrqState);
339
340    /// Runs a thunk synchronously on the target CPU.
341    fn run_on_cpu_sync(
342        &self,
343        cpu: CpuId,
344        f: unsafe fn(*mut ()),
345        arg: *mut (),
346    ) -> Result<(), IrqError>;
347
348    /// Routes a global IRQ line to the requested CPU affinity.
349    fn set_affinity(&self, _irq: IrqId, _affinity: IrqAffinity) -> Result<(), IrqError> {
350        Err(IrqError::Unsupported)
351    }
352
353    /// Enables or disables an IRQ line.
354    fn set_enabled(&self, irq: IrqId, cpu: Option<CpuId>, enabled: bool) -> Result<(), IrqError>;
355
356    /// Returns whether the IRQ line is enabled.
357    fn is_enabled(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError>;
358
359    /// Returns whether the IRQ line is pending.
360    fn is_pending(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError>;
361
362    /// Returns whether the IRQ line is in service.
363    fn is_in_service(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError>;
364
365    /// Relaxes a spin wait.
366    fn relax(&self);
367}
368
369/// Request parameters for an IRQ action.
370pub struct IrqRequest {
371    pub(crate) handler: Option<IrqHandler>,
372    pub(crate) scope: IrqScope,
373    pub(crate) affinity: IrqAffinity,
374    pub(crate) execution: IrqExecution,
375    pub(crate) share_mode: ShareMode,
376    pub(crate) auto_enable: AutoEnable,
377}
378
379impl IrqRequest {
380    /// Creates a new exclusive, global, auto-enabled IRQ request.
381    pub fn new(handler: impl FnMut(IrqContext) -> IrqReturn + Send + 'static) -> Self {
382        Self {
383            handler: Some(IrqHandler::NonReentrant(Box::new(handler))),
384            scope: IrqScope::Global,
385            affinity: IrqAffinity::Any,
386            execution: IrqExecution::NonReentrant,
387            share_mode: ShareMode::Exclusive,
388            auto_enable: AutoEnable::Yes,
389        }
390    }
391
392    /// Creates a new exclusive, global, auto-enabled concurrent IRQ request.
393    pub fn new_concurrent(
394        handler: impl Fn(IrqContext) -> IrqReturn + Send + Sync + 'static,
395    ) -> Self {
396        Self {
397            handler: Some(IrqHandler::Concurrent(Box::new(handler))),
398            scope: IrqScope::Global,
399            affinity: IrqAffinity::Any,
400            execution: IrqExecution::Concurrent,
401            share_mode: ShareMode::Exclusive,
402            auto_enable: AutoEnable::Yes,
403        }
404    }
405
406    pub(crate) fn supports_concurrent(&self) -> bool {
407        matches!(self.handler.as_ref(), Some(IrqHandler::Concurrent(_)))
408    }
409
410    /// Sets the IRQ scope.
411    pub fn scope(mut self, scope: IrqScope) -> Self {
412        self.scope = scope;
413        self
414    }
415
416    /// Sets the IRQ affinity.
417    pub fn affinity(mut self, affinity: IrqAffinity) -> Self {
418        self.affinity = affinity;
419        self
420    }
421
422    /// Sets the action execution contract.
423    pub fn execution(mut self, execution: IrqExecution) -> Self {
424        self.execution = execution;
425        self
426    }
427
428    /// Sets the sharing mode.
429    pub fn share_mode(mut self, share_mode: ShareMode) -> Self {
430        self.share_mode = share_mode;
431        self
432    }
433
434    /// Sets whether the action should be enabled after request.
435    pub fn auto_enable(mut self, auto_enable: AutoEnable) -> Self {
436        self.auto_enable = auto_enable;
437        self
438    }
439
440    /// Returns whether the action should be enabled after request.
441    pub const fn auto_enable_mode(&self) -> AutoEnable {
442        self.auto_enable
443    }
444}
445
446/// Token returned from request and used for later lifecycle operations.
447#[derive(Clone, Copy, Debug, Eq, PartialEq)]
448pub struct IrqHandle {
449    pub(crate) irq: IrqId,
450    pub(crate) id: u64,
451}
452
453impl IrqHandle {
454    /// Returns the IRQ number associated with this handle.
455    pub const fn irq(self) -> IrqId {
456        self.irq
457    }
458
459    /// Returns the framework-local action id.
460    pub const fn id(self) -> u64 {
461        self.id
462    }
463}