diff --git a/CHANGELOG.md b/CHANGELOG.md index ed532c5..b49b13c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use inline assembly instead of pre-compiled blobs - Removed bors in favor of GitHub Merge Queue - `start_trap_rust` is now marked as `unsafe` +- Implement `r0` as inline assembly ## [v0.11.0] - 2023-01-18 diff --git a/Cargo.toml b/Cargo.toml index 719beb5..c65c24e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ s-mode = [] single-hart = [] [dependencies] -r0 = "1.0.0" riscv = "0.10" riscv-rt-macros = { path = "macros", version = "0.2.0" } diff --git a/build.rs b/build.rs index 23177c2..c23da01 100644 --- a/build.rs +++ b/build.rs @@ -5,9 +5,17 @@ use std::env; use std::fs; use std::path::PathBuf; +fn add_linker_script(bytes: &[u8]) { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + // Put the linker script somewhere the linker can find it + fs::write(out_dir.join("link.x"), bytes).unwrap(); + println!("cargo:rustc-link-search={}", out_dir.display()); + println!("cargo:rerun-if-changed=link.x"); +} + fn main() { let target = env::var("TARGET").unwrap(); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let _name = env::var("CARGO_PKG_NAME").unwrap(); // set configuration flags depending on the target @@ -17,9 +25,11 @@ fn main() { match target.bits { 32 => { println!("cargo:rustc-cfg=riscv32"); + add_linker_script(include_bytes!("link-rv32.x")); } 64 => { println!("cargo:rustc-cfg=riscv64"); + add_linker_script(include_bytes!("link-rv64.x")); } _ => panic!("Unsupported bit width"), } @@ -27,9 +37,4 @@ fn main() { println!("cargo:rustc-cfg=riscvm"); // we can expose extensions this way } } - - // Put the linker script somewhere the linker can find it - fs::write(out_dir.join("link.x"), include_bytes!("link.x")).unwrap(); - println!("cargo:rustc-link-search={}", out_dir.display()); - println!("cargo:rerun-if-changed=link.x"); } diff --git a/link.x b/link-rv32.x similarity index 99% rename from link.x rename to link-rv32.x index 57301e2..deadea8 100644 --- a/link.x +++ b/link-rv32.x @@ -84,7 +84,7 @@ SECTIONS _edata = .; } > REGION_DATA AT > REGION_RODATA - .bss (NOLOAD) : + .bss (NOLOAD) : ALIGN(4) { _sbss = .; *(.sbss .sbss.* .bss .bss.*); diff --git a/link-rv64.x b/link-rv64.x new file mode 100644 index 0000000..8128eac --- /dev/null +++ b/link-rv64.x @@ -0,0 +1,174 @@ +PROVIDE(_stext = ORIGIN(REGION_TEXT)); +PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(UserSoft = DefaultHandler); +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(UserTimer = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(UserExternal = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > REGION_TEXT + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + } > REGION_TEXT + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > REGION_RODATA + + .data : ALIGN(8) + { + _sidata = LOADADDR(.data); + _sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(8); + _edata = .; + } > REGION_DATA AT > REGION_RODATA + + .bss (NOLOAD) : ALIGN(8) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(8); + _ebss = .; + } > REGION_BSS + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > REGION_HEAP + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > REGION_STACK + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_DATA) % 8 == 0, " +ERROR(riscv-rt): the start of the REGION_DATA must be 8-byte aligned"); + +ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_STACK) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 8 == 0 && _edata % 8 == 0, " +BUG(riscv-rt): .data is not 8-byte aligned"); + +ASSERT(_sidata % 8 == 0, " +BUG(riscv-rt): the LMA of .data is not 8-byte aligned"); + +ASSERT(_sbss % 8 == 0 && _ebss % 8 == 0, " +BUG(riscv-rt): .bss is not 8-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), " +ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region. +Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/src/lib.rs b/src/lib.rs index b96c428..3e86c49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -366,6 +366,8 @@ #[cfg(riscv)] mod asm; +use core::sync::atomic::{compiler_fence, Ordering}; + #[cfg(feature = "s-mode")] use riscv::register::{scause as xcause, stvec as xtvec, stvec::TrapMode as xTrapMode}; @@ -378,19 +380,6 @@ pub use riscv_rt_macros::{entry, pre_init}; #[doc(hidden)] pub static __ONCE__: () = (); -extern "C" { - // Boundaries of the .bss section - static mut _ebss: u32; - static mut _sbss: u32; - - // Boundaries of the .data section - static mut _edata: u32; - static mut _sdata: u32; - - // Initial values of the .data section (stored in Flash) - static _sidata: u32; -} - /// Rust entry point (_start_rust) /// /// 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) -> ! { if _mp_hook(hartid) { __pre_init(); - r0::zero_bss(&mut _sbss, &mut _ebss); - r0::init_data(&mut _sdata, &mut _edata, &_sidata); + // Initialize RAM + // 1. Copy over .data from flash to RAM + // 2. Zero out .bss + + #[cfg(target_arch = "riscv32")] + core::arch::asm!( + " + // Copy over .data + la {start},_sdata + la {end},_edata + la {input},_sidata + + bgeu {start},{end},2f + 1: + lw {a},0({input}) + addi {input},{input},4 + sw {a},0({start}) + addi {start},{start},4 + bltu {start},{end},1b + + 2: + li {a},0 + li {input},0 + + // Zero out .bss + la {start},_sbss + la {end},_ebss + + bgeu {start},{end},3f + 2: + sw zero,0({start}) + addi {start},{start},4 + bltu {start},{end},2b + + 3: + li {start},0 + li {end},0 + ", + start = out(reg) _, + end = out(reg) _, + input = out(reg) _, + a = out(reg) _, + ); + + #[cfg(target_arch = "riscv64")] + core::arch::asm!( + " + // Copy over .data + la {start},_sdata + la {end},_edata + la {input},_sidata + + bgeu {start},{end},2f + + 1: // .data Main Loop + ld {a},0({input}) + addi {input},{input},8 + sd {a},0({start}) + addi {start},{start},8 + bltu {start},{end},1b + + 2: // .data zero registers + li {a},0 + li {input},0 + + la {start},_sbss + la {end},_ebss + + bgeu {start},{end},4f + + 3: // .bss main loop + sd zero,0({start}) + addi {start},{start},8 + bltu {start},{end},3b + + 4: // .bss zero registers + // Zero out used registers + li {start},0 + li {end},0 + ", + start = out(reg) _, + end = out(reg) _, + input = out(reg) _, + a = out(reg) _, + ); + + compiler_fence(Ordering::SeqCst); } // TODO: Enable FPU when available