Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
58 changes: 33 additions & 25 deletions cortex-a-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
//!
//! ```rust
//! #[unsafe(no_mangle)]
//! extern "C" fn _undefined_handler(faulting_instruction: u32);
//! extern "C" fn _undefined_handler(addr: usize);
//! ```
//!
//! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception
Expand All @@ -103,7 +103,7 @@
//!
//! ```rust
//! #[unsafe(no_mangle)]
//! extern "C" fn _abort_handler(faulting_instruction: u32);
//! extern "C" fn _abort_handler(addr: usize);
//! ```
//!
//! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception
Expand All @@ -115,7 +115,7 @@
//!
//! ```rust
//! #[unsafe(no_mangle)]
//! extern "C" fn _prefetch_handler(faulting_instruction: u32);
//! extern "C" fn _prefetch_handler(addr: usize);
//! ```
//!
//! ### ASM functions
Expand All @@ -130,7 +130,7 @@
//! * `_asm_undefined_handler` - a naked function to call when an Undefined
//! Exception occurs. Our linker script PROVIDEs a default function at
//! `_asm_default_undefined_handler` but you can override it.
//! * `_asm_prefetch_handler` - a naked function to call when an Prefetch
//! * `_asm_prefetch_handler` - a naked function to call when a Prefetch
//! Exception occurs. Our linker script PROVIDEs a default function at
//! `_asm_default_prefetch_handler` but you can override it. The provided default
//! handler will perform an exception return to the faulting address.
Expand Down Expand Up @@ -381,7 +381,7 @@ core::arch::global_asm!(

// Called from the vector table when we have an software interrupt.
// Saves state and calls a C-compatible handler like
// `extern "C" fn svc_handler(svc: u32, context: *const u32);`
// `extern "C" fn svc_handler(svc: u32);`
.global _asm_svc_handler
.type _asm_svc_handler, %function
_asm_svc_handler:
Expand Down Expand Up @@ -426,44 +426,52 @@ core::arch::global_asm!(

// Called from the vector table when we have an undefined exception.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _undefined_handler();`
// `extern "C" fn _undefined_handler(addr: usize);`
.global _asm_default_undefined_handler
.type _asm_default_undefined_handler, %function
_asm_default_undefined_handler:
// 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.
// Load processor status
mrs r4, cpsr
// Occurred in Thumb state?
tst r4, {t_bit}
// If not in Thumb mode, branch to not_thumb
beq not_thumb
subs lr, lr, #2
b done
not_thumb:
// Subtract 4 from LR (ARM mode)
subs lr, lr, #4
done:
// state save from compiled code
srsfd sp!, {und_mode}
// to work out what mode we're in, we need R0
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.
// Load processor status for the calling code
mrs r0, spsr
// Was the code that triggered the exception in Thumb state?
tst r0, {t_bit}
// Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual.
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!(),
r#"
// Pass the faulting instruction address to the handler.
mov r0, lr
mov r0, lr
// call C handler
bl _undefined_handler
// do our standard restore
"#,
restore_context!(),
r#"
// get our saved LR
pop {{lr}}
Copy link

@chrisnc chrisnc Apr 15, 2025

Choose a reason for hiding this comment

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

This will clobber the lr that was returned by _undefined_handler, right? The advertised API suggests the callee can cause the handler to return to an arbitrary new instruction, and mov lr, r0 and associated comment appear to be trying to support that, but that register move is then discarded here and replaced with the value that was pushed before save_context.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's a bug. I added support for returning a new location because I realised that sometimes you want to emulate the faulting instruction and return to the one after. But I didn't write a test for it and this is what happens when you don't test things. Thank you for checking so diligently!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See #29

// get our real saved R0
pop {{r0}}
// 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_undefined_handler, . - _asm_default_undefined_handler


// Called from the vector table when we have an undefined exception.
// Called from the vector table when we have a prefetch exception.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _prefetch_handler();`
// `extern "C" fn _prefetch_handler(addr: usize);`
.global _asm_default_prefetch_handler
.type _asm_default_prefetch_handler, %function
_asm_default_prefetch_handler:
Expand All @@ -488,7 +496,7 @@ done:

// Called from the vector table when we have an undefined exception.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _abort_handler();`
// `extern "C" fn _abort_handler(addr: usize);`
.global _asm_default_abort_handler
.type _asm_default_abort_handler, %function
_asm_default_abort_handler:
Expand All @@ -500,7 +508,7 @@ done:
save_context!(),
r#"
// Pass the faulting instruction address to the handler.
mov r0, lr
mov r0, lr
// call C handler
bl _abort_handler
"#,
Expand Down
8 changes: 5 additions & 3 deletions cortex-r-rt/link.x
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,13 @@ ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of ABT stack is not 8
ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of UND stack is not 8-byte aligned");
ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-r-rt): size of SVC stack is not 8-byte aligned");

PROVIDE(_asm_undefined_handler =_asm_default_handler);
PROVIDE(_asm_prefetch_handler =_asm_default_handler);
PROVIDE(_asm_abort_handler =_asm_default_handler);
/* Weak aliases for ASM default handlers */
PROVIDE(_asm_undefined_handler =_asm_default_undefined_handler);
PROVIDE(_asm_prefetch_handler =_asm_default_prefetch_handler);
PROVIDE(_asm_abort_handler =_asm_default_abort_handler);
PROVIDE(_asm_fiq_handler =_asm_default_fiq_handler);

/* Weak aliases for C default handlers */
PROVIDE(_undefined_handler =_default_handler);
PROVIDE(_abort_handler =_default_handler);
PROVIDE(_prefetch_handler =_default_handler);
Expand Down
61 changes: 35 additions & 26 deletions cortex-r-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
//!
//! ```rust
//! #[unsafe(no_mangle)]
//! extern "C" fn _undefined_handler(faulting_instruction: u32);
//! extern "C" fn _undefined_handler(addr: usize);
//! ```
//!
//! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception
Expand All @@ -86,7 +86,7 @@
//!
//! ```rust
//! #[unsafe(no_mangle)]
//! extern "C" fn _abort_handler(faulting_instruction: u32);
//! extern "C" fn _abort_handler(addr: usize);
//! ```
//!
//! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception
Expand All @@ -98,7 +98,7 @@
//!
//! ```rust
//! #[unsafe(no_mangle)]
//! extern "C" fn _prefetch_handler(faulting_instruction: u32);
//! extern "C" fn _prefetch_handler(addr: usize);
//! ```
//!
//! ### ASM functions
Expand All @@ -113,7 +113,7 @@
//! * `_asm_undefined_handler` - a naked function to call when an Undefined
//! Exception occurs. Our linker script PROVIDEs a default function at
//! `_asm_default_undefined_handler` but you can override it.
//! * `_asm_prefetch_handler` - a naked function to call when an Prefetch
//! * `_asm_prefetch_handler` - a naked function to call when a Prefetch
//! Exception occurs. Our linker script PROVIDEs a default function at
//! `_asm_default_prefetch_handler` but you can override it. The provided default
//! handler will perform an exception return to the faulting address.
Expand Down Expand Up @@ -313,7 +313,7 @@ core::arch::global_asm!(

// Called from the vector table when we have an software interrupt.
// Saves state and calls a C-compatible handler like
// `extern "C" fn svc_handler(svc: u32, context: *const u32);`
// `extern "C" fn svc_handler(svc: u32);`
.global _asm_svc_handler
.type _asm_svc_handler, %function
_asm_svc_handler:
Expand All @@ -335,6 +335,7 @@ core::arch::global_asm!(
rfefd sp!
.size _asm_svc_handler, . - _asm_svc_handler


// Called from the vector table when we have an interrupt.
// Saves state and calls a C-compatible handler like
// `extern "C" fn irq_handler();`
Expand All @@ -357,56 +358,64 @@ core::arch::global_asm!(

// Called from the vector table when we have an undefined exception.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _undefined_handler();`
// `extern "C" fn _undefined_handler(addr: usize);`
.global _asm_default_undefined_handler
.type _asm_default_undefined_handler, %function
_asm_default_undefined_handler:
// 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.
// Load processor status
mrs r4, cpsr
// Occurred in Thumb state?
tst r4, {t_bit}
// If not in Thumb mode, branch to not_thumb
beq not_thumb
subs lr, lr, #2
b done
not_thumb:
// Subtract 4 from LR (ARM mode)
subs lr, lr, #4
done:
// state save from compiled code
srsfd sp!, {und_mode}
// to work out what mode we're in, we need R0
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.
// Load processor status for the calling code
mrs r0, spsr
// Was the code that triggered the exception in Thumb state?
tst r0, {t_bit}
// Subtract 2 in Thumb Mode, 4 in Arm Mode - see p.1206 of the ARMv7-A architecture manual.
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!(),
r#"
// Pass the faulting instruction address to the handler.
mov r0, lr
// call C handler
bl _undefined_handler
// do our standard restore
"#,
restore_context!(),
r#"
// get our saved LR
pop {{lr}}
// get our real saved R0
pop {{r0}}
// 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_undefined_handler, . - _asm_default_undefined_handler


// Called from the vector table when we have an undefined exception.
// Called from the vector table when we have a prefetch exception.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _prefetch_handler();`
// `extern "C" fn _prefetch_handler(addr: usize);`
.global _asm_default_prefetch_handler
.type _asm_default_prefetch_handler, %function
_asm_default_prefetch_handler:
// Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual.
subs lr, lr, #4
subs lr, lr, #4
// state save from compiled code
srsfd sp!, {abt_mode}
"#,
save_context!(),
r#"
// Pass the faulting instruction address to the handler.
mov r0, lr
mov r0, lr
// call C handler
bl _prefetch_handler
"#,
Expand All @@ -419,12 +428,12 @@ done:

// Called from the vector table when we have an undefined exception.
// Saves state and calls a C-compatible handler like
// `extern "C" fn _abort_handler();`
// `extern "C" fn _abort_handler(addr: usize);`
.global _asm_default_abort_handler
.type _asm_default_abort_handler, %function
_asm_default_abort_handler:
// Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual.
subs lr, lr, #8
subs lr, lr, #8
// state save from compiled code
srsfd sp!, {abt_mode}
"#,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
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)
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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Hello, this is a prefetch exception example
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
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Hello, this is a prefetch exception example
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
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Hello, this is a undef exception example
undefined abort occurred
caught udf_from_a32
undefined abort occurred
caught udf_from_a32
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Hello, this is a undef exception example
undefined abort occurred
caught udf_from_t32
undefined abort occurred
caught udf_from_t32
Loading