Selaa lähdekoodia

doc: add documents for derive macro RustSBI

... as well as a small fix on Cargo.toml

Signed-off-by: Zhouqi Jiang <[email protected]>
Zhouqi Jiang 1 vuosi sitten
vanhempi
commit
acf4007b83
2 muutettua tiedostoa jossa 232 lisäystä ja 2 poistoa
  1. 1 1
      Cargo.toml
  2. 231 1
      src/lib.rs

+ 1 - 1
Cargo.toml

@@ -28,7 +28,7 @@ default = []
 # This feature enables to use RISC-V primitives on current machine mode environment
 # If you are developing a cross-architecture virtual machine, consider disabling this feature
 # to customize environment variables for RISC-V architecture like mvendorid, mimpid, etc.
-machine = ["dep:riscv"]
+machine = ["rustsbi-macros/machine", "dep:riscv"]
 
 [package.metadata.docs.rs]
 default-target = "riscv64imac-unknown-none-elf"

+ 231 - 1
src/lib.rs

@@ -546,7 +546,231 @@ const RUSTSBI_VERSION: usize =
 pub const VERSION: &str = env!("CARGO_PKG_VERSION");
 
 pub extern crate sbi_spec as spec;
-/// Implement RustSBI trait for structure of each extensions.
+/// Generate `RustSBI` implementation for structure of each extensions.
+///
+/// # Usage
+///
+/// The `#[derive(RustSBI)]` macro provides a convenient way of building `RustSBI` trait implementation.
+/// To use this macro, say that we have a struct `MyFence` with RISC-V SBI Remote Fence extension
+/// implemented using `rustsbi::Fence` trait. Then, we build a struct around it, representing a
+/// whole SBI implementation including one `Fence` extension only; we can name it `MySBI`:
+///
+/// ```rust
+/// struct MySBI {
+///     fence: MyFence,
+/// }
+///
+/// struct MyFence { /* fields */ }
+///
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// impl rustsbi::Fence for MyFence {
+///     /* implementation details */
+/// #   fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #   fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #   fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// }
+/// ```
+///
+/// Here, we declared the field named `fence` with type `MyFence`. The name `fence` is special, it tells
+/// RustSBI derive macro that this field implements SBI Remote Fence instead of other SBI extensions.
+/// We can continue to adding more fields into `MySBI`. For example, if we have RISC-V SBI Time extension
+/// implementation `MyTimer`, we can add it to `MySBI`:
+///
+/// ```rust
+/// struct MySBI {
+///     fence: MyFence,
+///     timer: MyTimer,
+/// }
+/// # struct MyFence;
+/// # struct MyTimer;
+/// ```
+///
+/// Don't forget that the name `timer` is also a special field name. There is a detailed list after this
+/// chapter describing what special field name would the `RustSBI` macro identify.
+///
+/// It looks like we are ready to derive `RustSBI` macro on `MySBI`! Let's try it now ...
+///
+/// ```compile_fail
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     fence: MyFence,
+///     timer: MyTimer,
+/// #   #[cfg(feature = "machine")] info: () // compile would success on #[cfg(feature = "machine")], cause it always to fail
+/// }
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyTimer;
+/// # impl rustsbi::Timer for MyTimer {
+/// #     fn set_timer(&self, stime_value: u64) { unimplemented!() }
+/// # }
+/// ```
+///
+/// Oops! Compile failed. We'd check what happened here:
+///
+/// ```text
+/// error: can't derive RustSBI: #[cfg(feature = "machine")] is needed to derive RustSBI with no extra MachineInfo provided; consider adding an `info` parameter to provide machine information if RustSBI is not run on machine mode.
+///  --> example.rs:LL:10
+///    |
+/// LL | #[derive(RustSBI)]
+///    |          ^^^^^^^
+///    |
+///  = note: this error originates in the derive macro `RustSBI` (in Nightly builds, run with -Z macro-backtrace for more info)
+///
+/// error: aborting due to previous error
+/// ```
+///
+/// The error message hints that we don't have machine information implementing trait `MachineInfo`
+/// provided. By default, RustSBI is targeted to provide RISC-V supervisor environment on any hardware,
+/// targeting hypervisor, emulator and binary translation applications. In this case, the virtualized
+/// environment should provide the supervisor with machine information like `mvendorid`, `marchid` and
+/// `mimpid` values. RustSBI could also be used on bare-metal RISC-V machines where such values would
+/// be directly accessible through CSR read operations.
+///
+/// If we are targeting bare-metal, we can use the RustSBI library with `#[cfg(feature = "machine")]`
+/// enabled by changing `dependencies` section in `Cargo.toml` file (if we are using Cargo):
+///
+/// ```toml
+/// [dependencies]
+/// rustsbi = { version = "0.4.0", features = ["machine"] }
+/// ```
+///
+/// If that's not the case and we are writing a virtualization targeted application, we should add a
+/// `MachineInfo` implementation into the structure like `MySBI` mentioned above, with a special field
+/// name `info`. We can do it like:
+///
+/// ```rust
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     fence: MyFence,
+///     timer: MyTimer,
+/// #   #[cfg(not(feature = "machine"))]
+///     info: MyMachineInfo,
+/// }
+///
+/// struct MyMachineInfo;
+///
+/// impl rustsbi::MachineInfo for MyMachineInfo {
+///     #[inline]
+///     fn mvendorid(&self) -> usize { todo!("add real value here") }
+///     #[inline]
+///     fn marchid(&self) -> usize { todo!("add real value here") }
+///     #[inline]
+///     fn mimpid(&self) -> usize { todo!("add real value here") }
+/// }
+/// # use rustsbi::{RustSBI, HartMask};
+/// # use sbi_spec::binary::SbiRet;
+/// # struct MyFence;
+/// # impl rustsbi::Fence for MyFence {
+/// #     fn remote_fence_i(&self, _: HartMask) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma(&self, _: HartMask, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// #     fn remote_sfence_vma_asid(&self, _: HartMask, _: usize, _: usize, _: usize) -> SbiRet { unimplemented!() }
+/// # }
+/// # struct MyTimer;
+/// # impl rustsbi::Timer for MyTimer {
+/// #     fn set_timer(&self, stime_value: u64) { unimplemented!() }
+/// # }
+/// ```
+///
+/// Then, when we compile our code with `MySBI`, we'll found that the code now compiles successfully.
+///
+/// To use the derived `RustSBI` implementation, we note out that this structure now implements the trait
+/// `RustSBI` with function `handle_ecall`. We can pass SBI extension, function and parameters into
+/// `handle_ecall`, and reads the SBI call result from its return value with the type `SbiRet`.
+/// To illustrate this feature, we make an SBI call to read the SBI implementation ID, like:
+///
+/// ```rust
+/// # use rustsbi::RustSBI;
+/// #[derive(RustSBI)]
+/// struct MySBI {
+///     /* we omit the extension fields by now */
+/// #   info: MyMachineInfo,
+/// }
+///
+/// fn main() {
+///     // Create a MySBI instance.
+///     let sbi = MySBI {
+///         /* include initial values for fields */
+/// #       info: MyMachineInfo
+///     };
+///     // Make the call. Read SBI implementation ID resides in extension Base (0x10),
+///     // with function id 1, and it doesn't have any parameters.
+///     let ret = sbi.handle_ecall(0x10, 0x1, [0; 6]);
+///     // Let's check the result...
+///     println!("SBI implementation ID for MySBI: {}", ret.value);
+///     assert_eq!(ret.value, 4);
+/// }
+/// # struct MyMachineInfo;
+/// # impl rustsbi::MachineInfo for MyMachineInfo {
+/// #     fn mvendorid(&self) -> usize { unimplemented!() }
+/// #     fn marchid(&self) -> usize { unimplemented!() }
+/// #     fn mimpid(&self) -> usize { unimplemented!() }
+/// # }
+/// ```
+///
+/// Run the code and we'll find following output in the console:
+///
+/// ```text
+/// SBI implementation ID for MySBI: 4
+/// ```
+///
+/// The SBI call returns the number 4 as SBI call result. By looking up the RISC-V SBI Specification,
+/// we acknowledge that RustSBI have the implementation ID of 4. You have successfully made your first
+/// SBI call from a derived `RustSBI` implementation!
+///
+/// If we learn further from the RISC-V privileged software architecture, we may know more about how
+/// RISC-V SBI works on an environment to support supervisor software. RISC-V SBI implementations
+/// accept SBI calls by supervisor-level environment call caused by `ecall` instruction under supervisor
+/// mode. Each `ecall` raises a RISC-V exception which the environment must process with. The SBI
+/// environment, either bare-metal or virtually, would save context, read extension, function and parameters
+/// and call the `handle_ecall` function provided by `RustSBI` trait. Then, the function returns
+/// with an `SbiRet`; we reads back `value` and `error` to store them into the saved context.
+/// Finally, when the context restores, the supervisor mode software (kernels, etc.) could get the
+/// SBI call result from register values.
+///
+/// Now we have learned basical usages of the derive macro `RustSBI`. We can dive deeper and use RustSBI
+/// in real cases with ease. Congratulations!
+///
+/// # Supported extensions
+///
+/// The derive macro `RustSBI` supports all the standard RISC-V SBI extensions this library supports.
+/// When we add extensions into SBI structure fields, special field names are identified by RustSBI
+/// derive macro. Here is a list on them:
+///
+/// | Field names | RustSBI trait | Extension |
+/// |:------------|:----------|:--------------|
+/// | `time` or `timer` | [`Timer`](trait.Timer.html) | Timer programmer extension |
+/// | `ipi` or `spi` | [`Ipi`](trait.Ipi.html) | S-mode Inter Processor Interrupt |
+/// | `fence` or `rfnc` | [`Fence`](trait.Fence.html) | Remote Fence extension |
+/// | `hsm` | [`Hsm`](trait.Hsm.html) | Hart State Monitor extension |
+/// | `reset` or `srst` | [`Reset`](trait.Reset.html) | System Reset extension |
+/// | `pmu` | [`Pmu`](trait.Pmu.html) | Performance Monitor Unit extension |
+/// | `console` or `dbcn` | [`Console`](trait.Console.html) | Debug Console extension |
+/// | `susp` | [`Susp`](trait.Susp.html) | System Suspend extension |
+/// | `cppc` | [`Cppc`](trait.Cppc.html) | SBI CPPC extension |
+/// | `nacl` | [`Nacl`](trait.Nacl.html) | Nested Acceleration extension |
+/// | `sta` | [`Sta`](trait.Sta.html) | Steal Time Accounting extension |
+///
+/// The `MachineInfo` parameter is used by RISC-V SBI Base extension which is always supported on all
+/// RISC-V SBI implementations. RustSBI provides Base extension with additional `MachineInfo` by default.
+///
+/// | Field names | RustSBI trait | Description |
+/// |:------------|:----------|:--------------|
+/// | `info` or `machine_info` | [`MachineInfo`](trait.MachineInfo.html) | Machine information used by Base extension |
+///
+/// Or, if `#[cfg(feature = "machine")]` is enabled, RustSBI derive macro does not require additional
+/// machine information but reads them by RISC-V CSR operation when we don't have any `MachineInfo`
+/// in the structure. This feature would only work if RustSBI runs directly on machine mode hardware.
+/// If we are targeting other environments (virtualization etc.) we should provide `MachineInfo` instead
+/// of using the machine feature.
+///
+/// # Examples
 ///
 /// This macro should be used over a struct of RISC-V SBI extension implementaions.
 /// For example:
@@ -557,6 +781,9 @@ pub extern crate sbi_spec as spec;
 ///     fence: MyFence,
 ///     info: MyMachineInfo,
 /// }
+///
+/// // we assume that `MyFence` implements `rustsbi::Fence`
+/// // and `MyMachineInfo` implements `rustsbi::MachineInfo`.
 /// # use rustsbi::{RustSBI, HartMask};
 /// # use sbi_spec::binary::SbiRet;
 /// # struct MyFence;
@@ -572,6 +799,9 @@ pub extern crate sbi_spec as spec;
 /// #     fn mimpid(&self) -> usize { 3 }
 /// # }
 /// ```
+///
+/// # Notes
+// note: following documents are inherted from `RustSBI` in the `rustsbi_macros` package.
 #[doc(inline)]
 pub use rustsbi_macros::RustSBI;