diff --git a/cortex-a-rt/src/lib.rs b/cortex-a-rt/src/lib.rs index 488697f..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,9 +459,7 @@ 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 + // now do our standard exception save (which saves the 'wrong' R0) "#, save_context!(), r#" @@ -471,17 +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 saved LR - pop {{lr}} - // 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 @@ -537,7 +533,9 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // Return to the failing instruction which is the recommended approach by ARM. + // overwrite the saved LR with the one from the C handler + str lr, [sp] + // Return from the asm handler rfefd sp! .size _asm_default_abort_handler, . - _asm_default_abort_handler @@ -566,7 +564,9 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // Return to the failing instruction which is the recommended approach by ARM. + // overwrite the saved LR with the one from the C handler + str lr, [sp] + // 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 d82f7fc..b064856 100644 --- a/cortex-r-rt/src/lib.rs +++ b/cortex-r-rt/src/lib.rs @@ -409,9 +409,7 @@ 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 + // now do our standard exception save (which saves the 'wrong' R0) "#, save_context!(), r#" @@ -421,17 +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 saved LR - pop {{lr}} - // 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 @@ -487,7 +483,9 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // Return to the failing instruction which is the recommended approach by ARM. + // overwrite the saved LR with the one from the C handler + str lr, [sp] + // Return from the asm handler rfefd sp! .size _asm_default_abort_handler, . - _asm_default_abort_handler @@ -516,7 +514,9 @@ core::arch::global_asm!( "#, restore_context!(), r#" - // Return to the failing instruction which is the recommended approach by ARM. + // overwrite the saved LR with the one from the C handler + str lr, [sp] + // Return from the asm handler rfefd sp! .size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler @@ -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() }, ); 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 67% 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..d521f0b 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,13 @@ 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) +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) +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..0067a8b --- /dev/null +++ b/examples/mps3-an536/reference/abt-exception-t32-armv8r-none-eabihf.out @@ -0,0 +1,14 @@ +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) +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) +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-a32.rs b/examples/mps3-an536/src/bin/abt-exception-a32.rs new file mode 100644 index 0000000..3432e65 --- /dev/null +++ b/examples/mps3-an536/src/bin/abt-exception-a32.rs @@ -0,0 +1,140 @@ +//! 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_a32(); + } + + 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); + 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(); + enable_alignment_check(); + + // 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 + ); + } + + 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..4048e42 --- /dev/null +++ b/examples/mps3-an536/src/bin/abt-exception-t32.rs @@ -0,0 +1,140 @@ +//! 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(); + 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/abt-exception.rs b/examples/mps3-an536/src/bin/abt-exception.rs deleted file mode 100644 index ddf599c..0000000 --- a/examples/mps3-an536/src/bin/abt-exception.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! 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: 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(); - // 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); - } - - addr -} 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 67% 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..a41df9a 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,13 @@ 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) +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) +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 67% 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..a41df9a 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,13 @@ 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) +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) +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 67% 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..a41df9a 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,13 @@ 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) +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) +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..35dff8b --- /dev/null +++ b/examples/versatileab/reference/abt-exception-t32-armv7a-none-eabi.out @@ -0,0 +1,14 @@ +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) +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) +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..35dff8b --- /dev/null +++ b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabi.out @@ -0,0 +1,14 @@ +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) +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) +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..35dff8b --- /dev/null +++ b/examples/versatileab/reference/abt-exception-t32-armv7r-none-eabihf.out @@ -0,0 +1,14 @@ +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) +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) +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-a32.rs b/examples/versatileab/src/bin/abt-exception-a32.rs new file mode 100644 index 0000000..6a31e9c --- /dev/null +++ b/examples/versatileab/src/bin/abt-exception-a32.rs @@ -0,0 +1,140 @@ +//! 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_a32(); + } + + 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); + 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(); + enable_alignment_check(); + + // 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 + ); + } + + 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..cf5dce3 --- /dev/null +++ b/examples/versatileab/src/bin/abt-exception-t32.rs @@ -0,0 +1,140 @@ +//! 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(); + 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/abt-exception.rs b/examples/versatileab/src/bin/abt-exception.rs deleted file mode 100644 index ab03ecd..0000000 --- a/examples/versatileab/src/bin/abt-exception.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! 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 versatileab 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: 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 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); - } - - addr -} 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)]