Skip to content

Commit da226b6

Browse files
Merge pull request #123 from coastalwhite/r0-in-asm
Implement r0 crate in assembly
2 parents db136b8 + 5612518 commit da226b6

File tree

6 files changed

+276
-23
lines changed

6 files changed

+276
-23
lines changed

riscv-rt/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1616
- Use inline assembly instead of pre-compiled blobs
1717
- Removed bors in favor of GitHub Merge Queue
1818
- `start_trap_rust` is now marked as `unsafe`
19+
- Implement `r0` as inline assembly
1920

2021
## [v0.11.0] - 2023-01-18
2122

riscv-rt/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ s-mode = []
1515
single-hart = []
1616

1717
[dependencies]
18-
r0 = "1.0.0"
1918
riscv = "0.10"
2019
riscv-rt-macros = { path = "macros", version = "0.2.0" }
2120

riscv-rt/build.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,17 @@ use std::env;
55
use std::fs;
66
use std::path::PathBuf;
77

8+
fn add_linker_script(bytes: &[u8]) {
9+
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
10+
11+
// Put the linker script somewhere the linker can find it
12+
fs::write(out_dir.join("link.x"), bytes).unwrap();
13+
println!("cargo:rustc-link-search={}", out_dir.display());
14+
println!("cargo:rerun-if-changed=link.x");
15+
}
16+
817
fn main() {
918
let target = env::var("TARGET").unwrap();
10-
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
1119
let _name = env::var("CARGO_PKG_NAME").unwrap();
1220

1321
// set configuration flags depending on the target
@@ -17,19 +25,16 @@ fn main() {
1725
match target.bits {
1826
32 => {
1927
println!("cargo:rustc-cfg=riscv32");
28+
add_linker_script(include_bytes!("link-rv32.x"));
2029
}
2130
64 => {
2231
println!("cargo:rustc-cfg=riscv64");
32+
add_linker_script(include_bytes!("link-rv64.x"));
2333
}
2434
_ => panic!("Unsupported bit width"),
2535
}
2636
if target.has_extension('m') {
2737
println!("cargo:rustc-cfg=riscvm"); // we can expose extensions this way
2838
}
2939
}
30-
31-
// Put the linker script somewhere the linker can find it
32-
fs::write(out_dir.join("link.x"), include_bytes!("link.x")).unwrap();
33-
println!("cargo:rustc-link-search={}", out_dir.display());
34-
println!("cargo:rerun-if-changed=link.x");
3540
}

riscv-rt/link.x renamed to riscv-rt/link-rv32.x

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ SECTIONS
8484
_edata = .;
8585
} > REGION_DATA AT > REGION_RODATA
8686

87-
.bss (NOLOAD) :
87+
.bss (NOLOAD) : ALIGN(4)
8888
{
8989
_sbss = .;
9090
*(.sbss .sbss.* .bss .bss.*);

riscv-rt/link-rv64.x

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
PROVIDE(_stext = ORIGIN(REGION_TEXT));
2+
PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));
3+
PROVIDE(_max_hart_id = 0);
4+
PROVIDE(_hart_stack_size = 2K);
5+
PROVIDE(_heap_size = 0);
6+
7+
PROVIDE(UserSoft = DefaultHandler);
8+
PROVIDE(SupervisorSoft = DefaultHandler);
9+
PROVIDE(MachineSoft = DefaultHandler);
10+
PROVIDE(UserTimer = DefaultHandler);
11+
PROVIDE(SupervisorTimer = DefaultHandler);
12+
PROVIDE(MachineTimer = DefaultHandler);
13+
PROVIDE(UserExternal = DefaultHandler);
14+
PROVIDE(SupervisorExternal = DefaultHandler);
15+
PROVIDE(MachineExternal = DefaultHandler);
16+
17+
PROVIDE(DefaultHandler = DefaultInterruptHandler);
18+
PROVIDE(ExceptionHandler = DefaultExceptionHandler);
19+
20+
/* # Pre-initialization function */
21+
/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function,
22+
then the function this points to will be called before the RAM is initialized. */
23+
PROVIDE(__pre_init = default_pre_init);
24+
25+
/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */
26+
PROVIDE(_setup_interrupts = default_setup_interrupts);
27+
28+
/* # Multi-processing hook function
29+
fn _mp_hook() -> bool;
30+
31+
This function is called from all the harts and must return true only for one hart,
32+
which will perform memory initialization. For other harts it must return false
33+
and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
34+
*/
35+
PROVIDE(_mp_hook = default_mp_hook);
36+
37+
/* # Start trap function override
38+
By default uses the riscv crates default trap handler
39+
but by providing the `_start_trap` symbol external crates can override.
40+
*/
41+
PROVIDE(_start_trap = default_start_trap);
42+
43+
SECTIONS
44+
{
45+
.text.dummy (NOLOAD) :
46+
{
47+
/* This section is intended to make _stext address work */
48+
. = ABSOLUTE(_stext);
49+
} > REGION_TEXT
50+
51+
.text _stext :
52+
{
53+
/* Put reset handler first in .text section so it ends up as the entry */
54+
/* point of the program. */
55+
KEEP(*(.init));
56+
KEEP(*(.init.rust));
57+
. = ALIGN(4);
58+
*(.trap);
59+
*(.trap.rust);
60+
*(.text.abort);
61+
*(.text .text.*);
62+
} > REGION_TEXT
63+
64+
.rodata : ALIGN(4)
65+
{
66+
*(.srodata .srodata.*);
67+
*(.rodata .rodata.*);
68+
69+
/* 4-byte align the end (VMA) of this section.
70+
This is required by LLD to ensure the LMA of the following .data
71+
section will have the correct alignment. */
72+
. = ALIGN(4);
73+
} > REGION_RODATA
74+
75+
.data : ALIGN(8)
76+
{
77+
_sidata = LOADADDR(.data);
78+
_sdata = .;
79+
/* Must be called __global_pointer$ for linker relaxations to work. */
80+
PROVIDE(__global_pointer$ = . + 0x800);
81+
*(.sdata .sdata.* .sdata2 .sdata2.*);
82+
*(.data .data.*);
83+
. = ALIGN(8);
84+
_edata = .;
85+
} > REGION_DATA AT > REGION_RODATA
86+
87+
.bss (NOLOAD) : ALIGN(8)
88+
{
89+
_sbss = .;
90+
*(.sbss .sbss.* .bss .bss.*);
91+
. = ALIGN(8);
92+
_ebss = .;
93+
} > REGION_BSS
94+
95+
/* fictitious region that represents the memory available for the heap */
96+
.heap (NOLOAD) :
97+
{
98+
_sheap = .;
99+
. += _heap_size;
100+
. = ALIGN(4);
101+
_eheap = .;
102+
} > REGION_HEAP
103+
104+
/* fictitious region that represents the memory available for the stack */
105+
.stack (NOLOAD) :
106+
{
107+
_estack = .;
108+
. = ABSOLUTE(_stack_start);
109+
_sstack = .;
110+
} > REGION_STACK
111+
112+
/* fake output .got section */
113+
/* Dynamic relocations are unsupported. This section is only used to detect
114+
relocatable code in the input files and raise an error if relocatable code
115+
is found */
116+
.got (INFO) :
117+
{
118+
KEEP(*(.got .got.*));
119+
}
120+
121+
.eh_frame (INFO) : { KEEP(*(.eh_frame)) }
122+
.eh_frame_hdr (INFO) : { *(.eh_frame_hdr) }
123+
}
124+
125+
/* Do not exceed this mark in the error messages above | */
126+
ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, "
127+
ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
128+
129+
ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, "
130+
ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned");
131+
132+
ASSERT(ORIGIN(REGION_DATA) % 8 == 0, "
133+
ERROR(riscv-rt): the start of the REGION_DATA must be 8-byte aligned");
134+
135+
ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, "
136+
ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned");
137+
138+
ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, "
139+
ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned");
140+
141+
ASSERT(ORIGIN(REGION_STACK) % 4 == 0, "
142+
ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned");
143+
144+
ASSERT(_stext % 4 == 0, "
145+
ERROR(riscv-rt): `_stext` must be 4-byte aligned");
146+
147+
ASSERT(_sdata % 8 == 0 && _edata % 8 == 0, "
148+
BUG(riscv-rt): .data is not 8-byte aligned");
149+
150+
ASSERT(_sidata % 8 == 0, "
151+
BUG(riscv-rt): the LMA of .data is not 8-byte aligned");
152+
153+
ASSERT(_sbss % 8 == 0 && _ebss % 8 == 0, "
154+
BUG(riscv-rt): .bss is not 8-byte aligned");
155+
156+
ASSERT(_sheap % 4 == 0, "
157+
BUG(riscv-rt): start of .heap is not 4-byte aligned");
158+
159+
ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), "
160+
ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region.
161+
Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'");
162+
163+
ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, "
164+
ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts.
165+
Consider changing `_max_hart_id` or `_hart_stack_size`.");
166+
167+
ASSERT(SIZEOF(.got) == 0, "
168+
.got section detected in the input files. Dynamic relocations are not
169+
supported. If you are linking to C code compiled using the `gcc` crate
170+
then modify your build script to compile the C code _without_ the
171+
-fPIC flag. See the documentation of the `gcc::Config.fpic` method for
172+
details.");
173+
174+
/* Do not exceed this mark in the error messages above | */

riscv-rt/src/lib.rs

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,8 @@
366366
#[cfg(riscv)]
367367
mod asm;
368368

369+
use core::sync::atomic::{compiler_fence, Ordering};
370+
369371
#[cfg(feature = "s-mode")]
370372
use riscv::register::{scause as xcause, stvec as xtvec, stvec::TrapMode as xTrapMode};
371373

@@ -378,19 +380,6 @@ pub use riscv_rt_macros::{entry, pre_init};
378380
#[doc(hidden)]
379381
pub static __ONCE__: () = ();
380382

381-
extern "C" {
382-
// Boundaries of the .bss section
383-
static mut _ebss: u32;
384-
static mut _sbss: u32;
385-
386-
// Boundaries of the .data section
387-
static mut _edata: u32;
388-
static mut _sdata: u32;
389-
390-
// Initial values of the .data section (stored in Flash)
391-
static _sidata: u32;
392-
}
393-
394383
/// Rust entry point (_start_rust)
395384
///
396385
/// Zeros bss section, initializes data section and calls main. This function never returns.
@@ -424,8 +413,93 @@ pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! {
424413
if _mp_hook(hartid) {
425414
__pre_init();
426415

427-
r0::zero_bss(&mut _sbss, &mut _ebss);
428-
r0::init_data(&mut _sdata, &mut _edata, &_sidata);
416+
// Initialize RAM
417+
// 1. Copy over .data from flash to RAM
418+
// 2. Zero out .bss
419+
420+
#[cfg(target_arch = "riscv32")]
421+
core::arch::asm!(
422+
"
423+
// Copy over .data
424+
la {start},_sdata
425+
la {end},_edata
426+
la {input},_sidata
427+
428+
bgeu {start},{end},2f
429+
1:
430+
lw {a},0({input})
431+
addi {input},{input},4
432+
sw {a},0({start})
433+
addi {start},{start},4
434+
bltu {start},{end},1b
435+
436+
2:
437+
li {a},0
438+
li {input},0
439+
440+
// Zero out .bss
441+
la {start},_sbss
442+
la {end},_ebss
443+
444+
bgeu {start},{end},3f
445+
2:
446+
sw zero,0({start})
447+
addi {start},{start},4
448+
bltu {start},{end},2b
449+
450+
3:
451+
li {start},0
452+
li {end},0
453+
",
454+
start = out(reg) _,
455+
end = out(reg) _,
456+
input = out(reg) _,
457+
a = out(reg) _,
458+
);
459+
460+
#[cfg(target_arch = "riscv64")]
461+
core::arch::asm!(
462+
"
463+
// Copy over .data
464+
la {start},_sdata
465+
la {end},_edata
466+
la {input},_sidata
467+
468+
bgeu {start},{end},2f
469+
470+
1: // .data Main Loop
471+
ld {a},0({input})
472+
addi {input},{input},8
473+
sd {a},0({start})
474+
addi {start},{start},8
475+
bltu {start},{end},1b
476+
477+
2: // .data zero registers
478+
li {a},0
479+
li {input},0
480+
481+
la {start},_sbss
482+
la {end},_ebss
483+
484+
bgeu {start},{end},4f
485+
486+
3: // .bss main loop
487+
sd zero,0({start})
488+
addi {start},{start},8
489+
bltu {start},{end},3b
490+
491+
4: // .bss zero registers
492+
// Zero out used registers
493+
li {start},0
494+
li {end},0
495+
",
496+
start = out(reg) _,
497+
end = out(reg) _,
498+
input = out(reg) _,
499+
a = out(reg) _,
500+
);
501+
502+
compiler_fence(Ordering::SeqCst);
429503
}
430504

431505
// TODO: Enable FPU when available

0 commit comments

Comments
 (0)