ostd/irq/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Handling of Interrupt ReQuests (IRQs).
4//!
5//! # Top vs bottom half
6//!
7//! OSTD divides the handling of an IRQ into two parts:
8//! **top half** and **bottom half**.
9//!
10//! A driver can assign to a target device an IRQ line, [`IrqLine`],
11//! to which a callback function may be registered.
12//! When an IRQ arrives at an IRQ line,
13//! OSTD will invoke all the callbacks registered on the line,
14//! with all local IRQs on the CPU disabled.
15//! Thus, the `IrqLine` callbacks should be written as short as possible,
16//! performing the most critical tasks.
17//! This is the so-called top half of IRQ handling.
18//!
19//! When the top half finishes,
20//! OSTD continues on the handling of the IRQ with the bottom half.
21//! The logic of the bottom half is specified
22//! by a callback function registered via [`register_bottom_half_handler_l1`]
23//! (or [`register_bottom_half_handler_l2`], as we will see later).
24//! The implementer of this callback function may re-enable local IRQs,
25//! thus allowing the less critical tasks performed in the bottom half
26//! to be preempted by the more critical ones done in the top half.
27//!
28//! OSTD's split of IRQ handling in top and bottom halves
29//! closely resembles that of Linux,
30//! but with a key difference:
31//! OSTD itself does not hardcode any concrete mechanisms for the bottom-half,
32//! e.g., Linux's softirqs or tasklets.
33//! OSTD's APIs are flexible and powerful enough to
34//! enable an OSTD-based kernel to implement such mechanisms itself.
35//! This design helps contain the size and complexity of OSTD.
36//!
37//! # Nested interrupts
38//!
39//! OSTD allows interrupts to be nested.
40//! The top-half for handling nested interrupts are still done by `IrqLine` callbacks,
41//! yet the bottom-half logic is done by a new callback
42//! registered via [`register_bottom_half_handler_l2`],
43//! rather than [`register_bottom_half_handler_l1`].
44//!
45//! We introduce the concept of **interrupt level** to
46//! mark the nesting depth of interrupts.
47//! [`InterruptLevel::current`] keeps track of the current nesting depth
48//! on the CPU where the code is executing.
49//! There are three interrupt levels:
50//!
51//! - **Level 0 (Task Context):**
52//!   Normal execution for a kernel or user task.
53//!   Code at this level can be preempted by a hardware interrupt.
54//! - **Level 1 (Interrupt Context):**
55//!   Entered when an interrupt preempts task context code.
56//!   Interrupt handling callbacks that may be invoked at this level are:
57//!   - The top-half callbacks registered via [`IrqLine`];
58//!   - The bottom-half callback registered via [`register_bottom_half_handler_l1`].
59//! - **Level 2 (Nested Interrupt Context):**
60//!   The maximum nesting level,
61//!   entered when a level 1 bottom-half callback
62//!   (registered via `register_bottom_half_handler_l1`) is interrupted.
63//!   `IrqLine` callbacks always have IRQ disabled;
64//!   thus, they can never be preempted.
65//!
66//!   Interrupt handling callbacks that may be invoked at this level are:
67//!   - The top-half callbacks registered via [`IrqLine`];
68//!   - The bottom-half callback registered via [`register_bottom_half_handler_l2`]
69//!     (not [`register_bottom_half_handler_l1`]).
70//!
71//!   At this level, all local IRQs are disabled to prevent further nesting.
72//!
73
74mod bottom_half;
75mod guard;
76mod level;
77mod top_half;
78
79pub use bottom_half::{register_bottom_half_handler_l1, register_bottom_half_handler_l2};
80pub use guard::{DisabledLocalIrqGuard, disable_local};
81pub use level::InterruptLevel;
82pub use top_half::{IrqCallbackFunction, IrqLine};
83
84use crate::{
85    arch::{irq::HwIrqLine, trap::TrapFrame},
86    cpu::PrivilegeLevel,
87};
88
89pub(crate) fn call_irq_callback_functions(
90    trap_frame: &TrapFrame,
91    hw_irq_line: &HwIrqLine,
92    cpu_priv_at_irq: PrivilegeLevel,
93) {
94    level::enter(
95        move || {
96            top_half::process(trap_frame, hw_irq_line);
97            bottom_half::process(hw_irq_line.irq_num());
98        },
99        cpu_priv_at_irq,
100    );
101}