lib.rs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. //! A minimal RISC-V's SBI implementation library in Rust.
  2. //!
  3. //! *Note: If you are a user looking for binary distribution download for RustSBI, you may consider
  4. //! using the [RustSBI Prototyping System](https://github.com/rustsbi/standalone)
  5. //! which will provide binaries for each platforms.
  6. //! If you are a vendor or contributor who wants to adapt RustSBI to your new product or board,
  7. //! you may consider adapting the Prototyping System first to get your board adapted in an afternoon;
  8. //! you are only advised to build a discrete crate if your team have a lot of time working on this board.*
  9. //!
  10. //! *For more details on binary downloads the the RustSBI Prototyping System,
  11. //! see section [Prototyping System vs discrete packages](#download-binary-file-the-prototyping-system-vs-discrete-packages).*
  12. //!
  13. //! The crate `rustsbi` acts as core trait and instance abstraction of the RustSBI ecosystem.
  14. //!
  15. //! # What is RISC-V SBI?
  16. //!
  17. //! RISC-V SBI is short for RISC-V Supervisor Binary Interface. SBI acts as an interface to environment
  18. //! for your operating system kernel.
  19. //! An SBI implementation will allow furtherly bootstrap your kernel, and provide an environment while the kernel is running.
  20. //!
  21. //! More generally, The SBI allows supervisor-mode (S-mode or VS-mode) software to be portable across
  22. //! all RISC-V implementations by defining an abstraction for platform (or hypervisor) specific functionality.
  23. //!
  24. //! # Use RustSBI services in your supervisor software
  25. //!
  26. //! SBI environment features include boot sequence and a kernel environment. To bootstrap your kernel,
  27. //! place kernel into RustSBI implementation defined address, then RustSBI will prepare an
  28. //! environment and call the entry function on this address.
  29. //!
  30. //! ## Make SBI environment calls
  31. //!
  32. //! To use the kernel environment, you either use SBI calls or emulated instructions.
  33. //! SBI calls are similar to operating systems' `syscall`s. RISC-V SBI defined many SBI extensions,
  34. //! and in each extension there are different functions, you should pick a function before calling.
  35. //! Then, you should prepare some parameters, whose definition are not the same among functions.
  36. //!
  37. //! Now you have an extension number, a function number, and a few SBI call parameters.
  38. //! You invoke a special `ecall` instruction on supervisor level, and it will trap into machine level
  39. //! SBI implementation. It will handle your `ecall`, similar to your kernel handling system calls
  40. //! from user level.
  41. //!
  42. //! SBI functions return two values other than one. First value will be an error number,
  43. //! it will tell if SBI call have succeeded, or which error have occurred.
  44. //! Second value is the real return value, its meaning is different according to which function you calls.
  45. //!
  46. //! ## Call SBI in different programming languages
  47. //!
  48. //! Making SBI calls are similar to making system calls.
  49. //!
  50. //! Extension number is required to put on register `a7`, function number on `a6` if applicable.
  51. //! Parameters should be placed from `a0` to `a5`, first into `a0`, second into `a1`, etc.
  52. //! Unused parameters can be set to any value or leave untouched.
  53. //!
  54. //! After registers are ready, invoke an instruction called `ecall`.
  55. //! Then, the return value is placed into `a0` and `a1` registers.
  56. //! The error value could be read from `a0`, and return value is placed into `a1`.
  57. //!
  58. //! In Rust, here is an example to call SBI functions using inline assembly:
  59. //!
  60. //! ```no_run
  61. //! # #[repr(C)] struct SbiRet { error: usize, value: usize }
  62. //! # const EXTENSION_BASE: usize = 0x10;
  63. //! # const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0;
  64. //! #[inline(always)]
  65. //! fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize) -> SbiRet {
  66. //! let (error, value);
  67. //! match () {
  68. //! #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
  69. //! () => unsafe { asm!(
  70. //! "ecall",
  71. //! in("a0") arg0, in("a1") arg1,
  72. //! in("a6") function, in("a7") extension,
  73. //! lateout("a0") error, lateout("a1") value,
  74. //! ) },
  75. //! #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
  76. //! () => {
  77. //! drop((extension, function, arg0, arg1));
  78. //! unimplemented!("not RISC-V instruction set architecture")
  79. //! }
  80. //! };
  81. //! SbiRet { error, value }
  82. //! }
  83. //!
  84. //! #[inline]
  85. //! pub fn get_spec_version() -> SbiRet {
  86. //! sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0)
  87. //! }
  88. //! ```
  89. //!
  90. //! SBI functions would return a result thus some of these may fail.
  91. //! In this example we only take the value, but in complete designs we should handle the `error`
  92. //! returned by SbiRet.
  93. //!
  94. //! You may use other languages to call SBI environment. In C programming language, we can call like this:
  95. //!
  96. //! ```text
  97. //! #define SBI_CALL(ext, funct, arg0, arg1, arg2, arg3) ({ \
  98. //! register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \
  99. //! register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \
  100. //! register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \
  101. //! register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); \
  102. //! register uintptr_t a6 asm ("a6") = (uintptr_t)(funct); \
  103. //! register uintptr_t a7 asm ("a7") = (uintptr_t)(ext); \
  104. //! asm volatile ("ecall" \
  105. //! : "+r" (a0), "+r" (a1) \
  106. //! : "r" (a1), "r" (a2), "r" (a3), "r" (a6), "r" (a7) \
  107. //! : "memory") \
  108. //! {a0, a1}; \
  109. //! })
  110. //!
  111. //! #define SBI_CALL_0(ext, funct) SBI_CALL(ext, funct, 0, 0, 0, 0)
  112. //!
  113. //! static inline sbiret get_spec_version() {
  114. //! SBI_CALL_0(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION)
  115. //! }
  116. //! ```
  117. //!
  118. //! # Implement RustSBI on machine environment
  119. //!
  120. //! Boards, SoC vendors, machine environment emulators and research projects may adapt RustSBI
  121. //! to specific environments.
  122. //! RustSBI project supports these demands either by discrete package or the Prototyping System.
  123. //! Developers may choose the Prototyping System to shorten development time,
  124. //! or discrete packages to include fine-grained features.
  125. //!
  126. //! Hypervisor and supervisor environment emulator developers may refer to
  127. //! [Hypervisor and emulator development with RustSBI](#hypervisor-and-emulator-development-with-rustsbi)
  128. //! for such purposes as RustSBI provide different set of features dedicated for emulated or virtual
  129. //! environments.
  130. //!
  131. //! ## Use the Prototyping System
  132. //!
  133. //! The RustSBI Prototyping System aims to get your platform working with SBI in an afternoon.
  134. //! It supports most RISC-V platforms available by providing scalable set of drivers and features.
  135. //! It provides custom features such as Penglai TEE, DramForever's emulated hypervisor extension, and Raven
  136. //! the firmware debugger framework.
  137. //!
  138. //! You may find further documents on [RustSBI Prototyping System repository](https://github.com/rustsbi/standalone).
  139. //!
  140. //! ## Discrete RustSBI package on bare metal RISC-V hardware
  141. //!
  142. //! Discrete packages provide developers with most scalability and complete control of underlying
  143. //! hardware. It is ideal if advanced low power features, management cores and other features should
  144. //! be used in this implementation.
  145. //!
  146. //! RustSBI supports discrete package by default. Create a new `#![no_std]` bare-metal package
  147. //! to get started. Add following lines to `Cargo.toml`:
  148. //!
  149. //! ```toml
  150. //! [dependencies]
  151. //! rustsbi = { version = "0.4.0", features = ["machine"] }
  152. //! ```
  153. //!
  154. //! The feature `machine` indicates that RustSBI library is run directly on machine mode RISC-V
  155. //! environment; it will use `riscv` crate to fetch machine mode environment, which fits our demand
  156. //! of using it on bare metal RISC-V.
  157. //!
  158. //! After hardware initialization process, the part of firmware with RustSBI linked should run on memory
  159. //! blocks with fast accesses, as it would be called frequently by operating system.
  160. //! If the supervisor is called by trap generator semantics, insert `rustsbi::RustSBI` structure
  161. //! in your hart executor structure.
  162. //!
  163. //! ```rust
  164. //! # struct Clint;
  165. //! # struct MyPlatRfnc;
  166. //! # struct MyPlatHsm;
  167. //! # struct MyBoardPower;
  168. //! # struct MyPlatPmu;
  169. //! # struct MyPlatDbcn;
  170. //! # struct MyPlatSusp;
  171. //! # struct MyPlatCppc;
  172. //! use rustsbi::RustSBI;
  173. //!
  174. //! # struct SupervisorContext;
  175. //! /// Executes the supervisor within.
  176. //! struct Executor {
  177. //! ctx: SupervisorContext,
  178. //! /* other environment variables ... */
  179. //! // sbi: RustSBI<Clint, Clint, MyPlatRfnc, MyPlatHsm, MyBoardPower, MyPlatPmu, MyPlatDbcn, MyPlatSusp, MyPlatCppc>,
  180. //! /* custom_1: CustomSBI<...> */
  181. //! }
  182. //!
  183. //! # struct Trap;
  184. //! impl Executor {
  185. //! /// A function that runs the provided supervisor, uses `&mut self` for it
  186. //! /// modifies `SupervisorContext`.
  187. //! ///
  188. //! /// It returns for every Trap the supervisor produces. Its handler should read
  189. //! /// and modify `self.ctx` if necessary. After handled, `run()` this structure
  190. //! /// again or exit execution process.
  191. //! pub fn run(&mut self) -> Trap {
  192. //! todo!("fill in generic or platform specific trampoline procedure")
  193. //! }
  194. //! }
  195. //! ```
  196. //!
  197. //! After each `run()`, process the trap returned with the RustSBI instance in executor.
  198. //! Call `RustSBI::handle_ecall` and fill in developer provided `SupervisorContext` if necessary.
  199. //!
  200. //! ```no_run
  201. //! # use sbi_spec::binary::SbiRet;
  202. //! # struct RustSBI {} // Mock, prevent doc test error when feature singleton is enabled
  203. //! # impl RustSBI { fn handle_ecall(&self, e: (), f: (), p: ()) -> SbiRet { SbiRet::success(0) } }
  204. //! # struct Executor { sbi: RustSBI }
  205. //! # #[derive(Copy, Clone)] enum Trap { Exception(Exception) }
  206. //! # impl Trap { fn cause(&self) -> Self { *self } }
  207. //! # #[derive(Copy, Clone)] enum Exception { SupervisorEcall }
  208. //! # impl Executor {
  209. //! # fn new(board_params: BoardParams) -> Executor { let _ = board_params; Executor { sbi: RustSBI {} } }
  210. //! # fn run(&mut self) -> Trap { Trap::Exception(Exception::SupervisorEcall) }
  211. //! # fn sbi_extension(&self) -> () { }
  212. //! # fn sbi_function(&self) -> () { }
  213. //! # fn sbi_params(&self) -> () { }
  214. //! # fn fill_sbi_return(&mut self, ans: SbiRet) { let _ = ans; }
  215. //! # }
  216. //! # struct BoardParams;
  217. //! # const MY_SPECIAL_EXIT: usize = 0x233;
  218. //! /// Board specific power operations.
  219. //! enum Operation {
  220. //! Reboot,
  221. //! Shutdown,
  222. //! }
  223. //!
  224. //! # impl From<SbiRet> for Operation { fn from(_: SbiRet) -> Self { todo!() } }
  225. //! /// Execute supervisor in given board parameters.
  226. //! pub fn execute_supervisor(board_params: BoardParams) -> Operation {
  227. //! let mut exec = Executor::new(board_params);
  228. //! loop {
  229. //! let trap = exec.run();
  230. //! if let Trap::Exception(Exception::SupervisorEcall) = trap.cause() {
  231. //! let ans = exec.sbi.handle_ecall(
  232. //! exec.sbi_extension(),
  233. //! exec.sbi_function(),
  234. //! exec.sbi_params(),
  235. //! );
  236. //! if ans.error == MY_SPECIAL_EXIT {
  237. //! break Operation::from(ans)
  238. //! }
  239. //! // This line would also advance `sepc` with `4` to indicate the `ecall` is handled.
  240. //! exec.fill_sbi_return(ans);
  241. //! } else {
  242. //! // other trap types ...
  243. //! }
  244. //! }
  245. //! }
  246. //! ```
  247. //!
  248. //! Now, call supervisor execution function in your bare metal package to finish the discrete
  249. //! package project.
  250. //!
  251. //! ```no_run
  252. //! # #[cfg(nightly)] // disable checks
  253. //! #[naked]
  254. //! #[link_section = ".text.entry"]
  255. //! #[export_name = "_start"]
  256. //! unsafe extern "C" fn entry() -> ! {
  257. //! #[link_section = ".bss.uninit"]
  258. //! static mut SBI_STACK: [u8; LEN_STACK_SBI] = [0; LEN_STACK_SBI];
  259. //!
  260. //! // Note: actual assembly code varies between platforms.
  261. //! // Double check documents before continue on.
  262. //! core::arch::asm!(
  263. //! // 1. Turn off interrupt
  264. //! "csrw mie, zero",
  265. //! // 2. Initialize programming langauge runtime
  266. //! // only clear bss if hartid is zero
  267. //! "csrr t0, mhartid",
  268. //! "bnez t0, 2f",
  269. //! // clear bss segment
  270. //! "la t0, sbss",
  271. //! "la t1, ebss",
  272. //! "1:",
  273. //! "bgeu t0, t1, 2f",
  274. //! "sd zero, 0(t0)",
  275. //! "addi t0, t0, 8",
  276. //! "j 1b",
  277. //! "2:",
  278. //! // 3. Prepare stack for each hart
  279. //! "la sp, {stack}",
  280. //! "li t0, {per_hart_stack_size}",
  281. //! "csrr t1, mhartid",
  282. //! "addi t1, t1, 1",
  283. //! "1: ",
  284. //! "add sp, sp, t0",
  285. //! "addi t1, t1, -1",
  286. //! "bnez t1, 1b",
  287. //! "j {rust_main}",
  288. //! // 4. Clean up
  289. //! "j {finalize}",
  290. //! per_hart_stack_size = const LEN_STACK_PER_HART,
  291. //! stack = sym SBI_STACK,
  292. //! rust_main = sym rust_main,
  293. //! finalize = sym finalize,
  294. //! options(noreturn)
  295. //! )
  296. //! }
  297. //!
  298. //! # fn board_init_once() {}
  299. //! # fn print_information_once() {}
  300. //! # fn execute_supervisor(_bp: &()) -> Operation { Operation::Shutdown }
  301. //! /// Power operation after main function
  302. //! enum Operation {
  303. //! Reboot,
  304. //! Shutdown,
  305. //! // Add board specific low power modes if necessary. This will allow the
  306. //! // function `finalize` to operate on board specific power management chips.
  307. //! }
  308. //!
  309. //! /// Rust entry, call in `entry` assembly function
  310. //! extern "C" fn rust_main(_hartid: usize, opaque: usize) -> Operation {
  311. //! // .. board initialization process ...
  312. //! let board_params = board_init_once();
  313. //! // .. print necessary information and rustsbi::LOGO ..
  314. //! print_information_once();
  315. //! // execute supervisor, return as Operation
  316. //! execute_supervisor(&board_params)
  317. //! }
  318. //!
  319. //! # fn wfi() {}
  320. //! /// Perform board specific power operations
  321. //! ///
  322. //! /// The function here provides a stub to example power operations.
  323. //! /// Actual board developers should provide with more practical communications
  324. //! /// to external chips on power operation.
  325. //! unsafe extern "C" fn finalize(op: Operation) -> ! {
  326. //! match op {
  327. //! Operation::Shutdown => {
  328. //! // easiest way to make a hart look like powered off
  329. //! loop { wfi(); }
  330. //! }
  331. //! Operation::Reboot => {
  332. //! # fn entry() -> ! { loop {} } // mock
  333. //! // easiest software reset is to jump to entry directly
  334. //! entry()
  335. //! }
  336. //! // .. more power operations goes here ..
  337. //! }
  338. //! }
  339. //! ```
  340. //!
  341. //! Now RustSBI would run on machine environment, you may start a kernel or use an SBI test suite
  342. //! to check if it is properly implemented.
  343. //!
  344. //! Some platforms would provide system memory under different grades in speed and size to reduce product cost.
  345. //! Those platforms would typically provide two parts of code memory, first one being relatively small, not fast
  346. //! but instantly available after chip start, while the second one is larger in size but typically requires
  347. //! memory training. The former one would include built-in SRAM memory, and the later would include
  348. //! external SRAM or DDR memory. On those platforms, a first stage bootloader is typically needed to
  349. //! train memory for later stages. In such situation, RustSBI implementation should be linked or concatenated
  350. //! to the second stage bootloader, and the first stage could be a standalone binary package bundled with it.
  351. //!
  352. //! # Hypervisor and emulator development with RustSBI
  353. //!
  354. //! RustSBI crate supports to develop RISC-V emulators, and both Type-1 and Type-2 hypervisors.
  355. //! Hypervisor developers may find it easy to handle standard SBI functions with an instance
  356. //! based RustSBI interface.
  357. //!
  358. //! ## Hypervisors using RustSBI
  359. //!
  360. //! Both Type-1 and Type-2 hypervisors on RISC-V run on HS-mode hardware. Depending on demands
  361. //! of virtualized systems, hypervisors may either provide transparent information from host machine
  362. //! or provide another set of information to override the current environment. Notably,
  363. //! RISC-V hypervisors do not have direct access to machine mode (M-mode) registers.
  364. //!
  365. //! RustSBI supports both by providing a `EnvInfo` structure in instance based interface.
  366. //! If RISC-V hypervisors choose to use existing information on current machine, it may require
  367. //! to call underlying M-mode environment using SBI calls and fill in information into `EnvInfo`.
  368. //! If hypervisors use customized information other than taking the same one from the
  369. //! environment they reside in, they may fill in custom one into `EnvInfo` structures.
  370. //! When creating RustSBI instance, `EnvInfo` structure is required as an input of constructor.
  371. //!
  372. //! To begin with, include RustSBI library in file `Cargo.toml`:
  373. //!
  374. //! ```toml
  375. //! [dependencies]
  376. //! rustsbi = "0.4.0"
  377. //! ```
  378. //!
  379. //! This will disable default feature `machine` which will assume that RustSBI runs on M-mode directly,
  380. //! which is not appropriate in our purpose. After that, a `RustSBI` instance may be placed
  381. //! in the virtual machine structure to prepare for SBI environment:
  382. //!
  383. //! ```rust
  384. //! # struct RustSBI<>();
  385. //! struct VmHart {
  386. //! // other fields ...
  387. //! env: RustSBI</* Types, .. */>,
  388. //! }
  389. //! ```
  390. //!
  391. //! When the virtual machine hart traps into hypervisor, its code should decide whether
  392. //! this trap is an SBI environment call. If that is true, pass in parameters by `env.handle_ecall`
  393. //! function. RustSBI will handle with SBI standard constants, call corresponding extension module
  394. //! and provide parameters according to the extension and function IDs.
  395. //!
  396. //! Crate `rustsbi` adapts to standard RISC-V SBI calls.
  397. //! If the hypervisor has custom SBI extensions that RustSBI does not recognize, those extension
  398. //! and function IDs can be checked before calling RustSBI `env.handle_ecall`.
  399. //!
  400. //! ```no_run
  401. //! # use sbi_spec::binary::SbiRet;
  402. //! # struct MyExtensionEnv {}
  403. //! # impl MyExtensionEnv { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
  404. //! # struct RustSBI {} // Mock, prevent doc test error when feature singleton is enabled
  405. //! # impl RustSBI { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
  406. //! # struct VmHart { my_extension_env: MyExtensionEnv, env: RustSBI }
  407. //! # #[derive(Copy, Clone)] enum Trap { Exception(Exception) }
  408. //! # impl Trap { fn cause(&self) -> Self { *self } }
  409. //! # #[derive(Copy, Clone)] enum Exception { SupervisorEcall }
  410. //! # impl VmHart {
  411. //! # fn new() -> VmHart { VmHart { my_extension_env: MyExtensionEnv {}, env: RustSBI {} } }
  412. //! # fn run(&mut self) -> Trap { Trap::Exception(Exception::SupervisorEcall) }
  413. //! # fn trap_params(&self) -> () { }
  414. //! # fn fill_in(&mut self, ans: SbiRet) { let _ = ans; }
  415. //! # }
  416. //! let mut hart = VmHart::new();
  417. //! loop {
  418. //! let trap = hart.run();
  419. //! if let Trap::Exception(Exception::SupervisorEcall) = trap.cause() {
  420. //! // Firstly, handle custom extensions
  421. //! let my_extension_sbiret = hart.my_extension_env.handle_ecall(hart.trap_params());
  422. //! // If custom extension handles correctly, fill in its result and continue to hart.
  423. //! // The custom handler may handle `probe_extension` in `base` extension as well
  424. //! // to allow detections to whether custom extension exists.
  425. //! if my_extension_sbiret != SbiRet::not_supported() {
  426. //! hart.fill_in(my_extension_sbiret);
  427. //! continue;
  428. //! }
  429. //! // Then, if it's not a custom extension, handle it using standard SBI handler.
  430. //! let standard_sbiret = hart.env.handle_ecall(hart.trap_params());
  431. //! hart.fill_in(standard_sbiret);
  432. //! }
  433. //! }
  434. //! ```
  435. //!
  436. //! RustSBI would interact well with custom extension environments in this way.
  437. //!
  438. //! ## Emulators using RustSBI
  439. //!
  440. //! RustSBI library may be used to write RISC-V emulators. Other than hardware accelereted binary
  441. //! translation methods, emulators typically do not use host hardware specific features,
  442. //! thus may build and run on any architecture.
  443. //! Like hardware RISC-V implementations, software emulated RISC-V environment would still need SBI
  444. //! implementation to support supervisor environment.
  445. //!
  446. //! Writing emulators would follow the similiar process with writing hypervisors, see
  447. //! [Hypervisors using RustSBI](#hypervisors-using-rustsbi) for details.
  448. //!
  449. //! # Download binary file: the Prototyping System vs discrete packages
  450. //!
  451. //! RustSBI ecosystem would typically provide support for most platforms. Those support packages
  452. //! would be provided either from the RustSBI Prototyping System or vendor provided discrete SBI
  453. //! implementation packages.
  454. //!
  455. //! The RustSBI Prototyping System is a universal support package provided by RustSBI ecosystem.
  456. //! It is designed to save development time while providing most SBI features possible.
  457. //! It also includes a universal test kernel to allow testing SBI implementations on current environment.
  458. //! Users may choose to download from [Prototyping System repository](https://github.com/rustsbi/standalone)
  459. //! to get various types of RustSBI packages for their boards.
  460. //! Vendors and contributors may find it easy to adapt new SoCs and boards into Prototyping System.
  461. //!
  462. //! Discrete SBI packages are SBI environment support packages specially designed for one board
  463. //! or SoC, it will be provided by board vendor or RustSBI ecosystem.
  464. //! Vendors may find it easy to include fine grained features in each support package, but the
  465. //! maintenance situation would vary between vendors and it would likely to cost a lot of time
  466. //! to develop from a bare-metal executable. Users may find a boost in performance, energy saving
  467. //! indexes and feature granularity in discrete packages, but it would depend on whether the
  468. //! vendor provides it.
  469. //!
  470. //! To download binary package for the Prototyping System, visit its project website for a download link.
  471. //! To download them for discrete packages, RustSBI users may visit distribution source of SoC or board
  472. //! manufacturers. Additionally, users may visit [the awesome page](https://github.com/rustsbi/awesome-rustsbi)
  473. //! for a curated list of both Prototyping System and discrete packages provided by RustSBI ecosystem.
  474. //!
  475. //! # Notes for RustSBI developers
  476. //!
  477. //! Following useful hints are for firmware and kernel developers when working with SBI and RustSBI.
  478. //!
  479. //! ## RustSBI is a library for interfaces
  480. //!
  481. //! This library adapts to individual Rust traits to provide basic SBI features.
  482. //! When building for your own platform, implement traits in this library and pass them to the functions
  483. //! begin with `init`. After that, you may call `rustsbi::ecall`, `RustSBI::handle_ecall` or
  484. //! similiar functions in your own exception handler.
  485. //! It would dispatch parameters from supervisor to the traits to execute SBI functions.
  486. //!
  487. //! The library also implements useful functions which may help with platform specific binaries.
  488. //! The `LOGO` can be printed if necessary when the binary is initializing.
  489. //!
  490. //! Note that this crate is a library which contains common building blocks in SBI implementation.
  491. //! The RustSBI ecosystem would provide different level of support for each board, those support
  492. //! packages would use `rustsbi` crate as library to provide different type of SBI binary releases.
  493. //!
  494. //! ## Hardware discovery and feature detection
  495. //!
  496. //! According to the RISC-V SBI specification, SBI itself does not specify any method for hardware discovery.
  497. //! The supervisor software must rely on the other industry standard hardware
  498. //! discovery methods (i.e. Device Tree or ACPI) for that purpose.
  499. //!
  500. //! To detect any feature under bare metal or under supervisor level, developers may depend on
  501. //! any hardware discovery methods, or use try-execute-trap method to detect any instructions or
  502. //! CSRs. If SBI is implemented in user level emulators, it may require to depend on operating
  503. //! system calls or use the signal trap method to detect any RISC-V core features.
  504. #![no_std]
  505. mod console;
  506. mod cppc;
  507. mod hart_mask;
  508. mod hsm;
  509. // mod instance;
  510. mod ipi;
  511. mod nacl;
  512. mod pmu;
  513. mod reset;
  514. mod rfence;
  515. mod sta;
  516. mod susp;
  517. mod timer;
  518. mod traits;
  519. /// The RustSBI logo without blank lines on the beginning.
  520. pub const LOGO: &str = r".______ __ __ _______.___________. _______..______ __
  521. | _ \ | | | | / | | / || _ \ | |
  522. | |_) | | | | | | (----`---| |----`| (----`| |_) || |
  523. | / | | | | \ \ | | \ \ | _ < | |
  524. | |\ \----.| `--' |.----) | | | .----) | | |_) || |
  525. | _| `._____| \______/ |_______/ |__| |_______/ |______/ |__|";
  526. // RustSBI supports RISC-V SBI specification 2.0-rc1
  527. const SBI_SPEC_MAJOR: usize = 2;
  528. const SBI_SPEC_MINOR: usize = 0;
  529. /// RustSBI implementation ID: 4
  530. ///
  531. /// Ref: https://github.com/riscv-non-isa/riscv-sbi-doc/pull/61
  532. const IMPL_ID_RUSTSBI: usize = 4;
  533. const RUSTSBI_VERSION_MAJOR: usize = (env!("CARGO_PKG_VERSION_MAJOR").as_bytes()[0] - b'0') as _;
  534. const RUSTSBI_VERSION_MINOR: usize = (env!("CARGO_PKG_VERSION_MINOR").as_bytes()[0] - b'0') as _;
  535. const RUSTSBI_VERSION_PATCH: usize = (env!("CARGO_PKG_VERSION_PATCH").as_bytes()[0] - b'0') as _;
  536. const RUSTSBI_VERSION: usize =
  537. (RUSTSBI_VERSION_MAJOR << 16) + (RUSTSBI_VERSION_MINOR << 8) + RUSTSBI_VERSION_PATCH;
  538. /// RustSBI version as a string.
  539. pub const VERSION: &str = env!("CARGO_PKG_VERSION");
  540. pub extern crate sbi_spec as spec;
  541. /// Generate `RustSBI` implementation for structure of each extensions.
  542. ///
  543. /// # Usage
  544. ///
  545. /// The `#[derive(RustSBI)]` macro provides a convenient way of building `RustSBI` trait implementations.
  546. /// To use this macro, say that we have a struct `MyFence` with RISC-V SBI Remote Fence extension
  547. /// implemented using `rustsbi::Fence` trait. Then, we build a struct around it, representing a
  548. /// whole SBI implementation including one `Fence` extension only; we can name it `MySBI`:
  549. ///
  550. /// ```rust
  551. /// struct MySBI {
  552. /// fence: MyFence,
  553. /// }
  554. ///
  555. /// struct MyFence { /* fields */ }
  556. ///
  557. /// # use rustsbi::{RustSBI, HartMask};
  558. /// # use sbi_spec::binary::SbiRet;
  559. /// impl rustsbi::Fence for MyFence {
  560. /// /* implementation details */
  561. /// # fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
  562. /// # fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
  563. /// # fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
  564. /// }
  565. /// ```
  566. ///
  567. /// Here, we declared the field named `fence` with type `MyFence`. The variable name `fence` is special,
  568. /// it tells RustSBI derive macro that this field implements SBI Remote Fence instead of other SBI extensions.
  569. /// We can continue to adding more fields into `MySBI`. For example, if we have RISC-V SBI Time extension
  570. /// implementation with type `MyTimer`, we can add it to `MySBI`:
  571. ///
  572. /// ```rust
  573. /// struct MySBI {
  574. /// fence: MyFence,
  575. /// timer: MyTimer,
  576. /// }
  577. /// # struct MyFence;
  578. /// # struct MyTimer;
  579. /// ```
  580. ///
  581. /// Don't forget that the name `timer` is also a special field name. There is a detailed list after this
  582. /// chapter describing what special field name would the `RustSBI` macro identify.
  583. ///
  584. /// It looks like we are ready to derive `RustSBI` macro on `MySBI`! Let's try it now ...
  585. ///
  586. /// ```compile_fail
  587. /// #[derive(RustSBI)]
  588. /// struct MySBI {
  589. /// fence: MyFence,
  590. /// timer: MyTimer,
  591. /// # #[cfg(feature = "machine")] info: () // compile would success on #[cfg(feature = "machine")], cause it always to fail
  592. /// }
  593. /// # use rustsbi::{RustSBI, HartMask};
  594. /// # use sbi_spec::binary::SbiRet;
  595. /// # struct MyFence;
  596. /// # impl rustsbi::Fence for MyFence {
  597. /// # fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
  598. /// # fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
  599. /// # fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
  600. /// # }
  601. /// # struct MyTimer;
  602. /// # impl rustsbi::Timer for MyTimer {
  603. /// # fn set_timer(&self, stime_value: u64) { unimplemented!() }
  604. /// # }
  605. /// ```
  606. ///
  607. /// Oops! Compile failed. We'd check what happened here:
  608. ///
  609. /// ```text
  610. /// error: can't derive RustSBI: #[cfg(feature = "machine")] is needed to derive RustSBI with no extra `EnvInfo` provided; consider adding an `info` parameter to provide machine information implementing `rustsbi::EnvInfo` if RustSBI is not run on machine mode.
  611. /// --> example.rs:LL:10
  612. /// |
  613. /// LL | #[derive(RustSBI)]
  614. /// | ^^^^^^^
  615. /// |
  616. /// = note: this error originates in the derive macro `RustSBI` (in Nightly builds, run with -Z macro-backtrace for more info)
  617. ///
  618. /// error: aborting due to previous error
  619. /// ```
  620. ///
  621. /// The error message hints that we didn't provide any SBI environment information implementing trait
  622. /// `EnvInfo`. By default, RustSBI is targeted to provide RISC-V supervisor environment on any hardware,
  623. /// targeting hypervisor, emulator and binary translation applications. In this case, the virtualized
  624. /// environment should provide the supervisor with machine environment information like `mvendorid`,
  625. /// `marchid` and `mimpid` values. RustSBI could also be used on bare-metal RISC-V machines where such
  626. /// values would be directly accessible through CSR read operations.
  627. ///
  628. /// If we are targeting bare-metal, we can use the RustSBI library with `#[cfg(feature = "machine")]`
  629. /// enabled by changing `dependencies` section in `Cargo.toml` file (if we are using Cargo):
  630. ///
  631. /// ```toml
  632. /// [dependencies]
  633. /// rustsbi = { version = "0.4.0", features = ["machine"] }
  634. /// ```
  635. ///
  636. /// If that's not the case and we are writing a virtualization targeted application, we should add a
  637. /// `EnvInfo` implementation into the structure like `MySBI` mentioned above, with a special field
  638. /// name `info`. We can do it like:
  639. ///
  640. /// ```rust
  641. /// #[derive(RustSBI)]
  642. /// struct MySBI {
  643. /// fence: MyFence,
  644. /// timer: MyTimer,
  645. /// # #[cfg(not(feature = "machine"))]
  646. /// info: MyEnvInfo,
  647. /// }
  648. ///
  649. /// struct MyEnvInfo;
  650. ///
  651. /// impl rustsbi::EnvInfo for MyEnvInfo {
  652. /// #[inline]
  653. /// fn mvendorid(&self) -> usize { todo!("add real value here") }
  654. /// #[inline]
  655. /// fn marchid(&self) -> usize { todo!("add real value here") }
  656. /// #[inline]
  657. /// fn mimpid(&self) -> usize { todo!("add real value here") }
  658. /// }
  659. /// # use rustsbi::{RustSBI, HartMask};
  660. /// # use sbi_spec::binary::SbiRet;
  661. /// # struct MyFence;
  662. /// # impl rustsbi::Fence for MyFence {
  663. /// # fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
  664. /// # fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
  665. /// # fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
  666. /// # }
  667. /// # struct MyTimer;
  668. /// # impl rustsbi::Timer for MyTimer {
  669. /// # fn set_timer(&self, stime_value: u64) { unimplemented!() }
  670. /// # }
  671. /// ```
  672. ///
  673. /// Then, when we compile our code with `MySBI`, we'll found that the code now compiles successfully.
  674. ///
  675. /// To use the derived `RustSBI` implementation, we note out that this structure now implements the trait
  676. /// `RustSBI` with function `handle_ecall`. We can pass SBI extension, function and parameters into
  677. /// `handle_ecall`, and reads the SBI call result from its return value with the type `SbiRet`.
  678. /// To illustrate this feature, we make an SBI call to read the SBI implementation ID, like:
  679. ///
  680. /// ```rust
  681. /// # use rustsbi::RustSBI;
  682. /// #[derive(RustSBI)]
  683. /// struct MySBI {
  684. /// /* we omit the extension fields by now */
  685. /// # info: MyEnvInfo,
  686. /// }
  687. ///
  688. /// fn main() {
  689. /// // Create a MySBI instance.
  690. /// let sbi = MySBI {
  691. /// /* include initial values for fields */
  692. /// # info: MyEnvInfo
  693. /// };
  694. /// // Make the call. Read SBI implementation ID resides in extension Base (0x10),
  695. /// // with function id 1, and it doesn't have any parameters.
  696. /// let ret = sbi.handle_ecall(0x10, 0x1, [0; 6]);
  697. /// // Let's check the result...
  698. /// println!("SBI implementation ID for MySBI: {}", ret.value);
  699. /// assert_eq!(ret.value, 4);
  700. /// }
  701. /// # struct MyEnvInfo;
  702. /// # impl rustsbi::EnvInfo for MyEnvInfo {
  703. /// # fn mvendorid(&self) -> usize { unimplemented!() }
  704. /// # fn marchid(&self) -> usize { unimplemented!() }
  705. /// # fn mimpid(&self) -> usize { unimplemented!() }
  706. /// # }
  707. /// ```
  708. ///
  709. /// Run the code and we'll find following output in the console:
  710. ///
  711. /// ```text
  712. /// SBI implementation ID for MySBI: 4
  713. /// ```
  714. ///
  715. /// The SBI call returns the number 4 as SBI call result. By looking up
  716. /// [the RISC-V SBI Specification](https://github.com/riscv-non-isa/riscv-sbi-doc/blob/cf86bda6f57afb8e0e7011b61504d4e8664b9b1d/src/ext-base.adoc#sbi-implementation-ids),
  717. /// we can know that RustSBI have the implementation ID of 4. You have successfully made your first
  718. /// SBI call from a derived `RustSBI` implementation!
  719. ///
  720. /// If we learn further from the RISC-V privileged software architecture, we may know more about how
  721. /// RISC-V SBI works on an environment to support supervisor software. RISC-V SBI implementations
  722. /// accept SBI calls by supervisor-level environment call caused by `ecall` instruction under supervisor
  723. /// mode. Each `ecall` raises a RISC-V exception which the environment must process with. The SBI
  724. /// environment, either bare-metal or virtually, would save context, read extension, function and parameters
  725. /// and call the `handle_ecall` function provided by `RustSBI` trait. Then, the function returns
  726. /// with an `SbiRet`; we reads back `value` and `error` to store them into the saved context.
  727. /// Finally, when the context restores, the supervisor mode software (kernels, etc.) could get the
  728. /// SBI call result from register values.
  729. ///
  730. /// Now we have learned basical usages of the derive macro `RustSBI`. We can dive deeper and use RustSBI
  731. /// in real cases with ease. Congratulations!
  732. ///
  733. /// # Supported extensions
  734. ///
  735. /// The derive macro `RustSBI` supports all the standard RISC-V SBI extensions this library supports.
  736. /// When we add extensions into SBI structure fields, special field names are identified by RustSBI
  737. /// derive macro. Here is a list on them:
  738. ///
  739. /// | Field names | RustSBI trait | Extension |
  740. /// |:------------|:----------|:--------------|
  741. /// | `time` or `timer` | [`Timer`](trait.Timer.html) | Timer programmer extension |
  742. /// | `ipi` or `spi` | [`Ipi`](trait.Ipi.html) | S-mode Inter Processor Interrupt |
  743. /// | `fence` or `rfnc` | [`Fence`](trait.Fence.html) | Remote Fence extension |
  744. /// | `hsm` | [`Hsm`](trait.Hsm.html) | Hart State Monitor extension |
  745. /// | `reset` or `srst` | [`Reset`](trait.Reset.html) | System Reset extension |
  746. /// | `pmu` | [`Pmu`](trait.Pmu.html) | Performance Monitor Unit extension |
  747. /// | `console` or `dbcn` | [`Console`](trait.Console.html) | Debug Console extension |
  748. /// | `susp` | [`Susp`](trait.Susp.html) | System Suspend extension |
  749. /// | `cppc` | [`Cppc`](trait.Cppc.html) | SBI CPPC extension |
  750. /// | `nacl` | [`Nacl`](trait.Nacl.html) | Nested Acceleration extension |
  751. /// | `sta` | [`Sta`](trait.Sta.html) | Steal Time Accounting extension |
  752. ///
  753. /// The `EnvInfo` parameter is used by RISC-V SBI Base extension which is always supported on all
  754. /// RISC-V SBI implementations. RustSBI provides Base extension with additional `EnvInfo` by default.
  755. ///
  756. /// | Field names | RustSBI trait | Description |
  757. /// |:------------|:----------|:--------------|
  758. /// | `info` or `env_info` | [`EnvInfo`](trait.EnvInfo.html) | Machine environment information used by Base extension |
  759. ///
  760. /// Or, if `#[cfg(feature = "machine")]` is enabled, RustSBI derive macro does not require additional
  761. /// machine environment information but reads them by RISC-V CSR operation when we don't have any `EnvInfo`s
  762. /// in the structure. This feature would only work if RustSBI runs directly on machine mode hardware.
  763. /// If we are targeting other environments (virtualization etc.) we should provide `EnvInfo` instead
  764. /// of using the machine feature.
  765. ///
  766. /// # Examples
  767. ///
  768. /// This macro should be used over a struct of RISC-V SBI extension implementaions.
  769. /// For example:
  770. ///
  771. /// ```rust
  772. /// #[derive(RustSBI)]
  773. /// struct MySBI {
  774. /// fence: MyFence,
  775. /// info: MyEnvInfo,
  776. /// }
  777. ///
  778. /// // we assume that `MyFence` implements `rustsbi::Fence`
  779. /// // and `MyEnvInfo` implements `rustsbi::EnvInfo`.
  780. /// # use rustsbi::{RustSBI, HartMask};
  781. /// # use sbi_spec::binary::SbiRet;
  782. /// # struct MyFence;
  783. /// # impl rustsbi::Fence for MyFence {
  784. /// # fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
  785. /// # fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
  786. /// # fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
  787. /// # }
  788. /// # struct MyEnvInfo;
  789. /// # impl rustsbi::EnvInfo for MyEnvInfo {
  790. /// # fn mvendorid(&self) -> usize { 1 }
  791. /// # fn marchid(&self) -> usize { 2 }
  792. /// # fn mimpid(&self) -> usize { 3 }
  793. /// # }
  794. /// ```
  795. ///
  796. /// # Notes
  797. // note: following documents are inherted from `RustSBI` in the `rustsbi_macros` package.
  798. #[doc(inline)]
  799. pub use rustsbi_macros::RustSBI;
  800. pub use console::Console;
  801. pub use cppc::Cppc;
  802. pub use hart_mask::HartMask;
  803. pub use hsm::Hsm;
  804. pub use ipi::Ipi;
  805. pub use nacl::Nacl;
  806. pub use pmu::Pmu;
  807. pub use reset::Reset;
  808. pub use rfence::Rfence as Fence;
  809. pub use sta::Sta;
  810. pub use susp::Susp;
  811. pub use timer::Timer;
  812. pub use traits::{EnvInfo, RustSBI};
  813. // Macro internal functions and structures
  814. #[cfg(feature = "machine")]
  815. #[doc(hidden)]
  816. pub use traits::_rustsbi_base_bare;
  817. #[doc(hidden)]
  818. pub use traits::{
  819. _StandardExtensionProbe, _rustsbi_base_env_info, _rustsbi_console, _rustsbi_cppc,
  820. _rustsbi_fence, _rustsbi_hsm, _rustsbi_ipi, _rustsbi_nacl, _rustsbi_pmu, _rustsbi_reset,
  821. _rustsbi_sta, _rustsbi_susp, _rustsbi_timer,
  822. };