Skip to main content

ax_cpu/
trap.rs

1//! Trap handling.
2
3use core::sync::atomic::{AtomicUsize, Ordering};
4
5use ax_memory_addr::VirtAddr;
6pub use ax_page_table_entry::MappingFlags as PageFaultFlags;
7
8pub use crate::TrapFrame;
9
10/// IRQ trap hook type.
11pub type IrqHandler = fn(usize) -> bool;
12
13/// Page-fault trap hook type.
14pub type PageFaultHandler = fn(VirtAddr, PageFaultFlags) -> bool;
15
16fn default_irq_handler(irq: usize) -> bool {
17    trace!("IRQ {} triggered", irq);
18    false
19}
20
21fn default_page_fault_handler(addr: VirtAddr, flags: PageFaultFlags) -> bool {
22    warn!("Page fault at {:#x} with flags {:?}", addr, flags);
23    false
24}
25
26static IRQ_HANDLER: AtomicUsize = AtomicUsize::new(0);
27static PAGE_FAULT_HANDLER: AtomicUsize = AtomicUsize::new(0);
28
29/// Installs the global IRQ trap hook and returns the previous one.
30pub fn set_irq_handler(handler: IrqHandler) -> IrqHandler {
31    let old = IRQ_HANDLER.swap(handler as usize, Ordering::AcqRel);
32    if old == 0 {
33        default_irq_handler
34    } else {
35        // SAFETY: the atomic only stores function pointers of type `IrqHandler`.
36        unsafe { core::mem::transmute::<usize, IrqHandler>(old) }
37    }
38}
39
40/// Installs the global page-fault trap hook and returns the previous one.
41pub fn set_page_fault_handler(handler: PageFaultHandler) -> PageFaultHandler {
42    let old = PAGE_FAULT_HANDLER.swap(handler as usize, Ordering::AcqRel);
43    if old == 0 {
44        default_page_fault_handler
45    } else {
46        // SAFETY: the atomic only stores function pointers of type `PageFaultHandler`.
47        unsafe { core::mem::transmute::<usize, PageFaultHandler>(old) }
48    }
49}
50
51/// Dispatches an IRQ through the runtime-registered handler, or the default handler.
52pub fn dispatch_irq(irq: usize) -> bool {
53    let handler = IRQ_HANDLER.load(Ordering::Acquire);
54    let handler = if handler == 0 {
55        default_irq_handler
56    } else {
57        // SAFETY: the atomic only stores function pointers of type `IrqHandler`.
58        unsafe { core::mem::transmute::<usize, IrqHandler>(handler) }
59    };
60    handler(irq)
61}
62
63/// Dispatches a page fault through the runtime-registered handler, or the default handler.
64pub fn dispatch_page_fault(addr: VirtAddr, flags: PageFaultFlags) -> bool {
65    let handler = PAGE_FAULT_HANDLER.load(Ordering::Acquire);
66    let handler = if handler == 0 {
67        default_page_fault_handler
68    } else {
69        // SAFETY: the atomic only stores function pointers of type `PageFaultHandler`.
70        unsafe { core::mem::transmute::<usize, PageFaultHandler>(handler) }
71    };
72    handler(addr, flags)
73}
74
75/// IRQ handler.
76#[eii]
77pub fn irq_handler(irq: usize) -> bool {
78    default_irq_handler(irq)
79}
80
81/// Page fault handler.
82#[eii]
83pub fn page_fault_handler(addr: VirtAddr, flags: PageFaultFlags) -> bool {
84    default_page_fault_handler(addr, flags)
85}
86
87/// Invoke the page-fault slow path with the IRQ state restored to the
88/// faulting context.
89#[inline]
90pub(crate) fn call_page_fault_handler_with_parent_irqs(
91    addr: VirtAddr,
92    flags: PageFaultFlags,
93    parent_irqs_enabled: bool,
94) -> bool {
95    if parent_irqs_enabled {
96        crate::asm::enable_irqs();
97    }
98    let handled = page_fault_handler(addr, flags);
99    if parent_irqs_enabled {
100        crate::asm::disable_irqs();
101    }
102    handled
103}
104
105/// Breakpoint handler.
106///
107/// The handler is invoked with a mutable reference to the trapped [`TrapFrame`]
108/// and must return a boolean indicating whether it has fully handled the trap:
109///
110/// - `true` means the breakpoint has been handled and control should resume
111///   according to the state encoded in the trap frame.
112/// - `false` means the breakpoint was not handled and default processing
113///   (such as falling back to another mechanism or terminating) should occur.
114///
115/// When returning `true`, the handler is responsible for updating the saved
116/// program counter (or equivalent PC field) in the trap frame as required by
117/// the target architecture. In particular, the handler must ensure that,
118/// upon resuming from the trap, execution does not immediately re-trigger the
119/// same breakpoint instruction or condition, which could otherwise lead to an
120/// infinite trap loop. The exact way to advance or modify the PC is
121/// architecture-specific and depends on how [`TrapFrame`] encodes the saved
122/// context.
123#[eii]
124pub fn breakpoint_handler(_tf: &mut TrapFrame) -> bool {
125    false
126}
127
128/// Debug handler.
129///
130/// On `x86_64`, the handler is invoked for debug-related traps (for
131/// example, hardware breakpoints, single-step traps, or other debug
132/// exceptions). The handler receives a mutable reference to the trapped
133/// [`TrapFrame`] and returns a boolean with the following meaning:
134///
135/// - `true` means the debug trap has been fully handled and execution should
136///   resume from the state stored in the trap frame.
137/// - `false` means the debug trap was not handled and default/secondary
138///   processing should take place.
139///
140/// As with [`breakpoint_handler()`], when returning `true`, the handler must adjust
141/// the saved program counter (or equivalent) in the trap frame if required by
142/// the architecture so that resuming execution does not immediately cause the
143/// same debug condition to fire again. Callers must take the architecture-
144/// specific PC semantics into account when deciding how to advance or modify
145/// the PC.
146#[cfg(target_arch = "x86_64")]
147#[eii]
148pub fn debug_handler(_tf: &mut TrapFrame) -> bool {
149    false
150}