1use core::{borrow::Borrow, ops::Deref};
6
7#[derive(Clone, Debug, Eq, PartialEq)]
9#[repr(C)]
10pub struct RegisterState {
11 pub registers: [u64; 19],
13 padding: u64,
14 pub fp: u64,
16 pub sp: u64,
18 pub elr: usize,
19 pub spsr: u64,
20}
21
22const _: () = assert!(size_of::<RegisterState>() == 8 * 24);
23
24#[derive(Debug, Eq, PartialEq)]
26#[repr(transparent)]
27pub struct RegisterStateRef<'a>(&'a mut RegisterState);
28
29impl RegisterStateRef<'_> {
30 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
66pub trait ExceptionHandlers {
70 extern "C" fn sync_current(register_state: RegisterStateRef) {
72 _ = register_state;
73 panic!("Unexpected synchronous exception from current EL");
74 }
75
76 extern "C" fn irq_current(register_state: RegisterStateRef) {
78 _ = register_state;
79 panic!("Unexpected IRQ from current EL");
80 }
81
82 extern "C" fn fiq_current(register_state: RegisterStateRef) {
84 _ = register_state;
85 panic!("Unexpected FIQ from current EL");
86 }
87
88 extern "C" fn serror_current(register_state: RegisterStateRef) {
90 _ = register_state;
91 panic!("Unexpected SError from current EL");
92 }
93
94 extern "C" fn sync_lower(register_state: RegisterStateRef) {
96 _ = register_state;
97 panic!("Unexpected synchronous exception from lower EL");
98 }
99
100 extern "C" fn irq_lower(register_state: RegisterStateRef) {
102 _ = register_state;
103 panic!("Unexpected IRQ from lower EL");
104 }
105
106 extern "C" fn fiq_lower(register_state: RegisterStateRef) {
108 _ = register_state;
109 panic!("Unexpected FIQ from lower EL");
110 }
111
112 extern "C" fn serror_lower(register_state: RegisterStateRef) {
114 _ = register_state;
115 panic!("Unexpected SError from lower EL");
116 }
117}
118
119#[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}