// ARM exception vector table + IRQ trampoline. Installed via VBAR
// (see boot.s) rather than relying on the fixed low-vectors address
// 0x00000000, so this works the same regardless of where this code is
// linked/loaded.
//
// Standard 8-entry ARM vector table: each slot is a PC-relative load
// from a nearby literal (the assembler manages the exact offset/pool
// placement for `ldr pc, =label`), so unlike the rest of this
// project's hand-computed addressing, there's no manual offset to get
// wrong here.
.section ".text.vectors"
.align 5
.global __vectors
__vectors:
ldr pc, =_start // Reset (unused: VBAR is
// programmed by boot.s
// before this table is
// reachable)
ldr pc, =__fault_undefined // Undefined instruction
ldr pc, =__fault_supervisor // Supervisor call (SWI)
ldr pc, =__fault_prefetch_abort // Prefetch abort
ldr pc, =__fault_data_abort // Data abort
.word 0 // Reserved, never taken
ldr pc, =__irq_trampoline // IRQ
ldr pc, =__fault_fiq // FIQ (never unmasked)
.ltorg
// Every faulting slot above lands here rather than branching straight to
// `__unhandled_exception`, so that the handler is told *which* one it
// was. Nothing else can tell it: all four of these enter through
// different vector slots but only two distinct CPSR modes -- a prefetch
// abort and a data abort are both mode 0x17 -- and the two differ in
// which fault registers hold the answer (IFAR/IFSR against DFAR/DFSR)
// and in how far `lr` is biased past the faulting instruction. A handler
// reading the mode alone has to print both pairs and let a person guess.
//
// The kind is the vector's own index, passed in r0 so that
// `__unhandled_exception` can be an ordinary `extern "C"` function of
// one argument. Two properties matter and both are load-bearing:
//
// * `b`, never `bl`. `lr` on entry to `__unhandled_exception` is still
// the biased faulting address, which is how a handler written before
// these stubs existed -- taking no argument and reading `lr` itself --
// goes on working unchanged.
// * r0 only. r0 is call-clobbered in the C ABI, so a handler that
// ignores the argument loses nothing by it, and every other register
// the faulting context was using is still what it was.
__fault_undefined:
mov r0, #1
b __unhandled_exception
__fault_supervisor:
mov r0, #2
b __unhandled_exception
__fault_prefetch_abort:
mov r0, #3
b __unhandled_exception
__fault_data_abort:
mov r0, #4
b __unhandled_exception
__fault_fiq:
mov r0, #7
b __unhandled_exception
// `__unhandled_exception` is where every faulting slot above ends up,
// reached through the stubs that number it -- so a handler is
// `extern "C" fn(kind: u32)`, with `kind` the vector index (1 undefined,
// 2 supervisor call, 3 prefetch abort, 4 data abort, 7 FIQ). One written
// against the older signature, taking nothing and reading `lr`, still
// links and still works; see the stubs for why.
//
// The symbol itself is defined elsewhere, and exactly one definition is
// in any build: `fault.s` under the `fault-report` feature, which
// reports and halts, or `fault_fallback.s` without it, which is weak and
// parks silently so an application can override it. They are separate
// files rather than a weak default here and a strong override there
// because both would be in this crate's single stream of `global_asm!`,
// where the assembler sees a duplicate definition rather than a
// resolvable weak symbol -- and whether it noticed came down to which
// codegen unit each landed in.
//
// `boot.s` gives ABT/UND/FIQ real stacks so that a handler can be an
// ordinary Rust function rather than something that has to avoid
// pushing. Those three are shared by every core rather than per-core
// (see boot.s), so two cores faulting at once would overwrite each
// other's frame -- the report of the second is the one to trust.
.global __irq_trampoline
__irq_trampoline:
// The IRQ return address is one instruction ahead of where
// execution should resume (architectural quirk of this exception
// type); back it up before saving so `movs pc, lr` below resumes
// at the correct instruction.
sub lr, lr, #4
push {{r0-r12, lr}}
bl __irq_handler
pop {{r0-r12, lr}}
// Copies SPSR_irq back into CPSR and branches to lr in one step —
// the standard ARM idiom for returning from an exception handler.
movs pc, lr
// Weak default so examples that never enable IRQ (the vast majority)
// don't need to define this themselves — a strong `__irq_handler`
// defined elsewhere (e.g. in an example, as an ordinary `#[no_mangle]
// extern "C" fn`) overrides this at link time, standard ELF weak-symbol
// semantics. Never actually reached unless something unmasks IRQ and
// enables a source without registering a real handler.
.weak __irq_handler
__irq_handler:
bx lr