Skip to content

Commit 555441c

Browse files
Merge pull request #190 from rust-embedded/weak-symbols
`riscv-rt`: `_pre_init_trap` and weak symbols
2 parents f5a2da9 + f7d5ef5 commit 555441c

File tree

4 files changed

+107
-94
lines changed

4 files changed

+107
-94
lines changed

riscv-rt/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Add `pre_init_trap` to detect early errors during the boot process.
13+
1014
### Changed
1115

1216
- Moved all the assembly code to `asm.rs`
17+
- Use `weak` symbols for functions such as `_mp_hook` or `_start_trap`
1318

1419
### Removed
1520

1621
- `start_rust` is no longer needed, as it is now written in assembly
22+
- `default_*` symbols are no longer needed, as we use `weak` symbols now.
1723

1824
## [v0.12.2] - 2024-02-15
1925

riscv-rt/link.x.in

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
static mut _heap_size }`).
88

99
- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
10-
symbol if not dropped if it appears in or near the front of the linker arguments and "it's not
10+
symbol is not dropped if it appears in or near the front of the linker arguments and "it's not
1111
needed" by any of the preceding objects (linker arguments)
1212

1313
- `PROVIDE` is used to provide default values that can be overridden by a user linker script
@@ -28,6 +28,15 @@ PROVIDE(_max_hart_id = 0);
2828
PROVIDE(_hart_stack_size = 2K);
2929
PROVIDE(_heap_size = 0);
3030

31+
/** EXCEPTION HANDLERS **/
32+
33+
/* Default exception handler. The riscv-rt crate provides a weak alias of this function,
34+
which is a busy loop. Users can override this alias by defining the symbol themselves */
35+
EXTERN(ExceptionHandler);
36+
37+
/* It is possible to define a special handler for each exception type.
38+
By default, all exceptions are handled by ExceptionHandler. However, users can
39+
override these alias by defining the symbol themselves */
3140
PROVIDE(InstructionMisaligned = ExceptionHandler);
3241
PROVIDE(InstructionFault = ExceptionHandler);
3342
PROVIDE(IllegalInstruction = ExceptionHandler);
@@ -43,39 +52,22 @@ PROVIDE(InstructionPageFault = ExceptionHandler);
4352
PROVIDE(LoadPageFault = ExceptionHandler);
4453
PROVIDE(StorePageFault = ExceptionHandler);
4554

55+
/** INTERRUPT HANDLERS **/
56+
57+
/* Default interrupt handler. The riscv-rt crate provides a weak alias of this function,
58+
which is a busy loop. Users can override this alias by defining the symbol themselves */
59+
EXTERN(DefaultHandler);
60+
61+
/* It is possible to define a special handler for each interrupt type.
62+
By default, all interrupts are handled by DefaultHandler. However, users can
63+
override these alias by defining the symbol themselves */
4664
PROVIDE(SupervisorSoft = DefaultHandler);
4765
PROVIDE(MachineSoft = DefaultHandler);
4866
PROVIDE(SupervisorTimer = DefaultHandler);
4967
PROVIDE(MachineTimer = DefaultHandler);
5068
PROVIDE(SupervisorExternal = DefaultHandler);
5169
PROVIDE(MachineExternal = DefaultHandler);
5270

53-
PROVIDE(DefaultHandler = DefaultInterruptHandler);
54-
PROVIDE(ExceptionHandler = DefaultExceptionHandler);
55-
56-
/* # Pre-initialization function */
57-
/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function,
58-
then the function this points to will be called before the RAM is initialized. */
59-
PROVIDE(__pre_init = default_pre_init);
60-
61-
/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */
62-
PROVIDE(_setup_interrupts = default_setup_interrupts);
63-
64-
/* # Multi-processing hook function
65-
fn _mp_hook() -> bool;
66-
67-
This function is called from all the harts and must return true only for one hart,
68-
which will perform memory initialization. For other harts it must return false
69-
and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
70-
*/
71-
PROVIDE(_mp_hook = default_mp_hook);
72-
73-
/* # Start trap function override
74-
By default uses the riscv crates default trap handler
75-
but by providing the `_start_trap` symbol external crates can override.
76-
*/
77-
PROVIDE(_start_trap = default_start_trap);
78-
7971
SECTIONS
8072
{
8173
.text.dummy (NOLOAD) :
@@ -91,6 +83,8 @@ SECTIONS
9183
KEEP(*(.init));
9284
KEEP(*(.init.rust));
9385
. = ALIGN(4);
86+
KEEP(*(.init.trap));
87+
. = ALIGN(4);
9488
*(.trap);
9589
*(.trap.rust);
9690
*(.text.abort);

riscv-rt/src/asm.rs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,19 @@ _abs_start:
6666
.option norelax
6767
.cfi_startproc
6868
.cfi_undefined ra",
69+
// Disable interrupts
6970
#[cfg(feature = "s-mode")]
7071
"csrw sie, 0
7172
csrw sip, 0",
7273
#[cfg(not(feature = "s-mode"))]
7374
"csrw mie, 0
7475
csrw mip, 0",
76+
// Set pre-init trap vector
77+
"la t0, _pre_init_trap",
78+
#[cfg(feature = "s-mode")]
79+
"csrw stvec, t0",
80+
#[cfg(not(feature = "s-mode"))]
81+
"csrw mtvec, t0",
7582
);
7683

7784
// ZERO OUT GENERAL-PURPOSE REGISTERS
@@ -220,6 +227,53 @@ cfg_global_asm!(
220227
.cfi_endproc",
221228
);
222229

230+
cfg_global_asm!(
231+
// Default implementation of `__pre_init` does nothing.
232+
// Users can override this function with the [`#[pre_init]`] macro.
233+
".weak __pre_init
234+
__pre_init:
235+
ret",
236+
#[cfg(not(feature = "single-hart"))]
237+
// Default implementation of `_mp_hook` wakes hart 0 and busy-loops all the other harts.
238+
// Users can override this function by defining their own `_mp_hook`.
239+
// This function is only used when the `single-hart` feature is not enabled.
240+
".weak _mp_hook
241+
_mp_hook:
242+
beqz a0, 2f // if hartid is 0, return true
243+
1: wfi // Otherwise, wait for interrupt in a loop
244+
j 1b
245+
2: li a0, 1
246+
ret",
247+
// Default implementation of `_setup_interrupts` sets the trap vector to `_start_trap`.
248+
// Trap mode is set to `Direct` by default.
249+
// Users can override this function by defining their own `_setup_interrupts`
250+
".weak _setup_interrupts
251+
_setup_interrupts:
252+
la t0, _start_trap", // _start_trap is 16-byte aligned, so it corresponds to the Direct trap mode
253+
#[cfg(feature = "s-mode")]
254+
"csrw stvec, t0",
255+
#[cfg(not(feature = "s-mode"))]
256+
"csrw mtvec, t0",
257+
"ret",
258+
// Default implementation of `ExceptionHandler` is an infinite loop.
259+
// Users can override this function by defining their own `ExceptionHandler`
260+
".weak ExceptionHandler
261+
ExceptionHandler:
262+
j ExceptionHandler",
263+
// Default implementation of `DefaultHandler` is an infinite loop.
264+
// Users can override this function by defining their own `DefaultHandler`
265+
".weak DefaultHandler
266+
DefaultHandler:
267+
j DefaultHandler",
268+
// Default implementation of `_pre_init_trap` is an infinite loop.
269+
// Users can override this function by defining their own `_pre_init_trap`
270+
// If the execution reaches this point, it means that there is a bug in the boot code.
271+
".section .init.trap, \"ax\"
272+
.weak _pre_init_trap
273+
_pre_init_trap:
274+
j _pre_init_trap",
275+
);
276+
223277
/// Trap entry point (_start_trap). It saves caller saved registers, calls
224278
/// _start_trap_rust, restores caller saved registers and then returns.
225279
///
@@ -241,8 +295,8 @@ macro_rules! trap_handler {
241295
global_asm!(
242296
"
243297
.section .trap, \"ax\"
244-
.global default_start_trap
245-
default_start_trap:",
298+
.weak _start_trap
299+
_start_trap:",
246300
// save space for trap handler in stack
247301
concat!("addi sp, sp, -", stringify!($TRAP_SIZE * $BYTES)),
248302
// save registers in the desired order
@@ -280,10 +334,10 @@ trap_handler!(
280334
(a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)]
281335
);
282336

283-
// Make sure there is an abort when linking
337+
#[rustfmt::skip]
284338
global_asm!(
285339
".section .text.abort
286-
.globl abort
287-
abort:
340+
.global abort
341+
abort: // make sure there is an abort symbol when linking
288342
j abort"
289343
);

riscv-rt/src/lib.rs

Lines changed: 21 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,16 @@
228228
//! }
229229
//! ```
230230
//!
231+
//! ## `_pre_init_trap`
232+
//!
233+
//! This function is set as a provisional trap handler for the early trap handling.
234+
//! If either an exception or an interrupt occurs during the boot process, this
235+
//! function is triggered. The default implementation of this function is a busy-loop.
236+
//! While this function can be redefined, it is not recommended to do so, as it is
237+
//! intended to be a temporary trap handler to detect bugs in the early boot process.
238+
//! Recall that this trap is triggered before the `.bss` and `.data` sections are
239+
//! initialized, so it is not safe to use any global variables in this function.
240+
//!
231241
//! ### `_mp_hook`
232242
//!
233243
//! This function is called from all the harts and must return true only for one hart,
@@ -247,7 +257,15 @@
247257
//! Default implementation of this function wakes hart 0 and busy-loops all the other harts.
248258
//!
249259
//! `_mp_hook` is only necessary in multi-core targets. If the `single-hart` feature is enabled,
250-
//! `_mp_hook` is not called, as it is assumed that there is only one hart on the target.
260+
//! `_mp_hook` is not included in the binary.
261+
//!
262+
//! ### `_setup_interrupts`
263+
//!
264+
//! This function is called right before the main function and is responsible for setting up
265+
//! the interrupt controller.
266+
//!
267+
//! Default implementation sets the trap vector to `_start_trap` in direct mode.
268+
//! Users can override this function by defining their own `_setup_interrupts`
251269
//!
252270
//! ### Core exception handlers
253271
//!
@@ -404,10 +422,10 @@
404422
mod asm;
405423

406424
#[cfg(feature = "s-mode")]
407-
use riscv::register::{scause as xcause, stvec as xtvec, stvec::TrapMode as xTrapMode};
425+
use riscv::register::scause as xcause;
408426

409427
#[cfg(not(feature = "s-mode"))]
410-
use riscv::register::{mcause as xcause, mtvec as xtvec, mtvec::TrapMode as xTrapMode};
428+
use riscv::register::mcause as xcause;
411429

412430
pub use riscv_rt_macros::{entry, pre_init};
413431

@@ -487,28 +505,6 @@ pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) {
487505
}
488506
}
489507

490-
#[doc(hidden)]
491-
#[no_mangle]
492-
#[allow(unused_variables, non_snake_case)]
493-
pub fn DefaultExceptionHandler(trap_frame: &TrapFrame) -> ! {
494-
loop {
495-
// Prevent this from turning into a UDF instruction
496-
// see rust-lang/rust#28728 for details
497-
continue;
498-
}
499-
}
500-
501-
#[doc(hidden)]
502-
#[no_mangle]
503-
#[allow(non_snake_case)]
504-
pub fn DefaultInterruptHandler() {
505-
loop {
506-
// Prevent this from turning into a UDF instruction
507-
// see rust-lang/rust#28728 for details
508-
continue;
509-
}
510-
}
511-
512508
extern "C" {
513509
fn InstructionMisaligned(trap_frame: &TrapFrame);
514510
fn InstructionFault(trap_frame: &TrapFrame);
@@ -572,40 +568,3 @@ pub static __INTERRUPTS: [Option<unsafe extern "C" fn()>; 12] = [
572568
None,
573569
Some(MachineExternal),
574570
];
575-
576-
/// Default implementation of `_pre_init` does nothing.
577-
/// Users can override this function with the [`#[pre_init]`] macro.
578-
#[doc(hidden)]
579-
#[no_mangle]
580-
#[rustfmt::skip]
581-
pub extern "Rust" fn default_pre_init() {}
582-
583-
/// Default implementation of `_mp_hook` wakes hart 0 and busy-loops all the other harts.
584-
/// Users can override this function by defining their own `_mp_hook`.
585-
///
586-
/// # Note
587-
///
588-
/// If the `single-hart` feature is enabled, `_mp_hook` is not called.
589-
#[doc(hidden)]
590-
#[no_mangle]
591-
#[rustfmt::skip]
592-
pub extern "Rust" fn default_mp_hook(hartid: usize) -> bool {
593-
match hartid {
594-
0 => true,
595-
_ => loop {
596-
riscv::asm::wfi();
597-
},
598-
}
599-
}
600-
601-
/// Default implementation of `_setup_interrupts` sets `mtvec`/`stvec` to the address of `_start_trap`.
602-
#[doc(hidden)]
603-
#[no_mangle]
604-
#[rustfmt::skip]
605-
pub unsafe extern "Rust" fn default_setup_interrupts() {
606-
extern "C" {
607-
fn _start_trap();
608-
}
609-
610-
xtvec::write(_start_trap as usize, xTrapMode::Direct);
611-
}

0 commit comments

Comments
 (0)