From 4c451f69cab33b7a4f1d7e48b8053777f4003fce Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Wed, 16 Apr 2025 22:11:52 +0100 Subject: [PATCH 1/4] Don't trash the LR returned from _undefined_handler. Also modifies the undef-exception test to make sure we can actually recover from an undef exception. And updates the abt-exception test to also check returning, and dealing with both A32 and T32 modes. --- cortex-a-rt/src/lib.rs | 8 +- cortex-r-rt/src/lib.rs | 8 +- ... abt-exception-a32-armv8r-none-eabihf.out} | 11 +- .../abt-exception-t32-armv8r-none-eabihf.out | 16 ++ ...fetch-exception-a32-armv8r-none-eabihf.out | 3 + ...fetch-exception-t32-armv8r-none-eabihf.out | 3 + ...undef-exception-a32-armv8r-none-eabihf.out | 3 + ...undef-exception-t32-armv8r-none-eabihf.out | 3 + ...{abt-exception.rs => abt-exception-a32.rs} | 82 ++++++++-- .../mps3-an536/src/bin/abt-exception-t32.rs | 141 ++++++++++++++++++ .../src/bin/prefetch-exception-a32.rs | 27 +++- .../src/bin/prefetch-exception-t32.rs | 27 +++- .../mps3-an536/src/bin/undef-exception-a32.rs | 33 ++-- .../mps3-an536/src/bin/undef-exception-t32.rs | 27 +++- ...=> abt-exception-a32-armv7a-none-eabi.out} | 11 +- ...=> abt-exception-a32-armv7r-none-eabi.out} | 11 +- ... abt-exception-a32-armv7r-none-eabihf.out} | 11 +- .../abt-exception-t32-armv7a-none-eabi.out | 16 ++ .../abt-exception-t32-armv7r-none-eabi.out | 16 ++ .../abt-exception-t32-armv7r-none-eabihf.out | 16 ++ ...refetch-exception-a32-armv7a-none-eabi.out | 3 + ...refetch-exception-a32-armv7r-none-eabi.out | 3 + ...fetch-exception-a32-armv7r-none-eabihf.out | 3 + ...refetch-exception-t32-armv7a-none-eabi.out | 3 + ...refetch-exception-t32-armv7r-none-eabi.out | 3 + ...fetch-exception-t32-armv7r-none-eabihf.out | 3 + .../undef-exception-a32-armv7a-none-eabi.out | 3 + .../undef-exception-a32-armv7r-none-eabi.out | 3 + ...undef-exception-a32-armv7r-none-eabihf.out | 3 + .../undef-exception-t32-armv7a-none-eabi.out | 3 + .../undef-exception-t32-armv7r-none-eabi.out | 3 + ...undef-exception-t32-armv7r-none-eabihf.out | 3 + ...{abt-exception.rs => abt-exception-a32.rs} | 88 ++++++++--- .../versatileab/src/bin/abt-exception-t32.rs | 141 ++++++++++++++++++ .../src/bin/prefetch-exception-a32.rs | 27 +++- .../src/bin/prefetch-exception-t32.rs | 27 +++- .../src/bin/undef-exception-a32.rs | 32 ++-- .../src/bin/undef-exception-t32.rs | 27 +++- 38 files changed, 731 insertions(+), 120 deletions(-) rename examples/mps3-an536/reference/{abt-exception-armv8r-none-eabihf.out => abt-exception-a32-armv8r-none-eabihf.out} (55%) create mode 100644 examples/mps3-an536/reference/abt-exception-t32-armv8r-none-eabihf.out rename examples/mps3-an536/src/bin/{abt-exception.rs => abt-exception-a32.rs} (50%) create mode 100644 examples/mps3-an536/src/bin/abt-exception-t32.rs rename examples/versatileab/reference/{abt-exception-armv7r-none-eabihf.out => abt-exception-a32-armv7a-none-eabi.out} (56%) rename examples/versatileab/reference/{abt-exception-armv7a-none-eabi.out => abt-exception-a32-armv7r-none-eabi.out} (56%) rename examples/versatileab/reference/{abt-exception-armv7r-none-eabi.out => abt-exception-a32-armv7r-none-eabihf.out} (56%) create mode 100644 examples/versatileab/reference/abt-exception-t32-armv7a-none-eabi.out create mode 100644 examples/versatileab/reference/abt-exception-t32-armv7r-none-eabi.out create mode 100644 examples/versatileab/reference/abt-exception-t32-armv7r-none-eabihf.out rename examples/versatileab/src/bin/{abt-exception.rs => abt-exception-a32.rs} (50%) create mode 100644 examples/versatileab/src/bin/abt-exception-t32.rs diff --git a/cortex-a-rt/src/lib.rs b/cortex-a-rt/src/lib.rs index 488697f..cfbd1ad 100644 --- a/cortex-a-rt/src/lib.rs +++ b/cortex-a-rt/src/lib.rs @@ -459,8 +459,6 @@ core::arch::global_asm!( 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!(), @@ -475,8 +473,6 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // get our saved LR - pop {{lr}} // get our real saved R0 pop {{r0}} // overwrite the saved LR with the adjusted one @@ -537,6 +533,8 @@ core::arch::global_asm!( "#, restore_context!(), r#" + // 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_abort_handler, . - _asm_default_abort_handler @@ -566,6 +564,8 @@ core::arch::global_asm!( "#, restore_context!(), r#" + // 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_prefetch_handler, . - _asm_default_prefetch_handler diff --git a/cortex-r-rt/src/lib.rs b/cortex-r-rt/src/lib.rs index d82f7fc..a19515a 100644 --- a/cortex-r-rt/src/lib.rs +++ b/cortex-r-rt/src/lib.rs @@ -409,8 +409,6 @@ core::arch::global_asm!( 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!(), @@ -425,8 +423,6 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // get our saved LR - pop {{lr}} // get our real saved R0 pop {{r0}} // overwrite the saved LR with the adjusted one @@ -487,6 +483,8 @@ core::arch::global_asm!( "#, restore_context!(), r#" + // 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_abort_handler, . - _asm_default_abort_handler @@ -516,6 +514,8 @@ core::arch::global_asm!( "#, restore_context!(), r#" + // 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_prefetch_handler, . - _asm_default_prefetch_handler diff --git a/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out b/examples/mps3-an536/reference/abt-exception-a32-armv8r-none-eabihf.out similarity index 55% rename from examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out rename to examples/mps3-an536/reference/abt-exception-a32-armv8r-none-eabihf.out index b12baf8..51e25aa 100644 --- a/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/abt-exception-a32-armv8r-none-eabihf.out @@ -2,8 +2,15 @@ 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) +DFAR (Faulting Address Register): Dfar(536870913) +caught unaligned_from_a32 +caught fault on COUNTER +Doing it again 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) +DFAR (Faulting Address Register): Dfar(536870913) +caught unaligned_from_a32 +caught fault on COUNTER +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536/reference/abt-exception-t32-armv8r-none-eabihf.out b/examples/mps3-an536/reference/abt-exception-t32-armv8r-none-eabihf.out new file mode 100644 index 0000000..c2b98b6 --- /dev/null +++ b/examples/mps3-an536/reference/abt-exception-t32-armv8r-none-eabihf.out @@ -0,0 +1,16 @@ +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(536870913) +caught unaligned_from_t32 +caught fault on COUNTER +Doing it again +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(536870913) +caught unaligned_from_t32 +caught fault on COUNTER +Skipping instruction +Recovered from fault OK! 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 index fab7e13..a2be48f 100644 --- a/examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/prefetch-exception-a32-armv8r-none-eabihf.out @@ -4,8 +4,11 @@ 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 +Doing it again 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 +Skipping instruction +Recovered from fault OK! 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 index f00a35c..75f99bd 100644 --- a/examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/prefetch-exception-t32-armv8r-none-eabihf.out @@ -4,8 +4,11 @@ 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 +Doing it again 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 +Skipping instruction +Recovered from fault OK! 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 index a176992..dbafb84 100644 --- a/examples/mps3-an536/reference/undef-exception-a32-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/undef-exception-a32-armv8r-none-eabihf.out @@ -1,5 +1,8 @@ Hello, this is a undef exception example undefined abort occurred caught udf_from_a32 +Doing it again undefined abort occurred caught udf_from_a32 +Skipping instruction +Recovered from fault OK! 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 index f15c47e..083a29c 100644 --- a/examples/mps3-an536/reference/undef-exception-t32-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/undef-exception-t32-armv8r-none-eabihf.out @@ -1,5 +1,8 @@ Hello, this is a undef exception example undefined abort occurred caught udf_from_t32 +Doing it again undefined abort occurred caught udf_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/mps3-an536/src/bin/abt-exception.rs b/examples/mps3-an536/src/bin/abt-exception-a32.rs similarity index 50% rename from examples/mps3-an536/src/bin/abt-exception.rs rename to examples/mps3-an536/src/bin/abt-exception-a32.rs index ddf599c..0763bbd 100644 --- a/examples/mps3-an536/src/bin/abt-exception.rs +++ b/examples/mps3-an536/src/bin/abt-exception-a32.rs @@ -3,7 +3,7 @@ #![no_std] #![no_main] -use core::sync::atomic::AtomicU32; +use core::sync::atomic::{AtomicU32, Ordering}; use cortex_ar::register::{Dfar, Dfsr, Sctlr}; // pull in our start-up code @@ -11,6 +11,7 @@ use mps3_an536 as _; use semihosting::println; +#[no_mangle] static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. @@ -30,19 +31,36 @@ fn main() -> ! { 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 - ); + // Unaligned read + unaligned_from_a32(); } - unreachable!("should never be here!"); + println!("Recovered from fault OK!"); + + semihosting::process::exit(0); } +// These functions are written in assembly +extern "C" { + fn unaligned_from_a32(); +} + +core::arch::global_asm!( + r#" + // fn unaligned_from_a32(); + .arm + .global unaligned_from_a32 + .type unaligned_from_a32, %function + unaligned_from_a32: + ldr r0, =COUNTER + add r0, r0, 1 + ldr r0, [r0] + bx lr + .size unaligned_from_a32, . - unaligned_from_a32 +"# +); + fn enable_alignment_check() { let mut sctrl = Sctlr::read(); sctrl.set_a(true); @@ -77,11 +95,47 @@ unsafe extern "C" fn _abort_handler(addr: usize) -> usize { 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); + + // note the fault isn't at the start of the function + let expect_fault_at = unaligned_from_a32 as usize + 8; + + if addr == expect_fault_at { + println!("caught unaligned_from_a32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, expect_fault_at + ); } - addr + let expect_fault_from = core::ptr::addr_of!(COUNTER) as usize + 1; + + if dfar.0 as usize == expect_fault_from { + println!("caught fault on COUNTER"); + } else { + println!( + "Bad DFAR address {:08x} is not {:08x}", + dfar.0, expect_fault_from + ); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 4 + } + _ => { + // we've faulted thrice - time to quit + println!("We triple faulted"); + semihosting::process::abort(); + } + } } diff --git a/examples/mps3-an536/src/bin/abt-exception-t32.rs b/examples/mps3-an536/src/bin/abt-exception-t32.rs new file mode 100644 index 0000000..f464f64 --- /dev/null +++ b/examples/mps3-an536/src/bin/abt-exception-t32.rs @@ -0,0 +1,141 @@ +//! Example triggering an data abort exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use cortex_ar::register::{Dfar, Dfsr, Sctlr}; +// pull in our start-up code +use mps3_an536 as _; + +use semihosting::println; + +#[no_mangle] +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"); + unsafe { + // Unaligned read + unaligned_from_t32(); + } + + println!("Recovered from fault OK!"); + + semihosting::process::exit(0); +} + +// These functions are written in assembly +extern "C" { + fn unaligned_from_t32(); +} + +core::arch::global_asm!( + r#" + // fn unaligned_from_t32(); + .thumb + .global unaligned_from_t32 + .type unaligned_from_t32, %function + unaligned_from_t32: + ldr r0, =COUNTER + add r0, r0, 1 + ldr r0, [r0] + bx lr + .size unaligned_from_t32, . - unaligned_from_t32 +"# +); + +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: 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. + 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(); + + // note the fault isn't at the start of the function + let expect_fault_at = unaligned_from_t32 as usize + 5; + + if addr == expect_fault_at { + println!("caught unaligned_from_t32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, expect_fault_at + ); + } + + let expect_fault_from = core::ptr::addr_of!(COUNTER) as usize + 1; + + if dfar.0 as usize == expect_fault_from { + println!("caught fault on COUNTER"); + } else { + println!( + "Bad DFAR address {:08x} is not {:08x}", + dfar.0, expect_fault_from + ); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 2 + } + _ => { + // we've faulted thrice - time to quit + println!("We triple faulted"); + semihosting::process::abort(); + } + } +} diff --git a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs index b561ce1..58c404a 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs @@ -26,10 +26,9 @@ pub extern "C" fn kmain() -> ! { bkpt_from_a32(); } - // this should be impossible because returning from the fault handler will - // immediately trigger the fault again. + println!("Recovered from fault OK!"); - unreachable!("should never be here!"); + semihosting::process::exit(0); } // These functions are written in assembly @@ -73,12 +72,24 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { ); } - if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { - // we've faulted twice - time to quit - semihosting::process::exit(0); + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 4 + } + _ => { + // we've faulted thrice - time to quit + panic!("_prefetch_handler called too often"); + } } - - addr } #[unsafe(no_mangle)] diff --git a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs index 12249cb..6eb02a6 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs @@ -26,10 +26,9 @@ pub extern "C" fn kmain() -> ! { bkpt_from_t32(); } - // this should be impossible because returning from the fault handler will - // immediately trigger the fault again. + println!("Recovered from fault OK!"); - unreachable!("should never be here!"); + semihosting::process::exit(0); } // These functions are written in assembly @@ -76,12 +75,24 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { ); } - if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { - // we've faulted twice - time to quit - semihosting::process::exit(0); + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 2 + } + _ => { + // we've faulted thrice - time to quit + panic!("_prefetch_handler called too often"); + } } - - addr } #[unsafe(no_mangle)] diff --git a/examples/mps3-an536/src/bin/undef-exception-a32.rs b/examples/mps3-an536/src/bin/undef-exception-a32.rs index ff672f6..5862559 100644 --- a/examples/mps3-an536/src/bin/undef-exception-a32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-a32.rs @@ -19,14 +19,13 @@ pub extern "C" fn kmain() -> ! { println!("Hello, this is a undef exception example"); unsafe { - // trigger an Undefined exception, from A32 (Thumb) mode + // trigger an Undefined exception, from A32 (Arm) mode udf_from_a32(); } - // this should be impossible because returning from the fault handler will - // immediately trigger the fault again. + println!("Recovered from fault OK!"); - unreachable!("should never be here!"); + semihosting::process::exit(0); } // These functions are written in assembly @@ -57,9 +56,6 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> 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!( @@ -68,13 +64,24 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { ); } - if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { - // we've faulted twice - time to quit - semihosting::process::exit(0); + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 4 + } + _ => { + // we've faulted thrice - time to quit + panic!("_undefined_handler called too often"); + } } - - // otherwise go back to where we came from - addr } #[unsafe(no_mangle)] diff --git a/examples/mps3-an536/src/bin/undef-exception-t32.rs b/examples/mps3-an536/src/bin/undef-exception-t32.rs index 1cd70fd..fe6e24f 100644 --- a/examples/mps3-an536/src/bin/undef-exception-t32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-t32.rs @@ -23,10 +23,9 @@ pub extern "C" fn kmain() -> ! { udf_from_t32(); } - // this should be impossible because returning from the fault handler will - // immediately trigger the fault again. + println!("Recovered from fault OK!"); - unreachable!("should never be here!"); + semihosting::process::exit(0); } // These functions are written in assembly @@ -68,12 +67,24 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { ); } - if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { - // we've faulted twice - time to quit - semihosting::process::exit(0); + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 2 + } + _ => { + // we've faulted thrice - time to quit + panic!("_undefined_handler called too often"); + } } - - addr } #[unsafe(no_mangle)] diff --git a/examples/versatileab/reference/abt-exception-armv7r-none-eabihf.out b/examples/versatileab/reference/abt-exception-a32-armv7a-none-eabi.out similarity index 56% rename from examples/versatileab/reference/abt-exception-armv7r-none-eabihf.out rename to examples/versatileab/reference/abt-exception-a32-armv7a-none-eabi.out index 703128d..8b21bec 100644 --- a/examples/versatileab/reference/abt-exception-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/abt-exception-a32-armv7a-none-eabi.out @@ -2,8 +2,15 @@ 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) +DFAR (Faulting Address Register): Dfar(39853) +caught unaligned_from_a32 +caught fault on COUNTER +Doing it again 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) +DFAR (Faulting Address Register): Dfar(39853) +caught unaligned_from_a32 +caught fault on COUNTER +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/abt-exception-armv7a-none-eabi.out b/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabi.out similarity index 56% rename from examples/versatileab/reference/abt-exception-armv7a-none-eabi.out rename to examples/versatileab/reference/abt-exception-a32-armv7r-none-eabi.out index 703128d..afb7abc 100644 --- a/examples/versatileab/reference/abt-exception-armv7a-none-eabi.out +++ b/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabi.out @@ -2,8 +2,15 @@ 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) +DFAR (Faulting Address Register): Dfar(39765) +caught unaligned_from_a32 +caught fault on COUNTER +Doing it again 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) +DFAR (Faulting Address Register): Dfar(39765) +caught unaligned_from_a32 +caught fault on COUNTER +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/abt-exception-armv7r-none-eabi.out b/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabihf.out similarity index 56% rename from examples/versatileab/reference/abt-exception-armv7r-none-eabi.out rename to examples/versatileab/reference/abt-exception-a32-armv7r-none-eabihf.out index 703128d..0d09997 100644 --- a/examples/versatileab/reference/abt-exception-armv7r-none-eabi.out +++ b/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabihf.out @@ -2,8 +2,15 @@ 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) +DFAR (Faulting Address Register): Dfar(39849) +caught unaligned_from_a32 +caught fault on COUNTER +Doing it again 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) +DFAR (Faulting Address Register): Dfar(39849) +caught unaligned_from_a32 +caught fault on COUNTER +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/abt-exception-t32-armv7a-none-eabi.out b/examples/versatileab/reference/abt-exception-t32-armv7a-none-eabi.out new file mode 100644 index 0000000..b54a947 --- /dev/null +++ b/examples/versatileab/reference/abt-exception-t32-armv7a-none-eabi.out @@ -0,0 +1,16 @@ +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(39849) +caught unaligned_from_t32 +caught fault on COUNTER +Doing it again +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(39849) +caught unaligned_from_t32 +caught fault on COUNTER +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabi.out b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabi.out new file mode 100644 index 0000000..1f6cf81 --- /dev/null +++ b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabi.out @@ -0,0 +1,16 @@ +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(39761) +caught unaligned_from_t32 +caught fault on COUNTER +Doing it again +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(39761) +caught unaligned_from_t32 +caught fault on COUNTER +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabihf.out b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabihf.out new file mode 100644 index 0000000..808e878 --- /dev/null +++ b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabihf.out @@ -0,0 +1,16 @@ +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(39845) +caught unaligned_from_t32 +caught fault on COUNTER +Doing it again +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(39845) +caught unaligned_from_t32 +caught fault on COUNTER +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out b/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out index 5aff8c4..e013c87 100644 --- a/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out +++ b/examples/versatileab/reference/prefetch-exception-a32-armv7a-none-eabi.out @@ -4,8 +4,11 @@ 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 +Doing it again 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 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out index 5aff8c4..e013c87 100644 --- a/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out +++ b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabi.out @@ -4,8 +4,11 @@ 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 +Doing it again 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 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out index 5aff8c4..e013c87 100644 --- a/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/prefetch-exception-a32-armv7r-none-eabihf.out @@ -4,8 +4,11 @@ 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 +Doing it again 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 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out b/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out index dc73215..fd0da4c 100644 --- a/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out +++ b/examples/versatileab/reference/prefetch-exception-t32-armv7a-none-eabi.out @@ -4,8 +4,11 @@ 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 +Doing it again 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 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out index dc73215..fd0da4c 100644 --- a/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out +++ b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabi.out @@ -4,8 +4,11 @@ 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 +Doing it again 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 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out index dc73215..fd0da4c 100644 --- a/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/prefetch-exception-t32-armv7r-none-eabihf.out @@ -4,8 +4,11 @@ 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 +Doing it again 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 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/undef-exception-a32-armv7a-none-eabi.out b/examples/versatileab/reference/undef-exception-a32-armv7a-none-eabi.out index a176992..dbafb84 100644 --- a/examples/versatileab/reference/undef-exception-a32-armv7a-none-eabi.out +++ b/examples/versatileab/reference/undef-exception-a32-armv7a-none-eabi.out @@ -1,5 +1,8 @@ Hello, this is a undef exception example undefined abort occurred caught udf_from_a32 +Doing it again undefined abort occurred caught udf_from_a32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabi.out b/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabi.out index a176992..dbafb84 100644 --- a/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabi.out +++ b/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabi.out @@ -1,5 +1,8 @@ Hello, this is a undef exception example undefined abort occurred caught udf_from_a32 +Doing it again undefined abort occurred caught udf_from_a32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabihf.out b/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabihf.out index a176992..dbafb84 100644 --- a/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/undef-exception-a32-armv7r-none-eabihf.out @@ -1,5 +1,8 @@ Hello, this is a undef exception example undefined abort occurred caught udf_from_a32 +Doing it again undefined abort occurred caught udf_from_a32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/undef-exception-t32-armv7a-none-eabi.out b/examples/versatileab/reference/undef-exception-t32-armv7a-none-eabi.out index f15c47e..083a29c 100644 --- a/examples/versatileab/reference/undef-exception-t32-armv7a-none-eabi.out +++ b/examples/versatileab/reference/undef-exception-t32-armv7a-none-eabi.out @@ -1,5 +1,8 @@ Hello, this is a undef exception example undefined abort occurred caught udf_from_t32 +Doing it again undefined abort occurred caught udf_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabi.out b/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabi.out index f15c47e..083a29c 100644 --- a/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabi.out +++ b/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabi.out @@ -1,5 +1,8 @@ Hello, this is a undef exception example undefined abort occurred caught udf_from_t32 +Doing it again undefined abort occurred caught udf_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabihf.out b/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabihf.out index f15c47e..083a29c 100644 --- a/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/undef-exception-t32-armv7r-none-eabihf.out @@ -1,5 +1,8 @@ Hello, this is a undef exception example undefined abort occurred caught udf_from_t32 +Doing it again undefined abort occurred caught udf_from_t32 +Skipping instruction +Recovered from fault OK! diff --git a/examples/versatileab/src/bin/abt-exception.rs b/examples/versatileab/src/bin/abt-exception-a32.rs similarity index 50% rename from examples/versatileab/src/bin/abt-exception.rs rename to examples/versatileab/src/bin/abt-exception-a32.rs index ab03ecd..8aed329 100644 --- a/examples/versatileab/src/bin/abt-exception.rs +++ b/examples/versatileab/src/bin/abt-exception-a32.rs @@ -3,7 +3,7 @@ #![no_std] #![no_main] -use core::sync::atomic::AtomicU32; +use core::sync::atomic::{AtomicU32, Ordering}; use cortex_ar::register::{Dfar, Dfsr, Sctlr}; // pull in our start-up code @@ -11,6 +11,7 @@ use versatileab as _; use semihosting::println; +#[no_mangle] static COUNTER: AtomicU32 = AtomicU32::new(0); /// The entry-point to the Rust application. @@ -30,19 +31,36 @@ fn main() -> ! { 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 - ); + // Unaligned read + unaligned_from_a32(); } - unreachable!("should never be here!"); + println!("Recovered from fault OK!"); + + semihosting::process::exit(0); +} + +// These functions are written in assembly +extern "C" { + fn unaligned_from_a32(); } +core::arch::global_asm!( + r#" + // fn unaligned_from_a32(); + .arm + .global unaligned_from_a32 + .type unaligned_from_a32, %function + unaligned_from_a32: + ldr r0, =COUNTER + add r0, r0, 1 + ldr r0, [r0] + bx lr + .size unaligned_from_a32, . - unaligned_from_a32 +"# +); + fn enable_alignment_check() { let mut sctrl = Sctlr::read(); sctrl.set_a(true); @@ -68,20 +86,56 @@ unsafe extern "C" fn _prefetch_handler(_addr: u32) -> ! { #[unsafe(no_mangle)] unsafe extern "C" fn _abort_handler(addr: usize) -> usize { println!("data abort occurred"); - let dfsr = Dfsr::read(); - println!("DFSR (Fault Status Register): {:?}", dfsr); - println!("DFSR Status: {:?}", dfsr.status()); // 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); + + // note the fault isn't at the start of the function + let expect_fault_at = unaligned_from_a32 as usize + 8; + + if addr == expect_fault_at { + println!("caught unaligned_from_a32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, expect_fault_at + ); } - addr + let expect_fault_from = core::ptr::addr_of!(COUNTER) as usize + 1; + + if dfar.0 as usize == expect_fault_from { + println!("caught fault on COUNTER"); + } else { + println!( + "Bad DFAR address {:08x} is not {:08x}", + dfar.0, expect_fault_from + ); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 4 + } + _ => { + // we've faulted thrice - time to quit + println!("We triple faulted"); + semihosting::process::abort(); + } + } } diff --git a/examples/versatileab/src/bin/abt-exception-t32.rs b/examples/versatileab/src/bin/abt-exception-t32.rs new file mode 100644 index 0000000..79e103f --- /dev/null +++ b/examples/versatileab/src/bin/abt-exception-t32.rs @@ -0,0 +1,141 @@ +//! Example triggering an data abort exception. + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use cortex_ar::register::{Dfar, Dfsr, Sctlr}; +// pull in our start-up code +use versatileab as _; + +use semihosting::println; + +#[no_mangle] +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"); + unsafe { + // Unaligned read + unaligned_from_t32(); + } + + println!("Recovered from fault OK!"); + + semihosting::process::exit(0); +} + +// These functions are written in assembly +extern "C" { + fn unaligned_from_t32(); +} + +core::arch::global_asm!( + r#" + // fn unaligned_from_t32(); + .thumb + .global unaligned_from_t32 + .type unaligned_from_t32, %function + unaligned_from_t32: + ldr r0, =COUNTER + add r0, r0, 1 + ldr r0, [r0] + bx lr + .size unaligned_from_t32, . - unaligned_from_t32 +"# +); + +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: 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. + 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(); + + // note the fault isn't at the start of the function + let expect_fault_at = unaligned_from_t32 as usize + 5; + + if addr == expect_fault_at { + println!("caught unaligned_from_t32"); + } else { + println!( + "Bad fault address {:08x} is not {:08x}", + addr, expect_fault_at + ); + } + + let expect_fault_from = core::ptr::addr_of!(COUNTER) as usize + 1; + + if dfar.0 as usize == expect_fault_from { + println!("caught fault on COUNTER"); + } else { + println!( + "Bad DFAR address {:08x} is not {:08x}", + dfar.0, expect_fault_from + ); + } + + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 2 + } + _ => { + // we've faulted thrice - time to quit + println!("We triple faulted"); + semihosting::process::abort(); + } + } +} diff --git a/examples/versatileab/src/bin/prefetch-exception-a32.rs b/examples/versatileab/src/bin/prefetch-exception-a32.rs index d773dd3..7865d5c 100644 --- a/examples/versatileab/src/bin/prefetch-exception-a32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-a32.rs @@ -26,10 +26,9 @@ pub extern "C" fn kmain() -> ! { bkpt_from_a32(); } - // this should be impossible because returning from the fault handler will - // immediately trigger the fault again. + println!("Recovered from fault OK!"); - unreachable!("should never be here!"); + semihosting::process::exit(0); } // These functions are written in assembly @@ -73,12 +72,24 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { ); } - if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { - // we've faulted twice - time to quit - semihosting::process::exit(0); + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 4 + } + _ => { + // we've faulted thrice - time to quit + panic!("_prefetch_handler called too often"); + } } - - addr } #[unsafe(no_mangle)] diff --git a/examples/versatileab/src/bin/prefetch-exception-t32.rs b/examples/versatileab/src/bin/prefetch-exception-t32.rs index acea6c8..e4303a7 100644 --- a/examples/versatileab/src/bin/prefetch-exception-t32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-t32.rs @@ -26,10 +26,9 @@ pub extern "C" fn kmain() -> ! { bkpt_from_t32(); } - // this should be impossible because returning from the fault handler will - // immediately trigger the fault again. + println!("Recovered from fault OK!"); - unreachable!("should never be here!"); + semihosting::process::exit(0); } // These functions are written in assembly @@ -76,12 +75,24 @@ unsafe extern "C" fn _prefetch_handler(addr: usize) -> usize { ); } - if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { - // we've faulted twice - time to quit - semihosting::process::exit(0); + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 2 + } + _ => { + // we've faulted thrice - time to quit + panic!("_prefetch_handler called too often"); + } } - - addr } #[unsafe(no_mangle)] diff --git a/examples/versatileab/src/bin/undef-exception-a32.rs b/examples/versatileab/src/bin/undef-exception-a32.rs index 6679d2d..dc90e31 100644 --- a/examples/versatileab/src/bin/undef-exception-a32.rs +++ b/examples/versatileab/src/bin/undef-exception-a32.rs @@ -19,14 +19,13 @@ pub extern "C" fn kmain() -> ! { println!("Hello, this is a undef exception example"); unsafe { - // trigger an Undefined exception, from A32 (Thumb) mode + // trigger an Undefined exception, from A32 (Arm) mode udf_from_a32(); } - // this should be impossible because returning from the fault handler will - // immediately trigger the fault again. + println!("Recovered from fault OK!"); - unreachable!("should never be here!"); + semihosting::process::exit(0); } // These functions are written in assembly @@ -57,9 +56,6 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> 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!( @@ -68,12 +64,24 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { ); } - if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { - // we've faulted twice - time to quit - semihosting::process::exit(0); + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 4 + } + _ => { + // we've faulted thrice - time to quit + panic!("_undefined_handler called too often"); + } } - - addr } #[unsafe(no_mangle)] diff --git a/examples/versatileab/src/bin/undef-exception-t32.rs b/examples/versatileab/src/bin/undef-exception-t32.rs index c9869b4..8c8ac68 100644 --- a/examples/versatileab/src/bin/undef-exception-t32.rs +++ b/examples/versatileab/src/bin/undef-exception-t32.rs @@ -23,10 +23,9 @@ pub extern "C" fn kmain() -> ! { udf_from_t32(); } - // this should be impossible because returning from the fault handler will - // immediately trigger the fault again. + println!("Recovered from fault OK!"); - unreachable!("should never be here!"); + semihosting::process::exit(0); } // These functions are written in assembly @@ -68,12 +67,24 @@ unsafe extern "C" fn _undefined_handler(addr: usize) -> usize { ); } - if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 { - // we've faulted twice - time to quit - semihosting::process::exit(0); + match COUNTER.fetch_add(1, Ordering::Relaxed) { + 0 => { + // first time, huh? + // go back and do it again + println!("Doing it again"); + addr + } + 1 => { + // second time, huh? + // go back but skip the instruction + println!("Skipping instruction"); + addr + 2 + } + _ => { + // we've faulted thrice - time to quit + panic!("_undefined_handler called too often"); + } } - - addr } #[unsafe(no_mangle)] From 68766498e6d52844eeb202bff08bc44b0187d255 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Thu, 17 Apr 2025 13:10:07 +0100 Subject: [PATCH 2/4] Don't print the DFAR because the variable can move. The linker doesn't give a consistent address for the value used for the abort test, so check the address but don't print the address. --- .../reference/abt-exception-a32-armv8r-none-eabihf.out | 2 -- .../reference/abt-exception-t32-armv8r-none-eabihf.out | 2 -- examples/mps3-an536/src/bin/abt-exception-a32.rs | 1 - examples/mps3-an536/src/bin/abt-exception-t32.rs | 1 - .../reference/abt-exception-a32-armv7a-none-eabi.out | 2 -- .../reference/abt-exception-a32-armv7r-none-eabi.out | 2 -- .../reference/abt-exception-a32-armv7r-none-eabihf.out | 2 -- .../reference/abt-exception-t32-armv7a-none-eabi.out | 2 -- .../reference/abt-exception-t32-armv7r-none-eabi.out | 2 -- .../reference/abt-exception-t32-armv7r-none-eabihf.out | 2 -- examples/versatileab/src/bin/abt-exception-a32.rs | 1 - examples/versatileab/src/bin/abt-exception-t32.rs | 1 - 12 files changed, 20 deletions(-) diff --git a/examples/mps3-an536/reference/abt-exception-a32-armv8r-none-eabihf.out b/examples/mps3-an536/reference/abt-exception-a32-armv8r-none-eabihf.out index 51e25aa..d521f0b 100644 --- a/examples/mps3-an536/reference/abt-exception-a32-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/abt-exception-a32-armv8r-none-eabihf.out @@ -2,14 +2,12 @@ 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(536870913) caught unaligned_from_a32 caught fault on COUNTER Doing it again 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(536870913) caught unaligned_from_a32 caught fault on COUNTER Skipping instruction diff --git a/examples/mps3-an536/reference/abt-exception-t32-armv8r-none-eabihf.out b/examples/mps3-an536/reference/abt-exception-t32-armv8r-none-eabihf.out index c2b98b6..0067a8b 100644 --- a/examples/mps3-an536/reference/abt-exception-t32-armv8r-none-eabihf.out +++ b/examples/mps3-an536/reference/abt-exception-t32-armv8r-none-eabihf.out @@ -2,14 +2,12 @@ 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(536870913) caught unaligned_from_t32 caught fault on COUNTER Doing it again 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(536870913) caught unaligned_from_t32 caught fault on COUNTER Skipping instruction diff --git a/examples/mps3-an536/src/bin/abt-exception-a32.rs b/examples/mps3-an536/src/bin/abt-exception-a32.rs index 0763bbd..3432e65 100644 --- a/examples/mps3-an536/src/bin/abt-exception-a32.rs +++ b/examples/mps3-an536/src/bin/abt-exception-a32.rs @@ -93,7 +93,6 @@ unsafe extern "C" fn _abort_handler(addr: usize) -> usize { println!("DFSR (Fault Status Register): {:?}", dfsr); println!("DFSR Status: {:?}", dfsr.status()); let dfar = Dfar::read(); - println!("DFAR (Faulting Address Register): {:?}", dfar); enable_alignment_check(); // note the fault isn't at the start of the function diff --git a/examples/mps3-an536/src/bin/abt-exception-t32.rs b/examples/mps3-an536/src/bin/abt-exception-t32.rs index f464f64..4048e42 100644 --- a/examples/mps3-an536/src/bin/abt-exception-t32.rs +++ b/examples/mps3-an536/src/bin/abt-exception-t32.rs @@ -93,7 +93,6 @@ unsafe extern "C" fn _abort_handler(addr: usize) -> usize { println!("DFSR (Fault Status Register): {:?}", dfsr); println!("DFSR Status: {:?}", dfsr.status()); let dfar = Dfar::read(); - println!("DFAR (Faulting Address Register): {:?}", dfar); enable_alignment_check(); // note the fault isn't at the start of the function diff --git a/examples/versatileab/reference/abt-exception-a32-armv7a-none-eabi.out b/examples/versatileab/reference/abt-exception-a32-armv7a-none-eabi.out index 8b21bec..a41df9a 100644 --- a/examples/versatileab/reference/abt-exception-a32-armv7a-none-eabi.out +++ b/examples/versatileab/reference/abt-exception-a32-armv7a-none-eabi.out @@ -2,14 +2,12 @@ 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(39853) caught unaligned_from_a32 caught fault on COUNTER Doing it again 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(39853) caught unaligned_from_a32 caught fault on COUNTER Skipping instruction diff --git a/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabi.out b/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabi.out index afb7abc..a41df9a 100644 --- a/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabi.out +++ b/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabi.out @@ -2,14 +2,12 @@ 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(39765) caught unaligned_from_a32 caught fault on COUNTER Doing it again 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(39765) caught unaligned_from_a32 caught fault on COUNTER Skipping instruction diff --git a/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabihf.out b/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabihf.out index 0d09997..a41df9a 100644 --- a/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/abt-exception-a32-armv7r-none-eabihf.out @@ -2,14 +2,12 @@ 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(39849) caught unaligned_from_a32 caught fault on COUNTER Doing it again 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(39849) caught unaligned_from_a32 caught fault on COUNTER Skipping instruction diff --git a/examples/versatileab/reference/abt-exception-t32-armv7a-none-eabi.out b/examples/versatileab/reference/abt-exception-t32-armv7a-none-eabi.out index b54a947..35dff8b 100644 --- a/examples/versatileab/reference/abt-exception-t32-armv7a-none-eabi.out +++ b/examples/versatileab/reference/abt-exception-t32-armv7a-none-eabi.out @@ -2,14 +2,12 @@ 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(39849) caught unaligned_from_t32 caught fault on COUNTER Doing it again 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(39849) caught unaligned_from_t32 caught fault on COUNTER Skipping instruction diff --git a/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabi.out b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabi.out index 1f6cf81..35dff8b 100644 --- a/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabi.out +++ b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabi.out @@ -2,14 +2,12 @@ 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(39761) caught unaligned_from_t32 caught fault on COUNTER Doing it again 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(39761) caught unaligned_from_t32 caught fault on COUNTER Skipping instruction diff --git a/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabihf.out b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabihf.out index 808e878..35dff8b 100644 --- a/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabihf.out +++ b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabihf.out @@ -2,14 +2,12 @@ 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(39845) caught unaligned_from_t32 caught fault on COUNTER Doing it again 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(39845) caught unaligned_from_t32 caught fault on COUNTER Skipping instruction diff --git a/examples/versatileab/src/bin/abt-exception-a32.rs b/examples/versatileab/src/bin/abt-exception-a32.rs index 8aed329..6a31e9c 100644 --- a/examples/versatileab/src/bin/abt-exception-a32.rs +++ b/examples/versatileab/src/bin/abt-exception-a32.rs @@ -93,7 +93,6 @@ unsafe extern "C" fn _abort_handler(addr: usize) -> usize { println!("DFSR (Fault Status Register): {:?}", dfsr); println!("DFSR Status: {:?}", dfsr.status()); let dfar = Dfar::read(); - println!("DFAR (Faulting Address Register): {:?}", dfar); enable_alignment_check(); // note the fault isn't at the start of the function diff --git a/examples/versatileab/src/bin/abt-exception-t32.rs b/examples/versatileab/src/bin/abt-exception-t32.rs index 79e103f..cf5dce3 100644 --- a/examples/versatileab/src/bin/abt-exception-t32.rs +++ b/examples/versatileab/src/bin/abt-exception-t32.rs @@ -93,7 +93,6 @@ unsafe extern "C" fn _abort_handler(addr: usize) -> usize { println!("DFSR (Fault Status Register): {:?}", dfsr); println!("DFSR Status: {:?}", dfsr.status()); let dfar = Dfar::read(); - println!("DFAR (Faulting Address Register): {:?}", dfar); enable_alignment_check(); // note the fault isn't at the start of the function From 798037dae62a2220de8f05ab0ce4eb214a25c514 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 26 Apr 2025 08:35:03 +0100 Subject: [PATCH 3/4] Fixup comments in asm handlers --- cortex-a-rt/src/lib.rs | 20 ++++++++++---------- cortex-r-rt/src/lib.rs | 16 ++++++++-------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cortex-a-rt/src/lib.rs b/cortex-a-rt/src/lib.rs index cfbd1ad..c084f1e 100644 --- a/cortex-a-rt/src/lib.rs +++ b/cortex-a-rt/src/lib.rs @@ -447,7 +447,7 @@ core::arch::global_asm!( _asm_default_undefined_handler: // state save from compiled code srsfd sp!, {und_mode} - // to work out what mode we're in, we need R0 + // to work out what mode we're in, we need R0, so save it 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. @@ -459,7 +459,7 @@ core::arch::global_asm!( ite eq subeq lr, lr, #4 subne lr, lr, #2 - // now do our standard exception save + // now do our standard exception save (which saves the 'wrong' R0) "#, save_context!(), r#" @@ -469,15 +469,15 @@ core::arch::global_asm!( bl _undefined_handler // if we get back here, assume they returned a new LR in r0 mov lr, r0 - // do our standard restore + // do our standard restore (with the 'wrong' R0) "#, restore_context!(), r#" - // get our real saved R0 + // get the R0 we saved early pop {{r0}} - // overwrite the saved LR with the adjusted one + // overwrite the saved LR with the one from the C handler str lr, [sp] - // Return to the failing instruction which is the recommended approach by ARM. + // Return from the asm handler rfefd sp! .size _asm_default_undefined_handler, . - _asm_default_undefined_handler @@ -533,9 +533,9 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // overwrite the saved LR with the adjusted one + // overwrite the saved LR with the one from the C handler str lr, [sp] - // Return to the failing instruction which is the recommended approach by ARM. + // Return from the asm handler rfefd sp! .size _asm_default_abort_handler, . - _asm_default_abort_handler @@ -564,9 +564,9 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // overwrite the saved LR with the adjusted one + // overwrite the saved LR with the one from the C handler str lr, [sp] - // Return to the failing instruction which is the recommended approach by ARM. + // Return from the asm handler rfefd sp! .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler diff --git a/cortex-r-rt/src/lib.rs b/cortex-r-rt/src/lib.rs index a19515a..7b5808e 100644 --- a/cortex-r-rt/src/lib.rs +++ b/cortex-r-rt/src/lib.rs @@ -419,15 +419,15 @@ core::arch::global_asm!( bl _undefined_handler // if we get back here, assume they returned a new LR in r0 mov lr, r0 - // do our standard restore + // do our standard restore (with the 'wrong' R0) "#, restore_context!(), r#" - // get our real saved R0 + // get the R0 we saved early pop {{r0}} - // overwrite the saved LR with the adjusted one + // overwrite the saved LR with the one from the C handler str lr, [sp] - // Return to the failing instruction which is the recommended approach by ARM. + // Return from the asm handler rfefd sp! .size _asm_default_undefined_handler, . - _asm_default_undefined_handler @@ -483,9 +483,9 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // overwrite the saved LR with the adjusted one + // overwrite the saved LR with the one from the C handler str lr, [sp] - // Return to the failing instruction which is the recommended approach by ARM. + // Return from the asm handler rfefd sp! .size _asm_default_abort_handler, . - _asm_default_abort_handler @@ -514,9 +514,9 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // overwrite the saved LR with the adjusted one + // overwrite the saved LR with the one from the C handler str lr, [sp] - // Return to the failing instruction which is the recommended approach by ARM. + // Return from the asm handler rfefd sp! .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler From 104fe1d34f3c979ccd802b23fe13fa6a610c33cf Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 26 Apr 2025 08:35:09 +0100 Subject: [PATCH 4/4] Whitespace --- cortex-r-rt/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cortex-r-rt/src/lib.rs b/cortex-r-rt/src/lib.rs index 7b5808e..b064856 100644 --- a/cortex-r-rt/src/lib.rs +++ b/cortex-r-rt/src/lib.rs @@ -409,7 +409,7 @@ core::arch::global_asm!( ite eq subeq lr, lr, #4 subne lr, lr, #2 - // now do our standard exception save + // now do our standard exception save (which saves the 'wrong' R0) "#, save_context!(), r#" @@ -558,8 +558,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() }, );