Skip to content

Commit f0e4edb

Browse files
Fix the undef handler.
It was looking at CPSR's Thumb bit, which tells you if the *handler* is in thumb mode, not the code that threw the fault. Change the test to validate the address of the failing function, to verify that we've got this right. Also fixes the issue of _asm_default_undefined_handler damaging r4. You have to save all the state first, then you can touch registers.
1 parent d20623c commit f0e4edb

File tree

5 files changed

+84
-56
lines changed

5 files changed

+84
-56
lines changed

cortex-a-rt/src/lib.rs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -430,28 +430,24 @@ core::arch::global_asm!(
430430
.global _asm_default_undefined_handler
431431
.type _asm_default_undefined_handler, %function
432432
_asm_default_undefined_handler:
433-
// First adjust LR for two purposes: Passing the faulting instruction to the C handler,
434-
// and to return to the failing instruction after the C handler returns.
435-
// Load processor status
436-
mrs r4, cpsr
437-
// Occurred in Thumb state?
438-
tst r4, {t_bit}
439-
// If not in Thumb mode, branch to not_thumb
440-
beq not_thumb
441-
subs lr, lr, #2
442-
b done
443-
not_thumb:
444-
// Subtract 4 from LR (ARM mode)
445-
subs lr, lr, #4
446-
done:
447433
// state save from compiled code
448434
srsfd sp!, {und_mode}
449435
"#,
450436
save_context!(),
451437
r#"
438+
// First adjust LR for two purposes: Passing the faulting instruction to the C handler,
439+
// and to return to the failing instruction after the C handler returns.
440+
// Load processor status for the calling code
441+
mrs r4, spsr
442+
// Was the code that triggered the exception in Thumb state?
443+
tst r4, {t_bit}
444+
// Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual.
445+
ite eq
446+
subeq lr, lr, #4
447+
subne lr, lr, #2
452448
// Pass the faulting instruction address to the handler.
453-
mov r0, lr
454-
// call C handler
449+
mov r0, lr
450+
// call C handler like `extern "C" fn _undefined_handler(addr: usize);`
455451
bl _undefined_handler
456452
"#,
457453
restore_context!(),
@@ -461,7 +457,7 @@ done:
461457
.size _asm_default_undefined_handler, . - _asm_default_undefined_handler
462458
463459
464-
// Called from the vector table when we have an undefined exception.
460+
// Called from the vector table when we have an prefetch exception.
465461
// Saves state and calls a C-compatible handler like
466462
// `extern "C" fn _prefetch_handler();`
467463
.global _asm_default_prefetch_handler
@@ -476,7 +472,7 @@ done:
476472
r#"
477473
// Pass the faulting instruction address to the handler.
478474
mov r0, lr
479-
// call C handler
475+
// call C handler like `extern "C" fn _prefetch_handler(addr: usize);`
480476
bl _prefetch_handler
481477
"#,
482478
restore_context!(),
@@ -500,8 +496,8 @@ done:
500496
save_context!(),
501497
r#"
502498
// Pass the faulting instruction address to the handler.
503-
mov r0, lr
504-
// call C handler
499+
mov r0, lr
500+
// call C handler like `extern "C" fn _abort_handler(addr: usize);`
505501
bl _abort_handler
506502
"#,
507503
restore_context!(),

cortex-r-rt/src/lib.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -361,28 +361,24 @@ core::arch::global_asm!(
361361
.global _asm_default_undefined_handler
362362
.type _asm_default_undefined_handler, %function
363363
_asm_default_undefined_handler:
364-
// First adjust LR for two purposes: Passing the faulting instruction to the C handler,
365-
// and to return to the failing instruction after the C handler returns.
366-
// Load processor status
367-
mrs r4, cpsr
368-
// Occurred in Thumb state?
369-
tst r4, {t_bit}
370-
// If not in Thumb mode, branch to not_thumb
371-
beq not_thumb
372-
subs lr, lr, #2
373-
b done
374-
not_thumb:
375-
// Subtract 4 from LR (ARM mode)
376-
subs lr, lr, #4
377-
done:
378364
// state save from compiled code
379365
srsfd sp!, {und_mode}
380366
"#,
381367
save_context!(),
382368
r#"
369+
// First adjust LR for two purposes: Passing the faulting instruction to the C handler,
370+
// and to return to the failing instruction after the C handler returns.
371+
// Load processor status for the calling code
372+
mrs r4, spsr
373+
// Was the code that triggered the exception in Thumb state?
374+
tst r4, {t_bit}
375+
// Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual.
376+
ite eq
377+
subeq lr, lr, #4
378+
subne lr, lr, #2
383379
// Pass the faulting instruction address to the handler.
384380
mov r0, lr
385-
// call C handler
381+
// call C handler like `extern "C" fn _undefined_handler(addr: usize);`
386382
bl _undefined_handler
387383
"#,
388384
restore_context!(),
@@ -392,22 +388,22 @@ done:
392388
.size _asm_default_undefined_handler, . - _asm_default_undefined_handler
393389
394390
395-
// Called from the vector table when we have an undefined exception.
391+
// Called from the vector table when we have an prefetch exception.
396392
// Saves state and calls a C-compatible handler like
397393
// `extern "C" fn _prefetch_handler();`
398394
.global _asm_default_prefetch_handler
399395
.type _asm_default_prefetch_handler, %function
400396
_asm_default_prefetch_handler:
401397
// Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual.
402-
subs lr, lr, #4
398+
subs lr, lr, #4
403399
// state save from compiled code
404400
srsfd sp!, {abt_mode}
405401
"#,
406402
save_context!(),
407403
r#"
408404
// Pass the faulting instruction address to the handler.
409-
mov r0, lr
410-
// call C handler
405+
mov r0, lr
406+
// call C handler like `extern "C" fn _prefetch_handler(addr: usize);`
411407
bl _prefetch_handler
412408
"#,
413409
restore_context!(),
@@ -424,15 +420,15 @@ done:
424420
.type _asm_default_abort_handler, %function
425421
_asm_default_abort_handler:
426422
// Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual.
427-
subs lr, lr, #8
423+
subs lr, lr, #8
428424
// state save from compiled code
429425
srsfd sp!, {abt_mode}
430426
"#,
431427
save_context!(),
432428
r#"
433429
// Pass the faulting instruction address to the handler.
434430
mov r0, lr
435-
// call C handler
431+
// call C handler like `extern "C" fn _abort_handler(addr: usize);`
436432
bl _abort_handler
437433
"#,
438434
restore_context!(),
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
Hello, this is an undefined exception example
2-
undefined exception occurred
3-
undefined exception occurred
2+
caught undef_from_t32
3+
caught undef_from_a32
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
Hello, this is an undefined exception example
2-
undefined exception occurred
3-
undefined exception occurred
2+
caught undef_from_t32
3+
caught undef_from_a32

examples/versatileab/src/bin/undef-exception.rs

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ use versatileab as _;
1010

1111
use semihosting::println;
1212

13+
// These functions are written in assembly
14+
extern "C" {
15+
fn undef_from_a32();
16+
fn undef_from_t32();
17+
}
18+
1319
/// The entry-point to the Rust application.
1420
///
1521
/// It is called by the start-up.
@@ -18,25 +24,55 @@ pub extern "C" fn kmain() -> ! {
1824
main();
1925
}
2026

21-
static COUNTER: AtomicU32 = AtomicU32::new(0);
22-
2327
/// The main function of our Rust application.
2428
#[export_name = "main"]
2529
fn main() -> ! {
2630
println!("Hello, this is an undefined exception example");
2731
unsafe {
28-
core::arch::asm!("udf #0");
32+
// trigger an undefined exception, from T32 (Thumb) mode
33+
undef_from_t32();
34+
// trigger an undefined exception, from A32 (Arm) mode
35+
undef_from_a32();
2936
}
30-
unreachable!("should never be here!");
37+
38+
semihosting::process::exit(0);
3139
}
3240

41+
core::arch::global_asm!(
42+
r#"
43+
// fn undef_from_a32();
44+
.arm
45+
.global undef_from_a32
46+
.type undef_from_a32, %function
47+
undef_from_a32:
48+
udf #0
49+
bx lr
50+
.size undef_from_a32, . - undef_from_a32
51+
52+
// fn undef_from_t32();
53+
.thumb
54+
.global undef_from_t32
55+
.type undef_from_t32, %function
56+
undef_from_t32:
57+
udf #0
58+
bx lr
59+
.size undef_from_t32, . - undef_from_t32
60+
"#
61+
);
62+
3363
#[no_mangle]
34-
unsafe extern "C" fn _undefined_handler(_faulting_instruction: u32) {
35-
println!("undefined exception occurred");
36-
// For the first iteration, we do a regular exception return, which should
37-
// trigger the exception again.
38-
let counter_val = COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed) + 1;
39-
if counter_val == 2 {
40-
semihosting::process::exit(0);
64+
unsafe extern "C" fn _undefined_handler(faulting_instruction: usize) {
65+
if faulting_instruction == undef_from_a32 as usize {
66+
println!("caught undef_from_a32");
67+
} else if (faulting_instruction + 1) == undef_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 undef_from_t32");
72+
} else {
73+
println!(
74+
"Bad fault address {:08x} is not {:08x} or {:08x}",
75+
faulting_instruction, undef_from_a32 as usize, undef_from_t32 as usize
76+
);
4177
}
4278
}

0 commit comments

Comments
 (0)