Skip to content

Commit 654fcc4

Browse files
committed
Use callee-saved registers for preserving arguments
1 parent cdd76e9 commit 654fcc4

File tree

3 files changed

+68
-42
lines changed

3 files changed

+68
-42
lines changed

riscv-rt/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3131
- Now, `_start_rust` jumps to `hal_main` instead of `main` directly. At linker level,
3232
`hal_main` maps to `main` if not defined. However, we now allow HALs to inject
3333
additional configuration code before jumping to the final user's `main` function.
34+
- Now, `a0` is preserved in `s1` during the startup process.
35+
36+
### Removed
37+
38+
- Removed usage of the stack before `_start_rust`. This was unsound, as in the `.init`
39+
section RAM is still uninitialized.
40+
- Now, `a1` and `a2` are **not** preserved during the startup process. New documentation
41+
of startup functions (`_mp_hook` and `__pre_init`) now provide additional implementation
42+
guidelines to ensure a correct behavior of the runtime.
3443

3544
### Fixed
3645

riscv-rt/src/asm.rs

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ _abs_start:
8585
bgeu t0, a0, 1f
8686
la t0, abort // If hart_id > _max_hart_id, jump to abort
8787
jr t0
88-
1:", // only valid harts reach this point
88+
1: mv s1, a0", // If hart ID is valid, preserve it in s1
8989

9090
// INITIALIZE GLOBAL POINTER, STACK POINTER, AND FRAME POINTER
9191
".option push
@@ -113,18 +113,6 @@ _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 _start_rust
117-
#[cfg(target_arch = "riscv32")]
118-
"addi sp, sp, -4 * 4 // we must keep stack aligned to 16-bytes
119-
sw a0, 4 * 0(sp)
120-
sw a1, 4 * 1(sp)
121-
sw a2, 4 * 2(sp)",
122-
#[cfg(target_arch = "riscv64")]
123-
"addi sp, sp, -8 * 4 // we must keep stack aligned to 16-bytes
124-
sd a0, 8 * 0(sp)
125-
sd a1, 8 * 1(sp)
126-
sd a2, 8 * 2(sp)",
127-
128116
// CALL __pre_init (IF ENABLED) AND INITIALIZE RAM
129117
#[cfg(not(feature = "single-hart"))]
130118
// Skip RAM initialization if current hart is not the boot hart
@@ -183,18 +171,9 @@ _abs_start:
183171
"fscsr x0",
184172
}
185173

186-
// RESTORE a0..a2, AND JUMP TO _start_rust FUNCTION
187-
#[cfg(target_arch = "riscv32")]
188-
"lw a0, 4 * 0(sp)
189-
lw a1, 4 * 1(sp)
190-
lw a2, 4 * 2(sp)
191-
addi sp, sp, 4 * 4",
192-
#[cfg(target_arch = "riscv64")]
193-
"ld a0, 8 * 0(sp)
194-
ld a1, 8 * 1(sp)
195-
ld a2, 8 * 2(sp)
196-
addi sp, sp, 8 * 4",
197-
"la t0, _start_rust
174+
// RESTORE a0 AND JUMP TO _start_rust FUNCTION
175+
"mv a0, s1
176+
la t0, _start_rust
198177
jr t0
199178
.cfi_endproc",
200179

riscv-rt/src/lib.rs

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//!
66
//! # Features
77
//!
8-
//! This crates takes care of:
8+
//! This crate takes care of:
99
//!
1010
//! - The memory layout of the program.
1111
//!
@@ -327,21 +327,11 @@
327327
//! Furthermore, as this function is expected to behave like a trap handler, it is
328328
//! necessary to make it be 4-byte aligned.
329329
//!
330-
//! ## `_mp_hook`
330+
//! ## `_mp_hook` (for multi-core targets only)
331331
//!
332332
//! This function is called from all the harts and must return true only for one hart,
333333
//! which will perform memory initialization. For other harts it must return false
334334
//! and implement wake-up in platform-dependent way (e.g., after waiting for a user interrupt).
335-
//! The parameter `hartid` specifies the hartid of the caller.
336-
//!
337-
//! This function can be redefined in the following way:
338-
//!
339-
//! ``` no_run
340-
//! #[export_name = "_mp_hook"]
341-
//! pub extern "Rust" fn mp_hook(hartid: usize) -> bool {
342-
//! // ...
343-
//! }
344-
//! ```
345335
//!
346336
//! Default implementation of this function wakes hart 0 and busy-loops all the other harts.
347337
//!
@@ -350,6 +340,36 @@
350340
//! `_mp_hook` is only necessary in multi-core targets. If the `single-hart` feature is enabled,
351341
//! `_mp_hook` is not included in the binary.
352342
//!
343+
//! ### Important implementation guidelines
344+
//!
345+
//! This function is called during the early boot process. Thus, when implementing it, you **MUST** follow these guidelines:
346+
//!
347+
//! - Implement it in assembly (no Rust code is allowed at this point).
348+
//! - Allocate this function within the `.init` section.
349+
//! - You can get the hart id from the `a0` register.
350+
//! - You must set the return value in the `a0` register.
351+
//! - You must **NOT** use the `a1-a2` registers, as they are required later by the runtime.
352+
//!
353+
//! **Violating these constraints will result in incorrect arguments being passed to `main()`.**
354+
//!
355+
//! ### Implementation example
356+
//!
357+
//! The following example shows how to implement the `_mp_hook` function in assembly.
358+
//!
359+
//! ``` no_run
360+
//! core::arch::global_asm!(
361+
//! r#".section .init.mp_hook, "ax"
362+
//! .global _mp_hook
363+
//! _mp_hook:
364+
//! beqz a0, 2f // check if hartid is 0
365+
//! 1: wfi // If not, wait for interrupt in a loop
366+
//! j 1b
367+
//! 2: li a0, 1 // Otherwise, return true
368+
//! ret
369+
//! "#
370+
//! );
371+
//! ```
372+
//!
353373
//! ## `_setup_interrupts`
354374
//!
355375
//! This function is called right before the main function and is responsible for setting up
@@ -506,12 +526,30 @@
506526
//! If the feature is enabled, the `__pre_init` function must be defined in the user code (i.e., no default implementation is
507527
//! provided by this crate). If the feature is disabled, the `__pre_init` function is not required.
508528
//!
509-
//! As `__pre_init` runs before RAM is initialised, it is not sound to use a Rust function for `__pre_init`, and
510-
//! instead it should typically be written in assembly using `global_asm` or an external assembly file.
529+
//! ### Important implementation guidelines
530+
//!
531+
//! This function is called during the early boot process. Thus, when implementing it, you **MUST** follow these guidelines:
532+
//!
533+
//! - Implement it in assembly (no Rust code is allowed at this point).
534+
//! - Allocate this function within the `.init` section.
535+
//! - You must **NOT** modify the `a1-a2` registers, as they are required later by the runtime.
511536
//!
512-
//! Alternatively, you can use the [`#[pre_init]`][attr-pre-init] attribute to define a pre-init function with Rust.
513-
//! Note that using this macro is discouraged, as it may lead to undefined behavior.
514-
//! We left this option for backwards compatibility, but it is subject to removal in the future.
537+
//! **Violating these constraints will result in incorrect arguments being passed to `main()`.**
538+
//!
539+
//! ### Implementation example
540+
//!
541+
//! The following example shows how to implement the `__pre_init` function in assembly.
542+
//!
543+
//! ``` no_run
544+
//! core::arch::global_asm!(
545+
//! r#".section .init.__pre_init, "ax"
546+
//! .global __pre_init
547+
//! __pre_init:
548+
//! // Do some pre-initialization work here and return
549+
//! ret
550+
//! "#
551+
//! );
552+
//! ```
515553
//!
516554
//! ## `single-hart`
517555
//!

0 commit comments

Comments
 (0)