Skip to content

Commit ca2c26c

Browse files
committed
Add default exception handlers and simplify default handler
- Add three new default assembly exception handlers for data abort, prefetch abort and undefined exception. These handlers implement the ARM exception return model. - The new ASM handlers will call the Rust _default_handler by default, but the Rust handler can be overriden with _undefined_handler, _prefetch_handler and _abort_handler - The default handler is now a simple empty permanent loop
1 parent 42f7cca commit ca2c26c

21 files changed

+738
-53
lines changed

cortex-a-rt/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ version = "0.1.0"
1616

1717
[dependencies]
1818
cortex-ar = {version = "0.1.0", path = "../cortex-ar"}
19-
semihosting = {version = "0.1.18", features = ["stdio"]}
2019

2120
[features]
2221
# Enable the FPU on start-up, even on a soft-float EABI target

cortex-a-rt/link.x

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,16 @@ ASSERT(_abt_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of ABT stack is not 8
9292
ASSERT(_und_stack_size % 8 == 0, "ERROR(cortex-a-rt): size of UND stack is not 8-byte aligned");
9393
ASSERT(_svc_stack_size % 8 == 0, "ERROR(cortex-a-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);
100+
101+
/* Weak aliases for C default handlers */
102+
PROVIDE(_undefined_handler =_default_handler);
103+
PROVIDE(_abort_handler =_default_handler);
104+
PROVIDE(_prefetch_handler =_default_handler);
99105
PROVIDE(_irq_handler =_default_handler);
100106
PROVIDE(_svc_handler =_default_handler);
101107
PROVIDE(_start =_default_start);

cortex-a-rt/src/lib.rs

Lines changed: 190 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Run-time support for Arm Cortex-A
1+
//! # Run-time support for Arm Cortex-A
22
//!
33
//! This library implements a simple Arm vector table, suitable for getting into
44
//! a Rust application running in System Mode. It also provides a reference start up method.
@@ -22,47 +22,116 @@
2222
//!
2323
//! We assume the following global symbols exist:
2424
//!
25-
//! * `__start` - a Reset handler. Our linker script PROVIDEs a default function
26-
//! at `_default_start` but you can override it. Most Cortex-A SoCs require
27-
//! a chip specific startup for tasks like MMU initialization or chip specific
28-
//! initialization routines.
25+
//! ### Constants
26+
//!
2927
//! * `_stack_top` - the address of the top of some region of RAM that we can
3028
//! use as stack space, with eight-byte alignment. Our linker script PROVIDEs
3129
//! a default pointing at the top of RAM.
30+
//! * `__sbss` - the start of zero-initialised data in RAM. Must be 4-byte
31+
//! aligned.
32+
//! * `__ebss` - the end of zero-initialised data in RAM. Must be 4-byte
33+
//! aligned.
3234
//! * `_fiq_stack_size` - the number of bytes to be reserved for stack space
3335
//! when in FIQ mode; must be a multiple of 8.
3436
//! * `_irq_stack_size` - the number of bytes to be reserved for stack space
3537
//! when in FIQ mode; must be a multiple of 8.
3638
//! * `_svc_stack_size` - the number of bytes to be reserved for stack space
37-
//! when in SVC mode; must be a multiple of 8.F
39+
//! when in SVC mode; must be a multiple of 8.
40+
//! * `__sdata` - the start of initialised data in RAM. Must be 4-byte aligned.
41+
//! * `__edata` - the end of initialised data in RAM. Must be 4-byte aligned.
42+
//! * `__sidata` - the start of the initialisation values for data, in read-only
43+
//! memory. Must be 4-byte aligned.
44+
//!
45+
//! ### Functions
46+
//!
47+
//! * `boot_core` - the `extern "C"` entry point to your application. The CPU ID
48+
//! will be passed as the first argument to this function.
49+
//!
50+
//! Expected prototype:
51+
//!
52+
//! ```rust
53+
//! #[unsafe(no_mangle)]
54+
//! extern "C" fn boot_core(cpu_id: u32) -> !;
55+
//! ```
56+
//!
3857
//! * `_svc_handler` - an `extern "C"` function to call when an SVC Exception
3958
//! occurs. Our linker script PROVIDEs a default function at
4059
//! `_default_handler` but you can override it.
60+
//!
61+
//! Expected prototype:
62+
//!
63+
//! ```rust
64+
//! #[unsafe(no_mangle)]
65+
//! extern "C" fn _svc_handler(svc: u32);
66+
//! ```
67+
//!
4168
//! * `_irq_handler` - an `extern "C"` function to call when an Interrupt
4269
//! occurs. Our linker script PROVIDEs a default function at
4370
//! `_default_handler` but you can override it.
71+
//!
72+
//! Expected prototype:
73+
//!
74+
//! ```rust
75+
//! #[unsafe(no_mangle)]
76+
//! extern "C" fn _irq_handler();
77+
//! ```
78+
//!
79+
//! * `_undefined_handler` - an `extern "C"` function to call when an Undefined Exception
80+
//! occurs. Our linker script PROVIDEs a default function at
81+
//! `_default_handler` but you can override it. It will be called by the
82+
//! `_asm_default_undefined_handler` unless that function is overriden as well.
83+
//!
84+
//! Expected prototype:
85+
//!
86+
//! ```rust
87+
//! #[unsafe(no_mangle)]
88+
//! extern "C" fn _undefined_handler(faulting_instruction: u32);
89+
//! ```
90+
//!
91+
//! * `_abort_handler` - an `extern "C"` function to call when a Data Abort Exception
92+
//! occurs. Our linker script PROVIDEs a default function at
93+
//! `_default_handler` but you can override it. It will be called by the
94+
//! `_asm_default_abort_handler` unless that function is overriden as well.
95+
//!
96+
//! Expected prototype:
97+
//!
98+
//! ```rust
99+
//! #[unsafe(no_mangle)]
100+
//! extern "C" fn _abort_handler(faulting_instruction: u32);
101+
//! ```
102+
//!
103+
//! * `_prefetch_handler` - an `extern "C"` function to call when a Prefetch Abort Exception
104+
//! occurs. Our linker script PROVIDEs a default function at
105+
//! `_default_handler` but you can override it. It will be called by the
106+
//! `_asm_default_prefetch_handler` unless that function is overriden as well.
107+
//!
108+
//! Expected prototype:
109+
//!
110+
//! ```rust
111+
//! #[unsafe(no_mangle)]
112+
//! extern "C" fn _prefetch_handler(faulting_instruction: u32);
113+
//! ```
114+
//!
115+
//! ### ASM functions
116+
//!
117+
//! * `__start` - a Reset handler. Our linker script PROVIDEs a default function
118+
//! at `_default_start` but you can override it. Most Cortex-A SoCs require
119+
//! a chip specific startup for tasks like MMU initialization or chip specific
120+
//! initialization routines.
44121
//! * `_asm_fiq_handler` - a naked function to call when a Fast Interrupt
45122
//! Request (FIQ) occurs. Our linker script PROVIDEs a default function at
46123
//! `_asm_default_fiq_handler` but you can override it.
47124
//! * `_asm_undefined_handler` - a naked function to call when an Undefined
48125
//! Exception occurs. Our linker script PROVIDEs a default function at
49-
//! `_asm_default_handler` but you can override it.
126+
//! `_asm_default_undefined_handler` but you can override it.
50127
//! * `_asm_prefetch_handler` - a naked function to call when an Prefetch
51128
//! Exception occurs. Our linker script PROVIDEs a default function at
52-
//! `_asm_default_handler` but you can override it.
129+
//! `_asm_default_prefetch_handler` but you can override it. The provided default
130+
//! handler will perform an exception return to the faulting address.
53131
//! * `_asm_abort_handler` - a naked function to call when an Abort Exception
54132
//! occurs. Our linker script PROVIDEs a default function at
55-
//! `_asm_default_handler` but you can override it.
56-
//! * `boot_core` - the `extern "C"` entry point to your application. The CPU ID
57-
//! will be passed as the first argument to this fumction.
58-
//! * `__sdata` - the start of initialised data in RAM. Must be 4-byte aligned.
59-
//! * `__edata` - the end of initialised data in RAM. Must be 4-byte aligned.
60-
//! * `__sidata` - the start of the initialisation values for data, in read-only
61-
//! memory. Must be 4-byte aligned.
62-
//! * `__sbss` - the start of zero-initialised data in RAM. Must be 4-byte
63-
//! aligned.
64-
//! * `__ebss` - the end of zero-initialised data in RAM. Must be 4-byte
65-
//! aligned.
133+
//! `_asm_default_abort_handler` but you can override it. The provided default handler
134+
//! will perform an exception return to the faulting address.
66135
//!
67136
//! On start-up, the memory between `__sbss` and `__ebss` is zeroed, and the
68137
//! memory between `__sdata` and `__edata` is initialised with the data found at
@@ -95,19 +164,28 @@
95164
//! If our start-up routine doesn't work for you (e.g. if you have to initialise
96165
//! your memory controller before you touch RAM), supply your own `_start`
97166
//! function (but feel free to call our `_default_start` as part of it).
167+
//!
168+
//! ## Examples
169+
//!
170+
//! You can find example code using QEMU inside the
171+
//! [project repository](https://github.com/rust-embedded/cortex-ar/tree/main/examples)
98172
99173
#![no_std]
100174

101-
use cortex_ar::register::{cpsr::ProcessorMode, Cpsr};
175+
use cortex_ar::{
176+
asm::nop,
177+
register::{cpsr::ProcessorMode, Cpsr},
178+
};
102179

103180
/// Our default exception handler.
104181
///
105182
/// We end up here if an exception fires and the weak 'PROVIDE' in the link.x
106183
/// file hasn't been over-ridden.
107184
#[no_mangle]
108185
pub extern "C" fn _default_handler() {
109-
semihosting::eprintln!("Unhandled exception!");
110-
semihosting::process::abort();
186+
loop {
187+
nop();
188+
}
111189
}
112190

113191
// The Interrupt Vector Table, and some default assembly-language handler.
@@ -319,6 +397,7 @@ core::arch::global_asm!(
319397
rfefd sp!
320398
.size _asm_svc_handler, . - _asm_svc_handler
321399
400+
322401
// Called from the vector table when we have an interrupt.
323402
// Saves state and calls a C-compatible handler like
324403
// `extern "C" fn irq_handler();`
@@ -337,9 +416,98 @@ core::arch::global_asm!(
337416
r#"
338417
rfefd sp!
339418
.size _asm_irq_handler, . - _asm_irq_handler
419+
420+
421+
// Called from the vector table when we have an undefined exception.
422+
// Saves state and calls a C-compatible handler like
423+
// `extern "C" fn _undefined_handler();`
424+
.global _asm_default_undefined_handler
425+
.type _asm_default_undefined_handler, %function
426+
_asm_default_undefined_handler:
427+
// First adjust LR for two purposes: Passing the faulting instruction to the C handler,
428+
// and to return to the failing instruction after the C handler returns.
429+
// Load processor status
430+
mrs r4, cpsr
431+
// Occurred in Thumb state?
432+
tst r4, {t_bit}
433+
// If not in Thumb mode, branch to not_thumb
434+
beq not_thumb
435+
subs lr, lr, #2
436+
b done
437+
not_thumb:
438+
// Subtract 4 from LR (ARM mode)
439+
subs lr, lr, #4
440+
done:
441+
// state save from compiled code
442+
srsfd sp!, {und_mode}
443+
"#,
444+
save_context!(),
445+
r#"
446+
// Pass the faulting instruction address to the handler.
447+
mov r0, lr
448+
// call C handler
449+
bl _undefined_handler
450+
"#,
451+
restore_context!(),
452+
r#"
453+
// Return to the failing instruction which is the recommended approach by ARM.
454+
rfefd sp!
455+
.size _asm_default_undefined_handler, . - _asm_default_undefined_handler
456+
457+
458+
// Called from the vector table when we have an undefined exception.
459+
// Saves state and calls a C-compatible handler like
460+
// `extern "C" fn _prefetch_handler();`
461+
.global _asm_default_prefetch_handler
462+
.type _asm_default_prefetch_handler, %function
463+
_asm_default_prefetch_handler:
464+
// Subtract 4 from the stored LR, see p.1212 of the ARMv7-A architecture manual.
465+
subs lr, lr, #4
466+
// state save from compiled code
467+
srsfd sp!, {abt_mode}
468+
"#,
469+
save_context!(),
470+
r#"
471+
// Pass the faulting instruction address to the handler.
472+
mov r0, lr
473+
// call C handler
474+
bl _prefetch_handler
475+
"#,
476+
restore_context!(),
477+
r#"
478+
// Return to the failing instruction which is the recommended approach by ARM.
479+
rfefd sp!
480+
.size _asm_default_prefetch_handler, . - _asm_default_prefetch_handler
481+
482+
483+
// Called from the vector table when we have an undefined exception.
484+
// Saves state and calls a C-compatible handler like
485+
// `extern "C" fn _abort_handler();`
486+
.global _asm_default_abort_handler
487+
.type _asm_default_abort_handler, %function
488+
_asm_default_abort_handler:
489+
// Subtract 8 from the stored LR, see p.1214 of the ARMv7-A architecture manual.
490+
subs lr, lr, #8
491+
// state save from compiled code
492+
srsfd sp!, {abt_mode}
493+
"#,
494+
save_context!(),
495+
r#"
496+
// Pass the faulting instruction address to the handler.
497+
mov r0, lr
498+
// call C handler
499+
bl _abort_handler
500+
"#,
501+
restore_context!(),
502+
r#"
503+
// Return to the failing instruction which is the recommended approach by ARM.
504+
rfefd sp!
505+
.size _asm_default_abort_handler, . - _asm_default_abort_handler
340506
"#,
341507
svc_mode = const ProcessorMode::Svc as u8,
342508
irq_mode = const ProcessorMode::Irq as u8,
509+
und_mode = const ProcessorMode::Und as u8,
510+
abt_mode = const ProcessorMode::Abt as u8,
343511
t_bit = const {
344512
Cpsr::new_with_raw_value(0)
345513
.with_t(true)

cortex-ar/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ version = "0.1.0"
2727
[dependencies]
2828
arbitrary-int = "1.3.0"
2929
bitbybit = "1.3.3"
30+
num_enum = { version = "0.7", default-features = false }
3031
critical-section = {version = "1.2.0", features = ["restore-state-bool"], optional = true}
3132
defmt = {version = "0.3", optional = true}
3233

cortex-ar/src/register/dfar.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::register::{SysReg, SysRegRead, SysRegWrite};
44

55
/// DFAR (*Data Fault Address Register*)
6+
#[derive(Debug)]
67
pub struct Dfar(pub u32);
78
impl SysReg for Dfar {
89
const CP: u32 = 15;

0 commit comments

Comments
 (0)