Skip to main content

ax_cpu/aarch64/
trap.rs

1use aarch64_cpu::registers::*;
2use tock_registers::interfaces::Readable;
3
4use super::TrapFrame;
5use crate::{TrapOrigin, trap::PageFaultFlags};
6
7/// Untrusted register image produced and consumed by trap assembly.
8#[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
16/// Lifetime-bound view of a kernel-origin AArch64 trap frame.
17pub struct KernelTrapFrame<'a> {
18    raw: &'a mut RawTrapFrame,
19    _not_send: core::marker::PhantomData<*mut ()>,
20}
21
22impl<'a> KernelTrapFrame<'a> {
23    /// Returns the privilege domain represented by this view.
24    pub const fn origin(&self) -> TrapOrigin {
25        TrapOrigin::Kernel
26    }
27
28    /// Copies the saved register image for inspection or probe emulation.
29    pub const fn snapshot(&self) -> TrapFrame {
30        self.raw.0
31    }
32
33    /// Applies task-register changes while preserving origin and saved SP.
34    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    /// Returns the saved instruction pointer.
44    pub const fn ip(&self) -> usize {
45        self.raw.0.ip()
46    }
47
48    /// Sets the saved instruction pointer.
49    pub const fn set_ip(&mut self, ip: usize) {
50        self.raw.0.set_ip(ip);
51    }
52
53    /// Creates the typed view at the assembly boundary.
54    ///
55    /// # Safety
56    ///
57    /// `raw` must be the uniquely borrowed, live kernel-origin frame built by
58    /// the AArch64 vector entry and must remain valid for `'a`.
59    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    // Only handle Translation fault and Permission fault
135    matches!(iss & 0b111100, 0b0100 | 0b1100) // IFSC or DFSC bits
136}
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    // SAFETY: the vector assembly passes its aligned, live stack frame and
208    // retains exclusive ownership until this handler returns.
209    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; // WnR: Write not Read
266                    let cm = (iss & (1 << 8)) != 0; // CM: Cache maintenance
267                    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; // WnR: Write not Read
279                    let cm = (iss & (1 << 8)) != 0; // CM: Cache maintenance
280                    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}