riscv_rt/lib.rs
1//! Startup code and minimal runtime for RISC-V CPUs
2//!
3//! This crate contains all the required parts to build a `no_std` application
4//! (binary crate) that targets a RISC-V microcontroller.
5//!
6//! # Features
7//!
8//! This crate takes care of:
9//!
10//! - The memory layout of the program.
11//!
12//! - Initializing `static` variables before the program entry point.
13//!
14//! - Enabling the FPU before the program entry point if the target has the `f` or `d` extension.
15//!
16//! - Support for a runtime in supervisor mode, that can be bootstrapped via
17//! [Supervisor Binary Interface (SBI)](https://github.com/riscv-non-isa/riscv-sbi-doc).
18//!
19//! - Support for bootstrapping a runtime with [U-Boot](https://github.com/u-boot/u-boot).
20//!
21//! This crate also provides the following attributes:
22//!
23//! - Before main initialization of the `.bss` and `.data` sections.
24//!
25//! - [`#[entry]`][attr-entry] to declare the entry point of the program
26//! - [`#[exception]`][attr-exception] to override an exception handler.
27//! - [`#[core_interrupt]`][attr-core-interrupt] to override a core interrupt handler.
28//! - [`#[external_interrupt]`][attr-external-interrupt] to override an external interrupt handler.
29//!
30//! If not overridden, all exception and interrupt handlers default to an infinite loop.
31//!
32//! The documentation for these attributes can be found in the [Attribute Macros](#attributes)
33//! section.
34//!
35//! # Requirements
36//!
37//! ## `memory.x`
38//!
39//! This crate expects the user, or some other crate, to provide the memory layout of the target
40//! device via a linker script, described in this section. We refer to this file as `memory.x`.
41//!
42//! ### `MEMORY`
43//!
44//! The main information that this file must provide is the memory layout of
45//! the device in the form of the `MEMORY` command. The command is documented
46//! [here][2], but at a minimum you'll want to create at least one memory region.
47//!
48//! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html
49//!
50//! To support different relocation models (RAM-only, FLASH+RAM) multiple regions are used:
51//!
52//! - `REGION_TEXT` - for `.init`, `.trap` and `.text` sections
53//! - `REGION_RODATA` - for `.rodata` section and storing initial values for `.data` section
54//! - `REGION_DATA` - for `.data` section
55//! - `REGION_BSS` - for `.bss` section
56//! - `REGION_HEAP` - for the heap area
57//! - `REGION_STACK` - for hart stacks
58//!
59//! These aliases must be mapped to a valid `MEMORY` region. Usually, `REGION_TEXT` and
60//! `REGION_RODATA` are mapped to the flash memory, while `REGION_DATA`, `REGION_BSS`,
61//! `REGION_HEAP`, and `REGION_STACK` are mapped to the RAM.
62//!
63//! ### `_stext`
64//!
65//! This symbol provides the loading address of `.text` section. This value can be changed
66//! to override the loading address of the firmware (for example, in case of bootloader present).
67//!
68//! If omitted this symbol value will default to `ORIGIN(REGION_TEXT)`.
69//!
70//! ### `_heap_size`
71//!
72//! This symbol provides the size of a heap region. The default value is 0. You can set
73//! `_heap_size` to a non-zero value if you are planning to use heap allocations.
74//!
75//!
76//! More information about using the heap can be found in the
77//! [Using the heap](#using-the-heap) section.
78//!
79//! ### `_max_hart_id`
80//!
81//! This symbol defines the maximum hart id supported. All harts with id
82//! greater than `_max_hart_id` will be redirected to `abort()`.
83//!
84//! This symbol is supposed to be redefined in platform support crates for
85//! multi-core targets.
86//!
87//! If omitted this symbol value will default to 0 (single core).
88//!
89//! ### `_hart_stack_size`
90//!
91//! This symbol defines stack area size for *one* hart.
92//!
93//! If omitted this symbol value will default to `SIZEOF(.stack) / (_max_hart_id + 1)`.
94//!
95//! Note that due to alignment, each individual stack may differ slightly in
96//! size.
97//!
98//! ### `_stack_start`
99//!
100//! This symbol provides the address at which the call stack will be allocated.
101//! The call stack grows downwards so this address is usually set to the highest
102//! valid RAM address plus one (this *is* an invalid address but the processor
103//! will decrement the stack pointer *before* using its value as an address).
104//!
105//! In case of multiple harts present, this address defines the initial stack pointer for hart 0.
106//! Stack pointer for hart `N` is calculated as `_stack_start - N * _hart_stack_size`.
107//!
108//! If omitted this symbol value will default to `ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)`.
109//!
110//! ### Example of a fully featured `memory.x` file
111//!
112//! Next, we present a `memory.x` file that includes all the symbols
113//! that can be defined in the file. It also allocates the stack on a different RAM region:
114//!
115//! ```text
116//! /* Fully featured memory.x file */
117//! MEMORY
118//! {
119//! L2_LIM : ORIGIN = 0x08000000, LENGTH = 1M /* different RAM region for stack */
120//! RAM : ORIGIN = 0x80000000, LENGTH = 16K
121//! FLASH : ORIGIN = 0x20000000, LENGTH = 16M
122//! }
123//!
124//! REGION_ALIAS("REGION_TEXT", FLASH);
125//! REGION_ALIAS("REGION_RODATA", FLASH);
126//! REGION_ALIAS("REGION_DATA", RAM);
127//! REGION_ALIAS("REGION_BSS", RAM);
128//! REGION_ALIAS("REGION_HEAP", RAM);
129//! REGION_ALIAS("REGION_STACK", L2_LIM);
130//!
131//! _stext = ORIGIN(REGION_TEXT) + 0x400000; /* Skip first 4M of text region */
132//! _heap_size = 1K; /* Set heap size to 1KB */
133//! _max_hart_id = 1; /* Two harts present */
134//! _hart_stack_size = 1K; /* Set stack size per hart to 1KB */
135//! _stack_start = ORIGIN(L2_LIM) + LENGTH(L2_LIM);
136//! ```
137//!
138//! # Starting a minimal application
139//!
140//! This section presents a minimal application built on top of `riscv-rt`.
141//! Let's create a new binary crate:
142//!
143//! ```text
144//! $ cargo new --bin app && cd $_
145//! ```
146//!
147//! Next, we will add a few dependencies to the `Cargo.toml` file:
148//!
149//! ```toml
150//! # in Cargo.toml
151//!
152//! [dependencies]
153//! riscv-rt = "0.13.0" # <- this crate
154//! panic-halt = "1.0.0" # <- a simple panic handler
155//! ```
156//!
157//! Our application would look like this:
158//!
159//! ```ignore,no_run
160//! // src/main.rs
161//! #![no_main]
162//! #![no_std]
163//!
164//! // make sure the panic handler is linked in
165//! extern crate panic_halt;
166//!
167//! // Use `main` as the entry point of this application, which may not return.
168//! #[riscv_rt::entry]
169//! fn main() -> ! {
170//! // initialization
171//! loop {
172//! // application logic
173//! }
174//! }
175//! ```
176//!
177//! To actually build this program you need to place a `memory.x` linker script
178//! somewhere the linker can find it, e.g., in the current directory:
179//!
180//! ```text
181//! /* memory.x */
182//! MEMORY
183//! {
184//! RAM : ORIGIN = 0x80000000, LENGTH = 16K
185//! FLASH : ORIGIN = 0x20000000, LENGTH = 16M
186//! }
187//!
188//! REGION_ALIAS("REGION_TEXT", FLASH);
189//! REGION_ALIAS("REGION_RODATA", FLASH);
190//! REGION_ALIAS("REGION_DATA", RAM);
191//! REGION_ALIAS("REGION_BSS", RAM);
192//! REGION_ALIAS("REGION_HEAP", RAM);
193//! REGION_ALIAS("REGION_STACK", RAM);
194//! ```
195//!
196//! Feel free to adjust the memory layout to your needs.
197//!
198//! Next, let's make sure that Cargo uses this linker script by adding a build script:
199//!
200//! ``` ignore,no_run
201//! // build.rs
202//! use std::env;
203//! use std::fs;
204//! use std::path::PathBuf;
205//!
206//! fn main() {
207//! let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
208//!
209//! // Put the linker script somewhere the linker can find it.
210//! fs::write(out_dir.join("memory.x"), include_bytes!("memory.x")).unwrap();
211//! println!("cargo:rustc-link-search={}", out_dir.display());
212//! println!("cargo:rerun-if-changed=memory.x");
213//!
214//! println!("cargo:rerun-if-changed=build.rs");
215//! }
216//! ```
217//!
218//! In this way, the `memory.x` file will be copied to the build directory so the linker can
219//! find it. Also, we tell Cargo to re-run the build script if the `memory.x` file changes.
220//!
221//! Finally, we can add a `.cargo/config.toml` file to specify the linker script to use, as well
222//! as the target to build for when using `cargo build`. In this case, we will build for the
223//! `riscv32imac-unknown-none-elf` target:
224//!
225//! ```toml
226//! # .cargo/config.toml
227//! [target.riscv32imac-unknown-none-elf]
228//! rustflags = [
229//! "-C", "link-arg=-Tmemory.x", # memory.x must appear BEFORE link.x
230//! "-C", "link-arg=-Tlink.x",
231//! ]
232//!
233//! [build]
234//! target = "riscv32imac-unknown-none-elf"
235//! ```
236//!
237//! ``` text
238//! $ cargo build
239//!
240//! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head
241//!
242//! Disassembly of section .text:
243//!
244//! 20000000 <__stext>:
245//! 20000000: 200000b7 lui ra,0x20000
246//! 20000004: 00808067 jr 8(ra) # 20000008 <_abs_start>
247//! ```
248//!
249//! # Using the heap
250//!
251//! To use the heap, you need to define the `_heap_size` symbol in the `memory.x` file.
252//! For instance, we can define a 1 K heap region like this:
253//!
254//! ``` text
255//! /* memory.x */
256//!
257//! /* ... */
258//!
259//! _heap_size = 1K;
260//! ```
261//!
262//! The heap region will start right after the `.bss` and `.data` sections.
263//!
264//! If you plan to use heap allocations, you must include a heap allocator.
265//! For example, you can use [`embedded-alloc`](https://github.com/rust-embedded/embedded-alloc).
266//! When initializing the heap, you must provide the start address and the size of the heap.
267//! You can use the [`heap_start`] function to get the start address of the heap.
268//! This symbol is 4 byte aligned so that address will be a multiple of 4.
269//!
270//! ## Example
271//!
272//! ``` ignore,no_run
273//! extern crate some_allocator; // e.g., embedded_alloc::LlffHeap
274//!
275//! extern "C" {
276//! static _heap_size: u8;
277//! }
278//!
279//! fn main() {
280//! unsafe {
281//! let heap_bottom = riscv_rt::heap_start() as usize;
282//! let heap_size = core::ptr::addr_of!(_heap_size) as usize;
283//! some_allocator::initialize(heap_bottom, heap_size);
284//! }
285//! }
286//! ```
287//!
288//! # Additional weak functions
289//!
290//! This crate uses additional functions to control the behavior of the runtime.
291//! These functions are weakly defined in the `riscv-rt` crate, but they can be redefined
292//! in the user code. Next, we will describe these symbols and how to redefine them.
293//!
294//! ## `abort`
295//!
296//! This function is called when an unrecoverable error occurs. For example, if the
297//! current hart id is greater than `_max_hart_id`, the `abort` function is called.
298//! This function is also called when an exception or an interrupt occurs and there is no
299//! handler for it.
300//!
301//! If this function is not defined, the linker will use the `_default_abort` function
302//! defined in the `riscv-rt` crate. This function is a busy-loop that will never return.
303//!
304//! ### Note
305//!
306//! Recall that the `abort` function is called when an unrecoverable error occurs.
307//! This function should not be used to handle recoverable errors. Additionally, it may
308//! be triggered before the `.bss` and `.data` sections are initialized, so it is not safe
309//! to use any global variable in this function.
310//!
311//! ## `_pre_init_trap`
312//!
313//! This function is set as a provisional trap handler for the early trap handling.
314//! If either an exception or an interrupt occurs during the boot process, this
315//! function is triggered.
316//!
317//! If this function is not defined, the linker will use the `_default_abort` function
318//! defined in the `riscv-rt` crate. This function is a busy-loop that will never return.
319//!
320//! ### Note
321//!
322//! While this function can be redefined, it is not recommended to do so, as it is
323//! intended to be a temporary trap handler to detect bugs in the early boot process.
324//! Recall that this trap is triggered before the `.bss` and `.data` sections are
325//! initialized, so it is not safe to use any global variables in this function.
326//!
327//! Furthermore, as this function is expected to behave like a trap handler, it is
328//! necessary to make it be 4-byte aligned.
329//!
330//! ## `_mp_hook` (for multi-core targets only)
331//!
332//! This function is called from all the harts and must return true only for one hart,
333//! which will perform memory initialization. For other harts it must return false
334//! and implement wake-up in platform-dependent way (e.g., after waiting for a user interrupt).
335//!
336//! Default implementation of this function wakes hart 0 and busy-loops all the other harts.
337//!
338//! ### Note
339//!
340//! `_mp_hook` is only necessary in multi-core targets. If the `single-hart` feature is enabled,
341//! `_mp_hook` is not included in the binary.
342//!
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//! - Do **NOT** use callee-saved registers `s0-s2`, as they are used to preserve the initial values of `a0-a2` registers.
352//! - In RVE targets, do **NOT** use the `a5` register, as it is used to preserve the `a2` register.
353//!
354//! **Violating these constraints will result in incorrect arguments being passed to `main()`.**
355//!
356//! ### Implementation example
357//!
358//! The following example shows how to implement the `_mp_hook` function in assembly.
359//!
360//! ``` ignore,no_run
361//! core::arch::global_asm!(
362//! r#".section .init.mp_hook, "ax"
363//! .global _mp_hook
364//! _mp_hook:
365//! beqz a0, 2f // check if hartid is 0
366//! 1: wfi // If not, wait for interrupt in a loop
367//! j 1b
368//! 2: li a0, 1 // Otherwise, return true
369//! ret
370//! "#
371//! );
372//! ```
373//!
374//! ## `hal_main`
375//!
376//! Internally, `riscv-rt` does not jump to the `main` function created by the user using the
377//! [`#[entry]`][attr-entry] attribute. Instead, it jumps to the `hal_main` function.
378//! The linker will map `hal_main` to `main` if the prior is not defined, which is the typical case.
379//! However, the `hal_main` function allows HALs to inject additional code before jumping to the
380//! user's `main` function. This might be useful for certain HALs that need to perform additional
381//! configuration before the main function is executed.
382//!
383//! # Attributes
384//!
385//! ## Core exception handlers
386//!
387//! This functions are called when corresponding exception occurs.
388//! You can define an exception handler with the [`exception`] attribute.
389//! The attribute expects the path to the exception source as an argument.
390//!
391//! The [`exception`] attribute ensures at compile time that there is a valid
392//! exception source for the given handler.
393//!
394//! For example:
395//! ``` no_run
396//! use riscv::interrupt::Exception; // or a target-specific exception enum
397//!
398//! #[riscv_rt::exception(Exception::MachineEnvCall)]
399//! fn custom_menv_call_handler(trap_frame: &mut riscv_rt::TrapFrame) {
400//! todo!()
401//! }
402//!
403//! #[riscv_rt::exception(Exception::LoadFault)]
404//! fn custom_load_fault_handler() -> ! {
405//! loop {}
406//! }
407//! ```
408//!
409//! If exception handler is not explicitly defined, `ExceptionHandler` is called.
410//!
411//! ## `ExceptionHandler`
412//!
413//! This function is called when exception without defined exception handler is occurred.
414//! The exception reason can be decoded from the `mcause`/`scause` register.
415//!
416//! This function can be redefined in the following way:
417//!
418//! ``` ignore,no_run
419//! #[export_name = "ExceptionHandler"]
420//! fn custom_exception_handler(trap_frame: &riscv_rt::TrapFrame) -> ! {
421//! // ...
422//! }
423//! ```
424//! or
425//! ``` no_run
426//! #[no_mangle]
427//! fn ExceptionHandler(trap_frame: &mut riscv_rt::TrapFrame) {
428//! // ...
429//! }
430//! ```
431//!
432//! If `ExceptionHandler` is not defined, the linker will use the `abort` function instead.
433//!
434//! ## Core interrupt handlers
435//!
436//! This functions are called when corresponding interrupt is occurred.
437//! You can define a core interrupt handler with the [`core_interrupt`] attribute.
438//! The attribute expects the path to the interrupt source as an argument.
439//!
440//! The [`core_interrupt`] attribute ensures at compile time that there is a valid
441//! core interrupt source for the given handler.
442//!
443//! For example:
444//! ``` no_run
445//! use riscv::interrupt::Interrupt; // or a target-specific core interrupt enum
446//!
447//! #[riscv_rt::core_interrupt(Interrupt::MachineSoft)]
448//! unsafe fn custom_machine_soft_handler() {
449//! todo!()
450//! }
451//!
452//! #[riscv_rt::core_interrupt(Interrupt::MachineTimer)]
453//! fn custom_machine_timer_handler() -> ! {
454//! loop {}
455//! }
456//! ```
457//!
458//! In vectored mode, this macro will also generate a proper trap handler for the interrupt.
459//! For example, `MachineSoft` interrupt will generate a `_start_MachineSoft_trap` trap handler.
460//!
461//! If interrupt handler is not explicitly defined, `DefaultHandler` is called.
462//!
463//! ## External interrupt handlers
464//!
465//! This functions are called when corresponding interrupt is occurred.
466//! You can define an external interrupt handler with the [`external_interrupt`] attribute.
467//! The attribute expects the path to the interrupt source as an argument.
468//!
469//! The [`external_interrupt`] attribute ensures at compile time that there is a valid
470//! external interrupt source for the given handler.
471//! Note that external interrupts are target-specific and may not be available on all platforms.
472//!
473//! If interrupt handler is not explicitly defined, `DefaultHandler` is called.
474//!
475//! ## `DefaultHandler`
476//!
477//! This function is called when interrupt without defined interrupt handler is occurred.
478//! The interrupt reason can be decoded from the `mcause`/`scause` register.
479//! If it is an external interrupt, the interrupt reason can be decoded from a
480//! target-specific peripheral interrupt controller.
481//!
482//! This function can be redefined in the following way:
483//!
484//! ``` no_run
485//! #[export_name = "DefaultHandler"]
486//! unsafe fn custom_interrupt_handler() {
487//! // ...
488//! }
489//! ```
490//! or
491//! ``` no_run
492//! #[no_mangle]
493//! fn DefaultHandler() -> ! {
494//! loop {}
495//! }
496//! ```
497//!
498//! If `DefaultHandler` is not defined, the linker will use the `abort` function instead.
499//!
500//! # Cargo Features
501//!
502//! Those unfamiliar with crate dependency features may want to first refer to
503//! [The Cargo Book](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features)
504//! for a quick rundown on they work. None are enabled by default.
505//!
506//! ## `pre-init`
507//!
508//! When enabled, the runtime will execute the `__pre_init` function to be run **before RAM is initialized**.
509//! If the feature is enabled, the `__pre_init` function must be defined in the user code (i.e., no default implementation is
510//! provided by this crate). If the feature is disabled, the `__pre_init` function is not required.
511//!
512//! ### Important implementation guidelines
513//!
514//! This function is called during the early boot process. Thus, when implementing it, you **MUST** follow these guidelines:
515//!
516//! - Implement it in assembly (no Rust code is allowed at this point).
517//! - Allocate this function within the `.init` section.
518//! - Do **NOT** use callee-saved registers `s0-s2`, as they are used to preserve the initial values of `a0-a2` registers.
519//! - In RVE targets, do **NOT** use the `a5` register, as it is used to preserve the `a2` register.
520//!
521//! **Violating these constraints will result in incorrect arguments being passed to `main()`.**
522//!
523//! ### Implementation example
524//!
525//! The following example shows how to implement the `__pre_init` function in assembly.
526//!
527//! ``` ignore,no_run
528//! core::arch::global_asm!(
529//! r#".section .init.pre_init, "ax"
530//! .global __pre_init
531//! __pre_init:
532//! // Do some pre-initialization work here and return
533//! ret
534//! "#
535//! );
536//! ```
537//!
538//! ## `post-init`
539//!
540//! When enabled, the runtime will execute the `__post_init` function to be run before jumping to the main function.
541//! If the feature is enabled, the `__post_init` function must be defined in the user code (i.e., no default implementation
542//! is provided by this crate). If the feature is disabled, the `__post_init` function is not required.
543//!
544//! You can use the [`#[post_init]`][attr-post-init] attribute to define a post-init function with Rust.
545//!
546//! ## `custom-setup-interrupts`
547//!
548//! The `riscv-rt` crate provides a default implementation for the `_setup_interrupts` function.
549//! This function is called right before the main function and is responsible for setting up the interrupt controller.
550//! Default implementation sets the trap vector to `_start_trap` in direct mode. If the `v-trap` feature is enabled,
551//! the trap vector is set to `_vector_table` in vectored mode.
552//!
553//! However, in some cases, users may want to provide their own implementation of this function to customize the interrupt
554//! setup process. Users can override this function by:
555//!
556//! 1. Enabling the `custom-setup-interrupts` feature to opt-out the default implementation.
557//! 2. Using the [`#[setup_interrupts]`][attr-setup-interrupts] attribute on their custom function.
558//!
559//! This function can be redefined in the following way:
560//!
561//! ``` no_run
562//! #[riscv_rt::setup_interrupts]
563//! fn setup_interrupts(hart_id: usize) {
564//! // ...
565//! }
566//!```
567//!
568//! ## `single-hart`
569//!
570//! Saves a little code size if there is only one hart on the target.
571//!
572//! ## `no-mhartid`
573//!
574//! Skips reading `mhartid` and uses 0 instead. Useful for targets that doesn't implement this instruction.
575//! Automatically enables `single-hart`.
576//!
577//! ## `no-xtvec`
578//!
579//! Skips interrupts setup.
580//!
581//! ## `no-xie-xip`
582//!
583//! Skips disabling interrupts (to support chips without XIE/XIP CSRs).
584//!
585//! ## `custom-interrupts`
586//!
587//! Opts out of the default implementation for `_dispatch_core_interrupt` to support platforms
588//! with custom core interrupt sources.
589//!
590//! ## `custom-exceptions`
591//!
592//! Opts out of the default implementation for `_dispatch_exception` to support platforms
593//! with custom exception sources.
594//!
595//! ## `s-mode`
596//!
597//! Supervisor mode. While most registers/instructions have variants for both `mcause` and
598//! `scause`, the `mhartid` hardware thread register is not available in supervisor mode.
599//! Instead, the hartid is passed as parameter by a bootstrapping firmware (i.e., SBI).
600//!
601//! Use case: QEMU supports [OpenSBI](https://github.com/riscv-software-src/opensbi) as default firmware.
602//! Using the SBI requires riscv-rt to be run in supervisor mode instead of machine mode.
603//! ``` text
604//! APP_BINARY=$(find target -name app)
605//! sudo qemu-system-riscv64 -m 2G -nographic -machine virt -kernel $APP_BINARY
606//! ```
607//! It requires the memory layout to be non-overlapping, like
608//! ``` text
609//! MEMORY
610//! {
611//! RAM : ORIGIN = 0x80200000, LENGTH = 0x8000000
612//! FLASH : ORIGIN = 0x20000000, LENGTH = 16M
613//! }
614//! ```
615//!
616//! ## `v-trap`
617//!
618//! When vectored trap handling is enabled, the trap vector is set to `_vector_table` in vectored mode.
619//! This table is a list of `j _start_INTERRUPT_trap` instructions, where `INTERRUPT` is the name of the
620//! core interrupt.
621//!
622//! ## `u-boot`
623//!
624//! When the U-boot feature is enabled, acceptable signature for `#[post_init]`, `#[setup_interrupts]`,
625//! and`#[entry]` macros is changed. This is required because when booting from elf, U-boot passes
626//! `argc` and `argv`. This feature also implies `single-hart`. The only way to get boot-hart is through
627//! fdt, so other harts initialization is up to you.
628//!
629//! ## `pre-default-start-trap`
630//!
631//! This provides a mechanism to execute custom code prior to `_default_start_trap`.
632//!
633//! To use it, the user must define a symbol named `_pre_default_start_trap`, which the system will jump to.
634//! After executing the custom code, control should return by jumping to `_pre_default_start_trap_ret`.
635//!
636//! It's recommended to place the code in the `.trap.start` section to make sure it's reachable from `_default_start_trap`.
637//!
638//! It is expected that the custom code does not clobber any registers.
639//!
640//! Please note that your code won't be run for interrupts in vectored mode.
641//!
642//! ### Example
643//!
644//! ```rust,ignore,no_run
645//! core::arch::global_asm!(
646//! r#"
647//! .section .trap.start, "ax"
648//! .extern _pre_default_start_trap_ret
649//! .global _pre_default_start_trap
650//!
651//! _pre_default_start_trap:
652//!
653//! // your code goes here remember to not clobber any registers,
654//! // use mscratch to retain a single register if needed
655//!
656//! // jump back to continue with _default_start_trap
657//! j _pre_default_start_trap_ret
658//! "#
659//! );
660//! ```
661//!
662//! ## `device`
663//!
664//! Automatically includes `device.x` (typically provided by PACs to provide weak aliases to interrupt handlers)
665//! in the linker script.
666//!
667//! ## `memory`
668//!
669//! Automatically includes [`memory.x`](#memoryx) (typically provided by BSPs) in the linker script.
670//!
671//! ## `defmt`
672//!
673//! Implements `defmt::Format` on certain types.
674//!
675//! [attr-entry]: attr.entry.html
676//! [attr-exception]: attr.exception.html
677//! [attr-external-interrupt]: attr.external_interrupt.html
678//! [attr-core-interrupt]: attr.core_interrupt.html
679//! [attr-post-init]: attr.post_init.html
680//! [attr-setup-interrupts]: attr.setup_interrupts.html
681
682// NOTE: Adapted from cortex-m/src/lib.rs
683#![no_std]
684#![deny(missing_docs)]
685
686extern crate self as riscv_rt; // To use macros that refer to items in this crate.
687
688/// Backwards-compatibility deprecation warnings for renamed feature `no-interrupts`.
689/// If a user enables the old feature, emit a warning pointing them to the new `custom-interrupts`.
690#[cfg(feature = "no-interrupts")]
691#[deprecated(note = "feature `no-interrupts` is deprecated; use `custom-interrupts` instead")]
692pub const __RISCV_RT_DEPRECATED_NO_INTERRUPTS: () = ();
693
694#[cfg(feature = "no-interrupts")]
695#[allow(clippy::let_unit_value)]
696const _: () = {
697 let _ = __RISCV_RT_DEPRECATED_NO_INTERRUPTS;
698};
699
700/// Backwards-compatibility deprecation warnings for renamed feature `no-exceptions`.
701/// If a user enables the old feature, emit a warning pointing them to the new `custom-exceptions`.
702#[cfg(feature = "no-exceptions")]
703#[deprecated(note = "feature `no-exceptions` is deprecated; use `custom-exceptions` instead")]
704pub const __RISCV_RT_DEPRECATED_NO_EXCEPTIONS: () = ();
705
706#[cfg(feature = "no-exceptions")]
707#[allow(clippy::let_unit_value)]
708const _: () = {
709 let _ = __RISCV_RT_DEPRECATED_NO_EXCEPTIONS;
710};
711
712#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
713mod asm;
714
715#[cfg(not(feature = "custom-exceptions"))]
716pub mod exceptions;
717
718#[cfg(not(feature = "custom-interrupts"))]
719pub mod interrupts;
720
721#[cfg(feature = "s-mode")]
722use riscv::register::scause as xcause;
723#[cfg(all(feature = "s-mode", not(feature = "custom-setup-interrupts")))]
724use riscv::register::stvec::{self as xtvec, Stvec as Xtvec, TrapMode};
725
726#[cfg(not(feature = "s-mode"))]
727use riscv::register::mcause as xcause;
728#[cfg(all(not(feature = "s-mode"), not(feature = "custom-setup-interrupts")))]
729use riscv::register::mtvec::{self as xtvec, Mtvec as Xtvec, TrapMode};
730
731pub use riscv_macros::{core_interrupt, entry, exception, external_interrupt};
732pub use riscv_types::*;
733
734#[cfg(feature = "post-init")]
735pub use riscv_macros::post_init;
736
737#[cfg(feature = "custom-setup-interrupts")]
738pub use riscv_macros::setup_interrupts;
739
740/// We export this static with an informative name so that if an application attempts to link
741/// two copies of riscv-rt together, linking will fail. We also declare a links key in
742/// Cargo.toml which is the more modern way to solve the same problem, but we have to keep
743/// __ONCE__ around to prevent linking with versions before the links key was added.
744#[export_name = "error: riscv-rt appears more than once in the dependency graph"]
745#[doc(hidden)]
746pub static __ONCE__: () = ();
747
748/// Rust entry point (_start_rust)
749///
750/// Configures interrupts and calls main. This function never returns.
751///
752/// # Safety
753///
754/// This function should not be called directly by the user, and should instead
755/// be invoked by the runtime implicitly.
756#[cfg_attr(
757 any(target_arch = "riscv32", target_arch = "riscv64"),
758 link_section = ".init.rust"
759)]
760#[export_name = "_start_rust"]
761pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! {
762 extern "Rust" {
763 #[cfg(feature = "post-init")]
764 fn __post_init(a0: usize);
765 fn _setup_interrupts(a0: usize);
766 fn hal_main(a0: usize, a1: usize, a2: usize) -> !;
767 }
768
769 #[cfg(feature = "post-init")]
770 __post_init(a0);
771 _setup_interrupts(a0);
772 hal_main(a0, a1, a2);
773}
774
775/// Default implementation of `_setup_interrupts`.
776///
777/// In direct mode (i.e., `v-trap` feature disabled), it sets the trap vector to `_start_trap`.
778/// In vectored mode (i.e., `v-trap` feature enabled), it sets the trap vector to `_vector_table`.
779///
780/// # Note
781///
782/// You can define your own `_setup_interrupts` function to override the default implementation.
783/// To do so, you must enable the `custom-setup-interrupts` feature to opt-out the default implementation.
784/// Then, you can use the [`riscv_macros::setup_interrupts`] attribute on your custom function.
785/// This macro is re-exported by this crate if the `custom-setup-interrupts` feature is enabled.
786///
787/// # Safety
788///
789/// This function should not be called directly by the user, and should instead
790/// be invoked by the runtime implicitly. It is expected to be called before the main function.
791#[cfg(not(feature = "custom-setup-interrupts"))]
792#[riscv_macros::setup_interrupts]
793unsafe fn default_setup_interrupts() {
794 extern "C" {
795 #[cfg(not(feature = "v-trap"))]
796 fn _start_trap();
797 #[cfg(feature = "v-trap")]
798 fn _vector_table();
799 }
800
801 let xtvec_val = match () {
802 #[cfg(not(feature = "v-trap"))]
803 _ => Xtvec::new(_start_trap as *const () as usize, TrapMode::Direct),
804 #[cfg(feature = "v-trap")]
805 _ => Xtvec::new(_vector_table as *const () as usize, TrapMode::Vectored),
806 };
807 xtvec::write(xtvec_val);
808}
809
810/// Registers saved in trap handler
811#[repr(C)]
812#[derive(Debug)]
813#[cfg_attr(feature = "defmt", derive(defmt::Format))]
814pub struct TrapFrame {
815 /// `x1`: return address, stores the address to return to after a function call or interrupt.
816 pub ra: usize,
817 /// `x5`: temporary register `t0`, used for intermediate values.
818 pub t0: usize,
819 /// `x6`: temporary register `t1`, used for intermediate values.
820 pub t1: usize,
821 /// `x7`: temporary register `t2`, used for intermediate values.
822 pub t2: usize,
823 /// `x28`: temporary register `t3`, used for intermediate values.
824 #[cfg(riscvi)]
825 pub t3: usize,
826 /// `x29`: temporary register `t4`, used for intermediate values.
827 #[cfg(riscvi)]
828 pub t4: usize,
829 /// `x30`: temporary register `t5`, used for intermediate values.
830 #[cfg(riscvi)]
831 pub t5: usize,
832 /// `x31`: temporary register `t6`, used for intermediate values.
833 #[cfg(riscvi)]
834 pub t6: usize,
835 /// `x10`: argument register `a0`. Used to pass the first argument to a function.
836 pub a0: usize,
837 /// `x11`: argument register `a1`. Used to pass the second argument to a function.
838 pub a1: usize,
839 /// `x12`: argument register `a2`. Used to pass the third argument to a function.
840 pub a2: usize,
841 /// `x13`: argument register `a3`. Used to pass the fourth argument to a function.
842 pub a3: usize,
843 /// `x14`: argument register `a4`. Used to pass the fifth argument to a function.
844 pub a4: usize,
845 /// `x15`: argument register `a5`. Used to pass the sixth argument to a function.
846 pub a5: usize,
847 #[cfg(riscvi)]
848 /// `x16`: argument register `a6`. Used to pass the seventh argument to a function.
849 pub a6: usize,
850 #[cfg(riscvi)]
851 /// `x17`: argument register `a7`. Used to pass the eighth argument to a function.
852 pub a7: usize,
853}
854
855/// Trap entry point rust (_start_trap_rust)
856///
857/// `scause`/`mcause` is read to determine the cause of the trap. XLEN-1 bit indicates
858/// if it's an interrupt or an exception. The result is examined and one of the
859/// exception handlers or one of the core interrupt handlers is called.
860///
861/// # Note
862///
863/// Exception dispatching is performed by an extern `_dispatch_exception` function.
864/// Targets that comply with the RISC-V standard can use the implementation provided
865/// by this crate in the [`exceptions`] module. Targets with special exception sources
866/// may provide their custom implementation of the `_dispatch_exception` function. You may
867/// also need to enable the `custom-exceptions` feature to op-out the default implementation.
868///
869/// In direct mode (i.e., `v-trap` feature disabled), interrupt dispatching is performed
870/// by an extern `_dispatch_core_interrupt` function. Targets that comply with the RISC-V
871/// standard can use the implementation provided by this crate in the [`interrupts`] module.
872/// Targets with special interrupt sources may provide their custom implementation of the
873/// `_dispatch_core_interrupt` function. You may also need to enable the `custom-interrupts`
874/// feature to op-out the default implementation.
875///
876/// In vectored mode (i.e., `v-trap` feature enabled), interrupt dispatching is performed
877/// directly by hardware, and thus this function should **not** be triggered due to an
878/// interrupt. If this abnormal situation happens, this function will directly call the
879/// `DefaultHandler` function.
880///
881/// # Safety
882///
883/// This function must be called only from assembly `_start_trap` function.
884/// Do **NOT** call this function directly.
885#[cfg_attr(
886 any(target_arch = "riscv32", target_arch = "riscv64"),
887 link_section = ".trap.rust"
888)]
889#[export_name = "_start_trap_rust"]
890pub unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) {
891 extern "C" {
892 #[cfg(not(feature = "v-trap"))]
893 fn _dispatch_core_interrupt(code: usize);
894 #[cfg(feature = "v-trap")]
895 fn DefaultHandler();
896 fn _dispatch_exception(trap_frame: &TrapFrame, code: usize);
897 }
898
899 match xcause::read().cause() {
900 #[cfg(not(feature = "v-trap"))]
901 xcause::Trap::Interrupt(code) => _dispatch_core_interrupt(code),
902 #[cfg(feature = "v-trap")]
903 xcause::Trap::Interrupt(_) => DefaultHandler(),
904 xcause::Trap::Exception(code) => _dispatch_exception(&*trap_frame, code),
905 }
906}
907
908/// Returns a pointer to the start of the heap
909///
910/// The returned pointer is guaranteed to be 4-byte aligned.
911#[inline]
912pub fn heap_start() -> *mut usize {
913 extern "C" {
914 static mut __sheap: usize;
915 }
916
917 #[allow(unused_unsafe)] // no longer unsafe since rust 1.82.0
918 unsafe {
919 core::ptr::addr_of_mut!(__sheap)
920 }
921}