lib.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. //! Minimal startup / runtime for RISC-V CPU's
  2. //!
  3. //! # Minimum Supported Rust Version (MSRV)
  4. //!
  5. //! This crate is guaranteed to compile on stable Rust 1.31 and up. It *might*
  6. //! compile with older versions but that may change in any new patch release.
  7. //! Note that `riscv64imac-unknown-none-elf` and `riscv64gc-unknown-none-elf` targets
  8. //! are not supported on stable yet.
  9. //!
  10. //! # Features
  11. //!
  12. //! This crate provides
  13. //!
  14. //! - Before main initialization of the `.bss` and `.data` sections.
  15. //!
  16. //! - `#[entry]` to declare the entry point of the program
  17. //! - `#[pre_init]` to run code *before* `static` variables are initialized
  18. //!
  19. //! - A linker script that encodes the memory layout of a generic RISC-V
  20. //! microcontroller. This linker script is missing some information that must
  21. //! be supplied through a `memory.x` file (see example below). This file
  22. //! must be supplied using rustflags and listed *before* `link.x`. Arbitrary
  23. //! filename can be use instead of `memory.x`.
  24. //!
  25. //! - A `_sheap` symbol at whose address you can locate a heap.
  26. //!
  27. //! ``` text
  28. //! $ cargo new --bin app && cd $_
  29. //!
  30. //! $ # add this crate as a dependency
  31. //! $ edit Cargo.toml && cat $_
  32. //! [dependencies]
  33. //! riscv-rt = "0.6.0"
  34. //! panic-halt = "0.2.0"
  35. //!
  36. //! $ # memory layout of the device
  37. //! $ edit memory.x && cat $_
  38. //! MEMORY
  39. //! {
  40. //! RAM : ORIGIN = 0x80000000, LENGTH = 16K
  41. //! FLASH : ORIGIN = 0x20000000, LENGTH = 16M
  42. //! }
  43. //!
  44. //! REGION_ALIAS("REGION_TEXT", FLASH);
  45. //! REGION_ALIAS("REGION_RODATA", FLASH);
  46. //! REGION_ALIAS("REGION_DATA", RAM);
  47. //! REGION_ALIAS("REGION_BSS", RAM);
  48. //! REGION_ALIAS("REGION_HEAP", RAM);
  49. //! REGION_ALIAS("REGION_STACK", RAM);
  50. //!
  51. //! $ edit src/main.rs && cat $_
  52. //! ```
  53. //!
  54. //! ``` ignore,no_run
  55. //! #![no_std]
  56. //! #![no_main]
  57. //!
  58. //! extern crate panic_halt;
  59. //!
  60. //! use riscv_rt::entry;
  61. //!
  62. //! // use `main` as the entry point of this application
  63. //! // `main` is not allowed to return
  64. //! #[entry]
  65. //! fn main() -> ! {
  66. //! // do something here
  67. //! loop { }
  68. //! }
  69. //! ```
  70. //!
  71. //! ``` text
  72. //! $ mkdir .cargo && edit .cargo/config && cat $_
  73. //! [target.riscv32imac-unknown-none-elf]
  74. //! rustflags = [
  75. //! "-C", "link-arg=-Tmemory.x",
  76. //! "-C", "link-arg=-Tlink.x",
  77. //! ]
  78. //!
  79. //! [build]
  80. //! target = "riscv32imac-unknown-none-elf"
  81. //! $ edit build.rs && cat $_
  82. //! ```
  83. //!
  84. //! ``` ignore,no_run
  85. //! use std::env;
  86. //! use std::fs::File;
  87. //! use std::io::Write;
  88. //! use std::path::Path;
  89. //!
  90. //! /// Put the linker script somewhere the linker can find it.
  91. //! fn main() {
  92. //! let out_dir = env::var("OUT_DIR").expect("No out dir");
  93. //! let dest_path = Path::new(&out_dir);
  94. //! let mut f = File::create(&dest_path.join("memory.x"))
  95. //! .expect("Could not create file");
  96. //!
  97. //! f.write_all(include_bytes!("memory.x"))
  98. //! .expect("Could not write file");
  99. //!
  100. //! println!("cargo:rustc-link-search={}", dest_path.display());
  101. //!
  102. //! println!("cargo:rerun-if-changed=memory.x");
  103. //! println!("cargo:rerun-if-changed=build.rs");
  104. //! }
  105. //! ```
  106. //!
  107. //! ``` text
  108. //! $ cargo build
  109. //!
  110. //! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head
  111. //!
  112. //! Disassembly of section .text:
  113. //!
  114. //! 20000000 <_start>:
  115. //! 20000000: 800011b7 lui gp,0x80001
  116. //! 20000004: 80018193 addi gp,gp,-2048 # 80000800 <_stack_start+0xffffc800>
  117. //! 20000008: 80004137 lui sp,0x80004
  118. //! ```
  119. //!
  120. //! # Symbol interfaces
  121. //!
  122. //! This crate makes heavy use of symbols, linker sections and linker scripts to
  123. //! provide most of its functionality. Below are described the main symbol
  124. //! interfaces.
  125. //!
  126. //! ## `memory.x`
  127. //!
  128. //! This file supplies the information about the device to the linker.
  129. //!
  130. //! ### `MEMORY`
  131. //!
  132. //! The main information that this file must provide is the memory layout of
  133. //! the device in the form of the `MEMORY` command. The command is documented
  134. //! [here][2], but at a minimum you'll want to create at least one memory region.
  135. //!
  136. //! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html
  137. //!
  138. //! To support different relocation models (RAM-only, FLASH+RAM) multiple regions are used:
  139. //!
  140. //! - `REGION_TEXT` - for `.init`, `.trap` and `.text` sections
  141. //! - `REGION_RODATA` - for `.rodata` section and storing initial values for `.data` section
  142. //! - `REGION_DATA` - for `.data` section
  143. //! - `REGION_BSS` - for `.bss` section
  144. //! - `REGION_HEAP` - for the heap area
  145. //! - `REGION_STACK` - for hart stacks
  146. //!
  147. //! Specific aliases for these regions must be defined in `memory.x` file (see example below).
  148. //!
  149. //! ### `_stext`
  150. //!
  151. //! This symbol provides the loading address of `.text` section. This value can be changed
  152. //! to override the loading address of the firmware (for example, in case of bootloader present).
  153. //!
  154. //! If omitted this symbol value will default to `ORIGIN(REGION_TEXT)`.
  155. //!
  156. //! ### `_stack_start`
  157. //!
  158. //! This symbol provides the address at which the call stack will be allocated.
  159. //! The call stack grows downwards so this address is usually set to the highest
  160. //! valid RAM address plus one (this *is* an invalid address but the processor
  161. //! will decrement the stack pointer *before* using its value as an address).
  162. //!
  163. //! In case of multiple harts present, this address defines the initial stack pointer for hart 0.
  164. //! Stack pointer for hart `N` is calculated as `_stack_start - N * _hart_stack_size`.
  165. //!
  166. //! If omitted this symbol value will default to `ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)`.
  167. //!
  168. //! #### Example
  169. //!
  170. //! Allocating the call stack on a different RAM region.
  171. //!
  172. //! ``` text
  173. //! MEMORY
  174. //! {
  175. //! L2_LIM : ORIGIN = 0x08000000, LENGTH = 1M
  176. //! RAM : ORIGIN = 0x80000000, LENGTH = 16K
  177. //! FLASH : ORIGIN = 0x20000000, LENGTH = 16M
  178. //! }
  179. //!
  180. //! REGION_ALIAS("REGION_TEXT", FLASH);
  181. //! REGION_ALIAS("REGION_RODATA", FLASH);
  182. //! REGION_ALIAS("REGION_DATA", RAM);
  183. //! REGION_ALIAS("REGION_BSS", RAM);
  184. //! REGION_ALIAS("REGION_HEAP", RAM);
  185. //! REGION_ALIAS("REGION_STACK", L2_LIM);
  186. //!
  187. //! _stack_start = ORIGIN(L2_LIM) + LENGTH(L2_LIM);
  188. //! ```
  189. //!
  190. //! ### `_max_hart_id`
  191. //!
  192. //! This symbol defines the maximum hart id suppoted. All harts with id
  193. //! greater than `_max_hart_id` will be redirected to `abort()`.
  194. //!
  195. //! This symbol is supposed to be redefined in platform support crates for
  196. //! multi-core targets.
  197. //!
  198. //! If omitted this symbol value will default to 0 (single core).
  199. //!
  200. //! ### `_hart_stack_size`
  201. //!
  202. //! This symbol defines stack area size for *one* hart.
  203. //!
  204. //! If omitted this symbol value will default to 2K.
  205. //!
  206. //! ### `_heap_size`
  207. //!
  208. //! This symbol provides the size of a heap region. The default value is 0. You can set `_heap_size`
  209. //! to a non-zero value if you are planning to use heap allocations.
  210. //!
  211. //! ### `_sheap`
  212. //!
  213. //! This symbol is located in RAM right after the `.bss` and `.data` sections.
  214. //! You can use the address of this symbol as the start address of a heap
  215. //! region. This symbol is 4 byte aligned so that address will be a multiple of 4.
  216. //!
  217. //! #### Example
  218. //!
  219. //! ``` no_run
  220. //! extern crate some_allocator;
  221. //!
  222. //! extern "C" {
  223. //! static _sheap: u8;
  224. //! static _heap_size: u8;
  225. //! }
  226. //!
  227. //! fn main() {
  228. //! unsafe {
  229. //! let heap_bottom = &_sheap as *const u8 as usize;
  230. //! let heap_size = &_heap_size as *const u8 as usize;
  231. //! some_allocator::initialize(heap_bottom, heap_size);
  232. //! }
  233. //! }
  234. //! ```
  235. //!
  236. //! ### `_mp_hook`
  237. //!
  238. //! This function is called from all the harts and must return true only for one hart,
  239. //! which will perform memory initialization. For other harts it must return false
  240. //! and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
  241. //!
  242. //! This function can be redefined in the following way:
  243. //!
  244. //! ``` no_run
  245. //! #[export_name = "_mp_hook"]
  246. //! pub extern "Rust" fn mp_hook() -> bool {
  247. //! // ...
  248. //! }
  249. //! ```
  250. //!
  251. //! Default implementation of this function wakes hart 0 and busy-loops all the other harts.
  252. // NOTE: Adapted from cortex-m/src/lib.rs
  253. #![no_std]
  254. #![deny(missing_docs)]
  255. #![deny(warnings)]
  256. extern crate riscv;
  257. extern crate riscv_rt_macros as macros;
  258. extern crate r0;
  259. pub use macros::{entry, pre_init};
  260. use riscv::register::mstatus;
  261. #[export_name = "error: riscv-rt appears more than once in the dependency graph"]
  262. #[doc(hidden)]
  263. pub static __ONCE__: () = ();
  264. extern "C" {
  265. // Boundaries of the .bss section
  266. static mut _ebss: u32;
  267. static mut _sbss: u32;
  268. // Boundaries of the .data section
  269. static mut _edata: u32;
  270. static mut _sdata: u32;
  271. // Initial values of the .data section (stored in Flash)
  272. static _sidata: u32;
  273. }
  274. /// Rust entry point (_start_rust)
  275. ///
  276. /// Zeros bss section, initializes data section and calls main. This function
  277. /// never returns.
  278. #[link_section = ".init.rust"]
  279. #[export_name = "_start_rust"]
  280. pub unsafe extern "C" fn start_rust() -> ! {
  281. extern "Rust" {
  282. // This symbol will be provided by the user via `#[entry]`
  283. fn main() -> !;
  284. // This symbol will be provided by the user via `#[pre_init]`
  285. fn __pre_init();
  286. fn _mp_hook() -> bool;
  287. }
  288. if _mp_hook() {
  289. __pre_init();
  290. r0::zero_bss(&mut _sbss, &mut _ebss);
  291. r0::init_data(&mut _sdata, &mut _edata, &_sidata);
  292. }
  293. // TODO: Enable FPU when available
  294. main();
  295. }
  296. /// Trap entry point rust (_start_trap_rust)
  297. ///
  298. /// mcause is read to determine the cause of the trap. XLEN-1 bit indicates
  299. /// if it's an interrupt or an exception. The result is converted to an element
  300. /// of the Interrupt or Exception enum and passed to handle_interrupt or
  301. /// handle_exception.
  302. #[link_section = ".trap.rust"]
  303. #[export_name = "_start_trap_rust"]
  304. pub extern "C" fn start_trap_rust() {
  305. extern "C" {
  306. fn trap_handler();
  307. }
  308. unsafe {
  309. // dispatch trap to handler
  310. trap_handler();
  311. // mstatus, remain in M-mode after mret
  312. mstatus::set_mpp(mstatus::MPP::Machine);
  313. }
  314. }
  315. #[doc(hidden)]
  316. #[no_mangle]
  317. pub fn default_trap_handler() {}
  318. #[doc(hidden)]
  319. #[no_mangle]
  320. pub unsafe extern "Rust" fn default_pre_init() {}
  321. #[doc(hidden)]
  322. #[no_mangle]
  323. pub extern "Rust" fn default_mp_hook() -> bool {
  324. use riscv::register::mhartid;
  325. match mhartid::read() {
  326. 0 => true,
  327. _ => loop {
  328. unsafe { riscv::asm::wfi() }
  329. },
  330. }
  331. }