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