aarch32-rt 0.4.0

Run-time support for Arm AArch32
Documentation
//! IRQ handler for for Armv4 to Armv6

use crate::{Cpsr, ProcessorMode};

#[cfg(target_arch = "arm")]
core::arch::global_asm!(
    r#"
    // Work around https://github.com/rust-lang/rust/issues/127269
    .fpu vfp2

    // Called from the vector table when we have an interrupt. Saves state and
    // calls a C-compatible handler like `extern "C" fn _irq_handler();` in
    // system mode (or SVC mode if the `svc-stack-interrupt` feature is
    // enabled).
    //
    // We call the C-compatible handler in a different mode because when when an
    // IRQ occurs, the PC is copied to LR_irq immediately. If the C code was
    // running in IRQ mode, then it will be using LR_irq for normal LR things
    // (because that's the LR register when you are in IRQ mode). Instantly
    // trashing the LR register of running code is bad. So, by switching to SYS
    // mode (or SVC mode) we ensure that LR_irq is always unused at the point an
    // IRQ occurs.
    //
    // See [ARM Cortex-R Series (Armv7-R) Programmer's Guide] for more details.
    //
    // [ARM Cortex-R Series (Armv7-R) Programmer's Guide]:
    //     https://developer.arm.com/documentation/den0042/0100/Exceptions-and-Interrupts/External-interrupt-requests/Nested-interrupt-handling
    .pushsection .text._asm_default_irq_handler
    .arm
    .global _asm_default_irq_handler
    .type _asm_default_irq_handler, %function
    _asm_default_irq_handler:
        sub     lr, lr, 4                 // Make sure we jump back to the right place
        stmfd   sp!, {{ lr }}             // Save adjusted LR to IRQ stack (1)
        mrs     lr, spsr                  // Grab SPSR (2)
        push    {{ lr }}                  // Push SPSR value (3)
        msr     cpsr_c, {handler_mode}    // Switch to handler mode (4)
        push    {{ lr }}                  // Push LR of handler mode before using it for stack alignment (5)
        and     lr, sp, 7                 // Align SP down to eight byte boundary using LR
        sub     sp, lr                    // SP now aligned - only push 64-bit values from here (6)
        push    {{ r0-r3, r12, lr }}      // Push preserved registers and alignment amount (7)
     "#,
    crate::fpu_context!("save"),
    r#"
        bl      _irq_handler              // Call C handler in the selected handler mode (they may choose to re-enable interrupts)
    "#,
    crate::fpu_context!("restore"),
    r#"
        pop     {{ r0-r3, r12, lr }}      // Pop alignment amount (in LR) and preserved registers to undo (7)
        add     sp, lr                    // Restore SP alignment using LR to undo (6)
        pop     {{ lr }}                  // Pop the actual link register of handler mode to undo (5)
        msr     cpsr_c, {irq_mode}        // Switch back to IRQ mode (with IRQ masked) to undo (4)
        pop     {{ lr }}                  // Grab saved SPSR to undo (3)
        msr     spsr, lr                  // Restore SPSR using LR to undo (2)
        ldmfd   sp!, {{ pc }}^            // Return from exception to undo (1) (^ => restore SPSR to CPSR)
    .size _asm_default_irq_handler, . - _asm_default_irq_handler
    .popsection
    "#,
    handler_mode = const HANDLER_MODE,
    irq_mode = const {
        Cpsr::new_with_raw_value(0)
            .with_mode(ProcessorMode::Irq)
            .with_i(true)
            .raw_value()
    }
);

#[cfg(feature = "svc-stack-interrupt")]
const HANDLER_MODE: u32 = const {
    Cpsr::new_with_raw_value(0)
        .with_mode(ProcessorMode::Svc)
        .with_i(true)
        .raw_value()
};

#[cfg(not(feature = "svc-stack-interrupt"))]
const HANDLER_MODE: u32 = const {
    Cpsr::new_with_raw_value(0)
        .with_mode(ProcessorMode::Sys)
        .with_i(true)
        .raw_value()
};