//! Exceptions and interrupts. //! //! On arm64 everything that interrupts normal flow is an "exception": a //! fault, a system call, a hardware interrupt (IRQ). The CPU jumps into a //! **vector table** whose address is in `VBAR_EL1`: 16 slots of 128 bytes, //! 4 kinds (synchronous, IRQ, FIQ, SError) for each of 4 situations //! (current level with SP_EL0, current level with SP_ELx, lower level in //! 64-bit, lower level in 32-bit). We always run at EL1 on SP_EL1, so slots //! 4-7 are the ones that matter for now. //! //! Each slot saves x0/x1 and jumps to common code that saves the rest //! (plus ELR_EL1, where to return, and SPSR_EL1, the saved flags) and calls //! `aarch64_exception(kind, frame)`. //! //! - **IRQ**: ask the interrupt controller which one, handle it (the //! timer), say we're done, return with `eret`. //! - **anything synchronous** (a bad memory access, an undefined //! instruction, `brk`...): print what happened, using `ESR_EL1` (the //! "syndrome": what kind of exception, `EC` = bits 31:26) and `ELR_EL1` //! (the instruction that caused it), and stop: under QEMU through //! semihosting with exit status 3, so a script can tell. use super::{board, console, timer}; use core::arch::asm; core::arch::global_asm!( r#" .macro VENTRY kind .balign 0x80 sub sp, sp, #272 stp x0, x1, [sp, #0] mov x0, #\kind b __exc_common .endm .section .text.vectors, "ax" .balign 0x800 .global __vectors __vectors: VENTRY 0 VENTRY 1 VENTRY 2 VENTRY 3 VENTRY 4 VENTRY 5 VENTRY 6 VENTRY 7 VENTRY 8 VENTRY 9 VENTRY 10 VENTRY 11 VENTRY 12 VENTRY 13 VENTRY 14 VENTRY 15 __exc_common: stp x2, x3, [sp, #16] stp x4, x5, [sp, #32] stp x6, x7, [sp, #48] stp x8, x9, [sp, #64] stp x10, x11, [sp, #80] stp x12, x13, [sp, #96] stp x14, x15, [sp, #112] stp x16, x17, [sp, #128] stp x18, x19, [sp, #144] stp x20, x21, [sp, #160] stp x22, x23, [sp, #176] stp x24, x25, [sp, #192] stp x26, x27, [sp, #208] stp x28, x29, [sp, #224] mrs x1, elr_el1 stp x30, x1, [sp, #240] mrs x1, spsr_el1 str x1, [sp, #256] mov x1, sp bl aarch64_exception ldr x1, [sp, #256] msr spsr_el1, x1 ldp x30, x1, [sp, #240] msr elr_el1, x1 ldp x2, x3, [sp, #16] ldp x4, x5, [sp, #32] ldp x6, x7, [sp, #48] ldp x8, x9, [sp, #64] ldp x10, x11, [sp, #80] ldp x12, x13, [sp, #96] ldp x14, x15, [sp, #112] ldp x16, x17, [sp, #128] ldp x18, x19, [sp, #144] ldp x20, x21, [sp, #160] ldp x22, x23, [sp, #176] ldp x24, x25, [sp, #192] ldp x26, x27, [sp, #208] ldp x28, x29, [sp, #224] ldp x0, x1, [sp, #0] add sp, sp, #272 eret "# ); /// What the common code saved, in this order. #[repr(C)] pub struct Frame { pub x: [u64; 31], pub elr: u64, pub spsr: u64, } unsafe extern "C" { static __vectors: u8; } /// Point VBAR_EL1 at the table. pub fn init() { unsafe { asm!("msr vbar_el1, {}", "isb", in(reg) &raw const __vectors as u64); } } /// Let IRQs in (clear the I bit in DAIF). pub fn enable() { unsafe { asm!("msr daifclr, #2", options(nomem, nostack)) }; } const KINDS: [&str; 4] = ["synchronous", "IRQ", "FIQ", "SError"]; const FROM: [&str; 4] = ["EL1 with SP_EL0", "EL1", "EL0 (64-bit)", "EL0 (32-bit)"]; /// What `ESR_EL1.EC` means, for the ones we're likely to see. pub fn describe_ec(ec: u64) -> &'static str { match ec { 0x00 => "unknown reason (an undefined instruction?)", 0x01 => "WFI/WFE trapped", 0x07 => "FP/SIMD access while disabled", 0x0e => "illegal execution state", 0x15 => "SVC (system call) from 64-bit code", 0x18 => "MSR/MRS trapped", 0x20 => "instruction abort from a lower level", 0x21 => "instruction abort (bad address to run code from)", 0x22 => "PC not aligned", 0x24 => "data abort from a lower level", 0x25 => "data abort (bad memory access)", 0x26 => "SP not aligned", 0x2f => "SError", 0x3c => "BRK instruction", _ => "(see the Arm ARM, ESR_EL1)", } } #[unsafe(no_mangle)] extern "C" fn aarch64_exception(kind: u64, frame: &mut Frame) { if kind == 5 { handle_irq(); return; } let (esr, far): (u64, u64); unsafe { asm!("mrs {}, esr_el1", "mrs {}, far_el1", out(reg) esr, out(reg) far); console::force_unlock(); } let ec = esr >> 26; crate::println!( "[exception] {} exception from {}: ESR_EL1={:#010x} (EC {:#04x}: {})", KINDS[(kind & 3) as usize], FROM[(kind >> 2) as usize & 3], esr, ec, describe_ec(ec) ); crate::println!("[exception] ELR_EL1={:#x} FAR_EL1={:#x} SPSR_EL1={:#x}", frame.elr, far, frame.spsr); crate::println!("[exception] halting (QEMU exit status 3)"); super::exit(3); } fn handle_irq() { let b = board::current(); while let Some(irq) = b.irq().ack() { if irq == b.timer_irq() { timer::on_interrupt(); } else { crate::println!("[irq] unexpected interrupt {}", irq); } b.irq().eoi(irq); } }