Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions cortex-a-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(),
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions cortex-r-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(),
Expand All @@ -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
Expand Down Expand Up @@ -487,6 +483,8 @@ core::arch::global_asm!(
"#,
restore_context!(),
r#"
// overwrite the saved LR with the adjusted one
Copy link
Contributor

@robamu robamu Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I understanding it correctly that the LR returned by the C/Rust handler is stored here, which does not necessarily have to be the adjusted LR?

Maybe "overwrite the saved LR with the one returned by the C handler" is better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, fair, the handler could have but might not have adjusted the value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Original file line number Diff line number Diff line change
@@ -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!
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Original file line number Diff line number Diff line change
@@ -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!
Original file line number Diff line number Diff line change
@@ -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!
140 changes: 140 additions & 0 deletions examples/mps3-an536/src/bin/abt-exception-a32.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Loading