Skip to main content

ax_cpu/trap/
mod.rs

1//! Trap handling.
2
3pub mod boot;
4pub mod diagnostics;
5pub mod fatal;
6mod fault;
7mod interrupted;
8use core::sync::atomic::{AtomicUsize, Ordering};
9
10use ax_memory_addr::VirtAddr;
11pub use diagnostics::BacktraceRegisters;
12pub use fault::PageFaultFlags;
13pub use interrupted::{InterruptedContext, InterruptedPrivilege};
14
15pub use crate::arch::current::{context::TrapFrame as UserRegisters, trap::KernelTrapFrame};
16
17/// Privilege domain that owns a saved register image.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum TrapOrigin {
20    /// The trap interrupted kernel execution.
21    Kernel,
22    /// The trap interrupted a less-privileged user context.
23    User,
24}
25
26/// IRQ trap hook type.
27pub type IrqHandler = fn(usize, TrapOrigin, Option<InterruptedContext>) -> bool;
28
29/// Page-fault trap hook type.
30pub type PageFaultHandler = fn(VirtAddr, PageFaultFlags) -> bool;
31
32/// Breakpoint trap hook type.
33pub type BreakpointHandler = fn(&mut KernelTrapFrame<'_>) -> bool;
34
35/// Debug trap hook type.
36pub type DebugHandler = fn(&mut KernelTrapFrame<'_>) -> bool;
37
38fn default_irq_handler(
39    irq: usize,
40    _origin: TrapOrigin,
41    _context: Option<InterruptedContext>,
42) -> bool {
43    trace!("IRQ {} triggered", irq);
44    false
45}
46
47fn default_page_fault_handler(addr: VirtAddr, flags: PageFaultFlags) -> bool {
48    warn!("Page fault at {:#x} with flags {:?}", addr, flags);
49    false
50}
51
52fn default_breakpoint_handler(_tf: &mut KernelTrapFrame<'_>) -> bool {
53    false
54}
55
56fn default_debug_handler(_tf: &mut KernelTrapFrame<'_>) -> bool {
57    false
58}
59
60static IRQ_HANDLER: AtomicUsize = AtomicUsize::new(0);
61static PAGE_FAULT_HANDLER: AtomicUsize = AtomicUsize::new(0);
62static BREAKPOINT_HANDLER: AtomicUsize = AtomicUsize::new(0);
63static DEBUG_HANDLER: AtomicUsize = AtomicUsize::new(0);
64
65/// Installs the global IRQ trap hook and returns the previous one.
66pub fn set_irq_handler(handler: IrqHandler) -> IrqHandler {
67    let old = IRQ_HANDLER.swap(handler as usize, Ordering::AcqRel);
68    if old == 0 {
69        default_irq_handler
70    } else {
71        // SAFETY: the atomic only stores function pointers of type `IrqHandler`.
72        unsafe { core::mem::transmute::<usize, IrqHandler>(old) }
73    }
74}
75
76/// Installs the global page-fault trap hook and returns the previous one.
77pub fn set_page_fault_handler(handler: PageFaultHandler) -> PageFaultHandler {
78    let old = PAGE_FAULT_HANDLER.swap(handler as usize, Ordering::AcqRel);
79    if old == 0 {
80        default_page_fault_handler
81    } else {
82        // SAFETY: the atomic only stores function pointers of type `PageFaultHandler`.
83        unsafe { core::mem::transmute::<usize, PageFaultHandler>(old) }
84    }
85}
86
87/// Installs the global breakpoint trap hook and returns the previous one.
88pub fn set_breakpoint_handler(handler: BreakpointHandler) -> BreakpointHandler {
89    let old = BREAKPOINT_HANDLER.swap(handler as usize, Ordering::AcqRel);
90    if old == 0 {
91        default_breakpoint_handler
92    } else {
93        // SAFETY: the atomic only stores function pointers of type `BreakpointHandler`.
94        unsafe { core::mem::transmute::<usize, BreakpointHandler>(old) }
95    }
96}
97
98/// Installs the global debug trap hook and returns the previous one.
99pub fn set_debug_handler(handler: DebugHandler) -> DebugHandler {
100    let old = DEBUG_HANDLER.swap(handler as usize, Ordering::AcqRel);
101    if old == 0 {
102        default_debug_handler
103    } else {
104        // SAFETY: the atomic only stores function pointers of type `DebugHandler`.
105        unsafe { core::mem::transmute::<usize, DebugHandler>(old) }
106    }
107}
108
109/// Dispatches an IRQ through the runtime-registered handler, or the default handler.
110pub fn dispatch_irq(irq: usize, origin: TrapOrigin, context: Option<InterruptedContext>) -> bool {
111    let handler = IRQ_HANDLER.load(Ordering::Acquire);
112    let handler = if handler == 0 {
113        default_irq_handler
114    } else {
115        // SAFETY: the atomic only stores function pointers of type `IrqHandler`.
116        unsafe { core::mem::transmute::<usize, IrqHandler>(handler) }
117    };
118    handler(irq, origin, context)
119}
120
121/// Dispatches a page fault through the runtime-registered handler, or the default handler.
122pub fn dispatch_page_fault(addr: VirtAddr, flags: PageFaultFlags) -> bool {
123    let handler = PAGE_FAULT_HANDLER.load(Ordering::Acquire);
124    let handler = if handler == 0 {
125        default_page_fault_handler
126    } else {
127        // SAFETY: the atomic only stores function pointers of type `PageFaultHandler`.
128        unsafe { core::mem::transmute::<usize, PageFaultHandler>(handler) }
129    };
130    handler(addr, flags)
131}
132
133/// Dispatches an IRQ to the installed trap hook.
134pub fn irq_handler(irq: usize) -> bool {
135    dispatch_irq(irq, TrapOrigin::Kernel, None)
136}
137
138/// Dispatches a page fault to the installed trap hook.
139pub fn page_fault_handler(addr: VirtAddr, flags: PageFaultFlags) -> bool {
140    dispatch_page_fault(addr, flags)
141}
142
143/// Invoke the page-fault slow path with the IRQ state restored to the
144/// faulting context.
145#[inline]
146pub(crate) fn call_page_fault_handler_with_parent_irqs(
147    addr: VirtAddr,
148    flags: PageFaultFlags,
149    parent_irqs_enabled: bool,
150) -> bool {
151    if parent_irqs_enabled {
152        crate::asm::enable_irqs();
153    }
154    let handled = page_fault_handler(addr, flags);
155    if parent_irqs_enabled {
156        crate::asm::disable_irqs();
157    }
158    handled
159}
160
161/// Breakpoint handler.
162///
163/// The handler is invoked with a typed view of the trapped kernel registers
164/// and must return a boolean indicating whether it has fully handled the trap:
165///
166/// - `true` means the breakpoint has been handled and control should resume
167///   according to the state encoded in the trap frame.
168/// - `false` means the breakpoint was not handled and default processing
169///   (such as falling back to another mechanism or terminating) should occur.
170///
171/// When returning `true`, the handler is responsible for updating the saved
172/// program counter (or equivalent PC field) in the trap frame as required by
173/// the target architecture. In particular, the handler must ensure that,
174/// upon resuming from the trap, execution does not immediately re-trigger the
175/// same breakpoint instruction or condition, which could otherwise lead to an
176/// infinite trap loop. Register changes must go through
177/// [`KernelTrapFrame::apply_registers`], which preserves CPU-owned and
178/// privilege-origin state.
179pub fn breakpoint_handler(tf: &mut KernelTrapFrame<'_>) -> bool {
180    let handler = BREAKPOINT_HANDLER.load(Ordering::Acquire);
181    let handler = if handler == 0 {
182        default_breakpoint_handler
183    } else {
184        // SAFETY: the atomic only stores function pointers of type `BreakpointHandler`.
185        unsafe { core::mem::transmute::<usize, BreakpointHandler>(handler) }
186    };
187    handler(tf)
188}
189
190/// Debug handler.
191///
192/// On `x86_64`, the handler is invoked for debug-related traps (for
193/// example, hardware breakpoints, single-step traps, or other debug
194/// exceptions). The handler receives a typed kernel-register view and returns
195/// a boolean with the following meaning:
196///
197/// - `true` means the debug trap has been fully handled and execution should
198///   resume from the state stored in the trap frame.
199/// - `false` means the debug trap was not handled and default/secondary
200///   processing should take place.
201///
202/// As with [`breakpoint_handler()`], when returning `true`, the handler must adjust
203/// the saved program counter (or equivalent) in the trap frame if required by
204/// the architecture so that resuming execution does not immediately cause the
205/// same debug condition to fire again. Callers must take the architecture-
206/// specific PC semantics into account when deciding how to advance or modify
207/// the PC. Register changes must go through
208/// [`KernelTrapFrame::apply_registers`], which preserves CPU-owned and
209/// privilege-origin state.
210pub fn debug_handler(tf: &mut KernelTrapFrame<'_>) -> bool {
211    let handler = DEBUG_HANDLER.load(Ordering::Acquire);
212    let handler = if handler == 0 {
213        default_debug_handler
214    } else {
215        // SAFETY: the atomic only stores function pointers of type `DebugHandler`.
216        unsafe { core::mem::transmute::<usize, DebugHandler>(handler) }
217    };
218    handler(tf)
219}
220
221#[cfg(target_arch = "loongarch64")]
222pub use crate::arch::current::unaligned::{
223    UnalignedAccess, UnalignedAccessType, UnalignedError, UnalignedPageFault,
224};