aarch64_rt/
exceptions.rs

1// Copyright 2025 The aarch64-rt Authors.
2// This project is dual-licensed under Apache 2.0 and MIT terms.
3// See LICENSE-APACHE and LICENSE-MIT for details.
4
5use core::{borrow::Borrow, ops::Deref};
6
7/// The register state saved before calling the exception handler.
8#[derive(Clone, Debug, Eq, PartialEq)]
9#[repr(C)]
10pub struct RegisterState {
11    /// Registers x0-x18.
12    pub registers: [u64; 19],
13    padding: u64,
14    /// Register x29, the Frame Pointer.
15    pub fp: u64,
16    /// Register x30, the Stack Pointer.
17    pub sp: u64,
18    pub elr: usize,
19    pub spsr: u64,
20}
21
22const _: () = assert!(size_of::<RegisterState>() == 8 * 24);
23
24/// A reference to the register state saved when an exception happened.
25#[derive(Debug, Eq, PartialEq)]
26#[repr(transparent)]
27pub struct RegisterStateRef<'a>(&'a mut RegisterState);
28
29impl RegisterStateRef<'_> {
30    /// Returns a mutable reference to the register state.
31    ///
32    /// # Safety
33    ///
34    /// Any changes made to the saved register state made via this reference must not cause
35    /// undefined behaviour when returning from the exception.
36    ///
37    /// Exactly what this means depends on the exception and the context in which it happened, but
38    /// for example changing the ELR to point to an invalid or unexpected location, or changing some
39    /// general-purpose register value which the code doesn't expect to change could cause undefined
40    /// behaviour.
41    pub unsafe fn get_mut(&mut self) -> &mut RegisterState {
42        self.0
43    }
44}
45
46impl AsRef<RegisterState> for RegisterStateRef<'_> {
47    fn as_ref(&self) -> &RegisterState {
48        self.0
49    }
50}
51
52impl Borrow<RegisterState> for RegisterStateRef<'_> {
53    fn borrow(&self) -> &RegisterState {
54        self.0
55    }
56}
57
58impl Deref for RegisterStateRef<'_> {
59    type Target = RegisterState;
60
61    fn deref(&self) -> &Self::Target {
62        self.0
63    }
64}
65
66/// Functions to handle aarch64 exceptions.
67///
68/// Each method has a default implementation which will panic.
69pub trait ExceptionHandlers {
70    /// Handles synchronous exceptions from the current exception level.
71    extern "C" fn sync_current(register_state: RegisterStateRef) {
72        _ = register_state;
73        panic!("Unexpected synchronous exception from current EL");
74    }
75
76    /// Handles IRQs from the current exception level.
77    extern "C" fn irq_current(register_state: RegisterStateRef) {
78        _ = register_state;
79        panic!("Unexpected IRQ from current EL");
80    }
81
82    /// Handles FIQs from the current exception level.
83    extern "C" fn fiq_current(register_state: RegisterStateRef) {
84        _ = register_state;
85        panic!("Unexpected FIQ from current EL");
86    }
87
88    /// Handles SErrors from the current exception level.
89    extern "C" fn serror_current(register_state: RegisterStateRef) {
90        _ = register_state;
91        panic!("Unexpected SError from current EL");
92    }
93
94    /// Handles synchronous exceptions from a lower exception level.
95    extern "C" fn sync_lower(register_state: RegisterStateRef) {
96        _ = register_state;
97        panic!("Unexpected synchronous exception from lower EL");
98    }
99
100    /// Handles IRQs from the a lower exception level.
101    extern "C" fn irq_lower(register_state: RegisterStateRef) {
102        _ = register_state;
103        panic!("Unexpected IRQ from lower EL");
104    }
105
106    /// Handles FIQs from the a lower exception level.
107    extern "C" fn fiq_lower(register_state: RegisterStateRef) {
108        _ = register_state;
109        panic!("Unexpected FIQ from lower EL");
110    }
111
112    /// Handles SErrors from a lower exception level.
113    extern "C" fn serror_lower(register_state: RegisterStateRef) {
114        _ = register_state;
115        panic!("Unexpected SError from lower EL");
116    }
117}
118
119/// Registers an implementation of the [`ExceptionHandlers`] trait to handle exceptions.
120#[macro_export]
121macro_rules! exception_handlers {
122    ($handlers:ty) => {
123        core::arch::global_asm!(
124            r#"
125/**
126 * Saves the volatile registers onto the stack. This currently takes 14
127 * instructions, so it can be used in exception handlers with 18 instructions
128 * left.
129 *
130 * On return, x0 and x1 are initialised to elr_elX and spsr_elX respectively,
131 * which can be used as the first and second arguments of a subsequent call.
132 */
133.macro save_volatile_to_stack el:req
134	/* Reserve stack space and save registers x0-x18, x29 & x30. */
135	stp x0, x1, [sp, #-(8 * 24)]!
136	stp x2, x3, [sp, #8 * 2]
137	stp x4, x5, [sp, #8 * 4]
138	stp x6, x7, [sp, #8 * 6]
139	stp x8, x9, [sp, #8 * 8]
140	stp x10, x11, [sp, #8 * 10]
141	stp x12, x13, [sp, #8 * 12]
142	stp x14, x15, [sp, #8 * 14]
143	stp x16, x17, [sp, #8 * 16]
144	str x18, [sp, #8 * 18]
145	stp x29, x30, [sp, #8 * 20]
146
147	/*
148	 * Save elr_elX & spsr_elX. This such that we can take nested exception
149	 * and still be able to unwind.
150	 */
151	mrs x0, elr_\el
152	mrs x1, spsr_\el
153	stp x0, x1, [sp, #8 * 22]
154.endm
155
156/**
157 * Restores the volatile registers from the stack. This currently takes 14
158 * instructions, so it can be used in exception handlers while still leaving 18
159 * instructions left; if paired with save_volatile_to_stack, there are 4
160 * instructions to spare.
161 */
162.macro restore_volatile_from_stack el:req
163	/* Restore registers x2-x18, x29 & x30. */
164	ldp x2, x3, [sp, #8 * 2]
165	ldp x4, x5, [sp, #8 * 4]
166	ldp x6, x7, [sp, #8 * 6]
167	ldp x8, x9, [sp, #8 * 8]
168	ldp x10, x11, [sp, #8 * 10]
169	ldp x12, x13, [sp, #8 * 12]
170	ldp x14, x15, [sp, #8 * 14]
171	ldp x16, x17, [sp, #8 * 16]
172	ldr x18, [sp, #8 * 18]
173	ldp x29, x30, [sp, #8 * 20]
174
175	/* Restore registers elr_elX & spsr_elX, using x0 & x1 as scratch. */
176	ldp x0, x1, [sp, #8 * 22]
177	msr elr_\el, x0
178	msr spsr_\el, x1
179
180	/* Restore x0 & x1, and release stack space. */
181	ldp x0, x1, [sp], #8 * 24
182.endm
183
184/**
185 * This is a generic handler for exceptions taken at the current EL. It saves
186 * volatile registers to the stack, calls the Rust handler, restores volatile
187 * registers, then returns.
188 *
189 * This also works for exceptions taken from lower ELs, if we don't care about
190 * non-volatile registers.
191 *
192 * Saving state and jumping to the Rust handler takes 16 instructions, and
193 * restoring and returning also takes 15 instructions, so we can fit the whole
194 * handler in 31 instructions, under the limit of 32.
195 */
196.macro current_exception handler:req el:req
197	save_volatile_to_stack \el
198	mov x0, sp
199	bl \handler
200	restore_volatile_from_stack \el
201	eret
202.endm
203
204.macro vector_table el:req
205.section .text.vector_table_\el, "ax"
206.global vector_table_\el
207.balign 0x800
208vector_table_\el:
209sync_cur_sp0_\el:
210	current_exception {sync_current} \el
211
212.balign 0x80
213irq_cur_sp0_\el:
214	current_exception {irq_current} \el
215
216.balign 0x80
217fiq_cur_sp0_\el:
218	current_exception {fiq_current} \el
219
220.balign 0x80
221serr_cur_sp0_\el:
222	current_exception {serror_current} \el
223
224.balign 0x80
225sync_cur_spx_\el:
226	current_exception {sync_current} \el
227
228.balign 0x80
229irq_cur_spx_\el:
230	current_exception {irq_current} \el
231
232.balign 0x80
233fiq_cur_spx_\el:
234	current_exception {fiq_current} \el
235
236.balign 0x80
237serr_cur_spx_\el:
238	current_exception {serror_current} \el
239
240.balign 0x80
241sync_lower_64_\el:
242	current_exception {sync_lower} \el
243
244.balign 0x80
245irq_lower_64_\el:
246	current_exception {irq_lower} \el
247
248.balign 0x80
249fiq_lower_64_\el:
250	current_exception {fiq_lower} \el
251
252.balign 0x80
253serr_lower_64_\el:
254	current_exception {serror_lower} \el
255
256.balign 0x80
257sync_lower_32_\el:
258	current_exception {sync_lower} \el
259
260.balign 0x80
261irq_lower_32_\el:
262	current_exception {irq_lower} \el
263
264.balign 0x80
265fiq_lower_32_\el:
266	current_exception {fiq_lower} \el
267
268.balign 0x80
269serr_lower_32_\el:
270	current_exception {serror_lower} \el
271
272.endm
273
274vector_table el1
275vector_table el2
276vector_table el3
277            "#,
278            sync_current = sym <$handlers as $crate::ExceptionHandlers>::sync_current,
279            irq_current = sym <$handlers as $crate::ExceptionHandlers>::irq_current,
280            fiq_current = sym <$handlers as $crate::ExceptionHandlers>::fiq_current,
281            serror_current = sym <$handlers as $crate::ExceptionHandlers>::serror_current,
282            sync_lower = sym <$handlers as $crate::ExceptionHandlers>::sync_lower,
283            irq_lower = sym <$handlers as $crate::ExceptionHandlers>::irq_lower,
284            fiq_lower = sym <$handlers as $crate::ExceptionHandlers>::fiq_lower,
285            serror_lower = sym <$handlers as $crate::ExceptionHandlers>::serror_lower,
286        );
287    };
288}