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}