lib.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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.59 and up. It *might*
  6. //! compile with older versions but that may change in any new patch release.
  7. //!
  8. //! # Features
  9. //!
  10. //! This crate provides
  11. //!
  12. //! - Before main initialization of the `.bss` and `.data` sections.
  13. //!
  14. //! - `#[entry]` to declare the entry point of the program
  15. //! - `#[pre_init]` to run code *before* `static` variables are initialized
  16. //!
  17. //! - A linker script that encodes the memory layout of a generic RISC-V
  18. //! microcontroller. This linker script is missing some information that must
  19. //! be supplied through a `memory.x` file (see example below). This file
  20. //! must be supplied using rustflags and listed *before* `link.x`. Arbitrary
  21. //! filename can be use instead of `memory.x`.
  22. //!
  23. //! - A `_sheap` symbol at whose address you can locate a heap.
  24. //!
  25. //! - 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)
  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.1"
  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;
  87. //! use std::path::PathBuf;
  88. //!
  89. //! fn main() {
  90. //! let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
  91. //!
  92. //! // Put the linker script somewhere the linker can find it.
  93. //! fs::write(out_dir.join("memory.x"), include_bytes!("memory.x")).unwrap();
  94. //! println!("cargo:rustc-link-search={}", out_dir.display());
  95. //! println!("cargo:rerun-if-changed=memory.x");
  96. //!
  97. //! println!("cargo:rerun-if-changed=build.rs");
  98. //! }
  99. //! ```
  100. //!
  101. //! ``` text
  102. //! $ cargo build
  103. //!
  104. //! $ riscv32-unknown-elf-objdump -Cd $(find target -name app) | head
  105. //!
  106. //! Disassembly of section .text:
  107. //!
  108. //! 20000000 <_start>:
  109. //! 20000000: 800011b7 lui gp,0x80001
  110. //! 20000004: 80018193 addi gp,gp,-2048 # 80000800 <_stack_start+0xffffc800>
  111. //! 20000008: 80004137 lui sp,0x80004
  112. //! ```
  113. //!
  114. //! # Symbol interfaces
  115. //!
  116. //! This crate makes heavy use of symbols, linker sections and linker scripts to
  117. //! provide most of its functionality. Below are described the main symbol
  118. //! interfaces.
  119. //!
  120. //! ## `memory.x`
  121. //!
  122. //! This file supplies the information about the device to the linker.
  123. //!
  124. //! ### `MEMORY`
  125. //!
  126. //! The main information that this file must provide is the memory layout of
  127. //! the device in the form of the `MEMORY` command. The command is documented
  128. //! [here][2], but at a minimum you'll want to create at least one memory region.
  129. //!
  130. //! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html
  131. //!
  132. //! To support different relocation models (RAM-only, FLASH+RAM) multiple regions are used:
  133. //!
  134. //! - `REGION_TEXT` - for `.init`, `.trap` and `.text` sections
  135. //! - `REGION_RODATA` - for `.rodata` section and storing initial values for `.data` section
  136. //! - `REGION_DATA` - for `.data` section
  137. //! - `REGION_BSS` - for `.bss` section
  138. //! - `REGION_HEAP` - for the heap area
  139. //! - `REGION_STACK` - for hart stacks
  140. //!
  141. //! Specific aliases for these regions must be defined in `memory.x` file (see example below).
  142. //!
  143. //! ### `_stext`
  144. //!
  145. //! This symbol provides the loading address of `.text` section. This value can be changed
  146. //! to override the loading address of the firmware (for example, in case of bootloader present).
  147. //!
  148. //! If omitted this symbol value will default to `ORIGIN(REGION_TEXT)`.
  149. //!
  150. //! ### `_stack_start`
  151. //!
  152. //! This symbol provides the address at which the call stack will be allocated.
  153. //! The call stack grows downwards so this address is usually set to the highest
  154. //! valid RAM address plus one (this *is* an invalid address but the processor
  155. //! will decrement the stack pointer *before* using its value as an address).
  156. //!
  157. //! In case of multiple harts present, this address defines the initial stack pointer for hart 0.
  158. //! Stack pointer for hart `N` is calculated as `_stack_start - N * _hart_stack_size`.
  159. //!
  160. //! If omitted this symbol value will default to `ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)`.
  161. //!
  162. //! #### Example
  163. //!
  164. //! Allocating the call stack on a different RAM region.
  165. //!
  166. //! ``` text
  167. //! MEMORY
  168. //! {
  169. //! L2_LIM : ORIGIN = 0x08000000, LENGTH = 1M
  170. //! RAM : ORIGIN = 0x80000000, LENGTH = 16K
  171. //! FLASH : ORIGIN = 0x20000000, LENGTH = 16M
  172. //! }
  173. //!
  174. //! REGION_ALIAS("REGION_TEXT", FLASH);
  175. //! REGION_ALIAS("REGION_RODATA", FLASH);
  176. //! REGION_ALIAS("REGION_DATA", RAM);
  177. //! REGION_ALIAS("REGION_BSS", RAM);
  178. //! REGION_ALIAS("REGION_HEAP", RAM);
  179. //! REGION_ALIAS("REGION_STACK", L2_LIM);
  180. //!
  181. //! _stack_start = ORIGIN(L2_LIM) + LENGTH(L2_LIM);
  182. //! ```
  183. //!
  184. //! ### `_max_hart_id`
  185. //!
  186. //! This symbol defines the maximum hart id supported. All harts with id
  187. //! greater than `_max_hart_id` will be redirected to `abort()`.
  188. //!
  189. //! This symbol is supposed to be redefined in platform support crates for
  190. //! multi-core targets.
  191. //!
  192. //! If omitted this symbol value will default to 0 (single core).
  193. //!
  194. //! ### `_hart_stack_size`
  195. //!
  196. //! This symbol defines stack area size for *one* hart.
  197. //!
  198. //! If omitted this symbol value will default to 2K.
  199. //!
  200. //! ### `_heap_size`
  201. //!
  202. //! This symbol provides the size of a heap region. The default value is 0. You can set `_heap_size`
  203. //! to a non-zero value if you are planning to use heap allocations.
  204. //!
  205. //! ### `_sheap`
  206. //!
  207. //! This symbol is located in RAM right after the `.bss` and `.data` sections.
  208. //! You can use the address of this symbol as the start address of a heap
  209. //! region. This symbol is 4 byte aligned so that address will be a multiple of 4.
  210. //!
  211. //! #### Example
  212. //!
  213. //! ``` no_run
  214. //! extern crate some_allocator;
  215. //!
  216. //! extern "C" {
  217. //! static _sheap: u8;
  218. //! static _heap_size: u8;
  219. //! }
  220. //!
  221. //! fn main() {
  222. //! unsafe {
  223. //! let heap_bottom = &_sheap as *const u8 as usize;
  224. //! let heap_size = &_heap_size as *const u8 as usize;
  225. //! some_allocator::initialize(heap_bottom, heap_size);
  226. //! }
  227. //! }
  228. //! ```
  229. //!
  230. //! ### `_mp_hook`
  231. //!
  232. //! This function is called from all the harts and must return true only for one hart,
  233. //! which will perform memory initialization. For other harts it must return false
  234. //! and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
  235. //! The parameter `hartid` specifies the hartid of the caller.
  236. //!
  237. //! This function can be redefined in the following way:
  238. //!
  239. //! ``` no_run
  240. //! #[export_name = "_mp_hook"]
  241. //! pub extern "Rust" fn mp_hook(hartid: usize) -> bool {
  242. //! // ...
  243. //! }
  244. //! ```
  245. //!
  246. //! Default implementation of this function wakes hart 0 and busy-loops all the other harts.
  247. //!
  248. //! ### `ExceptionHandler`
  249. //!
  250. //! This function is called when exception is occured. The exception reason can be decoded from the
  251. //! `mcause`/`scause` register.
  252. //!
  253. //! This function can be redefined in the following way:
  254. //!
  255. //! ``` no_run
  256. //! #[export_name = "ExceptionHandler"]
  257. //! fn custom_exception_handler(trap_frame: &riscv_rt::TrapFrame) -> ! {
  258. //! // ...
  259. //! }
  260. //! ```
  261. //! or
  262. //! ``` no_run
  263. //! #[no_mangle]
  264. //! fn ExceptionHandler(trap_frame: &riscv_rt::TrapFrame) -> ! {
  265. //! // ...
  266. //! }
  267. //! ```
  268. //!
  269. //! Default implementation of this function stucks in a busy-loop.
  270. //!
  271. //!
  272. //! ### Core interrupt handlers
  273. //!
  274. //! This functions are called when corresponding interrupt is occured.
  275. //! You can define an interrupt handler with one of the following names:
  276. //! * `UserSoft`
  277. //! * `SupervisorSoft`
  278. //! * `MachineSoft`
  279. //! * `UserTimer`
  280. //! * `SupervisorTimer`
  281. //! * `MachineTimer`
  282. //! * `UserExternal`
  283. //! * `SupervisorExternal`
  284. //! * `MachineExternal`
  285. //!
  286. //! For example:
  287. //! ``` no_run
  288. //! #[export_name = "MachineTimer"]
  289. //! fn custom_timer_handler() {
  290. //! // ...
  291. //! }
  292. //! ```
  293. //! or
  294. //! ``` no_run
  295. //! #[no_mangle]
  296. //! fn MachineTimer() {
  297. //! // ...
  298. //! }
  299. //! ```
  300. //!
  301. //! If interrupt handler is not explicitly defined, `DefaultHandler` is called.
  302. //!
  303. //! ### `DefaultHandler`
  304. //!
  305. //! This function is called when interrupt without defined interrupt handler is occured.
  306. //! The interrupt reason can be decoded from the `mcause`/`scause` register.
  307. //!
  308. //! This function can be redefined in the following way:
  309. //!
  310. //! ``` no_run
  311. //! #[export_name = "DefaultHandler"]
  312. //! fn custom_interrupt_handler() {
  313. //! // ...
  314. //! }
  315. //! ```
  316. //! or
  317. //! ``` no_run
  318. //! #[no_mangle]
  319. //! fn DefaultHandler() {
  320. //! // ...
  321. //! }
  322. //! ```
  323. //!
  324. //! Default implementation of this function stucks in a busy-loop.
  325. //!
  326. //! # Features
  327. //!
  328. //! ## `single-hart`
  329. //!
  330. //! This feature saves a little code size by removing unnecessary stack space calculation if there is only one hart on the target.
  331. //!
  332. //! ## `s-mode`
  333. //!
  334. //! The supervisor mode feature (`s-mode`) can be activated via [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html).
  335. //!
  336. //! For example:
  337. //! ``` text
  338. //! [dependencies]
  339. //! riscv-rt = {features=["s-mode"]}
  340. //! ```
  341. //! Internally, riscv-rt uses different versions of precompiled static libraries
  342. //! for (i) machine mode and (ii) supervisor mode. If the `s-mode` feature was activated,
  343. //! the build script selects the s-mode library. While most registers/instructions have variants for
  344. //! both `mcause` and `scause`, the `mhartid` hardware thread register is not available in supervisor
  345. //! mode. Instead, the hartid is passed as parameter by a bootstrapping firmware (i.e., SBI).
  346. //!
  347. //! Use case: QEMU supports [OpenSBI](https://github.com/riscv-software-src/opensbi) as default firmware.
  348. //! Using the SBI requires riscv-rt to be run in supervisor mode instead of machine mode.
  349. //! ``` text
  350. //! APP_BINARY=$(find target -name app)
  351. //! sudo qemu-system-riscv64 -m 2G -nographic -machine virt -kernel $APP_BINARY
  352. //! ```
  353. //! It requires the memory layout to be non-overlapping, like
  354. //! ``` text
  355. //! MEMORY
  356. //! {
  357. //! RAM : ORIGIN = 0x80200000, LENGTH = 0x8000000
  358. //! FLASH : ORIGIN = 0x20000000, LENGTH = 16M
  359. //! }
  360. //! ```
  361. // NOTE: Adapted from cortex-m/src/lib.rs
  362. #![no_std]
  363. #![deny(missing_docs)]
  364. #[cfg(riscv)]
  365. mod asm;
  366. #[cfg(feature = "s-mode")]
  367. use riscv::register::{scause as xcause, stvec as xtvec, stvec::TrapMode as xTrapMode};
  368. #[cfg(not(feature = "s-mode"))]
  369. use riscv::register::{mcause as xcause, mhartid, mtvec as xtvec, mtvec::TrapMode as xTrapMode};
  370. pub use riscv_rt_macros::{entry, pre_init};
  371. #[export_name = "error: riscv-rt appears more than once in the dependency graph"]
  372. #[doc(hidden)]
  373. pub static __ONCE__: () = ();
  374. extern "C" {
  375. // Boundaries of the .bss section
  376. static mut _ebss: u32;
  377. static mut _sbss: u32;
  378. // Boundaries of the .data section
  379. static mut _edata: u32;
  380. static mut _sdata: u32;
  381. // Initial values of the .data section (stored in Flash)
  382. static _sidata: u32;
  383. }
  384. /// Rust entry point (_start_rust)
  385. ///
  386. /// Zeros bss section, initializes data section and calls main. This function
  387. /// never returns.
  388. #[link_section = ".init.rust"]
  389. #[export_name = "_start_rust"]
  390. pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! {
  391. #[rustfmt::skip]
  392. extern "Rust" {
  393. // This symbol will be provided by the user via `#[entry]`
  394. fn main(a0: usize, a1: usize, a2: usize) -> !;
  395. // This symbol will be provided by the user via `#[pre_init]`
  396. fn __pre_init();
  397. fn _setup_interrupts();
  398. fn _mp_hook(hartid: usize) -> bool;
  399. }
  400. // sbi passes hartid as first parameter (a0)
  401. #[cfg(feature = "s-mode")]
  402. let hartid = a0;
  403. #[cfg(not(feature = "s-mode"))]
  404. let hartid = mhartid::read();
  405. if _mp_hook(hartid) {
  406. __pre_init();
  407. r0::zero_bss(&mut _sbss, &mut _ebss);
  408. r0::init_data(&mut _sdata, &mut _edata, &_sidata);
  409. }
  410. // TODO: Enable FPU when available
  411. _setup_interrupts();
  412. main(a0, a1, a2);
  413. }
  414. /// Registers saved in trap handler
  415. #[allow(missing_docs)]
  416. #[repr(C)]
  417. #[derive(Debug)]
  418. pub struct TrapFrame {
  419. pub ra: usize,
  420. pub t0: usize,
  421. pub t1: usize,
  422. pub t2: usize,
  423. pub t3: usize,
  424. pub t4: usize,
  425. pub t5: usize,
  426. pub t6: usize,
  427. pub a0: usize,
  428. pub a1: usize,
  429. pub a2: usize,
  430. pub a3: usize,
  431. pub a4: usize,
  432. pub a5: usize,
  433. pub a6: usize,
  434. pub a7: usize,
  435. }
  436. /// Trap entry point rust (_start_trap_rust)
  437. ///
  438. /// `scause`/`mcause` is read to determine the cause of the trap. XLEN-1 bit indicates
  439. /// if it's an interrupt or an exception. The result is examined and ExceptionHandler
  440. /// or one of the core interrupt handlers is called.
  441. #[link_section = ".trap.rust"]
  442. #[export_name = "_start_trap_rust"]
  443. pub extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) {
  444. extern "C" {
  445. fn ExceptionHandler(trap_frame: &TrapFrame);
  446. fn DefaultHandler();
  447. }
  448. unsafe {
  449. let cause = xcause::read();
  450. if cause.is_exception() {
  451. ExceptionHandler(&*trap_frame)
  452. } else {
  453. if cause.code() < __INTERRUPTS.len() {
  454. let h = &__INTERRUPTS[cause.code()];
  455. if h.reserved == 0 {
  456. DefaultHandler();
  457. } else {
  458. (h.handler)();
  459. }
  460. } else {
  461. DefaultHandler();
  462. }
  463. }
  464. }
  465. }
  466. #[doc(hidden)]
  467. #[no_mangle]
  468. #[allow(unused_variables, non_snake_case)]
  469. pub fn DefaultExceptionHandler(trap_frame: &TrapFrame) -> ! {
  470. loop {
  471. // Prevent this from turning into a UDF instruction
  472. // see rust-lang/rust#28728 for details
  473. continue;
  474. }
  475. }
  476. #[doc(hidden)]
  477. #[no_mangle]
  478. #[allow(unused_variables, non_snake_case)]
  479. pub fn DefaultInterruptHandler() {
  480. loop {
  481. // Prevent this from turning into a UDF instruction
  482. // see rust-lang/rust#28728 for details
  483. continue;
  484. }
  485. }
  486. /* Interrupts */
  487. #[doc(hidden)]
  488. pub enum Interrupt {
  489. UserSoft,
  490. SupervisorSoft,
  491. MachineSoft,
  492. UserTimer,
  493. SupervisorTimer,
  494. MachineTimer,
  495. UserExternal,
  496. SupervisorExternal,
  497. MachineExternal,
  498. }
  499. pub use self::Interrupt as interrupt;
  500. extern "C" {
  501. fn UserSoft();
  502. fn SupervisorSoft();
  503. fn MachineSoft();
  504. fn UserTimer();
  505. fn SupervisorTimer();
  506. fn MachineTimer();
  507. fn UserExternal();
  508. fn SupervisorExternal();
  509. fn MachineExternal();
  510. }
  511. #[doc(hidden)]
  512. pub union Vector {
  513. pub handler: unsafe extern "C" fn(),
  514. pub reserved: usize,
  515. }
  516. #[doc(hidden)]
  517. #[no_mangle]
  518. pub static __INTERRUPTS: [Vector; 12] = [
  519. Vector { handler: UserSoft },
  520. Vector {
  521. handler: SupervisorSoft,
  522. },
  523. Vector { reserved: 0 },
  524. Vector {
  525. handler: MachineSoft,
  526. },
  527. Vector { handler: UserTimer },
  528. Vector {
  529. handler: SupervisorTimer,
  530. },
  531. Vector { reserved: 0 },
  532. Vector {
  533. handler: MachineTimer,
  534. },
  535. Vector {
  536. handler: UserExternal,
  537. },
  538. Vector {
  539. handler: SupervisorExternal,
  540. },
  541. Vector { reserved: 0 },
  542. Vector {
  543. handler: MachineExternal,
  544. },
  545. ];
  546. #[doc(hidden)]
  547. #[no_mangle]
  548. #[rustfmt::skip]
  549. pub unsafe extern "Rust" fn default_pre_init() {}
  550. #[doc(hidden)]
  551. #[no_mangle]
  552. #[rustfmt::skip]
  553. pub extern "Rust" fn default_mp_hook(hartid: usize) -> bool {
  554. match hartid {
  555. 0 => true,
  556. _ => loop {
  557. unsafe { riscv::asm::wfi() }
  558. },
  559. }
  560. }
  561. /// Default implementation of `_setup_interrupts` that sets `mtvec`/`stvec` to a trap handler address.
  562. #[doc(hidden)]
  563. #[no_mangle]
  564. #[rustfmt::skip]
  565. pub unsafe extern "Rust" fn default_setup_interrupts() {
  566. extern "C" {
  567. fn _start_trap();
  568. }
  569. xtvec::write(_start_trap as usize, xTrapMode::Direct);
  570. }