lib.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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. //! to use the RustSBI Prototyping System which will provide binaries for each platforms.
  5. //! If you are a vendor or contributor who wants to adapt RustSBI to your new product or board,
  6. //! you may consider adapting the Prototyping System first to get your board adapted in an afternoon;
  7. //! you are only advised to build a discrete crate if your team have a lot of time working on this board.*
  8. //!
  9. //! *For more details on binary downloads the the RustSBI Prototyping System,
  10. //! see section [Prototyping System vs discrete packages](#download-binary-file-the-prototyping-system-vs-discrete-packages).*
  11. //!
  12. //! The crate `rustsbi` acts as core trait and instance abstraction of RustSBI ecosystem.
  13. //!
  14. //! # What is RISC-V SBI?
  15. //!
  16. //! RISC-V SBI is short for RISC-V Supervisor Binary Interface. SBI acts as a bootloader environment to your operating system kernel.
  17. //! A SBI implementation will bootstrap your kernel, and provide an environment when your kernel is running.
  18. //!
  19. //! More generally, The SBI allows supervisor-mode (S-mode or VS-mode) software to be portable across
  20. //! all RISC-V implementations by defining an abstraction for platform (or hypervisor) specific functionality.
  21. //!
  22. //! # How to use RustSBI in your supervisor software
  23. //!
  24. //! SBI features include boot sequence and a kernel environment. To bootstrap your kernel,
  25. //! place kernel into RustSBI implementation defined address, then RustSBI will prepare an
  26. //! environment and jump to this address.
  27. //!
  28. //! ## Make SBI environment calls
  29. //!
  30. //! To use the kernel environment, you either use SBI calls or emulated instructions.
  31. //! SBI calls are similar to operating systems' `syscall`s. RISC-V SBI defined many SBI modules,
  32. //! and in each module there are different functions, you should pick a function before calling.
  33. //! Then, you should prepare some parameters, whose definition are not the same among functions.
  34. //!
  35. //! Now you have a module number, a function number, and a few SBI call parameters.
  36. //! You invoke a special `ecall` instruction on supervisor level, and it will trap into machine level
  37. //! SBI implementation. It will handle your `ecall`, similar to your kernel handling system calls
  38. //! from user level.
  39. //!
  40. //! SBI functions return two values other than one. First value will be an error number,
  41. //! it will tell if SBI call have succeeded, or which error have occurred.
  42. //! Second value is the real return value, its meaning is different according to which function you calls.
  43. //!
  44. //! ## Call SBI in different programming languages
  45. //!
  46. //! Making SBI calls are similar to making system calls.
  47. //!
  48. //! Module number is required to put on register `a7`, function number on `a6`.
  49. //! Parameters should be placed from `a0` to `a5`, first into `a0`, second into `a1`, etc.
  50. //! Unused parameters can be set to any value or leave untouched.
  51. //!
  52. //! After registers are ready, invoke an instruction called `ecall`.
  53. //! Then, the return value is placed into `a0` and `a1` registers.
  54. //! The error value could be read from `a0`, and return value is placed into `a1`.
  55. //!
  56. //! In Rust, here is an example to call SBI functions using inline assembly:
  57. //!
  58. //! ```no_run
  59. //! # #[repr(C)] struct SbiRet { error: usize, value: usize }
  60. //! # const EXTENSION_BASE: usize = 0x10;
  61. //! # const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0;
  62. //! #[inline(always)]
  63. //! fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize) -> SbiRet {
  64. //! let (error, value);
  65. //! match () {
  66. //! #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
  67. //! () => unsafe { asm!(
  68. //! "ecall",
  69. //! in("a0") arg0, in("a1") arg1,
  70. //! in("a6") function, in("a7") extension,
  71. //! lateout("a0") error, lateout("a1") value,
  72. //! ) },
  73. //! #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
  74. //! () => {
  75. //! drop((extension, function, arg0, arg1));
  76. //! unimplemented!("not RISC-V instruction set architecture")
  77. //! }
  78. //! };
  79. //! SbiRet { error, value }
  80. //! }
  81. //!
  82. //! #[inline]
  83. //! pub fn get_spec_version() -> SbiRet {
  84. //! sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0)
  85. //! }
  86. //! ```
  87. //!
  88. //! Complex SBI functions may fail. In this example we only take the value, but in complete designs
  89. //! we should handle the `error` value returned from SbiRet.
  90. //!
  91. //! You may use other languages to call SBI environment. In C programming language, we can call like this:
  92. //!
  93. //! ```text
  94. //! #define SBI_CALL(module, funct, arg0, arg1, arg2, arg3) ({ \
  95. //! register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \
  96. //! register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \
  97. //! register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \
  98. //! register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); \
  99. //! register uintptr_t a7 asm ("a6") = (uintptr_t)(funct); \
  100. //! register uintptr_t a7 asm ("a7") = (uintptr_t)(module); \
  101. //! asm volatile ("ecall" \
  102. //! : "+r" (a0), "+r" (a1) \
  103. //! : "r" (a1), "r" (a2), "r" (a3), "r" (a6), "r" (a7) \
  104. //! : "memory") \
  105. //! {a0, a1}; \
  106. //! })
  107. //!
  108. //! #define SBI_CALL_0(module, funct) SBI_CALL(module, funct, 0, 0, 0, 0)
  109. //!
  110. //! static inline sbiret get_spec_version() {
  111. //! SBI_CALL_0(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION)
  112. //! }
  113. //! ```
  114. //!
  115. //! # Hypervisor and emulator development with RustSBI
  116. //!
  117. //! RustSBI crate supports to develop RISC-V emulators, and both Type-1 and Type-2 hypervisors.
  118. //! Hypervisor developers may find it easy to handle standard SBI functions with an instance
  119. //! based RustSBI interface.
  120. //!
  121. //! ## Hypervisors using RustSBI
  122. //!
  123. //! Both Type-1 and Type-2 hypervisors on RISC-V runs on HS-mode hardware. Depending on demands
  124. //! of virtualized systems, hypervisors may either provide transparent information from host machine
  125. //! or provide another set of information to override the current environment. RISC-V hypervisors
  126. //! does not have direct access to machine mode (M-mode) registers.
  127. //!
  128. //! RustSBI supports both by instance based providing a `MachineInfo` structure. If RISC-V
  129. //! hypervisors choose to use existing information on current machine, it may require to call
  130. //! underlying machine environment using SBI calls and fill in information into `MachineInfo`.
  131. //! If hypervisors want to override hardware information, they may fill in custom ones into
  132. //! `MachineInfo` structures. When creating RustSBI instance, `MachineInfo` structure is
  133. //! required as an input of constructor.
  134. //!
  135. //! To begin with, disable default features in file `Cargo.toml`:
  136. //!
  137. //! ```toml
  138. //! [dependencies]
  139. //! rustsbi = { version = "0.3.0", default-features = false }
  140. //! ```
  141. //!
  142. //! This will disable default feature `machine` which will assume that RustSBI runs on M-mode directly,
  143. //! which is not appropriate in our purpose. After that, a `RustSBI` instance may be placed
  144. //! in the virtual machine structure to prepare for SBI environment:
  145. //!
  146. //! ```rust
  147. //! # struct RustSBI<>();
  148. //! struct VmHart {
  149. //! // other fields ...
  150. //! env: RustSBI</* Types, .. */>,
  151. //! }
  152. //! ```
  153. //!
  154. //! When the virtual machine hart trapped into hypervisor, its code should decide whether
  155. //! this trap is an SBI environment call. If that is true, pass in parameters by `env.handle_ecall`
  156. //! function. RustSBI will handle with SBI standard constants, call corresponding module and provide
  157. //! parameters according to the extension and function IDs.
  158. //!
  159. //! Crate `rustsbi` adapts to standard RISC-V SBI calls.
  160. //! If the hypervisor have custom SBI extensions that RustSBI does not recognize, those extension
  161. //! and function IDs can be checked before calling RustSBI `env.handle_ecall`.
  162. //!
  163. //! ```no_run
  164. //! # use sbi_spec::binary::SbiRet;
  165. //! # struct MyExtensionEnv {}
  166. //! # impl MyExtensionEnv { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
  167. //! # struct RustSBI {} // Mock, prevent doc test error when feature singleton is enabled
  168. //! # impl RustSBI { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
  169. //! # struct VmHart { my_extension_env: MyExtensionEnv, env: RustSBI }
  170. //! # #[derive(Copy, Clone)] enum Trap { Exception(Exception) }
  171. //! # impl Trap { fn cause(&self) -> Self { *self } }
  172. //! # #[derive(Copy, Clone)] enum Exception { SupervisorEcall }
  173. //! # impl VmHart {
  174. //! # fn new() -> VmHart { VmHart { my_extension_env: MyExtensionEnv {}, env: RustSBI {} } }
  175. //! # fn run(&mut self) -> Trap { Trap::Exception(Exception::SupervisorEcall) }
  176. //! # fn trap_params(&self) -> () { }
  177. //! # fn fill_in(&mut self, ans: SbiRet) { let _ = ans; }
  178. //! # }
  179. //! let mut hart = VmHart::new();
  180. //! loop {
  181. //! let trap = hart.run();
  182. //! if let Trap::Exception(Exception::SupervisorEcall) = trap.cause() {
  183. //! // Firstly, handle custom extensions
  184. //! let my_extension_sbiret = hart.my_extension_env.handle_ecall(hart.trap_params());
  185. //! // If custom extension handles correctly, fill in its result and continue to hart.
  186. //! // The custom handler may handle `probe_extension` in `base` extension as well
  187. //! // to allow detections to whether custom extension exists.
  188. //! if my_extension_sbiret != SbiRet::not_supported() {
  189. //! hart.fill_in(my_extension_sbiret);
  190. //! continue;
  191. //! }
  192. //! // Then, if it's not a custom extension, handle it using standard SBI handler.
  193. //! let standard_sbiret = hart.env.handle_ecall(hart.trap_params());
  194. //! hart.fill_in(standard_sbiret);
  195. //! }
  196. //! }
  197. //! ```
  198. //!
  199. //! RustSBI would interact well with custom extension environments in this way.
  200. //!
  201. //! ## Emulators using RustSBI
  202. //!
  203. //! RustSBI library may be used to write RISC-V emulators. Emulators do not use host hardware
  204. //! features and thus may build and run on any architecture. Like hardware RISC-V implementations,
  205. //! software emulated RISC-V environment would still need SBI implementation to support supervisor
  206. //! environment.
  207. //!
  208. //! Writing emulators would follow the similiar process with writing hypervisors, see
  209. //! [Hypervisors using RustSBI](#hypervisors-using-rustsbi) for details.
  210. //!
  211. //! # Download binary file: the Prototyping System vs discrete packages
  212. //!
  213. //! RustSBI ecosystem would typically provide support for most platforms. Those support packages
  214. //! would be provided either from the RustSBI Prototyping System or vendor provided discrete SBI
  215. //! implementation packages.
  216. //!
  217. //! The RustSBI Prototyping System is a universal support package provided by RustSBI ecosystem.
  218. //! It is designed to save development time while providing most SBI feature possible.
  219. //! Users may choose to download from Prototyping System repository to get various types of RustSBI
  220. //! packages for their boards. Vendors and contributors may find it easy to adapt new SoCs and
  221. //! boards into Prototyping System.
  222. //!
  223. //! Discrete SBI packages are SBI environment support packages specially designed for one board
  224. //! or SoC, it will be provided by board vendor or RustSBI ecosystem.
  225. //! Vendors may find it easy to include fine grained features in each support package, but the
  226. //! maintainence situation would vary between vendors and it would likely to cost a lot of time
  227. //! to develop from a bare-metal executable. Users may find a boost in performance, energy saving
  228. //! indexes and feature granularity in discrete packages, but it would depends on whether the
  229. //! vendor provide it.
  230. //!
  231. //! To download binary package for the Prototyping System, visit its project website for a download link.
  232. //! To download them for discrete packages, RustSBI users may visit distribution source of SoC or board
  233. //! manufacturers.
  234. //!
  235. //! # Non-features
  236. //!
  237. //! RustSBI is designed to strictly adapt to the RISC-V Supervisor Binary Interface specification.
  238. //! Other features useful in developing kernels and hypervisors maybe included in other Rust
  239. //! ecosystem crates other than this package.
  240. //!
  241. //! ## Hardware discovery and feature detection
  242. //!
  243. //! According to the RISC-V SBI specification, SBI does not specify any method for hardware discovery.
  244. //! The supervisor software must rely on the other industry standard hardware
  245. //! discovery methods (i.e. Device Tree or ACPI) for that purpose.
  246. //!
  247. //! To detect any feature under bare metal or under supervisor level, developers may depend on
  248. //! any hardware discovery methods, or use try-execute-trap method to detect any instructions or
  249. //! CSRs. If SBI is implemented in user level emulators, it may requires to depend on operating
  250. //! system calls or use the signal trap method to detect any RISC-V core features.
  251. //!
  252. //! # Notes for RustSBI developers
  253. //!
  254. //! Following useful hints are for firmware and kernel developers when working with SBI and RustSBI.
  255. //!
  256. //! ## RustSBI is a library for interfaces
  257. //!
  258. //! This library adapts to individual Rust traits to provide basic SBI features.
  259. //! When building for own platform, implement traits in this library and pass them to the functions
  260. //! begin with `init`. After that, you may call `rustsbi::ecall`, `RustSBI::handle_ecall` or
  261. //! similiar functions in your own exception handler.
  262. //! It would dispatch parameters from supervisor to the traits to execute SBI functions.
  263. //!
  264. //! The library also implements useful functions which may help with platform specific binaries.
  265. //! The `LOGO` can be printed if necessary when the binary is initializing.
  266. //!
  267. //! Note that this crate is a library which contains common building blocks in SBI implementation.
  268. //! The RustSBI ecosystem would provide different level of support for each board, those support
  269. //! packages would use `rustsbi` crate as library to provide different type of SBI binary releases.
  270. //!
  271. //! ## Legacy SBI extension
  272. //!
  273. //! *Note: RustSBI legacy support is only designed for backward compability of RISC-V SBI standard.
  274. //! It's disabled by default and it's not suggested to include legacy functions in newer firmware designs.
  275. //! Modules other than legacy console is replaced by individual modules in SBI.
  276. //! Kernels are not suggested to use legacy functions in practice.
  277. //! If you are a kernel developer, newer designs should consider relying on each SBI module other than
  278. //! legacy functions.*
  279. //!
  280. //! The SBI includes legacy extension which dated back to SBI 0.1 specification. Most of its features
  281. //! are replaced by individual SBI modules, thus the entire legacy extension is deprecated by
  282. //! SBI version 0.2. However, some users may find out SBI 0.1 legacy console useful in some situations
  283. //! even if it's deprecated.
  284. //!
  285. //! RustSBI keeps SBI 0.1 legacy support under feature gate `legacy`. To use RustSBI with legacy feature,
  286. //! you may change dependency code to:
  287. //!
  288. //! ```toml
  289. //! [dependencies]
  290. //! rustsbi = { version = "0.3.0", features = ["legacy"] }
  291. //! ```
  292. #![no_std]
  293. #![cfg_attr(feature = "singleton", feature(ptr_metadata))]
  294. #[cfg(feature = "legacy")]
  295. #[doc(hidden)]
  296. #[macro_use]
  297. pub mod legacy_stdio;
  298. mod base;
  299. #[cfg(feature = "singleton")]
  300. mod ecall;
  301. mod hart_mask;
  302. mod hsm;
  303. #[cfg(not(feature = "legacy"))]
  304. mod instance;
  305. mod ipi;
  306. mod pmu;
  307. mod reset;
  308. mod rfence;
  309. mod timer;
  310. #[cfg(feature = "singleton")]
  311. mod util;
  312. /// The RustSBI logo without blank lines on the beginning
  313. pub const LOGO: &str = r".______ __ __ _______.___________. _______..______ __
  314. | _ \ | | | | / | | / || _ \ | |
  315. | |_) | | | | | | (----`---| |----`| (----`| |_) || |
  316. | / | | | | \ \ | | \ \ | _ < | |
  317. | |\ \----.| `--' |.----) | | | .----) | | |_) || |
  318. | _| `._____| \______/ |_______/ |__| |_______/ |______/ |__|";
  319. const SBI_SPEC_MAJOR: usize = 1;
  320. const SBI_SPEC_MINOR: usize = 0;
  321. /// RustSBI implementation ID: 4
  322. ///
  323. /// Ref: https://github.com/riscv-non-isa/riscv-sbi-doc/pull/61
  324. const IMPL_ID_RUSTSBI: usize = 4;
  325. const RUSTSBI_VERSION_MAJOR: usize = (env!("CARGO_PKG_VERSION_MAJOR").as_bytes()[0] - b'0') as _;
  326. const RUSTSBI_VERSION_MINOR: usize = (env!("CARGO_PKG_VERSION_MINOR").as_bytes()[0] - b'0') as _;
  327. const RUSTSBI_VERSION_PATCH: usize = (env!("CARGO_PKG_VERSION_PATCH").as_bytes()[0] - b'0') as _;
  328. const RUSTSBI_VERSION: usize =
  329. (RUSTSBI_VERSION_MAJOR << 16) + (RUSTSBI_VERSION_MINOR << 8) + RUSTSBI_VERSION_PATCH;
  330. /// RustSBI version as a string
  331. pub const VERSION: &str = env!("CARGO_PKG_VERSION");
  332. pub extern crate sbi_spec as spec;
  333. #[cfg(feature = "singleton")]
  334. pub use ecall::handle_ecall as ecall;
  335. pub use hart_mask::HartMask;
  336. pub use hsm::Hsm;
  337. #[cfg(not(feature = "legacy"))]
  338. pub use instance::{Builder, RustSBI};
  339. pub use ipi::Ipi;
  340. #[cfg(feature = "legacy")]
  341. #[doc(hidden)]
  342. pub use legacy_stdio::{legacy_stdio_getchar, legacy_stdio_putchar};
  343. pub use pmu::Pmu;
  344. pub use reset::Reset;
  345. pub use rfence::Rfence as Fence;
  346. pub use timer::Timer;
  347. #[cfg(not(feature = "machine"))]
  348. pub use instance::MachineInfo;
  349. #[cfg(feature = "singleton")]
  350. pub use {
  351. hsm::init_hsm, ipi::init_ipi, pmu::init_pmu, reset::init_reset,
  352. rfence::init_rfence as init_remote_fence, timer::init_timer,
  353. };