Skip to content

Commit cdd76e9

Browse files
Merge pull request #324 from rust-embedded/rvrt-asm
`riscv-rt`: Use `_start_rust` and `hal_main`
2 parents afa79b5 + ce121f2 commit cdd76e9

File tree

4 files changed

+96
-22
lines changed

4 files changed

+96
-22
lines changed

riscv-rt/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2525
4. `_start_DefaultHandler_trap` and `_continue_trap` (if `v-trap` is enabled).
2626
5. `_start_trap_rust`.
2727
6. Other code in `.trap` section (usually, none)
28+
- Now, `riscv-rt` jumps to `_start_rust` instead of `main` directly. This allows us
29+
to leave input parameters preservation to the Rust compiler.
30+
- `_default_setup_interrupts` is now written in Rust and called from `_start_rust`.
31+
- Now, `_start_rust` jumps to `hal_main` instead of `main` directly. At linker level,
32+
`hal_main` maps to `main` if not defined. However, we now allow HALs to inject
33+
additional configuration code before jumping to the final user's `main` function.
2834

2935
### Fixed
3036

3137
- `clippy` fixes
3238
- Merged `cfg_global_asm!` macro invocations to guarantee contiguous code generation.
39+
- Use `.balign` instead of `.align` in `_default_abort`
3340

3441
## [v0.15.0] - 2025-06-10
3542

riscv-rt/link.x.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ PROVIDE(_start_trap = _default_start_trap);
5252
EXTERN(_default_setup_interrupts);
5353
PROVIDE(_setup_interrupts = _default_setup_interrupts);
5454

55+
/* Default main routine. If no hal_main symbol is provided, then hal_main maps to main, which
56+
is usually defined by final users via the #[riscv_rt::entry] attribute. Using hal_main
57+
instead of main directly allow HALs to inject code before jumping to user main. */
58+
PROVIDE(hal_main = main);
59+
5560
/* Default exception handler. By default, the exception handler is abort.
5661
Users can override this alias by defining the symbol themselves */
5762
PROVIDE(ExceptionHandler = abort);

riscv-rt/src/asm.rs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ _abs_start:
113113
"sub t1, t1, t0",
114114
"andi sp, t1, -16 // align stack to 16-bytes
115115
add s0, sp, zero",
116-
// STORE A0..A2 IN THE STACK, AS THEY WILL BE NEEDED LATER BY main
116+
// STORE A0..A2 IN THE STACK, AS THEY WILL BE NEEDED LATER BY _start_rust
117117
#[cfg(target_arch = "riscv32")]
118118
"addi sp, sp, -4 * 4 // we must keep stack aligned to 16-bytes
119119
sw a0, 4 * 0(sp)
@@ -183,8 +183,7 @@ _abs_start:
183183
"fscsr x0",
184184
}
185185

186-
// SET UP INTERRUPTS, RESTORE a0..a2, AND JUMP TO MAIN RUST FUNCTION
187-
"call _setup_interrupts",
186+
// RESTORE a0..a2, AND JUMP TO _start_rust FUNCTION
188187
#[cfg(target_arch = "riscv32")]
189188
"lw a0, 4 * 0(sp)
190189
lw a1, 4 * 1(sp)
@@ -195,7 +194,7 @@ _abs_start:
195194
ld a1, 8 * 1(sp)
196195
ld a2, 8 * 2(sp)
197196
addi sp, sp, 8 * 4",
198-
"la t0, main
197+
"la t0, _start_rust
199198
jr t0
200199
.cfi_endproc",
201200

@@ -210,21 +209,6 @@ _default_mp_hook:
210209
j 1b
211210
2: li a0, 1
212211
ret",
213-
// Default implementation of `_setup_interrupts` sets the trap vector to `_start_trap` in direct mode.
214-
// In vectored mode, it sets the trap vector to `_vector_table`.
215-
// Users can override this function by defining their own `_setup_interrupts`
216-
".global _default_setup_interrupts
217-
_default_setup_interrupts:",
218-
#[cfg(not(feature = "v-trap"))]
219-
"la t0, _start_trap", // _start_trap is 4-byte aligned, so it corresponds to the Direct trap mode
220-
#[cfg(feature = "v-trap")]
221-
"la t0, _vector_table
222-
ori t0, t0, 0x1", // _vector_table is at least 4-byte aligned, so we must set the bit 0 to activate the Vectored trap mode
223-
#[cfg(feature = "s-mode")]
224-
"csrw stvec, t0",
225-
#[cfg(not(feature = "s-mode"))]
226-
"csrw mtvec, t0",
227-
"ret",
228212
);
229213

230214
riscv_rt_macros::default_start_trap!();
@@ -235,7 +219,7 @@ riscv_rt_macros::vectored_interrupt_trap!();
235219
#[rustfmt::skip]
236220
global_asm!(
237221
".section .text.abort
238-
.align 4
222+
.balign 4
239223
.global _default_abort
240224
_default_abort: // make sure there is an abort symbol when linking
241225
j _default_abort"

riscv-rt/src/lib.rs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,24 @@
359359
//! If the `v-trap` feature is enabled, the trap vector is set to `_vector_table`
360360
//! in vectored mode. Users can override this function by defining their own `_setup_interrupts`.
361361
//!
362+
//! This function can be redefined in the following way:
363+
//!
364+
//! ``` no_run
365+
//! #[export_name = "_setup_interrupts"]
366+
//! pub fn setup_interrupts() {
367+
//! // ...
368+
//! }
369+
//! ```
370+
//!
371+
//! ## `hal_main`
372+
//!
373+
//! Internally, `riscv-rt` does not jump to the `main` function created by the user using the
374+
//! [`#[entry]`][attr-entry] attribute. Instead, it jumps to the `hal_main` function.
375+
//! The linker will map `hal_main` to `main` if the prior is not defined, which is the typical case.
376+
//! However, the `hal_main` function allows HALs to inject additional code before jumping to the
377+
//! user's `main` function. This might be useful for certain HALs that need to perform additional
378+
//! configuration before the main function is executed.
379+
//!
362380
//! # Attributes
363381
//!
364382
//! ## Core exception handlers
@@ -552,10 +570,16 @@ pub mod exceptions;
552570
pub mod interrupts;
553571

554572
#[cfg(feature = "s-mode")]
555-
use riscv::register::scause as xcause;
573+
use riscv::register::{
574+
scause as xcause,
575+
stvec::{self as xtvec, Stvec as Xtvec, TrapMode},
576+
};
556577

557578
#[cfg(not(feature = "s-mode"))]
558-
use riscv::register::mcause as xcause;
579+
use riscv::register::{
580+
mcause as xcause,
581+
mtvec::{self as xtvec, Mtvec as Xtvec, TrapMode},
582+
};
559583

560584
pub use riscv_pac::*;
561585
pub use riscv_rt_macros::{core_interrupt, entry, exception, external_interrupt};
@@ -571,6 +595,60 @@ pub use riscv_rt_macros::pre_init;
571595
#[doc(hidden)]
572596
pub static __ONCE__: () = ();
573597

598+
/// Rust entry point (_start_rust)
599+
///
600+
/// Configures interrupts and calls main. This function never returns.
601+
///
602+
/// # Safety
603+
///
604+
/// This function should not be called directly by the user, and should instead
605+
/// be invoked by the runtime implicitly.
606+
#[cfg_attr(
607+
any(target_arch = "riscv32", target_arch = "riscv64"),
608+
link_section = ".init.rust"
609+
)]
610+
#[export_name = "_start_rust"]
611+
pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! {
612+
extern "Rust" {
613+
fn _setup_interrupts();
614+
fn hal_main(a0: usize, a1: usize, a2: usize) -> !;
615+
}
616+
617+
_setup_interrupts();
618+
hal_main(a0, a1, a2);
619+
}
620+
621+
/// Default implementation of `_setup_interrupts`.
622+
///
623+
/// In direct mode (i.e., `v-trap` feature disabled), it sets the trap vector to `_start_trap`.
624+
/// In vectored mode (i.e., `v-trap` feature enabled), it sets the trap vector to `_vector_table`.
625+
///
626+
/// # Note
627+
///
628+
/// Users can override this function by defining their own `_setup_interrupts` function.
629+
///
630+
/// # Safety
631+
///
632+
/// This function should not be called directly by the user, and should instead
633+
/// be invoked by the runtime implicitly. It is expected to be called before the main function.
634+
#[export_name = "_default_setup_interrupts"]
635+
pub unsafe extern "Rust" fn setup_interrupts() {
636+
extern "C" {
637+
#[cfg(not(feature = "v-trap"))]
638+
fn _start_trap();
639+
#[cfg(feature = "v-trap")]
640+
fn _vector_table();
641+
}
642+
643+
let xtvec_val = match () {
644+
#[cfg(not(feature = "v-trap"))]
645+
_ => Xtvec::new(_start_trap as usize, TrapMode::Direct),
646+
#[cfg(feature = "v-trap")]
647+
_ => Xtvec::new(_vector_table as usize, TrapMode::Vectored),
648+
};
649+
xtvec::write(xtvec_val);
650+
}
651+
574652
/// Registers saved in trap handler
575653
#[repr(C)]
576654
#[derive(Debug)]

0 commit comments

Comments
 (0)