lib.rs 47 KB

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