tartan_arch/aarch64/
interrupt.rs

1//! Exception and interrupt handling.
2
3use super::ExceptionLevel;
4use crate::system_register_access;
5use core::arch::asm;
6use tartan_bitfield::bitfield;
7use tartan_c_enum::c_enum;
8
9// Must be re-exported so that crates that use these macros will be able to resolve it
10#[doc(hidden)]
11pub use paste::paste;
12
13
14/// Define an exception vector table that forwards all exceptions to the designated
15/// handler.
16///
17/// The first argument is the name of the vector table, which will be exported as an
18/// unmangled symbol and exposed to Rust code as an immutable static of type
19/// [`VectorTable`].
20///
21/// The second argument is the name of the handler function, which should have
22/// signature `func(Kind, Source)`.
23///
24/// # Example
25/// ```ignore
26/// # use tartan_arch::aarch64_exception_vector_table;
27/// # use tartan_arch::aarch64::ExceptionLevel;
28/// # use tartan_arch::aarch64::interrupt::{Kind, Source, VectorBaseAddressRegister};
29/// #
30/// aarch64_exception_vector_table!(exception_table_el1, handle_exception);
31///
32/// fn handle_exception(kind: Kind, source: Source) {
33///     panic!("Exception {:?} from {:?}", kind, source);
34/// }
35///
36/// fn main() {
37///     VectorBaseAddressRegister::set(
38///         ExceptionLevel::One,
39///         unsafe { &exception_table_el1 }
40///     );
41/// }
42/// ```
43#[macro_export]
44macro_rules! aarch64_exception_vector_table {
45    [$table:ident, $handler:path] => {
46        extern "C" {
47            #[no_mangle]
48            static $table: $crate::aarch64::interrupt::VectorTable;
49        }
50
51        core::arch::global_asm!(concat!(
52            "
53            //
54            // Exceptions from current level using SP_EL0 (thread mode)
55
56            .balign 0x800
57        ", stringify!($table), ":
58            // Synchronous exception
59            stp lr, fp, [sp, #-16]!
60            bl ", stringify!($table), "_save
61            bl ", stringify!($table), "_sync_exception_from_thread
62            bl ", stringify!($table), "_restore
63            ldp lr, fp, [sp], #16
64            eret
65
66            .balign 0x80
67            // Interrupt
68            stp lr, fp, [sp, #-16]!
69            bl ", stringify!($table), "_save
70            bl ", stringify!($table), "_interrupt_from_thread
71            bl ", stringify!($table), "_restore
72            ldp lr, fp, [sp], #16
73            eret
74
75            .balign 0x80
76            // Fast interrupt
77            stp lr, fp, [sp, #-16]!
78            bl ", stringify!($table), "_save
79            bl ", stringify!($table), "_fast_interrupt_from_thread
80            bl ", stringify!($table), "_restore
81            ldp lr, fp, [sp], #16
82            eret
83
84            .balign 0x80
85            // System error
86            stp lr, fp, [sp, #-16]!
87            bl ", stringify!($table), "_save
88            bl ", stringify!($table), "_system_error_from_thread
89            bl ", stringify!($table), "_restore
90            ldp lr, fp, [sp], #16
91            eret
92
93            //
94            // Exceptions from current level using current-level SP (handler mode)
95
96            .balign 0x80
97            // Synchronous exception
98            stp lr, fp, [sp, #-16]!
99            bl ", stringify!($table), "_save
100            bl ", stringify!($table), "_sync_exception_from_handler
101            bl ", stringify!($table), "_restore
102            ldp lr, fp, [sp], #16
103            eret
104
105            .balign 0x80
106            // Interrupt
107            stp lr, fp, [sp, #-16]!
108            bl ", stringify!($table), "_save
109            bl ", stringify!($table), "_interrupt_from_handler
110            bl ", stringify!($table), "_restore
111            ldp lr, fp, [sp], #16
112            eret
113
114            .balign 0x80
115            // Fast interrupt
116            stp lr, fp, [sp, #-16]!
117            bl ", stringify!($table), "_save
118            bl ", stringify!($table), "_fast_interrupt_from_handler
119            bl ", stringify!($table), "_restore
120            ldp lr, fp, [sp], #16
121            eret
122
123            .balign 0x80
124            // System error
125            stp lr, fp, [sp, #-16]!
126            bl ", stringify!($table), "_save
127            bl ", stringify!($table), "_system_error_from_handler
128            bl ", stringify!($table), "_restore
129            ldp lr, fp, [sp], #16
130            eret
131
132            //
133            // Exceptions from lower level in 64-bit mode
134
135            .balign 0x80
136            // Synchronous exception
137            stp lr, fp, [sp, #-16]!
138            bl ", stringify!($table), "_save
139            bl ", stringify!($table), "_sync_exception_from_lower
140            bl ", stringify!($table), "_restore
141            ldp lr, fp, [sp], #16
142            eret
143
144            .balign 0x80
145            // Interrupt
146            stp lr, fp, [sp, #-16]!
147            bl ", stringify!($table), "_save
148            bl ", stringify!($table), "_interrupt_from_lower
149            bl ", stringify!($table), "_restore
150            ldp lr, fp, [sp], #16
151            eret
152
153            .balign 0x80
154            // Fast interrupt
155            stp lr, fp, [sp, #-16]!
156            bl ", stringify!($table), "_save
157            bl ", stringify!($table), "_fast_interrupt_from_lower
158            bl ", stringify!($table), "_restore
159            ldp lr, fp, [sp], #16
160            eret
161
162            .balign 0x80
163            // System error
164            stp lr, fp, [sp, #-16]!
165            bl ", stringify!($table), "_save
166            bl ", stringify!($table), "_system_error_from_lower
167            bl ", stringify!($table), "_restore
168            ldp lr, fp, [sp], #16
169            eret
170
171            //
172            // Exceptions from lower level in 32-bit mode
173
174            .balign 0x80
175            // Synchronous exception
176            stp lr, fp, [sp, #-16]!
177            bl ", stringify!($table), "_save
178            bl ", stringify!($table), "_sync_exception_from_lower_32bit
179            bl ", stringify!($table), "_restore
180            ldp lr, fp, [sp], #16
181            eret
182
183            .balign 0x80
184            // Interrupt
185            stp lr, fp, [sp, #-16]!
186            bl ", stringify!($table), "_save
187            bl ", stringify!($table), "_interrupt_from_lower_32bit
188            bl ", stringify!($table), "_restore
189            ldp lr, fp, [sp], #16
190            eret
191
192            .balign 0x80
193            // Fast interrupt
194            stp lr, fp, [sp, #-16]!
195            bl ", stringify!($table), "_save
196            bl ", stringify!($table), "_fast_interrupt_from_lower_32bit
197            bl ", stringify!($table), "_restore
198            ldp lr, fp, [sp], #16
199            eret
200
201            .balign 0x80
202            // System error
203            stp lr, fp, [sp, #-16]!
204            bl ", stringify!($table), "_save
205            bl ", stringify!($table), "_system_error_from_lower_32bit
206            bl ", stringify!($table), "_restore
207            ldp lr, fp, [sp], #16
208            eret
209
210
211        ", stringify!($table), "_save:
212            stp x0, x1, [sp, #-16]!
213            stp x2, x3, [sp, #-16]!
214            stp x4, x5, [sp, #-16]!
215            stp x6, x7, [sp, #-16]!
216            stp x8, x9, [sp, #-16]!
217            stp x10, x11, [sp, #-16]!
218            stp x12, x13, [sp, #-16]!
219            stp x14, x15, [sp, #-16]!
220
221        ", stringify!($table), "_restore:
222            ldp x14, x15, [sp], #16
223            ldp x12, x13, [sp], #16
224            ldp x10, x11, [sp], #16
225            ldp x8, x9, [sp], #16
226            ldp x6, x7, [sp], #16
227            ldp x4, x5, [sp], #16
228            ldp x2, x3, [sp], #16
229            ldp x0, x1, [sp], #16
230            "
231        ));
232
233        $crate::aarch64::interrupt::paste! {
234            #[no_mangle]
235            fn [< $table _sync_exception_from_thread >]() {
236                $handler(
237                    $crate::aarch64::interrupt::Kind::Synchronous,
238                    $crate::aarch64::interrupt::Source::CurrentLevelThread,
239                )
240            }
241
242            #[no_mangle]
243            fn [< $table _interrupt_from_thread >]() {
244                $handler(
245                    $crate::aarch64::interrupt::Kind::Interrupt,
246                    $crate::aarch64::interrupt::Source::CurrentLevelThread,
247                )
248            }
249
250            #[no_mangle]
251            fn [< $table _fast_interrupt_from_thread >]() {
252                $handler(
253                    $crate::aarch64::interrupt::Kind::FastInterrupt,
254                    $crate::aarch64::interrupt::Source::CurrentLevelThread,
255                )
256            }
257
258            #[no_mangle]
259            fn [< $table _system_error_from_thread >]() {
260                $handler(
261                    $crate::aarch64::interrupt::Kind::SystemError,
262                    $crate::aarch64::interrupt::Source::CurrentLevelThread,
263                )
264            }
265
266            #[no_mangle]
267            fn [< $table _sync_exception_from_handler >]() {
268                $handler(
269                    $crate::aarch64::interrupt::Kind::Synchronous,
270                    $crate::aarch64::interrupt::Source::CurrentLevelHandler,
271                )
272            }
273
274            #[no_mangle]
275            fn [< $table _interrupt_from_handler >]() {
276                $handler(
277                    $crate::aarch64::interrupt::Kind::Interrupt,
278                    $crate::aarch64::interrupt::Source::CurrentLevelHandler,
279                )
280            }
281
282            #[no_mangle]
283            fn [< $table _fast_interrupt_from_handler >]() {
284                $handler(
285                    $crate::aarch64::interrupt::Kind::FastInterrupt,
286                    $crate::aarch64::interrupt::Source::CurrentLevelHandler,
287                )
288            }
289
290            #[no_mangle]
291            fn [< $table _system_error_from_handler >]() {
292                $handler(
293                    $crate::aarch64::interrupt::Kind::SystemError,
294                    $crate::aarch64::interrupt::Source::CurrentLevelHandler,
295                )
296            }
297
298            #[no_mangle]
299            fn [< $table _sync_exception_from_lower >]() {
300                $handler(
301                    $crate::aarch64::interrupt::Kind::Synchronous,
302                    $crate::aarch64::interrupt::Source::LowerLevel,
303                )
304            }
305
306            #[no_mangle]
307            fn [< $table _interrupt_from_lower >]() {
308                $handler(
309                    $crate::aarch64::interrupt::Kind::Interrupt,
310                    $crate::aarch64::interrupt::Source::LowerLevel,
311                )
312            }
313
314            #[no_mangle]
315            fn [< $table _fast_interrupt_from_lower >]() {
316                $handler(
317                    $crate::aarch64::interrupt::Kind::FastInterrupt,
318                    $crate::aarch64::interrupt::Source::LowerLevel,
319                )
320            }
321
322            #[no_mangle]
323            fn [< $table _system_error_from_lower >]() {
324                $handler(
325                    $crate::aarch64::interrupt::Kind::SystemError,
326                    $crate::aarch64::interrupt::Source::LowerLevel,
327                )
328            }
329
330            #[no_mangle]
331            fn [< $table _sync_exception_from_lower_32bit >]() {
332                $handler(
333                    $crate::aarch64::interrupt::Kind::Synchronous,
334                    $crate::aarch64::interrupt::Source::LowerLevel32Bit,
335                )
336            }
337
338            #[no_mangle]
339            fn [< $table _interrupt_from_lower_32bit >]() {
340                $handler(
341                    $crate::aarch64::interrupt::Kind::Interrupt,
342                    $crate::aarch64::interrupt::Source::LowerLevel32Bit,
343                )
344            }
345
346            #[no_mangle]
347            fn [< $table _fast_interrupt_from_lower_32bit >]() {
348                $handler(
349                    $crate::aarch64::interrupt::Kind::FastInterrupt,
350                    $crate::aarch64::interrupt::Source::LowerLevel32Bit,
351                )
352            }
353
354            #[no_mangle]
355            fn [< $table _system_error_from_lower_32bit >]() {
356                $handler(
357                    $crate::aarch64::interrupt::Kind::SystemError,
358                    $crate::aarch64::interrupt::Source::LowerLevel32Bit,
359                )
360            }
361        }
362    };
363}
364
365/// The kind of exception being handled: sync, IRQ, FIQ, SError.
366#[repr(u8)]
367#[derive(Debug, Clone, Copy, PartialEq, Eq)]
368pub enum Kind {
369    /// A standard exception traceable to a specific instruction.
370    Synchronous,
371    /// An external interrupt (IRQ).
372    Interrupt,
373    /// An external interrupt through the higher-priority fast interrupt (FIQ) mechanism.
374    FastInterrupt,
375    /// An internal error with the processor (SError).
376    SystemError,
377}
378
379/// The exception level that generated an exception.
380#[repr(u8)]
381#[derive(Debug, Clone, Copy, PartialEq, Eq)]
382pub enum Source {
383    /// Exception from current level using SP_EL0 (thread mode)
384    CurrentLevelThread,
385    /// Exception from current level using current-level SP (handler mode)
386    CurrentLevelHandler,
387    /// Exception from lower level (e.g., EL0) in 64-bit mode
388    LowerLevel,
389    /// Exception from lower level (e.g., EL0) in 32-bit mode
390    LowerLevel32Bit,
391}
392
393
394/// A single entry in the exception [`VectorTable`].
395///
396/// The bytes of the entry are interpreted as instructions and executed when an exception
397/// of the corresponding [`Kind`] + [`Source`] is encountered. Consequently, it isn't
398/// useful to instantiate this type directly. Use [`aarch64_exception_vector_table`]
399/// instead.
400#[repr(align(0x80))]
401#[allow(dead_code)]
402pub struct VectorEntry([u8; 0x80]);
403
404
405/// Exception vector table that contains code to handle exceptions from each combination
406/// of [`Kind`] and [`Source`].
407#[repr(align(0x800))]
408#[allow(dead_code)]
409pub struct VectorTable([VectorEntry; 16]);
410
411
412/// `VBAR_ELx`: Contains the address of the [`VectorTable`] that the processor should use
413/// at a given exception level.
414pub enum VectorBaseAddressRegister {}
415
416impl VectorBaseAddressRegister {
417    /// Retrieve the current value of this register for the specified exception level.
418    ///
419    /// Only defined for exception levels 1–3. The register for a given exception level is
420    /// only accessible from that level or higher.
421    pub fn get(level: ExceptionLevel) -> *const VectorTable {
422        let mut value: *const VectorTable;
423        unsafe {
424            match level {
425                ExceptionLevel::Zero => panic!("This register does not exist for EL0"),
426                ExceptionLevel::One => asm!("mrs {}, vbar_el1", out(reg) value),
427                ExceptionLevel::Two => asm!("mrs {}, vbar_el2", out(reg) value),
428                ExceptionLevel::Three => asm!("mrs {}, vbar_el3", out(reg) value),
429            }
430        }
431        value
432    }
433
434    /// Update the register for the specified exception level with the given value.
435    ///
436    /// Address must be aligned to 11 bits (2048).
437    pub fn set(level: ExceptionLevel, value: *const VectorTable) {
438        unsafe {
439            match level {
440                ExceptionLevel::Zero => panic!("This register does not exist for EL0"),
441                ExceptionLevel::One => asm!("msr vbar_el1, {}", in(reg) value),
442                ExceptionLevel::Two => asm!("msr vbar_el2, {}", in(reg) value),
443                ExceptionLevel::Three => asm!("msr vbar_el3, {}", in(reg) value),
444            }
445        }
446    }
447}
448
449
450bitfield! {
451    /// `DAIF`: Controls masking of different kinds of exceptions.
452    pub struct MaskRegister(u64) {
453        /// `F`: Mask fast (FIQ) interrupts.
454        [6] pub fast_interrupts_masked,
455        /// `I`: Mask regular (IRQ) interrupts.
456        [7] pub interrupts_masked,
457        /// `A`: Mask system error (SError) exceptions.
458        [8] pub system_error_masked,
459        /// `D`: Mask debugging-related exceptions (breakpoints, watchpoints, stepping)
460        /// for the current exception level.
461        [9] pub debug_masked,
462    }
463}
464
465system_register_access!(MaskRegister, "DAIF");
466
467
468bitfield! {
469    /// `ESR_ELx`: Holds information about the cause of the exception currently being
470    /// handled.
471    pub struct SyndromeRegister(u64) {
472        /// `EC`: The class of exception that was triggered.
473        [26..32] pub class: u8 as Class,
474        /// `IL`: When set, indicates that the instruction that triggered this exception
475        /// was a 32-bit opcode (A64 or classic Arm). Otherwise, the opcode was 16 bits
476        /// (Thumb).
477        [25] pub length_32bit,
478        /// `ISS`: Additional data about the exception in a format that is specific to
479        /// the [`class`](Self::class) value.
480        [ 0..25] pub class_data: u32,
481    }
482}
483
484impl SyndromeRegister {
485    /// Retrieve the current value of this register for the specified exception level.
486    ///
487    /// Only defined for exception levels 1–3. The register for a given exception level is
488    /// only accessible from that level or higher.
489    pub fn get(level: ExceptionLevel) -> Self {
490        let mut value = Self(0);
491        unsafe {
492            match level {
493                ExceptionLevel::Zero => panic!("This register does not exist for EL0"),
494                ExceptionLevel::One => asm!("mrs {}, esr_el1", out(reg) value.0),
495                ExceptionLevel::Two => asm!("mrs {}, esr_el2", out(reg) value.0),
496                ExceptionLevel::Three => asm!("mrs {}, esr_el3", out(reg) value.0),
497            }
498        }
499        value
500    }
501
502    /// Update the register for the specified exception level with the given value.
503    pub fn set(level: ExceptionLevel, value: Self) {
504        unsafe {
505            match level {
506                ExceptionLevel::Zero => panic!("This register does not exist for EL0"),
507                ExceptionLevel::One => asm!("msr esr_el1, {}", in(reg) value.0),
508                ExceptionLevel::Two => asm!("msr esr_el2, {}", in(reg) value.0),
509                ExceptionLevel::Three => asm!("msr esr_el3, {}", in(reg) value.0),
510            }
511        }
512    }
513}
514
515
516c_enum! {
517    /// Classifies different exception causes.
518    ///
519    /// Note that the Arm documentation defines more variants than are defined in this
520    /// type. This only includes exception classes that can be triggered from Aarch64
521    /// state and taken to EL1.
522    pub enum Class(u8) {
523        /// Tried to execute an undefined/unsupported opcode, or for any other reason that
524        /// does not fall under another exception class.
525        Unknown                     = 0b00_0000,
526        /// Trapped execution of a wait instruction (`WFE`/`WFI`).
527        Wait                        = 0b00_0001,
528        /// Trapped access to a FPU/vector register or instruction.
529        FPUAccess                   = 0b00_0111,
530        /// Tried to execute instructions after an illegal return.
531        IllegalState                = 0b00_1110,
532        /// Triggered a system call via the `SVC` instruction.
533        SupervisorCall              = 0b01_0101,
534        /// Trapped access to a system register or instruction.
535        SystemInstruction           = 0b01_1000,
536        /// Trapped access to a vector register or instruction.
537        VectorAccess                = 0b01_1001,
538        /// Improperly authenticated pointer detected. Requires `FEAT_FPAC`.
539        PointerAuthFailure          = 0b01_1100,
540        /// Instruction abort triggered at lower exception level.
541        InstructionAbortFromLower   = 0b10_0000,
542        /// Instruction abort triggered at current exception level.
543        InstructionAbortFromCurrent = 0b10_0001,
544        /// Program counter not properly aligned.
545        PCAlignment                 = 0b10_0010,
546        /// Data abort triggered at lower exception level.
547        DataAbortFromLower          = 0b10_0100,
548        /// Data abort triggered at current exception level.
549        DataAbortFromCurrent        = 0b10_0101,
550        /// Stack pointer not properly aligned.
551        SPAlignment                 = 0b10_0110,
552        /// Trapped floating point exception.
553        FloatException              = 0b10_1100,
554        /// System error (SError).
555        SystemError                 = 0b10_1111,
556        /// Breakpoint triggered at lower exception level.
557        BreakpointFromLower         = 0b11_0000,
558        /// Breakpoint triggered at current exception level.
559        BreakpointFromCurrent       = 0b11_0001,
560        /// Software step at lower exception level.
561        StepFromLower               = 0b11_0010,
562        /// Software step at current exception level.
563        StepFromCurrent             = 0b11_0011,
564        /// Watchpoint triggered at lower exception level.
565        WatchpointFromLower         = 0b11_0100,
566        /// Watchpoint triggered at current exception level.
567        WatchpointFromCurrent       = 0b11_0101,
568        /// Executed a `BRK` breakpoint instruction.
569        BreakpointInstruction       = 0b11_1100,
570    }
571}