1use aarch64_cpu::registers::*;
2use tock_registers::interfaces::Readable;
3
4use super::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(Debug)]
76pub(super) enum TrapKind {
77 Synchronous = 0,
78 Irq = 1,
79 Fiq = 2,
80 SError = 3,
81}
82
83impl TrapKind {
84 const fn from_raw(value: u8) -> Option<Self> {
85 match value {
86 0 => Some(Self::Synchronous),
87 1 => Some(Self::Irq),
88 2 => Some(Self::Fiq),
89 3 => Some(Self::SError),
90 _ => None,
91 }
92 }
93}
94
95#[repr(u8)]
96#[derive(Debug)]
97enum TrapSource {
98 CurrentSpEl0 = 0,
99 CurrentSpElx = 1,
100 LowerAArch64 = 2,
101 LowerAArch32 = 3,
102}
103
104impl TrapSource {
105 const fn from_raw(value: u8) -> Option<Self> {
106 match value {
107 0 => Some(Self::CurrentSpEl0),
108 1 => Some(Self::CurrentSpElx),
109 2 => Some(Self::LowerAArch64),
110 3 => Some(Self::LowerAArch32),
111 _ => None,
112 }
113 }
114}
115
116core::arch::global_asm!(
117 #[cfg(not(feature = "arm-el2"))]
118 include_str!("trap.S"),
119 #[cfg(feature = "arm-el2")]
120 concat!(".equ arm_el2, 1\n", include_str!("trap.S")),
121 trapframe_size = const core::mem::size_of::<RawTrapFrame>(),
122 TRAP_KIND_SYNC = const TrapKind::Synchronous as u8,
123 TRAP_KIND_IRQ = const TrapKind::Irq as u8,
124 TRAP_KIND_FIQ = const TrapKind::Fiq as u8,
125 TRAP_KIND_SERROR = const TrapKind::SError as u8,
126 TRAP_SRC_CURR_EL0 = const TrapSource::CurrentSpEl0 as u8,
127 TRAP_SRC_CURR_ELX = const TrapSource::CurrentSpElx as u8,
128 TRAP_SRC_LOWER_AARCH64 = const TrapSource::LowerAArch64 as u8,
129 TRAP_SRC_LOWER_AARCH32 = const TrapSource::LowerAArch32 as u8,
130);
131
132#[inline(always)]
133pub(super) fn is_valid_page_fault(iss: u64) -> bool {
134 matches!(iss & 0b111100, 0b0100 | 0b1100) }
137
138#[inline(always)]
139fn fault_addr() -> usize {
140 #[cfg(not(feature = "arm-el2"))]
141 {
142 FAR_EL1.get() as usize
143 }
144
145 #[cfg(feature = "arm-el2")]
146 {
147 FAR_EL2.get() as usize
148 }
149}
150
151#[inline(always)]
152fn esr_value() -> u64 {
153 #[cfg(not(feature = "arm-el2"))]
154 {
155 ESR_EL1.get()
156 }
157
158 #[cfg(feature = "arm-el2")]
159 {
160 ESR_EL2.get()
161 }
162}
163
164fn handle_breakpoint(tf: &mut KernelTrapFrame<'_>) {
165 if crate::trap::breakpoint_handler(tf) {
166 return;
167 }
168 tf.set_ip(tf.ip() + 4);
169}
170
171fn handle_page_fault(tf: &mut KernelTrapFrame<'_>, access_flags: PageFaultFlags) {
172 let vaddr = va!(fault_addr());
173 #[cfg(feature = "exception-table")]
174 if tf.raw.0.fixup_nofault_exception() {
175 return;
176 }
177 if crate::trap::call_page_fault_handler_with_parent_irqs(
178 vaddr,
179 access_flags,
180 tf.raw.0.spsr & (1 << 7) == 0,
181 ) {
182 return;
183 }
184 #[cfg(feature = "exception-table")]
185 if tf.raw.0.fixup_exception() {
186 return;
187 }
188 let snapshot = tf.snapshot();
189 let bt = snapshot.backtrace();
190 panic!(
191 "Unhandled Page Fault @ {:#x}, fault_vaddr={:#x}, ESR={:#x} ({:?}):\n{:#x?}\n{}",
192 tf.raw.0.elr,
193 vaddr,
194 esr_value(),
195 access_flags,
196 snapshot,
197 bt.kind("trap")
198 );
199}
200
201#[unsafe(no_mangle)]
202unsafe extern "C" fn aarch64_trap_handler(raw: *mut RawTrapFrame, raw_kind: u8, raw_source: u8) {
203 let kind = TrapKind::from_raw(raw_kind)
204 .unwrap_or_else(|| panic!("invalid AArch64 trap kind {raw_kind:#x}"));
205 let source = TrapSource::from_raw(raw_source)
206 .unwrap_or_else(|| panic!("invalid AArch64 trap source {raw_source:#x}"));
207 let raw = unsafe { &mut *raw };
210 if matches!(
211 source,
212 TrapSource::CurrentSpEl0 | TrapSource::LowerAArch64 | TrapSource::LowerAArch32
213 ) {
214 let bt = raw.0.backtrace();
215 panic!(
216 "Invalid exception {:?} from {:?}:\n{:#x?}\n{}",
217 kind,
218 source,
219 raw.0,
220 bt.kind("trap")
221 );
222 }
223 let mut tf = unsafe { KernelTrapFrame::from_raw(raw) };
224 match kind {
225 TrapKind::Fiq | TrapKind::SError => {
226 let snapshot = tf.snapshot();
227 let bt = snapshot.backtrace();
228 panic!(
229 "Unhandled exception {:?}:\n{:#x?}\n{}",
230 kind,
231 snapshot,
232 bt.kind("trap")
233 );
234 }
235 TrapKind::Irq => {
236 crate::trap::dispatch_irq(0, crate::trap::TrapOrigin::Kernel);
237 }
238 TrapKind::Synchronous => {
239 #[cfg(not(feature = "arm-el2"))]
240 let esr = ESR_EL1.extract();
241 #[cfg(feature = "arm-el2")]
242 let esr = ESR_EL2.extract();
243
244 #[cfg(not(feature = "arm-el2"))]
245 let iss = esr.read(ESR_EL1::ISS);
246 #[cfg(feature = "arm-el2")]
247 let iss = esr.read(ESR_EL2::ISS);
248
249 #[cfg(not(feature = "arm-el2"))]
250 let ec = esr.read_as_enum(ESR_EL1::EC);
251 #[cfg(feature = "arm-el2")]
252 let ec = esr.read_as_enum(ESR_EL2::EC);
253
254 match ec {
255 #[cfg(not(feature = "arm-el2"))]
256 Some(ESR_EL1::EC::Value::InstrAbortCurrentEL) if is_valid_page_fault(iss) => {
257 handle_page_fault(&mut tf, PageFaultFlags::EXECUTE);
258 }
259 #[cfg(feature = "arm-el2")]
260 Some(ESR_EL2::EC::Value::InstrAbortCurrentEL) if is_valid_page_fault(iss) => {
261 handle_page_fault(&mut tf, PageFaultFlags::EXECUTE);
262 }
263 #[cfg(not(feature = "arm-el2"))]
264 Some(ESR_EL1::EC::Value::DataAbortCurrentEL) if is_valid_page_fault(iss) => {
265 let wnr = (iss & (1 << 6)) != 0; let cm = (iss & (1 << 8)) != 0; handle_page_fault(
268 &mut tf,
269 if wnr & !cm {
270 PageFaultFlags::WRITE
271 } else {
272 PageFaultFlags::READ
273 },
274 );
275 }
276 #[cfg(feature = "arm-el2")]
277 Some(ESR_EL2::EC::Value::DataAbortCurrentEL) if is_valid_page_fault(iss) => {
278 let wnr = (iss & (1 << 6)) != 0; let cm = (iss & (1 << 8)) != 0; handle_page_fault(
281 &mut tf,
282 if wnr & !cm {
283 PageFaultFlags::WRITE
284 } else {
285 PageFaultFlags::READ
286 },
287 );
288 }
289 #[cfg(not(feature = "arm-el2"))]
290 Some(ESR_EL1::EC::Value::Brk64) => {
291 debug!("BRK #{:#x} @ {:#x} ", iss, tf.raw.0.elr);
292 handle_breakpoint(&mut tf);
293 }
294 #[cfg(feature = "arm-el2")]
295 Some(ESR_EL2::EC::Value::Brk64) => {
296 debug!("BRK #{:#x} @ {:#x} ", iss, tf.raw.0.elr);
297 handle_breakpoint(&mut tf);
298 }
299 e => {
300 let vaddr = va!(fault_addr());
301
302 #[cfg(not(feature = "arm-el2"))]
303 let ec_bits = esr.read(ESR_EL1::EC);
304 #[cfg(feature = "arm-el2")]
305 let ec_bits = esr.read(ESR_EL2::EC);
306
307 let snapshot = tf.snapshot();
308 let bt = snapshot.backtrace();
309 panic!(
310 "Unhandled synchronous exception {:?} @ {:#x}: ESR={:#x} (EC {:#08b}, \
311 FAR: {:#x} ISS {:#x})\n{}",
312 e,
313 tf.raw.0.elr,
314 esr.get(),
315 ec_bits,
316 vaddr,
317 iss,
318 bt.kind("trap")
319 );
320 }
321 }
322 }
323 }
324}