Skip to content

Don't trash the LR returned from _undefined_handler. #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 27, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 12 additions & 12 deletions cortex-a-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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#"
Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
26 changes: 13 additions & 13 deletions cortex-r-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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#"
Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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()
},
);

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