Woshiluo Luo 1 mese fa
parent
commit
756705c3ff
75 ha cambiato i file con 8250 aggiunte e 0 eliminazioni
  1. 5 0
      prototyper/.cargo/config.toml
  2. 49 0
      prototyper/.github/workflows/workflow.yml
  3. 2 0
      prototyper/.gitignore
  4. 87 0
      prototyper/.pre-commit-config.yaml
  5. 6 0
      prototyper/CHANGELOG.md
  6. 788 0
      prototyper/Cargo.lock
  7. 11 0
      prototyper/Cargo.toml
  8. 7 0
      prototyper/LICENSE-MIT
  9. 131 0
      prototyper/LICENSE-MULAN
  10. 56 0
      prototyper/README.md
  11. 7 0
      prototyper/_typos.toml
  12. 28 0
      prototyper/bench-kernel/Cargo.toml
  13. 52 0
      prototyper/bench-kernel/build.rs
  14. 43 0
      prototyper/bench-kernel/scripts/rustsbi-bench-kernel.its
  15. 339 0
      prototyper/bench-kernel/src/main.rs
  16. 68 0
      prototyper/cliff.toml
  17. 33 0
      prototyper/docs/booting-archlinux-in-qemu-using-uboot-and-rustsbi.md
  18. 129 0
      prototyper/docs/booting-fedora-in-qemu-using-uboot-and-rustsbi.md
  19. 147 0
      prototyper/docs/booting-freebsd-in-qemu-using-uboot-and-rustsbi.md
  20. 499 0
      prototyper/docs/booting-linux-kernel-in-qemu-using-uboot-and-opensbi.md
  21. 343 0
      prototyper/docs/booting-linux-kernel-in-qemu-using-uboot-and-rustsbi.md
  22. 107 0
      prototyper/docs/booting-openEuler-23.09-in-qemu-using-uboot-and-rustsbi.md
  23. 149 0
      prototyper/docs/booting-openwrt-in-qemu-using-uboot-and-rustsbi.md
  24. 156 0
      prototyper/docs/booting-polyos-in-qemu-using-uboot-and-rustsbi.md
  25. 319 0
      prototyper/docs/booting-test-kernel-in-qemu-using-uboot-and-rustsbi.md
  26. 101 0
      prototyper/docs/booting-ubuntu-24.04.1-in-qemu-using-edk2-and-opensbi.md
  27. 107 0
      prototyper/docs/booting-ubuntu-24.04.1-in-qemu-using-uboot-and-rustsbi.md
  28. 27 0
      prototyper/docs/openwrt-patch.patch
  29. 40 0
      prototyper/prototyper/Cargo.toml
  30. 82 0
      prototyper/prototyper/build.rs
  31. 16 0
      prototyper/prototyper/src/cfg.rs
  32. 92 0
      prototyper/prototyper/src/devicetree.rs
  33. 125 0
      prototyper/prototyper/src/fail.rs
  34. 147 0
      prototyper/prototyper/src/firmware/dynamic.rs
  35. 20 0
      prototyper/prototyper/src/firmware/jump.rs
  36. 133 0
      prototyper/prototyper/src/firmware/mod.rs
  37. 31 0
      prototyper/prototyper/src/firmware/payload.rs
  38. 49 0
      prototyper/prototyper/src/macros.rs
  39. 210 0
      prototyper/prototyper/src/main.rs
  40. 122 0
      prototyper/prototyper/src/platform/clint.rs
  41. 98 0
      prototyper/prototyper/src/platform/console.rs
  42. 434 0
      prototyper/prototyper/src/platform/mod.rs
  43. 34 0
      prototyper/prototyper/src/platform/reset.rs
  44. 63 0
      prototyper/prototyper/src/riscv/csr.rs
  45. 7 0
      prototyper/prototyper/src/riscv/mod.rs
  46. 126 0
      prototyper/prototyper/src/sbi/console.rs
  47. 21 0
      prototyper/prototyper/src/sbi/early_trap.rs
  48. 129 0
      prototyper/prototyper/src/sbi/extensions.rs
  49. 70 0
      prototyper/prototyper/src/sbi/fifo.rs
  50. 47 0
      prototyper/prototyper/src/sbi/hart_context.rs
  51. 21 0
      prototyper/prototyper/src/sbi/heap.rs
  52. 254 0
      prototyper/prototyper/src/sbi/hsm.rs
  53. 292 0
      prototyper/prototyper/src/sbi/ipi.rs
  54. 55 0
      prototyper/prototyper/src/sbi/logger.rs
  55. 50 0
      prototyper/prototyper/src/sbi/mod.rs
  56. 60 0
      prototyper/prototyper/src/sbi/reset.rs
  57. 323 0
      prototyper/prototyper/src/sbi/rfence.rs
  58. 86 0
      prototyper/prototyper/src/sbi/trap/boot.rs
  59. 182 0
      prototyper/prototyper/src/sbi/trap/handler.rs
  60. 75 0
      prototyper/prototyper/src/sbi/trap/mod.rs
  61. 88 0
      prototyper/prototyper/src/sbi/trap_stack.rs
  62. 5 0
      prototyper/rust-toolchain.toml
  63. 26 0
      prototyper/test-kernel/Cargo.toml
  64. 52 0
      prototyper/test-kernel/build.rs
  65. 44 0
      prototyper/test-kernel/scripts/rustsbi-test-kernel.its
  66. 213 0
      prototyper/test-kernel/src/main.rs
  67. 11 0
      prototyper/xtask/Cargo.toml
  68. 79 0
      prototyper/xtask/src/bench.rs
  69. 55 0
      prototyper/xtask/src/logger.rs
  70. 56 0
      prototyper/xtask/src/main.rs
  71. 120 0
      prototyper/xtask/src/prototyper.rs
  72. 79 0
      prototyper/xtask/src/test.rs
  73. 105 0
      prototyper/xtask/src/utils/cargo.rs
  74. 14 0
      prototyper/xtask/src/utils/envs.rs
  75. 13 0
      prototyper/xtask/src/utils/mod.rs

+ 5 - 0
prototyper/.cargo/config.toml

@@ -0,0 +1,5 @@
+[alias]
+xtask = "run --package xtask --release --"
+prototyper = "xtask prototyper"
+test-kernel = "xtask test"
+bench-kernel = "xtask bench"

+ 49 - 0
prototyper/.github/workflows/workflow.yml

@@ -0,0 +1,49 @@
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+# rust-clippy is a tool that runs a bunch of lints to catch common
+# mistakes in your Rust code and help improve your Rust code.
+# More details at https://github.com/rust-lang/rust-clippy
+# and https://rust-lang.github.io/rust-clippy/
+
+name: CI
+
+on:
+  pull_request:
+  push:
+    paths-ignore:
+      - '**.md'
+      - 'LICENSE'
+
+jobs:
+  rust-clippy-analyze:
+    name: Run rust-clippy analyzing
+    runs-on: ubuntu-latest
+    permissions:
+      security-events: write
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Check format
+        run: cargo fmt --check
+
+      - name: Run test
+        run: cargo test
+
+      - name: Install required cargo
+        run: cargo install clippy-sarif sarif-fmt
+
+      - name: Run rust-clippy
+        run: |
+          cargo clippy -p rustsbi-prototyper --target riscv64imac-unknown-none-elf  --message-format=json  | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt
+          cargo clippy -p rustsbi-test-kernel --target riscv64imac-unknown-none-elf --message-format=json  | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt
+          cargo clippy -p rustsbi-bench-kernel --target riscv64imac-unknown-none-elf --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt
+        continue-on-error: true
+
+      - name: Upload analysis results to GitHub
+        uses: github/codeql-action/upload-sarif@v3
+        with:
+          sarif_file: rust-clippy-results.sarif
+          wait-for-processing: true

+ 2 - 0
prototyper/.gitignore

@@ -0,0 +1,2 @@
+/target
+.idea/*

+ 87 - 0
prototyper/.pre-commit-config.yaml

@@ -0,0 +1,87 @@
+fail_fast: false
+repos:
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v4.3.0
+    hooks:
+      - id: check-byte-order-marker
+      - id: check-case-conflict
+      - id: check-merge-conflict
+      - id: check-symlinks
+      - id: check-yaml
+      - id: end-of-file-fixer
+      - id: mixed-line-ending
+      - id: trailing-whitespace
+  - repo: https://github.com/psf/black
+    rev: 22.10.0
+    hooks:
+      - id: black
+  - repo: local
+    hooks:
+      - id: cargo-fmt
+        name: cargo fmt
+        description: Format files with rustfmt.
+        entry: bash -c 'cargo fmt -- --check'
+        language: rust
+        files: \.rs$
+        args: []
+      - id: typos
+        name: typos
+        description: check typo
+        entry: bash -c 'typos'
+        language: rust
+        files: \.*$
+        pass_filenames: false
+      - id: cargo-check
+        name: cargo check
+        description: Check the package for errors.
+        entry: |
+          bash -c '
+            # Get all packages in the workspace
+            packages=$(cargo metadata --format-version 1 | jq -r ".packages[] | select(.name != \"xtask\") | .name")
+
+            # Check each package
+            for package in $packages; do
+              echo "Checking package: $package"
+              cargo check -p "$package" --target riscv64imac-unknown-none-elf
+              check_status=$?
+
+              # If the check fails, exit with the error code
+              if [ "$check_status" -ne 0 ]; then
+                echo "Package $package check failed, exit status: $check_status!"
+                exit $check_status
+              fi
+            done
+
+            echo "All packages checked successfully."
+            exit 0
+          '
+        language: rust
+        files: \.rs$
+        pass_filenames: false
+      - id: cargo-clippy
+        name: cargo clippy
+        description: Lint Rust sources.
+        entry: |
+          bash -c '
+            # Get all packages in the workspace
+            packages=$(cargo metadata --format-version 1 | jq -r ".packages[] | select(.name != \"xtask\") | .name")
+
+            # Lint each package
+            for package in $packages; do
+              echo "Linting package: $package"
+              cargo clippy -p "$package" --target riscv64imac-unknown-none-elf -- -D warnings
+              clippy_status=$?
+
+              # If the linting fails, exit with the error code
+              if [ "$clippy_status" -ne 0 ]; then
+                echo "Package $package clippy check failed, exit status: $clippy_status!"
+                exit $clippy_status
+              fi
+            done
+
+            echo "All packages linted successfully."
+            exit 0
+          '
+        language: rust
+        files: \.rs$
+        pass_filenames: false

+ 6 - 0
prototyper/CHANGELOG.md

@@ -0,0 +1,6 @@
+# Changelog
+
+All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.
+
+---
+## [unreleased]

+ 788 - 0
prototyper/Cargo.lock

@@ -0,0 +1,788 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "aclint"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cc30f3f60fd3106787fa9b540e64372dd4793813c400ba12d113506e94dcb8c"
+
+[[package]]
+name = "anstream"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+dependencies = [
+ "anstyle",
+ "once_cell",
+ "windows-sys",
+]
+
+[[package]]
+name = "as-slice"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
+dependencies = [
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
+[[package]]
+name = "bitflags"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
+
+[[package]]
+name = "bouffalo-hal"
+version = "0.0.0"
+source = "git+https://github.com/rustsbi/bouffalo-hal?rev=968b949#968b949466adeb6773f7ca3c1052e3a400533ed9"
+dependencies = [
+ "as-slice",
+ "cfg-if",
+ "embedded-hal 0.2.7",
+ "embedded-hal 1.0.0",
+ "embedded-hal-nb",
+ "embedded-io",
+ "embedded-time",
+ "nb 1.1.0",
+ "volatile-register",
+]
+
+[[package]]
+name = "buddy_system_allocator"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a0108968a3a2dab95b089c0fc3f1afa7759aa5ebe6f1d86d206d6f7ba726eb"
+dependencies = [
+ "spin",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.5.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap-verbosity-flag"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2678fade3b77aa3a8ff3aae87e9c008d3fb00473a41c71fbf74e91c8c7b37e84"
+dependencies = [
+ "clap",
+ "log",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+
+[[package]]
+name = "critical-section"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+
+[[package]]
+name = "dtb-walker"
+version = "0.2.0-alpha.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9404d41caa1aa659f7be44d5a902e318c0672900822fe9ca41d9e38c14b52332"
+
+[[package]]
+name = "embedded-hal"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
+dependencies = [
+ "nb 0.1.3",
+ "void",
+]
+
+[[package]]
+name = "embedded-hal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
+
+[[package]]
+name = "embedded-hal-nb"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
+dependencies = [
+ "embedded-hal 1.0.0",
+ "nb 1.1.0",
+]
+
+[[package]]
+name = "embedded-io"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
+
+[[package]]
+name = "embedded-time"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7a4b4d10ac48d08bfe3db7688c402baadb244721f30a77ce360bd24c3dffe58"
+dependencies = [
+ "num",
+]
+
+[[package]]
+name = "fast-trap"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46da95e6fcc7619a12d05594693e48591c0b574aef6fe5d7a7e765e6763a2cb2"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "nb"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
+dependencies = [
+ "nb 1.1.0",
+]
+
+[[package]]
+name = "nb"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
+
+[[package]]
+name = "num"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
+dependencies = [
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
+
+[[package]]
+name = "panic-halt"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11"
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "plic"
+version = "0.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ad606bf31d67b0e10a161b7df7d6a97dda7be22ce4bebcff889476e867c9b7a"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rcore-console"
+version = "0.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63aae49a6d2e6fd69821507a979b5871e4c47dc3abc9066347fa5c4a51a73dd6"
+dependencies = [
+ "log",
+ "spin",
+]
+
+[[package]]
+name = "riscv"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9"
+dependencies = [
+ "critical-section",
+ "embedded-hal 1.0.0",
+]
+
+[[package]]
+name = "riscv"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7"
+dependencies = [
+ "critical-section",
+ "embedded-hal 1.0.0",
+ "paste",
+ "riscv-macros",
+ "riscv-pac",
+]
+
+[[package]]
+name = "riscv-decode"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf8b4cfb0da0528321d22daee4299a23a8c5ac8848623d716e898d2a9eec0694"
+
+[[package]]
+name = "riscv-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "riscv-pac"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436"
+
+[[package]]
+name = "rustsbi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44c13763120794ed11d64bac885fb31d384ae385c3287b0697711b97affbf8ab"
+dependencies = [
+ "riscv 0.11.1",
+ "rustsbi-macros",
+ "sbi-spec 0.0.7",
+]
+
+[[package]]
+name = "rustsbi-bench-kernel"
+version = "0.0.0"
+dependencies = [
+ "log",
+ "rcore-console",
+ "riscv 0.11.1",
+ "sbi-spec 0.0.8 (git+https://github.com/rustsbi/rustsbi?rev=4821073)",
+ "sbi-testing 0.0.3-alpha.2 (git+https://github.com/rustsbi/rustsbi?rev=4821073)",
+ "serde",
+ "serde-device-tree",
+ "spin",
+ "uart16550",
+]
+
+[[package]]
+name = "rustsbi-macros"
+version = "0.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a71347da9582cc6b6f3652c7d2c06516c9555690b3738ecdff7e84297f4e17fc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "rustsbi-prototyper"
+version = "0.0.0"
+dependencies = [
+ "aclint",
+ "bouffalo-hal",
+ "buddy_system_allocator",
+ "cfg-if",
+ "fast-trap",
+ "log",
+ "panic-halt",
+ "riscv 0.12.1",
+ "riscv-decode",
+ "rustsbi",
+ "sbi-spec 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde",
+ "serde-device-tree",
+ "sifive-test-device",
+ "spin",
+ "uart16550",
+ "uart_xilinx",
+ "xuantie-riscv",
+]
+
+[[package]]
+name = "rustsbi-test-kernel"
+version = "0.0.0"
+dependencies = [
+ "dtb-walker",
+ "log",
+ "rcore-console",
+ "riscv 0.11.1",
+ "sbi-testing 0.0.3-alpha.2 (git+https://github.com/rustsbi/rustsbi)",
+ "spin",
+ "uart16550",
+]
+
+[[package]]
+name = "sbi-rt"
+version = "0.0.3"
+source = "git+https://github.com/rustsbi/rustsbi?rev=4821073#4821073b56a7223781c11a49aba743785d89d3ea"
+dependencies = [
+ "sbi-spec 0.0.8 (git+https://github.com/rustsbi/rustsbi?rev=4821073)",
+]
+
+[[package]]
+name = "sbi-rt"
+version = "0.0.3"
+source = "git+https://github.com/rustsbi/rustsbi#99f4177fbed12c96c2c62121d51953b1bfa0ff43"
+dependencies = [
+ "sbi-spec 0.0.8 (git+https://github.com/rustsbi/rustsbi)",
+]
+
+[[package]]
+name = "sbi-spec"
+version = "0.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e36312fb5ddc10d08ecdc65187402baba4ac34585cb9d1b78522ae2358d890"
+
+[[package]]
+name = "sbi-spec"
+version = "0.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8309630ab2b300d4fe52b6757e53a7cbb6672f55aa08b50e28b1952c06dd994d"
+
+[[package]]
+name = "sbi-spec"
+version = "0.0.8"
+source = "git+https://github.com/rustsbi/rustsbi?rev=4821073#4821073b56a7223781c11a49aba743785d89d3ea"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "sbi-spec"
+version = "0.0.8"
+source = "git+https://github.com/rustsbi/rustsbi#99f4177fbed12c96c2c62121d51953b1bfa0ff43"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "sbi-testing"
+version = "0.0.3-alpha.2"
+source = "git+https://github.com/rustsbi/rustsbi?rev=4821073#4821073b56a7223781c11a49aba743785d89d3ea"
+dependencies = [
+ "log",
+ "riscv 0.12.1",
+ "sbi-rt 0.0.3 (git+https://github.com/rustsbi/rustsbi?rev=4821073)",
+ "sbi-spec 0.0.8 (git+https://github.com/rustsbi/rustsbi?rev=4821073)",
+]
+
+[[package]]
+name = "sbi-testing"
+version = "0.0.3-alpha.2"
+source = "git+https://github.com/rustsbi/rustsbi#99f4177fbed12c96c2c62121d51953b1bfa0ff43"
+dependencies = [
+ "log",
+ "riscv 0.12.1",
+ "sbi-rt 0.0.3 (git+https://github.com/rustsbi/rustsbi)",
+ "sbi-spec 0.0.8 (git+https://github.com/rustsbi/rustsbi)",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.217"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-device-tree"
+version = "0.0.1"
+source = "git+https://github.com/rustsbi/serde-device-tree#e7f9404fc07bc3f8cce4e7a833be6a0fff93b5c4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.217"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "sifive-test-device"
+version = "0.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba50a6fd7cb5cdb2645fb93fb2bbae7d8d78390677a889bdcfaf13c3d29286d0"
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "uart16550"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "939f6f9ccad815fe3efca8fd06f2ec1620c0387fb1bca2b231b61ce710bffb9b"
+
+[[package]]
+name = "uart_xilinx"
+version = "0.2.0"
+source = "git+https://github.com/duskmoon314/uart-rs/#12be91421ad140f2a4bf4179578fd7a8fbc7ff5c"
+dependencies = [
+ "bitflags",
+ "volatile-register",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "vcell"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "volatile-register"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
+dependencies = [
+ "vcell",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "xtask"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "clap-verbosity-flag",
+ "log",
+]
+
+[[package]]
+name = "xuantie-riscv"
+version = "0.0.0"
+source = "git+https://github.com/rustsbi/xuantie#7a521c0400dc7edb7a3ee103206dd8246c78d542"
+dependencies = [
+ "bit_field",
+ "bitflags",
+ "plic",
+ "volatile-register",
+]

+ 11 - 0
prototyper/Cargo.toml

@@ -0,0 +1,11 @@
+[workspace]
+resolver = "3"
+members = ["prototyper", "bench-kernel", "test-kernel", "xtask"]
+
+[workspace.package]
+edition = "2024"
+license = "MulanPSL-2.0 OR MIT"
+repository = "https://github.com/rustsbi/prototyper"
+
+[profile.release]
+debug = true

+ 7 - 0
prototyper/LICENSE-MIT

@@ -0,0 +1,7 @@
+Copyright (C) 2024 RustSBI Organization
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 131 - 0
prototyper/LICENSE-MULAN

@@ -0,0 +1,131 @@
+木兰宽松许可证, 第2版
+
+木兰宽松许可证, 第2版
+
+2020年1月 http://license.coscl.org.cn/MulanPSL2
+
+您对"软件"的复制、使用、修改及分发受木兰宽松许可证,第2版("本许可证")的如下条款的约束:
+
+    0. 定义
+
+    "软件" 是指由"贡献"构成的许可在"本许可证"下的程序和相关文档的集合。
+
+    "贡献" 是指由任一"贡献者"许可在"本许可证"下的受版权法保护的作品。
+
+    "贡献者" 是指将受版权法保护的作品许可在"本许可证"下的自然人或"法人实体"。
+
+    "法人实体" 是指提交贡献的机构及其"关联实体"。
+
+    "关联实体" 是指,对"本许可证"下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
+    1. 授予版权许可
+
+    每个"贡献者"根据"本许可证"授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其"贡献",不论修改与否。
+    2. 授予专利许可
+
+    每个"贡献者"根据"本许可证"授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其"贡献"或以其他方式转移其"贡献"。前述专利许可仅限于"贡献者"现在或将来拥有或控制的其"贡献"本身或其"贡献"与许可"贡献"时的"软件"结合而将必然会侵犯的专利权利要求,不包括对"贡献"的修改或包含"贡献"的其他结合。如果您或您的"关联实体"直接或间接地,就"软件"或其中的"贡献"对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则"本许可证"授予您对"软件"的专利许可自您提起诉讼或发起维权行动之日终止。
+    3. 无商标许可
+
+    "本许可证"不提供对"贡献者"的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
+    4. 分发限制
+
+    您可以在任何媒介中将"软件"以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供"本许可证"的副本,并保留"软件"中的版权、商标、专利及免责声明。
+    5. 免责声明与责任限制
+
+    "软件"及其中的"贡献"在提供时不带任何明示或默示的担保。在任何情况下,"贡献者"或版权所有者不对任何人因使用"软件"或其中的"贡献"而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
+    6. 语言
+
+    "本许可证"以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
+
+条款结束
+
+如何将木兰宽松许可证,第2版,应用到您的软件
+
+如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
+
+    1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
+    2, 请您在软件包的一级目录下创建以"LICENSE"为名的文件,将整个许可证文本放入该文件中;
+    3, 请将如下声明文本放入每个源文件的头部注释中。
+
+Copyright (c) 2024 RustSBI Organization
+
+RustSBI Prototyper is licensed under Mulan PSL v2.
+
+You can use this software according to the terms and conditions of the Mulan PSL v2.
+
+You may obtain a copy of Mulan PSL v2 at:
+
+http://license.coscl.org.cn/MulanPSL2
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+
+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+
+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+
+See the Mulan PSL v2 for more details.
+
+Mulan Permissive Software License,Version 2
+
+Mulan Permissive Software License,Version 2 (Mulan PSL v2)
+
+January 2020 http://license.coscl.org.cn/MulanPSL2
+
+Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
+
+    0. Definition
+
+    Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
+
+    Contribution means the copyrightable work licensed by a particular Contributor under this License.
+
+    Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
+
+    Legal Entity means the entity making a Contribution and all its Affiliates.
+
+    Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, 'control' means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
+    1. Grant of Copyright License
+
+    Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
+    2. Grant of Patent License
+
+    Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
+    3. No Trademark License
+
+    No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4.
+    4. Distribution Restriction
+
+    You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
+    5. Disclaimer of Warranty and Limitation of Liability
+
+    THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT'S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+    6. Language
+
+    THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
+
+END OF THE TERMS AND CONDITIONS
+
+How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software
+
+To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
+
+    i. Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
+    ii. Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package;
+    iii. Attach the statement to the appropriate annotated syntax at the beginning of each source file.
+
+Copyright (c) 2024 RustSBI Organization
+
+RustSBI Prototyper is licensed under Mulan PSL v2.
+
+You can use this software according to the terms and conditions of the Mulan PSL v2.
+
+You may obtain a copy of Mulan PSL v2 at:
+
+http://license.coscl.org.cn/MulanPSL2
+
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+
+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+
+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+
+See the Mulan PSL v2 for more details.

+ 56 - 0
prototyper/README.md

@@ -0,0 +1,56 @@
+# RustSBI Prototyper
+
+RustSBI Prototyper is a developing RISC-V Secure Bootloader solution. It can be integrated with the Rust or C language ecosystem to form a complete RISC-V bootloader ecosystem.
+
+## Setting Up the Development Environment
+
+### Packages to be installed
+
+```bash
+cargo install cargo-binutils
+sudo apt install u-boot-tools
+```
+
+
+### Optional Tools
+
+The following tools are not mandatory but can be useful for enhancing your development experience.
+
+#### Install pre-commit
+
+pre-commit is a tool that runs code checks before you commit your code.
+
+```bash
+pipx install pre-commit
+
+# After installation, run pre-commit install to set it up for your project.
+pre-commit install
+```
+
+#### Install Cargo Deny
+
+Cargo deny is a Cargo plugin used to check the security of your dependencies.
+
+```bash
+cargo install --locked cargo-deny
+```
+
+#### Install typos
+
+typos is a spell-checking tool.
+
+```bash
+cargo install typos-cli
+```
+
+#### Install git cliff
+
+git cliff is a tool for generating changelogs.
+
+```bash
+cargo install git-cliff
+```
+
+## License
+
+This project is dual-licensed under MIT or Mulan-PSL v2. See [LICENSE-MIT](./LICENSE-MIT) and [LICENSE-MULAN](./LICENSE-MULAN) for details.

+ 7 - 0
prototyper/_typos.toml

@@ -0,0 +1,7 @@
+[default.extend-words]
+rela = "rela"
+sie = "sie"
+stip = "stip"
+
+[files]
+extend-exclude = ["CHANGELOG.md"]

+ 28 - 0
prototyper/bench-kernel/Cargo.toml

@@ -0,0 +1,28 @@
+cargo-features = ["per-package-target"]
+
+[package]
+name = "rustsbi-bench-kernel"
+version = "0.0.0"
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+forced-target = "riscv64imac-unknown-none-elf"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+sbi-testing = { git = "https://github.com/rustsbi/rustsbi", rev = "4821073", features = ["log"] }
+sbi-spec = { git = "https://github.com/rustsbi/rustsbi", rev = "4821073" }
+serde-device-tree = { git = "https://github.com/rustsbi/serde-device-tree", default-features = false }
+serde = { version = "1.0.202", default-features = false, features = ["derive"] }
+log = "0.4"
+riscv = "0.11.1"
+spin = "0.9"
+uart16550 = "0.0.1"
+rcore-console = "0.0.0"
+
+[[bin]]
+name = "rustsbi-bench-kernel"
+test = false
+bench = false

+ 52 - 0
prototyper/bench-kernel/build.rs

@@ -0,0 +1,52 @@
+use std::{env, path::PathBuf};
+
+fn main() {
+    let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+    let ld = &out.join("rustsbi-test-kernel.ld");
+
+    std::fs::write(ld, LINKER_SCRIPT).unwrap();
+
+    println!("cargo:rustc-link-arg=-T{}", ld.display());
+    println!("cargo:rustc-link-search={}", out.display());
+}
+
+const LINKER_SCRIPT: &[u8] = b"OUTPUT_ARCH(riscv)
+ENTRY(_start) 
+SECTIONS {
+    . = 0x80200000;
+    istart = .;
+	  .head.text : ALIGN(8) {		
+        KEEP(*(.head.text))
+	  }
+
+    .text : ALIGN(8) { 
+        *(.text.entry)
+        *(.text .text.*)
+    }
+    .rodata : ALIGN(8) { 
+        srodata = .;
+        *(.rodata .rodata.*)
+        *(.srodata .srodata.*)
+        . = ALIGN(8);  
+        erodata = .;
+    } 
+    .data : ALIGN(8) { 
+        sdata = .;
+        *(.data .data.*)
+        *(.sdata .sdata.*)
+        . = ALIGN(8); 
+        edata = .;
+    }
+    sidata = LOADADDR(.data);
+    .bss (NOLOAD) : ALIGN(8) {  
+        *(.bss.uninit)
+        sbss = .;
+        *(.bss .bss.*)
+        *(.sbss .sbss.*)
+        ebss = .;
+    } 
+    iend = .;
+    /DISCARD/ : {
+        *(.eh_frame)
+    }
+}";

+ 43 - 0
prototyper/bench-kernel/scripts/rustsbi-bench-kernel.its

@@ -0,0 +1,43 @@
+/*
+ * Configuration to load RustSBI before RustSBI Bench Kernel
+ */
+
+/dts-v1/;
+
+/ {
+			description = "Configuration to load RustSBI before RustSBI Bench Kernel";
+
+			images {
+				kernel {
+					description = "rustsbi-bench-kernel";
+					data = /incbin/("./rustsbi-bench-kernel.bin");
+					type = "standalone";
+					os = "u-boot";
+					arch = "riscv";
+					compression = "none";
+					load = /bits/ 64 <0x80200000>;
+				};
+
+				rustsbi {
+					description = "RustSBI Firmware";
+					data = /incbin/("./rustsbi-prototyper.bin");
+					type = "firmware";
+					os = "opensbi";
+					arch = "riscv";
+					compression = "none";
+					load = /bits/ 64 <0x80100000>;
+					entry = /bits/ 64 <0x80100000>;
+				};
+
+			};
+
+		configurations {
+				default = "conf-1";
+
+				conf-1 {
+					description = "RustSBI & RustSBI Bench Kernel";
+					firmware = "rustsbi";
+					loadables = "kernel";
+				};
+		};
+};

+ 339 - 0
prototyper/bench-kernel/src/main.rs

@@ -0,0 +1,339 @@
+#![no_std]
+#![no_main]
+#![feature(naked_functions)]
+#![allow(static_mut_refs)]
+
+#[macro_use]
+extern crate rcore_console;
+
+use core::mem::MaybeUninit;
+use core::sync::{atomic::AtomicBool, atomic::AtomicU64, atomic::Ordering};
+use core::{
+    arch::{asm, naked_asm},
+    ptr::null,
+};
+use log::*;
+use sbi::SbiRet;
+use sbi_spec::binary::{HartMask, MaskError};
+use sbi_spec::hsm::hart_state;
+use sbi_testing::sbi;
+use serde::Deserialize;
+use serde_device_tree::{
+    Dtb, DtbPtr,
+    buildin::{Node, NodeSeq, Reg, StrSeq},
+};
+use uart16550::Uart16550;
+
+const RISCV_HEAD_FLAGS: u64 = 0;
+const RISCV_HEADER_VERSION: u32 = 0x2;
+const RISCV_IMAGE_MAGIC: u64 = 0x5643534952; /* Magic number, little endian, "RISCV" */
+const RISCV_IMAGE_MAGIC2: u32 = 0x05435352; /* Magic number 2, little endian, "RSC\x05" */
+
+/// boot header
+#[naked]
+#[unsafe(no_mangle)]
+#[unsafe(link_section = ".head.text")]
+unsafe extern "C" fn _boot_header() -> ! {
+    unsafe {
+        naked_asm!(
+            "j _start",
+            ".word 0",
+            ".balign 8",
+            ".dword 0x200000",
+            ".dword iend - istart",
+            ".dword {RISCV_HEAD_FLAGS}",
+            ".word  {RISCV_HEADER_VERSION}",
+            ".word  0",
+            ".dword 0",
+            ".dword {RISCV_IMAGE_MAGIC}",
+            ".balign 4",
+            ".word  {RISCV_IMAGE_MAGIC2}",
+            ".word  0",
+            RISCV_HEAD_FLAGS = const RISCV_HEAD_FLAGS,
+            RISCV_HEADER_VERSION = const RISCV_HEADER_VERSION,
+            RISCV_IMAGE_MAGIC = const RISCV_IMAGE_MAGIC,
+            RISCV_IMAGE_MAGIC2 = const RISCV_IMAGE_MAGIC2,
+        );
+    }
+}
+
+const STACK_SIZE: usize = 512 * 1024; // 512 KiB
+const MAX_HART_NUM: usize = 128;
+
+#[allow(dead_code)]
+#[derive(Copy, Clone)]
+struct HartStack([u8; STACK_SIZE]);
+
+impl HartStack {
+    #[inline]
+    pub const fn new() -> Self {
+        HartStack([0; STACK_SIZE])
+    }
+}
+
+#[unsafe(link_section = ".bss.uninit")]
+static mut STACK: HartStack = HartStack::new();
+#[unsafe(link_section = ".bss.uninit")]
+static mut HART_STACK: [HartStack; MAX_HART_NUM] = [HartStack::new(); MAX_HART_NUM];
+#[unsafe(link_section = ".bss.uninit")]
+static mut IPI_SENT: [MaybeUninit<AtomicBool>; MAX_HART_NUM] =
+    [const { MaybeUninit::uninit() }; MAX_HART_NUM];
+#[unsafe(link_section = ".bss.uninit")]
+static mut SMP_COUNT: usize = 0;
+#[unsafe(link_section = ".bss.uninit")]
+static mut BOOT_HART_ID: usize = 0;
+
+/// 内核入口。
+///
+/// # Safety
+///
+/// 裸函数。
+#[naked]
+#[unsafe(no_mangle)]
+#[unsafe(link_section = ".text.entry")]
+unsafe extern "C" fn _start(hartid: usize, device_tree_paddr: usize) -> ! {
+    unsafe {
+        naked_asm!(
+            // clear bss segment
+            "   la      t0, sbss
+            la      t1, ebss
+        1:  bgeu    t0, t1, 2f
+            sd      zero, 0(t0)
+            addi    t0, t0, 8
+            j       1b",
+            "2:",
+            "   la sp, {stack} + {stack_size}",
+            "   j  {main}",
+            stack_size = const STACK_SIZE,
+            stack      =   sym STACK,
+            main       =   sym rust_main,
+        )
+    }
+}
+
+#[naked]
+#[unsafe(no_mangle)]
+extern "C" fn init_hart(hartid: usize, opaque: usize) {
+    unsafe {
+        naked_asm!(
+            "add sp, a1, zero",
+            "csrw sscratch, sp",
+            "call {init_main}",
+            init_main = sym init_main,
+        )
+    }
+}
+
+#[naked]
+#[unsafe(no_mangle)]
+extern "C" fn core_send_ipi(hartid: usize, opaque: usize) {
+    unsafe {
+        naked_asm!(
+            "add sp, a1, zero",
+            "csrw sscratch, sp",
+            "call {send_ipi}",
+            send_ipi = sym send_ipi,
+        )
+    }
+}
+
+extern "C" fn send_ipi(hartid: usize) -> ! {
+    if unsafe { !(IPI_SENT[hartid].assume_init_mut().load(Ordering::Relaxed)) } {
+        unsafe {
+            IPI_SENT[hartid]
+                .assume_init_mut()
+                .swap(true, Ordering::AcqRel);
+        };
+        let mut mask = Some(HartMask::from_mask_base(0, 0));
+        for i in 0..unsafe { SMP_COUNT } {
+            if i == unsafe { BOOT_HART_ID } {
+                continue;
+            }
+            if let Some(ref mut mask) = mask {
+                match mask.insert(i) {
+                    Ok(_) => continue,
+                    Err(MaskError::InvalidBit) => {
+                        sbi::remote_sfence_vma(*mask, 0, 0);
+                    }
+                    Err(_) => unreachable!("Failed to construct mask"),
+                }
+            }
+            mask = Some(HartMask::from_mask_base(0b1, i));
+        }
+        if let Some(mask) = mask {
+            sbi::remote_sfence_vma(mask, 0, 0);
+        }
+        unsafe {
+            WAIT_COUNT.fetch_sub(1, Ordering::AcqRel);
+            while WAIT_COUNT.load(Ordering::Relaxed) != 0 {}
+        }
+    } else {
+        unreachable!("resend {}", hartid);
+    }
+    sbi::hart_suspend(sbi::NonRetentive, core_send_ipi as _, unsafe {
+        core::ptr::addr_of!(HART_STACK[hartid + 1]) as _
+    });
+    unreachable!()
+}
+
+extern "C" fn init_main(hartid: usize) -> ! {
+    sbi::hart_suspend(sbi::NonRetentive, core_send_ipi as _, unsafe {
+        core::ptr::addr_of!(HART_STACK[hartid + 1]) as _
+    });
+    unreachable!()
+}
+
+static mut WAIT_COUNT: AtomicU64 = AtomicU64::new(0);
+
+const SUSPENDED: SbiRet = SbiRet::success(hart_state::SUSPENDED);
+
+fn get_time() -> u64 {
+    const CSR_TIME: u32 = 0xc01;
+    let mut low_time: u64;
+    unsafe {
+        asm!("csrr {}, {CSR_TIME}", out(reg) low_time, CSR_TIME = const CSR_TIME);
+    }
+
+    low_time
+}
+
+extern "C" fn rust_main(hartid: usize, dtb_pa: usize) -> ! {
+    #[derive(Deserialize)]
+    struct Tree<'a> {
+        cpus: Cpus<'a>,
+        chosen: Chosen<'a>,
+    }
+    #[derive(Deserialize)]
+    #[serde(rename_all = "kebab-case")]
+    struct Cpus<'a> {
+        timebase_frequency: u32,
+        cpu: NodeSeq<'a>,
+    }
+    #[derive(Deserialize)]
+    #[serde(rename_all = "kebab-case")]
+    struct Chosen<'a> {
+        stdout_path: StrSeq<'a>,
+    }
+    rcore_console::init_console(&Console);
+    rcore_console::set_log_level(option_env!("LOG"));
+    let dtb_ptr = DtbPtr::from_raw(dtb_pa as _).unwrap();
+    let dtb = Dtb::from(dtb_ptr).share();
+    let root: Node = serde_device_tree::from_raw_mut(&dtb).unwrap();
+    let tree: Tree = root.deserialize();
+    let stdout_path = tree.chosen.stdout_path.iter().next().unwrap();
+    if let Some(node) = root.find(stdout_path) {
+        let reg = node.get_prop("reg").unwrap().deserialize::<Reg>();
+        let address = reg.iter().next().unwrap().0.start;
+        unsafe { UART = Uart16550Map(address as _) };
+    }
+    let smp = tree.cpus.cpu.len();
+    let frequency = tree.cpus.timebase_frequency;
+    info!(
+        r"
+ ____                  _       _  __                    _
+| __ )  ___ _ __   ___| |__   | |/ /___ _ __ _ __   ___| |
+|  _ \ / _ \ '_ \ / __| '_ \  | ' // _ \ '__| '_ \ / _ \ |
+| |_) |  __/ | | | (__| | | | | . \  __/ |  | | | |  __/ |
+|____/ \___|_| |_|\___|_| |_| |_|\_\___|_|  |_| |_|\___|_|
+==========================================================
+| boot hart id          | {hartid:20} |
+| smp                   | {smp:20} |
+| timebase frequency    | {frequency:17} Hz |
+| dtb physical address  | {dtb_pa:#20x} |
+----------------------------------------------------------"
+    );
+    unsafe {
+        SMP_COUNT = smp;
+        BOOT_HART_ID = hartid;
+    }
+    for i in 0..smp {
+        unsafe {
+            IPI_SENT[i].write(AtomicBool::new(false));
+        }
+        if i != hartid {
+            sbi::hart_start(i, init_hart as _, unsafe {
+                core::ptr::addr_of!(HART_STACK[i + 1]) as _
+            });
+            while sbi::hart_get_status(i) != SUSPENDED {
+                core::hint::spin_loop();
+            }
+        }
+    }
+    info!("Starting test");
+    for i in 0..4 {
+        info!("Test #{i} started");
+        unsafe {
+            for (i, ipi_sent) in IPI_SENT.iter_mut().enumerate().take(smp) {
+                ipi_sent.assume_init_mut().swap(false, Ordering::AcqRel);
+                if i != hartid {
+                    while sbi::hart_get_status(i) != SUSPENDED {}
+                }
+            }
+            WAIT_COUNT.swap((smp - 1) as u64, Ordering::AcqRel);
+        }
+        debug!("send ipi!");
+        let start_time = get_time();
+        let mut mask = Some(HartMask::from_mask_base(0, 0));
+        for i in 0..smp {
+            if i == hartid {
+                continue;
+            }
+            if let Some(ref mut mask) = mask {
+                match mask.insert(i) {
+                    Ok(_) => continue,
+                    Err(MaskError::InvalidBit) => {
+                        sbi::send_ipi(*mask);
+                    }
+                    Err(_) => unreachable!("Failed to construct mask"),
+                }
+            }
+            mask = Some(HartMask::from_mask_base(0b1, i));
+        }
+        if let Some(mask) = mask {
+            sbi::send_ipi(mask);
+        }
+        while unsafe { WAIT_COUNT.load(Ordering::Acquire) } != 0 {}
+        let end_time = get_time();
+        println!("Test #{}: {}", i, end_time - start_time);
+    }
+    sbi::system_reset(sbi::Shutdown, sbi::NoReason);
+    unreachable!()
+}
+
+#[cfg_attr(not(test), panic_handler)]
+fn panic(info: &core::panic::PanicInfo) -> ! {
+    let (hart_id, pc): (usize, usize);
+    unsafe { asm!("mv    {}, tp", out(reg) hart_id) };
+    unsafe { asm!("auipc {},  0", out(reg) pc) };
+    info!("[test-kernel-panic] hart {hart_id} {info}");
+    info!("[test-kernel-panic] pc = {pc:#x}");
+    info!("[test-kernel-panic] SBI test FAILED due to panic");
+    sbi::system_reset(sbi::Shutdown, sbi::SystemFailure);
+    loop {}
+}
+
+struct Console;
+static mut UART: Uart16550Map = Uart16550Map(null());
+
+pub struct Uart16550Map(*const Uart16550<u8>);
+
+unsafe impl Sync for Uart16550Map {}
+
+impl Uart16550Map {
+    #[inline]
+    pub fn get(&self) -> &Uart16550<u8> {
+        unsafe { &*self.0 }
+    }
+}
+
+impl rcore_console::Console for Console {
+    #[inline]
+    fn put_char(&self, c: u8) {
+        unsafe { UART.get().write(core::slice::from_ref(&c)) };
+    }
+
+    #[inline]
+    fn put_str(&self, s: &str) {
+        unsafe { UART.get().write(s.as_bytes()) };
+    }
+}

+ 68 - 0
prototyper/cliff.toml

@@ -0,0 +1,68 @@
+# git-cliff ~ configuration file
+# https://git-cliff.org/docs/configuration
+
+[changelog]
+header = """
+# Changelog\n
+All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.\n
+"""
+
+body = """
+---
+{% if version %}
+    {% if previous.version %}
+        ## [{{ version | trim_start_matches(pat="v") }}]($REPO/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
+    {% else %}
+        ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
+    {% endif %}
+{% else %}
+    ## [unreleased]
+{% endif %}
+{% for group, commits in commits | group_by(attribute="group") %}
+    ### {{ group | striptags | trim | upper_first }}
+    {% for commit in commits | filter(attribute="scope") | sort(attribute="scope") %}
+        - **({{commit.scope}})**{% if commit.breaking %} [**breaking**]{% endif %} {{ commit.message|trim }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }}
+    {%- endfor -%}
+    {% raw %}\n{% endraw %}
+    {%- for commit in commits %}
+        {%- if not commit.scope -%}
+            - {% if commit.breaking %} [**breaking**]{% endif %}{{ commit.message|trim }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }}
+        {%- endif -%}
+    {% endfor -%}
+{% endfor %}\n
+"""
+
+footer = "<!-- generated by git-cliff -->"
+trim = true
+postprocessors = [
+    { pattern = '\$REPO', replace = "https://github.com/rustsbi/prototyper" },
+]
+
+[git]
+conventional_commits = true
+filter_unconventional = false
+split_commits = false
+commit_preprocessors = []
+commit_parsers = [
+    { message = "\\[skip", skip = true },
+    { message = "\\p{Han}", skip = true },
+    { message = "^feat", group = "Features" },
+    { message = "^fix", group = "Bug Fixes" },
+    { message = "^doc", group = "Documentation" },
+    { message = "^perf", group = "Performance" },
+    { message = "^refactor", group = "Refactoring" },
+    { message = "^style", group = "Style" },
+    { message = "^revert", group = "Revert" },
+    { message = "^test", group = "Tests" },
+    { message = "^chore\\(version\\):", skip = true },
+    { message = "^chore", group = "Miscellaneous Chores" },
+    { message = ".*", group = "Other" },
+    { body = ".*security", group = "Security" },
+]
+protect_breaking_commits = false
+filter_commits = false
+tag_pattern = "v[0-9].*"
+skip_tags = "v0.1.0-beta.1"
+ignore_tags = ""
+topo_order = false
+sort_commits = "oldest"

+ 33 - 0
prototyper/docs/booting-archlinux-in-qemu-using-uboot-and-rustsbi.md

@@ -0,0 +1,33 @@
+# 使用RustSBI & U-Boot在QEMU中启动ArchLinux
+
+本教程给出了使用RustSBI和U-Boot在QEMU中启动ArchLinux的基本流程。
+
+本教程要求您使用非RISC-V Arch Linux(x86_64 或 aarch64 等)机器上运行,因为我们使用了`pacstrap`和`pacman`。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+| riscv64-linux-gnu-gcc | 14.1.0  |
+|  qemu-system-riscv64  |  9.0.1  |
+|  RustSBI Prototyper   |  0.0.0  |
+|        U-Boot         | 2024.04 |
+
+1. 安装依赖环境。
+``` shell
+# pacman -Syu then reboot is recommended before this
+$ sudo pacman -S arch-install-scripts git qemu-img qemu-system-riscv riscv64-linux-gnu-gcc devtools-riscv64
+```
+
+2. Clone构建脚本,构建rootfs和镜像。
+``` shell
+$ git clone -b rustsbi https://github.com/guttatus/archriscv-scriptlet.git
+$ cd archriscv-scriptlet
+$ ./mkrootfs
+$ ./mkimg
+```
+3. 使用Qemu启动Archlinux
+``` shell
+$ ./startqemu.sh
+```
+如果在最后一步中,您发现自己卡在 `[ OK ] Reached target Graphical Interface` 超过5分钟,只需按 `Ctrl`-`C` 并重新运行 `startqemu.sh`。

+ 129 - 0
prototyper/docs/booting-fedora-in-qemu-using-uboot-and-rustsbi.md

@@ -0,0 +1,129 @@
+# 使用RustSBI & U-Boot在QEMU中启动 Fedora
+
+本教程给出了使用RustSBI和U-Boot在QEMU中启动 Fedora 的基本流程。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+| riscv64-linux-gnu-gcc | 14.1.0  |
+|  qemu-system-riscv64  |  9.0.1  |
+|  RustSBI Prototyper   |  0.0.0  |
+|        U-Boot         | 2024.04 |
+
+## 准备RustSBI Prototyper, U-Boot ,Fedora 
+
+创建工作目录并进入该目录
+
+``` shell
+$ mkdir workshop && cd workshop
+```
+
+### Clone RustSBI Prototyper
+
+``` shell
+$ git clone https://github.com/rustsbi/prototyper.git && cd prototyper && git checkout main && cd ..
+```
+
+### Clone U-Boot
+
+``` shell
+$ git clone https://github.com/u-boot/u-boot.git && cd u-boot && git checkout v2024.04 && cd ..
+```
+### 下载 Fedora 镜像文件
+
+下载链接:<https://dl.fedoraproject.org/pub/alt/risc-v/disk_images/Fedora-40/Fedora.riscv64-40-20240429.n.0.qcow2>
+```shell
+$ mkdir -p fedora
+$ cd fedora
+$ wget https://dl.fedoraproject.org/pub/alt/risc-v/disk_images/Fedora-40/Fedora.riscv64-40-20240429.n.0.qcow2
+$ cd ..
+```
+
+## 编译RustSBI  Prototyper
+
+进入prototyper目录
+
+``` shell
+$ cd prototyper
+```
+
+编译RustSBI  Prototyper
+
+``` shell
+$ cargo prototyper
+```
+
+## 编译U-Boot SPL
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+```shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin 
+```
+
+生成`.config`文件,编译U-Boot
+
+```shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+$ sed -i.bak 's/CONFIG_BOOTCOMMAND=*/CONFIG_BOOTCOMMAND="fatload virtio 0:1 84000000 EFI\/Linux\/6.8.7-300.4.riscv64.fc40.riscv64.efi; setenv bootargs root=UUID=57cbf0ca-8b99-45ae-ae9d-3715598f11c4 ro rootflags=subvol=root rhgb LANG=en_US.UTF-8 console=ttyS0 earlycon=sbi; bootefi 0x84000000 - ${fdtcontroladdr};"/' .config
+$ make -j$(nproc)
+```
+
+## 配置 cloud-init
+
+```shell
+$ touch network-config
+$ touch meta-data
+$ cat >user-data <<EOF
+
+#cloud-config
+password: password
+chpasswd:
+  expire: False
+ssh_pwauth: True
+EOF
+
+genisoimage \
+    -output seed.img \
+    -volid cidata -rational-rock -joliet \
+    user-data meta-data network-config
+```
+
+## 使用RustSBI 原型系统和U-Boot启动 Fedora
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 \
+    -nographic -machine virt \
+    -smp 4 -m 8G \
+    -bios ./u-boot/spl/u-boot-spl  \
+    -device loader,file=./u-boot/u-boot.itb,addr=0x80200000 \
+    -drive file=./fedora/Fedora.riscv64-40-20240429.n.0.qcow2,format=qcow2,if=none,id=hd0 \
+    -object rng-random,filename=/dev/urandom,id=rng0 \
+    -device virtio-vga \
+    -device virtio-rng-device,rng=rng0 \
+    -device virtio-blk-device,drive=hd0 \
+    -device virtio-net-device,netdev=usernet \
+    -netdev user,id=usernet,hostfwd=tcp::12055-:22 \
+    -device qemu-xhci -usb -device usb-kbd -device usb-tablet \
+    -cdrom ./seed.img
+```
+
+帐号默认为 `fedora`,密码应为 `cloud-init` 配置过程中的 `password` 项值。

+ 147 - 0
prototyper/docs/booting-freebsd-in-qemu-using-uboot-and-rustsbi.md

@@ -0,0 +1,147 @@
+# 使用RustSBI & U-Boot在QEMU中启动FreeBSD
+
+本教程给出了使用RustSBI和U-Boot在QEMU中启动FreeBSD的基本流程。
+
+请读者在其主机上安装必要的软件来尝试本教程的脚本。本教程是在Arch Linux上开发的。
+
+RustSBI 原型系统提供动态固件,根据前一个阶段传入的信息动态加载下一个阶段。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+| riscv64-linux-gnu-gcc | 14.2.0  |
+|  qemu-system-riscv64  |  9.1.1  |
+|  RustSBI Prototyper   |  0.0.0  |
+|        U-Boot         | 2024.04 |
+|       FreeBSD         |  14.1   |
+
+## 环境配置
+
+### 安装交叉编译器和QEMU
+
+For Arch Linux
+
+``` shell
+$ sudo pacman -S git riscv64-linux-gnu-gcc qemu-system-riscv
+```
+
+#### 测试是否成功安装
+
+For riscv64-linux-gnu-gcc:
+
+``` shell
+$ riscv64-linux-gnu-gcc --version
+```
+
+它将输出以下版本信息
+
+```
+riscv64-linux-gnu-gcc (GCC) 14.2.0
+Copyright (C) 2024 Free Software Foundation, Inc.
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+```
+
+For QEMU:
+
+``` shell
+$ qemu-system-riscv64 --version
+```
+
+它将输出以下版本信息
+
+```
+QEMU emulator version 9.1.1
+Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers
+```
+
+### 准备RustSBI Prototyper, U-Boot和FreeBSD镜像
+
+创建工作目录并进入该目录
+
+``` shell
+$ mkdir workshop && cd workshop
+```
+
+Clone RustSBI Prototyper
+
+``` shell
+$ git clone https://github.com/rustsbi/prototyper.git && cd prototyper && git checkout main && cd ..
+```
+
+Clone U-Boot
+
+``` shell
+$ git clone https://github.com/u-boot/u-boot.git && cd u-boot && git checkout v2024.04 && cd ..
+```
+
+Download FreeBSD
+``` shell
+$ wget https://download.freebsd.org/releases/VM-IMAGES/14.1-RELEASE/riscv64/Latest/FreeBSD-14.1-RELEASE-riscv-riscv64.raw.xz && xz -d FreeBSD-14.1-RELEASE-riscv-riscv64.raw.xz
+```
+
+
+## 编译RustSBI  Prototyper
+
+进入prototyper目录
+
+``` shell
+$ cd prototyper
+```
+
+编译RustSBI  Prototyper
+
+``` shell
+$ cargo prototyper
+```
+
+## 编译U-Boot SPL
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin
+```
+
+生成`.config`文件
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+```
+
+编译U-Boot
+
+``` shell
+# To build U-Boot
+$ make -j$(nproc)
+```
+
+本小节将使用二进制文件 `./spl/u-boot-spl`和`./u-boot.itb `。
+
+## 使用RustSBI 原型系统和U-Boot启动Linux Kernel
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 -M virt -smp 1 -m 256M -nographic \
+          -bios ./u-boot/spl/u-boot-spl \
+          -device loader,file=./u-boot/u-boot.itb,addr=0x80200000 \
+          -blockdev driver=file,filename=./FreeBSD-14.1-RELEASE-riscv-riscv64.raw,node-name=hd0 \
+          -device virtio-blk-device,drive=hd0
+```

+ 499 - 0
prototyper/docs/booting-linux-kernel-in-qemu-using-uboot-and-opensbi.md

@@ -0,0 +1,499 @@
+# 使用OpenSBI & U-Boot在QEMU中启动Linux内核
+
+本教程给出了使用OpenSBI和U-Boot在QEMU中启动Linux内核的基本流程。高级用户可以在本教程中配置或构建各种内容时尝试不同的选项。
+
+请读者在其主机上安装必要的软件来尝试本教程的脚本。本教程是在Arch Linux上开发的。
+
+[环境配置](#环境配置)小节给出了本教程的环境配置方法,用户在使用本教程时需要先完成环境配置小节内容。
+
+[编译Linux Kernel](#编译linux-kernel)小节给出了Linux Kernel的编译流程,并使用编译好的Linux Kernel镜像制作启动盘。
+
+OpenSBI 有三种 Firmware:
+
+- `fw_payload`:下一引导阶段被作为 payload 打包进来,通常是 U-Boot 或 Linux。这是兼容 Linux 的 RISC-V 硬件所使用的默认 Firmware。
+- `fw_jump`:跳转到一个固定地址,该地址上需存有下一个加载器。QEMU 的早期版本曾经使用过它。
+- `fw_dynamic`:根据前一个阶段传入的信息动态加载下一个阶段。U-Boot SPL/Coreboot 使用 `fw_dynamic`。现在 QEMU 默认使用 `fw_dynamic`。
+
+[`fw_payload`](#fw_payload)小节本给出了使用OpnSBI `fw_payload`类型固件和U-Boot在QEMU上启动Linux Kernel的教程。
+
+[`fw_jump`](#fw_jump)小节本给出了使用OpnSBI `fw_jump`类型固件和U-Boot在QEMU上启动Linux Kernel的教程。
+
+[`fw_dynamic`](#fw_dynamic)小节本给出了使用OpnSBI `fw_dynamic`类型固件和U-Boot在QEMU上启动Linux Kernel的教程。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+| riscv64-linux-gnu-gcc | 14.1.0  |
+|  qemu-system-riscv64  |  9.0.1  |
+|        OpenSBI        |   1.4   |
+|        U-Boot         | 2024.04 |
+|     Linux Kernel      |   6.2   |
+|        busybox        | 1.36.0  |
+
+## 环境配置
+
+### 安装交叉编译器和QEMU
+
+For Arch Linux:
+
+``` shell
+$ sudo pacman -S git riscv64-linux-gnu-gcc qemu-system-riscv
+```
+
+For Ubuntu:
+
+``` shell
+$ sudo apt-get update && sudo apt-get upgrade
+$ sudo apt-get install git qemu-system-misc gcc-riscv64-linux-gnu 
+```
+
+#### 测试是否成功安装
+
+For riscv64-linux-gnu-gcc:
+
+``` shell
+$  riscv64-linux-gnu-gcc --version
+```
+
+它将输出以下版本信息
+
+``` 
+riscv64-linux-gnu-gcc (GCC) 14.1.0
+Copyright (C) 2024 Free Software Foundation, Inc.
+This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+```
+
+For QEMU:
+
+``` shell
+$ qemu-system-riscv64  --version
+```
+
+它将输出以下版本信息
+
+``` 
+QEMU emulator version 9.0.1
+Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers
+```
+
+### 准备OpenSBI, U-Boot , busybox和Linux Kernel源码
+
+创建工作目录并进入该目录
+
+``` shell
+$ mkdir workshop && cd workshop
+```
+
+Clone OpenSBI
+
+``` shell
+$ git clone https://github.com/riscv/opensbi.git && cd opensbi && git checkout v1.4 && cd ..
+```
+
+Clone U-Boot
+
+``` shell
+$ git clone https://github.com/u-boot/u-boot.git && cd u-boot && git checkout v2024.04 && cd ..
+```
+
+Clone busybox
+
+``` shell
+$ git clone https://github.com/mirror/busybox.git && cd busybox && git checkout 1_36_0 && cd ..
+```
+
+Clone Linux Kernel
+
+``` shell
+$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git && cd linux && git checkout v6.2 && cd ..
+```
+
+## 编译Linux Kernel
+
+进入`linux`目录
+
+``` shell
+$ cd linux
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+```
+
+生成`.config`文件
+
+``` shell
+$ make defconfig
+```
+
+验证`.config`文件是否存在RISC-V
+
+``` shell
+$ grep --color=always -ni 'riscv' .config
+```
+
+观察到RISC-V 配置选项已启用
+
+``` 
+CONFIG_RISCV=y
+```
+
+编译Linux Kernel
+
+``` shell
+$ make -j$(nproc)
+```
+
+生成的文件`Image` 和 `Image.gz` 可以在`arch/riscv/boot/`目录找到。 `Image.gz`是 `Image` 的压缩形式。
+
+### 创建根文件系统
+
+#### 编译busybox
+
+> busybox在Ubuntu 22.04和Arch Linux系统上编译时会报错,推荐在Ubuntu 20.04系统上编译。
+
+进入busybox目录
+
+``` shell
+$ cd busybox
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+```
+
+编译busybox
+
+``` shell
+$ make defconfig
+$ make menuconfig
+# Enable the Build static binary (no shared libs) option in Settings-->Build Options
+$ make -j $(nproc)
+$ make install
+```
+
+#### 创建启动盘
+
+在`workshop`目录运行以下命令来创建一个1 GB的磁盘镜像
+
+``` shell
+# Create a 1 GB disk image
+$ qemu-img create linux-rootfs.img 1g
+```
+
+#### 创建分区
+
+将在磁盘映像`linux-rootfs.img`上创建1个分区,这个分区是可引导的。
+
+`parted`命令将用于在镜像`linux-rootfs.img`中创建分区。在镜像中创建分区表:
+
+``` shell
+$ sudo parted linux-rootfs.img mklabel gpt
+```
+
+现在`linux-rootfs.img`中有一个分区表。将`linux-rootfs.img`挂载为loop device,以便它可以用作块设备。将`linux-rootfs.img`挂载为块设备将允许在其中创建分区。
+
+``` shell
+# Attach linux-rootfs.img with the first available loop device
+$ sudo losetup --find --show linux-rootfs.img
+```
+
+> - `find`:查找第一个未使用的loop device
+> - `show`:显示`linux-rootfs.img`附加到的loop device的名称
+
+记下循环设备的完整路径。在本教程中它是`/dev/loop0`。对`/dev/loop0`的操作将会对`linux-rootfs.img`进行操作。
+
+对`/dev/loop0`分区
+
+``` shell
+# Create a couple of primary partitions
+$ sudo parted --align minimal /dev/loop0 mkpart primary ext4 0 100%
+
+$ sudo parted /dev/loop0 print
+```
+
+#### 格式化分区
+
+通过以下命令查看分区:
+
+``` shell
+$ ls -l /dev/loop0*
+```
+
+在本教程中,分区为`/dev/loop0p1`。
+
+格式化分区并创建`ext4`文件系统,同时将分区设置为可引导分区。
+
+``` shell
+$ sudo mkfs.ext4 /dev/loop0p1
+
+# Mark first partition as bootable
+$ sudo parted /dev/loop0 set 1 boot on
+```
+
+#### 将Linux Kernel和根文件系统拷贝进启动盘
+
+``` shell
+# Mount the 1st partition
+$ sudo mkdir rootfs
+$ sudo mount /dev/loop0p1 rootfs
+$ cd rootfs
+```
+拷贝Linux Kernel镜像
+``` shell
+$ sudo cp ../linux/arch/riscv/boot/Image .
+```
+
+拷贝根文件系统
+
+``` shell
+$ sudo cp -r ../busybox/_install/* .
+$ sudo mkdir proc sys dev etc etc/init.d
+$ cd etc/init.d/
+$ sudo cat > rcS << EOF
+  #!/bin/sh
+  mount -t proc none /proc
+  mount -t sysfs none /sys
+  /sbin/mdev -s
+  EOF
+$ sudo chmod +x rcS
+```
+
+卸载`rootfs`
+
+``` shell
+$ cd workshop
+$ sudo umount rootfs
+```
+
+将`/dev/loop0`分离
+
+``` shell
+$ sudo losetup -d /dev/loop0
+```
+
+## `fw_payload`
+
+本小节给出了使用OpnSBI `fw_payload`类型固件和U-Boot在QEMU上启动Linux Kernel的教程。
+
+### 编译U-Boot
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+```
+
+生成`.config`文件
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_smode_defconfig
+# add bootcmd value
+$ make menuconfig
+```
+U-Boot 配置选项将加载到终端。导航到 `Boot options` $\rightarrow$ `bootcmd value` 并将以下内容写入 `bootcmd` 值:
+
+``` 
+ext4load virtio 0:1 84000000 Image; setenv bootargs root=/dev/vda1 rw console=ttyS0; booti 0x84000000 - ${fdtcontroladdr}
+```
+
+编译U-Boot
+
+``` shell
+# To build U-Boot
+$ make -j$(nproc)
+```
+
+U-Boot 二进制文件位于 `./u-boot.bin`。
+
+### 编译OpenSBI
+
+进入OpenSBI目录
+
+``` shell
+$ cd opensbi
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+```
+
+编译OpenSBI
+
+``` shell
+$ make PLATFORM=generic FW_PAYLOAD_PATH=../u-boot/u-boot.bin -j$(nproc)
+```
+
+本小节将使用 QEMU 可以运行的输出文件 `build/platform/generic/firmware/fw_payload.elf`。由于`FW_PAYLOAD_PATH`指向 u-boot,因此 U-Boot 嵌入在输出中,OpenSBI 将自动启动 U-Boot。
+
+### 使用OpenSBI `fw_payload`固件启动Linux Kernel
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 -M virt -smp 4 -m 256M -nographic \
+      -bios ./opensbi/build/platform/generic/firmware/fw_payload.elf \
+      -blockdev driver=file,filename=./linux-rootfs.img,node-name=hd0 \
+      -device virtio-blk-device,drive=hd0
+```
+
+
+
+## `fw_jump`
+
+本小节给出了使用OpnSBI `fw_jump`类型固件和U-Boot在QEMU上启动Linux Kernel的教程。
+
+### 编译U-Boot
+
+和[`fw_payload`](#fw_payload)小节一致
+
+### 编译OpenSBI
+
+进入OpenSBI目录
+
+``` shell
+$ cd opensbi
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+```
+
+编译OpenSBI
+
+``` shell
+$ make all PLATFORM=generic PLATFORM_RISCV_XLEN=64 -j$(nproc)
+```
+
+本小节将使用 QEMU 可以运行的输出文件 `build/platform/generic/firmware/fw_jump.bin`。
+
+### 使用OpenSBI `fw_jump`固件启动Linux Kernel
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 -M virt -smp 4 -m 256M -nographic \
+      -bios ./opensbi/build/platform/generic/firmware/fw_jump.elf \
+      -kernel ./u-boot/u-boot.bin  \
+      -blockdev driver=file,filename=./linux-rootfs.img,node-name=hd0 \
+      -device virtio-blk-device,drive=hd0
+```
+
+## `fw_dynamic`
+
+本小节给出了使用OpnSBI `fw_dynamic`类型固件和U-Boot在QEMU上启动Linux Kernel的教程。
+
+### 编译OpenSBI
+
+进入OpenSBI目录
+
+``` shell
+$ cd opensbi
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+```
+
+编译OpenSBI
+
+``` shell
+$ make all PLATFORM=generic PLATFORM_RISCV_XLEN=64 -j$(nproc)
+```
+
+本小节将使用 QEMU 可以运行的输出文件 `build/platform/generic/firmware/fw_dynamic.bin`。
+
+### 编译U-Boot SPL
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../opensbi/build/platform/generic/firmware/fw_dynamic.bin 
+```
+
+生成`.config`文件
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+# add bootcmd value
+$ make menuconfig
+```
+
+U-Boot 配置选项将加载到终端。导航到 `Boot options` $\rightarrow$ `bootcmd value` 并将以下内容写入 `bootcmd` 值:
+
+``` 
+ext4load virtio 0:1 84000000 Image; setenv bootargs root=/dev/vda1 rw console=ttyS0; booti 0x84000000 - ${fdtcontroladdr}
+```
+
+编译U-Boot
+
+``` shell
+# To build U-Boot
+$ make -j$(nproc)
+```
+
+本小节将使用二进制文件 `./spl/u-boot-spl`和`./u-boot.itb `。
+
+### 使用OpenSBI `fw_dynamic`固件启动Linux Kernel
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 -M virt -smp 4 -m 256M -nographic \
+          -bios ./u-boot/spl/u-boot-spl \
+          -device loader,file=./u-boot/u-boot.itb,addr=0x80200000 \
+          -blockdev driver=file,filename=./linux-rootfs.img,node-name=hd0 \
+          -device virtio-blk-device,drive=hd0
+```
+

+ 343 - 0
prototyper/docs/booting-linux-kernel-in-qemu-using-uboot-and-rustsbi.md

@@ -0,0 +1,343 @@
+# 使用RustSBI & U-Boot在QEMU中启动Linux内核
+
+本教程给出了使用RustSBI和U-Boot在QEMU中启动Linux内核的基本流程。高级用户可以在本教程中配置或构建各种内容时尝试不同的选项。
+
+请读者在其主机上安装必要的软件来尝试本教程的脚本。本教程是在Arch Linux上开发的。
+
+[环境配置](#环境配置)小节给出了本教程的环境配置方法,用户在使用本教程时需要先完成环境配置小节内容。
+
+[编译Linux Kernel](#编译linux-kernel)小节给出了Linux Kernel的编译流程,并使用编译好的Linux Kernel镜像制作启动盘。
+
+RustSBI 原型系统提供动态固件,根据前一个阶段传入的信息动态加载下一个阶段。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+| riscv64-linux-gnu-gcc | 14.1.0  |
+|  qemu-system-riscv64  |  9.0.1  |
+|  RustSBI Prototyper   |  0.0.0  |
+|        U-Boot         | 2024.04 |
+|     Linux Kernel      |   6.2   |
+|        busybox        | 1.36.0  |
+
+## 环境配置
+
+### 安装交叉编译器和QEMU
+
+For Arch Linux:
+
+``` shell
+$ sudo pacman -S git riscv64-linux-gnu-gcc qemu-system-riscv
+```
+
+For Ubuntu:
+
+``` shell
+$ sudo apt-get update && sudo apt-get upgrade
+$ sudo apt-get install git qemu-system-misc gcc-riscv64-linux-gnu 
+```
+
+#### 测试是否成功安装
+
+For riscv64-linux-gnu-gcc:
+
+``` shell
+$ riscv64-linux-gnu-gcc --version
+```
+
+它将输出以下版本信息
+
+``` 
+riscv64-linux-gnu-gcc (GCC) 14.1.0
+Copyright (C) 2024 Free Software Foundation, Inc.
+This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+```
+
+For QEMU:
+
+``` shell
+$ qemu-system-riscv64 --version
+```
+
+它将输出以下版本信息
+
+``` 
+QEMU emulator version 9.0.1
+Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers
+```
+
+### 准备RustSBI Prototyper, U-Boot , busybox和Linux Kernel源码
+
+创建工作目录并进入该目录
+
+``` shell
+$ mkdir workshop && cd workshop
+```
+
+Clone RustSBI Prototyper
+
+``` shell
+$ git clone https://github.com/rustsbi/prototyper.git && cd prototyper && git checkout main && cd ..
+```
+
+Clone U-Boot
+
+``` shell
+$ git clone https://github.com/u-boot/u-boot.git && cd u-boot && git checkout v2024.04 && cd ..
+```
+
+Clone busybox
+
+``` shell
+$ git clone https://github.com/mirror/busybox.git && cd busybox && git checkout 1_36_0 && cd ..
+```
+
+Clone Linux Kernel
+
+``` shell
+$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git && cd linux && git checkout v6.2 && cd ..
+```
+
+## 编译Linux Kernel
+
+进入`linux`目录
+
+``` shell
+$ cd linux
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+```
+
+生成`.config`文件
+
+``` shell
+$ make defconfig
+```
+
+验证`.config`文件是否存在RISC-V
+
+``` shell
+$ grep --color=always -ni 'riscv' .config
+```
+
+观察到RISC-V 配置选项已启用
+
+``` 
+CONFIG_RISCV=y
+```
+
+编译Linux Kernel
+
+``` shell
+$ make -j$(nproc)
+```
+
+生成的文件`Image` 和 `Image.gz` 可以在`arch/riscv/boot/`目录找到。 `Image.gz`是 `Image` 的压缩形式。
+
+### 创建根文件系统
+
+#### 编译busybox
+
+> busybox在Ubuntu 22.04和Arch Linux系统上编译时会报错,推荐在Ubuntu 20.04系统上编译。
+
+进入busybox目录
+
+``` shell
+$ cd busybox
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+```
+
+编译busybox
+
+``` shell
+$ make defconfig
+$ make menuconfig
+# Enable the Build static binary (no shared libs) option in Settings-->Build Options
+$ make -j $(nproc)
+$ make install
+```
+
+#### 创建启动盘
+
+在`workshop`目录运行以下命令来创建一个1 GB的磁盘镜像
+
+``` shell
+# Create a 1 GB disk image
+$ qemu-img create linux-rootfs.img 1g
+```
+
+#### 创建分区
+
+将在磁盘映像`linux-rootfs.img`上创建1个分区,这个分区是可引导的。
+
+`parted`命令将用于在镜像`linux-rootfs.img`中创建分区。在镜像中创建分区表:
+
+``` shell
+$ sudo parted linux-rootfs.img mklabel gpt
+```
+
+现在`linux-rootfs.img`中有一个分区表。将`linux-rootfs.img`挂载为loop device,以便它可以用作块设备。将`linux-rootfs.img`挂载为块设备将允许在其中创建分区。
+
+``` shell
+# Attach linux-rootfs.img with the first available loop device
+$ sudo losetup --find --show linux-rootfs.img
+```
+
+> - `find`:查找第一个未使用的loop device
+> - `show`:显示`linux-rootfs.img`附加到的loop device的名称
+
+记下循环设备的完整路径。在本教程中它是`/dev/loop0`。对`/dev/loop0`的操作将会对`linux-rootfs.img`进行操作。
+
+对`/dev/loop0`分区
+
+``` shell
+# Create a couple of primary partitions
+$ sudo parted --align minimal /dev/loop0 mkpart primary ext4 0 100%
+
+$ sudo parted /dev/loop0 print
+```
+
+#### 格式化分区
+
+通过以下命令查看分区:
+
+``` shell
+$ ls -l /dev/loop0*
+```
+
+在本教程中,分区为`/dev/loop0p1`。
+
+格式化分区并创建`ext4`文件系统,同时将分区设置为可引导分区。
+
+``` shell
+$ sudo mkfs.ext4 /dev/loop0p1
+
+# Mark first partition as bootable
+$ sudo parted /dev/loop0 set 1 boot on
+```
+
+#### 将Linux Kernel和根文件系统拷贝进启动盘
+
+``` shell
+# Mount the 1st partition
+$ sudo mkdir rootfs
+$ sudo mount /dev/loop0p1 rootfs
+$ cd rootfs
+```
+拷贝Linux Kernel镜像
+``` shell
+$ sudo cp ../linux/arch/riscv/boot/Image .
+```
+
+拷贝根文件系统
+
+``` shell
+$ sudo cp -r ../busybox/_install/* .
+$ sudo mkdir proc sys dev etc etc/init.d
+$ cd etc/init.d/
+$ sudo cat > rcS << EOF
+  #!/bin/sh
+  mount -t proc none /proc
+  mount -t sysfs none /sys
+  /sbin/mdev -s
+  EOF
+$ sudo chmod +x rcS
+```
+
+卸载`rootfs`
+
+``` shell
+$ cd workshop
+$ sudo umount rootfs
+```
+
+将`/dev/loop0`分离
+
+``` shell
+$ sudo losetup -d /dev/loop0
+```
+
+## 编译RustSBI  Prototyper
+
+进入prototyper目录
+
+``` shell
+$ cd prototyper
+```
+
+编译RustSBI  Prototyper
+
+``` shell
+$ cargo prototyper
+```
+
+## 编译U-Boot SPL
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin 
+```
+
+生成`.config`文件
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+# add bootcmd value
+$ make menuconfig
+```
+
+U-Boot 配置选项将加载到终端。导航到 `Boot options` $\rightarrow$ `bootcmd value` 并将以下内容写入 `bootcmd` 值:
+
+``` 
+ext4load virtio 0:1 84000000 Image; setenv bootargs root=/dev/vda1 rw console=ttyS0; booti 0x84000000 - ${fdtcontroladdr}
+```
+
+编译U-Boot
+
+``` shell
+# To build U-Boot
+$ make -j$(nproc)
+```
+
+本小节将使用二进制文件 `./spl/u-boot-spl`和`./u-boot.itb `。
+
+## 使用RustSBI 原型系统和U-Boot启动Linux Kernel
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 -M virt -smp 1 -m 256M -nographic \
+          -bios ./u-boot/spl/u-boot-spl \
+          -device loader,file=./u-boot/u-boot.itb,addr=0x80200000 \
+          -blockdev driver=file,filename=./linux-rootfs.img,node-name=hd0 \
+          -device virtio-blk-device,drive=hd0
+```
+

+ 107 - 0
prototyper/docs/booting-openEuler-23.09-in-qemu-using-uboot-and-rustsbi.md

@@ -0,0 +1,107 @@
+# 使用RustSBI & U-Boot在QEMU中启动openEuler 23.09
+
+本教程给出了使用RustSBI和U-Boot在QEMU中启动openEuler 23.09的基本流程。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+| riscv64-linux-gnu-gcc | 14.1.0  |
+|  qemu-system-riscv64  |  9.0.1  |
+|  RustSBI Prototyper   |  0.0.0  |
+|        U-Boot         | 2024.04 |
+
+## 准备RustSBI Prototyper, U-Boot ,openEuler 23.09
+创建工作目录并进入该目录
+
+``` shell
+$ mkdir workshop && cd workshop
+```
+
+### Clone RustSBI Prototyper
+
+``` shell
+$ git clone https://github.com/rustsbi/prototyper.git && cd prototyper && git checkout main && cd ..
+```
+
+### Clone U-Boot
+
+``` shell
+$ git clone https://github.com/u-boot/u-boot.git && cd u-boot && git checkout v2024.04 && cd ..
+```
+### 下载openEuler 23.09 Qemu磁盘镜像文件
+
+下载链接:[openEuler 23.09](https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-23.09-V1-riscv64/QEMU/openEuler-23.09-V1-base-qemu-preview.qcow2.zst)
+```shell
+ $ unzstd openEuler-23.09-V1-base-qemu-preview.qcow2.zst
+```
+- The password of user `root` is `openEuler12#$`.
+- The password of the default user `openeuler` is `openEuler12#$`.
+
+
+## 编译RustSBI  Prototyper
+
+进入prototyper目录
+
+``` shell
+$ cd prototyper
+```
+
+编译RustSBI  Prototyper
+
+``` shell
+$ cargo prototyper
+```
+
+## 编译U-Boot SPL
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin 
+```
+
+生成`.config`文件,编译U-Boot
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+$ ./scripts/config -e CMD_BTRFS -e FS_BTRFS
+$ make olddefconfig
+$ sed -i.bak 's/# CONFIG_USE_BOOTARGS is not set/CONFIG_USE_BOOTARGS=y\nCONFIG_BOOTARGS="root=\/dev\/vda1 rw console=ttyS0 swiotlb=1 loglevel=7 systemd.default_timeout_start_sec=600 selinux=0 highres=off earlycon"/' .config
+$ make -j$(nproc)
+```
+
+## 使用RustSBI 原型系统和U-Boot启动openEuler 23.09
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 \
+    -nographic -machine virt \
+    -smp 4 -m 8G \
+    -bios ./u-boot/spl/u-boot-spl  \
+    -device loader,file=./u-boot/u-boot.itb,addr=0x80200000 \
+    -drive file=openEuler-23.09-V1-base-qemu-preview.qcow2,format=qcow2,id=hd0 \
+    -object rng-random,filename=/dev/urandom,id=rng0 \
+    -device virtio-vga \
+    -device virtio-rng-device,rng=rng0 \
+    -device virtio-blk-device,drive=hd0 \
+    -device virtio-net-device,netdev=usernet \
+    -netdev user,id=usernet,hostfwd=tcp::12055-:22 \
+    -device qemu-xhci -usb -device usb-kbd -device usb-tablet
+```

+ 149 - 0
prototyper/docs/booting-openwrt-in-qemu-using-uboot-and-rustsbi.md

@@ -0,0 +1,149 @@
+# 使用RustSBI & U-Boot在QEMU中启动 Openwrt
+
+本教程给出了使用RustSBI和U-Boot在QEMU中启动Openwrt的基本流程。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+| riscv64-linux-gnu-gcc | 14.1.0  |
+|  qemu-system-riscv64  |  9.0.1  |
+|  RustSBI Prototyper   |  0.0.0  |
+|        U-Boot         | 2024.04 |
+
+## 准备RustSBI Prototyper, U-Boot ,Openwrt
+创建工作目录并进入该目录
+
+``` shell
+$ mkdir workshop && cd workshop
+```
+
+### Clone RustSBI Prototyper
+
+``` shell
+$ git clone https://github.com/rustsbi/prototyper.git && cd prototyper && git checkout main && cd ..
+```
+
+### Clone U-Boot
+
+``` shell
+$ git clone https://github.com/u-boot/u-boot.git && cd u-boot && git checkout v2024.04 && cd ..
+```
+
+### Clone & Patch Openwrt
+
+``` shell
+$ git clone https://git.openwrt.org/openwrt/openwrt.git 
+$ cd ./openwrt
+$ git checkout 603a3c6
+```
+
+应用本项目目录下的 `docs/openwrt-patch.patch`。
+
+```shell
+$ curl https://raw.githubusercontent.com/rustsbi/prototyper/refs/heads/main/docs/openwrt-patch.patch --output openwrt-patch.patch
+$ git apply openwrt-patch.patch
+```
+
+## 编译RustSBI  Prototyper
+
+进入prototyper目录
+
+``` shell
+$ cd prototyper
+```
+
+编译RustSBI  Prototyper
+
+``` shell
+$ cargo prototyper
+```
+
+## 编译U-Boot SPL
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin 
+```
+
+生成`.config`文件,编译U-Boot
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+$ sed -i.bak 's/CONFIG_BOOTCOMMAND=*/CONFIG_BOOTCOMMAND="scsi scan; fatload scsi 0:3 84000000 Image; setenv bootargs root=\/dev\/sda4 rw earlycon console=\/dev\/ttyS0 rootwait; booti 0x84000000 - ${fdtcontroladdr};"/' .config
+$ make -j$(nproc)
+```
+
+## 编译 Openwrt
+
+首先,你应先按照 <https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem> 配置自己的编译环境。
+
+(以下内容参照并修改自 <https://openwrt.org/docs/guide-developer/toolchain/use-buildsystem>)
+
+更新 Feeds:
+```shell
+$ cd openwrt
+# Update the feeds
+$ ./scripts/feeds update -a
+$ ./scripts/feeds install -a
+```
+
+修改配置:
+```shell
+$ make -j$(nproc) menuconfig
+```
+
+进入 `Target System`,选中 `$SiFive U-based RISC-V boards`。
+
+修改内核配置:
+```shell
+$ make -j$(nproc) kernel_menuconfig
+```
+
+进入后将   
+`Device Drivers` $\rightarrow$ `Serial ATA and Parallel ATA drivers (libata)` $\rightarrow$ `AHCI SATA support`  
+`Device Drivers` $\rightarrow$ `Network device support` $\rightarrow$ `Ethernet driver support` $\rightarrow$ `Intel devices` $\rightarrow$ `Intel(R) PRO/1000 Gigabit Ethernet support`  
+设为 `built-in`。
+
+编译镜像:
+```shell
+# Build the firmware image
+$ make -j$(nproc) defconfig download clean world
+```
+
+拷贝并解压镜像:
+```shell
+$ cd ..
+$ cp ./openwrt/bin/targets/sifiveu/generic/openwrt-sifiveu-generic-sifive_unleashed-ext4-sdcard.img.gz ./
+$ gzip -dk openwrt-sifiveu-generic-sifive_unleashed-ext4-sdcard.img.gz
+```
+
+## 使用RustSBI 原型系统和U-Boot启动 Openwrt
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 \
+-machine virt -nographic -m 4096 -smp 1 \
+-bios ./u-boot/spl/u-boot-spl \
+-device virtio-rng-pci -device ahci,id=ahci -device ide-hd,bus=ahci.0,drive=mydrive \
+-drive file=./openwrt-sifiveu-generic-sifive_unleashed-ext4-sdcard.img,format=raw,if=none,id=mydrive \
+-device loader,file=./u-boot/u-boot.itb,addr=0x80200000 \
+-device e1000,netdev=n1 -netdev user,id=n1,hostfwd=tcp::12055-:22
+```

+ 156 - 0
prototyper/docs/booting-polyos-in-qemu-using-uboot-and-rustsbi.md

@@ -0,0 +1,156 @@
+# 使用RustSBI & U-Boot在QEMU中启动 PolyOS
+
+尽管 Openharmony 在 4.1 引入了 `device_qemu-riscv64-linux`,但是目前仍无法按照[文档](https://gitee.com/openharmony/device_qemu/tree/HEAD/riscv64_virt#)正常编译。
+
+本文于此介绍基于 OpenHarmony 的系统 -- PolyOS 使用 RustSBI 和 U-Boot 在 QEMU 中启动的方法。
+
+下令 `$workdir` 表示工作目录。
+
+### Clone & Compile RustSBI Prototyper
+
+``` shell
+$ cd $workdir
+$ git clone https://github.com/rustsbi/prototyper.git && cd prototyper && git checkout main && cd ..
+```
+
+``` shell
+$ cd prototyper
+```
+
+编译RustSBI  Prototyper
+
+``` shell
+$ cargo prototyper
+```
+
+### Clone & Compile U-Boot
+
+``` shell
+$ cd $workdir
+$ git clone https://github.com/u-boot/u-boot.git && cd u-boot && git checkout v2024.04 && cd ..
+```
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin 
+```
+
+生成`.config`文件,编译U-Boot
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+$ make -j$(nproc)
+```
+
+
+### Download & Configure PolyOS
+
+下载 PolyOS Mobile 镜像:<https://polyos.iscas.ac.cn/downloads/polyos-mobile-latest.img.tar.xz>。
+
+```shell
+$ cd $workdir
+$ wget https://polyos.iscas.ac.cn/downloads/polyos-mobile-latest.img.tar.xz
+$ tar xvf polyos-mobile-latest.img.tar.xz
+```
+
+创建一个带分区表的镜像,并创建一个分区。
+
+```shell
+$ cd ./image
+$ qemu-img create boot.img 1g
+$ fdisk boot.img
+# 创建 GPT 分区表
+> g
+# 新建一个分区
+> n
+# 保存
+> w
+```
+
+( fdisk 的提示全选是和默认项即可。)
+
+挂载本地回环设备:
+
+```shell
+$ sudo losetup --find --show -P ./boot.img
+```
+
+以下假设挂载的本地回环设备为 `/dev/loop1`。
+
+将给定的 boot.ext4 写入该分区:
+
+```shell
+$ dd if=./boot.ext4 of=/dev/loop1p1
+```
+
+挂载该分区:
+
+```shell
+$ mkdir boot
+$ mount /dev/loop1p1 ./boot
+```
+
+创建 `./boot/extlinux/extlinux.conf`,并写入以下内容:
+
+```shell
+default polyOS-RISC-V
+label   polyOS-RISC-V
+    kernel /Image
+    initrd /ramdisk.img
+    append 'loglevel=1 ip=192.168.137.2:192.168.137.1:192.168.137.1:255.255.255.0::eth0:off sn=0023456789 console=tty0,115200 console=ttyS0,115200 init=/bin/init ohos.boot.hardware=virt root=/dev/ram0 rw ohos.required_mount.system=/dev/block/vdb@/usr@ext4@ro,barrier=1@wait,required ohos.required_mount.vendor=/dev/block/vdc@/vendor@ext4@ro,barrier=1@wait,required ohos.required_mount.sys_prod=/dev/block/vde@/sys_prod@ext4@ro,barrier=1@wait,required ohos.required_mount.chip_prod=/dev/block/vdf@/chip_prod@ext4@ro,barrier=1@wait,required ohos.required_mount.data=/dev/block/vdd@/data@ext4@nosuid,nodev,noatime,barrier=1,data=ordered,noauto_da_alloc@wait,reservedsize=1073741824 ohos.required_mount.misc=/dev/block/vda@/misc@none@none=@wait,required'
+```
+
+卸载相关分区和本地回环设备:
+
+```shell
+$ umount ./boot
+$ losetup -d /dev/loop1
+```
+
+### USE Qemu to bootup
+
+使用 qemu 启动:
+```shell
+$ cd $workdir/image
+image_path=`pwd`
+qemu-system-riscv64 \
+    -name PolyOS-Mobile \
+    -machine virt \
+    -m 4096\
+    -smp 4 \
+    -no-reboot \
+	-bios ../u-boot/spl/u-boot-spl \
+	-device loader,file=../u-boot/u-boot.itb,addr=0x80200000 \
+    -drive if=none,file=${image_path}/boot.img,format=raw,id=boot,index=6 \
+	-device ahci,id=ahci -device ide-hd,bus=ahci.0,drive=boot \
+    -drive if=none,file=${image_path}/updater.img,format=raw,id=updater,index=5 \
+    -device virtio-blk-device,drive=updater \
+    -drive if=none,file=${image_path}/system.img,format=raw,id=system,index=4 \
+    -device virtio-blk-device,drive=system \
+    -drive if=none,file=${image_path}/vendor.img,format=raw,id=vendor,index=3 \
+    -device virtio-blk-device,drive=vendor \
+    -drive if=none,file=${image_path}/userdata.img,format=raw,id=userdata,index=2 \
+    -device virtio-blk-device,drive=userdata \
+    -drive if=none,file=${image_path}/sys_prod.img,format=raw,id=sys-prod,index=1 \
+    -device virtio-blk-device,drive=sys-prod \
+    -drive if=none,file=${image_path}/chip_prod.img,format=raw,id=chip-prod,index=0 \
+    -device virtio-blk-device,drive=chip-prod \
+    -nographic \
+    -device virtio-gpu-pci,xres=486,yres=864,max_outputs=1,addr=08.0 \
+    -monitor telnet:127.0.0.1:55555,server,nowait \
+    -device virtio-mouse-pci \
+    -device virtio-keyboard-pci \
+    -device es1370 \
+    -k en-us \
+    -display sdl,gl=off
+```

+ 319 - 0
prototyper/docs/booting-test-kernel-in-qemu-using-uboot-and-rustsbi.md

@@ -0,0 +1,319 @@
+# 使用RustSBI & U-Boot SPL在QEMU中启动Test Kernel
+
+本教程给出了使用RustSBI和U-Boot在QEMU中启动Test Kernel的基本流程。
+
+其中启动流程分为两种类型:
+1. 只使用U-Boot SPL的启动流程
+2. 同时使用U-Boot SPL和U-Boot的启动流程。
+
+请读者在其主机上安装必要的软件来尝试本教程的脚本。本教程是在Arch Linux上开发的。
+
+[环境配置](#环境配置)小节给出了本教程的环境配置方法,用户在使用本教程时需要先完成环境配置小节内容。
+
+[使用U-Boot SPL启动Test Kernel](#使用U-Boot-SPL启动Test-Kernel)小节给出了只使用U-Boot SPL的启动流程。
+
+[使用U-Boot SPL和U-Boot启动Test Kernel](#使用U-Boot-SPL和U-Boot启动Test-Kernel)小节给出了同时使用U-Boot SPL和U-Boot的启动流程。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+| riscv64-linux-gnu-gcc | 14.1.0  |
+|  qemu-system-riscv64  |  9.0.1  |
+|  RustSBI Prototyper   |  0.0.0  |
+|        U-Boot         | 2024.04 |
+|     Linux Kernel      |   6.2   |
+|        busybox        | 1.36.0  |
+
+## 环境配置
+
+### 安装交叉编译器、QEMU和相关依赖
+
+For Arch Linux:
+
+``` shell
+$ sudo pacman -S git riscv64-linux-gnu-gcc qemu-system-riscv uboot-tools
+```
+
+For Ubuntu:
+
+``` shell
+$ sudo apt-get update && sudo apt-get upgrade
+$ sudo apt-get install git qemu-system-misc gcc-riscv64-linux-gnu u-boot-tools
+```
+
+#### 测试是否成功安装
+
+For riscv64-linux-gnu-gcc:
+
+``` shell
+$ riscv64-linux-gnu-gcc --version
+```
+
+它将输出以下版本信息
+
+```
+riscv64-linux-gnu-gcc (GCC) 14.1.0
+Copyright (C) 2024 Free Software Foundation, Inc.
+This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+```
+
+For QEMU:
+
+``` shell
+$ qemu-system-riscv64 --version
+```
+
+它将输出以下版本信息
+
+```
+QEMU emulator version 9.0.1
+Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers
+```
+
+### 准备RustSBI Prototyper, Test Kernel, U-Boot源码
+
+创建工作目录并进入该目录
+
+``` shell
+$ mkdir workshop && cd workshop
+```
+
+Clone RustSBI Prototyper
+
+``` shell
+$ git clone https://github.com/rustsbi/prototyper.git && cd prototyper && git checkout main && cd ..
+```
+
+Clone U-Boot
+
+``` shell
+$ git clone https://github.com/u-boot/u-boot.git && cd u-boot && git checkout v2024.04 && cd ..
+```
+
+## 使用U-Boot SPL启动Test Kernel
+### 编译RustSBI  Prototyper和Test Kernel
+
+进入prototyper目录
+
+``` shell
+$ cd prototyper
+```
+
+编译RustSBI Prototyper和Test Kernel
+
+``` shell
+$ cargo prototyper
+$ cargo test-kernel --pack
+```
+
+本小节将使用二进制文件 `./target/riscv64imac-unknown-none-elf/release/rustsbi-test-kernel.itb`。
+
+### 编译U-Boot SPL
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin
+```
+
+生成`.config`文件
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+# add bootcmd value
+$ make menuconfig
+```
+
+编译U-Boot
+
+``` shell
+# To build U-Boot
+$ make -j$(nproc)
+```
+
+本小节将使用二进制文件 `./spl/u-boot-spl`。
+
+### 使用RustSBI原型系统和U-Boot启动Linux Kernel
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 -M virt -smp 1 -m 256M -nographic \
+          -bios ./u-boot/spl/u-boot-spl \
+          -device loader,file=./prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-test-kernel.itb,addr=0x80200000
+```
+
+## 使用U-Boot SPL和U-Boot启动Test Kernel
+### 编译RustSBI  Prototyper和Test Kernel
+
+进入prototyper目录
+
+``` shell
+$ cd prototyper
+```
+
+编译RustSBI Prototyper和Test Kernel
+
+``` shell
+$ cargo make prototyper
+$ cargo make test-kernel
+```
+本小节将使用二进制文件 `./target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin`和`./target/riscv64imac-unknown-none-elf/release/rustsbi-test-kernel.bin`。
+
+### 编译U-Boot SPL
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin
+```
+
+生成`.config`文件
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+# add bootcmd value
+$ make menuconfig
+```
+
+U-Boot 配置选项将加载到终端。导航到 `Boot options` $\rightarrow$ `bootcmd value` 并将以下内容写入 `bootcmd` 值:
+
+```
+ext4load virtio 0:1 84000000 rustsbi-test-kernel.bin; booti 0x84000000 - ${fdtcontroladdr}
+```
+
+编译U-Boot
+
+``` shell
+# To build U-Boot
+$ make -j$(nproc)
+```
+
+本小节将使用二进制文件 `./spl/u-boot-spl`和`./u-boot.itb `。
+
+### 创建启动盘
+在`workshop`目录运行以下命令来创建一个256 MB的磁盘镜像
+
+``` shell
+# Create a 256 MB disk image
+$ qemu-img create test-kernel.img 256m
+```
+
+#### 创建分区
+
+将在磁盘映像`test-kernel.img`上创建1个分区,这个分区是可引导的。
+
+`parted`命令将用于在镜像`test-kernel.img`中创建分区。在镜像中创建分区表:
+
+``` shell
+$ sudo parted test-kernel.img mklabel gpt
+```
+
+现在`test-kernel.img`中有一个分区表。将`test-kernel.img`挂载为loop device,以便它可以用作块设备。将`test-kernel.img`挂载为块设备将允许在其中创建分区。
+
+``` shell
+# Attach test-kernel.img with the first available loop device
+$ sudo losetup --find --show test-kernel.img
+```
+
+> - `find`:查找第一个未使用的loop device
+> - `show`:显示`test-kernel.img`附加到的loop device的名称
+
+记下循环设备的完整路径。在本教程中它是`/dev/loop0`。对`/dev/loop0`的操作将会对`test-kernel.img`进行操作。
+
+对`/dev/loop0`分区
+
+``` shell
+# Create a couple of primary partitions
+$ sudo parted --align minimal /dev/loop0 mkpart primary ext4 0 100%
+
+$ sudo parted /dev/loop0 print
+```
+
+#### 格式化分区
+
+通过以下命令查看分区:
+
+``` shell
+$ ls -l /dev/loop0*
+```
+
+在本教程中,分区为`/dev/loop0p1`。
+
+格式化分区并创建`ext4`文件系统,同时将分区设置为可引导分区。
+
+``` shell
+$ sudo mkfs.ext4 /dev/loop0p1
+
+# Mark first partition as bootable
+$ sudo parted /dev/loop0 set 1 boot on
+```
+
+#### 将Linux Kernel和根文件系统拷贝进启动盘
+
+``` shell
+# Mount the 1st partition
+$ sudo mkdir test-kernel
+$ sudo mount /dev/loop0p1 test-kernel
+$ cd test-kernel
+```
+拷贝Linux Kernel镜像
+``` shell
+$ sudo cp ../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-test-kernel.bin .
+```
+
+卸载`test-kernel`
+
+``` shell
+$ cd workshop
+$ sudo umount test-kernel
+```
+
+将`/dev/loop0`分离
+
+``` shell
+$ sudo losetup -d /dev/loop0
+```
+
+### 使用RustSBI 原型系统和U-Boot启动Linux Kernel
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 -M virt -smp 1 -m 256M -nographic \
+          -bios ./u-boot/spl/u-boot-spl \
+          -device loader,file=./u-boot/u-boot.itb,addr=0x80200000 \
+          -blockdev driver=file,filename=./test-kernel.img,node-name=hd0 \
+          -device virtio-blk-device,drive=hd0
+```

+ 101 - 0
prototyper/docs/booting-ubuntu-24.04.1-in-qemu-using-edk2-and-opensbi.md

@@ -0,0 +1,101 @@
+# 使用 OpenSBI & EDK2 在 QEMU 中启动 Ubuntu 24.04.1
+
+本教程给出了使用 OpenSBI 和 EDK II 在 QEMU 中启动 Ubuntu 24.04.1 的基本流程。
+
+请读者在其主机上安装必要的软件来尝试本教程。本教程是在 Arch Linux 上开发的,建议读者使用 x86_64 平台上的 Linux 环境按照本教程进行尝试。
+
+本教程使用软件版本如下:
+
+|         软件          |     版本     |
+| :-------------------: | :----------: |
+| riscv64-linux-gnu-gcc |    14.2.0    |
+|  qemu-system-riscv64  |     9.2.0    |
+|       OpenSBI         |      1.6     |
+|        EDK II         | stable202411 |
+
+## 准备 Opensbi,EDK II,Ubuntu 24.04.1
+创建工作目录并进入该目录
+
+``` shell
+$ mkdir workshop && cd workshop
+```
+
+### Clone Opensbi
+
+``` shell
+$ git clone -b v1.6 https://github.com/riscv/opensbi.git
+```
+
+### Clone EDK II
+
+``` shell
+$ git clone -b edk2-stable202411 --recurse-submodule [email protected]:tianocore/edk2.git
+```
+
+### 下载 Ubuntu 24.04.1 镜像文件
+
+下载链接:[Ubuntu 24.04.1](https://cdimage.ubuntu.com/releases/24.04.1/release/ubuntu-24.04.1-preinstalled-server-riscv64.img.xz)
+``` shell
+$ wget https://cdimage.ubuntu.com/releases/24.04.1/release/ubuntu-24.04.1-preinstalled-server-riscv64.img.xz
+$ xz -d ubuntu-24.04.1-preinstalled-server-riscv64.img.xz
+```
+
+- The password of the default user `ubuntu` is `ubuntu`.
+- 登录后应会被要求更改登录密码。
+- 可以通过 `sudo` 更改 root 密码。
+
+## 编译 EDK II
+
+设置环境变量
+
+``` shell
+$ export WORKSPACE=`pwd`
+$ export GCC5_RISCV64_PREFIX=riscv64-linux-gnu-
+$ export PACKAGES_PATH=$WORKSPACE/edk2
+$ export EDK_TOOLS_PATH=$WORKSPACE/edk2/BaseTools
+$ source edk2/edksetup.sh --reconfig
+```
+
+编译 BaseTools
+
+``` shell
+$ make -C edk2/BaseTools
+```
+
+编译 RiscVVirtQemu
+
+``` shell
+$ source edk2/edksetup.sh BaseTools
+$ build -a RISCV64 --buildtarget RELEASE -p OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc -t GCC5
+```
+
+## 编译 OpenSBI
+
+``` shell
+$ make -C opensbi \
+    -j $(nproc) \
+    CROSS_COMPILE=riscv64-linux-gnu- \
+    PLATFORM=generic
+```
+
+## 使用 OpenSBI 和 EDK II 启动 Ubuntu 24.04.1
+
+将 RISCV_VIRT_CODE.fd 和 RISCV_VIRT_VARS.fd 填充至 32M,以适应 RISC-V QEMU pflash devices 的需求
+
+``` shell
+$ truncate -s 32M Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_CODE.fd
+$ truncate -s 32M Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_VARS.fd
+```
+
+启动 qemu-system-riscv64
+
+``` shell
+$ qemu-system-riscv64  \
+    -M virt,pflash0=pflash0,pflash1=pflash1,acpi=off \
+    -m 4096 -smp 2  -nographic \
+    -bios opensbi/build/platform/generic/firmware/fw_dynamic.bin \
+    -blockdev node-name=pflash0,driver=file,read-only=on,filename=Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_CODE.fd  \
+    -blockdev node-name=pflash1,driver=file,filename=Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_VARS.fd \
+    -device virtio-blk-device,drive=hd0  \
+    -drive file=ubuntu-24.04.1-preinstalled-server-riscv64.img,format=raw,id=hd0,if=none
+```

+ 107 - 0
prototyper/docs/booting-ubuntu-24.04.1-in-qemu-using-uboot-and-rustsbi.md

@@ -0,0 +1,107 @@
+# 使用RustSBI & U-Boot在QEMU中启动 Ubuntu 24.04.1
+
+本教程给出了使用RustSBI和U-Boot在QEMU中启动Ubuntu 24.04.1的基本流程。
+
+本教程使用软件版本如下:
+
+|         软件          |  版本   |
+| :-------------------: | :-----: |
+| riscv64-linux-gnu-gcc | 14.1.0  |
+|  qemu-system-riscv64  |  9.0.1  |
+|  RustSBI Prototyper   |  0.0.0  |
+|        U-Boot         | 2024.04 |
+
+## 准备RustSBI Prototyper, U-Boot ,Ubuntu 24.04.1
+创建工作目录并进入该目录
+
+``` shell
+$ mkdir workshop && cd workshop
+```
+
+### Clone RustSBI Prototyper
+
+``` shell
+$ git clone https://github.com/rustsbi/prototyper.git && cd prototyper && git checkout main && cd ..
+```
+
+### Clone U-Boot
+
+``` shell
+$ git clone https://github.com/u-boot/u-boot.git && cd u-boot && git checkout v2024.04 && cd ..
+```
+### 下载并扩容 Ubuntu 24.04.1 磁盘镜像文件
+
+下载链接:[Ubuntu 24.04.1](https://cdimage.ubuntu.com/releases/noble/release/ubuntu-24.04.1-preinstalled-server-riscv64.img.xz)
+```shell
+ $ unar ubuntu-24.04.1-preinstalled-server-riscv64.img.xz
+ $ qemu-img resize -f raw ubuntu-24.04.1-preinstalled-server-riscv64.img +5G
+```
+
+- The password of the default user `ubuntu` is `ubuntu`.
+- 登录后应会被要求更改登录密码。
+- 可以通过 `sudo` 更改 root 密码。
+
+
+## 编译RustSBI  Prototyper
+
+进入prototyper目录
+
+``` shell
+$ cd prototyper
+```
+
+编译RustSBI  Prototyper
+
+``` shell
+$ cargo prototyper
+```
+
+## 编译U-Boot SPL
+
+进入U-Boot目录
+
+``` shell
+$ cd u-boot
+```
+
+导出环境变量
+
+``` shell
+$ export ARCH=riscv
+$ export CROSS_COMPILE=riscv64-linux-gnu-
+$ export OPENSBI=../prototyper/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper.bin 
+```
+
+生成`.config`文件,编译U-Boot
+
+``` shell
+# To generate .config file out of board configuration file
+$ make qemu-riscv64_spl_defconfig
+$ make -j$(nproc)
+```
+
+## 使用RustSBI 原型系统和U-Boot启动 Ubuntu 24.04.1
+
+进入`workshop`目录
+
+``` shell
+$ cd workshop
+```
+
+运行下面命令
+
+``` shell
+$ qemu-system-riscv64 \
+    -nographic -machine virt \
+    -smp 4 -m 8G \
+    -bios ./u-boot/spl/u-boot-spl  \
+    -device loader,file=./u-boot/u-boot.itb,addr=0x80200000 \
+    -drive file=ubuntu-24.04.1-preinstalled-server-riscv64.img,format=raw,if=none,id=hd0 \
+    -object rng-random,filename=/dev/urandom,id=rng0 \
+    -device virtio-vga \
+    -device virtio-rng-device,rng=rng0 \
+    -device virtio-blk-device,drive=hd0 \
+    -device virtio-net-device,netdev=usernet \
+    -netdev user,id=usernet,hostfwd=tcp::12055-:22 \
+    -device qemu-xhci -usb -device usb-kbd -device usb-tablet
+```

+ 27 - 0
prototyper/docs/openwrt-patch.patch

@@ -0,0 +1,27 @@
+diff --git a/package/boot/uboot-sifiveu/patches/200-invalid-version.patch b/package/boot/uboot-sifiveu/patches/200-invalid-version.patch
+new file mode 100644
+index 0000000000..dd52b479f8
+--- /dev/null
++++ b/package/boot/uboot-sifiveu/patches/200-invalid-version.patch
+@@ -0,0 +1,11 @@
++--- a/scripts/dtc/pylibfdt/Makefile
+++++ b/scripts/dtc/pylibfdt/Makefile
++@@ -17,7 +17,7 @@
++       cmd_pymod = unset CROSS_COMPILE; unset CFLAGS; \
++ 		CC="$(HOSTCC)" LDSHARED="$(HOSTCC) -shared " \
++ 		LDFLAGS="$(HOSTLDFLAGS)" \
++-		VERSION="u-boot-$(UBOOTVERSION)" \
+++		VERSION="$(UBOOTVERSION)" \
++ 		CPPFLAGS="$(HOSTCFLAGS) -I$(LIBFDT_srcdir)" OBJDIR=$(obj) \
++ 		SOURCES="$(PYLIBFDT_srcs)" \
++ 		SWIG_OPTS="-I$(LIBFDT_srcdir) -I$(LIBFDT_srcdir)/.." \
+diff --git a/target/linux/sifiveu/base-files/etc/inittab b/target/linux/sifiveu/base-files/etc/inittab
+index 69f97c47c8..0d8ead1d91 100644
+--- a/target/linux/sifiveu/base-files/etc/inittab
++++ b/target/linux/sifiveu/base-files/etc/inittab
+@@ -1,4 +1,5 @@
+ ::sysinit:/etc/init.d/rcS S boot
+ ::shutdown:/etc/init.d/rcS K shutdown
+ ttySIF0::askfirst:/usr/libexec/login.sh
++ttyS0::askfirst:/usr/libexec/login.sh
+ tty1::askfirst:/usr/libexec/login.sh

+ 40 - 0
prototyper/prototyper/Cargo.toml

@@ -0,0 +1,40 @@
+cargo-features = ["per-package-target"]
+
+[package]
+name = "rustsbi-prototyper"
+version = "0.0.0"
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+forced-target = "riscv64imac-unknown-none-elf"
+
+[dependencies]
+aclint = "=0.1.0"
+log = "0.4"
+panic-halt = "1.0.0"
+riscv = "0.12.1"
+sifive-test-device = "0.0.0"
+spin = "0.9.8"
+uart16550 = "0.0.1"
+riscv-decode = "0.2.1"
+cfg-if = "1.0.0"
+buddy_system_allocator = "0.11.0"
+rustsbi = { version = "0.4.0", features = ["machine"] }
+sbi-spec = { version = "0.0.8", features = ["legacy"] }
+serde = { version = "1.0.202", default-features = false, features = ["derive"] }
+fast-trap = { version = "0.1.0",  features = ["riscv-m"] }
+serde-device-tree = { git = "https://github.com/rustsbi/serde-device-tree", default-features = false }
+uart_xilinx = { git = "https://github.com/duskmoon314/uart-rs/" }
+xuantie-riscv = { git= "https://github.com/rustsbi/xuantie" }
+bouffalo-hal = { git = "https://github.com/rustsbi/bouffalo-hal", rev = "968b949", features = ["bl808"] }
+
+[[bin]]
+name = "rustsbi-prototyper"
+test = false
+bench = false
+
+[features]
+nemu = []
+payload = []
+jump = []
+fdt = []

+ 82 - 0
prototyper/prototyper/build.rs

@@ -0,0 +1,82 @@
+use std::{env, path::PathBuf};
+
+fn main() {
+    let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+    let ld = &out.join("rustsbi-prototyper.ld");
+
+    std::fs::write(ld, LINKER_SCRIPT).unwrap();
+
+    println!("cargo:rerun-if-env-changed=RUST_LOG,PROTOTYPER_FDT,PROTOTYPER_IMAGE");
+    println!("cargo:rustc-link-arg=-T{}", ld.display());
+    println!("cargo:rustc-link-search={}", out.display());
+}
+
+const LINKER_SCRIPT: &[u8] = b"OUTPUT_ARCH(riscv)
+ENTRY(_start) 
+SECTIONS {
+    . = 0x80000000;
+
+    . = ALIGN(0x1000); /* Need this to create proper sections */
+
+    sbi_start = .;
+    .text : ALIGN(0x1000) { 
+        *(.text.entry)
+        *(.text .text.*)
+    }
+
+    .rodata : ALIGN(0x1000) { 
+        sbi_rodata_start = .;
+        *(.rodata .rodata.*)
+        *(.srodata .srodata.*)
+        . = ALIGN(0x1000);  
+    } 
+
+    .dynsym : ALIGN(8) {
+        *(.dynsym)
+    }
+
+    .rela.dyn : ALIGN(8) {
+        __rel_dyn_start = .;
+        *(.rela*)
+        __rel_dyn_end = .;
+    }
+
+    sbi_rodata_end = .;
+
+	/*
+	 * PMP regions must be to be power-of-2. RX/RW will have separate
+	 * regions, so ensure that the split is power-of-2.
+	 */
+	. = ALIGN(1 << LOG2CEIL((SIZEOF(.rodata) + SIZEOF(.text)
+				+ SIZEOF(.dynsym) + SIZEOF(.rela.dyn))));
+
+    .data : ALIGN(0x1000) { 
+        sbi_data_start = .;
+        *(.data .data.*)
+        *(.sdata .sdata.*)
+        . = ALIGN(0x1000); 
+        sbi_data_end = .;
+    }
+    sidata = LOADADDR(.data);
+
+    .bss (NOLOAD) : ALIGN(0x1000) {  
+        *(.bss.stack)
+        sbi_heap_start = .;
+        *(.bss.heap)
+        sbi_heap_end = .;
+        sbi_bss_start = .;
+        *(.bss .bss.*)
+        *(.sbss .sbss.*)
+        sbi_bss_end = .;
+    } 
+    /DISCARD/ : {
+        *(.eh_frame)
+    }
+
+	. = ALIGN(0x1000); /* Need this to create proper sections */
+    sbi_end = .;
+
+    .text 0x80200000 : ALIGN(0x1000) {
+        *(.payload)
+    }
+}";

+ 16 - 0
prototyper/prototyper/src/cfg.rs

@@ -0,0 +1,16 @@
+/// The address where the SBI link start.
+pub const SBI_LINK_START_ADDRESS: usize = 0x80000000;
+/// Maximum number of supported harts.
+pub const NUM_HART_MAX: usize = 8;
+/// Stack size per hart (hardware thread) in bytes.
+pub const LEN_STACK_PER_HART: usize = 16 * 1024;
+/// Heap Size of SBI firmware.
+pub const HEAP_SIZE: usize = 32 * 1024;
+/// Platform page size.
+pub const PAGE_SIZE: usize = 4096;
+/// TLB_FLUSH_LIMIT defines the TLB refresh range limit.
+/// If the TLB refresh range is greater than TLB_FLUSH_LIMIT, the entire TLB is refreshed.
+pub const TLB_FLUSH_LIMIT: usize = 4 * PAGE_SIZE;
+
+#[cfg(feature = "jump")]
+pub const JUMP_ADDRESS: usize = 0x50000000;

+ 92 - 0
prototyper/prototyper/src/devicetree.rs

@@ -0,0 +1,92 @@
+use serde::Deserialize;
+use serde_device_tree::{
+    Dtb, DtbPtr,
+    buildin::{Node, NodeSeq, Reg, StrSeq},
+};
+
+use core::ops::Range;
+
+/// Root device tree structure containing system information.
+#[derive(Deserialize)]
+pub struct Tree<'a> {
+    /// Optional model name string.
+    pub model: Option<StrSeq<'a>>,
+    /// Memory information.
+    pub memory: NodeSeq<'a>,
+    /// CPU information.
+    pub cpus: Cpus<'a>,
+}
+
+/// CPU information container.
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct Cpus<'a> {
+    /// Sequence of CPU nodes.
+    pub cpu: NodeSeq<'a>,
+}
+
+/// Individual CPU node information.
+#[derive(Deserialize, Debug)]
+pub struct Cpu<'a> {
+    /// RISC-V ISA extensions supported by this CPU.
+    #[serde(rename = "riscv,isa-extensions")]
+    pub isa_extensions: Option<StrSeq<'a>>,
+    #[serde(rename = "riscv,isa")]
+    pub isa: Option<StrSeq<'a>>,
+    /// CPU register information.
+    pub reg: Reg<'a>,
+}
+
+/// Generic device node information.
+#[allow(unused)]
+#[derive(Deserialize, Debug)]
+pub struct Device<'a> {
+    /// Device register information.
+    pub reg: Reg<'a>,
+}
+
+/// Memory range.
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct Memory<'a> {
+    pub reg: Reg<'a>,
+}
+
+/// Errors that can occur during device tree parsing.
+pub enum ParseDeviceTreeError {
+    /// Invalid device tree format.
+    Format,
+}
+
+pub fn parse_device_tree(opaque: usize) -> Result<Dtb, ParseDeviceTreeError> {
+    let Ok(ptr) = DtbPtr::from_raw(opaque as *mut _) else {
+        return Err(ParseDeviceTreeError::Format);
+    };
+    let dtb = Dtb::from(ptr);
+    Ok(dtb)
+}
+
+pub fn get_compatible_and_range<'de>(node: &Node) -> Option<(StrSeq<'de>, Range<usize>)> {
+    let compatible = node
+        .get_prop("compatible")
+        .map(|prop_item| prop_item.deserialize::<StrSeq<'de>>());
+    let regs = node
+        .get_prop("reg")
+        .map(|prop_item| {
+            let reg = prop_item.deserialize::<serde_device_tree::buildin::Reg>();
+            if let Some(range) = reg.iter().next() {
+                return Some(range);
+            }
+            None
+        })
+        .map_or_else(|| None, |v| v);
+    if let Some(compatible) = compatible {
+        if let Some(regs) = regs {
+            Some((compatible, regs.0))
+        } else {
+            None
+        }
+    } else {
+        None
+    }
+}

+ 125 - 0
prototyper/prototyper/src/fail.rs

@@ -0,0 +1,125 @@
+use crate::riscv::current_hartid;
+use serde_device_tree::Dtb;
+
+use crate::devicetree;
+
+use riscv::interrupt::machine::{Exception, Interrupt};
+use riscv::register::{mcause::Trap, mepc, mtval};
+
+#[cfg(all(feature = "payload", feature = "jump"))]
+compile_error!("feature \"payload\" and feature \"jump\" cannot be enabled at the same time");
+
+#[panic_handler]
+fn panic(info: &core::panic::PanicInfo) -> ! {
+    use ::riscv::register::*;
+    error!("Hart {} {info}", current_hartid());
+    error!("-----------------------------");
+    error!("mcause:  {:?}", mcause::read().cause());
+    error!("mepc:    {:#018x}", mepc::read());
+    error!("mtval:   {:#018x}", mtval::read());
+    error!("-----------------------------");
+    error!("System shutdown scheduled due to RustSBI panic");
+    loop {}
+}
+
+pub fn unsupported_trap(trap: Option<Trap<Interrupt, Exception>>) -> ! {
+    error!("-----------------------------");
+    error!("trap:    {trap:?}");
+    error!("mepc:    {:#018x}", mepc::read());
+    error!("mtval:   {:#018x}", mtval::read());
+    error!("-----------------------------");
+    panic!("Stopped with unsupported trap")
+}
+
+/// Handles device tree format parsing errors by logging and resetting.
+#[cold]
+pub fn device_tree_format(_err: devicetree::ParseDeviceTreeError) -> Dtb {
+    loop {
+        core::hint::spin_loop()
+    }
+}
+
+#[cold]
+pub fn device_tree_deserialize_root<'a>(
+    _err: serde_device_tree::error::Error,
+) -> serde_device_tree::buildin::Node<'a> {
+    loop {
+        core::hint::spin_loop()
+    }
+}
+
+cfg_if::cfg_if! {
+    if #[cfg(feature = "payload")] {
+    } else if #[cfg(feature = "jump")] {
+    } else {
+        use crate::firmware::dynamic;
+        use crate::sbi::reset;
+        use riscv::register::mstatus;
+        /// Handles invalid dynamic information data by logging details and resetting.
+        #[cold]
+        pub fn invalid_dynamic_data(err: dynamic::DynamicError) -> (mstatus::MPP, usize) {
+            error!("Invalid data in dynamic information:");
+            if err.invalid_mpp {
+                error!("* dynamic information contains invalid privilege mode");
+            }
+            if err.invalid_next_addr {
+                error!("* dynamic information contains invalid next jump address");
+            }
+            let explain_next_mode = match err.bad_info.next_mode {
+                3 => "Machine",
+                1 => "Supervisor",
+                0 => "User",
+                _ => "Invalid",
+            };
+            error!(
+                "@ help: dynamic information contains magic value 0x{:x}, version {}, next jump address 0x{:x}, next privilege mode {} ({}), options {:x}, boot hart ID {}",
+                err.bad_info.magic, err.bad_info.version, err.bad_info.next_addr, err.bad_info.next_mode, explain_next_mode, err.bad_info.options, err.bad_info.boot_hart
+            );
+            reset::fail()
+        }
+
+        /// Handles case where dynamic information is not available by logging details and resetting.
+        #[cold]
+        pub fn no_dynamic_info_available(err: dynamic::DynamicReadError) -> dynamic::DynamicInfo {
+            if let Some(bad_paddr) = err.bad_paddr {
+                error!(
+                    "No dynamic information available at address 0x{:x}",
+                    bad_paddr
+                );
+            } else {
+                error!("No valid dynamic information available:");
+                if let Some(bad_magic) = err.bad_magic {
+                    error!(
+                        "* tried to identify dynamic information, but found invalid magic number 0x{:x}",
+                        bad_magic
+                    );
+                }
+                if let Some(bad_version) = err.bad_version {
+                    error!("* tries to identify version of dynamic information, but the version number {} is not supported", bad_version);
+                }
+                if err.bad_magic.is_none() {
+                    error!("@ help: magic number is valid")
+                }
+                if err.bad_version.is_none() {
+                    error!("@ help: dynamic information version is valid")
+                }
+            }
+            reset::fail()
+        }
+
+        /// Fallback function that returns default dynamic info with boot_hart set to MAX.
+        ///
+        /// Used when dynamic info read fails but execution should continue.
+        #[cold]
+        pub fn use_lottery(_err: dynamic::DynamicReadError) -> dynamic::DynamicInfo {
+            dynamic::DynamicInfo {
+                magic: 0,
+                version: 0,
+                next_addr: 0,
+                next_mode: 0,
+                options: 0,
+                boot_hart: usize::MAX,
+            }
+        }
+    }
+}

+ 147 - 0
prototyper/prototyper/src/firmware/dynamic.rs

@@ -0,0 +1,147 @@
+//! Frequently used first boot stage dynamic information on RISC-V.
+
+use core::ops::Range;
+use core::sync::atomic::{AtomicBool, Ordering};
+
+use super::BootInfo;
+use crate::fail;
+use crate::riscv::current_hartid;
+
+use riscv::register::mstatus;
+
+/// Determine whether the current hart is boot hart.
+///
+/// Return true if the current hart is boot hart.
+pub fn is_boot_hart(nonstandard_a2: usize) -> bool {
+    // Track whether this is the first hart to boot
+    static GENESIS: AtomicBool = AtomicBool::new(true);
+
+    let info = read_paddr(nonstandard_a2).unwrap_or_else(fail::use_lottery);
+
+    // Determine if this is the boot hart based on hart ID
+    if info.boot_hart == usize::MAX {
+        // If boot_hart is MAX, use atomic bool to determine first hart
+        GENESIS.swap(false, Ordering::AcqRel)
+    } else {
+        // Otherwise check if current hart matches designated boot hart
+        current_hartid() == info.boot_hart
+    }
+}
+
+/// Gets boot information from nonstandard_a2 parameter.
+///
+/// Returns BootInfo containing next stage address and privilege mode.
+pub fn get_boot_info(nonstandard_a2: usize) -> BootInfo {
+    let dynamic_info = read_paddr(nonstandard_a2).unwrap_or_else(fail::no_dynamic_info_available);
+    let (mpp, next_addr) = mpp_next_addr(&dynamic_info).unwrap_or_else(fail::invalid_dynamic_data);
+    BootInfo {
+        next_address: next_addr,
+        mpp,
+    }
+}
+
+/// M-mode firmware dynamic information.
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct DynamicInfo {
+    /// Dynamic information magic value.
+    pub magic: usize,
+    /// Version of dynamic information.
+    pub version: usize,
+    /// Address of the next boot-loading stage.
+    pub next_addr: usize,
+    /// RISC-V privilege mode of the next boot-loading stage.
+    pub next_mode: usize,
+    /// M-mode firmware options; its definition varies between SBI implementations.
+    pub options: usize,
+    /// Boot hart ID of current environment.
+    pub boot_hart: usize,
+}
+
+// Definition of `boot_hart` can be found at:
+// https://github.com/riscv-software-src/opensbi/blob/019a8e69a1dc0c0f011fabd0372e1ba80e40dd7c/include/sbi/fw_dynamic.h#L75
+
+const DYNAMIC_INFO_INVALID_ADDRESSES: usize = 0x00000000;
+const NEXT_ADDR_VALID_ADDRESSES: Range<usize> = 0x80000000..0x90000000;
+pub(crate) const MAGIC: usize = 0x4942534f;
+const SUPPORTED_VERSION: Range<usize> = 0..3;
+
+/// Error type for dynamic info read failures.
+pub struct DynamicReadError {
+    pub bad_paddr: Option<usize>,
+    pub bad_magic: Option<usize>,
+    pub bad_version: Option<usize>,
+}
+
+// TODO: unconstrained lifetime
+/// Reads dynamic info from physical address.
+///
+/// Returns Result containing DynamicInfo or error details.
+pub fn read_paddr(paddr: usize) -> Result<DynamicInfo, DynamicReadError> {
+    let mut error = DynamicReadError {
+        bad_paddr: None,
+        bad_magic: None,
+        bad_version: None,
+    };
+    // check pointer before dereference.
+    if DYNAMIC_INFO_INVALID_ADDRESSES == paddr {
+        error.bad_paddr = Some(paddr);
+        return Err(error);
+    }
+    let ans = unsafe { *(paddr as *const DynamicInfo) };
+
+    // Validate magic number and version.
+    if ans.magic != MAGIC {
+        error.bad_magic = Some(ans.magic);
+    }
+    if !SUPPORTED_VERSION.contains(&ans.version) {
+        error.bad_version = Some(ans.version);
+    }
+    if error.bad_magic.is_some() || error.bad_version.is_some() {
+        return Err(error);
+    }
+    Ok(ans)
+}
+
+/// Error type for dynamic info validation failures.
+pub struct DynamicError<'a> {
+    pub invalid_mpp: bool,
+    pub invalid_next_addr: bool,
+    pub bad_info: &'a DynamicInfo,
+}
+
+/// Validates and extracts privilege mode and next address from dynamic info.
+///
+/// Returns Result containing tuple of (MPP, next_addr) or error details.
+pub fn mpp_next_addr(info: &DynamicInfo) -> Result<(mstatus::MPP, usize), DynamicError> {
+    let mut error = DynamicError {
+        invalid_mpp: false,
+        invalid_next_addr: false,
+        bad_info: info,
+    };
+
+    // fail safe, errors will be aggregated after whole checking process.
+    let next_addr_valid = NEXT_ADDR_VALID_ADDRESSES.contains(&info.next_addr);
+    let mpp_valid = matches!(info.next_mode, 0 | 1 | 3);
+
+    if !next_addr_valid {
+        error.invalid_next_addr = true;
+    }
+    if !mpp_valid {
+        error.invalid_mpp = true;
+    }
+
+    if !next_addr_valid || !mpp_valid {
+        return Err(error);
+    }
+
+    let mpp = match info.next_mode {
+        3 => mstatus::MPP::Machine,
+        1 => mstatus::MPP::Supervisor,
+        // pattern `_` avoids `unreachable!`` which introduces panic handler.
+        // pattern 0 and _
+        _ => mstatus::MPP::User,
+    };
+
+    Ok((mpp, info.next_addr))
+}

+ 20 - 0
prototyper/prototyper/src/firmware/jump.rs

@@ -0,0 +1,20 @@
+use core::sync::atomic::{AtomicBool, Ordering};
+use riscv::register::mstatus;
+
+use super::BootInfo;
+use crate::cfg::JUMP_ADDRESS;
+
+/// Determine whether the current hart is boot hart.
+///
+/// Return true if the current hart is boot hart.
+pub fn is_boot_hart(_nonstandard_a2: usize) -> bool {
+    static GENESIS: AtomicBool = AtomicBool::new(true);
+    GENESIS.swap(false, Ordering::AcqRel)
+}
+
+pub fn get_boot_info(_nonstandard_a2: usize) -> BootInfo {
+    BootInfo {
+        next_address: JUMP_ADDRESS,
+        mpp: mstatus::MPP::Supervisor,
+    }
+}

+ 133 - 0
prototyper/prototyper/src/firmware/mod.rs

@@ -0,0 +1,133 @@
+cfg_if::cfg_if! {
+    if #[cfg(feature = "payload")] {
+        pub mod payload;
+        pub use payload::{get_boot_info, is_boot_hart};
+    } else if #[cfg(feature = "jump")] {
+        pub mod jump;
+        pub use jump::{get_boot_info, is_boot_hart};
+    } else {
+        pub mod dynamic;
+        pub use dynamic::{get_boot_info, is_boot_hart};
+    }
+}
+
+#[allow(unused)]
+use core::arch::{asm, naked_asm};
+use core::ops::Range;
+use riscv::register::mstatus;
+
+pub struct BootInfo {
+    pub next_address: usize,
+    pub mpp: mstatus::MPP,
+}
+
+pub struct BootHart {
+    pub fdt_address: usize,
+    pub is_boot_hart: bool,
+}
+
+#[naked]
+#[unsafe(link_section = ".rodata.fdt")]
+#[repr(align(16))]
+#[cfg(feature = "fdt")]
+pub extern "C" fn raw_fdt() {
+    unsafe { naked_asm!(concat!(".incbin \"", env!("PROTOTYPER_FDT_PATH"), "\""),) }
+}
+
+#[inline]
+#[cfg(feature = "fdt")]
+fn get_fdt_address() -> usize {
+    raw_fdt as usize
+}
+
+/// Gets boot hart information based on opaque and nonstandard_a2 parameters.
+///
+/// Returns a BootHart struct containing FDT address and whether this is the boot hart.
+#[allow(unused_mut, unused_assignments)]
+pub fn get_boot_hart(opaque: usize, nonstandard_a2: usize) -> BootHart {
+    let is_boot_hart = is_boot_hart(nonstandard_a2);
+
+    let mut fdt_address = opaque;
+
+    #[cfg(feature = "fdt")]
+    {
+        fdt_address = get_fdt_address();
+    }
+
+    BootHart {
+        fdt_address,
+        is_boot_hart,
+    }
+}
+
+static mut SBI_START_ADDRESS: usize = 0;
+static mut SBI_END_ADDRESS: usize = 0;
+static mut RODATA_START_ADDRESS: usize = 0;
+static mut RODATA_END_ADDRESS: usize = 0;
+
+pub fn set_pmp(memory_range: &Range<usize>) {
+    unsafe {
+        // [0..memory_range.start] RW
+        // [memory_range.start..sbi_start] RWX
+        // [sbi_start..sbi_rodata_start] NONE
+        // [sbi_rodata_start..sbi_rodata_end] NONE
+        // [sbi_rodata_end..sbi_end] NONE
+        // [sbi_end..memory_range.end] RWX
+        // [memory_range.end..INF] RW
+        use riscv::register::*;
+
+        asm!("la {}, sbi_start", out(reg) SBI_START_ADDRESS, options(nomem));
+        asm!("la {}, sbi_end", out(reg) SBI_END_ADDRESS, options(nomem));
+        asm!("la {}, sbi_rodata_start", out(reg) RODATA_START_ADDRESS, options(nomem));
+        asm!("la {}, sbi_rodata_end", out(reg) RODATA_END_ADDRESS, options(nomem));
+
+        pmpcfg0::set_pmp(0, Range::OFF, Permission::NONE, false);
+        pmpaddr0::write(0);
+        pmpcfg0::set_pmp(1, Range::TOR, Permission::RW, false);
+        pmpaddr1::write(memory_range.start >> 2);
+        pmpcfg0::set_pmp(2, Range::TOR, Permission::RWX, false);
+        pmpaddr2::write(SBI_START_ADDRESS >> 2);
+        pmpcfg0::set_pmp(3, Range::TOR, Permission::NONE, false);
+        pmpaddr3::write(RODATA_START_ADDRESS >> 2);
+        pmpcfg0::set_pmp(4, Range::TOR, Permission::RW, false);
+        pmpaddr4::write(RODATA_END_ADDRESS >> 2);
+        pmpcfg0::set_pmp(5, Range::TOR, Permission::NONE, false);
+        pmpaddr5::write(SBI_END_ADDRESS >> 2);
+        pmpcfg0::set_pmp(6, Range::TOR, Permission::RWX, false);
+        pmpaddr6::write(memory_range.end >> 2);
+        pmpcfg0::set_pmp(7, Range::TOR, Permission::RW, false);
+        pmpaddr7::write(usize::MAX >> 2);
+    }
+}
+
+pub fn log_pmp_cfg(memory_range: &Range<usize>) {
+    unsafe {
+        info!("PMP Configuration");
+
+        info!(
+            "{:<10} {:<10} {:<15} {:<30}",
+            "PMP", "Range", "Permission", "Address"
+        );
+
+        info!("{:<10} {:<10} {:<15} 0x{:08x}", "PMP 0:", "OFF", "NONE", 0);
+        info!(
+            "{:<10} {:<10} {:<15} 0x{:08x} - 0x{:08x}",
+            "PMP 1-2:", "TOR", "RW/RWX", memory_range.start, SBI_START_ADDRESS
+        );
+        info!(
+            "{:<10} {:<10} {:<15} 0x{:08x} - 0x{:08x} - 0x{:08x}",
+            "PMP 3-5:", "TOR", "NONE/RW", RODATA_START_ADDRESS, RODATA_END_ADDRESS, SBI_END_ADDRESS
+        );
+        info!(
+            "{:<10} {:<10} {:<15} 0x{:08x}",
+            "PMP 6:", "TOR", "RWX", memory_range.end
+        );
+        info!(
+            "{:<10} {:<10} {:<15} 0x{:08x}",
+            "PMP 7:",
+            "TOR",
+            "RW",
+            usize::MAX
+        );
+    }
+}

+ 31 - 0
prototyper/prototyper/src/firmware/payload.rs

@@ -0,0 +1,31 @@
+use core::arch::naked_asm;
+use core::sync::atomic::{AtomicBool, Ordering};
+use riscv::register::mstatus;
+
+use super::BootInfo;
+
+/// Determine whether the current hart is boot hart.
+///
+/// Return true if the current hart is boot hart.
+pub fn is_boot_hart(_nonstandard_a2: usize) -> bool {
+    static GENESIS: AtomicBool = AtomicBool::new(true);
+    GENESIS.swap(false, Ordering::AcqRel)
+}
+
+pub fn get_boot_info(_nonstandard_a2: usize) -> BootInfo {
+    BootInfo {
+        next_address: get_image_address(),
+        mpp: mstatus::MPP::Supervisor,
+    }
+}
+
+#[naked]
+#[unsafe(link_section = ".payload")]
+pub extern "C" fn payload_image() {
+    unsafe { naked_asm!(concat!(".incbin \"", env!("PROTOTYPER_PAYLOAD_PATH"), "\""),) }
+}
+
+#[inline]
+fn get_image_address() -> usize {
+    payload_image as usize
+}

+ 49 - 0
prototyper/prototyper/src/macros.rs

@@ -0,0 +1,49 @@
+#[allow(unused)]
+macro_rules! print {
+    ($($arg:tt)*) => {
+        use core::fmt::Write;
+        if unsafe {$crate::platform::PLATFORM.have_console()} {
+            let console = unsafe { $crate::platform::PLATFORM.sbi.console.as_mut().unwrap() };
+            console.write_fmt(core::format_args!($($arg)*)).unwrap();
+            drop(console);
+        }
+    }
+}
+
+#[allow(unused)]
+macro_rules! println {
+    () => ($crate::print!("\n\r"));
+    ($($arg:tt)*) => {{
+        use core::fmt::Write;
+        if unsafe {$crate::platform::PLATFORM.have_console()} {
+            let console = unsafe { $crate::platform::PLATFORM.sbi.console.as_mut().unwrap() };
+            console.write_fmt(core::format_args!($($arg)*)).unwrap();
+            console.write_str("\n\r").unwrap();
+        }
+    }}
+}
+
+#[allow(unused)]
+macro_rules! has_csr {
+    ($($x: expr)*) => {{
+            use core::arch::asm;
+            use riscv::register::mtvec;
+            use crate::sbi::early_trap::expected_trap;
+            let res: usize;
+            unsafe {
+                // Backup old mtvec
+                let mtvec = mtvec::read().bits();
+                // Write expected_trap
+                mtvec::write(expected_trap as _, mtvec::TrapMode::Direct);
+                asm!("addi a0, zero, 0",
+                    "addi a1, zero, 0",
+                    "csrr a2, {}",
+                    "mv {}, a0",
+                    const $($x)*,
+                    out(reg) res,
+                    options(nomem));
+                asm!("csrw mtvec, {}", in(reg) mtvec);
+            }
+            res == 0
+    }};
+}

+ 210 - 0
prototyper/prototyper/src/main.rs

@@ -0,0 +1,210 @@
+#![feature(alloc_error_handler)]
+#![feature(naked_functions)]
+#![feature(fn_align)]
+#![no_std]
+#![no_main]
+#![allow(static_mut_refs)]
+
+extern crate alloc;
+#[macro_use]
+extern crate log;
+#[macro_use]
+mod macros;
+
+mod cfg;
+mod devicetree;
+mod fail;
+mod firmware;
+mod platform;
+mod riscv;
+mod sbi;
+
+use core::arch::{asm, naked_asm};
+
+use crate::platform::PLATFORM;
+use crate::riscv::csr::menvcfg;
+use crate::riscv::current_hartid;
+use crate::sbi::extensions::{
+    Extension, PrivilegedVersion, hart_extension_probe, hart_privileged_version,
+    privileged_version_detection,
+};
+use crate::sbi::hart_context::NextStage;
+use crate::sbi::heap::sbi_heap_init;
+use crate::sbi::hsm::local_remote_hsm;
+use crate::sbi::ipi;
+use crate::sbi::trap;
+use crate::sbi::trap_stack;
+
+pub const R_RISCV_RELATIVE: usize = 3;
+
+#[unsafe(no_mangle)]
+extern "C" fn rust_main(_hart_id: usize, opaque: usize, nonstandard_a2: usize) {
+    // Track whether SBI is initialized and ready.
+
+    let boot_hart_info = firmware::get_boot_hart(opaque, nonstandard_a2);
+    // boot hart task entry.
+    if boot_hart_info.is_boot_hart {
+        // Initialize the sbi heap
+        sbi_heap_init();
+
+        // parse the device tree
+        let fdt_address = boot_hart_info.fdt_address;
+
+        unsafe {
+            PLATFORM.init(fdt_address);
+            PLATFORM.print_board_info();
+        }
+
+        firmware::set_pmp(unsafe { PLATFORM.info.memory_range.as_ref().unwrap() });
+        firmware::log_pmp_cfg(unsafe { PLATFORM.info.memory_range.as_ref().unwrap() });
+
+        // Get boot information and prepare for kernel entry.
+        let boot_info = firmware::get_boot_info(nonstandard_a2);
+        let (mpp, next_addr) = (boot_info.mpp, boot_info.next_address);
+
+        // Log boot hart ID and PMP information
+        let hart_id = current_hartid();
+        info!("{:<30}: {}", "Boot HART ID", hart_id);
+
+        // Detection Priv Version
+        privileged_version_detection();
+        let priv_version = hart_privileged_version(hart_id);
+        info!("{:<30}: {:?}", "Boot HART Privileged Version", priv_version);
+
+        // Start kernel.
+        local_remote_hsm().start(NextStage {
+            start_addr: next_addr,
+            next_mode: mpp,
+            opaque: fdt_address,
+        });
+
+        info!(
+            "Redirecting hart {} to 0x{:0>16x} in {:?} mode.",
+            current_hartid(),
+            next_addr,
+            mpp
+        );
+    } else {
+        // Other harts task entry.
+        trap_stack::prepare_for_trap();
+
+        // Wait for boot hart to complete SBI initialization.
+        while !unsafe { PLATFORM.ready() } {
+            core::hint::spin_loop()
+        }
+
+        firmware::set_pmp(unsafe { PLATFORM.info.memory_range.as_ref().unwrap() });
+        // Detection Priv Version
+        privileged_version_detection();
+    }
+    // Clear all pending IPIs.
+    ipi::clear_all();
+
+    // Configure CSRs and trap handling.
+    unsafe {
+        // Delegate all interrupts and exceptions to supervisor mode.
+        asm!("csrw mideleg,    {}", in(reg) !0);
+        asm!("csrw medeleg,    {}", in(reg) !0);
+        asm!("csrw mcounteren, {}", in(reg) !0);
+        asm!("csrw scounteren, {}", in(reg) !0);
+        use ::riscv::register::{medeleg, mtvec};
+        // Keep supervisor environment calls and illegal instructions in M-mode.
+        medeleg::clear_supervisor_env_call();
+        medeleg::clear_illegal_instruction();
+        if hart_privileged_version(current_hartid()) >= PrivilegedVersion::Version1_12 {
+            // Configure environment features based on available extensions.
+            if hart_extension_probe(current_hartid(), Extension::Sstc) {
+                menvcfg::set_bits(
+                    menvcfg::STCE | menvcfg::CBIE_INVALIDATE | menvcfg::CBCFE | menvcfg::CBZE,
+                );
+            } else {
+                menvcfg::set_bits(menvcfg::CBIE_INVALIDATE | menvcfg::CBCFE | menvcfg::CBZE);
+            }
+        }
+        // Set up trap handling.
+        mtvec::write(fast_trap::trap_entry as _, mtvec::TrapMode::Direct);
+    }
+}
+
+#[naked]
+#[unsafe(link_section = ".text.entry")]
+#[unsafe(export_name = "_start")]
+unsafe extern "C" fn start() -> ! {
+    unsafe {
+        naked_asm!(
+            ".option arch, +a",
+            // 1. Turn off interrupt.
+            "   csrw    mie, zero",
+            // 2. Initialize programming language runtime.
+            // only clear bss if hartid matches preferred boot hart id.
+            "   csrr    t0, mhartid",
+            "   bne     t0, zero, 4f",
+            "   call    {relocation_update}",
+            "1:",
+            // 3. Hart 0 clear bss segment.
+            "   lla     t0, sbi_bss_start
+            lla     t1, sbi_bss_end
+         2: bgeu    t0, t1, 3f
+            sd      zero, 0(t0)
+            addi    t0, t0, 8
+            j       2b",
+            "3: ", // Hart 0 set bss ready signal.
+            "   lla     t0, 6f
+            li      t1, 1
+            amoadd.w t0, t1, 0(t0)
+            j       5f",
+            "4:", // Other harts are waiting for bss ready signal.
+            "   li      t1, 1
+            lla     t0, 6f
+            lw      t0, 0(t0)
+            bne     t0, t1, 4b",
+            "5:",
+             // 4. Prepare stack for each hart.
+            "   call    {locate_stack}",
+            "   call    {main}",
+            "   csrw    mscratch, sp",
+            "   j       {hart_boot}",
+            "  .balign  4",
+            "6:",  // bss ready signal.
+            "  .word    0",
+            relocation_update = sym relocation_update,
+            locate_stack = sym trap_stack::locate,
+            main         = sym rust_main,
+            hart_boot    = sym trap::boot::boot,
+        )
+    }
+}
+
+// Handle relocations for position-independent code
+#[naked]
+unsafe extern "C" fn relocation_update() {
+    unsafe {
+        naked_asm!(
+            // Get load offset.
+            "   li t0, {START_ADDRESS}",
+            "   lla t1, sbi_start",
+            "   sub t2, t1, t0",
+
+            // Foreach rela.dyn and update relocation.
+            "   lla t0, __rel_dyn_start",
+            "   lla t1, __rel_dyn_end",
+            "   li  t3, {R_RISCV_RELATIVE}",
+            "1:",
+            "   ld  t4, 8(t0)",
+            "   bne t4, t3, 2f",
+            "   ld t4, 0(t0)", // Get offset
+            "   ld t5, 16(t0)", // Get append
+            "   add t4, t4, t2", // Add load offset to offset add append
+            "   add t5, t5, t2",
+            "   sd t5, 0(t4)", // Update address
+            "   addi t0, t0, 24", // Get next rela item
+            "2:",
+            "   blt t0, t1, 1b",
+
+            // Return
+            "   ret",
+            R_RISCV_RELATIVE = const R_RISCV_RELATIVE,
+            START_ADDRESS = const cfg::SBI_LINK_START_ADDRESS,
+        )
+    }
+}

+ 122 - 0
prototyper/prototyper/src/platform/clint.rs

@@ -0,0 +1,122 @@
+use aclint::SifiveClint;
+use core::arch::asm;
+use xuantie_riscv::peripheral::clint::THeadClint;
+
+use crate::sbi::ipi::IpiDevice;
+pub(crate) const SIFIVE_CLINT_COMPATIBLE: [&str; 1] = ["riscv,clint0"];
+pub(crate) const THEAD_CLINT_COMPATIBLE: [&str; 1] = ["thead,c900-clint"];
+
+#[doc(hidden)]
+#[allow(unused)]
+#[derive(Clone, Copy, Debug)]
+pub enum MachineClintType {
+    SiFiveClint,
+    TheadClint,
+}
+
+/// For SiFive Clint
+pub struct SifiveClintWrap {
+    inner: *const SifiveClint,
+}
+
+impl SifiveClintWrap {
+    pub fn new(base: usize) -> Self {
+        Self {
+            inner: base as *const SifiveClint,
+        }
+    }
+}
+
+impl IpiDevice for SifiveClintWrap {
+    #[inline(always)]
+    fn read_mtime(&self) -> u64 {
+        unsafe { (*self.inner).read_mtime() }
+    }
+
+    #[inline(always)]
+    fn write_mtime(&self, val: u64) {
+        unsafe { (*self.inner).write_mtime(val) }
+    }
+
+    #[inline(always)]
+    fn read_mtimecmp(&self, hart_idx: usize) -> u64 {
+        unsafe { (*self.inner).read_mtimecmp(hart_idx) }
+    }
+
+    #[inline(always)]
+    fn write_mtimecmp(&self, hart_idx: usize, val: u64) {
+        unsafe { (*self.inner).write_mtimecmp(hart_idx, val) }
+    }
+
+    #[inline(always)]
+    fn read_msip(&self, hart_idx: usize) -> bool {
+        unsafe { (*self.inner).read_msip(hart_idx) }
+    }
+
+    #[inline(always)]
+    fn set_msip(&self, hart_idx: usize) {
+        unsafe { (*self.inner).set_msip(hart_idx) }
+    }
+
+    #[inline(always)]
+    fn clear_msip(&self, hart_idx: usize) {
+        unsafe { (*self.inner).clear_msip(hart_idx) }
+    }
+}
+
+/// For T-Head Clint
+pub struct THeadClintWrap {
+    inner: *const THeadClint,
+}
+
+impl THeadClintWrap {
+    pub fn new(base: usize) -> Self {
+        Self {
+            inner: base as *const THeadClint,
+        }
+    }
+}
+
+impl IpiDevice for THeadClintWrap {
+    #[inline(always)]
+    fn read_mtime(&self) -> u64 {
+        unsafe {
+            let mut mtime: u64 = 0;
+            asm!(
+                "rdtime {}",
+                inout(reg) mtime,
+            );
+            mtime
+        }
+    }
+
+    #[inline(always)]
+    fn write_mtime(&self, _val: u64) {
+        unimplemented!()
+    }
+
+    #[inline(always)]
+    fn read_mtimecmp(&self, hart_idx: usize) -> u64 {
+        unsafe { (*self.inner).read_mtimecmp(hart_idx) }
+    }
+
+    #[inline(always)]
+    fn write_mtimecmp(&self, hart_idx: usize, val: u64) {
+        unsafe { (*self.inner).write_mtimecmp(hart_idx, val) }
+    }
+
+    #[inline(always)]
+    fn read_msip(&self, hart_idx: usize) -> bool {
+        unsafe { (*self.inner).read_msip(hart_idx) }
+    }
+
+    #[inline(always)]
+    fn set_msip(&self, hart_idx: usize) {
+        unsafe { (*self.inner).set_msip(hart_idx) }
+    }
+
+    #[inline(always)]
+    fn clear_msip(&self, hart_idx: usize) {
+        unsafe { (*self.inner).clear_msip(hart_idx) }
+    }
+}

+ 98 - 0
prototyper/prototyper/src/platform/console.rs

@@ -0,0 +1,98 @@
+use bouffalo_hal::uart::RegisterBlock as BflbUartRegisterBlock;
+use uart_xilinx::MmioUartAxiLite;
+use uart16550::{Register, Uart16550};
+
+use crate::sbi::console::ConsoleDevice;
+pub(crate) const UART16650U8_COMPATIBLE: [&str; 1] = ["ns16550a"];
+pub(crate) const UART16650U32_COMPATIBLE: [&str; 1] = ["snps,dw-apb-uart"];
+pub(crate) const UARTAXILITE_COMPATIBLE: [&str; 1] = ["xlnx,xps-uartlite-1.00.a"];
+pub(crate) const UARTBFLB_COMPATIBLE: [&str; 1] = ["bflb,bl808-uart"];
+
+#[doc(hidden)]
+#[allow(unused)]
+#[derive(Clone, Copy, Debug)]
+pub enum MachineConsoleType {
+    Uart16550U8,
+    Uart16550U32,
+    UartAxiLite,
+    UartBflb,
+}
+
+/// For Uart 16550
+pub struct Uart16550Wrap<R: Register> {
+    inner: *const Uart16550<R>,
+}
+
+impl<R: Register> Uart16550Wrap<R> {
+    pub fn new(base: usize) -> Self {
+        Self {
+            inner: base as *const Uart16550<R>,
+        }
+    }
+}
+
+impl<R: Register> ConsoleDevice for Uart16550Wrap<R> {
+    fn read(&self, buf: &mut [u8]) -> usize {
+        unsafe { (*self.inner).read(buf) }
+    }
+
+    fn write(&self, buf: &[u8]) -> usize {
+        unsafe { (*self.inner).write(buf) }
+    }
+}
+
+/// For Uart AxiLite
+impl ConsoleDevice for MmioUartAxiLite {
+    fn read(&self, buf: &mut [u8]) -> usize {
+        self.read(buf)
+    }
+
+    fn write(&self, buf: &[u8]) -> usize {
+        self.write(buf)
+    }
+}
+
+/// For Uart BFLB
+pub struct UartBflbWrap {
+    inner: *const BflbUartRegisterBlock,
+}
+
+impl UartBflbWrap {
+    pub fn new(base: usize) -> Self {
+        Self {
+            inner: base as *const BflbUartRegisterBlock,
+        }
+    }
+}
+
+impl ConsoleDevice for UartBflbWrap {
+    fn read(&self, buf: &mut [u8]) -> usize {
+        let uart = unsafe { &(*self.inner) };
+        while uart.fifo_config_1.read().receive_available_bytes() == 0 {
+            core::hint::spin_loop();
+        }
+        let len = core::cmp::min(
+            uart.fifo_config_1.read().receive_available_bytes() as usize,
+            buf.len(),
+        );
+        buf.iter_mut()
+            .take(len)
+            .for_each(|slot| *slot = uart.fifo_read.read());
+        len
+    }
+
+    fn write(&self, buf: &[u8]) -> usize {
+        let uart = unsafe { &(*self.inner) };
+        let mut count = 0;
+        for current in buf {
+            if uart.fifo_config_1.read().transmit_available_bytes() == 0 {
+                break;
+            }
+            count += 1;
+            unsafe {
+                uart.fifo_write.write(*current);
+            }
+        }
+        count
+    }
+}

+ 434 - 0
prototyper/prototyper/src/platform/mod.rs

@@ -0,0 +1,434 @@
+use alloc::string::String;
+use alloc::{boxed::Box, string::ToString};
+use clint::{SifiveClintWrap, THeadClintWrap};
+use core::{
+    ops::Range,
+    sync::atomic::{AtomicBool, Ordering},
+};
+use reset::SifiveTestDeviceWrap;
+use spin::Mutex;
+use uart_xilinx::MmioUartAxiLite;
+
+use crate::cfg::NUM_HART_MAX;
+use crate::devicetree::*;
+use crate::fail;
+use crate::platform::clint::{MachineClintType, SIFIVE_CLINT_COMPATIBLE, THEAD_CLINT_COMPATIBLE};
+use crate::platform::console::Uart16550Wrap;
+use crate::platform::console::UartBflbWrap;
+use crate::platform::console::{
+    MachineConsoleType, UART16650U8_COMPATIBLE, UART16650U32_COMPATIBLE, UARTAXILITE_COMPATIBLE,
+    UARTBFLB_COMPATIBLE,
+};
+use crate::platform::reset::SIFIVETEST_COMPATIBLE;
+use crate::sbi::SBI;
+use crate::sbi::console::SbiConsole;
+use crate::sbi::extensions;
+use crate::sbi::hsm::SbiHsm;
+use crate::sbi::ipi::SbiIpi;
+use crate::sbi::logger;
+use crate::sbi::reset::SbiReset;
+use crate::sbi::rfence::SbiRFence;
+use crate::sbi::trap_stack;
+
+mod clint;
+mod console;
+mod reset;
+
+type BaseAddress = usize;
+
+type CpuEnableList = [bool; NUM_HART_MAX];
+
+pub struct BoardInfo {
+    pub memory_range: Option<Range<usize>>,
+    pub console: Option<(BaseAddress, MachineConsoleType)>,
+    pub reset: Option<BaseAddress>,
+    pub ipi: Option<(BaseAddress, MachineClintType)>,
+    pub cpu_num: Option<usize>,
+    pub cpu_enabled: Option<CpuEnableList>,
+    pub model: String,
+}
+
+impl BoardInfo {
+    pub const fn new() -> Self {
+        BoardInfo {
+            memory_range: None,
+            console: None,
+            reset: None,
+            ipi: None,
+            cpu_enabled: None,
+            cpu_num: None,
+            model: String::new(),
+        }
+    }
+}
+
+pub struct Platform {
+    pub info: BoardInfo,
+    pub sbi: SBI,
+    pub ready: AtomicBool,
+}
+
+impl Platform {
+    pub const fn new() -> Self {
+        Platform {
+            info: BoardInfo::new(),
+            sbi: SBI::new(),
+            ready: AtomicBool::new(false),
+        }
+    }
+
+    pub fn init(&mut self, fdt_address: usize) {
+        self.info_init(fdt_address);
+        self.sbi_init();
+        trap_stack::prepare_for_trap();
+        self.ready.swap(true, Ordering::Release);
+    }
+
+    fn info_init(&mut self, fdt_address: usize) {
+        let dtb = parse_device_tree(fdt_address).unwrap_or_else(fail::device_tree_format);
+        let dtb = dtb.share();
+
+        let root: serde_device_tree::buildin::Node = serde_device_tree::from_raw_mut(&dtb)
+            .unwrap_or_else(fail::device_tree_deserialize_root);
+        let tree: Tree = root.deserialize();
+
+        // Get console device, init sbi console and logger
+        self.sbi_find_and_init_console(&root);
+
+        // Get ipi and reset device info
+        let mut find_device = |node: &serde_device_tree::buildin::Node| {
+            let info = get_compatible_and_range(node);
+            if let Some(info) = info {
+                let (compatible, regs) = info;
+                let base_address = regs.start;
+                for device_id in compatible.iter() {
+                    // Initialize clint device.
+                    if SIFIVE_CLINT_COMPATIBLE.contains(&device_id) {
+                        if node.get_prop("clint,has-no-64bit-mmio").is_some() {
+                            self.info.ipi = Some((base_address, MachineClintType::TheadClint));
+                        } else {
+                            self.info.ipi = Some((base_address, MachineClintType::SiFiveClint));
+                        }
+                    } else if THEAD_CLINT_COMPATIBLE.contains(&device_id) {
+                        self.info.ipi = Some((base_address, MachineClintType::TheadClint));
+                    }
+                    // Initialize reset device.
+                    if SIFIVETEST_COMPATIBLE.contains(&device_id) {
+                        self.info.reset = Some(base_address);
+                    }
+                }
+            }
+        };
+        root.search(&mut find_device);
+
+        // Get memory info
+        // TODO: More than one memory node or range?
+        let memory_reg = tree
+            .memory
+            .iter()
+            .next()
+            .unwrap()
+            .deserialize::<Memory>()
+            .reg;
+        let memory_range = memory_reg.iter().next().unwrap().0;
+        self.info.memory_range = Some(memory_range);
+
+        // Get cpu number info
+        self.info.cpu_num = Some(tree.cpus.cpu.len());
+
+        // Get model info
+        if let Some(model) = tree.model {
+            let model = model.iter().next().unwrap_or("<unspecified>");
+            self.info.model = model.to_string();
+        } else {
+            let model = "<unspecified>";
+            self.info.model = model.to_string();
+        }
+
+        // TODO: Need a better extension initialization method
+        extensions::init(&tree.cpus.cpu);
+
+        // Find which hart is enabled by fdt
+        let mut cpu_list: CpuEnableList = [false; NUM_HART_MAX];
+        for cpu_iter in tree.cpus.cpu.iter() {
+            let cpu = cpu_iter.deserialize::<Cpu>();
+            let hart_id = cpu.reg.iter().next().unwrap().0.start;
+            if let Some(x) = cpu_list.get_mut(hart_id) {
+                *x = true;
+            }
+        }
+        self.info.cpu_enabled = Some(cpu_list);
+    }
+
+    fn sbi_init(&mut self) {
+        self.sbi_ipi_init();
+        self.sbi_hsm_init();
+        self.sbi_reset_init();
+        self.sbi_rfence_init();
+    }
+
+    fn sbi_find_and_init_console(&mut self, root: &serde_device_tree::buildin::Node) {
+        //  Get console device info
+        if let Some(stdout_path) = root.chosen_stdout_path() {
+            if let Some(node) = root.find(stdout_path) {
+                let info = get_compatible_and_range(&node);
+                if let Some((compatible, regs)) = info {
+                    for device_id in compatible.iter() {
+                        if UART16650U8_COMPATIBLE.contains(&device_id) {
+                            self.info.console = Some((regs.start, MachineConsoleType::Uart16550U8));
+                        }
+                        if UART16650U32_COMPATIBLE.contains(&device_id) {
+                            self.info.console =
+                                Some((regs.start, MachineConsoleType::Uart16550U32));
+                        }
+                        if UARTAXILITE_COMPATIBLE.contains(&device_id) {
+                            self.info.console = Some((regs.start, MachineConsoleType::UartAxiLite));
+                        }
+                        if UARTBFLB_COMPATIBLE.contains(&device_id) {
+                            self.info.console = Some((regs.start, MachineConsoleType::UartBflb));
+                        }
+                    }
+                }
+            }
+        }
+
+        // init console and logger
+        self.sbi_console_init();
+        logger::Logger::init().unwrap();
+        info!("Hello RustSBI!");
+    }
+
+    fn sbi_console_init(&mut self) {
+        if let Some((base, console_type)) = self.info.console {
+            self.sbi.console = match console_type {
+                MachineConsoleType::Uart16550U8 => Some(SbiConsole::new(Mutex::new(Box::new(
+                    Uart16550Wrap::<u8>::new(base),
+                )))),
+                MachineConsoleType::Uart16550U32 => Some(SbiConsole::new(Mutex::new(Box::new(
+                    Uart16550Wrap::<u32>::new(base),
+                )))),
+                MachineConsoleType::UartAxiLite => Some(SbiConsole::new(Mutex::new(Box::new(
+                    MmioUartAxiLite::new(base),
+                )))),
+                MachineConsoleType::UartBflb => Some(SbiConsole::new(Mutex::new(Box::new(
+                    UartBflbWrap::new(base),
+                )))),
+            };
+        } else {
+            self.sbi.console = None;
+        }
+    }
+
+    fn sbi_reset_init(&mut self) {
+        if let Some(base) = self.info.reset {
+            self.sbi.reset = Some(SbiReset::new(Mutex::new(Box::new(
+                SifiveTestDeviceWrap::new(base),
+            ))));
+        } else {
+            self.sbi.reset = None;
+        }
+    }
+
+    fn sbi_ipi_init(&mut self) {
+        if let Some((base, clint_type)) = self.info.ipi {
+            self.sbi.ipi = match clint_type {
+                MachineClintType::SiFiveClint => Some(SbiIpi::new(
+                    Mutex::new(Box::new(SifiveClintWrap::new(base))),
+                    self.info.cpu_num.unwrap_or(NUM_HART_MAX),
+                )),
+                MachineClintType::TheadClint => Some(SbiIpi::new(
+                    Mutex::new(Box::new(THeadClintWrap::new(base))),
+                    self.info.cpu_num.unwrap_or(NUM_HART_MAX),
+                )),
+            };
+        } else {
+            self.sbi.ipi = None;
+        }
+    }
+
+    fn sbi_hsm_init(&mut self) {
+        // TODO: Can HSM work properly when there is no ipi device?
+        if self.info.ipi.is_some() {
+            self.sbi.hsm = Some(SbiHsm);
+        } else {
+            self.sbi.hsm = None;
+        }
+    }
+
+    fn sbi_rfence_init(&mut self) {
+        // TODO: Can rfence work properly when there is no ipi device?
+        if self.info.ipi.is_some() {
+            self.sbi.rfence = Some(SbiRFence);
+        } else {
+            self.sbi.rfence = None;
+        }
+    }
+
+    pub fn print_board_info(&self) {
+        info!("RustSBI version {}", rustsbi::VERSION);
+        rustsbi::LOGO.lines().for_each(|line| info!("{}", line));
+        info!("Initializing RustSBI machine-mode environment.");
+
+        self.print_platform_info();
+        self.print_cpu_info();
+        self.print_device_info();
+        self.print_memory_info();
+        self.print_additional_info();
+    }
+
+    #[inline]
+    fn print_platform_info(&self) {
+        info!("{:<30}: {}", "Platform Name", self.info.model);
+    }
+
+    fn print_cpu_info(&self) {
+        info!(
+            "{:<30}: {:?}",
+            "Platform HART Count",
+            self.info.cpu_num.unwrap_or(0)
+        );
+
+        if let Some(cpu_enabled) = &self.info.cpu_enabled {
+            let mut enabled_harts = [0; NUM_HART_MAX];
+            let mut count = 0;
+            for (i, &enabled) in cpu_enabled.iter().enumerate() {
+                if enabled {
+                    enabled_harts[count] = i;
+                    count += 1;
+                }
+            }
+            info!("{:<30}: {:?}", "Enabled HARTs", &enabled_harts[..count]);
+        } else {
+            warn!("{:<30}: Not Available", "Enabled HARTs");
+        }
+    }
+
+    #[inline]
+    fn print_device_info(&self) {
+        self.print_clint_info();
+        self.print_console_info();
+        self.print_reset_info();
+        self.print_hsm_info();
+        self.print_rfence_info();
+    }
+
+    #[inline]
+    fn print_clint_info(&self) {
+        match self.info.ipi {
+            Some((base, device)) => {
+                info!(
+                    "{:<30}: {:?} (Base Address: 0x{:x})",
+                    "Platform IPI Device", device, base
+                );
+            }
+            None => warn!("{:<30}: Not Available", "Platform IPI Device"),
+        }
+    }
+
+    #[inline]
+    fn print_console_info(&self) {
+        match self.info.console {
+            Some((base, device)) => {
+                info!(
+                    "{:<30}: {:?} (Base Address: 0x{:x})",
+                    "Platform Console Device", device, base
+                );
+            }
+            None => warn!("{:<30}: Not Available", "Platform Console Device"),
+        }
+    }
+
+    #[inline]
+    fn print_reset_info(&self) {
+        if let Some(base) = self.info.reset {
+            info!(
+                "{:<30}: Available (Base Address: 0x{:x})",
+                "Platform Reset Device", base
+            );
+        } else {
+            warn!("{:<30}: Not Available", "Platform Reset Device");
+        }
+    }
+
+    #[inline]
+    fn print_memory_info(&self) {
+        if let Some(memory_range) = &self.info.memory_range {
+            info!(
+                "{:<30}: 0x{:x} - 0x{:x}",
+                "Memory range", memory_range.start, memory_range.end
+            );
+        } else {
+            warn!("{:<30}: Not Available", "Memory range");
+        }
+    }
+
+    #[inline]
+    fn print_hsm_info(&self) {
+        info!(
+            "{:<30}: {}",
+            "Platform HSM Device",
+            if self.have_hsm() {
+                "Available"
+            } else {
+                "Not Available"
+            }
+        );
+    }
+
+    #[inline]
+    fn print_rfence_info(&self) {
+        info!(
+            "{:<30}: {}",
+            "Platform RFence Device",
+            if self.have_rfence() {
+                "Available"
+            } else {
+                "Not Available"
+            }
+        );
+    }
+
+    #[inline]
+    fn print_additional_info(&self) {
+        if !self.ready.load(Ordering::Acquire) {
+            warn!(
+                "{:<30}: Platform initialization is not complete.",
+                "Platform Status"
+            );
+        } else {
+            info!(
+                "{:<30}: Platform initialization complete and ready.",
+                "Platform Status"
+            );
+        }
+    }
+}
+
+#[allow(unused)]
+impl Platform {
+    pub fn have_console(&self) -> bool {
+        self.sbi.console.is_some()
+    }
+
+    pub fn have_reset(&self) -> bool {
+        self.sbi.reset.is_some()
+    }
+
+    pub fn have_ipi(&self) -> bool {
+        self.sbi.ipi.is_some()
+    }
+
+    pub fn have_hsm(&self) -> bool {
+        self.sbi.hsm.is_some()
+    }
+
+    pub fn have_rfence(&self) -> bool {
+        self.sbi.rfence.is_some()
+    }
+
+    pub fn ready(&self) -> bool {
+        self.ready.load(Ordering::Acquire)
+    }
+}
+
+pub(crate) static mut PLATFORM: Platform = Platform::new();

+ 34 - 0
prototyper/prototyper/src/platform/reset.rs

@@ -0,0 +1,34 @@
+use sifive_test_device::SifiveTestDevice;
+
+use crate::sbi::reset::ResetDevice;
+pub(crate) const SIFIVETEST_COMPATIBLE: [&str; 1] = ["sifive,test0"];
+
+pub struct SifiveTestDeviceWrap {
+    inner: *const SifiveTestDevice,
+}
+
+impl SifiveTestDeviceWrap {
+    pub fn new(base: usize) -> Self {
+        Self {
+            inner: base as *const SifiveTestDevice,
+        }
+    }
+}
+
+/// Reset Device: SifiveTestDevice
+impl ResetDevice for SifiveTestDeviceWrap {
+    #[inline]
+    fn fail(&self, code: u16) -> ! {
+        unsafe { (*self.inner).fail(code) }
+    }
+
+    #[inline]
+    fn pass(&self) -> ! {
+        unsafe { (*self.inner).pass() }
+    }
+
+    #[inline]
+    fn reset(&self) -> ! {
+        unsafe { (*self.inner).reset() }
+    }
+}

+ 63 - 0
prototyper/prototyper/src/riscv/csr.rs

@@ -0,0 +1,63 @@
+#![allow(unused)]
+
+/// CSR addresses for timer registers.
+///
+/// Time value (lower 32 bits).
+pub const CSR_TIME: u32 = 0xc01;
+/// Time value (upper 32 bits).
+pub const CSR_TIMEH: u32 = 0xc81;
+/// Supervisor timer compare value.
+pub const CSR_STIMECMP: u32 = 0x14D;
+
+/// Machine environment configuration register (menvcfg) bit fields.
+pub mod menvcfg {
+    use core::arch::asm;
+
+    /// Fence of I/O implies memory.
+    pub const FIOM: usize = 0x1 << 0;
+    /// Cache block invalidate - flush.
+    pub const CBIE_FLUSH: usize = 0x01 << 4;
+    /// Cache block invalidate - invalidate.
+    pub const CBIE_INVALIDATE: usize = 0x11 << 4;
+    /// Cache block clean for enclave.
+    pub const CBCFE: usize = 0x1 << 6;
+    /// Cache block zero for enclave.
+    pub const CBZE: usize = 0x1 << 7;
+    /// Page-based memory types enable.
+    pub const PBMTE: usize = 0x1 << 62;
+    /// Supervisor timer counter enable.
+    pub const STCE: usize = 0x1 << 63;
+
+    /// Sets the STCE bit to enable supervisor timer counter.
+    #[inline(always)]
+    pub fn set_stce() {
+        set_bits(STCE);
+    }
+
+    /// Sets specified bits in menvcfg register.
+    pub fn set_bits(option: usize) {
+        let mut bits: usize;
+        unsafe {
+            // Read current `menvcfg` value.
+            asm!("csrr {}, menvcfg", out(reg) bits, options(nomem));
+        }
+        // Set requested bits
+        bits |= option;
+        unsafe {
+            // Write back updated value
+            asm!("csrw menvcfg, {}", in(reg) bits, options(nomem));
+        }
+    }
+}
+
+/// Supervisor timer compare register operations.
+pub mod stimecmp {
+    use core::arch::asm;
+
+    /// Sets the supervisor timer compare value.
+    pub fn set(value: u64) {
+        unsafe {
+            asm!("csrrw zero, stimecmp, {}", in(reg) value, options(nomem));
+        }
+    }
+}

+ 7 - 0
prototyper/prototyper/src/riscv/mod.rs

@@ -0,0 +1,7 @@
+pub mod csr;
+
+/// Returns the current hart (hardware thread) ID.
+#[inline]
+pub fn current_hartid() -> usize {
+    riscv::register::mhartid::read()
+}

+ 126 - 0
prototyper/prototyper/src/sbi/console.rs

@@ -0,0 +1,126 @@
+use alloc::boxed::Box;
+use core::fmt::{self, Write};
+use rustsbi::{Console, Physical, SbiRet};
+use spin::Mutex;
+
+use crate::platform::PLATFORM;
+
+/// A trait that must be implemented by console devices to provide basic I/O functionality.
+pub trait ConsoleDevice {
+    /// Reads bytes from the console into the provided buffer.
+    ///
+    /// # Returns
+    /// The number of bytes that were successfully read.
+    fn read(&self, buf: &mut [u8]) -> usize;
+
+    /// Writes bytes from the provided buffer to the console.
+    ///
+    /// # Returns
+    /// The number of bytes that were successfully written.
+    fn write(&self, buf: &[u8]) -> usize;
+}
+
+/// An implementation of the SBI console interface that wraps a console device.
+///
+/// This provides a safe interface for interacting with console hardware through the
+/// SBI specification.
+pub struct SbiConsole {
+    inner: Mutex<Box<dyn ConsoleDevice>>,
+}
+
+impl SbiConsole {
+    /// Creates a new SBI console that wraps the provided locked console device.
+    ///
+    /// # Arguments
+    /// * `inner` - A mutex containing the console device implementation
+    #[inline]
+    pub fn new(inner: Mutex<Box<dyn ConsoleDevice>>) -> Self {
+        Self { inner }
+    }
+
+    /// Writes a single character to the console.
+    ///
+    /// # Arguments
+    /// * `c` - The character to write, as a usize
+    ///
+    /// # Returns
+    /// Always returns 0 to indicate success
+    #[inline]
+    pub fn putchar(&mut self, c: usize) -> usize {
+        self.write_char(c as u8 as char).unwrap();
+        0
+    }
+
+    /// Reads a single character from the console.
+    ///
+    /// This method will block until a character is available to be read.
+    ///
+    /// # Returns
+    /// The read character as a usize
+    #[inline]
+    pub fn getchar(&self) -> usize {
+        let mut c = 0u8;
+        let console = self.inner.lock();
+        // Block until we successfully read 1 byte
+        while console.read(core::slice::from_mut(&mut c)) != 1 {
+            core::hint::spin_loop();
+        }
+        c as usize
+    }
+}
+
+impl Console for SbiConsole {
+    /// Write a physical memory buffer to the console.
+    #[inline]
+    fn write(&self, bytes: Physical<&[u8]>) -> SbiRet {
+        // TODO: verify valid memory range for a `Physical` slice.
+        let start = bytes.phys_addr_lo();
+        let buf = unsafe { core::slice::from_raw_parts(start as *const u8, bytes.num_bytes()) };
+        let bytes_written = self.inner.lock().write(buf);
+        SbiRet::success(bytes_written)
+    }
+
+    /// Read from console into a physical memory buffer.
+    #[inline]
+    fn read(&self, bytes: Physical<&mut [u8]>) -> SbiRet {
+        // TODO: verify valid memory range for a `Physical` slice.
+        let start = bytes.phys_addr_lo();
+        let buf = unsafe { core::slice::from_raw_parts_mut(start as *mut u8, bytes.num_bytes()) };
+        let bytes_read = self.inner.lock().read(buf);
+        SbiRet::success(bytes_read)
+    }
+
+    /// Write a single byte to the console.
+    #[inline]
+    fn write_byte(&self, byte: u8) -> SbiRet {
+        self.inner.lock().write(&[byte]);
+        SbiRet::success(0)
+    }
+}
+
+impl fmt::Write for SbiConsole {
+    /// Implement Write trait for string formatting.
+    #[inline]
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        let mut bytes = s.as_bytes();
+        let console = self.inner.lock();
+        // Write all bytes in chunks
+        while !bytes.is_empty() {
+            let count = console.write(bytes);
+            bytes = &bytes[count..];
+        }
+        Ok(())
+    }
+}
+
+/// Global function to write a character to the console.
+#[inline]
+pub fn putchar(c: usize) -> usize {
+    unsafe { PLATFORM.sbi.console.as_mut().unwrap().putchar(c) }
+}
+
+/// Global function to read a character from the console.
+#[inline]
+pub fn getchar() -> usize {
+    unsafe { PLATFORM.sbi.console.as_mut().unwrap().getchar() }
+}

+ 21 - 0
prototyper/prototyper/src/sbi/early_trap.rs

@@ -0,0 +1,21 @@
+use core::arch::naked_asm;
+
+/// When you expected some insts will cause trap, use this.
+/// If trap happened, a0 will set to 1, otherwise will be 0.
+///
+/// This function will change a0 and a1 and will NOT change them back.
+#[naked]
+#[repr(align(16))]
+pub(crate) unsafe extern "C" fn expected_trap() {
+    unsafe {
+        naked_asm!(
+            "add a0, zero, zero",
+            "add a1, zero, zero",
+            "csrr a1, mepc",
+            "addi a1, a1, 4",
+            "csrw mepc, a1",
+            "addi a0, zero, 1",
+            "mret",
+        )
+    }
+}

+ 129 - 0
prototyper/prototyper/src/sbi/extensions.rs

@@ -0,0 +1,129 @@
+use serde_device_tree::buildin::NodeSeq;
+
+use crate::riscv::current_hartid;
+use crate::sbi::trap_stack::ROOT_STACK;
+
+pub struct HartFeatures {
+    extension: [bool; Extension::COUNT],
+    privileged_version: PrivilegedVersion,
+}
+
+#[derive(Copy, Clone)]
+pub enum Extension {
+    Sstc = 0,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum PrivilegedVersion {
+    Unknown = 0,
+    Version1_10 = 1,
+    Version1_11 = 2,
+    Version1_12 = 3,
+}
+
+impl Extension {
+    const COUNT: usize = 1;
+    const ITER: [Self; Extension::COUNT] = [Extension::Sstc];
+
+    pub fn as_str(&self) -> &'static str {
+        match self {
+            Extension::Sstc => "sstc",
+        }
+    }
+
+    #[inline]
+    pub fn index(&self) -> usize {
+        *self as usize
+    }
+}
+
+pub fn hart_extension_probe(hart_id: usize, ext: Extension) -> bool {
+    unsafe {
+        ROOT_STACK
+            .get_mut(hart_id)
+            .map(|x| x.hart_context().features.extension[ext.index()])
+            .unwrap()
+    }
+}
+
+pub fn hart_privileged_version(hart_id: usize) -> PrivilegedVersion {
+    unsafe {
+        ROOT_STACK
+            .get_mut(hart_id)
+            .map(|x| x.hart_context().features.privileged_version)
+            .unwrap()
+    }
+}
+
+#[cfg(not(feature = "nemu"))]
+pub fn init(cpus: &NodeSeq) {
+    use crate::devicetree::Cpu;
+    for cpu_iter in cpus.iter() {
+        let cpu = cpu_iter.deserialize::<Cpu>();
+        let hart_id = cpu.reg.iter().next().unwrap().0.start;
+        let mut hart_exts = [false; Extension::COUNT];
+        if cpu.isa_extensions.is_some() {
+            let isa = cpu.isa_extensions.unwrap();
+            Extension::ITER.iter().for_each(|ext| {
+                hart_exts[ext.index()] = isa.iter().any(|e| e == ext.as_str());
+            });
+        } else if cpu.isa.is_some() {
+            let isa_iter = cpu.isa.unwrap();
+            let isa = isa_iter.iter().next().unwrap_or_default();
+            Extension::ITER.iter().for_each(|ext| {
+                hart_exts[ext.index()] = isa.contains(ext.as_str());
+            })
+        }
+
+        unsafe {
+            ROOT_STACK
+                .get_mut(hart_id)
+                .map(|stack| stack.hart_context().features.extension = hart_exts)
+                .unwrap()
+        }
+    }
+}
+
+pub fn privileged_version_detection() {
+    let mut current_priv_ver = PrivilegedVersion::Unknown;
+    {
+        const CSR_MCOUNTEREN: u64 = 0x306;
+        const CSR_MCOUNTINHIBIT: u64 = 0x320;
+        const CSR_MENVCFG: u64 = 0x30a;
+
+        if has_csr!(CSR_MCOUNTEREN) {
+            current_priv_ver = PrivilegedVersion::Version1_10;
+            if has_csr!(CSR_MCOUNTINHIBIT) {
+                current_priv_ver = PrivilegedVersion::Version1_11;
+                if has_csr!(CSR_MENVCFG) {
+                    current_priv_ver = PrivilegedVersion::Version1_12;
+                }
+            }
+        }
+    }
+    unsafe {
+        ROOT_STACK
+            .get_mut(current_hartid())
+            .map(|stack| stack.hart_context().features.privileged_version = current_priv_ver)
+            .unwrap()
+    }
+}
+
+#[cfg(feature = "nemu")]
+pub fn init(cpus: &NodeSeq) {
+    for hart_id in 0..cpus.len() {
+        let mut hart_exts = [false; Extension::COUNT];
+        hart_exts[Extension::Sstc.index()] = true;
+        unsafe {
+            ROOT_STACK
+                .get_mut(hart_id)
+                .map(|stack| {
+                    stack.hart_context().features = HartFeatures {
+                        extension: hart_exts,
+                        privileged_version: PrivilegedVersion::Version1_12,
+                    }
+                })
+                .unwrap()
+        }
+    }
+}

+ 70 - 0
prototyper/prototyper/src/sbi/fifo.rs

@@ -0,0 +1,70 @@
+use core::mem::MaybeUninit;
+
+/// Size of the FIFO buffer.
+const FIFO_SIZE: usize = 16;
+
+#[derive(Debug)]
+pub enum FifoError {
+    Empty,
+    Full,
+}
+
+/// A fixed-size FIFO (First In First Out) queue implementation.
+pub struct Fifo<T: Copy + Clone> {
+    data: [MaybeUninit<T>; FIFO_SIZE],
+    head: usize,
+    tail: usize,
+    count: usize,
+}
+
+impl<T: Copy + Clone> Fifo<T> {
+    #[inline]
+    pub fn new() -> Self {
+        // Initialize array with uninitialized values
+        let data = [MaybeUninit::uninit(); FIFO_SIZE];
+        Self {
+            data,
+            head: 0,
+            tail: 0,
+            count: 0,
+        }
+    }
+
+    #[inline]
+    pub fn is_full(&self) -> bool {
+        self.count == FIFO_SIZE
+    }
+
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.count == 0
+    }
+
+    pub fn push(&mut self, element: T) -> Result<(), FifoError> {
+        if self.is_full() {
+            return Err(FifoError::Full);
+        }
+
+        // Write element and update tail position
+        self.data[self.tail].write(element);
+        self.tail = (self.tail + 1) % FIFO_SIZE;
+        self.count += 1;
+
+        Ok(())
+    }
+
+    pub fn pop(&mut self) -> Result<T, FifoError> {
+        if self.is_empty() {
+            return Err(FifoError::Empty);
+        }
+
+        // unsafe: Take ownership of element at head
+        let element = unsafe { self.data[self.head].assume_init_read() };
+
+        // Update head position
+        self.head = (self.head + 1) % FIFO_SIZE;
+        self.count -= 1;
+
+        Ok(element)
+    }
+}

+ 47 - 0
prototyper/prototyper/src/sbi/hart_context.rs

@@ -0,0 +1,47 @@
+use crate::sbi::extensions::HartFeatures;
+use crate::sbi::hsm::HsmCell;
+use crate::sbi::rfence::RFenceCell;
+use core::ptr::NonNull;
+use core::sync::atomic::AtomicU8;
+use fast_trap::FlowContext;
+use riscv::register::mstatus;
+
+/// Context for managing hart (hardware thread) state and operations.
+pub(crate) struct HartContext {
+    /// Trap context for handling exceptions and interrupts.
+    trap: FlowContext,
+    /// Hart state management cell containing next stage boot info.
+    pub hsm: HsmCell<NextStage>,
+    /// Remote fence synchronization cell.
+    pub rfence: RFenceCell,
+    /// Type of inter-processor interrupt pending.
+    pub ipi_type: AtomicU8,
+    /// Supported hart features.
+    pub features: HartFeatures,
+}
+
+impl HartContext {
+    /// Initialize the hart context by creating new HSM and RFence cells
+    #[inline]
+    pub fn init(&mut self) {
+        self.hsm = HsmCell::new();
+        self.rfence = RFenceCell::new();
+    }
+
+    /// Get a non-null pointer to the trap context.
+    #[inline]
+    pub fn context_ptr(&mut self) -> NonNull<FlowContext> {
+        unsafe { NonNull::new_unchecked(&mut self.trap) }
+    }
+}
+
+/// Information needed to boot into the next execution stage.
+#[derive(Debug)]
+pub struct NextStage {
+    /// Starting address to jump to.
+    pub start_addr: usize,
+    /// Opaque value passed to next stage.
+    pub opaque: usize,
+    /// Privilege mode for next stage.
+    pub next_mode: mstatus::MPP,
+}

+ 21 - 0
prototyper/prototyper/src/sbi/heap.rs

@@ -0,0 +1,21 @@
+use crate::cfg::HEAP_SIZE;
+use buddy_system_allocator::LockedHeap;
+
+#[unsafe(link_section = ".bss.heap")]
+static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
+
+#[global_allocator]
+static HEAP_ALLOCATOR: LockedHeap<15> = LockedHeap::<15>::empty();
+
+pub fn sbi_heap_init() {
+    unsafe {
+        HEAP_ALLOCATOR
+            .lock()
+            .init(HEAP.as_ptr() as usize, HEAP_SIZE);
+    }
+}
+
+#[alloc_error_handler]
+pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
+    panic!("Heap allocation error, layout = {:?}", layout);
+}

+ 254 - 0
prototyper/prototyper/src/sbi/hsm.rs

@@ -0,0 +1,254 @@
+use core::{
+    cell::UnsafeCell,
+    hint::spin_loop,
+    sync::atomic::{AtomicUsize, Ordering},
+};
+use riscv::register::mstatus::MPP;
+use rustsbi::{SbiRet, spec::hsm::hart_state};
+
+use crate::platform::PLATFORM;
+use crate::riscv::current_hartid;
+use crate::sbi::hart_context::NextStage;
+use crate::sbi::trap_stack::ROOT_STACK;
+
+/// Special state indicating a hart is in the process of starting.
+const HART_STATE_START_PENDING_EXT: usize = usize::MAX;
+
+type HsmState = AtomicUsize;
+
+/// Cell for managing hart state and shared data between harts.
+pub(crate) struct HsmCell<T> {
+    status: HsmState,
+    inner: UnsafeCell<Option<T>>,
+}
+
+impl<T> HsmCell<T> {
+    /// Creates a new HsmCell with STOPPED state and no inner data.
+    pub const fn new() -> Self {
+        Self {
+            status: HsmState::new(hart_state::STOPPED),
+            inner: UnsafeCell::new(None),
+        }
+    }
+
+    /// Gets a local view of this cell for the current hart.
+    ///
+    /// # Safety
+    ///
+    /// Caller must ensure this cell belongs to the current hart.
+    #[inline]
+    pub unsafe fn local(&self) -> LocalHsmCell<'_, T> {
+        LocalHsmCell(self)
+    }
+
+    /// Gets a remote view of this cell for accessing from other harts.
+    #[inline]
+    pub fn remote(&self) -> RemoteHsmCell<'_, T> {
+        RemoteHsmCell(self)
+    }
+}
+
+/// View of HsmCell for operations on the current hart.
+pub struct LocalHsmCell<'a, T>(&'a HsmCell<T>);
+
+/// View of HsmCell for operations from other harts.
+pub struct RemoteHsmCell<'a, T>(&'a HsmCell<T>);
+
+// Mark HsmCell as safe to share between threads
+unsafe impl<T: Send> Sync for HsmCell<T> {}
+unsafe impl<T: Send> Send for HsmCell<T> {}
+
+impl<T> LocalHsmCell<'_, T> {
+    /// Attempts to transition hart from START_PENDING to STARTED state.
+    ///
+    /// Returns inner data if successful, otherwise returns current state.
+    #[inline]
+    pub fn start(&self) -> Result<T, usize> {
+        loop {
+            match self.0.status.compare_exchange(
+                hart_state::START_PENDING,
+                hart_state::STARTED,
+                Ordering::AcqRel,
+                Ordering::Relaxed,
+            ) {
+                Ok(_) => break Ok(unsafe { (*self.0.inner.get()).take().unwrap() }),
+                Err(HART_STATE_START_PENDING_EXT) => spin_loop(),
+                Err(s) => break Err(s),
+            }
+        }
+    }
+
+    /// Transitions hart to STOPPED state.
+    #[allow(unused)]
+    #[inline]
+    pub fn stop(&self) {
+        self.0.status.store(hart_state::STOPPED, Ordering::Release)
+    }
+
+    /// Transitions hart to SUSPENDED state.
+    #[allow(unused)]
+    #[inline]
+    pub fn suspend(&self) {
+        self.0
+            .status
+            .store(hart_state::SUSPENDED, Ordering::Relaxed)
+    }
+
+    /// Transitions hart to STARTED state.
+    #[allow(unused)]
+    #[inline]
+    pub fn resume(&self) {
+        self.0.status.store(hart_state::STARTED, Ordering::Relaxed)
+    }
+}
+
+impl<T: core::fmt::Debug> RemoteHsmCell<'_, T> {
+    /// Attempts to start a stopped hart by providing startup data.
+    ///
+    /// Returns true if successful, false if hart was not in STOPPED state.
+    #[inline]
+    pub fn start(&self, t: T) -> bool {
+        if self
+            .0
+            .status
+            .compare_exchange(
+                hart_state::STOPPED,
+                HART_STATE_START_PENDING_EXT,
+                Ordering::Acquire,
+                Ordering::Relaxed,
+            )
+            .is_ok()
+        {
+            unsafe { *self.0.inner.get() = Some(t) };
+            self.0
+                .status
+                .store(hart_state::START_PENDING, Ordering::Release);
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Gets the current state of the hart.
+    #[allow(unused)]
+    #[inline]
+    pub fn sbi_get_status(&self) -> usize {
+        match self.0.status.load(Ordering::Relaxed) {
+            HART_STATE_START_PENDING_EXT => hart_state::START_PENDING,
+            normal => normal,
+        }
+    }
+
+    /// Checks if hart can receive IPIs (must be STARTED or SUSPENDED).
+    #[allow(unused)]
+    #[inline]
+    pub fn allow_ipi(&self) -> bool {
+        matches!(
+            self.0.status.load(Ordering::Relaxed),
+            hart_state::STARTED | hart_state::SUSPENDED
+        )
+    }
+}
+
+/// Gets the local HSM cell for the current hart.
+pub(crate) fn local_hsm() -> LocalHsmCell<'static, NextStage> {
+    unsafe {
+        ROOT_STACK
+            .get_unchecked_mut(current_hartid())
+            .hart_context()
+            .hsm
+            .local()
+    }
+}
+
+/// Gets a remote view of the current hart's HSM cell.
+pub(crate) fn local_remote_hsm() -> RemoteHsmCell<'static, NextStage> {
+    unsafe {
+        ROOT_STACK
+            .get_unchecked_mut(current_hartid())
+            .hart_context()
+            .hsm
+            .remote()
+    }
+}
+
+/// Gets a remote view of any hart's HSM cell.
+#[allow(unused)]
+pub(crate) fn remote_hsm(hart_id: usize) -> Option<RemoteHsmCell<'static, NextStage>> {
+    unsafe {
+        ROOT_STACK
+            .get_mut(hart_id)
+            .map(|x| x.hart_context().hsm.remote())
+    }
+}
+
+/// Implementation of SBI HSM (Hart State Management) extension.
+pub(crate) struct SbiHsm;
+
+impl rustsbi::Hsm for SbiHsm {
+    /// Starts execution on a stopped hart.
+    fn hart_start(&self, hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
+        match remote_hsm(hartid) {
+            Some(remote) => {
+                if remote.start(NextStage {
+                    start_addr,
+                    opaque,
+                    next_mode: MPP::Supervisor,
+                }) {
+                    unsafe {
+                        PLATFORM.sbi.ipi.as_ref().unwrap().set_msip(hartid);
+                    }
+                    SbiRet::success(0)
+                } else {
+                    SbiRet::already_available()
+                }
+            }
+            None => SbiRet::invalid_param(),
+        }
+    }
+
+    /// Stops execution on the current hart.
+    #[inline]
+    fn hart_stop(&self) -> SbiRet {
+        local_hsm().stop();
+        unsafe {
+            riscv::register::mie::clear_msoft();
+        }
+        riscv::asm::wfi();
+        SbiRet::success(0)
+    }
+
+    /// Gets the current state of a hart.
+    #[inline]
+    fn hart_get_status(&self, hartid: usize) -> SbiRet {
+        match remote_hsm(hartid) {
+            Some(remote) => SbiRet::success(remote.sbi_get_status()),
+            None => SbiRet::invalid_param(),
+        }
+    }
+
+    /// Suspends execution on the current hart.
+    fn hart_suspend(&self, suspend_type: u32, _resume_addr: usize, _opaque: usize) -> SbiRet {
+        use rustsbi::spec::hsm::suspend_type::{NON_RETENTIVE, RETENTIVE};
+        if matches!(suspend_type, NON_RETENTIVE | RETENTIVE) {
+            unsafe {
+                PLATFORM
+                    .sbi
+                    .ipi
+                    .as_ref()
+                    .unwrap()
+                    .clear_msip(current_hartid());
+            }
+            unsafe {
+                riscv::register::mie::set_msoft();
+            }
+            local_hsm().suspend();
+            riscv::asm::wfi();
+            crate::sbi::trap::handler::msoft_ipi_handler();
+            local_hsm().resume();
+            SbiRet::success(0)
+        } else {
+            SbiRet::not_supported()
+        }
+    }
+}

+ 292 - 0
prototyper/prototyper/src/sbi/ipi.rs

@@ -0,0 +1,292 @@
+use crate::platform::PLATFORM;
+use crate::riscv::csr::stimecmp;
+use crate::riscv::current_hartid;
+use crate::sbi::extensions::{Extension, hart_extension_probe};
+use crate::sbi::hsm::remote_hsm;
+use crate::sbi::rfence;
+use crate::sbi::trap_stack::ROOT_STACK;
+use alloc::boxed::Box;
+use core::sync::atomic::Ordering::Relaxed;
+use rustsbi::{HartMask, SbiRet};
+use spin::Mutex;
+
+/// IPI type for supervisor software interrupt.
+pub(crate) const IPI_TYPE_SSOFT: u8 = 1 << 0;
+/// IPI type for memory fence operations.
+pub(crate) const IPI_TYPE_FENCE: u8 = 1 << 1;
+
+/// Trait defining interface for inter-processor interrupt device
+#[allow(unused)]
+pub trait IpiDevice {
+    /// Read machine time value.
+    fn read_mtime(&self) -> u64;
+    /// Write machine time value.
+    fn write_mtime(&self, val: u64);
+    /// Read machine timer compare value for given hart.
+    fn read_mtimecmp(&self, hart_idx: usize) -> u64;
+    /// Write machine timer compare value for given hart.
+    fn write_mtimecmp(&self, hart_idx: usize, val: u64);
+    /// Read machine software interrupt pending bit for given hart.
+    fn read_msip(&self, hart_idx: usize) -> bool;
+    /// Set machine software interrupt pending bit for given hart.
+    fn set_msip(&self, hart_idx: usize);
+    /// Clear machine software interrupt pending bit for given hart.
+    fn clear_msip(&self, hart_idx: usize);
+}
+
+/// SBI IPI implementation.
+pub struct SbiIpi {
+    /// Reference to atomic pointer to IPI device.
+    pub ipi_dev: Mutex<Box<dyn IpiDevice>>,
+    /// Maximum hart ID in the system
+    pub max_hart_id: usize,
+}
+
+impl rustsbi::Timer for SbiIpi {
+    /// Set timer value for current hart.
+    #[inline]
+    fn set_timer(&self, stime_value: u64) {
+        let hart_id = current_hartid();
+        let uses_sstc = hart_extension_probe(hart_id, Extension::Sstc);
+
+        // Set timer value based on extension support.
+        if uses_sstc {
+            stimecmp::set(stime_value);
+        } else {
+            self.write_mtimecmp(hart_id, stime_value);
+            unsafe {
+                riscv::register::mip::clear_stimer();
+            }
+        }
+        // Enable machine timer interrupt.
+        unsafe {
+            riscv::register::mie::set_mtimer();
+        }
+    }
+}
+
+impl rustsbi::Ipi for SbiIpi {
+    /// Send IPI to specified harts.
+    #[inline]
+    fn send_ipi(&self, hart_mask: rustsbi::HartMask) -> SbiRet {
+        let mut hart_mask = hart_mask;
+
+        for hart_id in 0..=self.max_hart_id {
+            if !hart_mask.has_bit(hart_id) {
+                continue;
+            }
+
+            // There are 2 situation to return invalid_param:
+            // 1. We can not get hsm, which usually means this hart_id is bigger than MAX_HART_ID.
+            // 2. BOARD hasn't init or this hart_id is not enabled by device tree.
+            // In the next loop, we'll assume that all of above situation will not happened and
+            // directly send ipi.
+            let Some(hsm) = remote_hsm(hart_id) else {
+                return SbiRet::invalid_param();
+            };
+
+            if unsafe {
+                PLATFORM
+                    .info
+                    .cpu_enabled
+                    .is_none_or(|list| list.get(hart_id).is_none_or(|res| !(*res)))
+            } {
+                return SbiRet::invalid_param();
+            }
+
+            if !hsm.allow_ipi() {
+                hart_mask = hart_mask_clear(hart_mask, hart_id);
+            }
+        }
+        for hart_id in 0..=self.max_hart_id {
+            if !hart_mask.has_bit(hart_id) {
+                continue;
+            }
+
+            if set_ipi_type(hart_id, IPI_TYPE_SSOFT) == 0 {
+                self.set_msip(hart_id);
+            }
+        }
+
+        SbiRet::success(0)
+    }
+}
+
+impl SbiIpi {
+    /// Create new SBI IPI instance.
+    #[inline]
+    pub fn new(ipi_dev: Mutex<Box<dyn IpiDevice>>, max_hart_id: usize) -> Self {
+        Self {
+            ipi_dev,
+            max_hart_id,
+        }
+    }
+
+    /// Send IPI for remote fence operation.
+    pub fn send_ipi_by_fence(
+        &self,
+        hart_mask: rustsbi::HartMask,
+        ctx: rfence::RFenceContext,
+    ) -> SbiRet {
+        let current_hart = current_hartid();
+        let mut hart_mask = hart_mask;
+
+        for hart_id in 0..=self.max_hart_id {
+            if !hart_mask.has_bit(hart_id) {
+                continue;
+            }
+
+            // There are 2 situation to return invalid_param:
+            // 1. We can not get hsm, which usually means this hart_id is bigger than MAX_HART_ID.
+            // 2. BOARD hasn't init or this hart_id is not enabled by device tree.
+            // In the next loop, we'll assume that all of above situation will not happened and
+            // directly send ipi.
+            let Some(hsm) = remote_hsm(hart_id) else {
+                return SbiRet::invalid_param();
+            };
+
+            if unsafe {
+                PLATFORM
+                    .info
+                    .cpu_enabled
+                    .is_none_or(|list| list.get(hart_id).is_none_or(|res| !(*res)))
+            } {
+                return SbiRet::invalid_param();
+            }
+
+            if !hsm.allow_ipi() {
+                hart_mask = hart_mask_clear(hart_mask, hart_id);
+            }
+        }
+
+        // Send fence operations to target harts
+        for hart_id in 0..=self.max_hart_id {
+            if !hart_mask.has_bit(hart_id) {
+                continue;
+            }
+
+            if let Some(remote) = rfence::remote_rfence(hart_id) {
+                if let Some(local) = rfence::local_rfence() {
+                    local.add();
+                }
+                remote.set(ctx);
+                if hart_id != current_hart {
+                    let old_ipi_type = set_ipi_type(hart_id, IPI_TYPE_FENCE);
+                    if old_ipi_type == 0 {
+                        self.set_msip(hart_id);
+                    }
+                }
+            }
+        }
+
+        // Wait for all fence operations to complete
+        while !rfence::local_rfence().unwrap().is_sync() {
+            rfence::rfence_single_handler();
+        }
+
+        SbiRet::success(0)
+    }
+
+    /// Get lower 32 bits of machine time.
+    #[inline]
+    pub fn get_time(&self) -> usize {
+        self.ipi_dev.lock().read_mtime() as usize
+    }
+
+    /// Get upper 32 bits of machine time.
+    #[inline]
+    pub fn get_timeh(&self) -> usize {
+        (self.ipi_dev.lock().read_mtime() >> 32) as usize
+    }
+
+    /// Set machine software interrupt pending for hart.
+    #[inline]
+    pub fn set_msip(&self, hart_idx: usize) {
+        self.ipi_dev.lock().set_msip(hart_idx);
+    }
+
+    /// Clear machine software interrupt pending for hart.
+    #[inline]
+    pub fn clear_msip(&self, hart_idx: usize) {
+        self.ipi_dev.lock().clear_msip(hart_idx);
+    }
+
+    /// Write machine timer compare value for hart.
+    #[inline]
+    pub fn write_mtimecmp(&self, hart_idx: usize, val: u64) {
+        self.ipi_dev.lock().write_mtimecmp(hart_idx, val);
+    }
+
+    /// Clear all pending interrupts for current hart.
+    #[inline]
+    pub fn clear(&self) {
+        let hart_id = current_hartid();
+        // Load ipi_dev once instead of twice
+        let ipi_dev = self.ipi_dev.lock();
+        ipi_dev.clear_msip(hart_id);
+        ipi_dev.write_mtimecmp(hart_id, u64::MAX);
+    }
+}
+
+/// Set IPI type for specified hart.
+pub fn set_ipi_type(hart_id: usize, event_id: u8) -> u8 {
+    unsafe {
+        ROOT_STACK
+            .get_unchecked_mut(hart_id)
+            .hart_context()
+            .ipi_type
+            .fetch_or(event_id, Relaxed)
+    }
+}
+
+/// Get and reset IPI type for current hart.
+pub fn get_and_reset_ipi_type() -> u8 {
+    unsafe {
+        ROOT_STACK
+            .get_unchecked_mut(current_hartid())
+            .hart_context()
+            .ipi_type
+            .swap(0, Relaxed)
+    }
+}
+
+/// Clear machine software interrupt pending for current hart.
+#[inline]
+pub fn clear_msip() {
+    match unsafe { PLATFORM.sbi.ipi.as_ref() } {
+        Some(ipi) => ipi.clear_msip(current_hartid()),
+        None => error!("SBI or IPI device not initialized"),
+    }
+}
+
+/// Clear machine timer interrupt for current hart.
+#[inline]
+pub fn clear_mtime() {
+    match unsafe { PLATFORM.sbi.ipi.as_ref() } {
+        Some(ipi) => ipi.write_mtimecmp(current_hartid(), u64::MAX),
+        None => error!("SBI or IPI device not initialized"),
+    }
+}
+
+/// Clear all pending interrupts for current hart.
+#[inline]
+pub fn clear_all() {
+    match unsafe { PLATFORM.sbi.ipi.as_ref() } {
+        Some(ipi) => ipi.clear(),
+        None => error!("SBI or IPI device not initialized"),
+    }
+}
+
+pub fn hart_mask_clear(hart_mask: HartMask, hart_id: usize) -> HartMask {
+    let (mask, mask_base) = hart_mask.into_inner();
+    if mask_base == usize::MAX {
+        return HartMask::from_mask_base(mask & (!(1 << hart_id)), 0);
+    }
+    let Some(idx) = hart_id.checked_sub(mask_base) else {
+        return hart_mask;
+    };
+    if idx >= usize::BITS as usize {
+        return hart_mask;
+    }
+    HartMask::from_mask_base(mask & (!(1 << hart_id)), mask_base)
+}

+ 55 - 0
prototyper/prototyper/src/sbi/logger.rs

@@ -0,0 +1,55 @@
+use core::str::FromStr;
+use log::{Level, LevelFilter};
+
+/// Simple logger implementation for RustSBI that supports colored output.
+pub struct Logger;
+
+impl Logger {
+    /// Initialize the logger with log level from RUST_LOG env var or default to Info.
+    pub fn init() -> Result<(), log::SetLoggerError> {
+        // Set max log level from RUST_LOG env var if present, otherwise use Info
+        let max_level = option_env!("RUST_LOG")
+            .and_then(|s| LevelFilter::from_str(s).ok())
+            .unwrap_or(LevelFilter::Info);
+
+        log::set_max_level(max_level);
+        log::set_logger(&Logger)
+    }
+}
+
+impl log::Log for Logger {
+    // Always enable logging for all log levels
+    #[inline]
+    fn enabled(&self, _metadata: &log::Metadata) -> bool {
+        true
+    }
+
+    // Log messages with color-coded levels
+    #[inline]
+    fn log(&self, record: &log::Record) {
+        // ANSI color codes for different log levels
+        const ERROR_COLOR: u8 = 31; // Red
+        const WARN_COLOR: u8 = 93; // Bright yellow
+        const INFO_COLOR: u8 = 32; // Green
+        const DEBUG_COLOR: u8 = 36; // Cyan
+        const TRACE_COLOR: u8 = 90; // Bright black
+
+        let color_code = match record.level() {
+            Level::Error => ERROR_COLOR,
+            Level::Warn => WARN_COLOR,
+            Level::Info => INFO_COLOR,
+            Level::Debug => DEBUG_COLOR,
+            Level::Trace => TRACE_COLOR,
+        };
+
+        println!(
+            "\x1b[1;37m[RustSBI] \x1b[1;{color_code}m{:^5}\x1b[0m - {}",
+            record.level(),
+            record.args(),
+        );
+    }
+
+    // No-op flush since we use println! which is already line-buffered
+    #[inline]
+    fn flush(&self) {}
+}

+ 50 - 0
prototyper/prototyper/src/sbi/mod.rs

@@ -0,0 +1,50 @@
+use rustsbi::RustSBI;
+
+pub mod console;
+pub mod hsm;
+pub mod ipi;
+pub mod reset;
+pub mod rfence;
+
+pub mod early_trap;
+pub mod extensions;
+pub mod fifo;
+pub mod hart_context;
+pub mod heap;
+pub mod logger;
+pub mod trap;
+pub mod trap_stack;
+
+use console::SbiConsole;
+use hsm::SbiHsm;
+use ipi::SbiIpi;
+use reset::SbiReset;
+use rfence::SbiRFence;
+
+#[derive(RustSBI, Default)]
+#[rustsbi(dynamic)]
+#[allow(clippy::upper_case_acronyms)]
+pub struct SBI {
+    #[rustsbi(console)]
+    pub console: Option<SbiConsole>,
+    #[rustsbi(ipi, timer)]
+    pub ipi: Option<SbiIpi>,
+    #[rustsbi(hsm)]
+    pub hsm: Option<SbiHsm>,
+    #[rustsbi(reset)]
+    pub reset: Option<SbiReset>,
+    #[rustsbi(fence)]
+    pub rfence: Option<SbiRFence>,
+}
+
+impl SBI {
+    pub const fn new() -> Self {
+        SBI {
+            console: None,
+            ipi: None,
+            hsm: None,
+            reset: None,
+            rfence: None,
+        }
+    }
+}

+ 60 - 0
prototyper/prototyper/src/sbi/reset.rs

@@ -0,0 +1,60 @@
+use alloc::boxed::Box;
+use rustsbi::SbiRet;
+use spin::Mutex;
+
+use crate::platform::PLATFORM;
+
+pub trait ResetDevice {
+    fn fail(&self, code: u16) -> !;
+    fn pass(&self) -> !;
+    fn reset(&self) -> !;
+}
+
+pub struct SbiReset {
+    pub reset_dev: Mutex<Box<dyn ResetDevice>>,
+}
+
+impl SbiReset {
+    pub fn new(reset_dev: Mutex<Box<dyn ResetDevice>>) -> Self {
+        Self { reset_dev }
+    }
+
+    #[allow(unused)]
+    pub fn fail(&self) -> ! {
+        trace!("Test fail, invoke process exit procedure on Reset device");
+        self.reset_dev.lock().fail(0);
+    }
+}
+
+impl rustsbi::Reset for SbiReset {
+    #[inline]
+    fn system_reset(&self, reset_type: u32, reset_reason: u32) -> SbiRet {
+        use rustsbi::spec::srst::{
+            RESET_REASON_NO_REASON, RESET_REASON_SYSTEM_FAILURE, RESET_TYPE_COLD_REBOOT,
+            RESET_TYPE_SHUTDOWN, RESET_TYPE_WARM_REBOOT,
+        };
+        match reset_type {
+            RESET_TYPE_SHUTDOWN => match reset_reason {
+                RESET_REASON_NO_REASON => self.reset_dev.lock().pass(),
+                RESET_REASON_SYSTEM_FAILURE => self.reset_dev.lock().fail(u16::MAX),
+                value => self.reset_dev.lock().fail(value as _),
+            },
+            RESET_TYPE_COLD_REBOOT | RESET_TYPE_WARM_REBOOT => self.reset_dev.lock().reset(),
+
+            _ => SbiRet::invalid_param(),
+        }
+    }
+}
+
+#[allow(unused)]
+pub fn fail() -> ! {
+    match unsafe { PLATFORM.sbi.reset.as_ref() } {
+        Some(reset) => reset.fail(),
+        None => {
+            trace!("test fail, begin dead loop");
+            loop {
+                core::hint::spin_loop()
+            }
+        }
+    }
+}

+ 323 - 0
prototyper/prototyper/src/sbi/rfence.rs

@@ -0,0 +1,323 @@
+use rustsbi::{HartMask, SbiRet};
+use spin::Mutex;
+
+use crate::cfg::{PAGE_SIZE, TLB_FLUSH_LIMIT};
+use crate::platform::PLATFORM;
+use crate::riscv::current_hartid;
+use crate::sbi::fifo::{Fifo, FifoError};
+use crate::sbi::trap_stack::ROOT_STACK;
+use core::arch::asm;
+
+use core::sync::atomic::{AtomicU32, Ordering};
+
+/// Cell for managing remote fence operations between harts.
+pub(crate) struct RFenceCell {
+    // Queue of fence operations with source hart ID
+    queue: Mutex<Fifo<(RFenceContext, usize)>>,
+    // Counter for tracking pending synchronization operations
+    wait_sync_count: AtomicU32,
+}
+
+/// Context information for a remote fence operation.
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct RFenceContext {
+    /// Start address of memory region to fence.
+    pub start_addr: usize,
+    /// Size of memory region to fence.
+    pub size: usize,
+    /// Address space ID.
+    pub asid: usize,
+    /// Virtual machine ID.
+    pub vmid: usize,
+    /// Type of fence operation.
+    pub op: RFenceType,
+}
+
+/// Types of remote fence operations supported.
+#[allow(unused)]
+#[derive(Clone, Copy, Debug)]
+pub enum RFenceType {
+    /// Instruction fence.
+    FenceI,
+    /// Supervisor fence for virtual memory.
+    SFenceVma,
+    /// Supervisor fence for virtual memory with ASID.
+    SFenceVmaAsid,
+    /// Hypervisor fence for guest virtual memory with VMID.
+    HFenceGvmaVmid,
+    /// Hypervisor fence for guest virtual memory.
+    HFenceGvma,
+    /// Hypervisor fence for virtual machine virtual memory with ASID.
+    HFenceVvmaAsid,
+    /// Hypervisor fence for virtual machine virtual memory.
+    HFenceVvma,
+}
+
+impl RFenceCell {
+    /// Creates a new RFenceCell with empty queue and zero sync count.
+    pub fn new() -> Self {
+        Self {
+            queue: Mutex::new(Fifo::new()),
+            wait_sync_count: AtomicU32::new(0),
+        }
+    }
+
+    /// Gets a local view of this fence cell for the current hart.
+    #[inline]
+    pub fn local(&self) -> LocalRFenceCell<'_> {
+        LocalRFenceCell(self)
+    }
+
+    /// Gets a remote view of this fence cell for accessing from other harts.
+    #[inline]
+    pub fn remote(&self) -> RemoteRFenceCell<'_> {
+        RemoteRFenceCell(self)
+    }
+}
+
+// Mark RFenceCell as safe to share between threads
+unsafe impl Sync for RFenceCell {}
+unsafe impl Send for RFenceCell {}
+
+/// View of RFenceCell for operations on the current hart.
+pub struct LocalRFenceCell<'a>(&'a RFenceCell);
+
+/// View of RFenceCell for operations from other harts.
+pub struct RemoteRFenceCell<'a>(&'a RFenceCell);
+
+/// Gets the local fence context for the current hart.
+pub(crate) fn local_rfence() -> Option<LocalRFenceCell<'static>> {
+    unsafe {
+        ROOT_STACK
+            .get_mut(current_hartid())
+            .map(|x| x.hart_context().rfence.local())
+    }
+}
+
+/// Gets the remote fence context for a specific hart.
+pub(crate) fn remote_rfence(hart_id: usize) -> Option<RemoteRFenceCell<'static>> {
+    unsafe {
+        ROOT_STACK
+            .get_mut(hart_id)
+            .map(|x| x.hart_context().rfence.remote())
+    }
+}
+
+#[allow(unused)]
+impl LocalRFenceCell<'_> {
+    /// Checks if all synchronization operations are complete.
+    pub fn is_sync(&self) -> bool {
+        self.0.wait_sync_count.load(Ordering::Relaxed) == 0
+    }
+
+    /// Increments the synchronization counter.
+    pub fn add(&self) {
+        self.0.wait_sync_count.fetch_add(1, Ordering::Relaxed);
+    }
+
+    /// Checks if the operation queue is empty.
+    pub fn is_empty(&self) -> bool {
+        self.0.queue.lock().is_empty()
+    }
+
+    /// Gets the next fence operation from the queue.
+    pub fn get(&self) -> Option<(RFenceContext, usize)> {
+        self.0.queue.lock().pop().ok()
+    }
+
+    /// Adds a fence operation to the queue, retrying if full.
+    pub fn set(&self, ctx: RFenceContext) {
+        let hart_id = current_hartid();
+        loop {
+            let mut queue = self.0.queue.lock();
+            match queue.push((ctx, hart_id)) {
+                Ok(_) => break,
+                Err(FifoError::Full) => {
+                    drop(queue);
+                    rfence_single_handler();
+                }
+                Err(_) => panic!("Unable to push fence ops to fifo"),
+            }
+        }
+    }
+}
+
+#[allow(unused)]
+impl RemoteRFenceCell<'_> {
+    /// Adds a fence operation to the queue from a remote hart.
+    pub fn set(&self, ctx: RFenceContext) {
+        let hart_id = current_hartid();
+        loop {
+            let mut queue = self.0.queue.lock();
+            match queue.push((ctx, hart_id)) {
+                Ok(_) => return,
+                Err(FifoError::Full) => {
+                    drop(queue);
+                    rfence_single_handler();
+                }
+                Err(_) => panic!("Unable to push fence ops to fifo"),
+            }
+        }
+    }
+
+    /// Decrements the synchronization counter.
+    pub fn sub(&self) {
+        self.0.wait_sync_count.fetch_sub(1, Ordering::Relaxed);
+    }
+}
+
+/// Implementation of RISC-V remote fence operations.
+pub(crate) struct SbiRFence;
+
+/// Validates address range for fence operations
+#[inline(always)]
+fn validate_address_range(start_addr: usize, size: usize) -> Result<usize, SbiRet> {
+    // Check page alignment using bitwise AND instead of modulo
+    if start_addr & 0xFFF != 0 {
+        return Err(SbiRet::invalid_address());
+    }
+
+    // Avoid checked_add by checking for overflow directly
+    if size > usize::MAX - start_addr {
+        return Err(SbiRet::invalid_address());
+    }
+
+    Ok(size)
+}
+
+/// Processes a remote fence operation by sending IPI to target harts.
+fn remote_fence_process(rfence_ctx: RFenceContext, hart_mask: HartMask) -> SbiRet {
+    let sbi_ret = unsafe { PLATFORM.sbi.ipi.as_ref() }
+        .unwrap()
+        .send_ipi_by_fence(hart_mask, rfence_ctx);
+
+    sbi_ret
+}
+
+impl rustsbi::Fence for SbiRFence {
+    /// Remote instruction fence for specified harts.
+    fn remote_fence_i(&self, hart_mask: HartMask) -> SbiRet {
+        remote_fence_process(
+            RFenceContext {
+                start_addr: 0,
+                size: 0,
+                asid: 0,
+                vmid: 0,
+                op: RFenceType::FenceI,
+            },
+            hart_mask,
+        )
+    }
+
+    /// Remote supervisor fence for virtual memory on specified harts.
+    fn remote_sfence_vma(&self, hart_mask: HartMask, start_addr: usize, size: usize) -> SbiRet {
+        let flush_size = match validate_address_range(start_addr, size) {
+            Ok(size) => size,
+            Err(e) => return e,
+        };
+
+        remote_fence_process(
+            RFenceContext {
+                start_addr,
+                size: flush_size,
+                asid: 0,
+                vmid: 0,
+                op: RFenceType::SFenceVma,
+            },
+            hart_mask,
+        )
+    }
+
+    /// Remote supervisor fence for virtual memory with ASID on specified harts.
+    fn remote_sfence_vma_asid(
+        &self,
+        hart_mask: HartMask,
+        start_addr: usize,
+        size: usize,
+        asid: usize,
+    ) -> SbiRet {
+        let flush_size = match validate_address_range(start_addr, size) {
+            Ok(size) => size,
+            Err(e) => return e,
+        };
+
+        remote_fence_process(
+            RFenceContext {
+                start_addr,
+                size: flush_size,
+                asid,
+                vmid: 0,
+                op: RFenceType::SFenceVmaAsid,
+            },
+            hart_mask,
+        )
+    }
+}
+
+/// Handles a single remote fence operation.
+#[inline]
+pub fn rfence_single_handler() {
+    let rfence_context = local_rfence().unwrap().get();
+    if let Some((ctx, id)) = rfence_context {
+        match ctx.op {
+            // Handle instruction fence
+            RFenceType::FenceI => unsafe {
+                asm!("fence.i");
+                remote_rfence(id).unwrap().sub();
+            },
+            // Handle virtual memory address fence
+            RFenceType::SFenceVma => {
+                // If the flush size is greater than the maximum limit then simply flush all
+                if (ctx.start_addr == 0 && ctx.size == 0)
+                    || (ctx.size == usize::MAX)
+                    || (ctx.size > TLB_FLUSH_LIMIT)
+                {
+                    unsafe {
+                        asm!("sfence.vma");
+                    }
+                } else {
+                    for offset in (0..ctx.size).step_by(PAGE_SIZE) {
+                        let addr = ctx.start_addr + offset;
+                        unsafe {
+                            asm!("sfence.vma {}", in(reg) addr);
+                        }
+                    }
+                }
+                remote_rfence(id).unwrap().sub();
+            }
+            // Handle virtual memory address fence with ASID
+            RFenceType::SFenceVmaAsid => {
+                let asid = ctx.asid;
+                // If the flush size is greater than the maximum limit then simply flush all
+                if (ctx.start_addr == 0 && ctx.size == 0)
+                    || (ctx.size == usize::MAX)
+                    || (ctx.size > TLB_FLUSH_LIMIT)
+                {
+                    unsafe {
+                        asm!("sfence.vma {}, {}", in(reg) 0, in(reg) asid);
+                    }
+                } else {
+                    for offset in (0..ctx.size).step_by(PAGE_SIZE) {
+                        let addr = ctx.start_addr + offset;
+                        unsafe {
+                            asm!("sfence.vma {}, {}", in(reg) addr, in(reg) asid);
+                        }
+                    }
+                }
+                remote_rfence(id).unwrap().sub();
+            }
+            rfencetype => {
+                error!("Unsupported RFence Type: {:?}!", rfencetype);
+            }
+        }
+    }
+}
+
+/// Process all pending remote fence operations.
+#[inline]
+pub fn rfence_handler() {
+    while !local_rfence().unwrap().is_empty() {
+        rfence_single_handler();
+    }
+}

+ 86 - 0
prototyper/prototyper/src/sbi/trap/boot.rs

@@ -0,0 +1,86 @@
+use crate::riscv::current_hartid;
+use crate::sbi::hsm::local_hsm;
+use crate::sbi::ipi;
+use core::arch::naked_asm;
+use riscv::register::{mie, mstatus, satp, sstatus};
+
+/// Boot Function.
+/// After boot, this flow will never back again,
+/// so we can store a0, a1 and mepc only.
+#[naked]
+pub unsafe extern "C" fn boot() -> ! {
+    unsafe {
+        naked_asm!(
+            ".align 2",
+            // Switch stacks
+            "csrrw  sp, mscratch, sp",
+            // Allocate stack space
+            "addi   sp, sp, -3*8",
+            // Call handler with context pointer
+            "mv     a0, sp",
+            "call   {boot_handler}",
+            // Restore mepc
+            "ld     t0, 0*8(sp)
+            csrw    mepc, t0",
+            // Restore registers
+            "
+        ld      a0, 1*8(sp)
+        ld      a1, 2*8(sp)",
+            // Restore stack pointer
+            "addi   sp, sp, 3*8",
+            // Switch stacks back
+            "csrrw  sp, mscratch, sp",
+            // Return from machine mode
+            "mret",
+            boot_handler = sym boot_handler,
+        );
+    }
+}
+
+/// Boot Handler.
+pub extern "C" fn boot_handler(ctx: &mut BootContext) {
+    #[inline(always)]
+    fn boot(ctx: &mut BootContext, start_addr: usize, opaque: usize) {
+        unsafe {
+            sstatus::clear_sie();
+            satp::write(0);
+        }
+        ctx.a0 = current_hartid();
+        ctx.a1 = opaque;
+        ctx.mepc = start_addr;
+    }
+
+    match local_hsm().start() {
+        // Handle HSM Start
+        Ok(next_stage) => {
+            ipi::clear_msip();
+            unsafe {
+                mstatus::set_mpie();
+                mstatus::set_mpp(next_stage.next_mode);
+                mie::set_msoft();
+                mie::set_mtimer();
+            }
+            boot(ctx, next_stage.start_addr, next_stage.opaque);
+        }
+        // Handle HSM Stop
+        Err(rustsbi::spec::hsm::HART_STOP) => {
+            ipi::clear_msip();
+            unsafe {
+                mie::set_msoft();
+            }
+            riscv::asm::wfi();
+        }
+        _ => {
+            unreachable!("Boot stage hsm should be start or stop.");
+        }
+    }
+}
+
+/// Boot context structure containing saved register state.
+#[derive(Debug)]
+#[repr(C)]
+pub struct BootContext {
+    pub mepc: usize, // 0
+    pub a0: usize,
+    pub a1: usize, // 2
+}

+ 182 - 0
prototyper/prototyper/src/sbi/trap/handler.rs

@@ -0,0 +1,182 @@
+use fast_trap::{FastContext, FastResult};
+use riscv::register::{mepc, mie, mstatus, satp, sstatus};
+use rustsbi::RustSBI;
+
+use crate::platform::PLATFORM;
+use crate::riscv::csr::{CSR_TIME, CSR_TIMEH};
+use crate::riscv::current_hartid;
+use crate::sbi::console;
+use crate::sbi::hsm::local_hsm;
+use crate::sbi::ipi;
+use crate::sbi::rfence;
+
+#[inline]
+pub fn switch(mut ctx: FastContext, start_addr: usize, opaque: usize) -> FastResult {
+    unsafe {
+        sstatus::clear_sie();
+        satp::write(0);
+    }
+
+    ctx.regs().a[0] = current_hartid();
+    ctx.regs().a[1] = opaque;
+    ctx.regs().pc = start_addr;
+    ctx.call(2)
+}
+
+/// Handle machine software inter-processor interrupts.
+#[inline]
+pub fn msoft_ipi_handler() {
+    use ipi::get_and_reset_ipi_type;
+    ipi::clear_msip();
+    let ipi_type = get_and_reset_ipi_type();
+    // Handle supervisor software interrupt
+    if (ipi_type & ipi::IPI_TYPE_SSOFT) != 0 {
+        unsafe {
+            riscv::register::mip::set_ssoft();
+        }
+    }
+    // Handle fence operation
+    if (ipi_type & ipi::IPI_TYPE_FENCE) != 0 {
+        rfence::rfence_handler();
+    }
+}
+
+#[inline]
+pub fn msoft_handler(ctx: FastContext) -> FastResult {
+    match local_hsm().start() {
+        // Handle HSM Start
+        Ok(next_stage) => {
+            ipi::clear_msip();
+            unsafe {
+                mstatus::set_mpie();
+                mstatus::set_mpp(next_stage.next_mode);
+                mie::set_msoft();
+                mie::set_mtimer();
+            }
+            switch(ctx, next_stage.start_addr, next_stage.opaque)
+        }
+        // Handle HSM Stop
+        Err(rustsbi::spec::hsm::HART_STOP) => {
+            ipi::clear_msip();
+            unsafe {
+                mie::set_msoft();
+            }
+            riscv::asm::wfi();
+            ctx.restore()
+        }
+        // Handle RFence
+        _ => {
+            msoft_ipi_handler();
+            ctx.restore()
+        }
+    }
+}
+
+#[inline]
+#[allow(clippy::too_many_arguments)]
+pub fn sbi_call_handler(
+    mut ctx: FastContext,
+    a1: usize,
+    a2: usize,
+    a3: usize,
+    a4: usize,
+    a5: usize,
+    a6: usize,
+    a7: usize,
+) -> FastResult {
+    use sbi_spec::{base, hsm, legacy};
+    let mut ret = unsafe {
+        PLATFORM
+            .sbi
+            .handle_ecall(a7, a6, [ctx.a0(), a1, a2, a3, a4, a5])
+    };
+    if ret.is_ok() {
+        match (a7, a6) {
+            // Handle non-retentive suspend
+            (hsm::EID_HSM, hsm::HART_SUSPEND)
+                if matches!(ctx.a0() as u32, hsm::suspend_type::NON_RETENTIVE) =>
+            {
+                return switch(ctx, a1, a2);
+            }
+            // Handle legacy console probe
+            (base::EID_BASE, base::PROBE_EXTENSION)
+                if matches!(
+                    ctx.a0(),
+                    legacy::LEGACY_CONSOLE_PUTCHAR | legacy::LEGACY_CONSOLE_GETCHAR
+                ) =>
+            {
+                ret.value = 1;
+            }
+            _ => {}
+        }
+    } else {
+        match a7 {
+            legacy::LEGACY_CONSOLE_PUTCHAR => {
+                ret.error = console::putchar(ctx.a0());
+                ret.value = a1;
+            }
+            legacy::LEGACY_CONSOLE_GETCHAR => {
+                ret.error = console::getchar();
+                ret.value = a1;
+            }
+            _ => {}
+        }
+    }
+    ctx.regs().a = [ret.error, ret.value, a2, a3, a4, a5, a6, a7];
+    mepc::write(mepc::read() + 4);
+    ctx.restore()
+}
+
+/// Delegate trap handling to supervisor mode.
+#[inline]
+pub fn delegate(ctx: &mut FastContext) {
+    use riscv::register::{mcause, mepc, mtval, scause, sepc, sstatus, stval, stvec};
+    unsafe {
+        sepc::write(ctx.regs().pc);
+        scause::write(mcause::read().bits());
+        stval::write(mtval::read());
+        sstatus::clear_sie();
+        if mstatus::read().mpp() == mstatus::MPP::Supervisor {
+            sstatus::set_spp(sstatus::SPP::Supervisor);
+        } else {
+            sstatus::set_spp(sstatus::SPP::User);
+        }
+        mstatus::set_mpp(mstatus::MPP::Supervisor);
+        mepc::write(stvec::read().address());
+    }
+}
+
+/// Handle illegal instructions, particularly CSR access.
+#[inline]
+pub fn illegal_instruction_handler(ctx: &mut FastContext) -> bool {
+    use riscv::register::{mepc, mtval};
+    use riscv_decode::{Instruction, decode};
+
+    let inst = decode(mtval::read() as u32);
+    match inst {
+        Ok(Instruction::Csrrs(csr)) => match csr.csr() {
+            CSR_TIME => {
+                assert!(
+                    10 <= csr.rd() && csr.rd() <= 17,
+                    "Unsupported CSR rd: {}",
+                    csr.rd()
+                );
+                ctx.regs().a[(csr.rd() - 10) as usize] =
+                    unsafe { PLATFORM.sbi.ipi.as_ref() }.unwrap().get_time();
+            }
+            CSR_TIMEH => {
+                assert!(
+                    10 <= csr.rd() && csr.rd() <= 17,
+                    "Unsupported CSR rd: {}",
+                    csr.rd()
+                );
+                ctx.regs().a[(csr.rd() - 10) as usize] =
+                    unsafe { PLATFORM.sbi.ipi.as_ref() }.unwrap().get_timeh();
+            }
+            _ => return false,
+        },
+        _ => return false,
+    }
+    mepc::write(mepc::read() + 4);
+    true
+}

+ 75 - 0
prototyper/prototyper/src/sbi/trap/mod.rs

@@ -0,0 +1,75 @@
+pub mod boot;
+pub mod handler;
+
+use crate::fail::unsupported_trap;
+
+use fast_trap::{FastContext, FastResult};
+use riscv::interrupt::machine::{Exception, Interrupt};
+use riscv::register::{
+    mcause::{self, Trap},
+    mepc, mip, mstatus,
+};
+
+/// Fast trap handler for all trap.
+pub extern "C" fn fast_handler(
+    mut ctx: FastContext,
+    a1: usize,
+    a2: usize,
+    a3: usize,
+    a4: usize,
+    a5: usize,
+    a6: usize,
+    a7: usize,
+) -> FastResult {
+    // Save mepc into context
+    ctx.regs().pc = mepc::read();
+
+    let save_regs = |ctx: &mut FastContext| {
+        ctx.regs().a = [ctx.a0(), a1, a2, a3, a4, a5, a6, a7];
+    };
+
+    match mcause::read().cause().try_into() {
+        Ok(cause) => {
+            match cause {
+                // Handle Msoft
+                Trap::Interrupt(Interrupt::MachineSoft) => {
+                    save_regs(&mut ctx);
+                    handler::msoft_handler(ctx)
+                }
+                // Handle MTimer
+                Trap::Interrupt(Interrupt::MachineTimer) => {
+                    use crate::sbi::ipi;
+
+                    ipi::clear_mtime();
+                    unsafe {
+                        mip::clear_stimer();
+                    }
+                    save_regs(&mut ctx);
+                    ctx.restore()
+                }
+                // Handle SBI calls
+                Trap::Exception(Exception::SupervisorEnvCall) => {
+                    handler::sbi_call_handler(ctx, a1, a2, a3, a4, a5, a6, a7)
+                }
+                // Handle illegal instructions
+                Trap::Exception(Exception::IllegalInstruction) => {
+                    if mstatus::read().mpp() == mstatus::MPP::Machine {
+                        panic!("Cannot handle illegal instruction exception from M-MODE");
+                    }
+
+                    save_regs(&mut ctx);
+                    if !handler::illegal_instruction_handler(&mut ctx) {
+                        handler::delegate(&mut ctx);
+                    }
+                    ctx.restore()
+                }
+                // Handle other traps
+                trap => unsupported_trap(Some(trap)),
+            }
+        }
+        Err(err) => {
+            error!("Failed to parse mcause: {:?}", err);
+            unsupported_trap(None);
+        }
+    }
+}

+ 88 - 0
prototyper/prototyper/src/sbi/trap_stack.rs

@@ -0,0 +1,88 @@
+use crate::cfg::{LEN_STACK_PER_HART, NUM_HART_MAX};
+use crate::riscv::current_hartid;
+use crate::sbi::hart_context::HartContext;
+use crate::sbi::trap::fast_handler;
+use core::mem::forget;
+use fast_trap::FreeTrapStack;
+
+/// Root stack array for all harts, placed in BSS Stack section.
+#[unsafe(link_section = ".bss.stack")]
+pub(crate) static mut ROOT_STACK: [Stack; NUM_HART_MAX] = [Stack::ZERO; NUM_HART_MAX];
+
+/// Locates and initializes stack for each hart.
+///
+/// This is a naked function that sets up the stack pointer based on hart ID.
+#[naked]
+pub(crate) unsafe extern "C" fn locate() {
+    unsafe {
+        core::arch::naked_asm!(
+            "   la   sp, {stack}            // Load stack base address
+            li   t0, {per_hart_stack_size} // Load stack size per hart
+            csrr t1, mhartid            // Get current hart ID
+            addi t1, t1,  1             // Add 1 to hart ID
+         1: add  sp, sp, t0             // Calculate stack pointer
+            addi t1, t1, -1             // Decrement counter
+            bnez t1, 1b                 // Loop if not zero
+            call t1, {move_stack}       // Call stack reuse function
+            ret                         // Return
+        ",
+            per_hart_stack_size = const LEN_STACK_PER_HART,
+            stack               =   sym ROOT_STACK,
+            move_stack          =   sym fast_trap::reuse_stack_for_trap,
+        )
+    }
+}
+
+/// Prepares trap stack for current hart
+pub(crate) fn prepare_for_trap() {
+    unsafe {
+        ROOT_STACK
+            .get_unchecked_mut(current_hartid())
+            .load_as_stack()
+    };
+}
+
+/// Stack type for each hart.
+///
+/// Memory layout:
+/// - Bottom: HartContext struct.
+/// - Middle: Stack space for the hart.
+/// - Top: Trap handling space.
+///
+/// Each hart has a single stack that contains both its context and working space.
+#[repr(C, align(128))]
+pub(crate) struct Stack([u8; LEN_STACK_PER_HART]);
+
+impl Stack {
+    const ZERO: Self = Self([0; LEN_STACK_PER_HART]);
+
+    /// Gets mutable reference to hart context at bottom of stack.
+    #[inline]
+    pub fn hart_context(&mut self) -> &mut HartContext {
+        unsafe { &mut *self.0.as_mut_ptr().cast() }
+    }
+
+    /// Initializes stack for trap handling.
+    /// - Sets up hart context.
+    /// - Creates and loads FreeTrapStack with the stack range.
+    fn load_as_stack(&'static mut self) {
+        let hart = self.hart_context();
+        let context_ptr = hart.context_ptr();
+        hart.init();
+
+        // Get stack memory range.
+        let range = self.0.as_ptr_range();
+
+        // Create and load trap stack, forgetting it to avoid drop
+        forget(
+            FreeTrapStack::new(
+                range.start as usize..range.end as usize,
+                |_| {}, // Empty callback
+                context_ptr,
+                fast_handler,
+            )
+            .unwrap()
+            .load(),
+        );
+    }
+}

+ 5 - 0
prototyper/rust-toolchain.toml

@@ -0,0 +1,5 @@
+[toolchain]
+channel = "nightly-2025-02-08"
+components = ["rustfmt", "llvm-tools-preview", "clippy", "rust-src"]
+targets = ["riscv64imac-unknown-none-elf"]
+profile = "minimal"

+ 26 - 0
prototyper/test-kernel/Cargo.toml

@@ -0,0 +1,26 @@
+cargo-features = ["per-package-target"]
+
+[package]
+name = "rustsbi-test-kernel"
+version = "0.0.0"
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+forced-target = "riscv64imac-unknown-none-elf"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+sbi-testing = { git = "https://github.com/rustsbi/rustsbi" , features = ["log"] }
+log = "0.4"
+riscv = "0.11.1"
+spin = "0.9"
+uart16550 = "0.0.1"
+rcore-console = "0.0.0"
+dtb-walker = "=0.2.0-alpha.3"
+
+[[bin]]
+name = "rustsbi-test-kernel"
+test = false
+bench = false

+ 52 - 0
prototyper/test-kernel/build.rs

@@ -0,0 +1,52 @@
+use std::{env, path::PathBuf};
+
+fn main() {
+    let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+    let ld = &out.join("rustsbi-test-kernel.ld");
+
+    std::fs::write(ld, LINKER_SCRIPT).unwrap();
+
+    println!("cargo:rustc-link-arg=-T{}", ld.display());
+    println!("cargo:rustc-link-search={}", out.display());
+}
+
+const LINKER_SCRIPT: &[u8] = b"OUTPUT_ARCH(riscv)
+ENTRY(_start) 
+SECTIONS {
+    . = 0x80200000;
+    istart = .;
+	  .head.text : ALIGN(8) {		
+        KEEP(*(.head.text))
+	  }
+
+    .text : ALIGN(8) { 
+        *(.text.entry)
+        *(.text .text.*)
+    }
+    .rodata : ALIGN(8) { 
+        srodata = .;
+        *(.rodata .rodata.*)
+        *(.srodata .srodata.*)
+        . = ALIGN(8);  
+        erodata = .;
+    } 
+    .data : ALIGN(8) { 
+        sdata = .;
+        *(.data .data.*)
+        *(.sdata .sdata.*)
+        . = ALIGN(8); 
+        edata = .;
+    }
+    sidata = LOADADDR(.data);
+    .bss (NOLOAD) : ALIGN(8) {  
+        *(.bss.uninit)
+        sbss = .;
+        *(.bss .bss.*)
+        *(.sbss .sbss.*)
+        ebss = .;
+    } 
+    iend = .;
+    /DISCARD/ : {
+        *(.eh_frame)
+    }
+}";

+ 44 - 0
prototyper/test-kernel/scripts/rustsbi-test-kernel.its

@@ -0,0 +1,44 @@
+/*
+ * Configuration to load RustSBI before RustSBI Test Kernel
+ */
+ 
+/dts-v1/;
+
+/ {
+			description = "Configuration to load RustSBI before RustSBI Test Kernel";
+
+			images {
+				kernel {
+					description = "rustsbi-test-kernel";
+					data = /incbin/("./rustsbi-test-kernel.bin");
+					type = "standalone";
+					os = "u-boot";
+					arch = "riscv";
+					compression = "none";
+					load = /bits/ 64 <0x80200000>;
+				};
+
+				rustsbi {
+					description = "RustSBI Firmware";
+					data = /incbin/("./rustsbi-prototyper.bin");
+					type = "firmware";
+					os = "opensbi";
+					arch = "riscv";
+					compression = "none";
+					load = /bits/ 64 <0x80100000>;
+					entry = /bits/ 64 <0x80100000>;
+				};
+
+			};
+
+		configurations {
+				default = "conf-1";
+
+				conf-1 {
+					description = "RustSBI & RustSBI Test Kernel";
+					firmware = "rustsbi";
+					loadables = "kernel";
+				};
+		};
+};
+

+ 213 - 0
prototyper/test-kernel/src/main.rs

@@ -0,0 +1,213 @@
+#![no_std]
+#![no_main]
+#![feature(naked_functions)]
+#![allow(static_mut_refs)]
+
+#[macro_use]
+extern crate rcore_console;
+
+use core::{
+    arch::{asm, naked_asm},
+    ptr::null,
+};
+use sbi_testing::sbi;
+use uart16550::Uart16550;
+
+const RISCV_HEAD_FLAGS: u64 = 0;
+const RISCV_HEADER_VERSION: u32 = 0x2;
+const RISCV_IMAGE_MAGIC: u64 = 0x5643534952; /* Magic number, little endian, "RISCV" */
+const RISCV_IMAGE_MAGIC2: u32 = 0x05435352; /* Magic number 2, little endian, "RSC\x05" */
+
+/// boot header
+#[naked]
+#[unsafe(no_mangle)]
+#[unsafe(link_section = ".head.text")]
+unsafe extern "C" fn _boot_header() -> ! {
+    unsafe {
+        naked_asm!(
+            "j _start",
+            ".word 0",
+            ".balign 8",
+            ".dword 0x200000",
+            ".dword iend - istart",
+            ".dword {RISCV_HEAD_FLAGS}",
+            ".word  {RISCV_HEADER_VERSION}",
+            ".word  0",
+            ".dword 0",
+            ".dword {RISCV_IMAGE_MAGIC}",
+            ".balign 4",
+            ".word  {RISCV_IMAGE_MAGIC2}",
+            ".word  0",
+            RISCV_HEAD_FLAGS = const RISCV_HEAD_FLAGS,
+            RISCV_HEADER_VERSION = const RISCV_HEADER_VERSION,
+            RISCV_IMAGE_MAGIC = const RISCV_IMAGE_MAGIC,
+            RISCV_IMAGE_MAGIC2 = const RISCV_IMAGE_MAGIC2,
+        );
+    }
+}
+
+/// 内核入口。
+///
+/// # Safety
+///
+/// 裸函数。
+#[naked]
+#[unsafe(no_mangle)]
+#[unsafe(link_section = ".text.entry")]
+unsafe extern "C" fn _start(hartid: usize, device_tree_paddr: usize) -> ! {
+    const STACK_SIZE: usize = 16384; // 16 KiB
+
+    #[unsafe(link_section = ".bss.uninit")]
+    static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE];
+
+    unsafe {
+        naked_asm!(
+            // clear bss segment
+            "   la      t0, sbss
+            la      t1, ebss
+        1:  bgeu    t0, t1, 2f
+            sd      zero, 0(t0)
+            addi    t0, t0, 8
+            j       1b",
+            "2:",
+            "   la sp, {stack} + {stack_size}",
+            "   j  {main}",
+            stack_size = const STACK_SIZE,
+            stack      =   sym STACK,
+            main       =   sym rust_main,
+        )
+    }
+}
+
+extern "C" fn rust_main(hartid: usize, dtb_pa: usize) -> ! {
+    let BoardInfo {
+        smp,
+        frequency,
+        uart,
+    } = BoardInfo::parse(dtb_pa);
+    unsafe { UART = Uart16550Map(uart as _) };
+    rcore_console::init_console(&Console);
+    rcore_console::set_log_level(option_env!("LOG"));
+    println!(
+        r"
+ _____         _     _  __                    _
+|_   _|__  ___| |_  | |/ /___ _ __ _ __   ___| |
+  | |/ _ \/ __| __| | ' // _ \ '__| '_ \ / _ \ |
+  | |  __/\__ \ |_  | . \  __/ |  | | | |  __/ |
+  |_|\___||___/\__| |_|\_\___|_|  |_| |_|\___|_|
+================================================
+| boot hart id          | {hartid:20} |
+| smp                   | {smp:20} |
+| timebase frequency    | {frequency:17} Hz |
+| dtb physical address  | {dtb_pa:#20x} |
+------------------------------------------------"
+    );
+    let testing = sbi_testing::Testing {
+        hartid,
+        hart_mask: (1 << smp) - 1,
+        hart_mask_base: 0,
+        delay: frequency,
+    };
+    if testing.test() {
+        sbi::system_reset(sbi::Shutdown, sbi::NoReason);
+    } else {
+        sbi::system_reset(sbi::Shutdown, sbi::SystemFailure);
+    }
+    unreachable!()
+}
+
+#[cfg_attr(not(test), panic_handler)]
+fn panic(info: &core::panic::PanicInfo) -> ! {
+    let (hart_id, pc): (usize, usize);
+    unsafe { asm!("mv    {}, tp", out(reg) hart_id) };
+    unsafe { asm!("auipc {},  0", out(reg) pc) };
+    println!("[test-kernel-panic] hart {hart_id} {info}");
+    println!("[test-kernel-panic] pc = {pc:#x}");
+    println!("[test-kernel-panic] SBI test FAILED due to panic");
+    sbi::system_reset(sbi::Shutdown, sbi::SystemFailure);
+    loop {}
+}
+
+struct BoardInfo {
+    smp: usize,
+    frequency: u64,
+    uart: usize,
+}
+
+impl BoardInfo {
+    fn parse(dtb_pa: usize) -> Self {
+        use dtb_walker::{Dtb, DtbObj, HeaderError as E, Property, Str, WalkOperation::*};
+
+        let mut ans = Self {
+            smp: 0,
+            frequency: 0,
+            uart: 0,
+        };
+        unsafe {
+            Dtb::from_raw_parts_filtered(dtb_pa as _, |e| {
+                matches!(e, E::Misaligned(4) | E::LastCompVersion(_))
+            })
+        }
+        .unwrap()
+        .walk(|ctx, obj| match obj {
+            DtbObj::SubNode { name } => {
+                if ctx.is_root() && (name == Str::from("cpus") || name == Str::from("soc")) {
+                    StepInto
+                } else if ctx.name() == Str::from("cpus") && name.starts_with("cpu@") {
+                    ans.smp += 1;
+                    StepOver
+                } else if ctx.name() == Str::from("soc")
+                    && (name.starts_with("uart") || name.starts_with("serial"))
+                {
+                    StepInto
+                } else {
+                    StepOver
+                }
+            }
+            DtbObj::Property(Property::Reg(mut reg)) => {
+                if ctx.name().starts_with("uart") || ctx.name().starts_with("serial") {
+                    ans.uart = reg.next().unwrap().start;
+                }
+                StepOut
+            }
+            DtbObj::Property(Property::General { name, value }) => {
+                if ctx.name() == Str::from("cpus") && name == Str::from("timebase-frequency") {
+                    ans.frequency = match *value {
+                        [a, b, c, d] => u32::from_be_bytes([a, b, c, d]) as _,
+                        [a, b, c, d, e, f, g, h] => u64::from_be_bytes([a, b, c, d, e, f, g, h]),
+                        _ => unreachable!(),
+                    };
+                }
+                StepOver
+            }
+            DtbObj::Property(_) => StepOver,
+        });
+        ans
+    }
+}
+
+struct Console;
+static mut UART: Uart16550Map = Uart16550Map(null());
+
+pub struct Uart16550Map(*const Uart16550<u8>);
+
+unsafe impl Sync for Uart16550Map {}
+
+impl Uart16550Map {
+    #[inline]
+    pub fn get(&self) -> &Uart16550<u8> {
+        unsafe { &*self.0 }
+    }
+}
+
+impl rcore_console::Console for Console {
+    #[inline]
+    fn put_char(&self, c: u8) {
+        unsafe { UART.get().write(core::slice::from_ref(&c)) };
+    }
+
+    #[inline]
+    fn put_str(&self, s: &str) {
+        unsafe { UART.get().write(s.as_bytes()) };
+    }
+}

+ 11 - 0
prototyper/xtask/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "xtask"
+version = "0.1.0"
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+
+[dependencies]
+clap = { version = "4.5.4", features = ["derive", "env", "suggestions"] }
+log = "0.4.21"
+clap-verbosity-flag = "3.0.2"

+ 79 - 0
prototyper/xtask/src/bench.rs

@@ -0,0 +1,79 @@
+use std::{
+    env, fs,
+    process::{Command, ExitStatus},
+};
+
+use clap::Args;
+
+use crate::utils::cargo;
+
+#[derive(Debug, Args, Clone)]
+pub struct BenchArg {
+    /// Package Prototyper and Test-Kernel
+    #[clap(long)]
+    pub pack: bool,
+}
+
+#[must_use]
+pub fn run(arg: &BenchArg) -> Option<ExitStatus> {
+    let arch = "riscv64imac-unknown-none-elf";
+    let current_dir = env::current_dir();
+    let target_dir = current_dir
+        .as_ref()
+        .unwrap()
+        .join("target")
+        .join(arch)
+        .join("release");
+
+    info!("Building bench kernel");
+    cargo::Cargo::new("build")
+        .package("rustsbi-bench-kernel")
+        .target(arch)
+        .release()
+        .status()
+        .ok()?;
+
+    info!("Copy to binary");
+    let exit_status = Command::new("rust-objcopy")
+        .args(["-O", "binary"])
+        .arg("--binary-architecture=riscv64")
+        .arg(target_dir.join("rustsbi-bench-kernel"))
+        .arg(target_dir.join("rustsbi-bench-kernel.bin"))
+        .status()
+        .ok()?;
+
+    if arg.pack {
+        info!("Pack to image");
+        match fs::exists(target_dir.join("rustsbi-prototyper.bin")) {
+            Ok(true) => {}
+            Ok(false) => {
+                panic!(
+                    " Couldn't open \"rustsbi-prototyper.bin\": No such file or directory. Please compile Prototyper first"
+                );
+            }
+            Err(_) => {
+                panic!(
+                    "Can't check existence of file rustsbi-prototyper.bin, please compile Prototyper first"
+                );
+            }
+        }
+        fs::copy(
+            current_dir
+                .as_ref()
+                .unwrap()
+                .join("bench-kernel")
+                .join("scripts")
+                .join("rustsbi-bench-kernel.its"),
+            target_dir.join("rustsbi-bench-kernel.its"),
+        )
+        .ok()?;
+        env::set_current_dir(&target_dir).ok()?;
+        Command::new("mkimage")
+            .args(["-f", "rustsbi-bench-kernel.its"])
+            .arg("rustsbi-bench-kernel.itb")
+            .status()
+            .ok()?;
+        fs::remove_file(env::current_dir().unwrap().join("rustsbi-bench-kernel.its")).ok()?;
+    }
+    Some(exit_status)
+}

+ 55 - 0
prototyper/xtask/src/logger.rs

@@ -0,0 +1,55 @@
+use log::Level;
+use std::io::Write;
+
+use crate::Cli;
+
+/// Simple logger implementation for RustSBI that supports colored output.
+pub struct Logger;
+
+impl Logger {
+    /// Initialize the logger with log level from RUST_LOG env var or default to Info.
+    pub fn init(cli: &Cli) -> Result<(), log::SetLoggerError> {
+        // Set max log level from parmas env var if present, otherwise use Info
+        log::set_max_level(cli.verbose.log_level_filter());
+        log::set_logger(&Logger)
+    }
+}
+
+impl log::Log for Logger {
+    // Always enable logging for all log levels
+    #[inline]
+    fn enabled(&self, _metadata: &log::Metadata) -> bool {
+        true
+    }
+
+    // Log messages with color-coded levels
+    #[inline]
+    fn log(&self, record: &log::Record) {
+        // ANSI color codes for different log levels
+        const ERROR_COLOR: u8 = 31; // Red
+        const WARN_COLOR: u8 = 93; // Bright yellow
+        const INFO_COLOR: u8 = 32; // Green
+        const DEBUG_COLOR: u8 = 36; // Cyan
+        const TRACE_COLOR: u8 = 90; // Bright black
+
+        let color_code = match record.level() {
+            Level::Error => ERROR_COLOR,
+            Level::Warn => WARN_COLOR,
+            Level::Info => INFO_COLOR,
+            Level::Debug => DEBUG_COLOR,
+            Level::Trace => TRACE_COLOR,
+        };
+
+        eprintln!(
+            "\x1b[1;37m[RustSBI-xtask] \x1b[1;{color_code}m{:^5}\x1b[0m - {}",
+            record.level(),
+            record.args(),
+        );
+    }
+
+    // No-op flush since we use println! which is already line-buffered
+    #[inline]
+    fn flush(&self) {
+        std::io::stderr().flush().expect("Unable to flush stderr");
+    }
+}

+ 56 - 0
prototyper/xtask/src/main.rs

@@ -0,0 +1,56 @@
+use clap::{Parser, Subcommand};
+use clap_verbosity_flag::{InfoLevel, Verbosity};
+use std::process::ExitCode;
+
+#[macro_use]
+mod utils;
+mod bench;
+mod logger;
+mod prototyper;
+mod test;
+
+#[macro_use]
+extern crate log;
+
+use crate::bench::BenchArg;
+use crate::prototyper::PrototyperArg;
+use crate::test::TestArg;
+
+#[derive(Parser)]
+#[clap(
+    name = "xtask",
+    about = "A task runner for building, running and testing Prototyper",
+    long_about = None,
+)]
+struct Cli {
+    #[clap(subcommand)]
+    cmd: Cmd,
+    #[command(flatten)]
+    verbose: Verbosity<InfoLevel>,
+}
+
+#[derive(Subcommand)]
+enum Cmd {
+    Prototyper(PrototyperArg),
+    Test(TestArg),
+    Bench(BenchArg),
+}
+
+fn main() -> ExitCode {
+    let cli_args = Cli::parse();
+    logger::Logger::init(&cli_args).expect("Unable to init logger");
+
+    if let Some(code) = match cli_args.cmd {
+        Cmd::Prototyper(ref arg) => prototyper::run(arg),
+        Cmd::Test(ref arg) => test::run(arg),
+        Cmd::Bench(ref arg) => bench::run(arg),
+    } {
+        if code.success() {
+            info!("Finished");
+            return ExitCode::SUCCESS;
+        }
+    }
+
+    error!("Failed to run task!");
+    ExitCode::FAILURE
+}

+ 120 - 0
prototyper/xtask/src/prototyper.rs

@@ -0,0 +1,120 @@
+use std::{
+    env, fs,
+    process::{Command, ExitStatus},
+};
+
+use clap::Args;
+
+use crate::utils::CmdOptional;
+use crate::utils::cargo;
+
+#[derive(Debug, Args, Clone)]
+pub struct PrototyperArg {
+    #[clap(long, short = 'f')]
+    pub features: Vec<String>,
+
+    #[clap(long, env = "PROTOTYPER_FDT_PATH")]
+    pub fdt: Option<String>,
+
+    #[clap(long, env = "PROTOTYPER_PAYLOAD_PATH")]
+    pub payload: Option<String>,
+
+    #[clap(long)]
+    pub jump: bool,
+
+    #[clap(long, default_value = "INFO")]
+    pub log_level: String,
+}
+
+#[must_use]
+#[rustfmt::skip] // "export_env!("PROTOTYPER_FDT_PATH" ?= fdt.unwrap());" is a macro, rustfmt will not format it correctly
+pub fn run(arg: &PrototyperArg) -> Option<ExitStatus> {
+    let arch = "riscv64imac-unknown-none-elf";
+    let fdt = arg.fdt.clone();
+    let payload = arg.payload.clone();
+    let jump = arg.jump;
+    let current_dir = env::current_dir();
+    let target_dir = current_dir
+        .as_ref()
+        .unwrap()
+        .join("target")
+        .join(arch)
+        .join("release");
+
+    info!("Building Protoyper");
+    cargo::Cargo::new("build")
+        .package("rustsbi-prototyper")
+        .target(arch)
+        .unstable("build-std", ["core","alloc"])
+        .env("RUSTFLAGS", "-C relocation-model=pie -C link-arg=-pie")
+        .features(&arg.features)
+        .optional(arg.fdt.is_some(), |cargo| {
+            cargo.env("PROTOTYPER_FDT_PATH", fdt.as_ref().unwrap());
+            cargo.features(["fdt".to_string()])
+        })
+        .optional(payload.is_some(), |cargo| {
+            cargo.env("PROTOTYPER_PAYLOAD_PATH", payload.as_ref().unwrap());
+            cargo.features(["payload".to_string()])
+        })
+        .optional(jump, |cargo| {
+            cargo.features(["jump".to_string()])
+        })
+        .env("RUST_LOG", &arg.log_level)
+        .release()
+        .status()
+        .ok()?;
+
+    info!("Copy to binary");
+    let exit_status = Command::new("rust-objcopy")
+        .args(["-O", "binary"])
+        .arg("--binary-architecture=riscv64")
+        .arg(target_dir.join("rustsbi-prototyper"))
+        .arg(target_dir.join("rustsbi-prototyper.bin"))
+        .status()
+        .ok()?;
+    if !exit_status.success() {
+        error!("Failed to exec rust-objcopy, please check if cargo-binutils has been installed?");
+        return Some(exit_status);
+    }
+
+    if arg.payload.is_some() {
+        info!("Copy for payload mode");
+        fs::copy(
+            target_dir.join("rustsbi-prototyper"),
+            target_dir.join("rustsbi-prototyper-payload.elf"),
+        )
+        .ok()?;
+        fs::copy(
+            target_dir.join("rustsbi-prototyper.bin"),
+            target_dir.join("rustsbi-prototyper-payload.bin"),
+        )
+        .ok()?;
+    } else if arg.jump {
+        info!("Copy for jump mode");
+        fs::copy(
+            target_dir.join("rustsbi-prototyper"),
+            target_dir.join("rustsbi-prototyper-jump.elf"),
+        )
+        .ok()?;
+        fs::copy(
+            target_dir.join("rustsbi-prototyper.bin"),
+            target_dir.join("rustsbi-prototyper-jump.bin"),
+        )
+        .ok()?;
+    } else {
+        info!("Copy for dynamic mode");
+        fs::copy(
+            target_dir.join("rustsbi-prototyper"),
+            target_dir.join("rustsbi-prototyper-dynamic.elf"),
+        )
+        .ok()?;
+        fs::copy(
+            target_dir.join("rustsbi-prototyper.bin"),
+            target_dir.join("rustsbi-prototyper-dynamic.bin"),
+        )
+        .ok()?;
+
+    }
+
+    Some(exit_status)
+}

+ 79 - 0
prototyper/xtask/src/test.rs

@@ -0,0 +1,79 @@
+use std::{
+    env, fs,
+    process::{Command, ExitStatus},
+};
+
+use clap::Args;
+
+use crate::utils::cargo;
+
+#[derive(Debug, Args, Clone)]
+pub struct TestArg {
+    /// Package Prototyper and Test-Kernel
+    #[clap(long)]
+    pub pack: bool,
+}
+
+#[must_use]
+pub fn run(arg: &TestArg) -> Option<ExitStatus> {
+    let arch = "riscv64imac-unknown-none-elf";
+    let current_dir = env::current_dir();
+    let target_dir = current_dir
+        .as_ref()
+        .unwrap()
+        .join("target")
+        .join(arch)
+        .join("release");
+
+    info!("Building test kernel");
+    cargo::Cargo::new("build")
+        .package("rustsbi-test-kernel")
+        .target(arch)
+        .release()
+        .status()
+        .ok()?;
+
+    info!("Copy to binary");
+    let exit_status = Command::new("rust-objcopy")
+        .args(["-O", "binary"])
+        .arg("--binary-architecture=riscv64")
+        .arg(target_dir.join("rustsbi-test-kernel"))
+        .arg(target_dir.join("rustsbi-test-kernel.bin"))
+        .status()
+        .ok()?;
+
+    if arg.pack {
+        info!("Pack to image");
+        match fs::exists(target_dir.join("rustsbi-prototyper.bin")) {
+            Ok(true) => {}
+            Ok(false) => {
+                panic!(
+                    " Couldn't open \"rustsbi-prototyper.bin\": No such file or directory. Please compile Prototyper first"
+                );
+            }
+            Err(_) => {
+                panic!(
+                    "Can't check existence of file rustsbi-prototyper.bin, please compile Prototyper first"
+                );
+            }
+        }
+        fs::copy(
+            current_dir
+                .as_ref()
+                .unwrap()
+                .join("test-kernel")
+                .join("scripts")
+                .join("rustsbi-test-kernel.its"),
+            target_dir.join("rustsbi-test-kernel.its"),
+        )
+        .ok()?;
+        env::set_current_dir(&target_dir).ok()?;
+        Command::new("mkimage")
+            .args(["-f", "rustsbi-test-kernel.its"])
+            .arg("rustsbi-test-kernel.itb")
+            .status()
+            .ok()?;
+        fs::remove_file(env::current_dir().unwrap().join("rustsbi-test-kernel.its")).ok()?;
+    }
+    Some(exit_status)
+}

+ 105 - 0
prototyper/xtask/src/utils/cargo.rs

@@ -0,0 +1,105 @@
+use std::{
+    ffi::OsStr,
+    ops::{Deref, DerefMut},
+    path::Path,
+    process::Command,
+};
+
+use super::CmdOptional;
+
+pub struct Cargo {
+    cmd: Command,
+}
+
+#[allow(unused)]
+impl Cargo {
+    pub fn new(action: &str) -> Self {
+        let mut cmd = Command::new(env!("CARGO"));
+        cmd.arg(action);
+        Self { cmd }
+    }
+
+    pub fn package<S: AsRef<OsStr>>(&mut self, package: S) -> &mut Self {
+        self.args(["--package", package.as_ref().to_str().unwrap()]);
+        self
+    }
+
+    pub fn work_dir<S: AsRef<Path>>(&mut self, dir: S) -> &mut Self {
+        self.current_dir(dir);
+        self
+    }
+
+    pub fn release(&mut self) -> &mut Self {
+        self.arg("--release");
+        self
+    }
+
+    pub fn target<S: AsRef<OsStr>>(&mut self, target: S) -> &mut Self {
+        self.args(["--target", target.as_ref().to_str().unwrap()]);
+        self
+    }
+
+    pub fn features<I, S>(&mut self, features: I) -> &mut Self
+    where
+        I: IntoIterator<Item = S>,
+        S: AsRef<OsStr>,
+    {
+        self.args([
+            "--features",
+            features
+                .into_iter()
+                .map(|f| f.as_ref().to_str().unwrap().to_string())
+                .collect::<Vec<_>>()
+                .join(",")
+                .as_ref(),
+        ]);
+        self
+    }
+
+    pub fn no_default_features(&mut self) -> &mut Self {
+        self.arg("--no-default-features");
+        self
+    }
+
+    pub fn unstable<I, S>(&mut self, key: S, values: I) -> &mut Self
+    where
+        I: IntoIterator<Item = S>,
+        S: AsRef<OsStr>,
+    {
+        self.arg(format!(
+            "-Z{}={}",
+            key.as_ref().to_str().unwrap(),
+            values
+                .into_iter()
+                .map(|f| f.as_ref().to_str().unwrap().to_string())
+                .collect::<Vec<_>>()
+                .join(",")
+        ));
+        self
+    }
+
+    pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
+    where
+        K: AsRef<OsStr>,
+        V: AsRef<OsStr>,
+    {
+        self.cmd.env(key, value);
+        self
+    }
+}
+
+impl CmdOptional for Cargo {}
+
+impl Deref for Cargo {
+    type Target = Command;
+
+    fn deref(&self) -> &Self::Target {
+        &self.cmd
+    }
+}
+
+impl DerefMut for Cargo {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.cmd
+    }
+}

+ 14 - 0
prototyper/xtask/src/utils/envs.rs

@@ -0,0 +1,14 @@
+#[allow(unused)]
+macro_rules! export_env {
+    ($env:literal ?= $val:expr) => {
+        if std::env::vars_os().all(|(k, _)| k != $env) {
+            std::env::set_var($env, $val);
+        }
+    };
+    ($env0:literal ?= $val0:expr, $($env:literal ?= $val:expr,)+) => {
+        export_env!($env0 ?= $val0);
+        $(
+            export_env!($env ?= $val);
+        )+
+    };
+}

+ 13 - 0
prototyper/xtask/src/utils/mod.rs

@@ -0,0 +1,13 @@
+pub mod cargo;
+
+#[macro_use]
+pub mod envs;
+
+pub trait CmdOptional {
+    fn optional(&mut self, pred: bool, f: impl FnOnce(&mut Self) -> &mut Self) -> &mut Self {
+        if pred {
+            f(self);
+        }
+        self
+    }
+}