1use aarch64_cpu::registers::*;
2use tock_registers::interfaces::Readable;
3
4use super::context::TrapFrame;
5use crate::{TrapOrigin, trap::PageFaultFlags};
6
7#[repr(transparent)]
9struct RawTrapFrame(TrapFrame);
10
11const _: () = {
12 assert!(core::mem::size_of::<RawTrapFrame>() == core::mem::size_of::<TrapFrame>());
13 assert!(core::mem::align_of::<RawTrapFrame>() == core::mem::align_of::<TrapFrame>());
14};
15
16pub struct KernelTrapFrame<'a> {
18 raw: &'a mut RawTrapFrame,
19 _not_send: core::marker::PhantomData<*mut ()>,
20}
21
22impl<'a> KernelTrapFrame<'a> {
23 pub const fn origin(&self) -> TrapOrigin {
25 TrapOrigin::Kernel
26 }
27
28 pub const fn snapshot(&self) -> TrapFrame {
30 self.raw.0
31 }
32
33 pub fn apply_registers(&mut self, updated: &TrapFrame) {
35 const MODE_MASK: u64 = 0b1_1111;
36 let saved_mode = self.raw.0.spsr & MODE_MASK;
37 let sp = self.raw.0.sp;
38 self.raw.0 = *updated;
39 self.raw.0.spsr = (self.raw.0.spsr & !MODE_MASK) | saved_mode;
40 self.raw.0.sp = sp;
41 }
42
43 pub const fn ip(&self) -> usize {
45 self.raw.0.ip()
46 }
47
48 pub const fn set_ip(&mut self, ip: usize) {
50 self.raw.0.set_ip(ip);
51 }
52
53 unsafe fn from_raw(raw: &'a mut RawTrapFrame) -> Self {
60 debug_assert_eq!(raw.0.origin(), TrapOrigin::Kernel);
61 Self {
62 raw,
63 _not_send: core::marker::PhantomData,
64 }
65 }
66}
67
68impl core::fmt::Debug for KernelTrapFrame<'_> {
69 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70 self.snapshot().fmt(formatter)
71 }
72}
73
74#[repr(u8)]
75#[derive(Clone, Copy, Debug, Eq, PartialEq)]
76pub enum TrapKind {
78 Synchronous = 0,
80 Irq = 1,
82 Fiq = 2,
84 SError = 3,
86}
87
88impl TrapKind {
89 const fn from_raw(value: u8) -> Option<Self> {
90 match value {
91 0 => Some(Self::Synchronous),
92 1 => Some(Self::Irq),
93 2 => Some(Self::Fiq),
94 3 => Some(Self::SError),
95 _ => None,
96 }
97 }
98}
99
100#[repr(u8)]
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub enum TrapSource {
104 CurrentSpEl0 = 0,
106 CurrentSpElx = 1,
108 LowerAArch64 = 2,
110 LowerAArch32 = 3,
112}
113
114impl TrapSource {
115 const fn from_raw(value: u8) -> Option<Self> {
116 match value {
117 0 => Some(Self::CurrentSpEl0),
118 1 => Some(Self::CurrentSpElx),
119 2 => Some(Self::LowerAArch64),
120 3 => Some(Self::LowerAArch32),
121 _ => None,
122 }
123 }
124}
125
126core::arch::global_asm!(
127 include_str!("entry/gpr.S"),
128 include_str!("entry/trap.S"),
129 trapframe_size = const core::mem::size_of::<RawTrapFrame>(),
130 elr_offset = const core::mem::offset_of!(TrapFrame, elr),
131 sp_offset = const core::mem::offset_of!(TrapFrame, sp),
132 TRAP_KIND_SYNC = const TrapKind::Synchronous as u8,
133 TRAP_KIND_IRQ = const TrapKind::Irq as u8,
134 TRAP_KIND_FIQ = const TrapKind::Fiq as u8,
135 TRAP_KIND_SERROR = const TrapKind::SError as u8,
136 TRAP_SRC_CURR_EL0 = const TrapSource::CurrentSpEl0 as u8,
137 TRAP_SRC_CURR_ELX = const TrapSource::CurrentSpElx as u8,
138 TRAP_SRC_LOWER_AARCH64 = const TrapSource::LowerAArch64 as u8,
139 TRAP_SRC_LOWER_AARCH32 = const TrapSource::LowerAArch32 as u8,
140);
141
142#[inline(always)]
143pub(super) fn is_valid_page_fault(iss: u64) -> bool {
144 matches!(iss & 0b111100, 0b0100 | 0b1100) }
147
148fn handle_breakpoint(tf: &mut KernelTrapFrame<'_>) {
149 if crate::trap::breakpoint_handler(tf) {
150 return;
151 }
152 tf.set_ip(tf.ip() + 4);
153}
154
155fn handle_page_fault(
156 tf: &mut KernelTrapFrame<'_>,
157 access_flags: PageFaultFlags,
158 esr: u64,
159 far: usize,
160) {
161 let vaddr = va!(far);
162 #[cfg(feature = "exception-table")]
163 if tf.raw.0.fixup_nofault_exception() {
164 return;
165 }
166 if crate::trap::call_page_fault_handler_with_parent_irqs(
167 vaddr,
168 access_flags,
169 tf.raw.0.spsr & (1 << 7) == 0,
170 ) {
171 return;
172 }
173 #[cfg(feature = "exception-table")]
174 if tf.raw.0.fixup_exception() {
175 return;
176 }
177 let snapshot = tf.snapshot();
178 let bt = crate::trap::diagnostics::BacktraceDisplay(snapshot.backtrace_registers());
179 panic!(
180 "Unhandled Page Fault @ {:#x}, fault_vaddr={:#x}, ESR={:#x} ({:?}):\n{:#x?}\n{}",
181 tf.raw.0.elr, vaddr, esr, access_flags, snapshot, bt
182 );
183}
184
185#[unsafe(no_mangle)]
186unsafe extern "C" fn aarch64_trap_handler(
187 raw: *mut RawTrapFrame,
188 raw_kind: u8,
189 raw_source: u8,
190 level: u8,
191) {
192 let kind = TrapKind::from_raw(raw_kind)
193 .unwrap_or_else(|| panic!("invalid AArch64 trap kind {raw_kind:#x}"));
194 let source = TrapSource::from_raw(raw_source)
195 .unwrap_or_else(|| panic!("invalid AArch64 trap source {raw_source:#x}"));
196 let raw = unsafe { &mut *raw };
199 if matches!(
200 source,
201 TrapSource::CurrentSpEl0 | TrapSource::LowerAArch64 | TrapSource::LowerAArch32
202 ) {
203 let bt = crate::trap::diagnostics::BacktraceDisplay(raw.0.backtrace_registers());
204 panic!(
205 "Invalid exception {:?} from {:?}:\n{:#x?}\n{}",
206 kind, source, raw.0, bt
207 );
208 }
209 let mut tf = unsafe { KernelTrapFrame::from_raw(raw) };
210 match kind {
211 TrapKind::Fiq | TrapKind::SError => {
212 let snapshot = tf.snapshot();
213 let bt = crate::trap::diagnostics::BacktraceDisplay(snapshot.backtrace_registers());
214 panic!("Unhandled exception {:?}:\n{:#x?}\n{}", kind, snapshot, bt);
215 }
216 TrapKind::Irq => {
217 crate::trap::dispatch_irq(
218 0,
219 crate::trap::TrapOrigin::Kernel,
220 Some(raw.0.interrupted_context()),
221 );
222 }
223 TrapKind::Synchronous => {
224 let (esr, far) = match level {
226 1 => (ESR_EL1.get(), FAR_EL1.get() as usize),
227 2 => (ESR_EL2.get(), FAR_EL2.get() as usize),
228 _ => panic!("invalid exception level {level}"),
229 };
230 let iss = esr & 0x01ff_ffff;
231 let ec = (esr >> 26) & 0x3f;
232 match ec {
233 0x21 if is_valid_page_fault(iss) => {
234 handle_page_fault(&mut tf, PageFaultFlags::EXECUTE, esr, far);
235 }
236 0x25 if is_valid_page_fault(iss) => {
237 let write = iss & (1 << 6) != 0;
238 let cache_maintenance = iss & (1 << 8) != 0;
239 let access = if write && !cache_maintenance {
240 PageFaultFlags::WRITE
241 } else {
242 PageFaultFlags::READ
243 };
244 handle_page_fault(&mut tf, access, esr, far);
245 }
246 0x3c => handle_breakpoint(&mut tf),
247 _ => {
248 let snapshot = tf.snapshot();
249 let bt =
250 crate::trap::diagnostics::BacktraceDisplay(snapshot.backtrace_registers());
251 panic!(
252 "Unhandled EL{level} synchronous exception @ {:#x}: ESR={esr:#x}, \
253 FAR={far:#x}\n{bt}",
254 tf.ip()
255 );
256 }
257 }
258 }
259 }
260}
261
262#[unsafe(no_mangle)]
263unsafe extern "C" fn __ax_cpu_boot_trap(raw: *const RawTrapFrame, kind: u8, source: u8, level: u8) {
264 let frame = unsafe { &(*raw).0 };
267 let (syndrome, fault_address) = match level {
268 1 => (ESR_EL1.get(), FAR_EL1.get()),
269 2 => (ESR_EL2.get(), FAR_EL2.get()),
270 _ => panic!("invalid boot exception level"),
271 };
272 let exception = crate::trap::boot::BootException {
273 registers: frame.x,
274 pc: frame.elr as usize,
275 sp: frame.sp as usize,
276 status: frame.spsr,
277 syndrome,
278 fault_address: crate::VirtAddr::from_usize(fault_address as usize),
279 level,
280 kind: TrapKind::from_raw(kind).expect("CPU vector kind"),
281 source: TrapSource::from_raw(source).expect("CPU vector source"),
282 };
283 crate::trap::boot::boot_trap_handler::handle(&exception);
284}