Skip to content

Commit 16ac516

Browse files
Add extra exception tests for Cortex-R52
I had to move the DFSR read to after the alignment check was disabled, otherwise it double-faulted. Also it turns out the versatileab tests were running with the Cortex-A linker script not the Cortex-R linker script, and the Cortex-R linker script had a bug in it that we previously missed (it wasn't calling the new exception handlers).
1 parent 59e5b64 commit 16ac516

11 files changed

+464
-3
lines changed

cortex-r-rt/link.x

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,13 @@ ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of ABT stack is not 8
9292
ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of UND stack is not 8-byte aligned");
9393
ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of SVC stack is not 8-byte aligned");
9494

95-
PROVIDE(_asm_undefined_handler =_asm_default_handler);
96-
PROVIDE(_asm_prefetch_handler =_asm_default_handler);
97-
PROVIDE(_asm_abort_handler =_asm_default_handler);
95+
/* Weak aliases for ASM default handlers */
96+
PROVIDE(_asm_undefined_handler =_asm_default_undefined_handler);
97+
PROVIDE(_asm_prefetch_handler =_asm_default_prefetch_handler);
98+
PROVIDE(_asm_abort_handler =_asm_default_abort_handler);
9899
PROVIDE(_asm_fiq_handler =_asm_default_fiq_handler);
99100

101+
/* Weak aliases for C default handlers */
100102
PROVIDE(_undefined_handler =_default_handler);
101103
PROVIDE(_abort_handler =_default_handler);
102104
PROVIDE(_prefetch_handler =_default_handler);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Hello, this is an data abort exception example
2+
data abort occurred
3+
DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0010 Status=0b00001 }
4+
DFSR Status: Ok(AlignmentFault)
5+
DFAR (Faulting Address Register): Dfar(4097)
6+
data abort occurred
7+
DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0010 Status=0b00001 }
8+
DFSR Status: Ok(AlignmentFault)
9+
DFAR (Faulting Address Register): Dfar(4097)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Hello, this is a prefetch exception example
2+
prefetch abort occurred
3+
IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 }
4+
IFSR Status: Ok(DebugEvent)
5+
IFAR (Faulting Address Register): Ifar(0)
6+
caught bkpt_from_a32
7+
prefetch abort occurred
8+
IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 }
9+
IFSR Status: Ok(DebugEvent)
10+
IFAR (Faulting Address Register): Ifar(0)
11+
caught bkpt_from_a32
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Hello, this is a prefetch exception example
2+
prefetch abort occurred
3+
IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 }
4+
IFSR Status: Ok(DebugEvent)
5+
IFAR (Faulting Address Register): Ifar(0)
6+
caught bkpt_from_t32
7+
prefetch abort occurred
8+
IFSR (Fault Status Register): IFSR { ext=false Domain=0b0010 Status=0b00010 }
9+
IFSR Status: Ok(DebugEvent)
10+
IFAR (Faulting Address Register): Ifar(0)
11+
caught bkpt_from_t32
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Hello, this is a undef exception example
2+
undefined abort occurred
3+
caught udf_from_a32
4+
undefined abort occurred
5+
caught udf_from_a32
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Hello, this is a undef exception example
2+
undefined abort occurred
3+
caught udf_from_t32
4+
undefined abort occurred
5+
caught udf_from_t32
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//! Example triggering an data abort exception.
2+
3+
#![no_std]
4+
#![no_main]
5+
6+
use core::sync::atomic::AtomicU32;
7+
8+
use cortex_ar::register::{Dfar, Dfsr, Sctlr};
9+
// pull in our start-up code
10+
use mps3_an536 as _;
11+
12+
use semihosting::println;
13+
14+
static COUNTER: AtomicU32 = AtomicU32::new(0);
15+
16+
/// The entry-point to the Rust application.
17+
///
18+
/// It is called by the start-up.
19+
#[no_mangle]
20+
pub extern "C" fn kmain() -> ! {
21+
main();
22+
}
23+
24+
/// The main function of our Rust application.
25+
#[export_name = "main"]
26+
#[allow(unreachable_code)]
27+
fn main() -> ! {
28+
// Enable alignment check for Armv7-R. Was not required
29+
// on Cortex-A for some reason, even though the bit was not set.
30+
enable_alignment_check();
31+
32+
println!("Hello, this is an data abort exception example");
33+
// Unaligned read
34+
unsafe {
35+
let addr: *const u32 = 0x1001 as *const u32; // Unaligned address (not 4-byte aligned)
36+
core::arch::asm!(
37+
"ldr r0, [{addr}]", // Attempt unaligned load (should trigger Data Abort)
38+
addr = in(reg) addr, // Pass unaligned pointer
39+
options(nostack, preserves_flags) // No stack usage, preserves flags
40+
);
41+
}
42+
43+
unreachable!("should never be here!");
44+
}
45+
46+
fn enable_alignment_check() {
47+
let mut sctrl = Sctlr::read();
48+
sctrl.set_a(true);
49+
Sctlr::write(sctrl);
50+
}
51+
52+
fn disable_alignment_check() {
53+
let mut sctrl = Sctlr::read();
54+
sctrl.set_a(false);
55+
Sctlr::write(sctrl);
56+
}
57+
58+
#[unsafe(no_mangle)]
59+
unsafe extern "C" fn _undefined_handler(_addr: u32) {
60+
panic!("unexpected undefined exception");
61+
}
62+
63+
#[unsafe(no_mangle)]
64+
unsafe extern "C" fn _prefetch_handler(_addr: u32) {
65+
panic!("unexpected prefetch exception");
66+
}
67+
68+
#[unsafe(no_mangle)]
69+
unsafe extern "C" fn _abort_handler(_addr: u32) {
70+
println!("data abort occurred");
71+
// If this is not disabled, reading DFAR will trigger an alignment fault on Armv8-R, leading
72+
// to a loop.
73+
disable_alignment_check();
74+
let dfsr = Dfsr::read();
75+
println!("DFSR (Fault Status Register): {:?}", dfsr);
76+
println!("DFSR Status: {:?}", dfsr.status());
77+
let dfar = Dfar::read();
78+
println!("DFAR (Faulting Address Register): {:?}", dfar);
79+
enable_alignment_check();
80+
// For the first iteration, we do a regular exception return, which should
81+
// trigger the exception again. The second time around we quit.
82+
if COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) == 1 {
83+
semihosting::process::exit(0);
84+
}
85+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//! Example triggering a prefetch exception.
2+
3+
#![no_std]
4+
#![no_main]
5+
6+
use core::sync::atomic::{AtomicU32, Ordering};
7+
use cortex_ar::register::{Ifar, Ifsr};
8+
use semihosting::println;
9+
10+
// pull in our start-up code
11+
use mps3_an536 as _;
12+
13+
static COUNTER: AtomicU32 = AtomicU32::new(0);
14+
15+
/// The entry-point to the Rust application.
16+
///
17+
/// It is called by the start-up.
18+
#[no_mangle]
19+
pub extern "C" fn kmain() -> ! {
20+
println!("Hello, this is a prefetch exception example");
21+
22+
// A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled.
23+
// See p. 2038 of ARMv7-M Architecture Reference Manual
24+
unsafe {
25+
// trigger an prefetch exception, from A32 (Arm) mode
26+
bkpt_from_a32();
27+
}
28+
29+
// this should be impossible because returning from the fault handler will
30+
// immediately trigger the fault again.
31+
32+
unreachable!("should never be here!");
33+
}
34+
35+
// These functions are written in assembly
36+
extern "C" {
37+
fn bkpt_from_a32();
38+
}
39+
40+
core::arch::global_asm!(
41+
r#"
42+
// fn bkpt_from_a32();
43+
.arm
44+
.global bkpt_from_a32
45+
.type bkpt_from_a32, %function
46+
bkpt_from_a32:
47+
bkpt #0
48+
bx lr
49+
.size bkpt_from_a32, . - bkpt_from_a32
50+
"#
51+
);
52+
53+
#[unsafe(no_mangle)]
54+
unsafe extern "C" fn _undefined_handler(_addr: usize) {
55+
panic!("unexpected undefined exception");
56+
}
57+
58+
#[unsafe(no_mangle)]
59+
unsafe extern "C" fn _prefetch_handler(addr: usize) {
60+
println!("prefetch abort occurred");
61+
let ifsr = Ifsr::read();
62+
println!("IFSR (Fault Status Register): {:?}", ifsr);
63+
println!("IFSR Status: {:?}", ifsr.status());
64+
let ifar = Ifar::read();
65+
println!("IFAR (Faulting Address Register): {:?}", ifar);
66+
67+
if addr == bkpt_from_a32 as usize {
68+
println!("caught bkpt_from_a32");
69+
} else {
70+
println!(
71+
"Bad fault address {:08x} is not {:08x}",
72+
addr, bkpt_from_a32 as usize
73+
);
74+
}
75+
76+
if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 {
77+
// we've faulted twice - time to quit
78+
semihosting::process::exit(0);
79+
}
80+
}
81+
82+
#[unsafe(no_mangle)]
83+
unsafe extern "C" fn _abort_handler(_addr: usize) {
84+
panic!("unexpected abort exception");
85+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//! Example triggering a prefetch exception.
2+
3+
#![no_std]
4+
#![no_main]
5+
6+
use core::sync::atomic::{AtomicU32, Ordering};
7+
use cortex_ar::register::{Ifar, Ifsr};
8+
use semihosting::println;
9+
10+
// pull in our start-up code
11+
use mps3_an536 as _;
12+
13+
static COUNTER: AtomicU32 = AtomicU32::new(0);
14+
15+
/// The entry-point to the Rust application.
16+
///
17+
/// It is called by the start-up.
18+
#[no_mangle]
19+
pub extern "C" fn kmain() -> ! {
20+
println!("Hello, this is a prefetch exception example");
21+
22+
// A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled.
23+
// See p. 2038 of ARMv7-M Architecture Reference Manual
24+
unsafe {
25+
// trigger an prefetch exception, from T32 (Thumb) mode
26+
bkpt_from_t32();
27+
}
28+
29+
// this should be impossible because returning from the fault handler will
30+
// immediately trigger the fault again.
31+
32+
unreachable!("should never be here!");
33+
}
34+
35+
// These functions are written in assembly
36+
extern "C" {
37+
fn bkpt_from_t32();
38+
}
39+
40+
core::arch::global_asm!(
41+
r#"
42+
// fn bkpt_from_t32();
43+
.thumb
44+
.global bkpt_from_t32
45+
.type bkpt_from_t32, %function
46+
bkpt_from_t32:
47+
bkpt #0
48+
bx lr
49+
.size bkpt_from_t32, . - bkpt_from_t32
50+
"#
51+
);
52+
53+
#[unsafe(no_mangle)]
54+
unsafe extern "C" fn _undefined_handler(_addr: usize) {
55+
panic!("unexpected undefined exception");
56+
}
57+
58+
#[unsafe(no_mangle)]
59+
unsafe extern "C" fn _prefetch_handler(addr: usize) {
60+
println!("prefetch abort occurred");
61+
let ifsr = Ifsr::read();
62+
println!("IFSR (Fault Status Register): {:?}", ifsr);
63+
println!("IFSR Status: {:?}", ifsr.status());
64+
let ifar = Ifar::read();
65+
println!("IFAR (Faulting Address Register): {:?}", ifar);
66+
67+
if (addr + 1) == bkpt_from_t32 as usize {
68+
// note that thumb functions have their LSB set, despite always being a
69+
// multiple of two - that's how the CPU knows they are written in T32
70+
// machine code.
71+
println!("caught bkpt_from_t32");
72+
} else {
73+
println!(
74+
"Bad fault address {:08x} is not {:08x}",
75+
addr, bkpt_from_t32 as usize
76+
);
77+
}
78+
79+
if COUNTER.fetch_add(1, Ordering::Relaxed) == 1 {
80+
// we've faulted twice - time to quit
81+
semihosting::process::exit(0);
82+
}
83+
}
84+
85+
#[unsafe(no_mangle)]
86+
unsafe extern "C" fn _abort_handler(_addr: usize) {
87+
panic!("unexpected abort exception");
88+
}

0 commit comments

Comments
 (0)