Browse Source

lib: add documents on implementing RustSBI

luojia65 2 years ago
parent
commit
de8dbaa557
1 changed files with 265 additions and 28 deletions
  1. 265 28
      src/lib.rs

+ 265 - 28
src/lib.rs

@@ -1,7 +1,8 @@
 //! A minimal RISC-V's SBI implementation library in Rust.
 //!
 //! *Note: If you are a user looking for binary distribution download for RustSBI, you may consider
-//! to use the RustSBI Prototyping System which will provide binaries for each platforms.
+//! to use the [RustSBI Prototyping System](https://github.com/rustsbi/standalone)
+//! which will provide binaries for each platforms.
 //! If you are a vendor or contributor who wants to adapt RustSBI to your new product or board,
 //! you may consider adapting the Prototyping System first to get your board adapted in an afternoon;
 //! you are only advised to build a discrete crate if your team have a lot of time working on this board.*
@@ -19,7 +20,7 @@
 //! More generally, The SBI allows supervisor-mode (S-mode or VS-mode) software to be portable across
 //! all RISC-V implementations by defining an abstraction for platform (or hypervisor) specific functionality.
 //!
-//! # How to use RustSBI in your supervisor software
+//! # Use RustSBI services in your supervisor software
 //!
 //! SBI features include boot sequence and a kernel environment. To bootstrap your kernel,
 //! place kernel into RustSBI implementation defined address, then RustSBI will prepare an
@@ -45,7 +46,7 @@
 //!
 //! Making SBI calls are similar to making system calls.
 //!
-//! Module number is required to put on register `a7`, function number on `a6`.
+//! Module number is required to put on register `a7`, function number on `a6` if applicable.
 //! Parameters should be placed from `a0` to `a5`, first into `a0`, second into `a1`, etc.
 //! Unused parameters can be set to any value or leave untouched.
 //!
@@ -85,8 +86,9 @@
 //! }
 //! ```
 //!
-//! Complex SBI functions may fail. In this example we only take the value, but in complete designs
-//! we should handle the `error` value returned from SbiRet.
+//! SBI functions would return a result thus some of these may fail.
+//! In this example we only take the value, but in complete designs we should handle the `error`
+//! returned by SbiRet.
 //!
 //! You may use other languages to call SBI environment. In C programming language, we can call like this:
 //!
@@ -96,7 +98,7 @@
 //!     register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \
 //!     register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \
 //!     register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); \
-//!     register uintptr_t a7 asm ("a6") = (uintptr_t)(funct); \
+//!     register uintptr_t a6 asm ("a6") = (uintptr_t)(funct); \
 //!     register uintptr_t a7 asm ("a7") = (uintptr_t)(module); \
 //!     asm volatile ("ecall" \
 //!         : "+r" (a0), "+r" (a1) \
@@ -112,6 +114,245 @@
 //! }
 //! ```
 //!
+//! # Implement RustSBI on machine environment
+//!
+//! Board, SoC vendors, machine environment emulators and research projects may need RustSBI
+//! adapted to specific environments.
+//! RustSBI project supports these demands either by discrete package or the Prototyping System.
+//! Developers may choose the Prototyping System for fast developing time,
+//! or discrete packages for fine-grained features.
+//!
+//! Hypervisor and supervisor environment emulator developers may refer to
+//! [Hypervisor and emulator development with RustSBI](#hypervisor-and-emulator-development-with-rustsbi)
+//! for such purposes as RustSBI provide different set of features dedicated for emulated or virtual
+//! environments.
+//!
+//! ## Use the Prototyping System
+//!
+//! The RustSBI Prototyping System aims to get your platform working with SBI in an afternoon.
+//! It supports most RISC-V platforms available by providing scalable set of drivers and features.
+//! It provides custom features such as Penglai TEE, DramForever's emulated hypervisor extension, and Raven
+//! the firmware debugger framework.
+//!
+//! You may find further documents on [RustSBI Prototyping System repository](https://github.com/rustsbi/standalone).
+//!
+//! ## Discrete RustSBI package on bare metal RISC-V hardware
+//!
+//! Discrete packages provide developers with most scalability and complete control of underlying
+//! hardware. It is ideal if advanced low power features, management cores and other features should
+//! be used in this implementation.
+//!
+//! RustSBI supports discrete package by default. Create a new `#![no_std]` bare-metal package
+//! to get started. Add following lines to `Cargo.toml`:
+//!
+//! ```toml
+//! [dependencies]
+//! rustsbi = "0.3.0"
+//! ```
+//!
+//! After hardware initialization process, the part of firmware with RustSBI linked should run on memory
+//! blocks with fast accesses, as it would be called frequently by operating system.
+//! If the supervisor is called by trap generator semantics, insert `rustsbi::RustSBI` structure
+//! in your hart executor structure.
+//!
+//! ```rust
+//! # struct Clint;
+//! # struct MyPlatRfnc;
+//! # struct MyPlatHsm;
+//! # struct MyBoardPower;
+//! # struct MyPlatPmu;
+//! # #[cfg(not(feature = "legacy"))]
+//! use rustsbi::RustSBI;
+//!
+//! # struct SupervisorContext;
+//! /// Executes the supervisor within.
+//! struct Executor {
+//!     ctx: SupervisorContext,
+//!     /* other environment variables ... */
+//! # #[cfg(not(feature = "legacy"))]
+//!     sbi: RustSBI<Clint, Clint, MyPlatRfnc, MyPlatHsm, MyBoardPower, MyPlatPmu>,
+//!     /* custom_1: CustomSBI<...> */
+//! }
+//!
+//! # struct Trap;
+//! impl Executor {
+//!     /// A function that runs the provided supervisor, uses `&mut self` for it
+//!     /// modifies `SupervisorContext`.
+//!     ///
+//!     /// It returns for every Trap the supervisor produces. Its handler should read
+//!     /// and modify `self.ctx` if necessary. After handled, `run()` this structure
+//!     /// again or exit execution process.
+//!     pub fn run(&mut self) -> Trap {
+//!         todo!("fill in generic or platform specific trampoline procedure")
+//!     }
+//! }
+//! ```
+//!
+//! After each `run()`, process the trap returned with the RustSBI instance in executor.
+//! Call `RustSBI::handle_ecall` and fill in developer provided `SupervisorContext` if necessary.
+//!
+//! ```no_run
+//! # use sbi_spec::binary::SbiRet;
+//! # struct RustSBI {} // Mock, prevent doc test error when feature singleton is enabled
+//! # impl RustSBI { fn handle_ecall(&self, e: (), f: (), p: ()) -> SbiRet { SbiRet::success(0) } }
+//! # struct Executor { sbi: RustSBI }
+//! # #[derive(Copy, Clone)] enum Trap { Exception(Exception) }
+//! # impl Trap { fn cause(&self) -> Self { *self } }
+//! # #[derive(Copy, Clone)] enum Exception { SupervisorEcall }
+//! # impl Executor {
+//! #     fn new(board_params: BoardParams) -> Executor { let _ = board_params; Executor { sbi: RustSBI {} } }
+//! #     fn run(&mut self) -> Trap { Trap::Exception(Exception::SupervisorEcall) }
+//! #     fn sbi_extension(&self) -> () { }
+//! #     fn sbi_function(&self) -> () { }
+//! #     fn sbi_params(&self) -> () { }
+//! #     fn fill_sbi_return(&mut self, ans: SbiRet) { let _ = ans; }
+//! # }
+//! # struct BoardParams;
+//! # const MY_SPECIAL_EXIT: usize = 0x233;
+//! /// Board specific power operations.
+//! enum Operation {
+//!     Reboot,
+//!     Shutdown,
+//! }
+//!
+//! # impl From<SbiRet> for Operation { fn from(_: SbiRet) -> Self { todo!() } }
+//! /// Execute supervisor in given board parameters.
+//! pub fn execute_supervisor(board_params: BoardParams) -> Operation {
+//!     let mut exec = Executor::new(board_params);
+//!     loop {
+//!         let trap = exec.run();
+//!         if let Trap::Exception(Exception::SupervisorEcall) = trap.cause() {
+//!             let ans = exec.sbi.handle_ecall(
+//!                 exec.sbi_extension(),
+//!                 exec.sbi_function(),
+//!                 exec.sbi_params(),
+//!             );
+//!             if ans.error == MY_SPECIAL_EXIT {
+//!                 break Operation::from(ans)
+//!             }
+//!             // This line would also advance `spec` with `4` to indicate the `ecall` is handled.
+//!             exec.fill_sbi_return(ans);
+//!         } else {
+//!             // other trap types ...
+//!         }
+//!     }
+//! }
+//! ```
+//!
+//! Now, call supervisor execution function in your bare metal package to finish the discrete
+//! package project.
+//!
+//! ```no_run
+//! # #[cfg(nightly)] // disable checks
+//! #[naked]
+//! #[link_section = ".text.entry"]
+//! #[export_name = "_start"]
+//! unsafe extern "C" fn entry() -> ! {
+//!     #[link_section = ".bss.uninit"]
+//!     static mut SBI_STACK: [u8; LEN_STACK_SBI] = [0; LEN_STACK_SBI];
+//!
+//!     // Note: actual assembly code varies between platforms.
+//!     // Double check documents before continue on.
+//!     core::arch::asm!(
+//!         // 1. Turn off interrupt
+//!         "csrw  mie, zero",
+//!         // 2. Initialize programming langauge runtime
+//!         // only clear bss if hartid is zero
+//!         "csrr  t0, mhartid",
+//!         "bnez  t0, 2f",
+//!         // clear bss segment
+//!         "la  t0, sbss",
+//!         "la  t1, ebss",
+//!         "1:",
+//!         "bgeu  t0, t1, 2f",
+//!         "sd  zero, 0(t0)",
+//!         "addi  t0, t0, 8",
+//!         "j  1b",
+//!         "2:",
+//!         // 3. Prepare stack for each hart
+//!         "la  sp, {stack}",
+//!         "li  t0, {per_hart_stack_size}",
+//!         "csrr  t1, mhartid",
+//!         "addi  t1, t1, 1",
+//!         "1: ",
+//!         "add  sp, sp, t0",
+//!         "addi  t1, t1, -1",
+//!         "bnez  t1, 1b",
+//!         "j  {rust_main}",
+//!         // 4. Clean up
+//!         "j  {finalize}",
+//!         per_hart_stack_size = const LEN_STACK_PER_HART,
+//!         stack = sym SBI_STACK,
+//!         rust_main = sym rust_main,
+//!         finalize = sym finalize,
+//!         options(noreturn)
+//!     )
+//! }
+//!
+//! # fn board_init_once() {}
+//! # fn print_information_once() {}
+//! # fn execute_supervisor(_bp: &()) -> Operation { Operation::Shutdown }
+//! /// Power operation after main function
+//! enum Operation {
+//!     Reboot,
+//!     Shutdown,
+//!     // Add board specific low power modes if necessary. This will allow the
+//!     // function `finalize` to operate on board specific power management chips.
+//! }
+//!
+//! /// Rust entry, call in `entry` assembly function
+//! extern "C" fn rust_main(_hartid: usize, opaque: usize) -> Operation {
+//!     // .. board initialization process ...
+//!     let board_params = board_init_once();
+//!     // .. print necessary information and rustsbi::LOGO ..
+//!     print_information_once();
+//!     // execute supervisor, return as Operation
+//!     execute_supervisor(&board_params)
+//! }
+//!
+//! # fn wfi() {}
+//! /// Perform board specific power operations
+//! ///
+//! /// The function here provides a stub to example power operations.
+//! /// Actual board developers should provide with more practical communications
+//! /// to external chips on power operation.
+//! unsafe extern "C" fn finalize(op: Operation) -> ! {
+//!     match op {
+//!         Operation::Shutdown => {
+//!             // easiest way to make a hart look like powered off
+//!             loop { wfi(); }
+//!         }
+//!         Operation::Reboot => {
+//! # fn entry() -> ! { loop {} } // mock
+//!             // easiest software reset is to jump to entry directly
+//!             entry()
+//!         }
+//!         // .. more power operations goes here ..
+//!     }
+//! }
+//! ```
+//!
+//! Now RustSBI would run on machine environment, you may start a kernel or use SBI test suite
+//! to check if it is properly implemented.
+//!
+//! ## Discrete RustSBI package by singleton based interface
+//!
+//! *Note: Using singleton based RustSBI interface is discouraged in newer designs, for it requires
+//! nightly Rust and extra static memory in data and bss sections for a global interface.*
+//!
+//! Other than instance based interface, some users may find it convenient by using
+//! global singleton semantics. To use it users should enable the `singleton` feature by:
+//!
+//! ```toml
+//! [dependencies]
+//! rustsbi = { version = "0.3.0", features = ["singleton"] }
+//! ```
+//!
+//! RustSBI library will disable all instance based interfaces but provide `init_*`
+//! functions to allow initialize global RustSBI singleton instance.
+//! By enabling this feature, RustSBI uses unstable Rust features to create a universal
+//! lock structure by using atomic `amo` instructions other than `lr`/`sc`.
+//!
 //! # Hypervisor and emulator development with RustSBI
 //!
 //! RustSBI crate supports to develop RISC-V emulators, and both Type-1 and Type-2 hypervisors.
@@ -216,9 +457,10 @@
 //!
 //! The RustSBI Prototyping System is a universal support package provided by RustSBI ecosystem.
 //! It is designed to save development time while providing most SBI feature possible.
-//! Users may choose to download from Prototyping System repository to get various types of RustSBI
-//! packages for their boards. Vendors and contributors may find it easy to adapt new SoCs and
-//! boards into Prototyping System.
+//! It also includes a universal test kernel to allow testing SBI implementations on current environment.
+//! Users may choose to download from [Prototyping System repository](https://github.com/rustsbi/standalone)
+//! to get various types of RustSBI packages for their boards.
+//! Vendors and contributors may find it easy to adapt new SoCs and boards into Prototyping System.
 //!
 //! Discrete SBI packages are SBI environment support packages specially designed for one board
 //! or SoC, it will be provided by board vendor or RustSBI ecosystem.
@@ -230,24 +472,8 @@
 //!
 //! To download binary package for the Prototyping System, visit its project website for a download link.
 //! To download them for discrete packages, RustSBI users may visit distribution source of SoC or board
-//! manufacturers.
-//!
-//! # Non-features
-//!
-//! RustSBI is designed to strictly adapt to the RISC-V Supervisor Binary Interface specification.
-//! Other features useful in developing kernels and hypervisors maybe included in other Rust
-//! ecosystem crates other than this package.
-//!
-//! ## Hardware discovery and feature detection
-//!
-//! According to the RISC-V SBI specification, SBI does not specify any method for hardware discovery.
-//! The supervisor software must rely on the other industry standard hardware
-//! discovery methods (i.e. Device Tree or ACPI) for that purpose.
-//!
-//! To detect any feature under bare metal or under supervisor level, developers may depend on
-//! any hardware discovery methods, or use try-execute-trap method to detect any instructions or
-//! CSRs. If SBI is implemented in user level emulators, it may requires to depend on operating
-//! system calls or use the signal trap method to detect any RISC-V core features.
+//! manufacturers. Additionally, users may visit [the awesome page](https://github.com/rustsbi/awesome-rustsbi)
+//! for a curated list ofboth Prototyping System and discrete packages provided by RustSBI ecosystem.
 //!
 //! # Notes for RustSBI developers
 //!
@@ -268,9 +494,20 @@
 //! The RustSBI ecosystem would provide different level of support for each board, those support
 //! packages would use `rustsbi` crate as library to provide different type of SBI binary releases.
 //!
+//! ## Hardware discovery and feature detection
+//!
+//! According to the RISC-V SBI specification, SBI itself does not specify any method for hardware discovery.
+//! The supervisor software must rely on the other industry standard hardware
+//! discovery methods (i.e. Device Tree or ACPI) for that purpose.
+//!
+//! To detect any feature under bare metal or under supervisor level, developers may depend on
+//! any hardware discovery methods, or use try-execute-trap method to detect any instructions or
+//! CSRs. If SBI is implemented in user level emulators, it may requires to depend on operating
+//! system calls or use the signal trap method to detect any RISC-V core features.
+//!
 //! ## Legacy SBI extension
 //!
-//! *Note: RustSBI legacy support is only designed for backward compability of RISC-V SBI standard.
+//! *Note: RustSBI legacy support is only designed for backward compability of legacy RISC-V SBI standard.
 //! It's disabled by default and it's not suggested to include legacy functions in newer firmware designs.
 //! Modules other than legacy console is replaced by individual modules in SBI.
 //! Kernels are not suggested to use legacy functions in practice.