From af3fce85cd2dd7be57f389b9931896d3410c56d8 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Wed, 9 Apr 2025 17:02:51 +0100 Subject: [PATCH 1/6] Fix the undef handler. It was looking at CPSR's Thumb bit, which tells you if the *handler* is in thumb mode, not the code that threw the fault. Change the test to validate the address of the failing function, to verify that we've got this right. Also fixes the issue of _asm_default_undefined_handler damaging r4. You have to save all the state first, then you can touch registers. --- cortex-a-rt/src/lib.rs | 58 ++++++++------ cortex-r-rt/src/lib.rs | 61 ++++++++------ .../abt-exception-armv7r-none-eabi.out | 9 +++ ...efetch-exception-a32-armv7r-none-eabi.out} | 4 +- ...efetch-exception-t32-armv7r-none-eabi.out} | 4 +- .../undef-exception-a32-armv7a-none-eabi.out | 5 ++ .../undef-exception-a32-armv7r-none-eabi.out | 5 ++ ...undef-exception-a32-armv7r-none-eabihf.out | 5 ++ .../undef-exception-armv7a-none-eabi.out | 3 - .../undef-exception-armv7r-none-eabihf.out | 3 - .../undef-exception-t32-armv7a-none-eabi.out | 5 ++ .../undef-exception-t32-armv7r-none-eabi.out | 5 ++ ...undef-exception-t32-armv7r-none-eabihf.out | 5 ++ examples/versatileab/src/bin/abt-exception.rs | 6 +- .../src/bin/undef-exception-a32.rs | 80 +++++++++++++++++++ .../src/bin/undef-exception-t32.rs | 80 +++++++++++++++++++ .../versatileab/src/bin/undef-exception.rs | 42 ---------- tests.sh | 18 +++-- 18 files changed, 289 insertions(+), 109 deletions(-) create mode 100644 examples/versatileab/reference/abt-exception-armv7r-none-eabi.out rename examples/versatileab/reference/{prefetch-exception-armv7a-none-eabi.out => prefetch-exception-a32-armv7r-none-eabi.out} (80%) rename examples/versatileab/reference/{prefetch-exception-armv7r-none-eabihf.out => prefetch-exception-t32-armv7r-none-eabi.out} (80%) create mode 100644 examples/versatileab/reference/undef-exception-a32-armv7a-none-eabi.out create mode 100644 examples/versatileab/reference/undef-exception-a32-armv7r-none-eabi.out create mode 100644 examples/versatileab/reference/undef-exception-a32-armv7r-none-eabihf.out delete mode 100644 examples/versatileab/reference/undef-exception-armv7a-none-eabi.out delete mode 100644 examples/versatileab/reference/undef-exception-armv7r-none-eabihf.out create mode 100644 examples/versatileab/reference/undef-exception-t32-armv7a-none-eabi.out create mode 100644 examples/versatileab/reference/undef-exception-t32-armv7r-none-eabi.out create mode 100644 examples/versatileab/reference/undef-exception-t32-armv7r-none-eabihf.out create mode 100644 examples/versatileab/src/bin/undef-exception-a32.rs create mode 100644 examples/versatileab/src/bin/undef-exception-t32.rs delete mode 100644 examples/versatileab/src/bin/undef-exception.rs diff --git a/cortex-a-rt/src/lib.rs b/cortex-a-rt/src/lib.rs index a425435..ce8bae8 100644 --- a/cortex-a-rt/src/lib.rs +++ b/cortex-a-rt/src/lib.rs @@ -91,7 +91,7 @@ //! //! ```rust //! #[unsafe(no_mangle)] -//! extern "C" fn _undefined_handler(faulting_instruction: u32); +//! extern "C" fn _undefined_handler(addr: usize); //! ``` //! //! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception @@ -103,7 +103,7 @@ //! //! ```rust //! #[unsafe(no_mangle)] -//! extern "C" fn _abort_handler(faulting_instruction: u32); +//! extern "C" fn _abort_handler(addr: usize); //! ``` //! //! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception @@ -115,7 +115,7 @@ //! //! ```rust //! #[unsafe(no_mangle)] -//! extern "C" fn _prefetch_handler(faulting_instruction: u32); +//! extern "C" fn _prefetch_handler(addr: usize); //! ``` //! //! ### ASM functions @@ -130,7 +130,7 @@ //! * `_asm_undefined_handler` - a naked function to call when an Undefined //! Exception occurs. Our linker script PROVIDEs a default function at //! `_asm_default_undefined_handler` but you can override it. -//! * `_asm_prefetch_handler` - a naked function to call when an Prefetch +//! * `_asm_prefetch_handler` - a naked function to call when a Prefetch //! Exception occurs. Our linker script PROVIDEs a default function at //! `_asm_default_prefetch_handler` but you can override it. The provided default //! handler will perform an exception return to the faulting address. @@ -381,7 +381,7 @@ core::arch::global_asm!( // Called from the vector table when we have an software interrupt. // Saves state and calls a C-compatible handler like - // `extern "C" fn svc_handler(svc: u32, context: *const u32);` + // `extern "C" fn svc_handler(svc: u32);` .global _asm_svc_handler .type _asm_svc_handler, %function _asm_svc_handler: @@ -426,44 +426,52 @@ core::arch::global_asm!( // Called from the vector table when we have an undefined exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _undefined_handler();` + // `extern "C" fn _undefined_handler(addr: usize);` .global _asm_default_undefined_handler .type _asm_default_undefined_handler, %function _asm_default_undefined_handler: - // First adjust LR for two purposes: Passing the faulting instruction to the C handler, - // and to return to the failing instruction after the C handler returns. - // Load processor status - mrs r4, cpsr - // Occurred in Thumb state? - tst r4, {t_bit} - // If not in Thumb mode, branch to not_thumb - beq not_thumb - subs lr, lr, #2 - b done -not_thumb: - // Subtract 4 from LR (ARM mode) - subs lr, lr, #4 -done: // state save from compiled code srsfd sp!, {und_mode} + // to work out what mode we're in, we need R0 + push {{r0}} + // First adjust LR for two purposes: Passing the faulting instruction to the C handler, + // and to return to the failing instruction after the C handler returns. + // Load processor status for the calling code + mrs r0, spsr + // Was the code that triggered the exception in Thumb state? + tst r0, {t_bit} + // Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual. + ite eq + subeq lr, lr, #4 + subne lr, lr, #2 + // save the newly computed LR + push {{lr}} + // now do our standard exception save "#, save_context!(), r#" // Pass the faulting instruction address to the handler. - mov r0, lr + mov r0, lr // call C handler bl _undefined_handler + // do our standard restore "#, restore_context!(), r#" + // get our saved LR + pop {{lr}} + // get our real saved R0 + pop {{r0}} + // overwrite the saved LR with the adjusted one + str lr, [sp] // Return to the failing instruction which is the recommended approach by ARM. rfefd sp! .size _asm_default_undefined_handler, . - _asm_default_undefined_handler - // Called from the vector table when we have an undefined exception. + // Called from the vector table when we have a prefetch exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _prefetch_handler();` + // `extern "C" fn _prefetch_handler(addr: usize);` .global _asm_default_prefetch_handler .type _asm_default_prefetch_handler, %function _asm_default_prefetch_handler: @@ -488,7 +496,7 @@ done: // Called from the vector table when we have an undefined exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _abort_handler();` + // `extern "C" fn _abort_handler(addr: usize);` .global _asm_default_abort_handler .type _asm_default_abort_handler, %function _asm_default_abort_handler: @@ -500,7 +508,7 @@ done: save_context!(), r#" // Pass the faulting instruction address to the handler. - mov r0, lr + mov r0, lr // call C handler bl _abort_handler "#, diff --git a/cortex-r-rt/src/lib.rs b/cortex-r-rt/src/lib.rs index 3312eda..f4f4a15 100644 --- a/cortex-r-rt/src/lib.rs +++ b/cortex-r-rt/src/lib.rs @@ -74,7 +74,7 @@ //! //! ```rust //! #[unsafe(no_mangle)] -//! extern "C" fn _undefined_handler(faulting_instruction: u32); +//! extern "C" fn _undefined_handler(addr: usize); //! ``` //! //! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception @@ -86,7 +86,7 @@ //! //! ```rust //! #[unsafe(no_mangle)] -//! extern "C" fn _abort_handler(faulting_instruction: u32); +//! extern "C" fn _abort_handler(addr: usize); //! ``` //! //! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception @@ -98,7 +98,7 @@ //! //! ```rust //! #[unsafe(no_mangle)] -//! extern "C" fn _prefetch_handler(faulting_instruction: u32); +//! extern "C" fn _prefetch_handler(addr: usize); //! ``` //! //! ### ASM functions @@ -113,7 +113,7 @@ //! * `_asm_undefined_handler` - a naked function to call when an Undefined //! Exception occurs. Our linker script PROVIDEs a default function at //! `_asm_default_undefined_handler` but you can override it. -//! * `_asm_prefetch_handler` - a naked function to call when an Prefetch +//! * `_asm_prefetch_handler` - a naked function to call when a Prefetch //! Exception occurs. Our linker script PROVIDEs a default function at //! `_asm_default_prefetch_handler` but you can override it. The provided default //! handler will perform an exception return to the faulting address. @@ -313,7 +313,7 @@ core::arch::global_asm!( // Called from the vector table when we have an software interrupt. // Saves state and calls a C-compatible handler like - // `extern "C" fn svc_handler(svc: u32, context: *const u32);` + // `extern "C" fn svc_handler(svc: u32);` .global _asm_svc_handler .type _asm_svc_handler, %function _asm_svc_handler: @@ -335,6 +335,7 @@ core::arch::global_asm!( rfefd sp! .size _asm_svc_handler, . - _asm_svc_handler + // Called from the vector table when we have an interrupt. // Saves state and calls a C-compatible handler like // `extern "C" fn irq_handler();` @@ -357,26 +358,27 @@ core::arch::global_asm!( // Called from the vector table when we have an undefined exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _undefined_handler();` + // `extern "C" fn _undefined_handler(addr: usize);` .global _asm_default_undefined_handler .type _asm_default_undefined_handler, %function _asm_default_undefined_handler: - // First adjust LR for two purposes: Passing the faulting instruction to the C handler, - // and to return to the failing instruction after the C handler returns. - // Load processor status - mrs r4, cpsr - // Occurred in Thumb state? - tst r4, {t_bit} - // If not in Thumb mode, branch to not_thumb - beq not_thumb - subs lr, lr, #2 - b done -not_thumb: - // Subtract 4 from LR (ARM mode) - subs lr, lr, #4 -done: // state save from compiled code srsfd sp!, {und_mode} + // to work out what mode we're in, we need R0 + push {{r0}} + // First adjust LR for two purposes: Passing the faulting instruction to the C handler, + // and to return to the failing instruction after the C handler returns. + // Load processor status for the calling code + mrs r0, spsr + // Was the code that triggered the exception in Thumb state? + tst r0, {t_bit} + // Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual. + ite eq + subeq lr, lr, #4 + subne lr, lr, #2 + // save the newly computed LR + push {{lr}} + // now do our standard exception save "#, save_context!(), r#" @@ -384,29 +386,36 @@ done: mov r0, lr // call C handler bl _undefined_handler + // do our standard restore "#, restore_context!(), r#" + // get our saved LR + pop {{lr}} + // get our real saved R0 + pop {{r0}} + // overwrite the saved LR with the adjusted one + str lr, [sp] // Return to the failing instruction which is the recommended approach by ARM. rfefd sp! .size _asm_default_undefined_handler, . - _asm_default_undefined_handler - // Called from the vector table when we have an undefined exception. + // Called from the vector table when we have a prefetch exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _prefetch_handler();` + // `extern "C" fn _prefetch_handler(addr: usize);` .global _asm_default_prefetch_handler .type _asm_default_prefetch_handler, %function _asm_default_prefetch_handler: // Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual. - subs lr, lr, #4 + subs lr, lr, #4 // state save from compiled code srsfd sp!, {abt_mode} "#, save_context!(), r#" // Pass the faulting instruction address to the handler. - mov r0, lr + mov r0, lr // call C handler bl _prefetch_handler "#, @@ -419,12 +428,12 @@ done: // Called from the vector table when we have an undefined exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _abort_handler();` + // `extern "C" fn _abort_handler(addr: usize);` .global _asm_default_abort_handler .type _asm_default_abort_handler, %function _asm_default_abort_handler: // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. - subs lr, lr, #8 + subs lr, lr, #8 // state save from compiled code srsfd sp!, {abt_mode} "#, diff --git a/examples/versatileab/reference/abt-exception-armv7r-none-eabi.out b/examples/versatileab/reference/abt-exception-armv7r-none-eabi.out new file mode 100644 index 0000000..703128d --- /dev/null +++ b/examples/versatileab/reference/abt-exception-armv7r-none-eabi.out @@ -0,0 +1,9 @@ +Hello, this is an data abort exception example +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0000 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0000 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) diff --git a/examples/versatileab/reference/prefetch-exception-armv7a-none-eabi.out b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out similarity index 80% rename from examples/versatileab/reference/prefetch-exception-armv7a-none-eabi.out rename to examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out index 9744364..5aff8c4 100644 --- a/examples/versatileab/reference/prefetch-exception-armv7a-none-eabi.out +++ b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out @@ -1,9 +1,11 @@ -Hello, this is an prefetch exception example +Hello, this is a prefetch exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_a32 prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_a32 diff --git a/examples/versatileab/reference/prefetch-exception-armv7r-none-eabihf.out b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out similarity index 80% rename from examples/versatileab/reference/prefetch-exception-armv7r-none-eabihf.out rename to examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out index 9744364..dc73215 100644 --- a/examples/versatileab/reference/prefetch-exception-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out @@ -1,9 +1,11 @@ -Hello, this is an prefetch exception example +Hello, this is a prefetch exception example prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_t32 prefetch abort occurred IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } IFSR Status: Ok(DebugEvent) IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_t32 diff --git a/examples/versatileab/reference/undef-exception-a32-armv7a-none-eabi.out b/examples/versatileab/reference/undef-exception-a32-armv7a-none-eabi.out new file mode 100644 index 0000000..a176992 --- /dev/null +++ b/examples/versatileab/reference/undef-exception-a32-armv7a-none-eabi.out @@ -0,0 +1,5 @@ +Hello, this is a undef exception example +undefined abort occurred +caught udf_from_a32 +undefined abort occurred +caught udf_from_a32 diff --git a/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabi.out b/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabi.out new file mode 100644 index 0000000..a176992 --- /dev/null +++ b/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabi.out @@ -0,0 +1,5 @@ +Hello, this is a undef exception example +undefined abort occurred +caught udf_from_a32 +undefined abort occurred +caught udf_from_a32 diff --git a/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabihf.out b/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabihf.out new file mode 100644 index 0000000..a176992 --- /dev/null +++ b/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabihf.out @@ -0,0 +1,5 @@ +Hello, this is a undef exception example +undefined abort occurred +caught udf_from_a32 +undefined abort occurred +caught udf_from_a32 diff --git a/examples/versatileab/reference/undef-exception-armv7a-none-eabi.out b/examples/versatileab/reference/undef-exception-armv7a-none-eabi.out deleted file mode 100644 index b149c88..0000000 --- a/examples/versatileab/reference/undef-exception-armv7a-none-eabi.out +++ /dev/null @@ -1,3 +0,0 @@ -Hello, this is an undefined exception example -undefined exception occurred -undefined exception occurred diff --git a/examples/versatileab/reference/undef-exception-armv7r-none-eabihf.out b/examples/versatileab/reference/undef-exception-armv7r-none-eabihf.out deleted file mode 100644 index b149c88..0000000 --- a/examples/versatileab/reference/undef-exception-armv7r-none-eabihf.out +++ /dev/null @@ -1,3 +0,0 @@ -Hello, this is an undefined exception example -undefined exception occurred -undefined exception occurred diff --git a/examples/versatileab/reference/undef-exception-t32-armv7a-none-eabi.out b/examples/versatileab/reference/undef-exception-t32-armv7a-none-eabi.out new file mode 100644 index 0000000..f15c47e --- /dev/null +++ b/examples/versatileab/reference/undef-exception-t32-armv7a-none-eabi.out @@ -0,0 +1,5 @@ +Hello, this is a undef exception example +undefined abort occurred +caught udf_from_t32 +undefined abort occurred +caught udf_from_t32 diff --git a/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabi.out b/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabi.out new file mode 100644 index 0000000..f15c47e --- /dev/null +++ b/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabi.out @@ -0,0 +1,5 @@ +Hello, this is a undef exception example +undefined abort occurred +caught udf_from_t32 +undefined abort occurred +caught udf_from_t32 diff --git a/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabihf.out b/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabihf.out new file mode 100644 index 0000000..f15c47e --- /dev/null +++ b/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabihf.out @@ -0,0 +1,5 @@ +Hello, this is a undef exception example +undefined abort occurred +caught udf_from_t32 +undefined abort occurred +caught udf_from_t32 diff --git a/examples/versatileab/src/bin/abt-exception.rs b/examples/versatileab/src/bin/abt-exception.rs index d8427bc..5c1c089 100644 --- a/examples/versatileab/src/bin/abt-exception.rs +++ b/examples/versatileab/src/bin/abt-exception.rs @@ -56,17 +56,17 @@ fn disable_alignment_check() { } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_faulting_instruction: u32) { +unsafe extern "C" fn _undefined_handler(_addr: u32) { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_faulting_instruction: u32) { +unsafe extern "C" fn _prefetch_handler(_addr: u32) { panic!("unexpected prefetch exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_faulting_instruction: u32) { +unsafe extern "C" fn _abort_handler(_addr: u32) { println!("data abort occurred"); let dfsr = Dfsr::read(); println!("DFSR (Fault Status Register): {:?}", dfsr); diff --git a/examples/versatileab/src/bin/undef-exception-a32.rs b/examples/versatileab/src/bin/undef-exception-a32.rs new file mode 100644 index 0000000..b8833eb --- /dev/null +++ b/examples/versatileab/src/bin/undef-exception-a32.rs @@ -0,0 +1,80 @@ +//! Example triggering a undef exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use semihosting::println; + +// pull in our start-up code +use versatileab as _; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[no_mangle] +pub extern "C" fn kmain() -> ! { + println!("Hello, this is a undef exception example"); + + unsafe { + // trigger an Undefined exception, from A32 (Thumb) mode + udf_from_a32(); + } + + // this should be impossible because returning from the fault handler will + // immediately trigger the fault again. + + unreachable!("should never be here!"); +} + +// These functions are written in assembly +extern "C" { + fn udf_from_a32(); +} + +core::arch::global_asm!( + r#" + // fn udf_from_a32(); + .arm + .global udf_from_a32 + .type udf_from_a32, %function + udf_from_a32: + udf #0 + bx lr + .size udf_from_a32, . - udf_from_a32 +"# +); + +#[unsafe(no_mangle)] +unsafe extern "C" fn _prefetch_handler(_addr: usize) { + panic!("unexpected undefined exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _undefined_handler(addr: usize) { + println!("undefined abort occurred"); + + if addr == udf_from_a32 as usize { + // note that thumb functions have their LSB set, despite always being a + // multiple of two - that's how the CPU knows they are written in a32 + // machine code. + println!("caught udf_from_a32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, udf_from_a32 as usize + ); + } + + if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { + // we've faulted twice - time to quit + semihosting::process::exit(0); + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _abort_handler(_addr: usize) { + panic!("unexpected abort exception"); +} diff --git a/examples/versatileab/src/bin/undef-exception-t32.rs b/examples/versatileab/src/bin/undef-exception-t32.rs new file mode 100644 index 0000000..01ee741 --- /dev/null +++ b/examples/versatileab/src/bin/undef-exception-t32.rs @@ -0,0 +1,80 @@ +//! Example triggering a undef exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use semihosting::println; + +// pull in our start-up code +use versatileab as _; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[no_mangle] +pub extern "C" fn kmain() -> ! { + println!("Hello, this is a undef exception example"); + + unsafe { + // trigger an Undefined exception, from T32 (Thumb) mode + udf_from_t32(); + } + + // this should be impossible because returning from the fault handler will + // immediately trigger the fault again. + + unreachable!("should never be here!"); +} + +// These functions are written in assembly +extern "C" { + fn udf_from_t32(); +} + +core::arch::global_asm!( + r#" + // fn udf_from_t32(); + .thumb + .global udf_from_t32 + .type udf_from_t32, %function + udf_from_t32: + udf #0 + bx lr + .size udf_from_t32, . - udf_from_t32 +"# +); + +#[unsafe(no_mangle)] +unsafe extern "C" fn _prefetch_handler(_addr: usize) { + panic!("unexpected undefined exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _undefined_handler(addr: usize) { + println!("undefined abort occurred"); + + if (addr + 1) == udf_from_t32 as usize { + // note that thumb functions have their LSB set, despite always being a + // multiple of two - that's how the CPU knows they are written in T32 + // machine code. + println!("caught udf_from_t32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, udf_from_t32 as usize + ); + } + + if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { + // we've faulted twice - time to quit + semihosting::process::exit(0); + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _abort_handler(_addr: usize) { + panic!("unexpected abort exception"); +} diff --git a/examples/versatileab/src/bin/undef-exception.rs b/examples/versatileab/src/bin/undef-exception.rs deleted file mode 100644 index a87b73f..0000000 --- a/examples/versatileab/src/bin/undef-exception.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! Example triggering an undefined exception. - -#![no_std] -#![no_main] - -use core::sync::atomic::AtomicU32; - -// pull in our start-up code -use versatileab as _; - -use semihosting::println; - -/// The entry-point to the Rust application. -/// -/// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - main(); -} - -static COUNTER: AtomicU32 = AtomicU32::new(0); - -/// The main function of our Rust application. -#[export_name = "main"] -fn main() -> ! { - println!("Hello, this is an undefined exception example"); - unsafe { - core::arch::asm!("udf #0"); - } - unreachable!("should never be here!"); -} - -#[no_mangle] -unsafe extern "C" fn _undefined_handler(_faulting_instruction: u32) { - println!("undefined exception occurred"); - // For the first iteration, we do a regular exception return, which should - // trigger the exception again. - let counter_val = COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) + 1; - if counter_val == 2 { - semihosting::process::exit(0); - } -} diff --git a/tests.sh b/tests.sh index 7fe2653..19d4c13 100755 --- a/tests.sh +++ b/tests.sh @@ -34,19 +34,25 @@ my_diff() { } # armv7r-none-eabi tests -for binary in hello registers svc; do +for bin_path in $(ls examples/versatileab/src/bin/*.rs); do + filename=${bin_path##*/} + binary=${filename%.rs} cargo run ${versatile_ab_cargo} --target=armv7r-none-eabi --bin $binary | tee ./target/$binary-armv7r-none-eabi.out my_diff ./examples/versatileab/reference/$binary-armv7r-none-eabi.out ./target/$binary-armv7r-none-eabi.out || fail $binary "armv7r-none-eabi" done # armv7r-none-eabihf tests -for binary in hello registers svc undef-exception prefetch-exception abt-exception; do +for bin_path in $(ls examples/versatileab/src/bin/*.rs); do + filename=${bin_path##*/} + binary=${filename%.rs} cargo run ${versatile_ab_cargo} --target=armv7r-none-eabihf --bin $binary | tee ./target/$binary-armv7r-none-eabihf.out my_diff ./examples/versatileab/reference/$binary-armv7r-none-eabihf.out ./target/$binary-armv7r-none-eabihf.out || fail $binary "armv7r-none-eabihf" done # armv7a-none-eabi tests -for binary in hello registers svc undef-exception prefetch-exception abt-exception; do +for bin_path in $(ls examples/versatileab/src/bin/*.rs); do + filename=${bin_path##*/} + binary=${filename%.rs} cargo run ${versatile_ab_cargo} --target=armv7a-none-eabi --bin $binary | tee ./target/$binary-armv7a-none-eabi.out my_diff ./examples/versatileab/reference/$binary-armv7a-none-eabi.out ./target/$binary-armv7a-none-eabi.out || fail $binary "armv7a-none-eabi" done @@ -55,9 +61,11 @@ done # Ubuntu 24.04 supplies QEMU 8, which doesn't support the machine we have configured for this target if qemu-system-arm --version | grep "version 9"; then # armv8r-none-eabihf tests - for binary in hello registers svc gic generic_timer; do + for bin_path in $(ls examples/mps3-an536/src/bin/*.rs); do + filename=${bin_path##*/} + binary=${filename%.rs} cargo +nightly run ${mps3_an536_cargo} --target=armv8r-none-eabihf --bin $binary --features=gic -Zbuild-std=core | tee ./target/$binary-armv8r-none-eabihf.out - my_diff ./examples/mps3-an536/reference/$binary-armv8r-none-eabihf.out ./target/$binary-armv8r-none-eabihf.out || fail $binary "armv8r-none-eabihf" + my_diff ./examples/mps3-an536/reference/$binary-armv8r-none-eabihf.out ./target/$binary-armv8r-none-eabihf.out || fail $binary "armv8r-none-eabihf" done fi From 2c603d9c60b347b2d087d2a42ab776513f7dc94c Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Wed, 9 Apr 2025 17:03:27 +0100 Subject: [PATCH 2/6] Split the prefetch-exception handler into two. One for T32 and one for A32. We have to do this because the exception is non-recoverable (we return to the faulting instruction, which faults again). --- ...refetch-exception-a32-armv7a-none-eabi.out | 11 +++ ...fetch-exception-a32-armv7r-none-eabihf.out | 11 +++ ...refetch-exception-t32-armv7a-none-eabi.out | 11 +++ ...fetch-exception-t32-armv7r-none-eabihf.out | 11 +++ .../src/bin/prefetch-exception-a32.rs | 85 ++++++++++++++++++ .../src/bin/prefetch-exception-t32.rs | 88 +++++++++++++++++++ .../versatileab/src/bin/prefetch-exception.rs | 62 ------------- 7 files changed, 217 insertions(+), 62 deletions(-) create mode 100644 examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out create mode 100644 examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out create mode 100644 examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out create mode 100644 examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out create mode 100644 examples/versatileab/src/bin/prefetch-exception-a32.rs create mode 100644 examples/versatileab/src/bin/prefetch-exception-t32.rs delete mode 100644 examples/versatileab/src/bin/prefetch-exception.rs diff --git a/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out b/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out new file mode 100644 index 0000000..5aff8c4 --- /dev/null +++ b/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out @@ -0,0 +1,11 @@ +Hello, this is a prefetch exception example +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_a32 +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_a32 diff --git a/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out new file mode 100644 index 0000000..5aff8c4 --- /dev/null +++ b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out @@ -0,0 +1,11 @@ +Hello, this is a prefetch exception example +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_a32 +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_a32 diff --git a/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out b/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out new file mode 100644 index 0000000..dc73215 --- /dev/null +++ b/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out @@ -0,0 +1,11 @@ +Hello, this is a prefetch exception example +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_t32 +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_t32 diff --git a/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out new file mode 100644 index 0000000..dc73215 --- /dev/null +++ b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out @@ -0,0 +1,11 @@ +Hello, this is a prefetch exception example +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_t32 +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0000 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_t32 diff --git a/examples/versatileab/src/bin/prefetch-exception-a32.rs b/examples/versatileab/src/bin/prefetch-exception-a32.rs new file mode 100644 index 0000000..b26ebea --- /dev/null +++ b/examples/versatileab/src/bin/prefetch-exception-a32.rs @@ -0,0 +1,85 @@ +//! Example triggering a prefetch exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use cortex_ar::register::{Ifar, Ifsr}; +use semihosting::println; + +// pull in our start-up code +use versatileab as _; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[no_mangle] +pub extern "C" fn kmain() -> ! { + println!("Hello, this is a prefetch exception example"); + + // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. + // See p. 2038 of ARMv7-M Architecture Reference Manual + unsafe { + // trigger an prefetch exception, from A32 (Arm) mode + bkpt_from_a32(); + } + + // this should be impossible because returning from the fault handler will + // immediately trigger the fault again. + + unreachable!("should never be here!"); +} + +// These functions are written in assembly +extern "C" { + fn bkpt_from_a32(); +} + +core::arch::global_asm!( + r#" + // fn bkpt_from_a32(); + .arm + .global bkpt_from_a32 + .type bkpt_from_a32, %function + bkpt_from_a32: + bkpt #0 + bx lr + .size bkpt_from_a32, . - bkpt_from_a32 +"# +); + +#[unsafe(no_mangle)] +unsafe extern "C" fn _undefined_handler(_addr: usize) { + panic!("unexpected undefined exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _prefetch_handler(addr: usize) { + println!("prefetch abort occurred"); + let ifsr = Ifsr::read(); + println!("IFSR (Fault Status Register): {:?}", ifsr); + println!("IFSR Status: {:?}", ifsr.status()); + let ifar = Ifar::read(); + println!("IFAR (Faulting Address Register): {:?}", ifar); + + if addr == bkpt_from_a32 as usize { + println!("caught bkpt_from_a32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, bkpt_from_a32 as usize + ); + } + + if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { + // we've faulted twice - time to quit + semihosting::process::exit(0); + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _abort_handler(_addr: usize) { + panic!("unexpected abort exception"); +} diff --git a/examples/versatileab/src/bin/prefetch-exception-t32.rs b/examples/versatileab/src/bin/prefetch-exception-t32.rs new file mode 100644 index 0000000..d8f640e --- /dev/null +++ b/examples/versatileab/src/bin/prefetch-exception-t32.rs @@ -0,0 +1,88 @@ +//! Example triggering a prefetch exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use cortex_ar::register::{Ifar, Ifsr}; +use semihosting::println; + +// pull in our start-up code +use versatileab as _; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[no_mangle] +pub extern "C" fn kmain() -> ! { + println!("Hello, this is a prefetch exception example"); + + // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. + // See p. 2038 of ARMv7-M Architecture Reference Manual + unsafe { + // trigger an prefetch exception, from T32 (Thumb) mode + bkpt_from_t32(); + } + + // this should be impossible because returning from the fault handler will + // immediately trigger the fault again. + + unreachable!("should never be here!"); +} + +// These functions are written in assembly +extern "C" { + fn bkpt_from_t32(); +} + +core::arch::global_asm!( + r#" + // fn bkpt_from_t32(); + .thumb + .global bkpt_from_t32 + .type bkpt_from_t32, %function + bkpt_from_t32: + bkpt #0 + bx lr + .size bkpt_from_t32, . - bkpt_from_t32 +"# +); + +#[unsafe(no_mangle)] +unsafe extern "C" fn _undefined_handler(_addr: usize) { + panic!("unexpected undefined exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _prefetch_handler(addr: usize) { + println!("prefetch abort occurred"); + let ifsr = Ifsr::read(); + println!("IFSR (Fault Status Register): {:?}", ifsr); + println!("IFSR Status: {:?}", ifsr.status()); + let ifar = Ifar::read(); + println!("IFAR (Faulting Address Register): {:?}", ifar); + + if (addr + 1) == bkpt_from_t32 as usize { + // note that thumb functions have their LSB set, despite always being a + // multiple of two - that's how the CPU knows they are written in T32 + // machine code. + println!("caught bkpt_from_t32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, bkpt_from_t32 as usize + ); + } + + if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { + // we've faulted twice - time to quit + semihosting::process::exit(0); + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _abort_handler(_addr: usize) { + panic!("unexpected abort exception"); +} diff --git a/examples/versatileab/src/bin/prefetch-exception.rs b/examples/versatileab/src/bin/prefetch-exception.rs deleted file mode 100644 index 091a75f..0000000 --- a/examples/versatileab/src/bin/prefetch-exception.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! Example triggering an prefetch exception. - -#![no_std] -#![no_main] - -use core::sync::atomic::AtomicU32; - -use cortex_ar::register::{Ifar, Ifsr}; -// pull in our start-up code -use versatileab as _; - -use semihosting::println; - -/// The entry-point to the Rust application. -/// -/// It is called by the start-up. -#[no_mangle] -pub extern "C" fn kmain() -> ! { - main(); -} - -static COUNTER: AtomicU32 = AtomicU32::new(0); - -/// The main function of our Rust application. -#[export_name = "main"] -fn main() -> ! { - println!("Hello, this is an prefetch exception example"); - - // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. - // See p. 2038 of ARMv7-M Architecture Reference Manual - unsafe { - core::arch::asm!("bkpt"); - } - - unreachable!("should never be here!"); -} - -#[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_faulting_instruction: u32) { - panic!("unexpected undefined exception"); -} - -#[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_faulting_instruction: u32) { - println!("prefetch abort occurred"); - let ifsr = Ifsr::read(); - println!("IFSR (Fault Status Register): {:?}", ifsr); - println!("IFSR Status: {:?}", ifsr.status()); - let ifar = Ifar::read(); - println!("IFAR (Faulting Address Register): {:?}", ifar); - // For the first iteration, we do a regular exception return, which should - // trigger the exception again. - let counter_val = COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) + 1; - if counter_val == 2 { - semihosting::process::exit(0); - } -} - -#[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_faulting_instruction: u32) { - panic!("unexpected abort exception"); -} From 59e5b64bbb9984ca952092afeb4303f4f63f1979 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Wed, 9 Apr 2025 17:05:18 +0100 Subject: [PATCH 3/6] Extra clarification in abt-exception. --- examples/versatileab/src/bin/abt-exception.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/versatileab/src/bin/abt-exception.rs b/examples/versatileab/src/bin/abt-exception.rs index 5c1c089..d22566b 100644 --- a/examples/versatileab/src/bin/abt-exception.rs +++ b/examples/versatileab/src/bin/abt-exception.rs @@ -78,9 +78,8 @@ unsafe extern "C" fn _abort_handler(_addr: u32) { println!("DFAR (Faulting Address Register): {:?}", dfar); enable_alignment_check(); // For the first iteration, we do a regular exception return, which should - // trigger the exception again. - let counter_val = COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) + 1; - if counter_val == 2 { + // trigger the exception again. The second time around we quit. + if COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) == 1 { semihosting::process::exit(0); } } From 16ac5160a2c0adf6ed7646e4fbe114bf96cbaf5f Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Wed, 9 Apr 2025 18:25:56 +0100 Subject: [PATCH 4/6] Add extra exception tests for Cortex-R52 I had to move the DFSR read to after the alignment check was disabled, otherwise it double-faulted. Also it turns out the versatileab tests were running with the Cortex-A linker script not the Cortex-R linker script, and the Cortex-R linker script had a bug in it that we previously missed (it wasn't calling the new exception handlers). --- cortex-r-rt/link.x | 8 +- .../abt-exception-armv8r-none-eabihf.out | 9 ++ ...fetch-exception-a32-armv8r-none-eabihf.out | 11 +++ ...fetch-exception-t32-armv8r-none-eabihf.out | 11 +++ ...undef-exception-a32-armv8r-none-eabihf.out | 5 ++ ...undef-exception-t32-armv8r-none-eabihf.out | 5 ++ examples/mps3-an536/src/bin/abt-exception.rs | 85 ++++++++++++++++++ .../src/bin/prefetch-exception-a32.rs | 85 ++++++++++++++++++ .../src/bin/prefetch-exception-t32.rs | 88 +++++++++++++++++++ .../mps3-an536/src/bin/undef-exception-a32.rs | 80 +++++++++++++++++ .../mps3-an536/src/bin/undef-exception-t32.rs | 80 +++++++++++++++++ 11 files changed, 464 insertions(+), 3 deletions(-) create mode 100644 examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536/reference/undef-exception-a32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536/reference/undef-exception-t32-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536/src/bin/abt-exception.rs create mode 100644 examples/mps3-an536/src/bin/prefetch-exception-a32.rs create mode 100644 examples/mps3-an536/src/bin/prefetch-exception-t32.rs create mode 100644 examples/mps3-an536/src/bin/undef-exception-a32.rs create mode 100644 examples/mps3-an536/src/bin/undef-exception-t32.rs diff --git a/cortex-r-rt/link.x b/cortex-r-rt/link.x index 6e0b553..a0296a2 100644 --- a/cortex-r-rt/link.x +++ b/cortex-r-rt/link.x @@ -92,11 +92,13 @@ ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of ABT stack is not 8 ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of UND stack is not 8-byte aligned"); ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of SVC stack is not 8-byte aligned"); -PROVIDE(_asm_undefined_handler =_asm_default_handler); -PROVIDE(_asm_prefetch_handler =_asm_default_handler); -PROVIDE(_asm_abort_handler =_asm_default_handler); +/* Weak aliases for ASM default handlers */ +PROVIDE(_asm_undefined_handler =_asm_default_undefined_handler); +PROVIDE(_asm_prefetch_handler =_asm_default_prefetch_handler); +PROVIDE(_asm_abort_handler =_asm_default_abort_handler); PROVIDE(_asm_fiq_handler =_asm_default_fiq_handler); +/* Weak aliases for C default handlers */ PROVIDE(_undefined_handler =_default_handler); PROVIDE(_abort_handler =_default_handler); PROVIDE(_prefetch_handler =_default_handler); diff --git a/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out b/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out new file mode 100644 index 0000000..b12baf8 --- /dev/null +++ b/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out @@ -0,0 +1,9 @@ +Hello, this is an data abort exception example +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0010 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) +data abort occurred +DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0010 Status=0b00001 } +DFSR Status: Ok(AlignmentFault) +DFAR (Faulting Address Register): Dfar(4097) diff --git a/examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out b/examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out new file mode 100644 index 0000000..fab7e13 --- /dev/null +++ b/examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out @@ -0,0 +1,11 @@ +Hello, this is a prefetch exception example +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_a32 +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_a32 diff --git a/examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out b/examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out new file mode 100644 index 0000000..f00a35c --- /dev/null +++ b/examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out @@ -0,0 +1,11 @@ +Hello, this is a prefetch exception example +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_t32 +prefetch abort occurred +IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 } +IFSR Status: Ok(DebugEvent) +IFAR (Faulting Address Register): Ifar(0) +caught bkpt_from_t32 diff --git a/examples/mps3-an536/reference/undef-exception-a32-armv8r-none-eabihf.out b/examples/mps3-an536/reference/undef-exception-a32-armv8r-none-eabihf.out new file mode 100644 index 0000000..a176992 --- /dev/null +++ b/examples/mps3-an536/reference/undef-exception-a32-armv8r-none-eabihf.out @@ -0,0 +1,5 @@ +Hello, this is a undef exception example +undefined abort occurred +caught udf_from_a32 +undefined abort occurred +caught udf_from_a32 diff --git a/examples/mps3-an536/reference/undef-exception-t32-armv8r-none-eabihf.out b/examples/mps3-an536/reference/undef-exception-t32-armv8r-none-eabihf.out new file mode 100644 index 0000000..f15c47e --- /dev/null +++ b/examples/mps3-an536/reference/undef-exception-t32-armv8r-none-eabihf.out @@ -0,0 +1,5 @@ +Hello, this is a undef exception example +undefined abort occurred +caught udf_from_t32 +undefined abort occurred +caught udf_from_t32 diff --git a/examples/mps3-an536/src/bin/abt-exception.rs b/examples/mps3-an536/src/bin/abt-exception.rs new file mode 100644 index 0000000..4a0955c --- /dev/null +++ b/examples/mps3-an536/src/bin/abt-exception.rs @@ -0,0 +1,85 @@ +//! Example triggering an data abort exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::AtomicU32; + +use cortex_ar::register::{Dfar, Dfsr, Sctlr}; +// pull in our start-up code +use mps3_an536 as _; + +use semihosting::println; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[no_mangle] +pub extern "C" fn kmain() -> ! { + main(); +} + +/// The main function of our Rust application. +#[export_name = "main"] +#[allow(unreachable_code)] +fn main() -> ! { + // Enable alignment check for Armv7-R. Was not required + // on Cortex-A for some reason, even though the bit was not set. + enable_alignment_check(); + + println!("Hello, this is an data abort exception example"); + // Unaligned read + unsafe { + let addr: *const u32 = 0x1001 as *const u32; // Unaligned address (not 4-byte aligned) + core::arch::asm!( + "ldr r0, [{addr}]", // Attempt unaligned load (should trigger Data Abort) + addr = in(reg) addr, // Pass unaligned pointer + options(nostack, preserves_flags) // No stack usage, preserves flags + ); + } + + unreachable!("should never be here!"); +} + +fn enable_alignment_check() { + let mut sctrl = Sctlr::read(); + sctrl.set_a(true); + Sctlr::write(sctrl); +} + +fn disable_alignment_check() { + let mut sctrl = Sctlr::read(); + sctrl.set_a(false); + Sctlr::write(sctrl); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _undefined_handler(_addr: u32) { + panic!("unexpected undefined exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _prefetch_handler(_addr: u32) { + panic!("unexpected prefetch exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _abort_handler(_addr: u32) { + println!("data abort occurred"); + // If this is not disabled, reading DFAR will trigger an alignment fault on Armv8-R, leading + // to a loop. + disable_alignment_check(); + let dfsr = Dfsr::read(); + println!("DFSR (Fault Status Register): {:?}", dfsr); + println!("DFSR Status: {:?}", dfsr.status()); + let dfar = Dfar::read(); + println!("DFAR (Faulting Address Register): {:?}", dfar); + enable_alignment_check(); + // For the first iteration, we do a regular exception return, which should + // trigger the exception again. The second time around we quit. + if COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) == 1 { + semihosting::process::exit(0); + } +} diff --git a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs new file mode 100644 index 0000000..ced3ecd --- /dev/null +++ b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs @@ -0,0 +1,85 @@ +//! Example triggering a prefetch exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use cortex_ar::register::{Ifar, Ifsr}; +use semihosting::println; + +// pull in our start-up code +use mps3_an536 as _; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[no_mangle] +pub extern "C" fn kmain() -> ! { + println!("Hello, this is a prefetch exception example"); + + // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. + // See p. 2038 of ARMv7-M Architecture Reference Manual + unsafe { + // trigger an prefetch exception, from A32 (Arm) mode + bkpt_from_a32(); + } + + // this should be impossible because returning from the fault handler will + // immediately trigger the fault again. + + unreachable!("should never be here!"); +} + +// These functions are written in assembly +extern "C" { + fn bkpt_from_a32(); +} + +core::arch::global_asm!( + r#" + // fn bkpt_from_a32(); + .arm + .global bkpt_from_a32 + .type bkpt_from_a32, %function + bkpt_from_a32: + bkpt #0 + bx lr + .size bkpt_from_a32, . - bkpt_from_a32 +"# +); + +#[unsafe(no_mangle)] +unsafe extern "C" fn _undefined_handler(_addr: usize) { + panic!("unexpected undefined exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _prefetch_handler(addr: usize) { + println!("prefetch abort occurred"); + let ifsr = Ifsr::read(); + println!("IFSR (Fault Status Register): {:?}", ifsr); + println!("IFSR Status: {:?}", ifsr.status()); + let ifar = Ifar::read(); + println!("IFAR (Faulting Address Register): {:?}", ifar); + + if addr == bkpt_from_a32 as usize { + println!("caught bkpt_from_a32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, bkpt_from_a32 as usize + ); + } + + if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { + // we've faulted twice - time to quit + semihosting::process::exit(0); + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _abort_handler(_addr: usize) { + panic!("unexpected abort exception"); +} diff --git a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs new file mode 100644 index 0000000..cc03404 --- /dev/null +++ b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs @@ -0,0 +1,88 @@ +//! Example triggering a prefetch exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use cortex_ar::register::{Ifar, Ifsr}; +use semihosting::println; + +// pull in our start-up code +use mps3_an536 as _; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[no_mangle] +pub extern "C" fn kmain() -> ! { + println!("Hello, this is a prefetch exception example"); + + // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. + // See p. 2038 of ARMv7-M Architecture Reference Manual + unsafe { + // trigger an prefetch exception, from T32 (Thumb) mode + bkpt_from_t32(); + } + + // this should be impossible because returning from the fault handler will + // immediately trigger the fault again. + + unreachable!("should never be here!"); +} + +// These functions are written in assembly +extern "C" { + fn bkpt_from_t32(); +} + +core::arch::global_asm!( + r#" + // fn bkpt_from_t32(); + .thumb + .global bkpt_from_t32 + .type bkpt_from_t32, %function + bkpt_from_t32: + bkpt #0 + bx lr + .size bkpt_from_t32, . - bkpt_from_t32 +"# +); + +#[unsafe(no_mangle)] +unsafe extern "C" fn _undefined_handler(_addr: usize) { + panic!("unexpected undefined exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _prefetch_handler(addr: usize) { + println!("prefetch abort occurred"); + let ifsr = Ifsr::read(); + println!("IFSR (Fault Status Register): {:?}", ifsr); + println!("IFSR Status: {:?}", ifsr.status()); + let ifar = Ifar::read(); + println!("IFAR (Faulting Address Register): {:?}", ifar); + + if (addr + 1) == bkpt_from_t32 as usize { + // note that thumb functions have their LSB set, despite always being a + // multiple of two - that's how the CPU knows they are written in T32 + // machine code. + println!("caught bkpt_from_t32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, bkpt_from_t32 as usize + ); + } + + if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { + // we've faulted twice - time to quit + semihosting::process::exit(0); + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _abort_handler(_addr: usize) { + panic!("unexpected abort exception"); +} diff --git a/examples/mps3-an536/src/bin/undef-exception-a32.rs b/examples/mps3-an536/src/bin/undef-exception-a32.rs new file mode 100644 index 0000000..ed9b2e7 --- /dev/null +++ b/examples/mps3-an536/src/bin/undef-exception-a32.rs @@ -0,0 +1,80 @@ +//! Example triggering a undef exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use semihosting::println; + +// pull in our start-up code +use mps3_an536 as _; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[no_mangle] +pub extern "C" fn kmain() -> ! { + println!("Hello, this is a undef exception example"); + + unsafe { + // trigger an Undefined exception, from A32 (Thumb) mode + udf_from_a32(); + } + + // this should be impossible because returning from the fault handler will + // immediately trigger the fault again. + + unreachable!("should never be here!"); +} + +// These functions are written in assembly +extern "C" { + fn udf_from_a32(); +} + +core::arch::global_asm!( + r#" + // fn udf_from_a32(); + .arm + .global udf_from_a32 + .type udf_from_a32, %function + udf_from_a32: + udf #0 + bx lr + .size udf_from_a32, . - udf_from_a32 +"# +); + +#[unsafe(no_mangle)] +unsafe extern "C" fn _prefetch_handler(_addr: usize) { + panic!("unexpected undefined exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _undefined_handler(addr: usize) { + println!("undefined abort occurred"); + + if addr == udf_from_a32 as usize { + // note that thumb functions have their LSB set, despite always being a + // multiple of two - that's how the CPU knows they are written in a32 + // machine code. + println!("caught udf_from_a32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, udf_from_a32 as usize + ); + } + + if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { + // we've faulted twice - time to quit + semihosting::process::exit(0); + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _abort_handler(_addr: usize) { + panic!("unexpected abort exception"); +} diff --git a/examples/mps3-an536/src/bin/undef-exception-t32.rs b/examples/mps3-an536/src/bin/undef-exception-t32.rs new file mode 100644 index 0000000..75bd43d --- /dev/null +++ b/examples/mps3-an536/src/bin/undef-exception-t32.rs @@ -0,0 +1,80 @@ +//! Example triggering a undef exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use semihosting::println; + +// pull in our start-up code +use mps3_an536 as _; + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up. +#[no_mangle] +pub extern "C" fn kmain() -> ! { + println!("Hello, this is a undef exception example"); + + unsafe { + // trigger an Undefined exception, from T32 (Thumb) mode + udf_from_t32(); + } + + // this should be impossible because returning from the fault handler will + // immediately trigger the fault again. + + unreachable!("should never be here!"); +} + +// These functions are written in assembly +extern "C" { + fn udf_from_t32(); +} + +core::arch::global_asm!( + r#" + // fn udf_from_t32(); + .thumb + .global udf_from_t32 + .type udf_from_t32, %function + udf_from_t32: + udf #0 + bx lr + .size udf_from_t32, . - udf_from_t32 +"# +); + +#[unsafe(no_mangle)] +unsafe extern "C" fn _prefetch_handler(_addr: usize) { + panic!("unexpected undefined exception"); +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _undefined_handler(addr: usize) { + println!("undefined abort occurred"); + + if (addr + 1) == udf_from_t32 as usize { + // note that thumb functions have their LSB set, despite always being a + // multiple of two - that's how the CPU knows they are written in T32 + // machine code. + println!("caught udf_from_t32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, udf_from_t32 as usize + ); + } + + if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { + // we've faulted twice - time to quit + semihosting::process::exit(0); + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn _abort_handler(_addr: usize) { + panic!("unexpected abort exception"); +} From 260ed0951fdf5527dd76b672e263d4619ac72b19 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Wed, 9 Apr 2025 22:08:36 +0100 Subject: [PATCH 5/6] More exception handling updates. - The SVC asm trampoline can now be over-ridden - The Undefined, Prefetch and Abort handlers can either return never, or can return a new address to continue executing from when the handler is over - I tried to ensure the all the handlers are listed in the same order as the vector table, for consistency - Put every function in its own section in the hope that unused ones will be GC'd by the linker --- cortex-a-rt/link.x | 36 +- cortex-a-rt/src/lib.rs | 365 ++++++++++------- cortex-r-rt/link.x | 30 +- cortex-r-rt/src/lib.rs | 367 +++++++++++------- examples/mps3-an536/src/bin/abt-exception.rs | 8 +- .../src/bin/prefetch-exception-a32.rs | 8 +- .../src/bin/prefetch-exception-t32.rs | 8 +- .../mps3-an536/src/bin/undef-exception-a32.rs | 9 +- .../mps3-an536/src/bin/undef-exception-t32.rs | 8 +- examples/versatileab/src/bin/abt-exception.rs | 8 +- .../src/bin/prefetch-exception-a32.rs | 8 +- .../src/bin/prefetch-exception-t32.rs | 8 +- .../src/bin/undef-exception-a32.rs | 8 +- .../src/bin/undef-exception-t32.rs | 8 +- 14 files changed, 523 insertions(+), 356 deletions(-) diff --git a/cortex-a-rt/link.x b/cortex-a-rt/link.x index 0ba73fc..8ee325f 100644 --- a/cortex-a-rt/link.x +++ b/cortex-a-rt/link.x @@ -17,8 +17,6 @@ SECTIONS { .text : { /* The vector table must come first */ *(.vector_table) - /* Our exception handling routines */ - *(.text.handlers) /* Now the rest of the code */ *(.text .text*) } > CODE @@ -72,36 +70,38 @@ SECTIONS { } /* -We reserve some space at the top of the RAM for our stacks. We have an IRQ stack -and a FIQ stack, plus the remainder is our system stack. +We reserve some space at the top of the RAM for our exception stacks. The +remainder is our system mode stack. You must keep _stack_top and the stack sizes aligned to eight byte boundaries. */ PROVIDE(_stack_top = ORIGIN(DATA) + LENGTH(DATA)); -PROVIDE(_fiq_stack_size = 0x400); -PROVIDE(_irq_stack_size = 0x1000); -PROVIDE(_abt_stack_size = 0x400); PROVIDE(_und_stack_size = 0x400); -PROVIDE(_svc_stack_size = 0x1000); +PROVIDE(_svc_stack_size = 0x400); +PROVIDE(_abt_stack_size = 0x400); +PROVIDE(_irq_stack_size = 0x400); +PROVIDE(_fiq_stack_size = 0x400); -ASSERT(_stack_top % 8 == 0, "ERROR(cortex-a-rt): top of stack is not 8-byte aligned"); -ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of FIQ stack is not 8-byte aligned"); -ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of IRQ stack is not 8-byte aligned"); -ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of FIQ stack is not 8-byte aligned"); -ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of ABT stack is not 8-byte aligned"); -ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of UND stack is not 8-byte aligned"); -ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of SVC stack is not 8-byte aligned"); +ASSERT(_stack_top % 8 == 0, "ERROR(cortex-r-rt): top of stack is not 8-byte aligned"); +ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of UND stack is not 8-byte aligned"); +ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of SVC stack is not 8-byte aligned"); +ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of ABT stack is not 8-byte aligned"); +ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of IRQ stack is not 8-byte aligned"); +ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of FIQ stack is not 8-byte aligned"); /* Weak aliases for ASM default handlers */ +PROVIDE(_start =_default_start); PROVIDE(_asm_undefined_handler =_asm_default_undefined_handler); +PROVIDE(_asm_svc_handler =_asm_default_svc_handler); PROVIDE(_asm_prefetch_handler =_asm_default_prefetch_handler); PROVIDE(_asm_abort_handler =_asm_default_abort_handler); +PROVIDE(_asm_irq_handler =_asm_default_irq_handler); PROVIDE(_asm_fiq_handler =_asm_default_fiq_handler); /* Weak aliases for C default handlers */ PROVIDE(_undefined_handler =_default_handler); -PROVIDE(_abort_handler =_default_handler); +PROVIDE(_svc_handler =_default_handler); PROVIDE(_prefetch_handler =_default_handler); +PROVIDE(_abort_handler =_default_handler); PROVIDE(_irq_handler =_default_handler); -PROVIDE(_svc_handler =_default_handler); -PROVIDE(_start =_default_start); +/* There is no default C-language FIQ handler */ diff --git a/cortex-a-rt/src/lib.rs b/cortex-a-rt/src/lib.rs index ce8bae8..0d485a3 100644 --- a/cortex-a-rt/src/lib.rs +++ b/cortex-a-rt/src/lib.rs @@ -1,22 +1,25 @@ -//! # Run-time support for Arm Cortex-A +//! # Run-time support for Arm Cortex-R //! //! This library implements a simple Arm vector table, suitable for getting into -//! a Rust application running in System Mode. It also provides a reference start up method. -//! Usually, most Cortex-A based systems will require chip specific start-up code, so the -//! start-up method can over overriden. -//! -//! The default startup routine provided by this crate does not include any special handling -//! for multi-core support because this is oftentimes implementation defined and the exact -//! handling depends on the specific chip in use. Many implementations only -//! run the startup routine with one core and will keep other cores in reset until they are woken -//! up by an implementation specific mechanism. For other implementations where multi-core -//! specific startup adaptions are necessary, the startup routine can be overwritten by the user. +//! a Rust application running in System Mode. It also provides a reference +//! start up method. Most Cortex-A based systems will require chip specific +//! start-up code, so the start-up method can over overriden. +//! +//! The default startup routine provided by this crate does not include any +//! special handling for multi-core support because this is oftentimes +//! implementation defined and the exact handling depends on the specific chip +//! in use. Many implementations only run the startup routine with one core and +//! will keep other cores in reset until they are woken up by an implementation +//! specific mechanism. For other implementations where multi-core specific +//! startup adaptions are necessary, the startup routine can be overwritten by +//! the user. //! //! ## Features //! -//! - `vfp-dp`: Enables support for the double-precision VFP floating point support. If your target -//! CPU has this feature or support for NEON which also implies double-precision support, this -//! feature should be activated. +//! - `vfp-dp`: Enables support for the double-precision VFP floating point +//! support. If your target CPU has this feature or support for NEON which +//! also implies double-precision support, this feature should be activated. +//! - `eabi-fpu`: Enables the FPU, even if you selected a soft-float ABI target. //! //! ## Information about the Run-Time //! @@ -49,7 +52,11 @@ //! * `__sidata` - the start of the initialisation values for data, in read-only //! memory. Must be 4-byte aligned. //! -//! ### Functions +//! Using our default start-up function `_default_start`, the memory between +//! `__sbss` and `__ebss` is zeroed, and the memory between `__sdata` and +//! `__edata` is initialised with the data found at `__sidata`. +//! +//! ### C-Compatible Functions //! //! * `kmain` - the `extern "C"` entry point to your application. //! @@ -62,7 +69,9 @@ //! //! * `_svc_handler` - an `extern "C"` function to call when an SVC Exception //! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. +//! `_default_handler` but you can override it. Returning from this function +//! will cause execution to resume from the function the triggered the +//! exception, immediately after the SVC instruction. //! //! Expected prototype: //! @@ -73,108 +82,155 @@ //! //! * `_irq_handler` - an `extern "C"` function to call when an Interrupt //! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. +//! `_default_handler` but you can override it. Returning from this function +//! will cause execution to resume from the function the triggered the +//! exception. //! //! Expected prototype: //! //! ```rust +//! /// Upon return, the interrupt handler will end and execution +//! /// will continue at the interrupted instruction. //! #[unsafe(no_mangle)] //! extern "C" fn _irq_handler(); //! ``` //! -//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_undefined_handler` unless that function is overriden as well. +//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined +//! Exception occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_undefined_handler` is missing. //! -//! Expected prototype: +//! The expected prototype for `_undefined_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _undefined_handler(addr: usize); +//! extern "C" fn _undefined_handler(addr: usize) -> !; //! ``` +//! +//! or: //! -//! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_abort_handler` unless that function is overriden as well. +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _undefined_handler(addr: usize) -> usize; +//! ``` //! -//! Expected prototype: +//! * `_abort_handler` - an `extern "C"` function to call when an Data Abort +//! occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_abort_handler` is missing. +//! +//! The expected prototype for `_abort_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _abort_handler(addr: usize); +//! extern "C" fn _abort_handler(addr: usize) -> !; //! ``` +//! +//! or: //! -//! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_prefetch_handler` unless that function is overriden as well. +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _abort_handler(addr: usize) -> usize; +//! ``` //! -//! Expected prototype: +//! * `_prefetch_handler` - an `extern "C"` function to call when an Prefetch +//! Abort occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_prefetch_handler` is missing. +//! +//! The expected prototype for `_prefetch_handler` is either: +//! +//! ```rust +//! /// Does not return +//! #[unsafe(no_mangle)] +//! extern "C" fn _prefetch_handler(addr: usize) -> !; +//! ``` +//! +//! or: //! //! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. //! #[unsafe(no_mangle)] -//! extern "C" fn _prefetch_handler(addr: usize); +//! extern "C" fn _prefetch_handler(addr: usize) -> usize; //! ``` //! //! ### ASM functions //! -//! * `__start` - a Reset handler. Our linker script PROVIDEs a default function -//! at `_default_start` but you can override it. Most Cortex-A SoCs require -//! a chip specific startup for tasks like MMU initialization or chip specific -//! initialization routines. -//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt -//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_fiq_handler` but you can override it. +//! * `_start` - a Reset handler. Our linker script PROVIDEs a default function +//! at `_default_start` but you can override it. Some SoCs require a chip +//! specific startup for tasks like MMU initialization or chip specific +//! initialization routines, so if our start-up routine doesn't work for you, +//! supply your own `_start` function (but feel free to call our +//! `_default_start` as part of it). //! * `_asm_undefined_handler` - a naked function to call when an Undefined //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_undefined_handler` but you can override it. +//! `_asm_default_undefined_handler` but you can override it. The provided +//! default handler will call `_undefined_handler`, saving state as required. +//! * `_asm_svc_handler` - a naked function to call when an SVC Exception +//! occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_svc_handler` but you can override it. The provided default +//! handler will call `_svc_handler`, saving state as required. //! * `_asm_prefetch_handler` - a naked function to call when a Prefetch //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_prefetch_handler` but you can override it. The provided default -//! handler will perform an exception return to the faulting address. +//! `_asm_default_prefetch_handler` but you can override it. The provided +//! default handler will call `_prefetch_handler`, saving state as required. +//! Note that Prefetch Exceptions are handled in Abort Mode, Monitor Mode or +//! Hyp Mode, depending on CPU configuration. There is no Prefetch Abort mode, +//! so there is no Prefetch Abort Mode stack. //! * `_asm_abort_handler` - a naked function to call when an Abort Exception //! occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_abort_handler` but you can override it. The provided default handler -//! will perform an exception return to the faulting address. +//! `_asm_default_abort_handler` but you can override it. The provided default +//! handler will call `_abort_handler`, saving state as required. +//! * `_asm_irq_handler` - a naked function to call when an Undefined Exception +//! occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_irq_handler` but you can override it. The provided default +//! handler will call `_irq_handler`, saving state as required. +//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt +//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_fiq_handler` but you can override it. The provided default +//! just spins forever. //! -//! On start-up, the memory between `__sbss` and `__ebss` is zeroed, and the -//! memory between `__sdata` and `__edata` is initialised with the data found at -//! `__sidata`. +//! ## Outputs //! //! This library produces global symbols called: //! //! * `_vector_table` - the start of the interrupt vector table //! * `_default_start` - the default Reset handler, that sets up some stacks and //! calls an `extern "C"` function called `kmain`. +//! * `_asm_default_undefined_handler` - assembly language trampoline that calls +//! `_undefined_handler` +//! * `_asm_default_svc_handler` - assembly language trampoline that calls +//! `_svc_handler` +//! * `_asm_default_prefetch_handler` - assembly language trampoline that calls +//! `_prefetch_handler` +//! * `_asm_default_abort_handler` - assembly language trampoline that calls +//! `_abort_handler` +//! * `_asm_default_irq_handler` - assembly language trampoline that calls +//! `_irq_handler` //! * `_asm_default_fiq_handler` - an FIQ handler that just spins -//! * `_asm_default_handler` - an exception handler that just spins -//! * `_asm_svc_handler` - assembly language trampoline for SVC Exceptions that -//! calls `_svc_handler` -//! * `_asm_irq_handler` - assembly language trampoline for Interrupts that -//! calls `_irq_handler` -//! -//! The assembly language trampolines are required because Armv7-R (and Armv8-R) -//! processors do not save a great deal of state on entry to an exception -//! handler, unlike Armv7-M (and other M-Profile) processors. We must therefore -//! save this state to the stack using assembly language, before transferring to -//! an `extern "C"` function. We do not change modes before entering that -//! `extern "C"` function - that's for the handler to deal with as it wishes. We -//! supply a default handler that prints an error message to Semihosting so you -//! know if you hit an unexpected exception. Because FIQ is often +//! * `_default_handler` - a C compatible function that spins forever. +//! +//! The assembly language trampolines are required because Armv7-A processors do +//! not save a great deal of state on entry to an exception handler, unlike +//! Armv7-M (and other M-Profile) processors. We must therefore save this state +//! to the stack using assembly language, before transferring to an `extern "C"` +//! function. We do not change modes before entering that `extern "C"` function +//! - that's for the handler to deal with as it wishes. Because FIQ is often //! performance-sensitive, we don't supply an FIQ trampoline; if you want to use //! FIQ, you have to write your own assembly routine, allowing you to preserve //! only whatever state is important to you. //! -//! If our start-up routine doesn't work for you (e.g. if you have to initialise -//! your memory controller before you touch RAM), supply your own `_start` -//! function (but feel free to call our `_default_start` as part of it). -//! //! ## Examples //! -//! You can find example code using QEMU inside the -//! [project repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples) +//! You can find example code using QEMU inside the [project +//! repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples) #![no_std] @@ -197,8 +253,7 @@ pub extern "C" fn _default_handler() { // The Interrupt Vector Table, and some default assembly-language handler. core::arch::global_asm!( r#" - .section .vector_table - .align 0 + .section .vector_table,"ax",%progbits .global _vector_table .type _vector_table, %function @@ -212,20 +267,6 @@ core::arch::global_asm!( ldr pc, =_asm_irq_handler ldr pc, =_asm_fiq_handler .size _vector_table, . - _vector_table - - .section .text.handlers - - .global _asm_default_fiq_handler - .type _asm_default_fiq_handler, %function - _asm_default_fiq_handler: - b _asm_default_fiq_handler - .size _asm_default_fiq_handler, . - _asm_default_fiq_handler - - .global _asm_default_handler - .type _asm_default_handler, %function - _asm_default_handler: - b _asm_default_handler - .size _asm_default_handler, . - _asm_default_handler "# ); @@ -376,57 +417,13 @@ macro_rules! restore_context { // Our assembly language exception handlers core::arch::global_asm!( r#" - .section .text.handlers - .align 0 - - // Called from the vector table when we have an software interrupt. - // Saves state and calls a C-compatible handler like - // `extern "C" fn svc_handler(svc: u32);` - .global _asm_svc_handler - .type _asm_svc_handler, %function - _asm_svc_handler: - srsfd sp!, {svc_mode} - "#, - save_context!(), - r#" - mrs r0, cpsr // Load processor status - tst r0, {t_bit} // Occurred in Thumb state? - ldrhne r0, [lr,#-2] // Yes: Load halfword and... - bicne r0, r0, #0xFF00 // ...extract comment field - ldreq r0, [lr,#-4] // No: Load word and... - biceq r0, r0, #0xFF000000 // ...extract comment field - // r0 now contains SVC number - bl _svc_handler - "#, - restore_context!(), - r#" - rfefd sp! - .size _asm_svc_handler, . - _asm_svc_handler - - - // Called from the vector table when we have an interrupt. - // Saves state and calls a C-compatible handler like - // `extern "C" fn irq_handler();` - .global _asm_irq_handler - .type _asm_irq_handler, %function - _asm_irq_handler: - sub lr, lr, 4 - srsfd sp!, {irq_mode} - "#, - save_context!(), - r#" - // call C handler - bl _irq_handler - "#, - restore_context!(), - r#" - rfefd sp! - .size _asm_irq_handler, . - _asm_irq_handler - + .section .text._asm_default_undefined_handler // Called from the vector table when we have an undefined exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _undefined_handler(addr: usize);` + // `extern "C" fn _undefined_handler(addr: usize) -> usize;` + // or + // `extern "C" fn _undefined_handler(addr: usize) -> !;` .global _asm_default_undefined_handler .type _asm_default_undefined_handler, %function _asm_default_undefined_handler: @@ -454,6 +451,8 @@ core::arch::global_asm!( mov r0, lr // call C handler bl _undefined_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 // do our standard restore "#, restore_context!(), @@ -468,6 +467,64 @@ core::arch::global_asm!( rfefd sp! .size _asm_default_undefined_handler, . - _asm_default_undefined_handler + + .section .text._asm_default_svc_handler + + // Called from the vector table when we have an software interrupt. + // Saves state and calls a C-compatible handler like + // `extern "C" fn svc_handler(svc: u32);` + .global _asm_default_svc_handler + .type _asm_default_svc_handler, %function + _asm_default_svc_handler: + srsfd sp!, {svc_mode} + "#, + save_context!(), + r#" + mrs r0, cpsr // Load processor status + tst r0, {t_bit} // Occurred in Thumb state? + ldrhne r0, [lr,#-2] // Yes: Load halfword and... + bicne r0, r0, #0xFF00 // ...extract comment field + ldreq r0, [lr,#-4] // No: Load word and... + biceq r0, r0, #0xFF000000 // ...extract comment field + // r0 now contains SVC number + bl _svc_handler + "#, + restore_context!(), + r#" + rfefd sp! + .size _asm_default_svc_handler, . - _asm_default_svc_handler + + + .section .text._asm_default_abort_handler + + // Called from the vector table when we have an undefined exception. + // Saves state and calls a C-compatible handler like + // `extern "C" fn _abort_handler(addr: usize);` + .global _asm_default_abort_handler + .type _asm_default_abort_handler, %function + _asm_default_abort_handler: + // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. + subs lr, lr, #8 + // state save from compiled code + srsfd sp!, {abt_mode} + "#, + save_context!(), + r#" + // Pass the faulting instruction address to the handler. + mov r0, lr + // call C handler + bl _abort_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 + "#, + restore_context!(), + r#" + // Return to the failing instruction which is the recommended approach by ARM. + rfefd sp! + .size _asm_default_abort_handler, . - _asm_default_abort_handler + + + .section .text._asm_default_prefetch_handler // Called from the vector table when we have a prefetch exception. // Saves state and calls a C-compatible handler like @@ -486,6 +543,8 @@ core::arch::global_asm!( mov r0, lr // call C handler bl _prefetch_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 "#, restore_context!(), r#" @@ -494,29 +553,36 @@ core::arch::global_asm!( .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler - // Called from the vector table when we have an undefined exception. + .section .text._asm_default_irq_handler + + // Called from the vector table when we have an interrupt. // Saves state and calls a C-compatible handler like - // `extern "C" fn _abort_handler(addr: usize);` - .global _asm_default_abort_handler - .type _asm_default_abort_handler, %function - _asm_default_abort_handler: - // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. - subs lr, lr, #8 - // state save from compiled code - srsfd sp!, {abt_mode} + // `extern "C" fn irq_handler();` + .global _asm_default_irq_handler + .type _asm_default_irq_handler, %function + _asm_default_irq_handler: + sub lr, lr, 4 + srsfd sp!, {irq_mode} "#, save_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr // call C handler - bl _abort_handler + bl _irq_handler "#, restore_context!(), r#" - // Return to the failing instruction which is the recommended approach by ARM. rfefd sp! - .size _asm_default_abort_handler, . - _asm_default_abort_handler + .size _asm_default_irq_handler, . - _asm_default_irq_handler + + + .section .text._asm_default_fiq_handler + + // Our default FIQ handler + .global _asm_default_fiq_handler + .type _asm_default_fiq_handler, %function + _asm_default_fiq_handler: + b _asm_default_fiq_handler + .size _asm_default_fiq_handler, . - _asm_default_fiq_handler "#, svc_mode = const ProcessorMode::Svc as u8, irq_mode = const ProcessorMode::Irq as u8, @@ -560,7 +626,7 @@ macro_rules! fpu_enable { // We set up our stacks and `kmain` in system mode. core::arch::global_asm!( r#" - .section .text.startup + .section .text.default_start .align 0 .global _default_start @@ -600,7 +666,6 @@ core::arch::global_asm!( mrc p15, 0, r0, c1, c0, 0 bic r0, #{te_bit} mcr p15, 0, r0, c1, c0, 0 - "#, fpu_enable!(), r#" diff --git a/cortex-r-rt/link.x b/cortex-r-rt/link.x index a0296a2..309d1bf 100644 --- a/cortex-r-rt/link.x +++ b/cortex-r-rt/link.x @@ -17,8 +17,6 @@ SECTIONS { .text : { /* The vector table must come first */ *(.vector_table) - /* Our exception handling routines */ - *(.text.handlers) /* Now the rest of the code */ *(.text .text*) } > CODE @@ -72,36 +70,38 @@ SECTIONS { } /* -We reserve some space at the top of the RAM for our stacks. We have an IRQ stack -and a FIQ stack, plus the remainder is our system stack. +We reserve some space at the top of the RAM for our exception stacks. The +remainder is our system mode stack. You must keep _stack_top and the stack sizes aligned to eight byte boundaries. */ PROVIDE(_stack_top = ORIGIN(DATA) + LENGTH(DATA)); -PROVIDE(_fiq_stack_size = 0x400); -PROVIDE(_irq_stack_size = 0x1000); -PROVIDE(_abt_stack_size = 0x400); PROVIDE(_und_stack_size = 0x400); -PROVIDE(_svc_stack_size = 0x1000); +PROVIDE(_svc_stack_size = 0x400); +PROVIDE(_abt_stack_size = 0x400); +PROVIDE(_irq_stack_size = 0x400); +PROVIDE(_fiq_stack_size = 0x400); ASSERT(_stack_top % 8 == 0, "ERROR(cortex-r-rt): top of stack is not 8-byte aligned"); -ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of FIQ stack is not 8-byte aligned"); -ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of IRQ stack is not 8-byte aligned"); -ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of FIQ stack is not 8-byte aligned"); -ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of ABT stack is not 8-byte aligned"); ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of UND stack is not 8-byte aligned"); ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of SVC stack is not 8-byte aligned"); +ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of ABT stack is not 8-byte aligned"); +ASSERT(_irq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of IRQ stack is not 8-byte aligned"); +ASSERT(_fiq_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of FIQ stack is not 8-byte aligned"); /* Weak aliases for ASM default handlers */ +PROVIDE(_start =_default_start); PROVIDE(_asm_undefined_handler =_asm_default_undefined_handler); +PROVIDE(_asm_svc_handler =_asm_default_svc_handler); PROVIDE(_asm_prefetch_handler =_asm_default_prefetch_handler); PROVIDE(_asm_abort_handler =_asm_default_abort_handler); +PROVIDE(_asm_irq_handler =_asm_default_irq_handler); PROVIDE(_asm_fiq_handler =_asm_default_fiq_handler); /* Weak aliases for C default handlers */ PROVIDE(_undefined_handler =_default_handler); -PROVIDE(_abort_handler =_default_handler); +PROVIDE(_svc_handler =_default_handler); PROVIDE(_prefetch_handler =_default_handler); +PROVIDE(_abort_handler =_default_handler); PROVIDE(_irq_handler =_default_handler); -PROVIDE(_svc_handler =_default_handler); -PROVIDE(_start =_default_start); +/* There is no default C-language FIQ handler */ diff --git a/cortex-r-rt/src/lib.rs b/cortex-r-rt/src/lib.rs index f4f4a15..0220903 100644 --- a/cortex-r-rt/src/lib.rs +++ b/cortex-r-rt/src/lib.rs @@ -1,7 +1,24 @@ //! # Run-time support for Arm Cortex-R //! //! This library implements a simple Arm vector table, suitable for getting into -//! a Rust application running in System Mode. +//! a Rust application running in System Mode. It also provides a reference +//! start up method. Most Cortex-R based systems will require chip specific +//! start-up code, so the start-up method can over overriden. +//! +//! The default startup routine provided by this crate does not include any +//! special handling for multi-core support because this is oftentimes +//! implementation defined and the exact handling depends on the specific chip +//! in use. Many implementations only run the startup routine with one core and +//! will keep other cores in reset until they are woken up by an implementation +//! specific mechanism. For other implementations where multi-core specific +//! startup adaptions are necessary, the startup routine can be overwritten by +//! the user. +//! +//! ## Features +//! +//! - `eabi-fpu`: Enables the FPU, even if you selected a soft-float ABI target. +//! +//! ## Information about the Run-Time //! //! Transferring from System Mode to User Mode (i.e. implementing an RTOS) is //! not handled here. @@ -32,7 +49,11 @@ //! * `__sidata` - the start of the initialisation values for data, in read-only //! memory. Must be 4-byte aligned. //! -//! ### Functions +//! Using our default start-up function `_default_start`, the memory between +//! `__sbss` and `__ebss` is zeroed, and the memory between `__sdata` and +//! `__edata` is initialised with the data found at `__sidata`. +//! +//! ### C-Compatible Functions //! //! * `kmain` - the `extern "C"` entry point to your application. //! @@ -45,7 +66,9 @@ //! //! * `_svc_handler` - an `extern "C"` function to call when an SVC Exception //! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. +//! `_default_handler` but you can override it. Returning from this function +//! will cause execution to resume from the function the triggered the +//! exception, immediately after the SVC instruction. //! //! Expected prototype: //! @@ -56,108 +79,155 @@ //! //! * `_irq_handler` - an `extern "C"` function to call when an Interrupt //! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. +//! `_default_handler` but you can override it. Returning from this function +//! will cause execution to resume from the function the triggered the +//! exception. //! //! Expected prototype: //! //! ```rust +//! /// Upon return, the interrupt handler will end and execution +//! /// will continue at the interrupted instruction. //! #[unsafe(no_mangle)] //! extern "C" fn _irq_handler(); //! ``` //! -//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_undefined_handler` unless that function is overriden as well. +//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined +//! Exception occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_undefined_handler` is missing. //! -//! Expected prototype: +//! The expected prototype for `_undefined_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _undefined_handler(addr: usize); +//! extern "C" fn _undefined_handler(addr: usize) -> !; //! ``` +//! +//! or: //! -//! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_abort_handler` unless that function is overriden as well. +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _undefined_handler(addr: usize) -> usize; +//! ``` //! -//! Expected prototype: +//! * `_abort_handler` - an `extern "C"` function to call when an Data Abort +//! occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_abort_handler` is missing. +//! +//! The expected prototype for `_abort_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _abort_handler(addr: usize); +//! extern "C" fn _abort_handler(addr: usize) -> !; //! ``` +//! +//! or: //! -//! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception -//! occurs. Our linker script PROVIDEs a default function at -//! `_default_handler` but you can override it. It will be called by the -//! `_asm_default_prefetch_handler` unless that function is overriden as well. +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _abort_handler(addr: usize) -> usize; +//! ``` //! -//! Expected prototype: +//! * `_prefetch_handler` - an `extern "C"` function to call when an Prefetch +//! Abort occurs. Our linker script PROVIDEs a default implementation at +//! `_default_handler` which is used if `_prefetch_handler` is missing. +//! +//! The expected prototype for `_prefetch_handler` is either: //! //! ```rust +//! /// Does not return //! #[unsafe(no_mangle)] -//! extern "C" fn _prefetch_handler(addr: usize); +//! extern "C" fn _prefetch_handler(addr: usize) -> !; +//! ``` +//! +//! or: +//! +//! ```rust +//! /// Execution will continue from the returned address. +//! /// +//! /// Return `addr` to go back and execute the faulting instruction again. +//! #[unsafe(no_mangle)] +//! extern "C" fn _prefetch_handler(addr: usize) -> usize; //! ``` //! //! ### ASM functions //! -//! * `__start` - a Reset handler. Our linker script PROVIDEs a default function -//! at `_default_start` but you can override it. Most Cortex-A SoCs require -//! a chip specific startup for tasks like MMU initialization or chip specific -//! initialization routines. -//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt -//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_fiq_handler` but you can override it. +//! * `_start` - a Reset handler. Our linker script PROVIDEs a default function +//! at `_default_start` but you can override it. Some SoCs require a chip +//! specific startup for tasks like MMU initialization or chip specific +//! initialization routines, so if our start-up routine doesn't work for you, +//! supply your own `_start` function (but feel free to call our +//! `_default_start` as part of it). //! * `_asm_undefined_handler` - a naked function to call when an Undefined //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_undefined_handler` but you can override it. +//! `_asm_default_undefined_handler` but you can override it. The provided +//! default handler will call `_undefined_handler`, saving state as required. +//! * `_asm_svc_handler` - a naked function to call when an SVC Exception +//! occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_svc_handler` but you can override it. The provided default +//! handler will call `_svc_handler`, saving state as required. //! * `_asm_prefetch_handler` - a naked function to call when a Prefetch //! Exception occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_prefetch_handler` but you can override it. The provided default -//! handler will perform an exception return to the faulting address. +//! `_asm_default_prefetch_handler` but you can override it. The provided +//! default handler will call `_prefetch_handler`, saving state as required. +//! Note that Prefetch Exceptions are handled in Abort Mode, Monitor Mode or +//! Hyp Mode, depending on CPU configuration. There is no Prefetch Abort mode, +//! so there is no Prefetch Abort Mode stack. //! * `_asm_abort_handler` - a naked function to call when an Abort Exception //! occurs. Our linker script PROVIDEs a default function at -//! `_asm_default_abort_handler` but you can override it. The provided default handler -//! will perform an exception return to the faulting address. +//! `_asm_default_abort_handler` but you can override it. The provided default +//! handler will call `_abort_handler`, saving state as required. +//! * `_asm_irq_handler` - a naked function to call when an Undefined Exception +//! occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_irq_handler` but you can override it. The provided default +//! handler will call `_irq_handler`, saving state as required. +//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt +//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at +//! `_asm_default_fiq_handler` but you can override it. The provided default +//! just spins forever. //! -//! On start-up, the memory between `__sbss` and `__ebss` is zeroed, and the -//! memory between `__sdata` and `__edata` is initialised with the data found at -//! `__sidata`. +//! ## Outputs //! //! This library produces global symbols called: //! //! * `_vector_table` - the start of the interrupt vector table //! * `_default_start` - the default Reset handler, that sets up some stacks and //! calls an `extern "C"` function called `kmain`. +//! * `_asm_default_undefined_handler` - assembly language trampoline that calls +//! `_undefined_handler` +//! * `_asm_default_svc_handler` - assembly language trampoline that calls +//! `_svc_handler` +//! * `_asm_default_prefetch_handler` - assembly language trampoline that calls +//! `_prefetch_handler` +//! * `_asm_default_abort_handler` - assembly language trampoline that calls +//! `_abort_handler` +//! * `_asm_default_irq_handler` - assembly language trampoline that calls +//! `_irq_handler` //! * `_asm_default_fiq_handler` - an FIQ handler that just spins -//! * `_asm_default_handler` - an exception handler that just spins -//! * `_asm_svc_handler` - assembly language trampoline for SVC Exceptions that -//! calls `_svc_handler` -//! * `_asm_irq_handler` - assembly language trampoline for Interrupts that -//! calls `_irq_handler` +//! * `_default_handler` - a C compatible function that spins forever. //! //! The assembly language trampolines are required because Armv7-R (and Armv8-R) //! processors do not save a great deal of state on entry to an exception //! handler, unlike Armv7-M (and other M-Profile) processors. We must therefore //! save this state to the stack using assembly language, before transferring to //! an `extern "C"` function. We do not change modes before entering that -//! `extern "C"` function - that's for the handler to deal with as it wishes. We -//! supply a default handler that prints an error message to Semihosting so you -//! know if you hit an unexpected exception. Because FIQ is often -//! performance-sensitive, we don't supply an FIQ trampoline; if you want to use -//! FIQ, you have to write your own assembly routine, allowing you to preserve -//! only whatever state is important to you. -//! -//! If our start-up routine doesn't work for you (e.g. if you have to initialise -//! your memory controller before you touch RAM), supply your own `_start` -//! function (but feel free to call our `_default_start` as part of it). +//! `extern "C"` function - that's for the handler to deal with as it wishes. +//! Because FIQ is often performance-sensitive, we don't supply an FIQ +//! trampoline; if you want to use FIQ, you have to write your own assembly +//! routine, allowing you to preserve only whatever state is important to you. //! //! ## Examples //! -//! You can find example code using QEMU inside the -//! [project repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples) +//! You can find example code using QEMU inside the [project +//! repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples) #![no_std] @@ -183,9 +253,7 @@ pub extern "C" fn _default_handler() { // The Interrupt Vector Table, and some default assembly-language handler. core::arch::global_asm!( r#" - .section .vector_table - .align 0 - + .section .vector_table,"ax",%progbits .global _vector_table .type _vector_table, %function _vector_table: @@ -198,20 +266,6 @@ core::arch::global_asm!( ldr pc, =_asm_irq_handler ldr pc, =_asm_fiq_handler .size _vector_table, . - _vector_table - - .section .text.handlers - - .global _asm_default_fiq_handler - .type _asm_default_fiq_handler, %function - _asm_default_fiq_handler: - b _asm_default_fiq_handler - .size _asm_default_fiq_handler, . - _asm_default_fiq_handler - - .global _asm_default_handler - .type _asm_default_handler, %function - _asm_default_handler: - b _asm_default_handler - .size _asm_default_handler, . - _asm_default_handler "# ); @@ -306,59 +360,15 @@ macro_rules! restore_context { // Our assembly language exception handlers core::arch::global_asm!( r#" - .section .text.handlers // Work around https://github.com/rust-lang/rust/issues/127269 .fpu vfp3-d16 - .align 0 - - // Called from the vector table when we have an software interrupt. - // Saves state and calls a C-compatible handler like - // `extern "C" fn svc_handler(svc: u32);` - .global _asm_svc_handler - .type _asm_svc_handler, %function - _asm_svc_handler: - srsfd sp!, {svc_mode} - "#, - save_context!(), - r#" - mrs r0, cpsr // Load processor status - tst r0, {t_bit} // Occurred in Thumb state? - ldrhne r0, [lr,#-2] // Yes: Load halfword and... - bicne r0, r0, #0xFF00 // ...extract comment field - ldreq r0, [lr,#-4] // No: Load word and... - biceq r0, r0, #0xFF000000 // ...extract comment field - // r0 now contains SVC number - bl _svc_handler - "#, - restore_context!(), - r#" - rfefd sp! - .size _asm_svc_handler, . - _asm_svc_handler - - - // Called from the vector table when we have an interrupt. - // Saves state and calls a C-compatible handler like - // `extern "C" fn irq_handler();` - .global _asm_irq_handler - .type _asm_irq_handler, %function - _asm_irq_handler: - sub lr, lr, 4 - srsfd sp!, {irq_mode} - "#, - save_context!(), - r#" - // call C handler - bl _irq_handler - "#, - restore_context!(), - r#" - rfefd sp! - .size _asm_irq_handler, . - _asm_irq_handler - - + // Called from the vector table when we have an undefined exception. // Saves state and calls a C-compatible handler like - // `extern "C" fn _undefined_handler(addr: usize);` + // `extern "C" fn _undefined_handler(addr: usize) -> usize;` + // or + // `extern "C" fn _undefined_handler(addr: usize) -> !;` + .section .text._asm_default_undefined_handler .global _asm_default_undefined_handler .type _asm_default_undefined_handler, %function _asm_default_undefined_handler: @@ -386,6 +396,8 @@ core::arch::global_asm!( mov r0, lr // call C handler bl _undefined_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 // do our standard restore "#, restore_context!(), @@ -401,6 +413,64 @@ core::arch::global_asm!( .size _asm_default_undefined_handler, . - _asm_default_undefined_handler + .section .text._asm_default_svc_handler + + // Called from the vector table when we have an software interrupt. + // Saves state and calls a C-compatible handler like + // `extern "C" fn svc_handler(svc: u32);` + .global _asm_default_svc_handler + .type _asm_default_svc_handler, %function + _asm_default_svc_handler: + srsfd sp!, {svc_mode} + "#, + save_context!(), + r#" + mrs r0, cpsr // Load processor status + tst r0, {t_bit} // Occurred in Thumb state? + ldrhne r0, [lr,#-2] // Yes: Load halfword and... + bicne r0, r0, #0xFF00 // ...extract comment field + ldreq r0, [lr,#-4] // No: Load word and... + biceq r0, r0, #0xFF000000 // ...extract comment field + // r0 now contains SVC number + bl _svc_handler + "#, + restore_context!(), + r#" + rfefd sp! + .size _asm_default_svc_handler, . - _asm_default_svc_handler + + + .section .text._asm_default_abort_handler + + // Called from the vector table when we have an undefined exception. + // Saves state and calls a C-compatible handler like + // `extern "C" fn _abort_handler(addr: usize);` + .global _asm_default_abort_handler + .type _asm_default_abort_handler, %function + _asm_default_abort_handler: + // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. + subs lr, lr, #8 + // state save from compiled code + srsfd sp!, {abt_mode} + "#, + save_context!(), + r#" + // Pass the faulting instruction address to the handler. + mov r0, lr + // call C handler + bl _abort_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 + "#, + restore_context!(), + r#" + // Return to the failing instruction which is the recommended approach by ARM. + rfefd sp! + .size _asm_default_abort_handler, . - _asm_default_abort_handler + + + .section .text._asm_default_prefetch_handler + // Called from the vector table when we have a prefetch exception. // Saves state and calls a C-compatible handler like // `extern "C" fn _prefetch_handler(addr: usize);` @@ -418,6 +488,8 @@ core::arch::global_asm!( mov r0, lr // call C handler bl _prefetch_handler + // if we get back here, assume they returned a new LR in r0 + mov lr, r0 "#, restore_context!(), r#" @@ -426,29 +498,36 @@ core::arch::global_asm!( .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler - // Called from the vector table when we have an undefined exception. + .section .text._asm_default_irq_handler + + // Called from the vector table when we have an interrupt. // Saves state and calls a C-compatible handler like - // `extern "C" fn _abort_handler(addr: usize);` - .global _asm_default_abort_handler - .type _asm_default_abort_handler, %function - _asm_default_abort_handler: - // Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual. - subs lr, lr, #8 - // state save from compiled code - srsfd sp!, {abt_mode} + // `extern "C" fn irq_handler();` + .global _asm_default_irq_handler + .type _asm_default_irq_handler, %function + _asm_default_irq_handler: + sub lr, lr, 4 + srsfd sp!, {irq_mode} "#, save_context!(), r#" - // Pass the faulting instruction address to the handler. - mov r0, lr // call C handler - bl _abort_handler + bl _irq_handler "#, restore_context!(), r#" - // Return to the failing instruction which is the recommended approach by ARM. rfefd sp! - .size _asm_default_abort_handler, . - _asm_default_abort_handler + .size _asm_default_irq_handler, . - _asm_default_irq_handler + + + .section .text._asm_default_fiq_handler + + // Our default FIQ handler + .global _asm_default_fiq_handler + .type _asm_default_fiq_handler, %function + _asm_default_fiq_handler: + b _asm_default_fiq_handler + .size _asm_default_fiq_handler, . - _asm_default_fiq_handler "#, svc_mode = const ProcessorMode::Svc as u8, irq_mode = const ProcessorMode::Irq as u8, @@ -456,8 +535,8 @@ core::arch::global_asm!( abt_mode = const ProcessorMode::Abt as u8, t_bit = const { Cpsr::new_with_raw_value(0) - .with_t(true) - .raw_value() + .with_t(true) + .raw_value() }, ); @@ -492,11 +571,10 @@ macro_rules! fpu_enable { // We set up our stacks and `kmain` in system mode. core::arch::global_asm!( r#" - .section .text.startup - .align 0 // Work around https://github.com/rust-lang/rust/issues/127269 .fpu vfp3-d16 - + + .section .text.el1_start .type _el1_start, %function _el1_start: // Set up stacks. @@ -618,9 +696,10 @@ core::arch::global_asm!( #[cfg(arm_architecture = "v7-r")] core::arch::global_asm!( r#" - .section .text.startup - .align 0 + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3-d16 + .section .text.default_start .global _default_start .type _default_start, %function _default_start: @@ -638,8 +717,10 @@ core::arch::global_asm!( #[cfg(arm_architecture = "v8-r")] core::arch::global_asm!( r#" - .section .text.startup - .align 0 + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp3-d16 + + .section .text.default_start .global _default_start .type _default_start, %function diff --git a/examples/mps3-an536/src/bin/abt-exception.rs b/examples/mps3-an536/src/bin/abt-exception.rs index 4a0955c..ddf599c 100644 --- a/examples/mps3-an536/src/bin/abt-exception.rs +++ b/examples/mps3-an536/src/bin/abt-exception.rs @@ -56,17 +56,17 @@ fn disable_alignment_check() { } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: u32) { +unsafe extern "C" fn _undefined_handler(_addr: u32) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: u32) { +unsafe extern "C" fn _prefetch_handler(_addr: u32) -> ! { panic!("unexpected prefetch exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: u32) { +unsafe extern "C" fn _abort_handler(addr: usize) -> usize { println!("data abort occurred"); // If this is not disabled, reading DFAR will trigger an alignment fault on Armv8-R, leading // to a loop. @@ -82,4 +82,6 @@ unsafe extern "C" fn _abort_handler(_addr: u32) { if COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) == 1 { semihosting::process::exit(0); } + + addr } diff --git a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs index ced3ecd..b561ce1 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs @@ -51,12 +51,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) { +unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) { +unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -77,9 +77,11 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs index cc03404..12249cb 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs @@ -51,12 +51,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) { +unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) { +unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -80,9 +80,11 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/mps3-an536/src/bin/undef-exception-a32.rs b/examples/mps3-an536/src/bin/undef-exception-a32.rs index ed9b2e7..ff672f6 100644 --- a/examples/mps3-an536/src/bin/undef-exception-a32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-a32.rs @@ -48,12 +48,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) { +unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) { +unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if addr == udf_from_a32 as usize { @@ -72,9 +72,12 @@ unsafe extern "C" fn _undefined_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + // otherwise go back to where we came from + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/mps3-an536/src/bin/undef-exception-t32.rs b/examples/mps3-an536/src/bin/undef-exception-t32.rs index 75bd43d..1cd70fd 100644 --- a/examples/mps3-an536/src/bin/undef-exception-t32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-t32.rs @@ -48,12 +48,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) { +unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) { +unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if (addr + 1) == udf_from_t32 as usize { @@ -72,9 +72,11 @@ unsafe extern "C" fn _undefined_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/versatileab/src/bin/abt-exception.rs b/examples/versatileab/src/bin/abt-exception.rs index d22566b..ab03ecd 100644 --- a/examples/versatileab/src/bin/abt-exception.rs +++ b/examples/versatileab/src/bin/abt-exception.rs @@ -56,17 +56,17 @@ fn disable_alignment_check() { } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: u32) { +unsafe extern "C" fn _undefined_handler(_addr: u32) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: u32) { +unsafe extern "C" fn _prefetch_handler(_addr: u32) -> ! { panic!("unexpected prefetch exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: u32) { +unsafe extern "C" fn _abort_handler(addr: usize) -> usize { println!("data abort occurred"); let dfsr = Dfsr::read(); println!("DFSR (Fault Status Register): {:?}", dfsr); @@ -82,4 +82,6 @@ unsafe extern "C" fn _abort_handler(_addr: u32) { if COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) == 1 { semihosting::process::exit(0); } + + addr } diff --git a/examples/versatileab/src/bin/prefetch-exception-a32.rs b/examples/versatileab/src/bin/prefetch-exception-a32.rs index b26ebea..d773dd3 100644 --- a/examples/versatileab/src/bin/prefetch-exception-a32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-a32.rs @@ -51,12 +51,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) { +unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) { +unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -77,9 +77,11 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/versatileab/src/bin/prefetch-exception-t32.rs b/examples/versatileab/src/bin/prefetch-exception-t32.rs index d8f640e..acea6c8 100644 --- a/examples/versatileab/src/bin/prefetch-exception-t32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-t32.rs @@ -51,12 +51,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(_addr: usize) { +unsafe extern "C" fn _undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(addr: usize) { +unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { println!("prefetch abort occurred"); let ifsr = Ifsr::read(); println!("IFSR (Fault Status Register): {:?}", ifsr); @@ -80,9 +80,11 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/versatileab/src/bin/undef-exception-a32.rs b/examples/versatileab/src/bin/undef-exception-a32.rs index b8833eb..6679d2d 100644 --- a/examples/versatileab/src/bin/undef-exception-a32.rs +++ b/examples/versatileab/src/bin/undef-exception-a32.rs @@ -48,12 +48,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) { +unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) { +unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if addr == udf_from_a32 as usize { @@ -72,9 +72,11 @@ unsafe extern "C" fn _undefined_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } diff --git a/examples/versatileab/src/bin/undef-exception-t32.rs b/examples/versatileab/src/bin/undef-exception-t32.rs index 01ee741..c9869b4 100644 --- a/examples/versatileab/src/bin/undef-exception-t32.rs +++ b/examples/versatileab/src/bin/undef-exception-t32.rs @@ -48,12 +48,12 @@ core::arch::global_asm!( ); #[unsafe(no_mangle)] -unsafe extern "C" fn _prefetch_handler(_addr: usize) { +unsafe extern "C" fn _prefetch_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); } #[unsafe(no_mangle)] -unsafe extern "C" fn _undefined_handler(addr: usize) { +unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); if (addr + 1) == udf_from_t32 as usize { @@ -72,9 +72,11 @@ unsafe extern "C" fn _undefined_handler(addr: usize) { // we've faulted twice - time to quit semihosting::process::exit(0); } + + addr } #[unsafe(no_mangle)] -unsafe extern "C" fn _abort_handler(_addr: usize) { +unsafe extern "C" fn _abort_handler(_addr: usize) -> ! { panic!("unexpected abort exception"); } From 55bc8ad92eb3139f213f7b5ac588607df34dab25 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Thu, 10 Apr 2025 11:13:39 +0100 Subject: [PATCH 6/6] Fix cortex-a-rt docs title. Also clarify that both only apply to AArch32 systems. The terms 'Cortex-R' and 'Cortex-A' are particular brands of processor made by Arm, and there are processors under each of those brands that run in (or only run in) AArch64 mode. --- cortex-a-rt/src/lib.rs | 2 +- cortex-r-rt/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cortex-a-rt/src/lib.rs b/cortex-a-rt/src/lib.rs index 0d485a3..a59b3bd 100644 --- a/cortex-a-rt/src/lib.rs +++ b/cortex-a-rt/src/lib.rs @@ -1,4 +1,4 @@ -//! # Run-time support for Arm Cortex-R +//! # Run-time support for Arm Cortex-A (AArch32) //! //! This library implements a simple Arm vector table, suitable for getting into //! a Rust application running in System Mode. It also provides a reference diff --git a/cortex-r-rt/src/lib.rs b/cortex-r-rt/src/lib.rs index 0220903..75b5f34 100644 --- a/cortex-r-rt/src/lib.rs +++ b/cortex-r-rt/src/lib.rs @@ -1,4 +1,4 @@ -//! # Run-time support for Arm Cortex-R +//! # Run-time support for Arm Cortex-R (AArch32) //! //! This library implements a simple Arm vector table, suitable for getting into //! a Rust application running in System Mode. It also provides a reference