From 8f57be8b454d73b0f0dd403f7d51e9c83811d415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Mon, 4 Nov 2024 10:14:41 +0100 Subject: [PATCH] align symbols to cortex-m-rt convention --- riscv-rt/CHANGELOG.md | 24 ++- riscv-rt/Cargo.toml | 2 +- riscv-rt/link.x.in | 59 +++++-- riscv-rt/src/asm.rs | 10 +- riscv-rt/src/lib.rs | 379 ++++++++++++++++++++++++++---------------- 5 files changed, 310 insertions(+), 164 deletions(-) diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index 9afb714b..6ab2cb08 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -9,7 +9,29 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- `link.x.in`: remove references to `eh_frame` +- `link.x.in`: remove references to `eh_frame`. +- Rename start/end section symbols to align with `cortex-m-rt`: + - `_stext`: it remains, as linker files can modify it. + - `__stext`: it coincides with `_stext`. + - `__etext`: new symbol. It points to the end of the text section. + - `__srodata`: new symbol. It points to the start of the read-only data section. + - `__erodata`: new symbol. It points to the end of the read-only data section. + - `__sdata`: substitutes `_sdata`. It points to the start of the on-flash data section. + - `__edata`: substitutes `_edata`. It points to the end of the on-flash data section. + - `__idata`: substitutes `_idata`. It points to the start of the on-RAM data section. + - `__sbss`: substitutes `_sbss`. It points to the start of the BSS section. + - `__ebss`: substitutes `_ebss`. It points to the end of the BSS section. + - `__sheap`: substitutes `_sheap`. It points to the start of the heap section. + - `__eheap`: substitutes `_eheap`. It points to the end of the heap section. + - `__estack`: substitutes `_estack`. It points to the end of the stack section. + - `__sstack`: substitutes `_sstack`. It points to the start of the stack section. +- `__edata` and `__ebss` are now defined outside of their respective sections. + In this way, users can inject custom sections and benefit from the copying and + zeroing routines, respectively. +- As `__sheap` is now private, `riscv-rt` now provides a `heap_start` function to + allow users get the initial address of the heap when initializing an allocator. +- Update documentation. +- Removed `.init.rust` section, as it is no longer required. ## [v0.13.0] - 2024-10-19 diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index 2cc835d3..d5973f33 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -25,7 +25,7 @@ riscv-pac = { path = "../riscv-pac", version = "0.2.0" } riscv-rt-macros = { path = "macros", version = "0.2.2" } [dev-dependencies] -panic-halt = "0.2.0" +panic-halt = "1.0.0" [features] s-mode = ["riscv-rt-macros/s-mode"] diff --git a/riscv-rt/link.x.in b/riscv-rt/link.x.in index f8a44dfd..2743d397 100644 --- a/riscv-rt/link.x.in +++ b/riscv-rt/link.x.in @@ -100,10 +100,11 @@ SECTIONS .text _stext : { + __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); KEEP(*(.init.trap)); . = ALIGN(4); @@ -111,54 +112,76 @@ SECTIONS *(.trap.rust); *(.text.abort); *(.text .text.*); + + . = ALIGN(4); + __etext = .; } > REGION_TEXT .rodata : ALIGN(4) { + . = ALIGN(4); + __srodata = .; + *(.srodata .srodata.*); *(.rodata .rodata.*); - /* 4-byte align the end (VMA) of this section. + /* ${ARCH_WIDTH}-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); + . = ALIGN(${ARCH_WIDTH}); + __erodata = .; } > REGION_RODATA .data : ALIGN(${ARCH_WIDTH}) { - _sidata = LOADADDR(.data); - _sdata = .; + . = ALIGN(${ARCH_WIDTH}); + __sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ PROVIDE(__global_pointer$ = . + 0x800); *(.sdata .sdata.* .sdata2 .sdata2.*); *(.data .data.*); - . = ALIGN(${ARCH_WIDTH}); - _edata = .; + } > REGION_DATA AT > REGION_RODATA + + /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to + * use the .data loading mechanism by pushing __edata. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(${ARCH_WIDTH}); + __edata = .; + + /* LMA of .data */ + __sidata = LOADADDR(.data); .bss (NOLOAD) : ALIGN(${ARCH_WIDTH}) { - _sbss = .; - *(.sbss .sbss.* .bss .bss.*); . = ALIGN(${ARCH_WIDTH}); - _ebss = .; + __sbss = .; + + *(.sbss .sbss.* .bss .bss.*); } > REGION_BSS + /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to + * use the .bss zeroing mechanism by pushing __ebss. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(${ARCH_WIDTH}); + __ebss = .; + /* fictitious region that represents the memory available for the heap */ .heap (NOLOAD) : { - _sheap = .; + __sheap = .; . += _heap_size; . = ALIGN(4); - _eheap = .; + __eheap = .; } > REGION_HEAP /* fictitious region that represents the memory available for the stack */ .stack (NOLOAD) : { - _estack = .; + __estack = .; . = ABSOLUTE(_stack_start); - _sstack = .; + __sstack = .; } > REGION_STACK /* fake output .got section */ @@ -190,16 +213,16 @@ 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 % ${ARCH_WIDTH} == 0 && _edata % ${ARCH_WIDTH} == 0, " +ASSERT(__sdata % ${ARCH_WIDTH} == 0 && __edata % ${ARCH_WIDTH} == 0, " BUG(riscv-rt): .data is not ${ARCH_WIDTH}-byte aligned"); -ASSERT(_sidata % ${ARCH_WIDTH} == 0, " +ASSERT(__sidata % ${ARCH_WIDTH} == 0, " BUG(riscv-rt): the LMA of .data is not ${ARCH_WIDTH}-byte aligned"); -ASSERT(_sbss % ${ARCH_WIDTH} == 0 && _ebss % ${ARCH_WIDTH} == 0, " +ASSERT(__sbss % ${ARCH_WIDTH} == 0 && __ebss % ${ARCH_WIDTH} == 0, " BUG(riscv-rt): .bss is not ${ARCH_WIDTH}-byte aligned"); -ASSERT(_sheap % 4 == 0, " +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), " diff --git a/riscv-rt/src/asm.rs b/riscv-rt/src/asm.rs index bc177e7e..cf09782e 100644 --- a/riscv-rt/src/asm.rs +++ b/riscv-rt/src/asm.rs @@ -152,9 +152,9 @@ cfg_global_asm!( cfg_global_asm!( "call __pre_init // Copy .data from flash to RAM - la t0, _sdata - la t2, _edata - la t1, _sidata + la t0, __sdata + la t2, __edata + la t1, __sidata bgeu t0, t2, 2f 1: ", #[cfg(target_arch = "riscv32")] @@ -171,8 +171,8 @@ cfg_global_asm!( bltu t0, t2, 1b", " 2: // Zero out .bss - la t0, _sbss - la t2, _ebss + la t0, __sbss + la t2, __ebss bgeu t0, t2, 4f 3: ", #[cfg(target_arch = "riscv32")] diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 9705ad28..fe770d73 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -1,126 +1,44 @@ -//! Minimal startup / runtime for RISC-V CPU's +//! Startup code and minimal runtime for RISC-V CPUs //! -//! # Minimum Supported Rust Version (MSRV) -//! -//! This crate is guaranteed to compile on stable Rust 1.61 and up. It *might* -//! compile with older versions but that may change in any new patch release. +//! This crate contains all the required parts to build a `no_std` application +//! (binary crate) that targets a RISC-V microcontroller. //! //! # Features //! -//! This crate provides -//! -//! - Before main initialization of the `.bss` and `.data` sections. +//! This crates takes care of: //! -//! - `#[entry]` to declare the entry point of the program -//! - `#[pre_init]` to run code *before* `static` variables are initialized +//! - The memory layout of the program. //! -//! - A linker script that encodes the memory layout of a generic RISC-V -//! microcontroller. This linker script is missing some information that must -//! be supplied through a `memory.x` file (see example below). This file -//! must be supplied using rustflags and listed *before* `link.x`. Arbitrary -//! filename can be use instead of `memory.x`. +//! - Initializing `static` variables before the program entry point. //! -//! - A `_sheap` symbol at whose address you can locate a heap. +//! - Enabling the FPU before the program entry point if the target has the `f` or `d` extension. //! //! - Support for a runtime in supervisor mode, that can be bootstrapped via //! [Supervisor Binary Interface (SBI)](https://github.com/riscv-non-isa/riscv-sbi-doc). //! -//! ``` text -//! $ cargo new --bin app && cd $_ -//! -//! $ # add this crate as a dependency -//! $ edit Cargo.toml && cat $_ -//! [dependencies] -//! riscv-rt = "0.13.0" -//! panic-halt = "0.2.0" -//! -//! $ # memory layout of the device -//! $ edit memory.x && cat $_ -//! MEMORY -//! { -//! RAM : ORIGIN = 0x80000000, LENGTH = 16K -//! FLASH : ORIGIN = 0x20000000, LENGTH = 16M -//! } +//! - Support for bootstrapping a runtime with [U-Boot](https://github.com/u-boot/u-boot). //! -//! REGION_ALIAS("REGION_TEXT", FLASH); -//! REGION_ALIAS("REGION_RODATA", FLASH); -//! REGION_ALIAS("REGION_DATA", RAM); -//! REGION_ALIAS("REGION_BSS", RAM); -//! REGION_ALIAS("REGION_HEAP", RAM); -//! REGION_ALIAS("REGION_STACK", RAM); -//! -//! $ edit src/main.rs && cat $_ -//! ``` -//! -//! ``` ignore,no_run -//! #![no_std] -//! #![no_main] +//! This crate also provides the following attributes: //! -//! extern crate panic_halt; -//! -//! use riscv_rt::entry; -//! -//! // use `main` as the entry point of this application -//! // `main` is not allowed to return -//! #[entry] -//! fn main() -> ! { -//! // do something here -//! loop { } -//! } -//! ``` -//! -//! ``` text -//! $ mkdir .cargo && edit .cargo/config && cat $_ -//! [target.riscv32imac-unknown-none-elf] -//! rustflags = [ -//! "-C", "link-arg=-Tmemory.x", -//! "-C", "link-arg=-Tlink.x", -//! ] -//! -//! [build] -//! target = "riscv32imac-unknown-none-elf" -//! $ edit build.rs && cat $_ -//! ``` -//! -//! ``` ignore,no_run -//! use std::env; -//! use std::fs; -//! use std::path::PathBuf; -//! -//! fn main() { -//! 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("memory.x"), include_bytes!("memory.x")).unwrap(); -//! println!("cargo:rustc-link-search={}", out_dir.display()); -//! println!("cargo:rerun-if-changed=memory.x"); -//! -//! println!("cargo:rerun-if-changed=build.rs"); -//! } -//! ``` -//! -//! ``` text -//! $ cargo build -//! -//! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head +//! - Before main initialization of the `.bss` and `.data` sections. //! -//! Disassembly of section .text: +//! - [`#[entry]`][attr-entry] to declare the entry point of the program +//! - [`#[pre_init]`][attr-pre-init]to run code *before* `static` variables are initialized +//! - [`#[exception]`][attr-exception] to override an exception handler. +//! - [`#[core_interrupt]`][attr-core-interrupt] to override a core interrupt handler. +//! - [`#[external_interrupt]`][attr-external-interrupt] to override an external interrupt handler. //! -//! 20000000 <_start>: -//! 20000000: 800011b7 lui gp,0x80001 -//! 20000004: 80018193 addi gp,gp,-2048 # 80000800 <_stack_start+0xffffc800> -//! 20000008: 80004137 lui sp,0x80004 -//! ``` +//! If not overridden, all exception and interrupt handlers default to an infinite loop. //! -//! # Symbol interfaces +//! The documentation for these attributes can be found in the [Attribute Macros](#attributes) +//! section. //! -//! This crate makes heavy use of symbols, linker sections and linker scripts to -//! provide most of its functionality. Below are described the main symbol -//! interfaces. +//! # Requirements //! //! ## `memory.x` //! -//! This file supplies the information about the device to the linker. +//! This crate expects the user, or some other crate, to provide the memory layout of the target +//! device via a linker script, described in this section. We refer to this file as `memory.x`. //! //! ### `MEMORY` //! @@ -139,7 +57,9 @@ //! - `REGION_HEAP` - for the heap area //! - `REGION_STACK` - for hart stacks //! -//! Specific aliases for these regions must be defined in `memory.x` file (see example below). +//! These aliases must be mapped to a valid `MEMORY` region. Usually, `REGION_TEXT` and +//! `REGION_RODATA` are mapped to the flash memory, while `REGION_DATA`, `REGION_BSS`, +//! `REGION_HEAP`, and `REGION_STACK` are mapped to the RAM. //! //! ### `_stext` //! @@ -148,6 +68,31 @@ //! //! If omitted this symbol value will default to `ORIGIN(REGION_TEXT)`. //! +//! ### `_heap_size` +//! +//! This symbol provides the size of a heap region. The default value is 0. You can set +//! `_heap_size` to a non-zero value if you are planning to use heap allocations. +//! +//! +//! More information about using the heap can be found in the +//! [Using the heap](#using-the-heap) section. +//! +//! ### `_max_hart_id` +//! +//! This symbol defines the maximum hart id supported. All harts with id +//! greater than `_max_hart_id` will be redirected to `abort()`. +//! +//! This symbol is supposed to be redefined in platform support crates for +//! multi-core targets. +//! +//! If omitted this symbol value will default to 0 (single core). +//! +//! ### `_hart_stack_size` +//! +//! This symbol defines stack area size for *one* hart. +//! +//! If omitted this symbol value will default to 2K. +//! //! ### `_stack_start` //! //! This symbol provides the address at which the call stack will be allocated. @@ -160,14 +105,16 @@ //! //! If omitted this symbol value will default to `ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)`. //! -//! #### Example +//! ### Example of a fully featured `memory.x` file //! -//! Allocating the call stack on a different RAM region. +//! Next, we present a `memory.x` file that includes all the symbols +//! that can be defined in the file. It also allocates the stack on a different RAM region: //! -//! ``` text +//! ```text +//! /* Fully featured memory.x file */ //! MEMORY //! { -//! L2_LIM : ORIGIN = 0x08000000, LENGTH = 1M +//! L2_LIM : ORIGIN = 0x08000000, LENGTH = 1M /* different RAM region for stack */ //! RAM : ORIGIN = 0x80000000, LENGTH = 16K //! FLASH : ORIGIN = 0x20000000, LENGTH = 16M //! } @@ -179,70 +126,187 @@ //! REGION_ALIAS("REGION_HEAP", RAM); //! REGION_ALIAS("REGION_STACK", L2_LIM); //! +//! _stext = ORIGIN(REGION_TEXT) + 0x400000; /* Skip first 4M of text region */ +//! _heap_size = 1K; /* Set heap size to 1KB */ +//! _max_hart_id = 1; /* Two harts present */ +//! _hart_stack_size = 1K; /* Set stack size per hart to 1KB */ //! _stack_start = ORIGIN(L2_LIM) + LENGTH(L2_LIM); //! ``` //! -//! ### `_max_hart_id` +//! # Starting a minimal application //! -//! This symbol defines the maximum hart id supported. All harts with id -//! greater than `_max_hart_id` will be redirected to `abort()`. +//! This section presents a minimal application built on top of `riscv-rt`. +//! Let's create a new binary crate: //! -//! This symbol is supposed to be redefined in platform support crates for -//! multi-core targets. +//! ```text +//! $ cargo new --bin app && cd $_ +//! ``` //! -//! If omitted this symbol value will default to 0 (single core). +//! Next, we will add a few dependencies to the `Cargo.toml` file: //! -//! ### `_hart_stack_size` +//! ```toml +//! # in Cargo.toml //! -//! This symbol defines stack area size for *one* hart. +//! [dependencies] +//! riscv-rt = "0.13.0" # <- this crate +//! panic-halt = "1.0.0" # <- a simple panic handler +//! ``` //! -//! If omitted this symbol value will default to 2K. +//! Our application would look like this: //! -//! ### `_heap_size` +//! ```no_run +//! // src/main.rs +//! #![no_main] +//! #![no_std] //! -//! This symbol provides the size of a heap region. The default value is 0. You can set `_heap_size` -//! to a non-zero value if you are planning to use heap allocations. +//! // make sure the panic handler is linked in +//! extern crate panic_halt; //! -//! ### `_sheap` +//! // Use `main` as the entry point of this application, which may not return. +//! #[riscv_rt::entry] +//! fn main() -> ! { +//! // initialization +//! loop { +//! // application logic +//! } +//! } +//! ``` //! -//! This symbol is located in RAM right after the `.bss` and `.data` sections. -//! You can use the address of this symbol as the start address of a heap -//! region. This symbol is 4 byte aligned so that address will be a multiple of 4. +//! To actually build this program you need to place a `memory.x` linker script +//! somewhere the linker can find it, e.g., in the current directory: //! -//! #### Example +//! ```text +//! /* memory.x */ +//! MEMORY +//! { +//! RAM : ORIGIN = 0x80000000, LENGTH = 16K +//! FLASH : ORIGIN = 0x20000000, LENGTH = 16M +//! } +//! +//! REGION_ALIAS("REGION_TEXT", FLASH); +//! REGION_ALIAS("REGION_RODATA", FLASH); +//! REGION_ALIAS("REGION_DATA", RAM); +//! REGION_ALIAS("REGION_BSS", RAM); +//! REGION_ALIAS("REGION_HEAP", RAM); +//! REGION_ALIAS("REGION_STACK", RAM); +//! ``` +//! +//! Feel free to adjust the memory layout to your needs. +//! +//! Next, let's make sure that Cargo uses this linker script by adding a build script: +//! +//! ``` ignore,no_run +//! // build.rs +//! use std::env; +//! use std::fs; +//! use std::path::PathBuf; +//! +//! fn main() { +//! 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("memory.x"), include_bytes!("memory.x")).unwrap(); +//! println!("cargo:rustc-link-search={}", out_dir.display()); +//! println!("cargo:rerun-if-changed=memory.x"); +//! +//! println!("cargo:rerun-if-changed=build.rs"); +//! } +//! ``` +//! +//! In this way, the `memory.x` file will be copied to the build directory so the linker can +//! find it. Also, we tell Cargo to re-run the build script if the `memory.x` file changes. +//! +//! Finally, we can add a `.cargo/config.toml` file to specify the linker script to use, as well +//! as the target to build for when using `cargo build`. In this case, we will build for the +//! `riscv32imac-unknown-none-elf` target: +//! +//! ```toml +//! # .cargo/config.toml +//! [target.riscv32imac-unknown-none-elf] +//! rustflags = [ +//! "-C", "link-arg=-Tmemory.x", # memory.x must appear BEFORE link.x +//! "-C", "link-arg=-Tlink.x", +//! ] +//! +//! [build] +//! target = "riscv32imac-unknown-none-elf" +//! ``` +//! +//! ``` text +//! $ cargo build +//! +//! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head +//! +//! Disassembly of section .text: +//! +//! 20000000 <__stext>: +//! 20000000: 200000b7 lui ra,0x20000 +//! 20000004: 00808067 jr 8(ra) # 20000008 <_abs_start> +//! ``` +//! +//! # Using the heap +//! +//! To use the heap, you need to define the `_heap_size` symbol in the `memory.x` file. +//! For instance, we can define a 1 K heap region like this: +//! +//! ``` text +//! /* memory.x */ +//! +//! /* ... */ +//! +//! _heap_size = 1K; +//! ``` +//! +//! The heap region will start right after the `.bss` and `.data` sections. +//! +//! If you plan to use heap allocations, you must include a heap allocator. +//! For example, you can use [`embedded-alloc`](https://github.com/rust-embedded/embedded-alloc). +//! When initializing the heap, you must provide the start address and the size of the heap. +//! You can use the [`heap_start`] function to get the start address of the heap. +//! This symbol is 4 byte aligned so that address will be a multiple of 4. +//! +//! ## Example //! //! ``` no_run -//! extern crate some_allocator; +//! extern crate some_allocator; // e.g., embedded_alloc::LlffHeap //! //! extern "C" { -//! static _sheap: u8; //! static _heap_size: u8; //! } //! //! fn main() { //! unsafe { -//! let heap_bottom = &_sheap as *const u8 as usize; +//! let heap_bottom = riscv_rt::heap_start() as usize; //! let heap_size = &_heap_size as *const u8 as usize; //! some_allocator::initialize(heap_bottom, heap_size); //! } //! } //! ``` //! +//! # Additional weak functions +//! +//! This crate uses additional functions to control the behavior of the runtime. +//! These functions are weakly defined in the `riscv-rt` crate, but they can be redefined +//! in the user code. Next, we will describe these symbols and how to redefine them. +//! //! ## `_pre_init_trap` //! //! This function is set as a provisional trap handler for the early trap handling. //! If either an exception or an interrupt occurs during the boot process, this //! function is triggered. The default implementation of this function is a busy-loop. +//! +//! ### Note +//! //! While this function can be redefined, it is not recommended to do so, as it is //! intended to be a temporary trap handler to detect bugs in the early boot process. //! Recall that this trap is triggered before the `.bss` and `.data` sections are //! initialized, so it is not safe to use any global variables in this function. //! -//! ### `_mp_hook` +//! ## `_mp_hook` //! //! 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). +//! and implement wake-up in platform-dependent way (e.g., after waiting for a user interrupt). //! The parameter `hartid` specifies the hartid of the caller. //! //! This function can be redefined in the following way: @@ -256,18 +320,23 @@ //! //! Default implementation of this function wakes hart 0 and busy-loops all the other harts. //! +//! ### Note +//! //! `_mp_hook` is only necessary in multi-core targets. If the `single-hart` feature is enabled, //! `_mp_hook` is not included in the binary. //! -//! ### `_setup_interrupts` +//! ## `_setup_interrupts` //! //! This function is called right before the main function and is responsible for setting up //! the interrupt controller. //! //! Default implementation sets the trap vector to `_start_trap` in direct mode. -//! Users can override this function by defining their own `_setup_interrupts` +//! If the `v-trap` feature is enabled, the trap vector is set to `_vector_table` +//! in vectored mode. Users can override this function by defining their own `_setup_interrupts`. //! -//! ### Core exception handlers +//! # Attributes +//! +//! ## Core exception handlers //! //! This functions are called when corresponding exception occurs. //! You can define an exception handler with the [`exception`] attribute. @@ -317,7 +386,7 @@ //! //! Default implementation of this function stucks in a busy-loop. //! -//! ### Core interrupt handlers +//! ## Core interrupt handlers //! //! This functions are called when corresponding interrupt is occured. //! You can define a core interrupt handler with the [`core_interrupt`] attribute. @@ -342,10 +411,11 @@ //! ``` //! //! In vectored mode, this macro will also generate a proper trap handler for the interrupt. +//! For example, `MachineSoft` interrupt will generate a `_start_MachineSoft_trap` trap handler. //! //! If interrupt handler is not explicitly defined, `DefaultHandler` is called. //! -//! ### External interrupt handlers +//! ## External interrupt handlers //! //! This functions are called when corresponding interrupt is occured. //! You can define an external interrupt handler with the [`external_interrupt`] attribute. @@ -386,8 +456,15 @@ //! //! ## `single-hart` //! +//! The single hart feature (`single-hart) can be activated via [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html). +//! +//! For example: +//! ``` text +//! [dependencies] +//! riscv-rt = {features=["single-hart"]} +//! ``` +//! //! This feature saves a little code size if there is only one hart on the target. -//! If the `single-hart` feature is enabled, `_mp_hook` is not called. //! //! ## `s-mode` //! @@ -398,6 +475,7 @@ //! [dependencies] //! riscv-rt = {features=["s-mode"]} //! ``` +//! //! While most registers/instructions have variants for //! both `mcause` and `scause`, the `mhartid` hardware thread register is not available in supervisor //! mode. Instead, the hartid is passed as parameter by a bootstrapping firmware (i.e., SBI). @@ -426,21 +504,29 @@ //! [dependencies] //! riscv-rt = {features=["v-trap"]} //! ``` +//! //! When the vectored trap feature is enabled, the trap vector is set to `_vector_table` in vectored mode. //! This table is a list of `j _start_INTERRUPT_trap` instructions, where `INTERRUPT` is the name of the core interrupt. //! //! ## `u-boot` //! -//! The u-boot support feature (`u-boot`) can be activated via [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html). +//! The U-boot support feature (`u-boot`) can be activated via [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html). //! //! For example: //! ``` text //! [dependencies] //! riscv-rt = { features = ["u-boot"] } //! ``` -//! When the u-boot feature is enabled, acceptable signature for `#[entry]` macros is changed. This is required -//! because when booting from elf, u-boot passes `argc` and `argv`. This feature also implies `single-hart`. +//! +//! When the `u-boot` feature is enabled, acceptable signature for `#[entry]` macros is changed. This is required +//! because when booting from elf, U-boot passes `argc` and `argv`. This feature also implies `single-hart`. //! The only way to get boot-hart is through fdt, so other harts initialization is up to you. +//! +//! [attr-entry]: attr.entry.html +//! [attr-exception]: attr.exception.html +//! [attr-external-interrupt]: attr.external_interrupt.html +//! [attr-core-interrupt]: attr.core_interrupt.html +//! [attr-pre-init]: attr.pre_init.html // NOTE: Adapted from cortex-m/src/lib.rs #![no_std] @@ -552,3 +638,18 @@ pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) { xcause::Trap::Exception(code) => _dispatch_exception(&*trap_frame, code), } } + +/// Returns a pointer to the start of the heap +/// +/// The returned pointer is guaranteed to be 4-byte aligned. +#[inline] +pub fn heap_start() -> *mut usize { + extern "C" { + static mut __sheap: usize; + } + + #[allow(unused_unsafe)] // no longer unsafe since rust 1.82.0 + unsafe { + core::ptr::addr_of_mut!(__sheap) + } +}