lib.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. //! A minimal RISC-V's SBI implementation library in Rust.
  2. //!
  3. //! # What is RISC-V SBI?
  4. //!
  5. //! RISC-V SBI is short for RISC-V Supervisor Binary Interface. SBI acts as a bootloader environment to your operating system kernel.
  6. //! A SBI implementation will bootstrap your kernel, and provide an environment when your kernel is running.
  7. //!
  8. //! More generally, The SBI allows supervisor-mode (S-mode or VS-mode) software to be portable across
  9. //! all RISC-V implementations by defining an abstraction for platform (or hypervisor) specific functionality.
  10. //!
  11. //! # How to use RustSBI in your supervisor software
  12. //!
  13. //! SBI features include boot sequence and a kernel environment. To bootstrap your kernel,
  14. //! place kernel into RustSBI implementation defined address, then RustSBI will prepare an
  15. //! environment and jump to this address.
  16. //!
  17. //! ## Make SBI environment calls
  18. //!
  19. //! To use the kernel environment, you either use SBI calls or emulated instructions.
  20. //! SBI calls are similar to operating systems' `syscall`s. RISC-V SBI defined many SBI modules,
  21. //! and in each module there are different functions, you should pick a function before calling.
  22. //! Then, you should prepare some parameters, whose definition are not the same among functions.
  23. //!
  24. //! Now you have a module number, a function number, and a few SBI call parameters.
  25. //! You invoke a special `ecall` instruction on supervisor level, and it will trap into machine level
  26. //! SBI implementation. It will handle your `ecall`, similar to your kernel handling system calls
  27. //! from user level.
  28. //!
  29. //! SBI functions return two values other than one. First value will be an error number,
  30. //! it will tell if SBI call have succeeded, or which error have occurred.
  31. //! Second value is the real return value, its meaning is different according to which function you calls.
  32. //!
  33. //! ## Call SBI in different programming languages
  34. //!
  35. //! Making SBI calls are similar to making system calls.
  36. //!
  37. //! Module number is required to put on register `a7`, function number on `a6`.
  38. //! Parameters should be placed from `a0` to `a5`, first into `a0`, second into `a1`, etc.
  39. //! Unused parameters can be set to any value or leave untouched.
  40. //!
  41. //! After registers are ready, invoke an instruction called `ecall`.
  42. //! Then, the return value is placed into `a0` and `a1` registers.
  43. //! The error value could be read from `a0`, and return value is placed into `a1`.
  44. //!
  45. //! In Rust, here is an example to call SBI functions using inline assembly:
  46. //!
  47. //! ```no_run
  48. //! # #[repr(C)] struct SbiRet { error: usize, value: usize }
  49. //! # const EXTENSION_BASE: usize = 0x10;
  50. //! # const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0;
  51. //! #[inline(always)]
  52. //! fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize) -> SbiRet {
  53. //! let (error, value);
  54. //! match () {
  55. //! #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
  56. //! () => unsafe { asm!(
  57. //! "ecall",
  58. //! in("a0") arg0, in("a1") arg1,
  59. //! in("a6") function, in("a7") extension,
  60. //! lateout("a0") error, lateout("a1") value,
  61. //! ) },
  62. //! #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
  63. //! () => {
  64. //! drop((extension, function, arg0, arg1));
  65. //! unimplemented!("not RISC-V instruction set architecture")
  66. //! }
  67. //! };
  68. //! SbiRet { error, value }
  69. //! }
  70. //!
  71. //! #[inline]
  72. //! pub fn get_spec_version() -> SbiRet {
  73. //! sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0)
  74. //! }
  75. //! ```
  76. //!
  77. //! Complex SBI functions may fail. In this example we only take the value, but in complete designs
  78. //! we should handle the `error` value returned from SbiRet.
  79. //!
  80. //! You may use other languages to call SBI environment. In C programming language, we can call like this:
  81. //!
  82. //! ```text
  83. //! #define SBI_CALL(module, funct, arg0, arg1, arg2, arg3) ({ \
  84. //! register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \
  85. //! register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \
  86. //! register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \
  87. //! register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); \
  88. //! register uintptr_t a7 asm ("a6") = (uintptr_t)(funct); \
  89. //! register uintptr_t a7 asm ("a7") = (uintptr_t)(module); \
  90. //! asm volatile ("ecall" \
  91. //! : "+r" (a0), "+r" (a1) \
  92. //! : "r" (a1), "r" (a2), "r" (a3), "r" (a6), "r" (a7) \
  93. //! : "memory") \
  94. //! {a0, a1}; \
  95. //! })
  96. //!
  97. //! #define SBI_CALL_0(module, funct) SBI_CALL(module, funct, 0, 0, 0, 0)
  98. //!
  99. //! static inline sbiret get_spec_version() {
  100. //! SBI_CALL_0(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION)
  101. //! }
  102. //! ```
  103. //!
  104. //! # Non-features
  105. //!
  106. //! RustSBI is designed to strictly adapt to the RISC-V Supervisor Binary Interface specification.
  107. //! Other features useful in developing kernels and hypervisors maybe included in other Rust
  108. //! ecosystem crates other than this package.
  109. //!
  110. //! ## Hardware discovery and feature detection
  111. //!
  112. //! According to the RISC-V SBI specification, SBI does not specify any method for hardware discovery.
  113. //! The supervisor software must rely on the other industry standard hardware
  114. //! discovery methods (i.e. Device Tree or ACPI) for that.
  115. //!
  116. //! To detect any feature under bare metal or under supervisor level, developers may depend on
  117. //! any hardware discovery methods, or use try-execute-trap method to detect any instructions or
  118. //! CSRs. If SBI is implemented in user level emulators, it may requires to depend on operating
  119. //! system calls or use the signal trap method to detect any RISC-V core features.
  120. //!
  121. //! ## Where can I get RustSBI binary file for XX platform?
  122. //!
  123. //! RustSBI is designed to be a library instead of providing any binary files to specific platforms.
  124. //! Chip or board manufacturers should provide their own SBI implementation project using RustSBI as a dependency.
  125. //!
  126. //! The RustSBI team provides reference implementation for several platforms, but they are for evaluation
  127. //! only and should not be used in production.
  128. //! RustSBI itself cannot decide for all arbitrary users, so developers are encouraged to use RustSBI
  129. //! as a Rust crate dependency to support their own SBI implementation,
  130. //! other than use reference implementation directly when in production.
  131. //! SBI feature demands are different among users, one feature would be useful for this user,
  132. //! but it will be considered not useful and takes up lots of flash room for other users.
  133. //!
  134. //! RustSBI is not designed to include all platforms available in official repository.
  135. //! For an actual platform users may consult board or SoC manufacturer other than RustSBI repository itself.
  136. //!
  137. //! The reason to that is that if some repository includes all platforms it support,
  138. //! there could be lots of non technical reasons that will bar one or a certain group of developers
  139. //! from merging their code into main or upstream branches.
  140. //! For example, if a draft version of actual platform is produced, it will mean to write for one draft version as well
  141. //! as long as this software is under maintenance. As software developer may not want to write for it,
  142. //! it's better to include minimal feature in core repository, and leave other features for downstream developers.
  143. //!
  144. //! # Notes for RustSBI developers
  145. //!
  146. //! Following useful hints are for firmware and kernel developers when working with SBI and RustSBI.
  147. //!
  148. //! ## RustSBI is a library for interfaces
  149. //!
  150. //! This library adapts to individual Rust traits to provide basic SBI features.
  151. //! When building for own platform, implement traits in this library and pass them to the functions
  152. //! begin with `init`. After that, you may call `rustsbi::ecall` in your own exception handler
  153. //! which would dispatch parameters from supervisor to the traits to execute SBI functions.
  154. //!
  155. //! The library also implements useful functions which may help with platform specific binaries.
  156. //! The `enter_privileged` maybe used to enter the operating system after the initialization
  157. //! process is finished. The `LOGO` should be printed if necessary when the binary is initializing.
  158. //!
  159. //! Note that this crate is a library which contains common building blocks in SBI implementation.
  160. //! It is not intended to be used directly; users should build own platforms with this library.
  161. //! RustSBI provides implementations on common platforms in separate platform crates.
  162. //!
  163. //! ## Legacy SBI extension
  164. //!
  165. //! Note: RustSBI legacy support is only designed for backward compability. It's disabled by default and it's not
  166. //! suggested to include legacy functions in newer firmware designs. Modules other than legacy console is replaced by
  167. //! individual modules in SBI. Legacy console is not suggested to use in kernels.
  168. //! If you are a kernel developer, newer designs should consider relying on each SBI module other than
  169. //! legacy functions.
  170. //!
  171. //! The SBI includes legacy extension which dated back to SBI 0.1 specification. Most of its features
  172. //! are replaced by individual SBI modules, thus the entire legacy extension is deprecated by
  173. //! SBI version 0.2. However, some users may find out SBI 0.1 legacy console useful in some situations
  174. //! even if it's deprecated.
  175. //!
  176. //! RustSBI keeps SBI 0.1 legacy support under feature gate `legacy`. To use RustSBI with legacy feature,
  177. //! you may change dependency code to:
  178. //!
  179. //! ```toml
  180. //! [dependencies]
  181. //! rustsbi = { version = "0.3.0", features = ["legacy"] }
  182. //! ```
  183. #![no_std]
  184. #![cfg_attr(feature = "singleton", feature(ptr_metadata))]
  185. #[cfg(feature = "legacy")]
  186. #[doc(hidden)]
  187. #[macro_use]
  188. pub mod legacy_stdio;
  189. mod base;
  190. #[cfg(feature = "singleton")]
  191. mod ecall;
  192. mod hart_mask;
  193. mod hsm;
  194. #[cfg(not(feature = "legacy"))]
  195. mod instance;
  196. mod ipi;
  197. mod pmu;
  198. mod reset;
  199. mod rfence;
  200. mod timer;
  201. #[cfg(feature = "singleton")]
  202. mod util;
  203. /// The const rustsbi logo with blank line at the beginning.
  204. const LOGO: &str = r"
  205. .______ __ __ _______.___________. _______..______ __
  206. | _ \ | | | | / | | / || _ \ | |
  207. | |_) | | | | | | (----`---| |----`| (----`| |_) || |
  208. | / | | | | \ \ | | \ \ | _ < | |
  209. | |\ \----.| `--' |.----) | | | .----) | | |_) || |
  210. | _| `._____| \______/ |_______/ |__| |_______/ |______/ |__|";
  211. /// The RustSBI logo without blank lines
  212. pub fn logo() -> &'static str {
  213. // rust raw text 无法在保持格式的情况下去除头上的换行
  214. // include_str("logo.txt") 会由于 vscode 的自动格式化在末尾多一个换行
  215. LOGO.trim_start()
  216. }
  217. const SBI_SPEC_MAJOR: usize = 1;
  218. const SBI_SPEC_MINOR: usize = 0;
  219. /// RustSBI implementation ID: 4
  220. ///
  221. /// Ref: https://github.com/riscv-non-isa/riscv-sbi-doc/pull/61
  222. const IMPL_ID_RUSTSBI: usize = 4;
  223. const RUSTSBI_VERSION_MAJOR: usize = (env!("CARGO_PKG_VERSION_MAJOR").as_bytes()[0] - b'0') as _;
  224. const RUSTSBI_VERSION_MINOR: usize = (env!("CARGO_PKG_VERSION_MINOR").as_bytes()[0] - b'0') as _;
  225. const RUSTSBI_VERSION_PATCH: usize = (env!("CARGO_PKG_VERSION_PATCH").as_bytes()[0] - b'0') as _;
  226. const RUSTSBI_VERSION: usize =
  227. (RUSTSBI_VERSION_MAJOR << 16) + (RUSTSBI_VERSION_MINOR << 8) + RUSTSBI_VERSION_PATCH;
  228. /// RustSBI version as a string.
  229. pub const VERSION: &str = env!("CARGO_PKG_VERSION");
  230. pub extern crate sbi_spec as spec;
  231. #[cfg(feature = "singleton")]
  232. pub use ecall::handle_ecall as ecall;
  233. pub use hart_mask::HartMask;
  234. pub use hsm::Hsm;
  235. #[cfg(not(feature = "legacy"))]
  236. pub use instance::{Builder, RustSBI};
  237. pub use ipi::Ipi;
  238. #[cfg(feature = "legacy")]
  239. #[doc(hidden)]
  240. pub use legacy_stdio::{legacy_stdio_getchar, legacy_stdio_putchar};
  241. pub use pmu::Pmu;
  242. pub use reset::Reset;
  243. pub use rfence::Rfence as Fence;
  244. pub use timer::Timer;
  245. #[cfg(not(feature = "machine"))]
  246. pub use instance::MachineInfo;
  247. #[cfg(feature = "singleton")]
  248. pub use {
  249. hsm::init_hsm, ipi::init_ipi, pmu::init_pmu, reset::init_reset,
  250. rfence::init_rfence as init_remote_fence, timer::init_timer,
  251. };