ソースを参照

Merge branch 'main' of github.com:rustsbi/rustsbi

luojia65 2 年 前
コミット
91cfa36d14
25 ファイル変更338 行追加1699 行削除
  1. 6 46
      .github/workflows/rust.yml
  2. 20 2
      CHANGELOG.md
  3. 3 14
      Cargo.toml
  4. 11 12
      README.md
  5. 0 33
      src/base.rs
  6. 15 0
      src/console.rs
  7. 0 27
      src/ecall/base.rs
  8. 0 20
      src/ecall/hsm.rs
  9. 0 142
      src/ecall/mod.rs
  10. 0 57
      src/ecall/pmu.rs
  11. 0 25
      src/ecall/rfnc.rs
  12. 0 12
      src/ecall/spi.rs
  13. 0 14
      src/ecall/srst.rs
  14. 0 36
      src/ecall/time.rs
  15. 23 94
      src/hart_mask.rs
  16. 9 65
      src/hsm.rs
  17. 239 472
      src/instance.rs
  18. 0 29
      src/ipi.rs
  19. 0 93
      src/legacy_stdio.rs
  20. 10 78
      src/lib.rs
  21. 2 115
      src/pmu.rs
  22. 0 54
      src/reset.rs
  23. 0 105
      src/rfence.rs
  24. 0 31
      src/timer.rs
  25. 0 123
      src/util.rs

+ 6 - 46
.github/workflows/rust.yml

@@ -49,26 +49,16 @@ jobs:
         uses: Swatinem/rust-cache@v2
         with:
           key: ${{ matrix.TARGET }}
-      - name: Check (no default features, SBI 1.0.0)
+      - name: Check (no default features)
         uses: actions-rs/cargo@v1
         with:
           command: check
-          args: --target ${{ matrix.TARGET }} --no-default-features --verbose
-      - name: Check (no default features, SBI 2.0-rc1)
-        uses: actions-rs/cargo@v1
-        with:
-          command: check
-          args: --features "sbi_2_0" --target ${{ matrix.TARGET }} --no-default-features --verbose
-      - name: Check (machine, SBI 1.0.0)
+          args: --target ${{ matrix.TARGET }} --verbose
+      - name: Check (machine)
         uses: actions-rs/cargo@v1
         with:
           command: check
           args: --target ${{ matrix.TARGET }} --features "machine" --verbose
-      - name: Check (machine, SBI 2.0-rc1)
-        uses: actions-rs/cargo@v1
-        with:
-          command: check
-          args: --features "sbi_2_0" --target ${{ matrix.TARGET }} --features "machine" --verbose
 
   check-nightly:
     name: Cargo check (nightly)
@@ -89,26 +79,6 @@ jobs:
         uses: Swatinem/rust-cache@v2
         with:
           key: ${{ matrix.TARGET }}
-      - name: Check (singleton, SBI 1.0.0)
-        uses: actions-rs/cargo@v1
-        with:
-          command: check
-          args: --features "singleton" --target ${{ matrix.TARGET }} --verbose
-      - name: Check (singleton, SBI 2.0-rc1)
-        uses: actions-rs/cargo@v1
-        with:
-          command: check
-          args: --features "singleton,sbi_2_0" --target ${{ matrix.TARGET }} --verbose
-      - name: Check (legacy, SBI 1.0.0)
-        uses: actions-rs/cargo@v1
-        with:
-          command: check
-          args: --features "legacy" --target ${{ matrix.TARGET }} --verbose
-      - name: Check (legacy, SBI 2.0-rc1)
-        uses: actions-rs/cargo@v1
-        with:
-          command: check
-          args: --features "legacy,sbi_2_0" --target ${{ matrix.TARGET }} --verbose
 
   tests:
     name: Run tests
@@ -128,23 +98,13 @@ jobs:
         uses: actions-rs/cargo@v1
         with:
           command: clippy
-      - name: Run tests (no default features, SBI 1.0.0)
+      - name: Run tests (no default features)
         uses: actions-rs/cargo@v1
         with:
           command: test
-          args: --no-default-features --verbose
-      - name: Run tests (no default features, SBI 2.0-rc1)
-        uses: actions-rs/cargo@v1
-        with:
-          command: test
-          args: --features "sbi_2_0" --no-default-features --verbose
-      - name: Run tests (machine, SBI 1.0.0)
+          args: --verbose
+      - name: Run tests (machine)
         uses: actions-rs/cargo@v1
         with:
           command: test
           args: --features "machine" --verbose
-      - name: Run tests (machine, SBI 2.0-rc1)
-        uses: actions-rs/cargo@v1
-        with:
-          command: test
-          args: --features "machine,sbi_2_0" --verbose

+ 20 - 2
CHANGELOG.md

@@ -7,6 +7,21 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## Unreleased
 
+### Added
+
+### Modified
+
+- run on provided `MachineInfo` by default; bare metal M-mode environment should gate `machine`
+- doc: grammar tweaks in hsm module
+
+### Removed
+
+- `sbi_2_0` feature; RustSBI now supports SBI 2.0-rc1 by default
+- support for legacy SBI extensions
+- singleton based RustSBI interface; use struct `RustSBI` instead
+
+## [0.3.2] - 2023-02-26
+
 Bump RISC-V SBI specification version to 2.0-rc1.
 
 ### Added
@@ -19,7 +34,9 @@ Bump RISC-V SBI specification version to 2.0-rc1.
 
 - doc: amend using SBI 2.0-rc1 specification
 
-### Removed
+### Fixed
+
+- `impl<T: Console> Console for &T`
 
 ## [0.3.1] - 2023-01-20
 
@@ -171,7 +188,8 @@ RustSBI is adapted to SBI standard with implementation number 4.
 - RISC-V Privileged Specification v1.11
 - Backward compatible to privileged spec v1.9.1
 
-[Unreleased]: https://github.com/rustsbi/rustsbi/compare/v0.3.1...HEAD
+[Unreleased]: https://github.com/rustsbi/rustsbi/compare/v0.3.2...HEAD
+[0.3.2]: https://github.com/rustsbi/rustsbi/compare/v0.3.1...v0.3.2
 [0.3.1]: https://github.com/rustsbi/rustsbi/compare/v0.3.0...v0.3.1
 [0.3.0]: https://github.com/rustsbi/rustsbi/compare/v0.2.2...v0.3.0
 [0.2.2]: https://github.com/rustsbi/rustsbi/compare/v0.2.1...v0.2.2

+ 3 - 14
Cargo.toml

@@ -1,7 +1,7 @@
 [package]
 name = "rustsbi"
 description = "Minimal RISC-V's SBI implementation library in Rust"
-version = "0.3.1"
+version = "0.4.0-alpha.1"
 authors = [
     "Luo Jia <[email protected]>",
     "Campbell He <[email protected]>",
@@ -17,27 +17,16 @@ edition = "2021"
 exclude = ["/.github"]
 
 [dependencies]
-sbi-spec = "0.0.5-rc.2"
+sbi-spec = "0.0.5"
 riscv = { version = "0.10.1", optional = true }
 
 [features]
-default = ["machine"]
-# Enable RISC-V SBI 2.0-rc1 extensions, i.e. the DBCN extension
-# It will add a generic parameter to RustSBI instance structure.
-# FIXME: remove this feature (and from CI workflow) on rustsbi 0.4.0 and adds this generic parameter permanently onto RustSBI.
-sbi_2_0 = []
+default = []
 # Run RustSBI on machine mode
 # 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"]
-# Build RustSBI singleton
-# This would enable `init_*` functions. It will take extra place on bss or data region
-# to take care of singleton reference locks.
-# Disable this feature to use instance based RustSBI environment.
-singleton = ["dep:riscv", "machine"]
-# Support legacy extension; this feature is not included by default.
-legacy = ["sbi-spec/legacy", "singleton"]
 
 [package.metadata.docs.rs]
 default-target = "riscv64imac-unknown-none-elf"

+ 11 - 12
README.md

@@ -22,14 +22,20 @@ or consult vendors if they provide discrete RustSBI package support.
 
 To compile RustSBI library, you need at least stable Rust version of `rustc 1.65.0`.
 
-If you are using feature `singleton` or `legacy` to support legacy SBI extensions, you are required to install
-nightly Rust compiler. You may need at least nightly Rust version of `rustc 1.59.0-nightly (c5ecc1570 2021-12-15)`.
-
 ## Build this project
 
 RustSBI is usually used as a library or dependency. If you wish to, you may build RustSBI library itself using the
 following command:
 
+```bash
+cargo build
+```
+
+The build should finish without any errors.
+
+Building under native platform will allow development of hypervisors and emulators.
+To cross-build for a RISC-V platform environment, you may install the target and build onto it instead:
+
 ```bash
 # If you don't have the cross compile target installed, install it first
 rustup target add riscv64imac-unknown-none-elf
@@ -37,15 +43,8 @@ rustup target add riscv64imac-unknown-none-elf
 cargo build --target riscv64imac-unknown-none-elf
 ```
 
-The build should finish without any errors.
-
-If you see any error like `invalid register a0: unknown register`, it's likely that you cross built this project into
-platforms other than RISC-V. RustSBI adapts to RISC-V SBI interface, so you may cross build this project into any bare
-metal RISC-V platform targets instead.
-
-The target platform of RustSBI is usually a bare metal target. Under normal circumstances these targets in Rust would
-start with `riscv??-` and end with `-none-elf`. If a non-bare metal target is built to, it would throw build error
-in `riscv` dependency crate or RustSBI library itself.
+The target platform of RISC-V platform firmware is usually a bare metal target.
+Under normal circumstances these targets in Rust would start with `riscv??-` and end with `-none-elf`.
 
 ## Features
 

+ 0 - 33
src/base.rs

@@ -1,33 +0,0 @@
-#[cfg(feature = "singleton")]
-#[inline]
-pub fn probe_extension(extension: usize) -> bool {
-    use sbi_spec::*;
-    match extension {
-        base::EID_BASE => true,
-        time::EID_TIME => crate::timer::probe_timer(),
-        spi::EID_SPI => crate::ipi::probe_ipi(),
-        rfnc::EID_RFNC => crate::rfence::probe_rfence(),
-        srst::EID_SRST => crate::reset::probe_reset(),
-        hsm::EID_HSM => crate::hsm::probe_hsm(),
-        pmu::EID_PMU => crate::pmu::probe_pmu(),
-        // Legacy extensions
-        // if feature 'legacy' is not enabled, these extensions fall back to false.
-        #[cfg(feature = "legacy")]
-        legacy::LEGACY_SET_TIMER => crate::timer::probe_timer(),
-        #[cfg(feature = "legacy")]
-        legacy::LEGACY_SEND_IPI => crate::ipi::probe_ipi(),
-        // LEGACY_CLEAR_IPI implemented in ecall/mod.rs directly, so it always exists.
-        #[cfg(feature = "legacy")]
-        legacy::LEGACY_CLEAR_IPI => true,
-        #[cfg(feature = "legacy")]
-        legacy::LEGACY_SHUTDOWN => crate::reset::probe_reset(),
-        // we don't include LEGACY_REMOTE_FENCE_I, LEGACY_REMOTE_SFENCE_VMA
-        // and LEGACY_REMOTE_SFENCE_VMA_ASID here,
-        // for RustSBI ecall/mod.rs did not implement these legacy extensions.
-        #[cfg(feature = "legacy")]
-        legacy::LEGACY_CONSOLE_PUTCHAR | legacy::LEGACY_CONSOLE_GETCHAR => {
-            crate::legacy_stdio::probe_legacy_stdio()
-        }
-        _ => false,
-    }
-}

+ 15 - 0
src/console.rs

@@ -85,3 +85,18 @@ pub trait Console {
     /// | `SbiRet::failed()`        | Failed to write the byte due to I/O errors.
     fn write_byte(&self, byte: u8) -> SbiRet;
 }
+
+impl<T: Console> Console for &T {
+    #[inline]
+    fn write(&self, bytes: Physical<&[u8]>) -> SbiRet {
+        T::write(self, bytes)
+    }
+    #[inline]
+    fn read(&self, bytes: Physical<&mut [u8]>) -> SbiRet {
+        T::read(self, bytes)
+    }
+    #[inline]
+    fn write_byte(&self, byte: u8) -> SbiRet {
+        T::write_byte(self, byte)
+    }
+}

+ 0 - 27
src/ecall/base.rs

@@ -1,27 +0,0 @@
-use sbi_spec::binary::SbiRet;
-
-#[inline]
-pub(super) fn handle_ecall(function: usize, param0: usize) -> SbiRet {
-    use crate::base::*;
-    use crate::{IMPL_ID_RUSTSBI, RUSTSBI_VERSION, SBI_SPEC_MAJOR, SBI_SPEC_MINOR};
-    use riscv::register::{marchid, mimpid, mvendorid};
-    use sbi_spec::base::*;
-
-    let value = match function {
-        GET_SBI_SPEC_VERSION => (SBI_SPEC_MAJOR << 24) | (SBI_SPEC_MINOR),
-        GET_SBI_IMPL_ID => IMPL_ID_RUSTSBI,
-        GET_SBI_IMPL_VERSION => RUSTSBI_VERSION,
-        PROBE_EXTENSION => {
-            if probe_extension(param0) {
-                UNAVAILABLE_EXTENSION.wrapping_add(1)
-            } else {
-                UNAVAILABLE_EXTENSION
-            }
-        }
-        GET_MVENDORID => mvendorid::read().map(|r| r.bits()).unwrap_or(0),
-        GET_MARCHID => marchid::read().map(|r| r.bits()).unwrap_or(0),
-        GET_MIMPID => mimpid::read().map(|r| r.bits()).unwrap_or(0),
-        _ => return SbiRet::not_supported(),
-    };
-    SbiRet::success(value)
-}

+ 0 - 20
src/ecall/hsm.rs

@@ -1,20 +0,0 @@
-use sbi_spec::binary::SbiRet;
-
-#[inline]
-pub(super) fn handle_ecall(function: usize, param0: usize, param1: usize, param2: usize) -> SbiRet {
-    use crate::hsm::*;
-    use sbi_spec::hsm::*;
-    match function {
-        HART_START => hart_start(param0, param1, param2),
-        HART_STOP => hart_stop(),
-        HART_GET_STATUS => hart_get_status(param0),
-        HART_SUSPEND => {
-            if let Ok(suspend_type) = u32::try_from(param0) {
-                hart_suspend(suspend_type, param1, param2)
-            } else {
-                SbiRet::invalid_param()
-            }
-        }
-        _ => SbiRet::not_supported(),
-    }
-}

+ 0 - 142
src/ecall/mod.rs

@@ -1,142 +0,0 @@
-//! 这个模块将会处理所有的 SBI 调用陷入,你应该在中断处理函数里调用 `handle_ecall`
-
-// §4
-mod base;
-// §6
-mod time;
-// §7
-mod spi;
-// §8
-mod rfnc;
-// §9
-mod hsm;
-// §10
-mod srst;
-// §11
-mod pmu;
-
-#[cfg(feature = "legacy")]
-use crate::{
-    ipi::send_ipi, legacy_stdio_getchar, legacy_stdio_putchar, reset::legacy_reset, HartMask,
-};
-use sbi_spec::{self as spec, binary::SbiRet};
-
-/// Supervisor environment call handler function
-///
-/// This function is used by platform runtime to handle environment call `ecall` instruction.
-///
-/// You should call this function in your runtime's exception handler.
-/// If the incoming exception is caused by supervisor `ecall`,
-/// call this function with parameters extracted from trap frame.
-/// After this function returns, store the return value into `a0` and `a1` parameters.
-///
-/// This function also adapts to the legacy functions.
-/// If the supervisor called any of legacy function, the `a0` and `a1` parameter
-/// is transferred to error and value field of `SbiRet` respectively.
-/// In this case, implementations should always store the result into `a0` and `a1` in
-/// any environment call functions including legacy functions.
-///
-/// # Example
-///
-/// A typical usage:
-///
-/// ```no_run
-/// # use riscv::register::{mepc, mcause::{self, Trap, Exception}};
-/// # struct TrapFrame { a0: usize, a1: usize, a2: usize, a3: usize,
-/// # a4: usize, a5: usize, a6: usize, a7: usize }
-/// extern "C" fn rust_handle_exception(ctx: &mut TrapFrame) {
-///     if mcause::read().cause() == Trap::Exception(Exception::SupervisorEnvCall) {
-///         let params = [ctx.a0, ctx.a1, ctx.a2, ctx.a3, ctx.a4, ctx.a5];
-///         let ans = rustsbi::ecall(ctx.a7, ctx.a6, params);
-///         ctx.a0 = ans.error;
-///         ctx.a1 = ans.value;
-///         mepc::write(mepc::read().wrapping_add(4));
-///     }
-///     // other conditions..
-/// }
-/// ```
-///
-/// Do not forget to advance `mepc` by 4 after an ecall is handled.
-/// This skips the `ecall` instruction itself which is 4-byte long in all conditions.
-#[inline]
-pub fn handle_ecall(extension: usize, function: usize, param: [usize; 6]) -> SbiRet {
-    match extension {
-        spec::rfnc::EID_RFNC => {
-            rfnc::handle_ecall(function, param[0], param[1], param[2], param[3], param[4])
-        }
-        spec::time::EID_TIME => match () {
-            #[cfg(target_pointer_width = "64")]
-            () => time::handle_ecall(function, param[0]),
-            #[cfg(target_pointer_width = "32")]
-            () => time::handle_ecall(function, param[0], param[1]),
-        },
-        spec::spi::EID_SPI => spi::handle_ecall(function, param[0], param[1]),
-        spec::base::EID_BASE => base::handle_ecall(function, param[0]),
-        spec::hsm::EID_HSM => hsm::handle_ecall(function, param[0], param[1], param[2]),
-        spec::srst::EID_SRST => srst::handle_ecall(function, param[0], param[1]),
-        spec::pmu::EID_PMU => match () {
-            #[cfg(target_pointer_width = "64")]
-            () => pmu::handle_ecall(function, param[0], param[1], param[2], param[3], param[4]),
-            #[cfg(target_pointer_width = "32")]
-            () => pmu::handle_ecall(
-                function, param[0], param[1], param[2], param[3], param[4], param[5],
-            ),
-        },
-        // handle legacy callings.
-        //
-        // legacy 调用不使用 a1 返回值,总把 a1 填回 `SbiRet::value` 来模拟非 legacy 的行为。
-        #[cfg(feature = "legacy")]
-        spec::legacy::LEGACY_SET_TIMER => {
-            match () {
-                #[cfg(target_pointer_width = "64")]
-                () => crate::timer::set_timer(param[0] as _),
-                #[cfg(target_pointer_width = "32")]
-                () => crate::timer::set_timer(concat_u32(param[1] as _, param[0] as _)),
-            };
-            SbiRet {
-                error: param[0],
-                value: param[1],
-            }
-        }
-        #[cfg(feature = "legacy")]
-        spec::legacy::LEGACY_CONSOLE_PUTCHAR => {
-            legacy_stdio_putchar(param[0] as _);
-            SbiRet {
-                error: param[0],
-                value: param[1],
-            }
-        }
-        #[cfg(feature = "legacy")]
-        spec::legacy::LEGACY_CONSOLE_GETCHAR => SbiRet {
-            error: legacy_stdio_getchar(),
-            value: param[1],
-        },
-        #[cfg(feature = "legacy")]
-        spec::legacy::LEGACY_SEND_IPI => {
-            send_ipi(unsafe { HartMask::legacy_from_addr(param[0]) });
-            SbiRet {
-                error: param[0],
-                value: param[1],
-            }
-        }
-        #[cfg(feature = "legacy")]
-        spec::legacy::LEGACY_CLEAR_IPI => {
-            unsafe {
-                riscv::register::mip::clear_ssoft();
-            }
-            SbiRet {
-                error: param[0],
-                value: param[1],
-            }
-        }
-        #[cfg(feature = "legacy")]
-        spec::legacy::LEGACY_SHUTDOWN => legacy_reset(),
-        _ => SbiRet::not_supported(),
-    }
-}
-
-#[cfg(target_pointer_width = "32")]
-#[inline]
-const fn concat_u32(h: usize, l: usize) -> u64 {
-    ((h as u64) << 32) | (l as u64)
-}

+ 0 - 57
src/ecall/pmu.rs

@@ -1,57 +0,0 @@
-use sbi_spec::binary::SbiRet;
-
-#[cfg(target_pointer_width = "64")]
-#[inline]
-pub fn handle_ecall(
-    function: usize,
-    param0: usize,
-    param1: usize,
-    param2: usize,
-    param3: usize,
-    param4: usize,
-) -> SbiRet {
-    use crate::pmu::*;
-    use sbi_spec::pmu::*;
-    match function {
-        PMU_NUM_COUNTERS => num_counters(),
-        PMU_COUNTER_GET_INFO => counter_get_info(param0),
-        PMU_COUNTER_CONFIG_MATCHING => {
-            counter_config_matching(param0, param1, param2, param3, param4 as _)
-        }
-        PMU_COUNTER_START => counter_start(param0, param1, param2, param3 as _),
-        PMU_COUNTER_STOP => counter_stop(param0, param1, param2),
-        PMU_COUNTER_FW_READ => counter_fw_read(param0),
-        #[cfg(feature = "sbi_2_0")]
-        PMU_COUNTER_FW_READ_HI => counter_fw_read_hi(param0),
-        _ => SbiRet::not_supported(),
-    }
-}
-
-#[cfg(target_pointer_width = "32")]
-#[inline]
-pub fn handle_ecall(
-    function: usize,
-    param0: usize,
-    param1: usize,
-    param2: usize,
-    param3: usize,
-    param4: usize,
-    param5: usize,
-) -> SbiRet {
-    use super::concat_u32;
-    use crate::pmu::*;
-    use sbi_spec::pmu::*;
-    match function {
-        PMU_NUM_COUNTERS => num_counters(),
-        PMU_COUNTER_GET_INFO => counter_get_info(param0),
-        PMU_COUNTER_CONFIG_MATCHING => {
-            counter_config_matching(param0, param1, param2, param3, concat_u32(param5, param4))
-        }
-        PMU_COUNTER_START => counter_start(param0, param1, param2, concat_u32(param4, param3)),
-        PMU_COUNTER_STOP => counter_stop(param0, param1, param2),
-        PMU_COUNTER_FW_READ => counter_fw_read(param0),
-        #[cfg(feature = "sbi_2_0")]
-        PMU_COUNTER_FW_READ_HI => counter_fw_read_hi(param0),
-        _ => SbiRet::not_supported(),
-    }
-}

+ 0 - 25
src/ecall/rfnc.rs

@@ -1,25 +0,0 @@
-use sbi_spec::binary::SbiRet;
-
-#[inline]
-pub fn handle_ecall(
-    function: usize,
-    param0: usize,
-    param1: usize,
-    param2: usize,
-    param3: usize,
-    param4: usize,
-) -> SbiRet {
-    use crate::rfence::*;
-    use sbi_spec::rfnc::*;
-    let hart_mask = crate::HartMask::from_mask_base(param0, param1);
-    match function {
-        REMOTE_FENCE_I => remote_fence_i(hart_mask),
-        REMOTE_SFENCE_VMA => remote_sfence_vma(hart_mask, param2, param3),
-        REMOTE_SFENCE_VMA_ASID => remote_sfence_vma_asid(hart_mask, param2, param3, param4),
-        REMOTE_HFENCE_GVMA_VMID => remote_hfence_gvma_vmid(hart_mask, param2, param3, param4),
-        REMOTE_HFENCE_GVMA => remote_hfence_gvma(hart_mask, param2, param3),
-        REMOTE_HFENCE_VVMA_ASID => remote_hfence_vvma_asid(hart_mask, param2, param3, param4),
-        REMOTE_HFENCE_VVMA => remote_hfence_vvma(hart_mask, param2, param3),
-        _ => SbiRet::not_supported(),
-    }
-}

+ 0 - 12
src/ecall/spi.rs

@@ -1,12 +0,0 @@
-use sbi_spec::binary::SbiRet;
-
-#[inline]
-pub(super) fn handle_ecall(function: usize, param0: usize, param1: usize) -> SbiRet {
-    use crate::hart_mask::HartMask;
-    use crate::ipi::*;
-    use sbi_spec::spi::*;
-    match function {
-        SEND_IPI => send_ipi(HartMask::from_mask_base(param0, param1)),
-        _ => SbiRet::not_supported(),
-    }
-}

+ 0 - 14
src/ecall/srst.rs

@@ -1,14 +0,0 @@
-use sbi_spec::binary::SbiRet;
-
-#[inline]
-pub(super) fn handle_ecall(function: usize, param0: usize, param1: usize) -> SbiRet {
-    use crate::reset::*;
-    use sbi_spec::srst::*;
-    match function {
-        SYSTEM_RESET => match (u32::try_from(param0), u32::try_from(param1)) {
-            (Ok(reset_type), Ok(reset_reason)) => system_reset(reset_type, reset_reason),
-            (_, _) => SbiRet::invalid_param(),
-        },
-        _ => SbiRet::not_supported(),
-    }
-}

+ 0 - 36
src/ecall/time.rs

@@ -1,36 +0,0 @@
-use sbi_spec::binary::SbiRet;
-
-#[cfg(target_pointer_width = "64")]
-#[inline]
-pub(super) fn handle_ecall(function: usize, param0: usize) -> SbiRet {
-    use crate::timer::*;
-    use sbi_spec::time::*;
-    match function {
-        SET_TIMER => {
-            if set_timer(param0 as _) {
-                SbiRet::success(0)
-            } else {
-                SbiRet::not_supported()
-            }
-        }
-        _ => SbiRet::not_supported(),
-    }
-}
-
-#[cfg(target_pointer_width = "32")]
-#[inline]
-pub(super) fn handle_ecall(function: usize, param0: usize, param1: usize) -> SbiRet {
-    use super::concat_u32;
-    use crate::timer::*;
-    use sbi_spec::time::*;
-    match function {
-        SET_TIMER => {
-            if set_timer(concat_u32(param1, param0)) {
-                SbiRet::success(0)
-            } else {
-                SbiRet::not_supported()
-            }
-        }
-        _ => SbiRet::not_supported(),
-    }
-}

+ 23 - 94
src/hart_mask.rs

@@ -1,15 +1,15 @@
 /// Hart mask structure reference
 #[derive(Debug, Clone)]
 pub struct HartMask {
-    inner: MaskInner,
+    inner: BitVector,
 }
 
 impl HartMask {
     /// Construct a hart mask from mask value and base hart id.
     #[inline]
-    pub fn from_mask_base(hart_mask: usize, hart_mask_base: usize) -> HartMask {
+    pub const fn from_mask_base(hart_mask: usize, hart_mask_base: usize) -> HartMask {
         HartMask {
-            inner: MaskInner::BitVector {
+            inner: BitVector {
                 hart_mask,
                 hart_mask_base,
             },
@@ -18,103 +18,32 @@ impl HartMask {
 
     /// Check if the `hart_id` is included in this hart mask structure.
     #[inline]
-    pub fn has_bit(&self, hart_id: usize) -> bool {
-        match self.inner {
-            MaskInner::BitVector {
-                hart_mask,
-                hart_mask_base,
-            } => {
-                if hart_mask_base == usize::MAX {
-                    // If `hart_mask_base` equals `usize::MAX`, that means `hart_mask` is ignored
-                    // and all available harts must be considered.
-                    return true;
-                }
-                let Some(idx) = hart_id.checked_sub(hart_mask_base) else {
-                    // hart_id < hart_mask_base, not in current mask range
-                    return false;
-                };
-                if idx >= usize::BITS as usize {
-                    // hart_idx >= hart_mask_base + XLEN, not in current mask range
-                    return false;
-                }
-                hart_mask & (1 << idx) != 0
-            }
-            #[cfg(feature = "legacy")]
-            MaskInner::Legacy { legacy_bit_vector } => {
-                slow_legacy_has_bit(legacy_bit_vector, hart_id)
-            }
+    pub const fn has_bit(&self, hart_id: usize) -> bool {
+        let BitVector {
+            hart_mask,
+            hart_mask_base,
+        } = self.inner;
+        if hart_mask_base == usize::MAX {
+            // If `hart_mask_base` equals `usize::MAX`, that means `hart_mask` is ignored
+            // and all available harts must be considered.
+            return true;
         }
-    }
-
-    /// *This is a legacy function; it should not be used in newer designs. If `vaddr` is invalid
-    /// from S level, it would result in machine level load access or load misaligned exception.*
-    ///
-    /// Construct a hart mask from legacy bit vector and number of harts in current platform.
-    #[cfg(feature = "legacy")]
-    #[inline]
-    pub(crate) unsafe fn legacy_from_addr(vaddr: usize) -> HartMask {
-        HartMask {
-            inner: MaskInner::Legacy {
-                legacy_bit_vector: vaddr as *const _,
-            },
+        let Some(idx) = hart_id.checked_sub(hart_mask_base) else {
+            // hart_id < hart_mask_base, not in current mask range
+            return false;
+        };
+        if idx >= usize::BITS as usize {
+            // hart_idx >= hart_mask_base + XLEN, not in current mask range
+            return false;
         }
+        hart_mask & (1 << idx) != 0
     }
 }
 
 #[derive(Debug, Clone)]
-enum MaskInner {
-    BitVector {
-        hart_mask: usize,
-        hart_mask_base: usize,
-    },
-    #[cfg(feature = "legacy")]
-    Legacy { legacy_bit_vector: *const usize },
-}
-
-// not #[inline] to speed up new version bit vector
-#[cfg(feature = "legacy")]
-fn slow_legacy_has_bit(legacy_bit_vector: *const usize, hart_id: usize) -> bool {
-    fn split_index_usize(index: usize) -> (usize, usize) {
-        let bits_in_usize = usize::BITS as usize;
-        (index / bits_in_usize, index % bits_in_usize)
-    }
-    let (i, j) = split_index_usize(hart_id);
-    let cur_vector = unsafe { get_vaddr_usize(legacy_bit_vector.add(i)) };
-    cur_vector & (1 << j) != 0
-}
-
-#[cfg(feature = "legacy")]
-#[inline]
-unsafe fn get_vaddr_usize(vaddr_ptr: *const usize) -> usize {
-    match () {
-        #[cfg(target_arch = "riscv32")]
-        () => {
-            let mut ans: usize;
-            core::arch::asm!("
-                li      {tmp}, (1 << 17)
-                csrrs   {tmp}, mstatus, {tmp}
-                lw      {ans}, 0({vmem})
-                csrw    mstatus, {tmp}
-            ", ans = lateout(reg) ans, vmem = in(reg) vaddr_ptr, tmp = out(reg) _);
-            ans
-        }
-        #[cfg(target_arch = "riscv64")]
-        () => {
-            let mut ans: usize;
-            core::arch::asm!("
-                li      {tmp}, (1 << 17)
-                csrrs   {tmp}, mstatus, {tmp}
-                ld      {ans}, 0({vmem})
-                csrw    mstatus, {tmp}
-            ", ans = lateout(reg) ans, vmem = in(reg) vaddr_ptr, tmp = out(reg) _);
-            ans
-        }
-        #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
-        () => {
-            drop(vaddr_ptr);
-            unimplemented!("not RISC-V instruction set architecture")
-        }
-    }
+struct BitVector {
+    hart_mask: usize,
+    hart_mask_base: usize,
 }
 
 #[cfg(test)]

+ 9 - 65
src/hsm.rs

@@ -58,12 +58,12 @@ pub trait Hsm: Send + Sync {
     /// - The `opaque` parameter is a `usize` value which will be set in the `a1` register when the hart starts executing at `start_addr`.
     ///
     /// *NOTE:* A single `usize` parameter is sufficient as `start_addr`,
-    /// because the hart will start execution in the supervisor-mode with MMU off,
-    /// hence the `start_addr` must be less than XLEN bits wide.
+    /// because the hart will start execution in supervisor-mode with the MMU off,
+    /// hence `start_addr` must be less than XLEN bits wide.
     ///
     /// # Behavior
     ///
-    /// The target hart jumps to supervisor mode at address specified by `start_addr` with following values in specific registers.
+    /// The target hart jumps to supervisor mode at address specified by `start_addr` with specific register values described as follows.
     ///
     /// | Register Name | Register Value
     /// |:--------------|:--------------
@@ -79,10 +79,10 @@ pub trait Hsm: Send + Sync {
     /// | Return code                   | Description
     /// |:------------------------------|:----------------------------------------------
     /// | `SbiRet::success()`           | Hart was previously in stopped state. It will start executing from `start_addr`.
-    /// | `SbiRet::invalid_address()`   | `start_addr` is not valid, possibly due to the following reasons: it is not a valid physical address, or the address is prohibited by PMP or H-extension G-stage to run in supervisor-mode.
+    /// | `SbiRet::invalid_address()`   | `start_addr` is not valid, possibly due to the following reasons: it is not a valid physical address, or executable access to the address is prohibited by a physical memory protection mechanism or H-extension G-stage for supervisor-mode.
     /// | `SbiRet::invalid_param()`     | `hartid` is not a valid hartid as corresponding hart cannot started in supervisor mode.
     /// | `SbiRet::already_available()` | The given hartid is already started.
-    /// | `SbiRet::failed()`            | The start request failed for unknown reasons.
+    /// | `SbiRet::failed()`            | The start request failed for unspecified or unknown other reasons.
     fn hart_start(&self, hartid: usize, start_addr: usize, opaque: usize) -> SbiRet;
     /// Request the SBI implementation to stop executing the calling hart in supervisor-mode
     /// and return its ownership to the SBI implementation.
@@ -174,8 +174,8 @@ pub trait Hsm: Send + Sync {
     /// suspend.
     ///
     /// *NOTE:* A single `usize` parameter is sufficient as `resume_addr`,
-    /// because the hart will resume execution in the supervisor-mode with MMU off,
-    /// hence the `resume_addr` must be less than XLEN bits wide.
+    /// because the hart will resume execution in supervisor-mode with the MMU off,
+    /// hence `resume_addr` must be less than XLEN bits wide.
     ///
     /// The `opaque` parameter is an XLEN-bit value which will be set in the `a1`
     /// register when the hart resumes exectution at `resume_addr` after a
@@ -190,8 +190,8 @@ pub trait Hsm: Send + Sync {
     /// | `SbiRet::success()`         | Hart has suspended and resumed back successfully from a retentive suspend state.
     /// | `SbiRet::invalid_param()`   | `suspend_type` is not valid.
     /// | `SbiRet::not_supported()`   | `suspend_type` is valid but not implemented.
-    /// | `SbiRet::invalid_address()` | `resume_addr` is not valid, possibly due to the following reasons: it is not a valid physical address, or the address is prohibited by PMP or H-extension G-stage to run in supervisor-mode.
-    /// | `SbiRet::failed()`          | The suspend request failed for unknown reasons.
+    /// | `SbiRet::invalid_address()` | `resume_addr` is not valid, possibly due to the following reasons: it is not a valid physical address, or executable access to the address is prohibited by a physical memory protection mechanism or H-extension G-stage for supervisor-mode.
+    /// | `SbiRet::failed()`          | The suspend request failed for unspecified or unknown other reasons.
     fn hart_suspend(&self, suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
         let _ = (suspend_type, resume_addr, opaque);
         SbiRet::not_supported()
@@ -216,59 +216,3 @@ impl<T: Hsm> Hsm for &T {
         T::hart_suspend(self, suspend_type, resume_addr, opaque)
     }
 }
-
-#[cfg(feature = "singleton")]
-use crate::util::AmoOnceRef;
-
-#[cfg(feature = "singleton")]
-static HSM: AmoOnceRef<dyn Hsm> = AmoOnceRef::new();
-
-#[cfg(feature = "singleton")]
-/// Init HSM module
-pub fn init_hsm(hsm: &'static dyn Hsm) {
-    if !HSM.try_call_once(hsm) {
-        panic!("load sbi module when already loaded")
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn probe_hsm() -> bool {
-    HSM.get().is_some()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
-    if let Some(obj) = HSM.get() {
-        return obj.hart_start(hartid, start_addr, opaque);
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn hart_stop() -> SbiRet {
-    if let Some(obj) = HSM.get() {
-        return obj.hart_stop();
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn hart_get_status(hartid: usize) -> SbiRet {
-    if let Some(obj) = HSM.get() {
-        return obj.hart_get_status(hartid);
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn hart_suspend(suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
-    if let Some(obj) = HSM.get() {
-        return obj.hart_suspend(suspend_type, resume_addr, opaque);
-    }
-    SbiRet::not_supported()
-}

+ 239 - 472
src/instance.rs

@@ -1,9 +1,7 @@
 use crate::{
-    spec::binary::SbiRet, Fence, HartMask, Hsm, Ipi, Pmu, Reset, Timer, IMPL_ID_RUSTSBI,
-    RUSTSBI_VERSION, SBI_SPEC_MAJOR, SBI_SPEC_MINOR,
+    spec::binary::SbiRet, Console, Fence, HartMask, Hsm, Ipi, Physical, Pmu, Reset, Timer,
+    IMPL_ID_RUSTSBI, RUSTSBI_VERSION, SBI_SPEC_MAJOR, SBI_SPEC_MINOR,
 };
-#[cfg(feature = "sbi_2_0")]
-use crate::{Console, Physical};
 use core::convert::Infallible;
 #[cfg(feature = "machine")]
 use riscv::register::{marchid, mimpid, mvendorid};
@@ -13,14 +11,13 @@ use riscv::register::{marchid, mimpid, mvendorid};
 /// By now RustSBI supports to run instance based interface on systems has environment pointer width
 /// that is the same as supervisor pointer width.
 #[derive(Clone, Debug)]
-pub struct RustSBI<T, I, R, H, S, P, #[cfg(feature = "sbi_2_0")] C> {
+pub struct RustSBI<T, I, R, H, S, P, C> {
     timer: Option<T>,
     ipi: Option<I>,
     rfnc: Option<R>,
     hsm: Option<H>,
     srst: Option<S>,
     pmu: Option<P>,
-    #[cfg(feature = "sbi_2_0")]
     dbcn: Option<C>,
     #[cfg(not(feature = "machine"))]
     info: MachineInfo,
@@ -40,310 +37,6 @@ pub struct MachineInfo {
     pub mimpid: usize,
 }
 
-// FIXME: on RustSBI 0.4.0, Console extension will be officially supported without a gate,
-// this macro can be moved back to impl RustSBI<...> code region at that time.
-macro_rules! impl_rustsbi {
-    () => {
-        /// Handle supervisor environment call with given parameters and return the `SbiRet` result.
-        #[inline]
-        #[rustfmt::skip] // fixme: remove in RustSBI 0.4.0
-        pub fn handle_ecall(
-            &mut self,
-            extension: usize,
-            function: usize,
-            param: [usize; 6],
-        ) -> SbiRet {
-            match extension {
-                spec::rfnc::EID_RFNC => {
-                    let Some(rfnc) = &mut self.rfnc else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1, param2, param3, param4] =
-                        [param[0], param[1], param[2], param[3], param[4]];
-                    let hart_mask = crate::HartMask::from_mask_base(param0, param1);
-                    match function {
-                        spec::rfnc::REMOTE_FENCE_I => rfnc.remote_fence_i(hart_mask),
-                        spec::rfnc::REMOTE_SFENCE_VMA => {
-                            rfnc.remote_sfence_vma(hart_mask, param2, param3)
-                        }
-                        spec::rfnc::REMOTE_SFENCE_VMA_ASID => {
-                            rfnc.remote_sfence_vma_asid(hart_mask, param2, param3, param4)
-                        }
-                        spec::rfnc::REMOTE_HFENCE_GVMA_VMID => {
-                            rfnc.remote_hfence_gvma_vmid(hart_mask, param2, param3, param4)
-                        }
-                        spec::rfnc::REMOTE_HFENCE_GVMA => {
-                            rfnc.remote_hfence_gvma(hart_mask, param2, param3)
-                        }
-                        spec::rfnc::REMOTE_HFENCE_VVMA_ASID => {
-                            rfnc.remote_hfence_vvma_asid(hart_mask, param2, param3, param4)
-                        }
-                        spec::rfnc::REMOTE_HFENCE_VVMA => {
-                            rfnc.remote_hfence_vvma(hart_mask, param2, param3)
-                        }
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-                spec::time::EID_TIME => match () {
-                    #[cfg(target_pointer_width = "64")]
-                    () => {
-                        let Some(timer) = &mut self.timer else {
-                            return SbiRet::not_supported();
-                        };
-                        let [param0] = [param[0]];
-                        match function {
-                            spec::time::SET_TIMER => {
-                                timer.set_timer(param0 as _);
-                                SbiRet::success(0)
-                            }
-                            _ => SbiRet::not_supported(),
-                        }
-                    }
-                    #[cfg(target_pointer_width = "32")]
-                    () => {
-                        let Some(timer) = &mut self.timer else {
-                            return SbiRet::not_supported();
-                        };
-                        let [param0, param1] = [param[0], param[1]];
-                        match function {
-                            spec::time::SET_TIMER => {
-                                timer.set_timer(concat_u32(param1, param0));
-                                SbiRet::success(0)
-                            }
-                            _ => SbiRet::not_supported(),
-                        }
-                    }
-                },
-                spec::spi::EID_SPI => {
-                    let Some(ipi) = &mut self.ipi else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1] = [param[0], param[1]];
-                    match function {
-                        spec::spi::SEND_IPI => {
-                            ipi.send_ipi(HartMask::from_mask_base(param0, param1))
-                        }
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-                spec::base::EID_BASE => {
-                    let [param0] = [param[0]];
-                    let value = match function {
-                        spec::base::GET_SBI_SPEC_VERSION => {
-                            (SBI_SPEC_MAJOR << 24) | (SBI_SPEC_MINOR)
-                        }
-                        spec::base::GET_SBI_IMPL_ID => IMPL_ID_RUSTSBI,
-                        spec::base::GET_SBI_IMPL_VERSION => RUSTSBI_VERSION,
-                        spec::base::PROBE_EXTENSION => {
-                            // only provides probes to standard extensions. If you have customized extensions to be probed,
-                            // run it even before this `handle_ecall` function.
-                            self.probe_extension(param0)
-                        }
-                        spec::base::GET_MVENDORID => match () {
-                            #[cfg(feature = "machine")]
-                            () => mvendorid::read().map(|r| r.bits()).unwrap_or(0),
-                            #[cfg(not(feature = "machine"))]
-                            () => self.info.mvendorid,
-                        },
-                        spec::base::GET_MARCHID => match () {
-                            #[cfg(feature = "machine")]
-                            () => marchid::read().map(|r| r.bits()).unwrap_or(0),
-                            #[cfg(not(feature = "machine"))]
-                            () => self.info.marchid,
-                        },
-                        spec::base::GET_MIMPID => match () {
-                            #[cfg(feature = "machine")]
-                            () => mimpid::read().map(|r| r.bits()).unwrap_or(0),
-                            #[cfg(not(feature = "machine"))]
-                            () => self.info.mimpid,
-                        },
-                        _ => return SbiRet::not_supported(),
-                    };
-                    SbiRet::success(value)
-                }
-                spec::hsm::EID_HSM => {
-                    let Some(hsm) = &mut self.hsm else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1, param2] = [param[0], param[1], param[2]];
-                    match function {
-                        spec::hsm::HART_START => hsm.hart_start(param0, param1, param2),
-                        spec::hsm::HART_STOP => hsm.hart_stop(),
-                        spec::hsm::HART_GET_STATUS => hsm.hart_get_status(param0),
-                        spec::hsm::HART_SUSPEND => {
-                            if let Ok(suspend_type) = u32::try_from(param0) {
-                                hsm.hart_suspend(suspend_type, param1, param2)
-                            } else {
-                                SbiRet::invalid_param()
-                            }
-                        }
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-                spec::srst::EID_SRST => {
-                    let Some(srst) = &mut self.srst else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1] = [param[0], param[1]];
-                    match function {
-                        spec::srst::SYSTEM_RESET => {
-                            match (u32::try_from(param0), u32::try_from(param1)) {
-                                (Ok(reset_type), Ok(reset_reason)) => {
-                                    srst.system_reset(reset_type, reset_reason)
-                                }
-                                (_, _) => SbiRet::invalid_param(),
-                            }
-                        }
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-                spec::pmu::EID_PMU => match () {
-                    #[cfg(target_pointer_width = "64")]
-                    () => {
-                        let Some(pmu) = &mut self.pmu else {
-                            return SbiRet::not_supported();
-                        };
-                        let [param0, param1, param2, param3, param4] =
-                            [param[0], param[1], param[2], param[3], param[4]];
-                        match function {
-                            spec::pmu::PMU_NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
-                            spec::pmu::PMU_COUNTER_GET_INFO => pmu.counter_get_info(param0),
-                            spec::pmu::PMU_COUNTER_CONFIG_MATCHING => pmu.counter_config_matching(
-                                param0,
-                                param1,
-                                param2,
-                                param3,
-                                param4 as _,
-                            ),
-                            spec::pmu::PMU_COUNTER_START => {
-                                pmu.counter_start(param0, param1, param2, param3 as _)
-                            }
-                            spec::pmu::PMU_COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
-                            spec::pmu::PMU_COUNTER_FW_READ => pmu.counter_fw_read(param0),
-                            #[cfg(feature = "sbi_2_0")]
-                            spec::pmu::PMU_COUNTER_FW_READ_HI => pmu.counter_fw_read(param0),
-                            _ => SbiRet::not_supported(),
-                        }
-                    }
-                    #[cfg(target_pointer_width = "32")]
-                    () => {
-                        let Some(pmu) = &mut self.pmu else {
-                            return SbiRet::not_supported();
-                        };
-                        let [param0, param1, param2, param3, param4, param5] =
-                            [param[0], param[1], param[2], param[3], param[4], param[5]];
-                        match function {
-                            spec::pmu::PMU_NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
-                            spec::pmu::PMU_COUNTER_GET_INFO => pmu.counter_get_info(param0),
-                            spec::pmu::PMU_COUNTER_CONFIG_MATCHING => pmu.counter_config_matching(
-                                param0,
-                                param1,
-                                param2,
-                                param3,
-                                concat_u32(param5, param4),
-                            ),
-                            spec::pmu::PMU_COUNTER_START => pmu.counter_start(
-                                param0,
-                                param1,
-                                param2,
-                                concat_u32(param4, param3),
-                            ),
-                            spec::pmu::PMU_COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
-                            spec::pmu::PMU_COUNTER_FW_READ => pmu.counter_fw_read(param0),
-                            #[cfg(feature = "sbi_2_0")]
-                            spec::pmu::PMU_COUNTER_FW_READ_HI => pmu.counter_fw_read(param0),
-                            _ => SbiRet::not_supported(),
-                        }
-                    }
-                },
-                #[cfg(feature = "sbi_2_0")]
-                spec::dbcn::EID_DBCN => {
-                    let Some(dbcn) = &mut self.dbcn else {
-                        return SbiRet::not_supported();
-                    };
-                    let [param0, param1, param2] = [param[0], param[1], param[2]];
-                    match function {
-                        spec::dbcn::CONSOLE_WRITE => {
-                            let bytes = Physical::new(param0, param1, param2);
-                            dbcn.write(bytes)
-                        }
-                        spec::dbcn::CONSOLE_READ => {
-                            let bytes = Physical::new(param0, param1, param2);
-                            dbcn.read(bytes)
-                        }
-                        spec::dbcn::CONSOLE_WRITE_BYTE => dbcn.write_byte((param0 & 0xFF) as u8),
-                        _ => SbiRet::not_supported(),
-                    }
-                }
-                _ => SbiRet::not_supported(),
-            }
-        }
-
-        #[inline]
-        fn probe_extension(&self, extension: usize) -> usize {
-            let ans = match extension {
-                spec::base::EID_BASE => true,
-                spec::time::EID_TIME => self.timer.is_some(),
-                spec::spi::EID_SPI => self.ipi.is_some(),
-                spec::rfnc::EID_RFNC => self.rfnc.is_some(),
-                spec::srst::EID_SRST => self.srst.is_some(),
-                spec::hsm::EID_HSM => self.hsm.is_some(),
-                spec::pmu::EID_PMU => self.pmu.is_some(),
-                #[cfg(feature = "sbi_2_0")]
-                spec::dbcn::EID_DBCN => self.dbcn.is_some(),
-                _ => false,
-            };
-            if ans {
-                spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
-            } else {
-                spec::base::UNAVAILABLE_EXTENSION
-            }
-        }
-    };
-}
-
-#[cfg(not(feature = "sbi_2_0"))]
-impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu> RustSBI<T, I, R, H, S, P> {
-    /// Create RustSBI instance on current machine environment for all the SBI extensions
-    #[cfg(feature = "machine")]
-    #[inline]
-    pub const fn new_machine(timer: T, ipi: I, rfnc: R, hsm: H, srst: S, pmu: P) -> Self {
-        Self {
-            timer: Some(timer),
-            ipi: Some(ipi),
-            rfnc: Some(rfnc),
-            hsm: Some(hsm),
-            srst: Some(srst),
-            pmu: Some(pmu),
-        }
-    }
-
-    /// Create RustSBI instance on given machine information for all the SBI extensions
-    #[cfg(not(feature = "machine"))]
-    #[inline]
-    pub const fn with_machine_info(
-        timer: T,
-        ipi: I,
-        rfnc: R,
-        hsm: H,
-        srst: S,
-        pmu: P,
-        info: MachineInfo,
-    ) -> Self {
-        Self {
-            timer: Some(timer),
-            ipi: Some(ipi),
-            rfnc: Some(rfnc),
-            hsm: Some(hsm),
-            srst: Some(srst),
-            pmu: Some(pmu),
-            info,
-        }
-    }
-    impl_rustsbi! {}
-}
-
-#[cfg(feature = "sbi_2_0")]
 impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
     RustSBI<T, I, R, H, S, P, C>
 {
@@ -364,6 +57,7 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
 
     /// Create RustSBI instance on given machine information for all the SBI extensions
     #[cfg(not(feature = "machine"))]
+    #[allow(clippy::too_many_arguments)] // fixme: is it possible to have a better design here?
     #[inline]
     pub const fn with_machine_info(
         timer: T,
@@ -386,7 +80,240 @@ impl<T: Timer, I: Ipi, R: Fence, H: Hsm, S: Reset, P: Pmu, C: Console>
             info,
         }
     }
-    impl_rustsbi! {}
+    /// Handle supervisor environment call with given parameters and return the `SbiRet` result.
+    #[inline]
+    pub fn handle_ecall(&mut self, extension: usize, function: usize, param: [usize; 6]) -> SbiRet {
+        match extension {
+            spec::rfnc::EID_RFNC => {
+                let Some(rfnc) = &mut self.rfnc else {
+                    return SbiRet::not_supported();
+                };
+                let [param0, param1, param2, param3, param4] =
+                    [param[0], param[1], param[2], param[3], param[4]];
+                let hart_mask = crate::HartMask::from_mask_base(param0, param1);
+                match function {
+                    spec::rfnc::REMOTE_FENCE_I => rfnc.remote_fence_i(hart_mask),
+                    spec::rfnc::REMOTE_SFENCE_VMA => {
+                        rfnc.remote_sfence_vma(hart_mask, param2, param3)
+                    }
+                    spec::rfnc::REMOTE_SFENCE_VMA_ASID => {
+                        rfnc.remote_sfence_vma_asid(hart_mask, param2, param3, param4)
+                    }
+                    spec::rfnc::REMOTE_HFENCE_GVMA_VMID => {
+                        rfnc.remote_hfence_gvma_vmid(hart_mask, param2, param3, param4)
+                    }
+                    spec::rfnc::REMOTE_HFENCE_GVMA => {
+                        rfnc.remote_hfence_gvma(hart_mask, param2, param3)
+                    }
+                    spec::rfnc::REMOTE_HFENCE_VVMA_ASID => {
+                        rfnc.remote_hfence_vvma_asid(hart_mask, param2, param3, param4)
+                    }
+                    spec::rfnc::REMOTE_HFENCE_VVMA => {
+                        rfnc.remote_hfence_vvma(hart_mask, param2, param3)
+                    }
+                    _ => SbiRet::not_supported(),
+                }
+            }
+            spec::time::EID_TIME => match () {
+                #[cfg(target_pointer_width = "64")]
+                () => {
+                    let Some(timer) = &mut self.timer else {
+                        return SbiRet::not_supported();
+                    };
+                    let [param0] = [param[0]];
+                    match function {
+                        spec::time::SET_TIMER => {
+                            timer.set_timer(param0 as _);
+                            SbiRet::success(0)
+                        }
+                        _ => SbiRet::not_supported(),
+                    }
+                }
+                #[cfg(target_pointer_width = "32")]
+                () => {
+                    let Some(timer) = &mut self.timer else {
+                        return SbiRet::not_supported();
+                    };
+                    let [param0, param1] = [param[0], param[1]];
+                    match function {
+                        spec::time::SET_TIMER => {
+                            timer.set_timer(concat_u32(param1, param0));
+                            SbiRet::success(0)
+                        }
+                        _ => SbiRet::not_supported(),
+                    }
+                }
+            },
+            spec::spi::EID_SPI => {
+                let Some(ipi) = &mut self.ipi else {
+                    return SbiRet::not_supported();
+                };
+                let [param0, param1] = [param[0], param[1]];
+                match function {
+                    spec::spi::SEND_IPI => ipi.send_ipi(HartMask::from_mask_base(param0, param1)),
+                    _ => SbiRet::not_supported(),
+                }
+            }
+            spec::base::EID_BASE => {
+                let [param0] = [param[0]];
+                let value = match function {
+                    spec::base::GET_SBI_SPEC_VERSION => (SBI_SPEC_MAJOR << 24) | (SBI_SPEC_MINOR),
+                    spec::base::GET_SBI_IMPL_ID => IMPL_ID_RUSTSBI,
+                    spec::base::GET_SBI_IMPL_VERSION => RUSTSBI_VERSION,
+                    spec::base::PROBE_EXTENSION => {
+                        // only provides probes to standard extensions. If you have customized extensions to be probed,
+                        // run it even before this `handle_ecall` function.
+                        self.probe_extension(param0)
+                    }
+                    spec::base::GET_MVENDORID => match () {
+                        #[cfg(feature = "machine")]
+                        () => mvendorid::read().map(|r| r.bits()).unwrap_or(0),
+                        #[cfg(not(feature = "machine"))]
+                        () => self.info.mvendorid,
+                    },
+                    spec::base::GET_MARCHID => match () {
+                        #[cfg(feature = "machine")]
+                        () => marchid::read().map(|r| r.bits()).unwrap_or(0),
+                        #[cfg(not(feature = "machine"))]
+                        () => self.info.marchid,
+                    },
+                    spec::base::GET_MIMPID => match () {
+                        #[cfg(feature = "machine")]
+                        () => mimpid::read().map(|r| r.bits()).unwrap_or(0),
+                        #[cfg(not(feature = "machine"))]
+                        () => self.info.mimpid,
+                    },
+                    _ => return SbiRet::not_supported(),
+                };
+                SbiRet::success(value)
+            }
+            spec::hsm::EID_HSM => {
+                let Some(hsm) = &mut self.hsm else {
+                    return SbiRet::not_supported();
+                };
+                let [param0, param1, param2] = [param[0], param[1], param[2]];
+                match function {
+                    spec::hsm::HART_START => hsm.hart_start(param0, param1, param2),
+                    spec::hsm::HART_STOP => hsm.hart_stop(),
+                    spec::hsm::HART_GET_STATUS => hsm.hart_get_status(param0),
+                    spec::hsm::HART_SUSPEND => {
+                        if let Ok(suspend_type) = u32::try_from(param0) {
+                            hsm.hart_suspend(suspend_type, param1, param2)
+                        } else {
+                            SbiRet::invalid_param()
+                        }
+                    }
+                    _ => SbiRet::not_supported(),
+                }
+            }
+            spec::srst::EID_SRST => {
+                let Some(srst) = &mut self.srst else {
+                    return SbiRet::not_supported();
+                };
+                let [param0, param1] = [param[0], param[1]];
+                match function {
+                    spec::srst::SYSTEM_RESET => {
+                        match (u32::try_from(param0), u32::try_from(param1)) {
+                            (Ok(reset_type), Ok(reset_reason)) => {
+                                srst.system_reset(reset_type, reset_reason)
+                            }
+                            (_, _) => SbiRet::invalid_param(),
+                        }
+                    }
+                    _ => SbiRet::not_supported(),
+                }
+            }
+            spec::pmu::EID_PMU => match () {
+                #[cfg(target_pointer_width = "64")]
+                () => {
+                    let Some(pmu) = &mut self.pmu else {
+                        return SbiRet::not_supported();
+                    };
+                    let [param0, param1, param2, param3, param4] =
+                        [param[0], param[1], param[2], param[3], param[4]];
+                    match function {
+                        spec::pmu::PMU_NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
+                        spec::pmu::PMU_COUNTER_GET_INFO => pmu.counter_get_info(param0),
+                        spec::pmu::PMU_COUNTER_CONFIG_MATCHING => {
+                            pmu.counter_config_matching(param0, param1, param2, param3, param4 as _)
+                        }
+                        spec::pmu::PMU_COUNTER_START => {
+                            pmu.counter_start(param0, param1, param2, param3 as _)
+                        }
+                        spec::pmu::PMU_COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
+                        spec::pmu::PMU_COUNTER_FW_READ => pmu.counter_fw_read(param0),
+                        spec::pmu::PMU_COUNTER_FW_READ_HI => pmu.counter_fw_read(param0),
+                        _ => SbiRet::not_supported(),
+                    }
+                }
+                #[cfg(target_pointer_width = "32")]
+                () => {
+                    let Some(pmu) = &mut self.pmu else {
+                        return SbiRet::not_supported();
+                    };
+                    let [param0, param1, param2, param3, param4, param5] =
+                        [param[0], param[1], param[2], param[3], param[4], param[5]];
+                    match function {
+                        spec::pmu::PMU_NUM_COUNTERS => SbiRet::success(pmu.num_counters()),
+                        spec::pmu::PMU_COUNTER_GET_INFO => pmu.counter_get_info(param0),
+                        spec::pmu::PMU_COUNTER_CONFIG_MATCHING => pmu.counter_config_matching(
+                            param0,
+                            param1,
+                            param2,
+                            param3,
+                            concat_u32(param5, param4),
+                        ),
+                        spec::pmu::PMU_COUNTER_START => {
+                            pmu.counter_start(param0, param1, param2, concat_u32(param4, param3))
+                        }
+                        spec::pmu::PMU_COUNTER_STOP => pmu.counter_stop(param0, param1, param2),
+                        spec::pmu::PMU_COUNTER_FW_READ => pmu.counter_fw_read(param0),
+                        spec::pmu::PMU_COUNTER_FW_READ_HI => pmu.counter_fw_read(param0),
+                        _ => SbiRet::not_supported(),
+                    }
+                }
+            },
+            spec::dbcn::EID_DBCN => {
+                let Some(dbcn) = &mut self.dbcn else {
+                    return SbiRet::not_supported();
+                };
+                let [param0, param1, param2] = [param[0], param[1], param[2]];
+                match function {
+                    spec::dbcn::CONSOLE_WRITE => {
+                        let bytes = Physical::new(param0, param1, param2);
+                        dbcn.write(bytes)
+                    }
+                    spec::dbcn::CONSOLE_READ => {
+                        let bytes = Physical::new(param0, param1, param2);
+                        dbcn.read(bytes)
+                    }
+                    spec::dbcn::CONSOLE_WRITE_BYTE => dbcn.write_byte((param0 & 0xFF) as u8),
+                    _ => SbiRet::not_supported(),
+                }
+            }
+            _ => SbiRet::not_supported(),
+        }
+    }
+
+    #[inline]
+    fn probe_extension(&self, extension: usize) -> usize {
+        let ans = match extension {
+            spec::base::EID_BASE => true,
+            spec::time::EID_TIME => self.timer.is_some(),
+            spec::spi::EID_SPI => self.ipi.is_some(),
+            spec::rfnc::EID_RFNC => self.rfnc.is_some(),
+            spec::srst::EID_SRST => self.srst.is_some(),
+            spec::hsm::EID_HSM => self.hsm.is_some(),
+            spec::pmu::EID_PMU => self.pmu.is_some(),
+            spec::dbcn::EID_DBCN => self.dbcn.is_some(),
+            _ => false,
+        };
+        if ans {
+            spec::base::UNAVAILABLE_EXTENSION.wrapping_add(1)
+        } else {
+            spec::base::UNAVAILABLE_EXTENSION
+        }
+    }
 }
 
 #[cfg(target_pointer_width = "32")]
@@ -396,54 +323,10 @@ const fn concat_u32(h: usize, l: usize) -> u64 {
 }
 
 /// Structure to build a RustSBI instance
-// FIXME: remove #[cfg(feature = "sbi_2_0")] once RustSBI hits 0.4.0
-pub struct Builder<T, I, R, H, S, P, #[cfg(feature = "sbi_2_0")] C> {
-    #[cfg(not(feature = "sbi_2_0"))]
-    inner: RustSBI<T, I, R, H, S, P>,
-    #[cfg(feature = "sbi_2_0")]
+pub struct Builder<T, I, R, H, S, P, C> {
     inner: RustSBI<T, I, R, H, S, P, C>,
 }
 
-#[cfg(not(feature = "sbi_2_0"))]
-impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infallible> {
-    /// Create a new `Builder` from current machine environment
-    #[inline]
-    #[cfg(feature = "machine")]
-    pub const fn new_machine(
-    ) -> Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infallible> {
-        Builder {
-            inner: RustSBI {
-                timer: None,
-                ipi: None,
-                rfnc: None,
-                hsm: None,
-                srst: None,
-                pmu: None,
-            },
-        }
-    }
-
-    /// Create a new `Builder` from machine information
-    #[inline]
-    #[cfg(not(feature = "machine"))]
-    pub const fn with_machine_info(
-        info: MachineInfo,
-    ) -> Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infallible> {
-        Builder {
-            inner: RustSBI {
-                timer: None,
-                ipi: None,
-                rfnc: None,
-                hsm: None,
-                srst: None,
-                pmu: None,
-                info,
-            },
-        }
-    }
-}
-
-#[cfg(feature = "sbi_2_0")]
 impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infallible, Infallible> {
     /// Create a new `Builder` from current machine environment
     #[inline]
@@ -492,119 +375,6 @@ impl Builder<Infallible, Infallible, Infallible, Infallible, Infallible, Infalli
 
 // fixme: struct `Infallible` should be replaced to never type once it's stablized
 
-// fixme: remove feature sbi_2_0 once RustSBI hits 0.4.0
-#[cfg(not(feature = "sbi_2_0"))]
-impl<T, I, R, H, S, P> Builder<T, I, R, H, S, P> {
-    /// Add Timer programmer extension to RustSBI
-    #[inline]
-    pub fn with_timer<T2: Timer>(self, timer: T2) -> Builder<T2, I, R, H, S, P> {
-        Builder {
-            inner: RustSBI {
-                timer: Some(timer),
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add Inter-processor Interrupt extension to RustSBI
-    #[inline]
-    pub fn with_ipi<I2: Ipi>(self, ipi: I2) -> Builder<T, I2, R, H, S, P> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: Some(ipi),
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add Remote Fence extension to RustSBI
-    #[inline]
-    pub fn with_fence<R2: Fence>(self, fence: R2) -> Builder<T, I, R2, H, S, P> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: Some(fence),
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add Hart State Monitor extension to RustSBI
-    #[inline]
-    pub fn with_hsm<H2: Hsm>(self, hsm: H2) -> Builder<T, I, R, H2, S, P> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: Some(hsm),
-                srst: self.inner.srst,
-                pmu: self.inner.pmu,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add System Reset extension to RustSBI
-    #[inline]
-    pub fn with_reset<S2: Reset>(self, reset: S2) -> Builder<T, I, R, H, S2, P> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: Some(reset),
-                pmu: self.inner.pmu,
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Add Performance Monitor Unit extension to RustSBI
-    #[inline]
-    pub fn with_pmu<P2: Pmu>(self, pmu: P2) -> Builder<T, I, R, H, S, P2> {
-        Builder {
-            inner: RustSBI {
-                timer: self.inner.timer,
-                ipi: self.inner.ipi,
-                rfnc: self.inner.rfnc,
-                hsm: self.inner.hsm,
-                srst: self.inner.srst,
-                pmu: Some(pmu),
-                #[cfg(not(feature = "machine"))]
-                info: self.inner.info,
-            },
-        }
-    }
-
-    /// Build the target RustSBI instance
-    #[inline]
-    pub fn build(self) -> RustSBI<T, I, R, H, S, P> {
-        self.inner
-    }
-}
-
-#[cfg(feature = "sbi_2_0")]
 impl<T, I, R, H, S, P, C> Builder<T, I, R, H, S, P, C> {
     /// Add Timer programmer extension to RustSBI
     #[inline]
@@ -809,7 +579,6 @@ impl Reset for Infallible {
     fn system_reset(&self, _: u32, _: u32) -> SbiRet {
         unreachable!()
     }
-    // no leagcy_reset here, instance based interface is not compatible to legacy extension
 }
 
 impl Pmu for Infallible {
@@ -837,13 +606,11 @@ impl Pmu for Infallible {
         unreachable!()
     }
 
-    #[cfg(feature = "sbi_2_0")]
     fn counter_fw_read_hi(&self, _: usize) -> SbiRet {
         unreachable!()
     }
 }
 
-#[cfg(feature = "sbi_2_0")]
 impl Console for Infallible {
     fn write(&self, _: crate::Physical<&[u8]>) -> SbiRet {
         unreachable!()

+ 0 - 29
src/ipi.rs

@@ -1,9 +1,6 @@
 use crate::hart_mask::HartMask;
 use sbi_spec::binary::SbiRet;
 
-#[cfg(feature = "singleton")]
-use crate::util::AmoOnceRef;
-
 /// Inter-processor interrupt support
 pub trait Ipi: Send + Sync {
     /// Send an inter-processor interrupt to all the harts defined in `hart_mask`.
@@ -22,29 +19,3 @@ impl<T: Ipi> Ipi for &T {
         T::send_ipi(self, hart_mask)
     }
 }
-
-#[cfg(feature = "singleton")]
-static IPI: AmoOnceRef<dyn Ipi> = AmoOnceRef::new();
-
-/// Init singleton IPI module
-#[cfg(feature = "singleton")]
-pub fn init_ipi(ipi: &'static dyn Ipi) {
-    if !IPI.try_call_once(ipi) {
-        panic!("load sbi module when already loaded")
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn probe_ipi() -> bool {
-    IPI.get().is_some()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn send_ipi(hart_mask: HartMask) -> SbiRet {
-    if let Some(ipi) = IPI.get() {
-        return ipi.send_ipi(hart_mask);
-    }
-    SbiRet::not_supported()
-}

+ 0 - 93
src/legacy_stdio.rs

@@ -1,93 +0,0 @@
-//! 这个模块的两个宏应该公开
-//! 如果制造实例的时候,给定了stdout,那么就会打印到这个stdout里面
-use crate::util::AmoOnceRef;
-use core::fmt;
-
-/// Legacy standard input/output
-pub trait LegacyStdio: Send + Sync {
-    /// Get a character from legacy stdin
-    fn getchar(&self) -> u8;
-    /// Put a character into legacy stdout
-    fn putchar(&self, ch: u8);
-
-    fn write_str(&self, s: &str) {
-        for byte in s.as_bytes() {
-            self.putchar(*byte)
-        }
-    }
-}
-
-static LEGACY_STDIO: AmoOnceRef<dyn LegacyStdio> = AmoOnceRef::new();
-
-#[inline]
-pub fn init_legacy_stdio(stdio: &'static dyn LegacyStdio) {
-    if !LEGACY_STDIO.try_call_once(stdio) {
-        panic!("load sbi module when already loaded")
-    }
-}
-
-#[inline]
-pub fn legacy_stdio_putchar(ch: u8) {
-    if let Some(stdio) = LEGACY_STDIO.get() {
-        stdio.putchar(ch)
-    }
-}
-
-#[inline]
-pub fn legacy_stdio_getchar() -> usize {
-    if let Some(stdio) = LEGACY_STDIO.get() {
-        stdio.getchar() as usize
-    } else {
-        // According to RISC-V SBI spec 0.3.1-rc1, Section 4.3, this function returns -1
-        // when fails to read from debug console. Thank you @duskmoon314
-        usize::from_ne_bytes(isize::to_ne_bytes(-1))
-    }
-}
-
-#[inline]
-pub(crate) fn probe_legacy_stdio() -> bool {
-    LEGACY_STDIO.get().is_some()
-}
-
-struct Stdout;
-
-impl fmt::Write for Stdout {
-    #[inline]
-    fn write_str(&mut self, s: &str) -> fmt::Result {
-        if let Some(stdio) = LEGACY_STDIO.get() {
-            stdio.write_str(s);
-        }
-        Ok(())
-    }
-}
-
-#[inline]
-#[doc(hidden)]
-pub fn _print(args: fmt::Arguments) {
-    use fmt::Write;
-    Stdout.write_fmt(args).unwrap();
-}
-
-/// Prints to the legacy debug console.
-///
-/// This is only supported when there exists legacy extension;
-/// otherwise platform caller should use an early kernel input/output device
-/// declared in platform specific hardware.
-#[macro_export]
-macro_rules! print {
-    ($($arg:tt)*) => ($crate::legacy_stdio::_print(core::format_args!($($arg)*)));
-}
-
-/// Prints to the legacy debug console, with a newline.
-///
-/// This is only supported when there exists legacy extension;
-/// otherwise platform caller should use an early kernel input/output device
-/// declared in platform specific hardware.
-#[macro_export]
-macro_rules! println {
-    () => ($crate::print!("\r\n"));
-    ($($arg:tt)*) => {
-        $crate::legacy_stdio::_print(core::format_args!($($arg)*));
-        $crate::print!("\r\n");
-    }
-}

+ 10 - 78
src/lib.rs

@@ -148,9 +148,13 @@
 //!
 //! ```toml
 //! [dependencies]
-//! rustsbi = "0.3.1"
+//! rustsbi = { version = "0.4.0", features = ["machine"] }
 //! ```
 //!
+//! The feature `machine` indicates that RustSBI library is run directly on machine mode RISC-V
+//! environment; it will use `riscv` crate to fetch machine mode environment, which fits our demand
+//! of using it on bare metal RISC-V.
+//!
 //! 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
@@ -162,7 +166,7 @@
 //! # struct MyPlatHsm;
 //! # struct MyBoardPower;
 //! # struct MyPlatPmu;
-//! # #[cfg(not(feature = "legacy"))]
+//! # struct MyPlatDbcn;
 //! use rustsbi::RustSBI;
 //!
 //! # struct SupervisorContext;
@@ -170,9 +174,7 @@
 //! struct Executor {
 //!     ctx: SupervisorContext,
 //!     /* other environment variables ... */
-//! # #[cfg(not(feature = "legacy"))]
-//! # #[cfg(not(feature = "sbi_2_0"))] // fixme: remove in 0.4.0
-//!     sbi: RustSBI<Clint, Clint, MyPlatRfnc, MyPlatHsm, MyBoardPower, MyPlatPmu>,
+//!     sbi: RustSBI<Clint, Clint, MyPlatRfnc, MyPlatHsm, MyBoardPower, MyPlatPmu, MyPlatDbcn>,
 //!     /* custom_1: CustomSBI<...> */
 //! }
 //!
@@ -345,26 +347,6 @@
 //! train memory for later stages. In such situation, RustSBI implementation should be linked or concated
 //! to the second stage bootloader, and the first stage could be a standalone binary package bundled with it.
 //!
-//! ## Discrete RustSBI package by singleton based interface
-//!
-//! *Note: Using singleton based RustSBI interface is discouraged in newer designs, for it requires
-//! nightly Rust and global static memory. It takes extra bss and data storage to build a global singleton
-//! interface. New designs should follow the [instance based interface](#discrete-rustsbi-package-on-bare-metal-risc-v-hardware)
-//! to build discrete RustSBI packages.*
-//!
-//! 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.1", 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.
@@ -385,11 +367,11 @@
 //! environment they reside in, they may fill in custom one into `MachineInfo` structures.
 //! When creating RustSBI instance, `MachineInfo` structure is required as an input of constructor.
 //!
-//! To begin with, disable default features in file `Cargo.toml`:
+//! To begin with, include RustSBI library in file `Cargo.toml`:
 //!
 //! ```toml
 //! [dependencies]
-//! rustsbi = { version = "0.3.1", default-features = false }
+//! rustsbi = "0.4.0"
 //! ```
 //!
 //! This will disable default feature `machine` which will assume that RustSBI runs on M-mode directly,
@@ -517,43 +499,12 @@
 //! 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 deprecated legacy RISC-V
-//! SBI standard. It's disabled by default and it's not suggested to include legacy extensions in newer
-//! firmware designs. Extensions other than legacy console are replaced by individual extensions in SBI.
-//! Kernels are not suggested to use legacy extensions in practice.
-//! If you are a kernel developer, newer designs should consider relying on each SBI extension other than
-//! legacy extensions.*
-//!
-//! The SBI includes legacy extension which dated back to SBI 0.1 specification. Most of its features
-//! are replaced by individual SBI extensions, thus the entire legacy extension is deprecated by
-//! SBI version 0.2. However, some users may find out SBI 0.1 legacy console useful in some situations
-//! even if it's deprecated.
-//!
-//! RustSBI keeps SBI 0.1 legacy support under feature gate `legacy`. To use RustSBI with deprecated
-//! legacy feature, you may change dependency configuration to:
-//!
-//! ```toml
-//! [dependencies]
-//! rustsbi = { version = "0.3.1", features = ["legacy"] }
-//! ```
 
 #![no_std]
-#![cfg_attr(feature = "singleton", feature(ptr_metadata))]
 
-#[cfg(feature = "legacy")]
-#[doc(hidden)]
-#[macro_use]
-pub mod legacy_stdio;
-mod base;
 mod console;
-#[cfg(feature = "singleton")]
-mod ecall;
 mod hart_mask;
 mod hsm;
-#[cfg(not(feature = "legacy"))]
 mod instance;
 mod ipi;
 mod memory_range;
@@ -561,8 +512,6 @@ mod pmu;
 mod reset;
 mod rfence;
 mod timer;
-#[cfg(feature = "singleton")]
-mod util;
 
 /// The RustSBI logo without blank lines on the beginning
 pub const LOGO: &str = r".______       __    __      _______.___________.  _______..______   __
@@ -573,12 +522,7 @@ pub const LOGO: &str = r".______       __    __      _______.___________.  _____
 | _| `._____| \______/ |_______/       |__|  |_______/    |______/ |__|";
 
 // RustSBI supports RISC-V SBI specification 2.0-rc1
-const SBI_SPEC_MAJOR: usize = match () {
-    #[cfg(feature = "sbi_2_0")]
-    () => 2,
-    #[cfg(not(feature = "sbi_2_0"))]
-    () => 1,
-};
+const SBI_SPEC_MAJOR: usize = 2;
 const SBI_SPEC_MINOR: usize = 0;
 
 /// RustSBI implementation ID: 4
@@ -597,16 +541,10 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
 
 pub extern crate sbi_spec as spec;
 pub use console::Console;
-#[cfg(feature = "singleton")]
-pub use ecall::handle_ecall as ecall;
 pub use hart_mask::HartMask;
 pub use hsm::Hsm;
-#[cfg(not(feature = "legacy"))]
 pub use instance::{Builder, RustSBI};
 pub use ipi::Ipi;
-#[cfg(feature = "legacy")]
-#[doc(hidden)]
-pub use legacy_stdio::{legacy_stdio_getchar, legacy_stdio_putchar};
 pub use memory_range::Physical;
 pub use pmu::Pmu;
 pub use reset::Reset;
@@ -615,9 +553,3 @@ pub use timer::Timer;
 
 #[cfg(not(feature = "machine"))]
 pub use instance::MachineInfo;
-
-#[cfg(feature = "singleton")]
-pub use {
-    hsm::init_hsm, ipi::init_ipi, pmu::init_pmu, reset::init_reset,
-    rfence::init_rfence as init_remote_fence, timer::init_timer,
-};

+ 2 - 115
src/pmu.rs

@@ -209,17 +209,16 @@ pub trait Pmu: Send + Sync {
     /// |:--------------------------|:----------------------------------------------
     /// | `SbiRet::success()`       | firmware counter read successfully.
     /// | `SbiRet::invalid_param()` | `counter_idx` points to a hardware counter or an invalid counter.
-    #[cfg(feature = "sbi_2_0")]
     fn counter_fw_read_hi(&self, counter_idx: usize) -> SbiRet {
         match () {
             #[cfg(not(target_pointer_width = "32"))]
             () => {
-                drop(counter_idx);
+                let _ = counter_idx;
                 SbiRet::success(0)
             }
             #[cfg(target_pointer_width = "32")]
             () => {
-                drop(counter_idx);
+                let _ = counter_idx;
                 SbiRet::not_supported()
             }
         }
@@ -282,120 +281,8 @@ impl<T: Pmu> Pmu for &T {
     fn counter_fw_read(&self, counter_idx: usize) -> SbiRet {
         T::counter_fw_read(self, counter_idx)
     }
-    #[cfg(feature = "sbi_2_0")]
     #[inline]
     fn counter_fw_read_hi(&self, counter_idx: usize) -> SbiRet {
         T::counter_fw_read_hi(self, counter_idx)
     }
 }
-
-#[cfg(feature = "singleton")]
-use crate::util::AmoOnceRef;
-
-#[cfg(feature = "singleton")]
-static PMU: AmoOnceRef<dyn Pmu> = AmoOnceRef::new();
-
-#[cfg(feature = "singleton")]
-/// Init PMU module
-pub fn init_pmu(pmu: &'static dyn Pmu) {
-    if !PMU.try_call_once(pmu) {
-        panic!("load sbi module when already loaded")
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn probe_pmu() -> bool {
-    PMU.get().is_some()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn num_counters() -> SbiRet {
-    if let Some(obj) = PMU.get() {
-        // Returns the number of counters (both hardware and firmware) in sbiret.value
-        // and always returns `SbiRet::success()`.
-        return SbiRet::success(obj.num_counters());
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn counter_get_info(counter_idx: usize) -> SbiRet {
-    if let Some(obj) = PMU.get() {
-        return obj.counter_get_info(counter_idx);
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn counter_config_matching(
-    counter_idx_base: usize,
-    counter_idx_mask: usize,
-    config_flags: usize,
-    event_idx: usize,
-    event_data: u64,
-) -> SbiRet {
-    if let Some(obj) = PMU.get() {
-        return obj.counter_config_matching(
-            counter_idx_base,
-            counter_idx_mask,
-            config_flags,
-            event_idx,
-            event_data,
-        );
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn counter_start(
-    counter_idx_base: usize,
-    counter_idx_mask: usize,
-    start_flags: usize,
-    initial_value: u64,
-) -> SbiRet {
-    if let Some(obj) = PMU.get() {
-        return obj.counter_start(
-            counter_idx_base,
-            counter_idx_mask,
-            start_flags,
-            initial_value,
-        );
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn counter_stop(
-    counter_idx_base: usize,
-    counter_idx_mask: usize,
-    stop_flags: usize,
-) -> SbiRet {
-    if let Some(obj) = PMU.get() {
-        return obj.counter_stop(counter_idx_base, counter_idx_mask, stop_flags);
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn counter_fw_read(counter_idx: usize) -> SbiRet {
-    if let Some(obj) = PMU.get() {
-        return obj.counter_fw_read(counter_idx);
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(all(feature = "singleton", feature = "sbi_2_0"))]
-#[inline]
-pub(crate) fn counter_fw_read_hi(counter_idx: usize) -> SbiRet {
-    if let Some(obj) = PMU.get() {
-        return obj.counter_fw_read_hi(counter_idx);
-    }
-    SbiRet::not_supported()
-}

+ 0 - 54
src/reset.rs

@@ -37,17 +37,6 @@ pub trait Reset: Send + Sync {
     /// | `SbiRet::not_supported()` | `reset_type` is valid but not implemented.
     /// | `SbiRet::failed()`        | Reset request failed for unknown reasons.
     fn system_reset(&self, reset_type: u32, reset_reason: u32) -> SbiRet;
-
-    /// Legacy extension's reset function
-    ///
-    /// Puts all the harts to shut down state from supervisor point of view. This SBI call doesn’t return.
-    #[cfg(feature = "legacy")]
-    fn legacy_reset(&self) -> ! {
-        use sbi_spec::srst::{RESET_REASON_NO_REASON, RESET_TYPE_SHUTDOWN};
-        // By default, this function redirects to `system_reset`.
-        self.system_reset(RESET_TYPE_SHUTDOWN, RESET_REASON_NO_REASON);
-        unreachable!()
-    }
 }
 
 impl<T: Reset> Reset for &T {
@@ -55,47 +44,4 @@ impl<T: Reset> Reset for &T {
     fn system_reset(&self, reset_type: u32, reset_reason: u32) -> SbiRet {
         T::system_reset(self, reset_type, reset_reason)
     }
-    #[cfg(feature = "legacy")]
-    #[inline]
-    fn legacy_reset(&self) -> ! {
-        T::legacy_reset(self)
-    }
-}
-
-#[cfg(feature = "singleton")]
-use crate::util::AmoOnceRef;
-
-#[cfg(feature = "singleton")]
-static RESET: AmoOnceRef<dyn Reset> = AmoOnceRef::new();
-
-#[cfg(feature = "singleton")]
-/// Init SRST module
-pub fn init_reset(reset: &'static dyn Reset) {
-    if !RESET.try_call_once(reset) {
-        panic!("load sbi module when already loaded")
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn probe_reset() -> bool {
-    RESET.get().is_some()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn system_reset(reset_type: u32, reset_reason: u32) -> SbiRet {
-    if let Some(obj) = RESET.get() {
-        return obj.system_reset(reset_type, reset_reason);
-    }
-    SbiRet::not_supported()
-}
-
-#[cfg(all(feature = "singleton", feature = "legacy"))]
-#[inline]
-pub(crate) fn legacy_reset() -> ! {
-    if let Some(obj) = RESET.get() {
-        obj.legacy_reset()
-    }
-    unreachable!("no reset handler available; this is okay if your platform didn't declare a legacy reset handler")
 }

+ 0 - 105
src/rfence.rs

@@ -183,108 +183,3 @@ impl<T: Rfence> Rfence for &T {
         T::remote_hfence_vvma(self, hart_mask, start_addr, size)
     }
 }
-
-#[cfg(feature = "singleton")]
-use crate::util::AmoOnceRef;
-
-#[cfg(feature = "singleton")]
-static RFENCE: AmoOnceRef<dyn Rfence> = AmoOnceRef::new();
-
-#[cfg(feature = "singleton")]
-/// Init RFENCE module
-pub fn init_rfence(rfence: &'static dyn Rfence) {
-    if !RFENCE.try_call_once(rfence) {
-        panic!("load sbi module when already loaded")
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn probe_rfence() -> bool {
-    RFENCE.get().is_some()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn remote_fence_i(hart_mask: HartMask) -> SbiRet {
-    if let Some(rfence) = RFENCE.get() {
-        rfence.remote_fence_i(hart_mask)
-    } else {
-        SbiRet::not_supported()
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn remote_sfence_vma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
-    if let Some(rfence) = RFENCE.get() {
-        rfence.remote_sfence_vma(hart_mask, start_addr, size)
-    } else {
-        SbiRet::not_supported()
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn remote_sfence_vma_asid(
-    hart_mask: HartMask,
-    start_addr: usize,
-    size: usize,
-    asid: usize,
-) -> SbiRet {
-    if let Some(rfence) = RFENCE.get() {
-        rfence.remote_sfence_vma_asid(hart_mask, start_addr, size, asid)
-    } else {
-        SbiRet::not_supported()
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn remote_hfence_gvma_vmid(
-    hart_mask: HartMask,
-    start_addr: usize,
-    size: usize,
-    vmid: usize,
-) -> SbiRet {
-    if let Some(rfence) = RFENCE.get() {
-        rfence.remote_hfence_gvma_vmid(hart_mask, start_addr, size, vmid)
-    } else {
-        SbiRet::not_supported()
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn remote_hfence_gvma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
-    if let Some(rfence) = RFENCE.get() {
-        rfence.remote_hfence_gvma(hart_mask, start_addr, size)
-    } else {
-        SbiRet::not_supported()
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn remote_hfence_vvma_asid(
-    hart_mask: HartMask,
-    start_addr: usize,
-    size: usize,
-    asid: usize,
-) -> SbiRet {
-    if let Some(rfence) = RFENCE.get() {
-        rfence.remote_hfence_vvma_asid(hart_mask, start_addr, size, asid)
-    } else {
-        SbiRet::not_supported()
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn remote_hfence_vvma(hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
-    if let Some(rfence) = RFENCE.get() {
-        rfence.remote_hfence_vvma(hart_mask, start_addr, size)
-    } else {
-        SbiRet::not_supported()
-    }
-}

+ 0 - 31
src/timer.rs

@@ -1,6 +1,3 @@
-#[cfg(feature = "singleton")]
-use crate::util::AmoOnceRef;
-
 /// Timer programmer support
 pub trait Timer: Send + Sync {
     /// Programs the clock for next event after `stime_value` time.
@@ -19,31 +16,3 @@ impl<T: Timer> Timer for &T {
         T::set_timer(self, stime_value)
     }
 }
-
-#[cfg(feature = "singleton")]
-static TIMER: AmoOnceRef<dyn Timer> = AmoOnceRef::new();
-
-#[cfg(feature = "singleton")]
-/// Init TIMER module
-pub fn init_timer(timer: &'static dyn Timer) {
-    if !TIMER.try_call_once(timer) {
-        panic!("load sbi module when already loaded")
-    }
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn probe_timer() -> bool {
-    TIMER.get().is_some()
-}
-
-#[cfg(feature = "singleton")]
-#[inline]
-pub(crate) fn set_timer(time_value: u64) -> bool {
-    if let Some(timer) = TIMER.get() {
-        timer.set_timer(time_value);
-        true
-    } else {
-        false
-    }
-}

+ 0 - 123
src/util.rs

@@ -1,123 +0,0 @@
-//! useful structures
-
-use core::{arch::asm, cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, ptr::Pointee};
-
-/// 只使用 AMO 指令的一次初始化引用存储。
-pub struct AmoOnceRef<'a, T: ?Sized> {
-    /// As atomic bool, to check if it is the first time to set `ptr`.
-    lock: UnsafeCell<u32>,
-    ptr: UnsafeCell<*const ()>,
-    meta: UnsafeCell<MaybeUninit<<T as Pointee>::Metadata>>,
-    _lifetime: PhantomData<&'a ()>,
-}
-
-/// 如果 AmoOncePtr 保存的引用是静态的,自然可以随意移动。
-unsafe impl<T: ?Sized> Send for AmoOnceRef<'static, T> {}
-
-/// AmoOncePtr 不提供锁。
-unsafe impl<T: ?Sized + Sync> Sync for AmoOnceRef<'static, T> {}
-
-impl<'a, T: ?Sized> AmoOnceRef<'a, T> {
-    #[inline]
-    pub const fn new() -> Self {
-        Self {
-            lock: UnsafeCell::new(0),
-            ptr: UnsafeCell::new(core::ptr::null()),
-            meta: UnsafeCell::new(MaybeUninit::uninit()),
-            _lifetime: PhantomData,
-        }
-    }
-
-    pub fn try_call_once(&self, r#ref: &'a T) -> bool {
-        let ptr = r#ref as *const T;
-        let locked: u32;
-        unsafe {
-            asm!(
-                "
-                lw           {locked}, ({lock})
-                bnez         {locked}, 1f
-                amoswap.w.aq {locked}, {one}, ({lock})
-                1: ",
-                lock   =  in(reg) self.lock.get(),
-                one    =  in(reg) 1,
-                locked = out(reg) locked,
-            );
-        }
-        if locked == 0 {
-            // 取得锁,初始化对象
-            unsafe {
-                // amo rl 保证先初始化 meta 后设置指针
-                (*self.meta.get()) = MaybeUninit::new(core::ptr::metadata(ptr));
-                #[cfg(target_pointer_width = "32")]
-                asm!(
-                    "amoswap.w.rl zero, {src}, ({dst})",
-                    src = in(reg) ptr as *const (),
-                    dst = in(reg) self.ptr.get(),
-                );
-                #[cfg(target_pointer_width = "64")]
-                asm!(
-                    "amoswap.d.rl zero, {src}, ({dst})",
-                    src = in(reg) ptr as *const (),
-                    dst = in(reg) self.ptr.get(),
-                );
-            }
-
-            true
-        } else {
-            // 未取得锁,对象已被初始化过
-            false
-        }
-    }
-
-    #[allow(unused)]
-    pub fn call_once(&self, r#ref: &'static T) -> Result<&T, &T> {
-        if self.try_call_once(r#ref) {
-            Ok(r#ref)
-        } else {
-            Err(self.wait())
-        }
-    }
-
-    pub fn wait(&self) -> &T {
-        loop {
-            // 反复读直到非空。
-            let ptr = unsafe { *self.ptr.get() };
-            if !ptr.is_null() {
-                return unsafe { self.build_ref_unchecked(ptr) };
-            }
-        }
-    }
-
-    pub fn get(&self) -> Option<&T> {
-        let ptr: *const ();
-        unsafe {
-            // 先获取指针。如果指针非空,元数据一定存在。
-            // FIXME AMO 设的值是否一定对 LD 可见?如果确定就不需要 AMO 读了。
-            #[cfg(target_pointer_width = "32")]
-            asm!(" lw          {dst}, ({src})
-                   bnez        {dst}, 1f
-                   amoadd.w.aq {dst}, zero, ({src})
-                1: ",
-                src =  in(reg) self.ptr.get(),
-                dst = out(reg) ptr,
-            );
-            #[cfg(target_pointer_width = "64")]
-            asm!(" ld          {dst}, ({src})
-                   bnez        {dst}, 1f
-                   amoadd.d.aq {dst}, zero, ({src})
-                1: ",
-                src =  in(reg) self.ptr.get(),
-                dst = out(reg) ptr,
-            );
-        }
-        if !ptr.is_null() {
-            Some(unsafe { self.build_ref_unchecked(ptr) })
-        } else {
-            None
-        }
-    }
-
-    unsafe fn build_ref_unchecked(&self, ptr: *const ()) -> &T {
-        &*core::ptr::from_raw_parts(ptr, (*self.meta.get()).assume_init())
-    }
-}