diff --git a/.vscode/settings.json b/.vscode/settings.json
index e081f94b958eea038582805cc2731f38b8194dc3..16b16c7330ae617c137eb397cb6eb9424ecd8177 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -54,5 +54,8 @@
         "uarths.h": "c",
         "random": "c"
     },
-    "C_Cpp.errorSquiggles": "Disabled"
+    "C_Cpp.errorSquiggles": "Disabled",
+    "files.exclude": {
+        "**/.git": false
+    }
 }
\ No newline at end of file
diff --git a/bootloader/rustsbi-k210 b/bootloader/rustsbi-k210
deleted file mode 160000
index 5d367ab59cbc8a69dc44f1e435a2da9c8e3c135a..0000000000000000000000000000000000000000
--- a/bootloader/rustsbi-k210
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5d367ab59cbc8a69dc44f1e435a2da9c8e3c135a
diff --git a/bootloader/rustsbi-k210/.cargo/config.toml b/bootloader/rustsbi-k210/.cargo/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..140edb767634eb7e9785d34e544daf016353414b
--- /dev/null
+++ b/bootloader/rustsbi-k210/.cargo/config.toml
@@ -0,0 +1,7 @@
+[alias]
+xtask = "run --package xtask --"
+make = "xtask make"
+k210 = "xtask k210"
+asm = "xtask asm"
+size = "xtask size"
+detect = "xtask detect"
diff --git a/bootloader/rustsbi-k210/.gitignore b/bootloader/rustsbi-k210/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..355a18867d62187ac69f2dc3d2e327b4f60949f6
--- /dev/null
+++ b/bootloader/rustsbi-k210/.gitignore
@@ -0,0 +1,2 @@
+/target
+xtask/ktool.py
diff --git a/bootloader/rustsbi-k210/Cargo.lock b/bootloader/rustsbi-k210/Cargo.lock
new file mode 100644
index 0000000000000000000000000000000000000000..b8d13a0c6a76efb785b898a3e0afe45a04620fb9
--- /dev/null
+++ b/bootloader/rustsbi-k210/Cargo.lock
@@ -0,0 +1,503 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "CoreFoundation-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b"
+dependencies = [
+ "libc",
+ "mach 0.1.2",
+]
+
+[[package]]
+name = "IOKit-sys"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a"
+dependencies = [
+ "CoreFoundation-sys",
+ "libc",
+ "mach 0.1.2",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "bare-metal"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
+dependencies = [
+ "rustc_version",
+]
+
+[[package]]
+name = "bare-metal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
+
+[[package]]
+name = "bit_field"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56"
+
+[[package]]
+name = "bit_field"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "buddy_system_allocator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55703ac5f02c246ce6158eff6ae2dd9e9069917969682b6831f8a5123abb8a48"
+dependencies = [
+ "spin 0.7.1",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "clap"
+version = "2.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
+[[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 = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "k210-hal"
+version = "0.2.0"
+source = "git+https://github.com/riscv-rust/k210-hal?rev=7e9c8d70#7e9c8d70224c6a7f9502e6463b2b586d4a8bc494"
+dependencies = [
+ "bitflags",
+ "embedded-hal",
+ "k210-pac",
+ "nb 1.0.0",
+]
+
+[[package]]
+name = "k210-pac"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69b1cda20e843558e892373b1fd01a900232103f65fa4be8f18edcd130dde30e"
+dependencies = [
+ "bare-metal 0.2.5",
+ "riscv 0.5.4",
+ "vcell",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+dependencies = [
+ "spin 0.5.2",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.121"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
+
+[[package]]
+name = "libudev"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe"
+dependencies = [
+ "libc",
+ "libudev-sys",
+]
+
+[[package]]
+name = "libudev-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "mach"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "mach"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
+name = "nb"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
+dependencies = [
+ "nb 1.0.0",
+]
+
+[[package]]
+name = "nb"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
+
+[[package]]
+name = "nix"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0eaf8df8bab402257e0a5c17a254e4cc1f72a93588a1ddfb5d356c801aa7cb"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "void",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
+
+[[package]]
+name = "r0"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211"
+
+[[package]]
+name = "regex"
+version = "1.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "riscv"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb785ce81e0bd87b8d1d357266eeb03f081d9d5871a31e7f95b7e6fd67002eb"
+dependencies = [
+ "bare-metal 0.2.5",
+ "bit_field 0.9.0",
+]
+
+[[package]]
+name = "riscv"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba"
+dependencies = [
+ "bare-metal 1.0.0",
+ "bit_field 0.10.1",
+ "riscv-target",
+]
+
+[[package]]
+name = "riscv"
+version = "0.7.0"
+source = "git+https://github.com/rust-embedded/riscv?rev=cd31989b#cd31989ba11d5d64e1addd8aab98bfb00dd927d5"
+dependencies = [
+ "bare-metal 1.0.0",
+ "bit_field 0.10.1",
+ "embedded-hal",
+ "riscv-target",
+]
+
+[[package]]
+name = "riscv-target"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222"
+dependencies = [
+ "lazy_static",
+ "regex",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustsbi"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "039ff1f03eaf3ead3f6804b4e7abb7c5abd4f9e71b0483ebd554d78ce1298b5d"
+dependencies = [
+ "embedded-hal",
+ "nb 1.0.0",
+ "riscv 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustsbi-k210"
+version = "0.0.2"
+dependencies = [
+ "bit_field 0.10.1",
+ "buddy_system_allocator",
+ "k210-hal",
+ "r0",
+ "riscv 0.7.0 (git+https://github.com/rust-embedded/riscv?rev=cd31989b)",
+ "rustsbi",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "serialport"
+version = "4.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d8cd7c0f22290ee2c01457009fa6fc1cae4153d5608a924e5dc423babc2c655"
+dependencies = [
+ "CoreFoundation-sys",
+ "IOKit-sys",
+ "bitflags",
+ "cfg-if",
+ "libudev",
+ "mach 0.2.3",
+ "nix",
+ "regex",
+ "winapi",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "spin"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162"
+
+[[package]]
+name = "spin"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
+[[package]]
+name = "test-kernel"
+version = "0.1.0"
+dependencies = [
+ "bit_field 0.10.1",
+ "bitflags",
+ "buddy_system_allocator",
+ "lazy_static",
+ "r0",
+ "riscv 0.7.0 (git+https://github.com/rust-embedded/riscv?rev=cd31989b)",
+ "spin 0.9.2",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+
+[[package]]
+name = "vcell"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "xtask"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "serialport",
+]
diff --git a/bootloader/rustsbi-k210/Cargo.toml b/bootloader/rustsbi-k210/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..6583c82b9d3541c9d07b9484b32c6bd5367ab187
--- /dev/null
+++ b/bootloader/rustsbi-k210/Cargo.toml
@@ -0,0 +1,7 @@
+[workspace]
+members = [
+    "rustsbi-k210",
+    "test-kernel",
+    "xtask"
+]
+default-members = ["xtask"]
diff --git a/bootloader/rustsbi-k210/README.md b/bootloader/rustsbi-k210/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..cc2cd96fe96402db9c2b925652ed0e6150ac8ac9
--- /dev/null
+++ b/bootloader/rustsbi-k210/README.md
@@ -0,0 +1,39 @@
+# RustSBI K210 平台支持包
+
+这个平台支持包包含较多的平台兼容功能,允许在K210上运行1.12版本标准的操作系统内核。
+
+## 二进制包下载
+
+请参阅发行页面:[这里](https://github.com/rustsbi/rustsbi-k210/releases)。
+
+## 使用说明
+
+请先下载[ktool.py](https://github.com/loboris/ktool),放置在`xtask`目录下,即文件位置为`xtask/ktool.py`。
+
+运行以下指令,来直接在目标开发板运行代码。
+
+```
+cargo k210
+```
+
+这个平台支持包会启动位于`0x80020000`的操作系统内核,并在`a1`寄存器提供一个简单的设备树。
+操作系统内核应当使用《RISC-V指令集架构 第二卷:特权级指令》的1.12版本,而非芯片支持的1.9.1版本。
+
+## 兼容性使用文档
+
+稍后放出。包括`sfence.vma`指令、页异常编号转发等等。
+
+## 立即体验
+
+先下载代码,然后直接运行内核测试:
+
+```
+cargo test
+```
+
+## 版权声明
+
+项目的测试框架使用了[KTool](https://github.com/loboris/ktool)。这个项目使用Apache 2.0协议开源,感谢KTool项目和它的维护者们!
+
+Reference implementaion K210 includes Kendryte K210 DTS file from Western Digital, this file is
+(C) Western Digital Corporation or its affiliates under BSD-2-Clause license.
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/.cargo/config.toml b/bootloader/rustsbi-k210/rustsbi-k210/.cargo/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..b50c674cfec148dad88e0faa5bcd158c08cd5783
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/.cargo/config.toml
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64imac-unknown-none-elf"
+
+[target.riscv64imac-unknown-none-elf]
+rustflags = [
+    "-C", "link-arg=-Tlink-k210.ld",
+]
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/Cargo.toml b/bootloader/rustsbi-k210/rustsbi-k210/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..cfd18ba231a6571a1b884d7ae9f71bc286c1ee70
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "rustsbi-k210"
+version = "0.0.2"
+authors = ["luojia65 <me@luojia.cc>"]
+edition = "2021"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rustsbi = "0.2.2"
+riscv = { git = "https://github.com/rust-embedded/riscv", rev = "cd31989b", features = ["inline-asm"] }
+buddy_system_allocator = "0.8"
+k210-hal = { git = "https://github.com/riscv-rust/k210-hal", rev = "7e9c8d70" }
+r0 = "1.0"
+bit_field = "0.10"
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/build.rs b/bootloader/rustsbi-k210/rustsbi-k210/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0fd18aefd918fa03b0a57f2a783c2db14423fd1b
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/build.rs
@@ -0,0 +1,18 @@
+use std::env;
+use std::fs;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
+
+    // Put the linker script somewhere the linker can find it
+    fs::File::create(out_dir.join("link-k210.ld"))
+        .unwrap()
+        .write_all(include_bytes!("link-k210.ld"))
+        .unwrap();
+    println!("cargo:rustc-link-search={}", out_dir.display());
+
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rerun-if-changed=link-k210.ld");
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/kendryte-k210.dtb b/bootloader/rustsbi-k210/rustsbi-k210/kendryte-k210.dtb
new file mode 100644
index 0000000000000000000000000000000000000000..2fecbc95105cfb7411bbc23ce96f1df265170cf4
Binary files /dev/null and b/bootloader/rustsbi-k210/rustsbi-k210/kendryte-k210.dtb differ
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/kendryte-k210.dts b/bootloader/rustsbi-k210/rustsbi-k210/kendryte-k210.dts
new file mode 100644
index 0000000000000000000000000000000000000000..23d50e6e24869d4c483365307f1c8f4fcc290cbc
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/kendryte-k210.dts
@@ -0,0 +1,70 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ *   Damien Le Moal <damien.lemoal@wdc.com>
+ */
+
+/dts-v1/;
+/ {
+	#address-cells = <2>;
+	#size-cells = <2>;
+	compatible = "kendryte,k210";
+
+	chosen {
+        bootargs = "console=hvc0 earlycon=sbi";
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			clock-frequency = <390000000>;
+			i-cache-size = <32768>;
+			d-cache-size = <32768>;
+			mmu-type = "none";
+			reg = <0>;
+			riscv,isa = "rv64imafdc";
+			status = "okay";
+			cpu0_intc: interrupt-controller {
+				#interrupt-cells = <1>;
+				compatible = "riscv,cpu-intc";
+				interrupt-controller;
+			};
+		};
+		cpu1: cpu@1 {
+			device_type = "cpu";
+			clock-frequency = <390000000>;
+			d-cache-size = <32768>;
+			i-cache-size = <32768>;
+			mmu-type = "none";
+			reg = <1>;
+			riscv,isa = "rv64imafdc";
+			status = "okay";
+			cpu1_intc: interrupt-controller {
+				#interrupt-cells = <1>;
+				compatible = "riscv,cpu-intc";
+				interrupt-controller;
+			};
+		};
+	};
+
+	memory@80000000 {
+		/* Bank 0: 4 MB, Bank 1: 2 MB, AI chip SRAM: 2MB */
+		device_type = "memory";
+		reg = <0x00000000 0x80000000 0x00000000 0x00800000>;
+	};
+
+	plic0: interrupt-controller@C000000 {
+		#interrupt-cells = <1>;
+		compatible = "riscv,plic0";
+		interrupt-controller;
+		interrupts-extended =
+			<&cpu0_intc 11 &cpu0_intc 9
+			 &cpu1_intc 11 &cpu1_intc 9>;
+		reg = <0x0 0xc000000 0x0 0x4000000>;
+	};
+};
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/link-k210.ld b/bootloader/rustsbi-k210/rustsbi-k210/link-k210.ld
new file mode 100644
index 0000000000000000000000000000000000000000..20861b03fa5d374b9e25718d61924c29dc8306e6
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/link-k210.ld
@@ -0,0 +1,56 @@
+MEMORY {
+    /* 存储单元的物理地址 */
+    SRAM : ORIGIN = 0x80000000, LENGTH = 128K
+}
+
+PROVIDE(stext = 0x80000000);
+
+REGION_ALIAS("REGION_TEXT", SRAM);
+REGION_ALIAS("REGION_RODATA", SRAM);
+REGION_ALIAS("REGION_DATA", SRAM);
+REGION_ALIAS("REGION_BSS", SRAM);
+
+OUTPUT_ARCH(riscv)
+
+ENTRY(_start)
+
+SECTIONS
+{
+    .text stext : {
+        stext = .;
+        *(.text.entry)
+        *(.text .text.*)
+        . = ALIGN(4);
+        etext = .;
+    } > REGION_TEXT
+
+    .rodata : ALIGN(4) {
+        srodata = .;
+        *(.rodata .rodata.*)
+        *(.srodata .srodata.*)
+        . = ALIGN(4);
+        erodata = .;
+    } > REGION_RODATA
+
+    .data : ALIGN(4) {
+        sidata = LOADADDR(.data);
+        sdata = .;
+        *(.data .data.*)
+        *(.sdata .sdata.*)
+        . = ALIGN(4);
+        edata = .;
+    } > REGION_DATA
+
+    .bss (NOLOAD) : ALIGN(4) {
+        *(.bss.uninit)
+        sbss = .;
+        *(.bss .bss.*)
+        *(.sbss .sbss.*)
+        . = ALIGN(4);
+        ebss = .;
+    } > REGION_BSS
+
+    /DISCARD/ : {
+        *(.eh_frame .eh_frame_hdr)
+    }
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/execute.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/execute.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1ac2d40cc24fd624d7c48bab0a0474ea55e89cba
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/execute.rs
@@ -0,0 +1,134 @@
+use core::{
+    arch::asm,
+    ops::{Generator, GeneratorState},
+    pin::Pin,
+};
+use riscv::register::scause::{Exception, Trap};
+
+use crate::feature;
+use crate::runtime::{MachineTrap, Runtime, SupervisorContext};
+
+pub fn execute_supervisor(supervisor_mepc: usize, a0: usize, a1: usize) -> ! {
+    let mut rt = Runtime::new_sbi_supervisor(supervisor_mepc, a0, a1);
+    loop {
+        match Pin::new(&mut rt).resume(()) {
+            GeneratorState::Yielded(MachineTrap::SbiCall()) => {
+                let ctx = rt.context_mut();
+                if emulate_sbi_call(ctx) {
+                    continue;
+                }
+                feature::preprocess_supervisor_external(ctx); // specific for 1.9.1; see document for details
+                let param = [ctx.a0, ctx.a1, ctx.a2, ctx.a3, ctx.a4, ctx.a5];
+                let ans = rustsbi::ecall(ctx.a7, ctx.a6, param);
+                ctx.a0 = ans.error;
+                ctx.a1 = ans.value;
+                ctx.mepc = ctx.mepc.wrapping_add(4);
+            }
+            GeneratorState::Yielded(MachineTrap::IllegalInstruction()) => {
+                let ctx = rt.context_mut();
+                // FIXME: get_vaddr_u32这个过程可能出错。
+                let ins = unsafe { get_vaddr_u32(ctx.mepc) } as usize;
+                if !emulate_illegal_instruction(ctx, ins) {
+                    unsafe {
+                        if feature::should_transfer_trap(ctx) {
+                            feature::do_transfer_trap(
+                                ctx,
+                                Trap::Exception(Exception::IllegalInstruction),
+                            )
+                        } else {
+                            fail_illegal_instruction(ctx, ins)
+                        }
+                    }
+                }
+            }
+            GeneratorState::Yielded(MachineTrap::ExternalInterrupt()) => unsafe {
+                let ctx = rt.context_mut();
+                feature::call_supervisor_interrupt(ctx)
+            },
+            GeneratorState::Yielded(MachineTrap::MachineTimer()) => {
+                feature::forward_supervisor_timer()
+            }
+            GeneratorState::Yielded(MachineTrap::MachineSoft()) => {
+                feature::forward_supervisor_soft()
+            }
+            // todo:编写样例,验证store page fault和instruction page fault
+            GeneratorState::Yielded(MachineTrap::InstructionFault(addr)) => {
+                let ctx = rt.context_mut();
+                if feature::is_page_fault(addr) {
+                    unsafe {
+                        feature::do_transfer_trap(
+                            ctx,
+                            Trap::Exception(Exception::InstructionPageFault),
+                        )
+                    }
+                } else {
+                    unsafe {
+                        feature::do_transfer_trap(ctx, Trap::Exception(Exception::InstructionFault))
+                    }
+                }
+            }
+            GeneratorState::Yielded(MachineTrap::LoadFault(addr)) => {
+                let ctx = rt.context_mut();
+                if feature::is_page_fault(addr) {
+                    unsafe {
+                        feature::do_transfer_trap(ctx, Trap::Exception(Exception::LoadPageFault))
+                    }
+                } else {
+                    unsafe { feature::do_transfer_trap(ctx, Trap::Exception(Exception::LoadFault)) }
+                }
+            }
+            GeneratorState::Yielded(MachineTrap::StoreFault(addr)) => {
+                let ctx = rt.context_mut();
+                if feature::is_page_fault(addr) {
+                    unsafe {
+                        feature::do_transfer_trap(ctx, Trap::Exception(Exception::StorePageFault))
+                    }
+                } else {
+                    unsafe {
+                        feature::do_transfer_trap(ctx, Trap::Exception(Exception::StoreFault))
+                    }
+                }
+            }
+            GeneratorState::Complete(()) => unreachable!(),
+        }
+    }
+}
+
+#[inline]
+unsafe fn get_vaddr_u32(vaddr: usize) -> u32 {
+    get_vaddr_u16(vaddr) as u32 | ((get_vaddr_u16(vaddr.wrapping_add(2)) as u32) << 16)
+}
+
+#[inline]
+unsafe fn get_vaddr_u16(vaddr: usize) -> u16 {
+    let mut ans: u16;
+    asm!("
+        li      {2}, (1 << 17)
+        csrrs   {2}, mstatus, {2}
+        lhu     {0}, 0({1})
+        csrw    mstatus, {2}
+    ", out(reg) ans, in(reg) vaddr, out(reg) _);
+    ans
+}
+
+fn emulate_sbi_call(ctx: &mut SupervisorContext) -> bool {
+    if feature::emulate_sbi_rustsbi_k210_sext(ctx) || feature::sbi_set_mext(ctx) {
+        return true;
+    }
+    false
+}
+
+fn emulate_illegal_instruction(ctx: &mut SupervisorContext, ins: usize) -> bool {
+    if feature::emulate_rdtime(ctx, ins) {
+        return true;
+    }
+    if feature::emulate_sfence_vma(ctx, ins) {
+        return true;
+    }
+    false
+}
+
+// 真·非法指令异常,是M层出现的
+fn fail_illegal_instruction(ctx: &mut SupervisorContext, ins: usize) -> ! {
+    panic!("invalid instruction from machine level, mepc: {:016x?}, instruction: {:016x?}, context: {:016x?}", ctx.mepc, ins, ctx);
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/feature.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/feature.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8160b8c5c494e27fb4f07365573e719f36f32868
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/feature.rs
@@ -0,0 +1,14 @@
+mod delegate_page_fault;
+mod emulate_rdtime;
+mod sfence_vma;
+mod supervisor_interrupt;
+mod transfer_trap;
+
+pub use delegate_page_fault::is_page_fault;
+pub use emulate_rdtime::emulate_rdtime;
+pub use sfence_vma::emulate_sfence_vma;
+pub use supervisor_interrupt::{
+    call_supervisor_interrupt, emulate_sbi_rustsbi_k210_sext, forward_supervisor_soft,
+    forward_supervisor_timer, preprocess_supervisor_external,sbi_set_mext
+};
+pub use transfer_trap::{do_transfer_trap, should_transfer_trap};
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/feature/delegate_page_fault.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/delegate_page_fault.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2eb4e56d995dac829cb8ca5971719fa33a350b09
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/delegate_page_fault.rs
@@ -0,0 +1,199 @@
+use core::arch::asm;
+use riscv::register::{
+    mcause::{self, Exception, Trap},
+    mepc,
+    mtvec::{self, TrapMode},
+};
+
+// This function will lookup virtual memory module and page table system
+// if memory fault from address `addr` is a page fault, return true
+// otherwise when not a page fault, or when paging is disabled, return false
+pub fn is_page_fault(addr: usize) -> bool {
+    if !is_s1p9_mstatus_sv39_mode() {
+        return false;
+    }
+    if !check_sext_sv39(addr) {
+        return true;
+    }
+    let base_ppn = read_sptbr_ppn();
+    let level_2_ppn = unsafe {
+        let vpn2 = (addr >> 30) & 0x1FF;
+        let ptr = ((base_ppn << 12) as *const usize).add(vpn2);
+        let level_2_pte = if let Ok(ans) = try_read_address(ptr) {
+            ans
+        } else {
+            // level 2 ppn read failed
+            return true;
+        };
+        if (level_2_pte & 0b1) == 0 {
+            // level 2 pte is not valid
+            return true;
+        }
+        if (level_2_pte & 0b1110) != 0b0000 && (level_2_pte >> 10) & 0x3FFFF != 0 {
+            // 大页对齐出错,返回页异常
+            // level 2 huge page align not satisfied
+            return true;
+        }
+        (level_2_pte >> 10) & 0x3F_FFFF_FFFF
+    };
+    let level_1_ppn = unsafe {
+        let vpn1 = (addr >> 21) & 0x1FF;
+        let ptr = ((level_2_ppn << 12) as *const usize).add(vpn1);
+        let level_1_pte = if let Ok(ans) = try_read_address(ptr) {
+            ans
+        } else {
+            // level 1 ppn read failed
+            return true;
+        };
+        if (level_1_pte & 0b1) == 0 {
+            // level 1 pte is not valid
+            return true;
+        }
+        if (level_1_pte & 0b1110) != 0b0000 && (level_1_pte >> 10) & 0x1FF != 0 {
+            // 大页对齐出错,返回页异常
+            // level 1 huge page align not satisfied
+            return true;
+        }
+        (level_1_pte >> 10) & 0x3F_FFFF_FFFF
+    };
+    let _ppn = unsafe {
+        let vpn0 = (addr >> 12) & 0x1FF;
+        let ptr = ((level_1_ppn << 12) as *const usize).add(vpn0);
+        let final_pte = if let Ok(ans) = try_read_address(ptr) {
+            ans
+        } else {
+            // level 0 ppn read failed
+            return true;
+        };
+        if (final_pte & 0b1) == 0 {
+            // level 0 pte is not valid
+            return true;
+        }
+        if (final_pte & 0b1110) == 0b0000 {
+            // level 0 page cannot have leaves
+            return true;
+        }
+        (final_pte >> 10) & 0x3F_FFFF_FFFF
+    };
+    // 到这一步都没有错误,说明查找是成功的,并非页异常
+    false
+}
+
+// read Privileged Spec v1.9 defined mstatus to decide virtual memory mode
+// 9 -> Sv39
+fn is_s1p9_mstatus_sv39_mode() -> bool {
+    let mut mstatus_bits: usize;
+    unsafe { asm!("csrr {}, mstatus", out(reg) mstatus_bits) };
+    let mode = (mstatus_bits >> 24) & 0b1_1111;
+    mode == 9
+}
+
+// if sext is not valid, raise a page fault
+fn check_sext_sv39(addr: usize) -> bool {
+    let addr_b38 = (addr >> 38) & 0b1 == 1;
+    let sext = addr >> 39;
+    if addr_b38 && sext == 0x1FFFFFF {
+        return true;
+    }
+    if !addr_b38 && sext == 0 {
+        return true;
+    }
+    false
+}
+
+// get Privileged Spec v1.9 defined sptbr root page table base
+fn read_sptbr_ppn() -> usize {
+    let sptbr_bits: usize;
+    unsafe { asm!("csrr {}, 0x180", out(reg) sptbr_bits) };
+    sptbr_bits & 0xFFF_FFFF_FFFF
+}
+
+#[derive(Debug)]
+struct LoadAccessFault;
+
+unsafe fn try_read_address(ptr: *const usize) -> Result<usize, LoadAccessFault> {
+    let saved_mtvec_address = init_trap_vector();
+    let ans: usize;
+    asm!("li    tp, 0");
+    asm!("ld    {}, 0({})", out(reg) ans, in(reg) ptr);
+    let has_error: usize;
+    asm!("mv    {}, tp", out(reg) has_error);
+    let ans = if has_error == 1 {
+        Err(LoadAccessFault)
+    } else {
+        Ok(ans)
+    };
+    recover_trap_vector(saved_mtvec_address);
+    return ans;
+}
+
+extern "C" fn memory_fault_catch_handler() {
+    let cause = mcause::read().cause();
+    if cause == Trap::Exception(Exception::LoadFault) {
+        unsafe { asm!("li   tp, 1") }; // tp = 1 说明发生了错误
+    }
+    let bad_ins_addr = mepc::read();
+    let ins_16 = unsafe { core::ptr::read_volatile(bad_ins_addr as *const u16) };
+    let bytes = if ins_16 & 0b11 != 0b11 { 2 } else { 4 };
+    mepc::write(mepc::read().wrapping_add(bytes)); // skip current load instruction
+}
+
+fn init_trap_vector() -> usize {
+    let mut addr = delegate_catch_trap as usize;
+    if addr & 0x2 != 0 {
+        addr = addr.wrapping_add(0x2); // 必须对齐到4个字节
+    }
+    let saved_mtvec_address = mtvec::read().address();
+    unsafe { mtvec::write(addr, TrapMode::Direct) };
+    saved_mtvec_address
+}
+
+fn recover_trap_vector(saved_mtvec_address: usize) {
+    unsafe { mtvec::write(saved_mtvec_address, TrapMode::Direct) }
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn delegate_catch_trap() -> ! {
+    asm!(
+        ".align 4", // align to 4 bytes
+        "addi   sp, sp, -8*16
+        sd      ra, 8*0(sp)
+        sd      t0, 8*1(sp)
+        sd      t1, 8*2(sp)
+        sd      t2, 8*3(sp)
+        sd      t3, 8*4(sp)
+        sd      t4, 8*5(sp)
+        sd      t5, 8*6(sp)
+        sd      t6, 8*7(sp)
+        sd      a0, 8*8(sp)
+        sd      a1, 8*9(sp)
+        sd      a2, 8*10(sp)
+        sd      a3, 8*11(sp)
+        sd      a4, 8*12(sp)
+        sd      a5, 8*13(sp)
+        sd      a6, 8*14(sp)
+        sd      a7, 8*15(sp)",
+        "call   {memory_fault_catch_handler}",
+        "ld     ra, 8*0(sp)
+        ld      t0, 8*1(sp)
+        ld      t1, 8*2(sp)
+        ld      t2, 8*3(sp)
+        ld      t3, 8*4(sp)
+        ld      t4, 8*5(sp)
+        ld      t5, 8*6(sp)
+        ld      t6, 8*7(sp)
+        ld      a0, 8*8(sp)
+        ld      a1, 8*9(sp)
+        ld      a2, 8*10(sp)
+        ld      a3, 8*11(sp)
+        ld      a4, 8*12(sp)
+        ld      a5, 8*13(sp)
+        ld      a6, 8*14(sp)
+        ld      a7, 8*15(sp)
+        addi    sp, sp, 8*16",
+        "mret",
+        memory_fault_catch_handler = sym memory_fault_catch_handler,
+        options(noreturn)
+    )
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/feature/emulate_rdtime.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/emulate_rdtime.rs
new file mode 100644
index 0000000000000000000000000000000000000000..49534d1c22f53926bdc610ee58bb0ca54010821f
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/emulate_rdtime.rs
@@ -0,0 +1,27 @@
+use crate::runtime::SupervisorContext;
+
+#[inline]
+pub fn emulate_rdtime(ctx: &mut SupervisorContext, ins: usize) -> bool {
+    if ins & 0xFFFFF07F == 0xC0102073 {
+        // rdtime is actually a csrrw instruction
+        let rd = ((ins >> 7) & 0b1_1111) as u8;
+        let mtime = k210_hal::clint::mtime::read();
+        let time_usize = mtime as usize;
+        set_register_xi(ctx, rd, time_usize);
+        ctx.mepc = ctx.mepc.wrapping_add(4); // skip current instruction
+        return true;
+    } else {
+        return false; // is not a rdtime instruction
+    }
+}
+
+#[inline]
+fn set_register_xi(ctx: &mut SupervisorContext, i: u8, data: usize) {
+    let registers = unsafe { &mut *(ctx as *mut _ as *mut [usize; 31]) };
+    assert!(i <= 31, "i should be valid register target");
+    if i == 0 {
+        // x0, don't modify
+        return;
+    }
+    registers[(i - 1) as usize] = data;
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/feature/sfence_vma.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/sfence_vma.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b74175f7a44f242c050a0e7ed7c585d22685888d
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/sfence_vma.rs
@@ -0,0 +1,43 @@
+use crate::runtime::SupervisorContext;
+use core::arch::asm;
+use riscv::register::{mstatus, satp};
+
+// There is no `sfence.vma` in 1.9.1 privileged spec; however there is a `sfence.vm`.
+// For backward compability, here we emulate the first instruction using the second one.
+// sfence.vma: | 31..25 funct7=SFENCE.VMA(0001001) | 24..20 rs2/asid | 19..15 rs1/vaddr |
+//               14..12 funct3=PRIV(000) | 11..7 rd, =0 | 6..0 opcode=SYSTEM(1110011) |
+// sfence.vm(1.9):  | 31..=20 SFENCE.VM(000100000100) | 19..15 rs1/vaddr |
+//               14..12 funct3=PRIV(000) | 11..7 rd, =0 | 6..0 opcode=SYSTEM(1110011) |
+
+#[inline]
+pub fn emulate_sfence_vma(ctx: &mut SupervisorContext, ins: usize) -> bool {
+    if ins & 0xFE007FFF == 0x12000073 {
+        // sfence.vma instruction
+        // discard rs2 // let _rs2_asid = ((ins >> 20) & 0b1_1111) as u8;
+        // let rs1_vaddr = ((ins >> 15) & 0b1_1111) as u8;
+        // read paging mode from satp (sptbr)
+        let satp_bits = satp::read().bits();
+        // bit 63..20 is not readable and writeable on K210, so we cannot
+        // decide paging type from the 'satp' register.
+        // that also means that the asid function is not usable on this chip.
+        // we have to fix it to be Sv39.
+        let ppn = satp_bits & 0xFFF_FFFF_FFFF; // 43..0 PPN WARL
+                                               // write to sptbr
+        let sptbr_bits = ppn & 0x3F_FFFF_FFFF;
+        unsafe { asm!("csrw 0x180, {}", in(reg) sptbr_bits) }; // write to sptbr
+                                                               // enable paging (in v1.9.1, mstatus: | 28..24 VM[4:0] WARL | ... )
+        let mut mstatus_bits: usize;
+        unsafe { asm!("csrr {}, mstatus", out(reg) mstatus_bits) };
+        mstatus_bits &= !0x1F00_0000;
+        mstatus_bits |= 9 << 24;
+        unsafe { asm!("csrw mstatus, {}", in(reg) mstatus_bits) };
+        ctx.mstatus = mstatus::read();
+        // emulate with sfence.vm (declared in privileged spec v1.9)
+        unsafe { asm!(".word 0x10400073") }; // sfence.vm x0
+                                             // ::"r"(rs1_vaddr)
+        ctx.mepc = ctx.mepc.wrapping_add(4); // skip current instruction
+        return true;
+    } else {
+        return false; // is not a sfence.vma instruction
+    }
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/feature/supervisor_interrupt.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/supervisor_interrupt.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0c43acdacf30f58c5d431819fbf84cfcf2e95132
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/supervisor_interrupt.rs
@@ -0,0 +1,128 @@
+use crate::runtime::SupervisorContext;
+use core::arch::asm;
+use riscv::register::{mie, mip, mstatus, scause};
+use rustsbi::println;
+
+static mut DEVINTRENTRY: usize = 0;
+
+pub unsafe fn call_supervisor_interrupt(ctx: &mut SupervisorContext) {
+    // println!("ext called!");
+    mip::set_ssoft();
+    mie::clear_mext();
+    
+    // scause::write(0x1000_0000_0000_0002);
+
+    // let mut mstatus: usize;
+    // asm!("csrr {}, mstatus", out(reg) mstatus);
+    // // set mstatus.mprv
+    // mstatus |= 1 << 17;
+    // // it may trap from U/S Mode
+    // // save mpp and set mstatus.mpp to S Mode
+    // let mpp = (mstatus >> 11) & 3;
+    // mstatus = mstatus & !(3 << 11);
+    // mstatus |= 1 << 11;
+    // // drop mstatus.mprv protection
+    // asm!("csrw mstatus, {}", in(reg) mstatus);
+    // // compiler helps us save/restore caller-saved registers
+    // devintr();
+    // // restore mstatus
+    // mstatus = mstatus & !(3 << 11);
+    // mstatus |= mpp << 11;
+    // mstatus -= 1 << 17;
+    // asm!("csrw mstatus, {}", in(reg) mstatus);
+    // ctx.mstatus = mstatus::read();
+}
+
+#[inline]
+pub fn sbi_set_mext(ctx: &mut SupervisorContext) -> bool {
+    if ctx.a7 == 0x0A000009 && ctx.a6 == 0x210 {
+
+        unsafe {
+            mie::set_mext();
+        }
+        ctx.a0 = 0; // SbiRet::error = SBI_SUCCESS
+        ctx.a1 = 0; // SbiRet::value = 0
+        ctx.mepc = ctx.mepc.wrapping_add(4);
+        true
+    } else {
+        false
+    }
+}
+
+// We use implementation specific sbi_rustsbi_k210_sext function (extension
+// id: 0x0A000004, function id: 0x210) to register S-level interrupt handler
+// for K210 chip only. This chip uses 1.9.1 version of privileged spec,
+// which did not declare any S-level external interrupts.
+#[inline]
+pub fn emulate_sbi_rustsbi_k210_sext(ctx: &mut SupervisorContext) -> bool {
+    if ctx.a7 == 0x0A000004 && ctx.a6 == 0x210 {
+        // unsafe {
+        //     DEVINTRENTRY = ctx.a0;
+        // }
+        // enable mext
+        unsafe {
+            // mie::set_mext();
+            mip::set_ssoft();
+            mie::clear_mext();
+        }
+        // println!("sbi call success!");
+        // unsafe {
+        //     // mie::clear_mext();
+        //     mip::set_sext();
+        // }
+        // return values
+        ctx.a0 = 0; // SbiRet::error = SBI_SUCCESS
+        ctx.a1 = 0; // SbiRet::value = 0
+        ctx.mepc = ctx.mepc.wrapping_add(4);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+fn devintr() {
+    #[cfg(target_arch = "riscv")]
+    unsafe {
+        // call devintr defined in application
+        // we have to ask compiler save ra explicitly
+        asm!("jalr 0({})", in(reg) DEVINTRENTRY, lateout("ra") _);
+    }
+}
+
+// Due to legacy 1.9.1 version of privileged spec, if we are in S-level
+// timer handler (delegated from M mode), and we call SBI's `set_timer`,
+// a M-level external interrupt may be triggered. This may try to obtain
+// data structures locked previously by S-level interrupt handler, which
+// results in a deadlock.
+// Ref: https://github.com/luojia65/rustsbi/pull/5
+pub fn preprocess_supervisor_external(ctx: &mut SupervisorContext) {
+    if ctx.a7 == 0x0 {
+        unsafe {
+            let mtip = mip::read().mtimer();
+            if mtip {
+                if DEVINTRENTRY != 0 {
+                    mie::set_mext();
+                }
+            }
+        }
+    }
+}
+
+pub fn forward_supervisor_timer() {
+    // println!("forward timer intr!");
+    // Forward to S-level timer interrupt
+    unsafe {
+        mip::set_stimer(); // set S-timer interrupt flag
+        // mie::clear_mext(); // Ref: rustsbi Pull request #5
+        mie::clear_mtimer(); // mask M-timer interrupt
+    }
+}
+
+pub fn forward_supervisor_soft() {
+    // Forward to S-level software interrupt
+    println!("forward soft intr!");
+    unsafe {
+        mip::set_ssoft(); // set S-soft interrupt flag
+        mie::clear_msoft(); // mask M-soft interrupt
+    }
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/feature/transfer_trap.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/transfer_trap.rs
new file mode 100644
index 0000000000000000000000000000000000000000..713183ead90ec57d4bdd813b6f6369e61eb311c5
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/feature/transfer_trap.rs
@@ -0,0 +1,29 @@
+use crate::runtime::SupervisorContext;
+use riscv::register::{
+    mstatus::{self, MPP, SPP},
+    mtval, scause, sepc, stval, stvec,
+};
+
+pub unsafe fn should_transfer_trap(ctx: &mut SupervisorContext) -> bool {
+    ctx.mstatus.mpp() != MPP::Machine
+}
+
+pub unsafe fn do_transfer_trap(ctx: &mut SupervisorContext, cause: scause::Trap) {
+    // 设置S层异常原因为:非法指令
+    scause::set(cause);
+    // 填写异常指令的指令内容
+    stval::write(mtval::read());
+    // 填写S层需要返回到的地址,这里的mepc会被随后的代码覆盖掉
+    sepc::write(ctx.mepc);
+    // 设置中断位
+    mstatus::set_mpp(MPP::Supervisor);
+    mstatus::set_spp(SPP::Supervisor);
+    if mstatus::read().sie() {
+        mstatus::set_spie()
+    }
+    mstatus::clear_sie();
+    ctx.mstatus = mstatus::read();
+    // 设置返回地址,返回到S层
+    // 注意,无论是Direct还是Vectored模式,所有异常的向量偏移都是0,不需要处理中断向量,跳转到入口地址即可
+    ctx.mepc = stvec::read().address();
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/hart_csr_utils.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/hart_csr_utils.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a453aef35dbff8562273354772e1ac780bf7cbd5
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/hart_csr_utils.rs
@@ -0,0 +1,113 @@
+use alloc::vec::Vec;
+use riscv::register::{
+    medeleg, mideleg,
+    misa::{self, MXL},
+};
+use rustsbi::{print, println};
+
+pub fn print_hart_csrs() {
+    print_misa();
+    print_mideleg();
+    print_medeleg();
+}
+
+#[inline]
+fn print_misa() {
+    let isa = misa::read();
+    if let Some(isa) = isa {
+        let mxl_str = match isa.mxl() {
+            MXL::XLEN32 => "RV32",
+            MXL::XLEN64 => "RV64",
+            MXL::XLEN128 => "RV128",
+        };
+        print!("[rustsbi] misa: {}", mxl_str);
+        for ext in 'A'..='Z' {
+            if isa.has_extension(ext) {
+                print!("{}", ext);
+            }
+        }
+        println!("");
+    }
+}
+
+#[inline]
+fn print_mideleg() {
+    let mideleg = mideleg::read();
+    let mut delegs = Vec::new();
+    if mideleg.usoft() {
+        delegs.push("usoft")
+    }
+    if mideleg.utimer() {
+        delegs.push("utimer")
+    }
+    if mideleg.uext() {
+        delegs.push("uext")
+    }
+    if mideleg.ssoft() {
+        delegs.push("ssoft")
+    }
+    if mideleg.stimer() {
+        delegs.push("stimer")
+    }
+    if mideleg.sext() {
+        delegs.push("sext")
+    }
+    println!(
+        "[rustsbi] mideleg: {} ({:#x})",
+        delegs.join(", "),
+        mideleg.bits()
+    );
+}
+
+#[inline]
+fn print_medeleg() {
+    let medeleg = medeleg::read();
+    let mut delegs = Vec::new();
+    if medeleg.instruction_misaligned() {
+        delegs.push("ima")
+    }
+    if medeleg.instruction_fault() {
+        delegs.push("ia") // instruction access
+    }
+    if medeleg.illegal_instruction() {
+        delegs.push("illinsn")
+    }
+    if medeleg.breakpoint() {
+        delegs.push("bkpt")
+    }
+    if medeleg.load_misaligned() {
+        delegs.push("lma")
+    }
+    if medeleg.load_fault() {
+        delegs.push("la") // load access
+    }
+    if medeleg.store_misaligned() {
+        delegs.push("sma")
+    }
+    if medeleg.store_fault() {
+        delegs.push("sa") // store access
+    }
+    if medeleg.user_env_call() {
+        delegs.push("uecall")
+    }
+    if medeleg.supervisor_env_call() {
+        delegs.push("secall")
+    }
+    if medeleg.machine_env_call() {
+        delegs.push("mecall")
+    }
+    if medeleg.instruction_page_fault() {
+        delegs.push("ipage")
+    }
+    if medeleg.load_page_fault() {
+        delegs.push("lpage")
+    }
+    if medeleg.store_page_fault() {
+        delegs.push("spage")
+    }
+    println!(
+        "[rustsbi] medeleg: {} ({:#x})",
+        delegs.join(", "),
+        medeleg.bits()
+    );
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/main.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..eb3fd33e8299e825422e8f9929ec1032a454c98a
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/main.rs
@@ -0,0 +1,176 @@
+#![no_std]
+#![no_main]
+#![feature(naked_functions)]
+#![feature(generator_trait)]
+#![feature(default_alloc_error_handler)]
+#![feature(asm_sym, asm_const)]
+
+mod execute;
+mod feature;
+mod hart_csr_utils;
+mod peripheral;
+mod runtime;
+
+extern crate alloc;
+
+use buddy_system_allocator::LockedHeap;
+use core::arch::asm;
+use core::panic::PanicInfo;
+
+use rustsbi::println;
+
+const PER_HART_STACK_SIZE: usize = 8 * 1024; // 8KiB
+const SBI_STACK_SIZE: usize = 2 * PER_HART_STACK_SIZE;
+#[link_section = ".bss.uninit"]
+static mut SBI_STACK: [u8; SBI_STACK_SIZE] = [0; SBI_STACK_SIZE];
+
+const SBI_HEAP_SIZE: usize = 8 * 1024; // 8KiB
+#[link_section = ".bss.uninit"]
+static mut HEAP_SPACE: [u8; SBI_HEAP_SIZE] = [0; SBI_HEAP_SIZE];
+#[global_allocator]
+static SBI_HEAP: LockedHeap<32> = LockedHeap::empty();
+
+static DEVICE_TREE_BINARY: &[u8] = include_bytes!("../kendryte-k210.dtb");
+
+#[cfg_attr(not(test), panic_handler)]
+#[allow(unused)]
+fn panic(info: &PanicInfo) -> ! {
+    let hart_id = riscv::register::mhartid::read();
+    // 输出的信息大概是“[rustsbi-panic] hart 0 panicked at ...”
+    println!("[rustsbi-panic] hart {} {}", hart_id, info);
+    println!("[rustsbi-panic] system shutdown scheduled due to RustSBI panic");
+    use rustsbi::Reset;
+    peripheral::Reset.system_reset(
+        rustsbi::reset::RESET_TYPE_SHUTDOWN,
+        rustsbi::reset::RESET_REASON_SYSTEM_FAILURE,
+    );
+    loop {}
+}
+
+extern "C" fn rust_main() -> ! {
+    let hartid = riscv::register::mhartid::read();
+    if hartid == 0 {
+        init_bss();
+    }
+    pause_if_not_start_hart();
+    runtime::init();
+    if hartid == 0 {
+        init_heap();
+        peripheral::init_peripheral();
+        println!("[rustsbi] RustSBI version {}", rustsbi::VERSION);
+        println!("{}", rustsbi::LOGO);
+        println!(
+            "[rustsbi] Implementation: RustSBI-K210 Version {}",
+            env!("CARGO_PKG_VERSION")
+        );
+    }
+    delegate_interrupt_exception();
+    if hartid == 0 {
+        hart_csr_utils::print_hart_csrs();
+        println!("[rustsbi] enter supervisor 0x80020000");
+    }
+    execute::execute_supervisor(0x80020000, hartid, DEVICE_TREE_BINARY.as_ptr() as usize)
+}
+
+fn pause_if_not_start_hart() {
+    use k210_hal::clint::msip;
+    use riscv::asm::wfi;
+    use riscv::register::{mhartid, mie, mip};
+
+    let hartid = mhartid::read();
+    if hartid != 0 {
+        unsafe {
+            // Clear IPI
+            msip::clear_ipi(hartid);
+            // Start listening for software interrupts
+            mie::set_msoft();
+
+            loop {
+                wfi();
+                if mip::read().msoft() {
+                    break;
+                }
+            }
+
+            // Stop listening for software interrupts
+            mie::clear_msoft();
+            // Clear IPI
+            msip::clear_ipi(hartid);
+        }
+    }
+}
+
+fn init_bss() {
+    extern "C" {
+        static mut ebss: u32;
+        static mut sbss: u32;
+        static mut edata: u32;
+        static mut sdata: u32;
+        static sidata: u32;
+    }
+    unsafe {
+        r0::zero_bss(&mut sbss, &mut ebss);
+        r0::init_data(&mut sdata, &mut edata, &sidata);
+    }
+}
+
+fn init_heap() {
+    unsafe {
+        SBI_HEAP
+            .lock()
+            .init(HEAP_SPACE.as_ptr() as usize, SBI_HEAP_SIZE)
+    }
+}
+
+// 委托终端;把S的中断全部委托给S层
+fn delegate_interrupt_exception() {
+    use riscv::register::{medeleg, mideleg, mie};
+    unsafe {
+        //mideleg::set_sext();
+        mideleg::set_stimer();
+        mideleg::set_ssoft();
+        medeleg::set_instruction_misaligned();
+        medeleg::set_breakpoint();
+        medeleg::set_user_env_call();
+        /* MMU Exception Delegation
+        /* Page Faults are *Reserved* in 1.9.1 version */
+        - medeleg::set_instruction_page_fault();
+        - medeleg::set_load_page_fault();
+        - medeleg::set_store_page_fault();
+        /* Actually, in 1.9.1 they are merged into more general exceptions */
+        + medeleg::set_instruction_fault();
+        + medeleg::set_load_fault();
+        + medeleg::set_store_fault(); */
+        // medeleg::set_instruction_fault();
+        // medeleg::set_load_fault();
+        // medeleg::set_store_fault();
+        // 默认不打开mie::set_mext
+        // 不打开mie::set_mtimer
+        mie::set_mext();
+        mie::set_msoft();
+    }
+}
+
+#[naked]
+#[link_section = ".text.entry"]
+#[export_name = "_start"]
+unsafe extern "C" fn entry() -> ! {
+    asm!(
+    // 1. set sp
+    // sp = bootstack + (hartid + 1) * HART_STACK_SIZE
+    "
+    la      sp, {stack}
+    li      t0, {per_hart_stack_size}
+    csrr    a0, mhartid
+    addi    t1, a0, 1
+1:  add     sp, sp, t0
+    addi    t1, t1, -1
+    bnez    t1, 1b
+    ",
+    // 2. jump to rust_main (absolute address)
+    "j      {rust_main}", 
+    per_hart_stack_size = const PER_HART_STACK_SIZE,
+    stack = sym SBI_STACK,
+    rust_main = sym rust_main,
+    options(noreturn))
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/peripheral.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/peripheral.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7c5f2bf47cc0280cefd91b7acdd4296d93ff156b
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/peripheral.rs
@@ -0,0 +1,65 @@
+use k210_hal::{clint::msip, clock::Clocks, fpioa, pac, prelude::*};
+use riscv::register::{mhartid, mip};
+use rustsbi::println;
+
+pub fn init_peripheral() {
+    let p = pac::Peripherals::take().unwrap();
+
+    let mut sysctl = p.SYSCTL.constrain();
+    let fpioa = p.FPIOA.split(&mut sysctl.apb0);
+    let clocks = Clocks::new();
+    let _uarths_tx = fpioa.io5.into_function(fpioa::UARTHS_TX);
+    let _uarths_rx = fpioa.io4.into_function(fpioa::UARTHS_RX);
+    // Configure UART
+    let serial = p.UARTHS.configure(115_200.bps(), &clocks);
+    let (tx, rx) = serial.split();
+    // let uarths = pac::UARTHS::ptr();
+    // unsafe {
+    //     (*uarths).ie.write(|w| {
+    //         w.rxwm().set_bit();
+    //         w.txwm().set_bit();
+    //         w
+    //     });
+    // }
+    rustsbi::legacy_stdio::init_legacy_stdio_embedded_hal_fuse(tx, rx);
+    rustsbi::init_timer(Timer);
+    rustsbi::init_reset(Reset);
+    rustsbi::init_ipi(Ipi);
+}
+
+struct Ipi;
+
+impl rustsbi::Ipi for Ipi {
+    fn max_hart_id(&self) -> usize {
+        1
+    }
+    fn send_ipi_many(&self, hart_mask: rustsbi::HartMask) -> rustsbi::SbiRet {
+        for i in 0..=1 {
+            if hart_mask.has_bit(i) {
+                msip::set_ipi(i);
+                msip::clear_ipi(i);
+            }
+        }
+        rustsbi::SbiRet::ok(0)
+    }
+}
+
+struct Timer;
+
+impl rustsbi::Timer for Timer {
+    fn set_timer(&self, stime_value: u64) {
+        // This function must clear the pending timer interrupt bit as well.
+        use k210_hal::clint::mtimecmp;
+        mtimecmp::write(mhartid::read(), stime_value);
+        unsafe { mip::clear_mtimer() };
+    }
+}
+
+pub struct Reset;
+
+impl rustsbi::Reset for Reset {
+    fn system_reset(&self, reset_type: usize, reset_reason: usize) -> rustsbi::SbiRet {
+        println!("[rustsbi] reset triggered! todo: shutdown all harts on k210; program halt. Type: {}, reason: {}", reset_type, reset_reason);
+        loop {}
+    }
+}
diff --git a/bootloader/rustsbi-k210/rustsbi-k210/src/runtime.rs b/bootloader/rustsbi-k210/rustsbi-k210/src/runtime.rs
new file mode 100644
index 0000000000000000000000000000000000000000..55db6a3ae13067845653875c210a26a4de07654b
--- /dev/null
+++ b/bootloader/rustsbi-k210/rustsbi-k210/src/runtime.rs
@@ -0,0 +1,289 @@
+use core::{
+    arch::asm,
+    ops::{Generator, GeneratorState},
+    pin::Pin,
+};
+use riscv::register::{
+    mcause::{self, Exception, Interrupt, Trap},
+    mstatus::{self, Mstatus, MPP},
+    mtval,
+    mtvec::{self, TrapMode},
+};
+
+pub fn init() {
+    let mut addr = from_supervisor_save as usize;
+    if addr & 0x2 != 0 {
+        addr += 0x2; // 必须对齐到4个字节
+    }
+    unsafe { mtvec::write(addr, TrapMode::Direct) };
+}
+
+pub struct Runtime {
+    context: SupervisorContext,
+}
+
+impl Runtime {
+    pub fn new_sbi_supervisor(supervisor_mepc: usize, a0: usize, a1: usize) -> Self {
+        let context: SupervisorContext = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+        let mut ans = Runtime { context };
+        ans.prepare_supervisor(supervisor_mepc);
+        ans.context.a0 = a0;
+        ans.context.a1 = a1;
+        ans
+    }
+
+    fn reset(&mut self) {
+        unsafe { mstatus::set_mpp(MPP::Supervisor) };
+        self.context.mstatus = mstatus::read();
+        self.context.machine_stack = 0x2333333366666666; // 将会被resume函数覆盖
+    }
+
+    // 在处理异常的时候,使用context_mut得到运行时当前用户的上下文,可以改变上下文的内容
+    pub fn context_mut(&mut self) -> &mut SupervisorContext {
+        &mut self.context
+    }
+
+    pub fn prepare_supervisor(&mut self, new_mepc: usize) {
+        self.reset();
+        self.context.mepc = new_mepc;
+    }
+}
+
+impl Generator for Runtime {
+    type Yield = MachineTrap;
+    type Return = ();
+    fn resume(mut self: Pin<&mut Self>, _arg: ()) -> GeneratorState<Self::Yield, Self::Return> {
+        unsafe { do_resume(&mut self.context as *mut _) };
+        let mtval = mtval::read();
+        let trap = match mcause::read().cause() {
+            Trap::Exception(Exception::SupervisorEnvCall) => MachineTrap::SbiCall(),
+            Trap::Exception(Exception::IllegalInstruction) => MachineTrap::IllegalInstruction(),
+            Trap::Exception(Exception::InstructionFault) => MachineTrap::InstructionFault(mtval),
+            Trap::Exception(Exception::LoadFault) => MachineTrap::LoadFault(mtval),
+            Trap::Exception(Exception::StoreFault) => MachineTrap::StoreFault(mtval),
+            Trap::Interrupt(Interrupt::MachineExternal) => MachineTrap::ExternalInterrupt(),
+            Trap::Interrupt(Interrupt::MachineTimer) => MachineTrap::MachineTimer(),
+            Trap::Interrupt(Interrupt::MachineSoft) => MachineTrap::MachineSoft(),
+            e => panic!(
+                "unhandled exception: {:?}! mtval: {:#x?}, ctx: {:#x?}",
+                e, mtval, self.context
+            ),
+        };
+        GeneratorState::Yielded(trap)
+    }
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub enum MachineTrap {
+    SbiCall(),
+    IllegalInstruction(),
+    ExternalInterrupt(),
+    MachineTimer(),
+    MachineSoft(),
+    InstructionFault(usize),
+    LoadFault(usize),
+    StoreFault(usize),
+}
+
+#[derive(Debug)]
+#[repr(C)]
+pub struct SupervisorContext {
+    pub ra: usize, // 0
+    pub sp: usize,
+    pub gp: usize,
+    pub tp: usize,
+    pub t0: usize,
+    pub t1: usize,
+    pub t2: usize,
+    pub s0: usize,
+    pub s1: usize,
+    pub a0: usize,
+    pub a1: usize,
+    pub a2: usize,
+    pub a3: usize,
+    pub a4: usize,
+    pub a5: usize,
+    pub a6: usize,
+    pub a7: usize,
+    pub s2: usize,
+    pub s3: usize,
+    pub s4: usize,
+    pub s5: usize,
+    pub s6: usize,
+    pub s7: usize,
+    pub s8: usize,
+    pub s9: usize,
+    pub s10: usize,
+    pub s11: usize,
+    pub t3: usize,
+    pub t4: usize,
+    pub t5: usize,
+    pub t6: usize,            // 30
+    pub mstatus: Mstatus,     // 31
+    pub mepc: usize,          // 32
+    pub machine_stack: usize, // 33
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn do_resume(_supervisor_context: *mut SupervisorContext) {
+    asm!("j     {from_machine_save}", from_machine_save = sym from_machine_save, options(noreturn))
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn from_machine_save(_supervisor_context: *mut SupervisorContext) -> ! {
+    asm!( // sp:机器栈顶
+        "addi   sp, sp, -15*8", // sp:机器栈顶
+        // 进入函数之前,已经保存了调用者寄存器,应当保存被调用者寄存器
+        "sd     ra, 0*8(sp)
+        sd      gp, 1*8(sp)
+        sd      tp, 2*8(sp)
+        sd      s0, 3*8(sp)
+        sd      s1, 4*8(sp)
+        sd      s2, 5*8(sp)
+        sd      s3, 6*8(sp)
+        sd      s4, 7*8(sp)
+        sd      s5, 8*8(sp)
+        sd      s6, 9*8(sp)
+        sd      s7, 10*8(sp)
+        sd      s8, 11*8(sp)
+        sd      s9, 12*8(sp)
+        sd      s10, 13*8(sp)
+        sd      s11, 14*8(sp)", 
+        // a0:特权级上下文
+        "j      {to_supervisor_restore}",
+        to_supervisor_restore = sym to_supervisor_restore,
+        options(noreturn)
+    )
+}
+
+#[naked]
+#[link_section = ".text"]
+pub unsafe extern "C" fn to_supervisor_restore(_supervisor_context: *mut SupervisorContext) -> ! {
+    asm!(
+        // a0:特权级上下文
+        "sd     sp, 33*8(a0)", // 机器栈顶放进特权级上下文
+        "csrw   mscratch, a0", // 新mscratch:特权级上下文
+        // mscratch:特权级上下文
+        "mv     sp, a0", // 新sp:特权级上下文
+        "ld     t0, 31*8(sp)
+        ld      t1, 32*8(sp)
+        csrw    mstatus, t0
+        csrw    mepc, t1",
+        "ld     ra, 0*8(sp)
+        ld      gp, 2*8(sp)
+        ld      tp, 3*8(sp)
+        ld      t0, 4*8(sp)
+        ld      t1, 5*8(sp)
+        ld      t2, 6*8(sp)
+        ld      s0, 7*8(sp)
+        ld      s1, 8*8(sp)
+        ld      a0, 9*8(sp)
+        ld      a1, 10*8(sp)
+        ld      a2, 11*8(sp)
+        ld      a3, 12*8(sp)
+        ld      a4, 13*8(sp)
+        ld      a5, 14*8(sp)
+        ld      a6, 15*8(sp)
+        ld      a7, 16*8(sp)
+        ld      s2, 17*8(sp)
+        ld      s3, 18*8(sp)
+        ld      s4, 19*8(sp)
+        ld      s5, 20*8(sp)
+        ld      s6, 21*8(sp)
+        ld      s7, 22*8(sp)
+        ld      s8, 23*8(sp)
+        ld      s9, 24*8(sp)
+        ld     s10, 25*8(sp)
+        ld     s11, 26*8(sp)
+        ld      t3, 27*8(sp)
+        ld      t4, 28*8(sp)
+        ld      t5, 29*8(sp)
+        ld      t6, 30*8(sp)",
+        "ld     sp, 1*8(sp)", // 新sp:特权级栈
+        // sp:特权级栈, mscratch:特权级上下文
+        "mret",
+        options(noreturn)
+    )
+}
+
+// 中断开始
+
+#[naked]
+#[link_section = ".text"]
+pub unsafe extern "C" fn from_supervisor_save() -> ! {
+    asm!( // sp:特权级栈,mscratch:特权级上下文
+        ".p2align 2",
+        "csrrw  sp, mscratch, sp", // 新mscratch:特权级栈, 新sp:特权级上下文
+        "sd     ra, 0*8(sp)
+        sd      gp, 2*8(sp)
+        sd      tp, 3*8(sp)
+        sd      t0, 4*8(sp)
+        sd      t1, 5*8(sp)
+        sd      t2, 6*8(sp)
+        sd      s0, 7*8(sp)
+        sd      s1, 8*8(sp)
+        sd      a0, 9*8(sp)
+        sd      a1, 10*8(sp)
+        sd      a2, 11*8(sp)
+        sd      a3, 12*8(sp)
+        sd      a4, 13*8(sp)
+        sd      a5, 14*8(sp)
+        sd      a6, 15*8(sp)
+        sd      a7, 16*8(sp)
+        sd      s2, 17*8(sp)
+        sd      s3, 18*8(sp)
+        sd      s4, 19*8(sp)
+        sd      s5, 20*8(sp)
+        sd      s6, 21*8(sp)
+        sd      s7, 22*8(sp)
+        sd      s8, 23*8(sp)
+        sd      s9, 24*8(sp)
+        sd     s10, 25*8(sp)
+        sd     s11, 26*8(sp)
+        sd      t3, 27*8(sp)
+        sd      t4, 28*8(sp)
+        sd      t5, 29*8(sp)
+        sd      t6, 30*8(sp)",
+        "csrr   t0, mstatus
+        sd      t0, 31*8(sp)",
+        "csrr   t1, mepc
+        sd      t1, 32*8(sp)",
+        // mscratch:特权级栈,sp:特权级上下文
+        "csrrw  t2, mscratch, sp", // 新mscratch:特权级上下文,t2:特权级栈
+        "sd     t2, 1*8(sp)", // 保存特权级栈
+        "j      {to_machine_restore}",
+        to_machine_restore = sym to_machine_restore,
+        options(noreturn)
+    )
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn to_machine_restore() -> ! {
+    asm!(
+        // mscratch:特权级上下文
+        "csrr   sp, mscratch", // sp:特权级上下文
+        "ld     sp, 33*8(sp)", // sp:机器栈
+        "ld     ra, 0*8(sp)
+        ld      gp, 1*8(sp)
+        ld      tp, 2*8(sp)
+        ld      s0, 3*8(sp)
+        ld      s1, 4*8(sp)
+        ld      s2, 5*8(sp)
+        ld      s3, 6*8(sp)
+        ld      s4, 7*8(sp)
+        ld      s5, 8*8(sp)
+        ld      s6, 9*8(sp)
+        ld      s7, 10*8(sp)
+        ld      s8, 11*8(sp)
+        ld      s9, 12*8(sp)
+        ld      s10, 13*8(sp)
+        ld      s11, 14*8(sp)",
+        "addi   sp, sp, 15*8", // sp:机器栈顶
+        "jr     ra",           // 其实就是ret
+        options(noreturn)
+    )
+}
diff --git a/bootloader/rustsbi-k210/test-kernel/.cargo/config.toml b/bootloader/rustsbi-k210/test-kernel/.cargo/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..73ab437208a08639e8e97dffd1aa8a005f38adc0
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/.cargo/config.toml
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64imac-unknown-none-elf"
+
+[target.riscv64imac-unknown-none-elf]
+rustflags = [
+    "-C", "link-arg=-Tlinker.ld",
+]
diff --git a/bootloader/rustsbi-k210/test-kernel/Cargo.toml b/bootloader/rustsbi-k210/test-kernel/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..4b7751708b2f80ce07f980f47648a18c8bd82be4
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "test-kernel"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+r0 = "1"
+lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
+riscv = { git = "https://github.com/rust-embedded/riscv", rev = "cd31989b", features = ["inline-asm"] }
+buddy_system_allocator = "0.8"
+spin = "0.9"
+bitflags = "1.2"
+bit_field = "0.10"
diff --git a/bootloader/rustsbi-k210/test-kernel/build.rs b/bootloader/rustsbi-k210/test-kernel/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a93135f62938f585b82848c9987895c9e9054396
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/build.rs
@@ -0,0 +1,18 @@
+use std::env;
+use std::fs;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
+
+    // Put the linker script somewhere the linker can find it
+    fs::File::create(out_dir.join("linker.ld"))
+        .unwrap()
+        .write_all(include_bytes!("src/linker.ld"))
+        .unwrap();
+    println!("cargo:rustc-link-search={}", out_dir.display());
+
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rerun-if-changed=src/linker.ld");
+}
diff --git a/bootloader/rustsbi-k210/test-kernel/src/console.rs b/bootloader/rustsbi-k210/test-kernel/src/console.rs
new file mode 100644
index 0000000000000000000000000000000000000000..dda4911a3383f1ebdeeb9041433785168cf69c60
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/src/console.rs
@@ -0,0 +1,31 @@
+use crate::sbi::console_putchar;
+use core::fmt::{self, Write};
+
+struct Stdout;
+
+impl Write for Stdout {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        for c in s.chars() {
+            console_putchar(c as usize);
+        }
+        Ok(())
+    }
+}
+
+pub fn print(args: fmt::Arguments) {
+    Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+    }
+}
+
+#[macro_export]
+macro_rules! println {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+    }
+}
diff --git a/bootloader/rustsbi-k210/test-kernel/src/feature.rs b/bootloader/rustsbi-k210/test-kernel/src/feature.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c16875c947ba0ddd9a3b8db0d5cd72a6f6ab5937
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/src/feature.rs
@@ -0,0 +1,9 @@
+mod base_extension;
+mod catch_page_fault;
+mod delegate_trap;
+mod sfence_vma;
+
+pub use base_extension::test_base_extension;
+pub use catch_page_fault::test_catch_page_fault;
+pub use delegate_trap::test_delegate_trap;
+pub use sfence_vma::test_sfence_vma;
diff --git a/bootloader/rustsbi-k210/test-kernel/src/feature/base_extension.rs b/bootloader/rustsbi-k210/test-kernel/src/feature/base_extension.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3125073d6449349a21120f914bb9af92f2e51a4b
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/src/feature/base_extension.rs
@@ -0,0 +1,33 @@
+use crate::{println, sbi};
+
+pub fn test_base_extension() {
+    println!(">> Test-kernel: Testing base extension");
+    let base_version = sbi::probe_extension(sbi::EXTENSION_BASE);
+    if base_version == 0 {
+        println!("!! Test-kernel: no base extension probed; SBI call returned value '0'");
+        println!(
+            "!! Test-kernel: This SBI implementation may only have legacy extension implemented"
+        );
+        println!("!! Test-kernel: SBI test FAILED due to no base extension found");
+        sbi::shutdown()
+    }
+    println!("<< Test-kernel: Base extension version: {:x}", base_version);
+    println!(
+        "<< Test-kernel: SBI specification version: {:x}",
+        sbi::get_spec_version()
+    );
+    println!(
+        "<< Test-kernel: SBI implementation Id: {:x}",
+        sbi::get_sbi_impl_id()
+    );
+    println!(
+        "<< Test-kernel: SBI implementation version: {:x}",
+        sbi::get_sbi_impl_version()
+    );
+    println!(
+        "<< Test-kernel: Device mvendorid: {:x}",
+        sbi::get_mvendorid()
+    );
+    println!("<< Test-kernel: Device marchid: {:x}", sbi::get_marchid());
+    println!("<< Test-kernel: Device mimpid: {:x}", sbi::get_mimpid());
+}
diff --git a/bootloader/rustsbi-k210/test-kernel/src/feature/catch_page_fault.rs b/bootloader/rustsbi-k210/test-kernel/src/feature/catch_page_fault.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3d3038182df8949cea161eaaf3fd5b6b27ba43dc
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/src/feature/catch_page_fault.rs
@@ -0,0 +1,173 @@
+use crate::{println, sbi};
+use core::arch::{asm, riscv64::sfence_vma_all};
+use riscv::register::{
+    satp::{self, Mode},
+    scause::{self, Exception, Trap},
+    sepc,
+    stvec::{self, TrapMode},
+};
+
+#[repr(align(4096))]
+struct PageTable {
+    #[allow(unused)] // Will be used by RISC-V hardware
+    entries: [usize; 512],
+}
+
+static mut TEST_PAGE_TABLE_0: PageTable = PageTable { entries: [0; 512] };
+static mut TEST_PAGE_TABLE_1: PageTable = PageTable { entries: [0; 512] };
+static mut TEST_PAGE_TABLE_2: PageTable = PageTable { entries: [0; 512] };
+
+pub fn test_catch_page_fault() {
+    println!(">> Test-kernel: Testing catch page fault");
+    init_trap_vector();
+    let ppn = init_page_table();
+    unsafe { satp::set(Mode::Sv39, 0, ppn) };
+    unsafe { sfence_vma_all() };
+    unsafe {
+        println!(">> Test-kernel: Wrong sign extension");
+        assert!(is_read_page_fault(0xfeff_ff80_0000_0000 as *const usize));
+        assert!(is_read_page_fault(0x0100_0000_0000_0000 as *const usize));
+    }
+    unsafe {
+        println!(">> Test-kernel: Read from invalid entry");
+        assert!(is_read_page_fault(0x1_0000_0000 as *const usize));
+        assert!(is_read_page_fault(0x0_c040_0000 as *const usize));
+        assert!(is_read_page_fault(0x0_c020_2000 as *const usize));
+    };
+    // unsafe {
+    println!(">> Test-kernel: Unaligned huge page");
+    //     assert!(is_read_page_fault(0x1_4000_0000 as *const usize));
+    //     assert!(is_read_page_fault(0x0_c060_0000 as *const usize));
+    // };
+    // unsafe {
+    println!(">> Test-kernel: Non existing page");
+    //     assert!(is_read_page_fault(0x1_8000_0000 as *const usize));
+    //     assert!(is_read_page_fault(0x0_c080_0000 as *const usize));
+    //     assert!(is_read_page_fault(0x0_c020_3000 as *const usize));
+    // };
+    unsafe {
+        println!(">> Test-kernel: Level zero page cannot have leaves");
+        assert!(is_read_page_fault(0x0_c020_1000 as *const usize));
+    };
+}
+
+fn init_page_table() -> usize {
+    let ppn1 = (unsafe { &TEST_PAGE_TABLE_1 } as *const _ as usize) >> 12;
+    let ppn2 = (unsafe { &TEST_PAGE_TABLE_2 } as *const _ as usize) >> 12;
+    unsafe {
+        TEST_PAGE_TABLE_0.entries[2] = (0x80000 << 10) | 0xf; // RWX, V
+        TEST_PAGE_TABLE_0.entries[3] = (ppn1 << 10) | 0x1; // 叶子, V
+        TEST_PAGE_TABLE_0.entries[4] = 0; // 无效
+        TEST_PAGE_TABLE_0.entries[5] = (0x80200 << 10) | 0xf; // RWX, V
+        TEST_PAGE_TABLE_0.entries[6] = (0x7ffff << 10) | 0xf; // RWX, V
+        TEST_PAGE_TABLE_0.entries[7] = (0x80000 << 10) | 0x7; // RW, V
+    }
+    unsafe {
+        TEST_PAGE_TABLE_1.entries[1] = (ppn2 << 10) | 0x1; // 叶子, V
+        TEST_PAGE_TABLE_1.entries[2] = 0; // 无效
+        TEST_PAGE_TABLE_1.entries[3] = (0x80201 << 10) | 0xf; // RWX, V
+        TEST_PAGE_TABLE_1.entries[4] = (0x7ffff << 10) | 0xf; // RWX, V
+        TEST_PAGE_TABLE_1.entries[5] = (0x80200 << 10) | 0x3; // R, V
+    }
+    unsafe {
+        TEST_PAGE_TABLE_2.entries[1] = (0x80200 << 10) | 0x1; // 叶子, V
+        TEST_PAGE_TABLE_2.entries[2] = 0; // 无效
+        TEST_PAGE_TABLE_2.entries[3] = (0x7ffff << 10) | 0xf; // RWX, V
+        TEST_PAGE_TABLE_2.entries[4] = (0x80200 << 10) | 0x9; // X, V
+    }
+    let pa = unsafe { &TEST_PAGE_TABLE_0 } as *const _ as usize;
+    let ppn = pa >> 12;
+    ppn
+}
+
+#[derive(Debug)]
+struct NotPageFault;
+
+unsafe fn is_read_page_fault(ptr: *const usize) -> bool {
+    let saved_stvec_address = init_trap_vector();
+    let _ans: usize;
+    asm!("li    tp, 0");
+    asm!("ld    {}, 0({})", out(reg) _ans, in(reg) ptr);
+    let is_page_fault: usize;
+    asm!("mv    {}, tp", out(reg) is_page_fault);
+    let ans = is_page_fault == 1;
+    recover_trap_vector(saved_stvec_address);
+    return ans;
+}
+
+fn init_trap_vector() -> usize {
+    let mut addr = delegate_test_trap as usize;
+    if addr & 0x2 != 0 {
+        addr = addr.wrapping_add(0x2); // 必须对齐到4个字节
+    }
+    let saved_stvec_address = stvec::read().address();
+    unsafe { stvec::write(addr, TrapMode::Direct) };
+    saved_stvec_address
+}
+
+fn recover_trap_vector(saved_stvec_address: usize) {
+    unsafe { stvec::write(saved_stvec_address, TrapMode::Direct) }
+}
+
+extern "C" fn rust_test_trap_handler() {
+    let cause = scause::read().cause();
+    if cause != Trap::Exception(Exception::LoadPageFault) {
+        println!(
+            "!! Test-kernel: Wrong cause associated to page fault, sepc: {:#x}, stval: {:#x}",
+            riscv::register::sepc::read(),
+            riscv::register::stval::read()
+        );
+        sbi::shutdown()
+    }
+    unsafe { asm!("li   tp, 1") }; // tp = 1 说明是缺页异常
+    let bad_ins_addr = sepc::read();
+    let ins_16 = unsafe { core::ptr::read_volatile(bad_ins_addr as *const u16) };
+    let bytes = if ins_16 & 0b11 != 0b11 { 2 } else { 4 };
+    sepc::write(sepc::read().wrapping_add(bytes)); // skip current instruction
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn delegate_test_trap() -> ! {
+    asm!(
+        ".align 4", // align to 4 bytes
+        "addi   sp, sp, -8*16
+        sd      ra, 8*0(sp)
+        sd      t0, 8*1(sp)
+        sd      t1, 8*2(sp)
+        sd      t2, 8*3(sp)
+        sd      t3, 8*4(sp)
+        sd      t4, 8*5(sp)
+        sd      t5, 8*6(sp)
+        sd      t6, 8*7(sp)
+        sd      a0, 8*8(sp)
+        sd      a1, 8*9(sp)
+        sd      a2, 8*10(sp)
+        sd      a3, 8*11(sp)
+        sd      a4, 8*12(sp)
+        sd      a5, 8*13(sp)
+        sd      a6, 8*14(sp)
+        sd      a7, 8*15(sp)",
+        "call   {rust_test_trap_handler}",
+        "ld     ra, 8*0(sp)
+        ld      t0, 8*1(sp)
+        ld      t1, 8*2(sp)
+        ld      t2, 8*3(sp)
+        ld      t3, 8*4(sp)
+        ld      t4, 8*5(sp)
+        ld      t5, 8*6(sp)
+        ld      t6, 8*7(sp)
+        ld      a0, 8*8(sp)
+        ld      a1, 8*9(sp)
+        ld      a2, 8*10(sp)
+        ld      a3, 8*11(sp)
+        ld      a4, 8*12(sp)
+        ld      a5, 8*13(sp)
+        ld      a6, 8*14(sp)
+        ld      a7, 8*15(sp)
+        addi    sp, sp, 8*16",
+        "sret",
+        rust_test_trap_handler = sym rust_test_trap_handler,
+        options(noreturn)
+    )
+}
diff --git a/bootloader/rustsbi-k210/test-kernel/src/feature/delegate_trap.rs b/bootloader/rustsbi-k210/test-kernel/src/feature/delegate_trap.rs
new file mode 100644
index 0000000000000000000000000000000000000000..89e419c086735b118d0fb15c7f4bd264995c0a56
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/src/feature/delegate_trap.rs
@@ -0,0 +1,80 @@
+use crate::{println, sbi};
+use core::arch::asm;
+use riscv::register::{
+    scause::{self, Exception, Trap},
+    sepc,
+    stvec::{self, TrapMode},
+};
+
+pub fn test_delegate_trap() {
+    println!(">> Test-kernel: Trigger illegal exception");
+    let stvec_before = stvec::read().address();
+    init_trap_vector();
+    unsafe { asm!("csrw mcycle, x0") }; // mcycle cannot be written, this is always a 4-byte illegal instruction
+    unsafe { stvec::write(stvec_before, TrapMode::Direct) };
+}
+
+fn init_trap_vector() {
+    let mut addr = delegate_test_trap as usize;
+    if addr & 0x2 != 0 {
+        addr = addr.wrapping_add(0x2); // 必须对齐到4个字节
+    }
+    unsafe { stvec::write(addr, TrapMode::Direct) };
+}
+
+extern "C" fn rust_test_trap_handler() {
+    let cause = scause::read().cause();
+    println!("<< Test-kernel: Value of scause: {:?}", cause);
+    if cause != Trap::Exception(Exception::IllegalInstruction) {
+        println!("!! Test-kernel: Wrong cause associated to illegal instruction");
+        sbi::shutdown()
+    }
+    println!("<< Test-kernel: Illegal exception delegate success");
+    sepc::write(sepc::read().wrapping_add(4)); // skip mcycle write illegal instruction
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn delegate_test_trap() -> ! {
+    asm!(
+        ".align 4", // align to 4 bytes
+        "addi   sp, sp, -8*16
+        sd      ra, 8*0(sp)
+        sd      t0, 8*1(sp)
+        sd      t1, 8*2(sp)
+        sd      t2, 8*3(sp)
+        sd      t3, 8*4(sp)
+        sd      t4, 8*5(sp)
+        sd      t5, 8*6(sp)
+        sd      t6, 8*7(sp)
+        sd      a0, 8*8(sp)
+        sd      a1, 8*9(sp)
+        sd      a2, 8*10(sp)
+        sd      a3, 8*11(sp)
+        sd      a4, 8*12(sp)
+        sd      a5, 8*13(sp)
+        sd      a6, 8*14(sp)
+        sd      a7, 8*15(sp)",
+        "call   {rust_test_trap_handler}",
+        "ld     ra, 8*0(sp)
+        ld      t0, 8*1(sp)
+        ld      t1, 8*2(sp)
+        ld      t2, 8*3(sp)
+        ld      t3, 8*4(sp)
+        ld      t4, 8*5(sp)
+        ld      t5, 8*6(sp)
+        ld      t6, 8*7(sp)
+        ld      a0, 8*8(sp)
+        ld      a1, 8*9(sp)
+        ld      a2, 8*10(sp)
+        ld      a3, 8*11(sp)
+        ld      a4, 8*12(sp)
+        ld      a5, 8*13(sp)
+        ld      a6, 8*14(sp)
+        ld      a7, 8*15(sp)
+        addi    sp, sp, 8*16",
+        "sret",
+        rust_test_trap_handler = sym rust_test_trap_handler,
+        options(noreturn)
+    )
+}
diff --git a/bootloader/rustsbi-k210/test-kernel/src/feature/sfence_vma.rs b/bootloader/rustsbi-k210/test-kernel/src/feature/sfence_vma.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ac99abf87fb6d54d25b19262c7c9f23262269762
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/src/feature/sfence_vma.rs
@@ -0,0 +1,38 @@
+use crate::{println, sbi};
+use riscv::{
+    asm,
+    register::satp::{self, Mode},
+};
+
+#[repr(align(4096))]
+struct PageTable {
+    #[allow(unused)] // Will be used by RISC-V hardware
+    entries: [usize; 512],
+}
+
+static TEST_PAGE_TABLE: PageTable = {
+    let mut entries = [0; 512];
+    entries[2] = (0x80000 << 10) | 0xcf; // 0x8000_0000 -> 0x8000_0000,0xcf 表示 VRWXAD 均为 1
+    entries[508] = (0x00000 << 10) | 0xcf; // 0xffff_ffff_0000_0000 -> 0x0000_0000,0xcf 表示 VRWXAD 均为 1
+    entries[510] = (0x80000 << 10) | 0xcf; // 0xffff_ffff_8000_0000 -> 0x8000_0000,0xcf 表示 VRWXAD 均为 1
+    PageTable { entries }
+};
+
+static VARIABLE: usize = 0x6666233399998888;
+
+pub fn test_sfence_vma() {
+    println!(">> Test-kernel: Testing emulated virtual memory unit");
+    let pa = &TEST_PAGE_TABLE as *const _ as usize;
+    let ppn = pa >> 12;
+    unsafe { satp::set(Mode::Sv39, 0, ppn) };
+    unsafe { asm::sfence_vma_all() }; // SBI will emulate this instruction
+    println!("<< Test-kernel: Code memory page test success");
+    let ptr = &VARIABLE as *const _ as usize;
+    let mapped_ptr = ptr + 0xffff_ffff_0000_0000;
+    let mapped_variable = unsafe { *(mapped_ptr as *const usize) };
+    if mapped_variable != VARIABLE {
+        println!("!! Test-kernel: Multi mapping page test failed: variable value don't match");
+        sbi::shutdown()
+    }
+    println!("<< Test-kernel: Multi mapping page test success");
+}
diff --git a/bootloader/rustsbi-k210/test-kernel/src/linker.ld b/bootloader/rustsbi-k210/test-kernel/src/linker.ld
new file mode 100644
index 0000000000000000000000000000000000000000..3caee374b1d59c61ba3ae1da385545e5b9c87d61
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/src/linker.ld
@@ -0,0 +1,39 @@
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x80020000;
+
+SECTIONS
+{
+    . = BASE_ADDRESS;
+    start = .;
+
+    .text : ALIGN(4K) {
+        stext = .;
+        *(.text.entry)
+        *(.text .text.*)
+        etext = .;
+    }
+
+    .rodata : ALIGN(4K) {
+        srodata = .;
+        *(.rodata .rodata.*)
+        erodata = .;
+    }
+
+    .data : ALIGN(4K) {
+        sidata = LOADADDR(.data);
+        sdata = .;
+        *(.data .data.*)
+        edata = .;
+    }
+
+    .bss (NOLOAD) : ALIGN(4K)  {
+        *(.bss.uninit)
+        sbss = .;
+        *(.sbss .bss .bss.*)
+        ebss = .;
+    }
+
+    PROVIDE(end = .);
+}
diff --git a/bootloader/rustsbi-k210/test-kernel/src/main.rs b/bootloader/rustsbi-k210/test-kernel/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f656e9699f808132fd160760ca68895f46412218
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/src/main.rs
@@ -0,0 +1,105 @@
+#![feature(naked_functions)]
+#![feature(asm_sym, asm_const)]
+#![feature(generator_trait)]
+#![feature(default_alloc_error_handler)]
+#![feature(stdsimd)]
+#![no_std]
+#![no_main]
+
+use core::arch::asm;
+
+mod console;
+mod feature;
+mod sbi;
+
+const PER_HART_STACK_SIZE: usize = 64 * 1024; // 64KiB
+const KERNEL_STACK_SIZE: usize = 2 * PER_HART_STACK_SIZE;
+#[link_section = ".bss.uninit"]
+static mut KERNEL_STACK: [u8; KERNEL_STACK_SIZE] = [0; KERNEL_STACK_SIZE];
+
+const KERNEL_HEAP_SIZE: usize = 128 * 1024; // 128KiB
+#[link_section = ".bss.uninit"]
+static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE];
+#[global_allocator]
+static KERNEL_HEAP: LockedHeap<32> = LockedHeap::empty();
+
+use buddy_system_allocator::LockedHeap;
+
+extern "C" fn rust_main(hartid: usize, opaque: usize) -> ! {
+    if hartid == 0 {
+        init_bss();
+        init_heap();
+    }
+    println!(
+        "<< Test-kernel: Hart id = {}, opaque = {:#x}",
+        hartid, opaque
+    );
+    feature::test_base_extension();
+    feature::test_delegate_trap();
+    feature::test_sfence_vma();
+    test_emulate_rdtime();
+    feature::test_catch_page_fault();
+    println!("<< Test-kernel: SBI test SUCCESS, shutdown");
+    sbi::shutdown()
+}
+
+pub fn test_emulate_rdtime() {
+    println!(">> Test-kernel: Testing SBI instruction emulation");
+    let time = riscv::register::time::read64();
+    println!("<< Test-kernel: Current time: {:x}", time);
+}
+
+fn init_bss() {
+    extern "C" {
+        static mut ebss: u32;
+        static mut sbss: u32;
+        static mut edata: u32;
+        static mut sdata: u32;
+        static sidata: u32;
+    }
+    unsafe {
+        r0::zero_bss(&mut sbss, &mut ebss);
+        r0::init_data(&mut sdata, &mut edata, &sidata);
+    }
+}
+
+fn init_heap() {
+    unsafe {
+        KERNEL_HEAP
+            .lock()
+            .init(HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE)
+    }
+}
+
+use core::panic::PanicInfo;
+
+#[cfg_attr(not(test), panic_handler)]
+#[allow(unused)]
+fn panic(info: &PanicInfo) -> ! {
+    println!("!! Test-kernel: {}", info);
+    println!("!! Test-kernel: SBI test FAILED due to panic");
+    sbi::shutdown()
+}
+
+#[naked]
+#[link_section = ".text.entry"]
+#[export_name = "_start"]
+unsafe extern "C" fn entry() -> ! {
+    asm!(
+    // 1. set sp
+    // sp = bootstack + (hartid + 1) * HART_STACK_SIZE
+    "
+    la      sp, {stack}
+    li      t0, {per_hart_stack_size}
+    addi    t1, a0, 1
+1:  add     sp, sp, t0
+    addi    t1, t1, -1
+    bnez    t1, 1b
+    ",
+    // 2. jump to rust_main (absolute address)
+    "j      {rust_main}", 
+    per_hart_stack_size = const PER_HART_STACK_SIZE,
+    stack = sym KERNEL_STACK,
+    rust_main = sym rust_main,
+    options(noreturn))
+}
diff --git a/bootloader/rustsbi-k210/test-kernel/src/sbi.rs b/bootloader/rustsbi-k210/test-kernel/src/sbi.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7579e749dad304256f89074d0a3ce20adffb9960
--- /dev/null
+++ b/bootloader/rustsbi-k210/test-kernel/src/sbi.rs
@@ -0,0 +1,138 @@
+#![allow(unused)]
+use core::arch::asm;
+
+pub const EXTENSION_BASE: usize = 0x10;
+pub const EXTENSION_TIMER: usize = 0x54494D45;
+pub const EXTENSION_IPI: usize = 0x735049;
+pub const EXTENSION_RFENCE: usize = 0x52464E43;
+pub const EXTENSION_HSM: usize = 0x48534D;
+pub const EXTENSION_SRST: usize = 0x53525354;
+
+const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0;
+const FUNCTION_BASE_GET_SBI_IMPL_ID: usize = 0x1;
+const FUNCTION_BASE_GET_SBI_IMPL_VERSION: usize = 0x2;
+const FUNCTION_BASE_PROBE_EXTENSION: usize = 0x3;
+const FUNCTION_BASE_GET_MVENDORID: usize = 0x4;
+const FUNCTION_BASE_GET_MARCHID: usize = 0x5;
+const FUNCTION_BASE_GET_MIMPID: usize = 0x6;
+
+#[repr(C)]
+pub struct SbiRet {
+    /// Error number
+    pub error: usize,
+    /// Result value
+    pub value: usize,
+}
+
+#[inline(always)]
+fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize, arg2: usize) -> SbiRet {
+    let (error, value);
+    match () {
+        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
+        () => unsafe {
+            asm!(
+                "ecall",
+                in("a0") arg0, in("a1") arg1, in("a2") arg2,
+                in("a6") function, in("a7") extension,
+                lateout("a0") error, lateout("a1") value,
+            )
+        },
+        #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
+        () => {
+            drop((extension, function, arg0, arg1, arg2));
+            unimplemented!("not RISC-V instruction set architecture")
+        }
+    };
+    SbiRet { error, value }
+}
+
+#[inline]
+pub fn get_spec_version() -> usize {
+    sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0, 0).value
+}
+
+#[inline]
+pub fn get_sbi_impl_id() -> usize {
+    sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_ID, 0, 0, 0).value
+}
+
+#[inline]
+pub fn get_sbi_impl_version() -> usize {
+    sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_VERSION, 0, 0, 0).value
+}
+
+#[inline]
+pub fn probe_extension(extension_id: usize) -> usize {
+    sbi_call(
+        EXTENSION_BASE,
+        FUNCTION_BASE_PROBE_EXTENSION,
+        extension_id,
+        0,
+        0,
+    )
+    .value
+}
+
+#[inline]
+pub fn get_mvendorid() -> usize {
+    sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_MVENDORID, 0, 0, 0).value
+}
+
+#[inline]
+pub fn get_marchid() -> usize {
+    sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_MARCHID, 0, 0, 0).value
+}
+
+#[inline]
+pub fn get_mimpid() -> usize {
+    sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_MIMPID, 0, 0, 0).value
+}
+
+#[inline(always)]
+fn sbi_call_legacy(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
+    let ret;
+    match () {
+        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
+        () => unsafe {
+            asm!(
+                "ecall",
+                in("a0") arg0, in("a1") arg1, in("a2") arg2,
+                in("a7") which,
+                lateout("a0") ret,
+            )
+        },
+        #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
+        () => {
+            drop((which, arg0, arg1, arg2));
+            unimplemented!("not RISC-V instruction set architecture")
+        }
+    };
+    ret
+}
+
+const SBI_SET_TIMER: usize = 0;
+const SBI_CONSOLE_PUTCHAR: usize = 1;
+const SBI_CONSOLE_GETCHAR: usize = 2;
+const SBI_CLEAR_IPI: usize = 3;
+const SBI_SEND_IPI: usize = 4;
+const SBI_REMOTE_FENCE_I: usize = 5;
+const SBI_REMOTE_SFENCE_VMA: usize = 6;
+const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
+const SBI_SHUTDOWN: usize = 8;
+
+pub fn console_putchar(c: usize) {
+    sbi_call_legacy(SBI_CONSOLE_PUTCHAR, c, 0, 0);
+}
+
+pub fn console_getchar() -> usize {
+    sbi_call_legacy(SBI_CONSOLE_GETCHAR, 0, 0, 0)
+}
+
+pub fn shutdown() -> ! {
+    sbi_call_legacy(SBI_SHUTDOWN, 0, 0, 0);
+    unreachable!()
+}
+
+pub fn set_timer(time: usize) {
+    sbi_call_legacy(SBI_SET_TIMER, time, 0, 0);
+}
diff --git a/bootloader/rustsbi-k210/xtask/Cargo.toml b/bootloader/rustsbi-k210/xtask/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..ae290a4c7e7aa0aef1cf4cb263efe5e02778100c
--- /dev/null
+++ b/bootloader/rustsbi-k210/xtask/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "xtask"
+version = "0.1.0"
+authors = ["Luo Jia <me@luojia.cc>"]
+description = "interactive cargo runner"
+edition = "2018"
+publish = false
+
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = "2"
+serialport = "4"
diff --git a/bootloader/rustsbi-k210/xtask/src/detect.rs b/bootloader/rustsbi-k210/xtask/src/detect.rs
new file mode 100644
index 0000000000000000000000000000000000000000..cc2b733ebe45ad63b5380b39faaa5219143d6fb9
--- /dev/null
+++ b/bootloader/rustsbi-k210/xtask/src/detect.rs
@@ -0,0 +1,94 @@
+use std::{
+    fs,
+    io::{self, Write},
+    path::{Path, PathBuf},
+};
+
+use serialport::{SerialPortType, UsbPortInfo};
+
+pub fn detect_serial_ports() -> Option<(String, UsbPortInfo)> {
+    let ports = serialport::available_ports().expect("list available ports");
+    let mut ans = Vec::new();
+    for p in ports {
+        if let SerialPortType::UsbPort(info) = p.port_type {
+            if info.vid == 0x1a86 && info.pid == 0x7523 {
+                ans.push((p.port_name, info));
+            }
+        }
+    }
+    if ans.len() == 0 {
+        return None;
+    } else if ans.len() == 1 {
+        return Some(ans[0].clone());
+    } else {
+        let mut input = String::new();
+        print!("Multiple ports detected.");
+        for (port_name, info) in ans.iter() {
+            dump_port(port_name, info);
+        }
+        let (port_name, info) = 'outer: loop {
+            println!("Please select one port: ");
+            io::stdin().read_line(&mut input).expect("read line");
+            for (port_name, info) in ans.iter() {
+                if input.eq_ignore_ascii_case(port_name) {
+                    break 'outer (port_name, info);
+                }
+            }
+            println!(
+                "Input '{}' does not match to any ports! Please input again.",
+                input
+            );
+        };
+        return Some((port_name.clone(), info.clone()));
+    }
+}
+
+pub fn dump_port(port_name: &str, info: &UsbPortInfo) {
+    print!(
+        "Port {}: vid: {:x}, pid: {:x}",
+        port_name, info.vid, info.pid
+    );
+    if let Some(serial_number) = &info.serial_number {
+        print!(", serial number: {}", serial_number)
+    }
+    if let Some(manufacturer) = &info.manufacturer {
+        print!(", manufacturer: {}", manufacturer)
+    }
+    if let Some(product) = &info.product {
+        print!(", product: {}", product)
+    }
+    println!()
+}
+
+pub fn save_to_file(port_name: &str) {
+    fs::create_dir_all(project_root().join("target").join("xtask")).expect("create folder");
+    let mut file = fs::OpenOptions::new()
+        .read(true)
+        .write(true)
+        .create(true)
+        .open(
+            project_root()
+                .join("target")
+                .join("xtask")
+                .join("serial-port.txt"),
+        )
+        .expect("create and open file");
+    file.write(port_name.as_bytes()).expect("write file");
+}
+
+pub fn read_serial_port_choose_file() -> io::Result<String> {
+    fs::read_to_string(
+        project_root()
+            .join("target")
+            .join("xtask")
+            .join("serial-port.txt"),
+    )
+}
+
+fn project_root() -> PathBuf {
+    Path::new(&env!("CARGO_MANIFEST_DIR"))
+        .ancestors()
+        .nth(1)
+        .unwrap()
+        .to_path_buf()
+}
diff --git a/bootloader/rustsbi-k210/xtask/src/main.rs b/bootloader/rustsbi-k210/xtask/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7426db70b15b5f59aa825766eb901ab9f66aa06e
--- /dev/null
+++ b/bootloader/rustsbi-k210/xtask/src/main.rs
@@ -0,0 +1,227 @@
+mod detect;
+mod test;
+
+use clap::{clap_app, crate_authors, crate_description, crate_version};
+use std::{
+    env, fs,
+    io::{Seek, SeekFrom, Write},
+    path::{Path, PathBuf},
+    process::{self, Command},
+};
+
+#[derive(Debug)]
+struct XtaskEnv {
+    compile_mode: CompileMode,
+}
+
+#[derive(Debug)]
+enum CompileMode {
+    Debug,
+    Release,
+}
+
+const DEFAULT_TARGET: &'static str = "riscv64imac-unknown-none-elf";
+
+fn main() {
+    let matches = clap_app!(xtask =>
+        (version: crate_version!())
+        (author: crate_authors!())
+        (about: crate_description!())
+        (@subcommand make =>
+            (about: "Build project")
+            (@arg release: --release "Build artifacts in release mode, with optimizations")
+        )
+        (@subcommand k210 =>
+            (about: "Run project on actual board")
+            (@arg release: --release "Build artifacts in release mode, with optimizations")
+        )
+        (@subcommand detect =>
+            (about: "Detect target serial port")
+        )
+        (@subcommand asm =>
+            (about: "View asm code for project")
+        )
+        (@subcommand size =>
+            (about: "View size for project")
+        )
+    )
+    .get_matches();
+    let mut xtask_env = XtaskEnv {
+        compile_mode: CompileMode::Debug,
+    };
+    // Read: python xtask/ktool.py -p COM11 -a 0x80000000 -R -L 0x20000 ./target/xtask/flash_dump.bin
+    if let Some(matches) = matches.subcommand_matches("k210") {
+        let port = match detect::read_serial_port_choose_file() {
+            Ok(string) => {
+                println!("xtask: using previously selected serial port {}.", string);
+                string
+            }
+            Err(_e) => detect_save_port_or_exit(),
+        };
+        if matches.is_present("release") {
+            xtask_env.compile_mode = CompileMode::Release;
+        }
+        println!("xtask: mode: {:?}", xtask_env.compile_mode);
+        // println!("Run k210 on {}", port);
+        xtask_build_sbi(&xtask_env);
+        xtask_binary_sbi(&xtask_env);
+        xtask_build_test_kernel(&xtask_env);
+        xtask_binary_test_kernel(&xtask_env);
+        xtask_fuse_binary(&xtask_env);
+        xtask_run_k210(&xtask_env, &port);
+    } else if let Some(matches) = matches.subcommand_matches("make") {
+        if matches.is_present("release") {
+            xtask_env.compile_mode = CompileMode::Release;
+        }
+        println!("xtask: mode: {:?}", xtask_env.compile_mode);
+        xtask_build_sbi(&xtask_env);
+        xtask_binary_sbi(&xtask_env);
+    } else if let Some(_matches) = matches.subcommand_matches("detect") {
+        let ans = detect::detect_serial_ports();
+        if let Some((port_name, info)) = ans {
+            detect::dump_port(&port_name, &info);
+            detect::save_to_file(&port_name);
+        } else {
+            println!("xtask: no CH340 serial port found.");
+        }
+    } else {
+        println!("Use `cargo k210` to run, `cargo xtask --help` for help")
+    }
+}
+
+fn detect_save_port_or_exit() -> String {
+    if let Some((port_name, info)) = detect::detect_serial_ports() {
+        println!("xtask: port detected");
+        detect::dump_port(&port_name, &info);
+        detect::save_to_file(&port_name);
+        port_name
+    } else {
+        println!("xtask: no serial port found; program exit");
+        std::process::exit(1);
+    }
+}
+
+// @python ./ktool.py --port {{k210-serialport}} -b 1500000 --terminal {{fused-bin}}
+fn xtask_run_k210(xtask_env: &XtaskEnv, port: &str) {
+    let status = Command::new("python3")
+        .current_dir(project_root().join("xtask"))
+        .arg("ktool.py")
+        .args(&["--port", port])
+        .args(&["--baudrate", "1500000"]) // todo: configurate baudrate
+        .arg("--terminal")
+        .arg(dist_dir(xtask_env).join("k210-fused.bin"))
+        .status()
+        .unwrap();
+    if !status.success() {
+        panic!("run k210 failed")
+    }
+}
+
+fn xtask_build_sbi(xtask_env: &XtaskEnv) {
+    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
+    let mut command = Command::new(cargo);
+    command.current_dir(project_root().join("rustsbi-k210"));
+    command.arg("build");
+    match xtask_env.compile_mode {
+        CompileMode::Debug => {}
+        CompileMode::Release => {
+            command.arg("--release");
+        }
+    }
+    command.args(&["--package", "rustsbi-k210"]);
+    command.args(&["--target", DEFAULT_TARGET]);
+    let status = command.status().unwrap();
+    if !status.success() {
+        println!("cargo build failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_binary_sbi(xtask_env: &XtaskEnv) {
+    let objcopy = "rust-objcopy";
+    let status = Command::new(objcopy)
+        .current_dir(dist_dir(xtask_env))
+        .arg("rustsbi-k210")
+        .arg("--binary-architecture=riscv64")
+        .arg("--strip-all")
+        .args(&["-O", "binary", "rustsbi-k210.bin"])
+        .status()
+        .unwrap();
+
+    if !status.success() {
+        println!("objcopy binary failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_build_test_kernel(xtask_env: &XtaskEnv) {
+    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
+    let mut command = Command::new(cargo);
+    command.current_dir(project_root().join("test-kernel"));
+    command.arg("build");
+    match xtask_env.compile_mode {
+        CompileMode::Debug => {}
+        CompileMode::Release => {
+            command.arg("--release");
+        }
+    }
+    command.args(&["--package", "test-kernel"]);
+    command.args(&["--target", DEFAULT_TARGET]);
+    let status = command.status().unwrap();
+    if !status.success() {
+        println!("cargo build failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_binary_test_kernel(xtask_env: &XtaskEnv) {
+    let objcopy = "rust-objcopy";
+    let status = Command::new(objcopy)
+        .current_dir(dist_dir(xtask_env))
+        .arg("test-kernel")
+        .arg("--binary-architecture=riscv64")
+        .arg("--strip-all")
+        .args(&["-O", "binary", "test-kernel.bin"])
+        .status()
+        .unwrap();
+
+    if !status.success() {
+        println!("objcopy binary failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_fuse_binary(xtask_env: &XtaskEnv) {
+    let sbi_binary_path = dist_dir(xtask_env).join("rustsbi-k210.bin");
+    let test_kernel_binary_path = dist_dir(xtask_env).join("test-kernel.bin");
+    let output_path = dist_dir(xtask_env).join("k210-fused.bin");
+    let offset = 0x20000;
+    fs::copy(sbi_binary_path, &output_path).expect("copy sbi base");
+    let mut output = fs::OpenOptions::new()
+        .read(true)
+        .write(true)
+        .open(output_path)
+        .expect("open output file");
+    let buf = fs::read(test_kernel_binary_path).expect("read kernel binary");
+    output
+        .seek(SeekFrom::Start(offset))
+        .expect("seek to offset");
+    output.write(&buf).expect("write output");
+}
+
+fn dist_dir(xtask_env: &XtaskEnv) -> PathBuf {
+    let mut path_buf = project_root().join("target").join(DEFAULT_TARGET);
+    path_buf = match xtask_env.compile_mode {
+        CompileMode::Debug => path_buf.join("debug"),
+        CompileMode::Release => path_buf.join("release"),
+    };
+    path_buf
+}
+
+fn project_root() -> PathBuf {
+    Path::new(&env!("CARGO_MANIFEST_DIR"))
+        .ancestors()
+        .nth(1)
+        .unwrap()
+        .to_path_buf()
+}
diff --git a/bootloader/rustsbi-k210/xtask/src/test.rs b/bootloader/rustsbi-k210/xtask/src/test.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d7414aa37a4a5fe6dfff407b59fa5a373b2fef0b
--- /dev/null
+++ b/bootloader/rustsbi-k210/xtask/src/test.rs
@@ -0,0 +1,4 @@
+#[test]
+fn run_test_kernel() {
+    eprintln!("Test!");
+}
diff --git a/bootloader/rustsbi-qemu b/bootloader/rustsbi-qemu
deleted file mode 160000
index 999e3556fcfa1b0900dd797ae2186667af8d2dc6..0000000000000000000000000000000000000000
--- a/bootloader/rustsbi-qemu
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 999e3556fcfa1b0900dd797ae2186667af8d2dc6
diff --git a/bootloader/rustsbi-qemu/.cargo/config.toml b/bootloader/rustsbi-qemu/.cargo/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..57f591795640c67b3456799803f06f6558e6d7fe
--- /dev/null
+++ b/bootloader/rustsbi-qemu/.cargo/config.toml
@@ -0,0 +1,8 @@
+[alias]
+xtask = "run --package xtask --"
+make = "xtask make"
+qemu = "xtask qemu"
+asm = "xtask asm"
+size = "xtask size"
+debug = "xtask debug"
+gdb = "xtask gdb"
diff --git a/bootloader/rustsbi-qemu/.gitignore b/bootloader/rustsbi-qemu/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ea8c4bf7f35f6f77f75d92ad8ce8349f6e81ddba
--- /dev/null
+++ b/bootloader/rustsbi-qemu/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/bootloader/rustsbi-qemu/.vscode/settings.json b/bootloader/rustsbi-qemu/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..b58d9613ad4301cb93ac352c66872d0913055d7a
--- /dev/null
+++ b/bootloader/rustsbi-qemu/.vscode/settings.json
@@ -0,0 +1,10 @@
+{   
+    // Prevent "can't find crate for `test`" error on no_std
+    // Ref: https://github.com/rust-lang/vscode-rust/issues/729
+    // For vscode-rust plugin users:
+    "rust.target": "riscv64imac-unknown-none-elf",
+    "rust.all_targets": false,
+    // For Rust Analyzer plugin users:
+    "rust-analyzer.cargo.target": "riscv64imac-unknown-none-elf",
+    "rust-analyzer.checkOnSave.allTargets": false
+}
diff --git a/bootloader/rustsbi-qemu/CHANGELOG.md b/bootloader/rustsbi-qemu/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..daba95f6439ffe2054307c791ba935797ebc9707
--- /dev/null
+++ b/bootloader/rustsbi-qemu/CHANGELOG.md
@@ -0,0 +1,28 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
+to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+### Added
+
+- Handle possible failure of deref virtual address by machine trap detection
+
+### Modified
+
+## [0.1.0] - 2022-02-13
+
+### Added
+
+- Adapts to RustSBI version 0.2.0
+- Implement SBI non-retentive resume procedure
+- PMP updates, use stabilized core::arch::asm! macro, thanks to @wyfcyx
+- Fixes on usage of CLINT peripheral, thanks to @duskmoon314
+- Numerous fixes to HSM module implementation, more documents
+
+[Unreleased]: https://github.com/rustsbi/rustsbi-qemu/compare/v0.1.0...HEAD
+
+[0.1.0]: https://github.com/rustsbi/rustsbi-qemu/releases/tag/v0.1.0
diff --git a/bootloader/rustsbi-qemu/Cargo.lock b/bootloader/rustsbi-qemu/Cargo.lock
new file mode 100644
index 0000000000000000000000000000000000000000..6baa1544d31f3da3a5eda9b99c6a40bd6a77e182
--- /dev/null
+++ b/bootloader/rustsbi-qemu/Cargo.lock
@@ -0,0 +1,463 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ahash"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bare-metal"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
+dependencies = [
+ "rustc_version",
+]
+
+[[package]]
+name = "bare-metal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
+
+[[package]]
+name = "bit_field"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
+name = "buddy_system_allocator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55703ac5f02c246ce6158eff6ae2dd9e9069917969682b6831f8a5123abb8a48"
+dependencies = [
+ "spin 0.7.1",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "2.33.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
+[[package]]
+name = "ctrlc"
+version = "3.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf"
+dependencies = [
+ "nix",
+ "winapi",
+]
+
+[[package]]
+name = "device_tree"
+version = "1.0.3"
+source = "git+https://github.com/rcore-os/device_tree-rs/#b89dffc9bb2d0ee1269999c5004e66d5e85772c2"
+
+[[package]]
+name = "embedded-hal"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e36cfb62ff156596c892272f3015ef952fe1525e85261fa3a7f327bd6b384ab9"
+dependencies = [
+ "nb 0.1.3",
+ "void",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+dependencies = [
+ "spin 0.5.2",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
+
+[[package]]
+name = "lock_api"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "memchr"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "nb"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
+dependencies = [
+ "nb 1.0.0",
+]
+
+[[package]]
+name = "nb"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
+
+[[package]]
+name = "nix"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
+
+[[package]]
+name = "regex"
+version = "1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "riscv"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2f0b705d428e9d0f78e2bb73093887ee58a83c9688de3faedbb4c0631c4618e"
+dependencies = [
+ "bare-metal 0.2.5",
+ "bit_field",
+ "riscv-target",
+]
+
+[[package]]
+name = "riscv"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba"
+dependencies = [
+ "bare-metal 1.0.0",
+ "bit_field",
+ "riscv-target",
+]
+
+[[package]]
+name = "riscv"
+version = "0.7.0"
+source = "git+https://github.com/rust-embedded/riscv?rev=dc0bc37e#dc0bc37e760ae6cec247f54c4e69c5d3789cedd8"
+dependencies = [
+ "bare-metal 1.0.0",
+ "bit_field",
+ "embedded-hal",
+ "riscv-target",
+]
+
+[[package]]
+name = "riscv-target"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222"
+dependencies = [
+ "lazy_static",
+ "regex",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustsbi"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "056a8dda9be891af799d65ea3d56f397c08384e36b34912f6dc094a21c32b028"
+dependencies = [
+ "embedded-hal",
+ "nb 1.0.0",
+ "riscv 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustsbi-qemu"
+version = "0.1.0"
+dependencies = [
+ "bit_field",
+ "bitflags",
+ "buddy_system_allocator",
+ "device_tree",
+ "embedded-hal",
+ "hashbrown",
+ "lazy_static",
+ "nb 1.0.0",
+ "riscv 0.7.0 (git+https://github.com/rust-embedded/riscv?rev=dc0bc37e)",
+ "rustsbi",
+ "spin 0.9.0",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "spin"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162"
+
+[[package]]
+name = "spin"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b87bbf98cb81332a56c1ee8929845836f85e8ddd693157c30d76660196014478"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
+[[package]]
+name = "test-kernel"
+version = "0.1.0"
+dependencies = [
+ "buddy_system_allocator",
+ "lazy_static",
+ "riscv 0.6.0",
+ "spin 0.7.1",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "version_check"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "xtask"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "ctrlc",
+]
diff --git a/bootloader/rustsbi-qemu/Cargo.toml b/bootloader/rustsbi-qemu/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..a8cc84a2ca4bf2c161cd1502d6a300b56faad7b1
--- /dev/null
+++ b/bootloader/rustsbi-qemu/Cargo.toml
@@ -0,0 +1,7 @@
+[workspace]
+members = [
+    "rustsbi-qemu",
+    "test-kernel",
+    "xtask"
+]
+default-members = ["xtask"]
diff --git a/bootloader/rustsbi-qemu/LICENSE b/bootloader/rustsbi-qemu/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..a4fa5bfedc273b7c751ae38860fcd03d3a733e98
--- /dev/null
+++ b/bootloader/rustsbi-qemu/LICENSE
@@ -0,0 +1,43 @@
+木兰宽松许可证, 第2版
+
+2020年1月 http://license.coscl.org.cn/MulanPSL2
+
+您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
+
+0.   定义
+
+“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
+
+“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
+
+“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
+
+“法人实体” 是指提交贡献的机构及其“关联实体”。
+
+“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
+
+1.   授予版权许可
+
+每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
+
+2.   授予专利许可
+
+每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
+
+3.   无商标许可
+
+“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
+
+4.   分发限制
+
+您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
+
+5.   免责声明与责任限制
+
+“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
+
+6.   语言
+
+“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
+
+条款结束
diff --git a/bootloader/rustsbi-qemu/README.md b/bootloader/rustsbi-qemu/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..f3f8d6e9dac718e34cd10ebe930a44b948359daa
--- /dev/null
+++ b/bootloader/rustsbi-qemu/README.md
@@ -0,0 +1,124 @@
+# QEMU support from RustSBI
+
+RustSBI is designed as a library to craft a bootable binary or ELF file. However, QEMU provides us a way to load ELF
+file and implement simple SBI directly, thus RustSBI provides a bootable ELF file for this platform.
+
+## Try it out!
+
+Compile and run with:
+
+```shell
+cargo qemu
+```
+
+When running `cargo qemu`, the test kernel will build and run. Expected output should be:
+
+```
+[rustsbi] RustSBI version 0.2.0, adapting to RISC-V SBI v0.3
+.______       __    __      _______.___________.  _______..______   __
+|   _  \     |  |  |  |    /       |           | /       ||   _  \ |  |
+|  |_)  |    |  |  |  |   |   (----`---|  |----`|   (----`|  |_)  ||  |
+|      /     |  |  |  |    \   \       |  |      \   \    |   _  < |  |
+|  |\  \----.|  `--'  |.----)   |      |  |  .----)   |   |  |_)  ||  |
+| _| `._____| \______/ |_______/       |__|  |_______/    |______/ |__|
+
+[rustsbi] Implementation: RustSBI-QEMU Version 0.1.0
+[rustsbi-dtb] Hart count: cluster0 with 8 cores
+[rustsbi] misa: RV64ACDFIMSU
+[rustsbi] mideleg: ssoft, stimer, sext (0x222)
+[rustsbi] medeleg: ima, ia, bkpt, la, sa, uecall, ipage, lpage, spage (0xb1ab)
+[rustsbi] pmp0: 0x10000000 ..= 0x10001fff (rw-)
+[rustsbi] pmp1: 0x2000000 ..= 0x200ffff (rw-)
+[rustsbi] pmp2: 0xc000000 ..= 0xc3fffff (rw-)
+[rustsbi] pmp3: 0x80000000 ..= 0x8fffffff (rwx)
+[rustsbi] enter supervisor 0x80200000
+<< Test-kernel: Hart id = 0, DTB physical address = 0x87000000
+>> Test-kernel: Testing base extension
+<< Test-kernel: Base extension version: 1
+<< Test-kernel: SBI specification version: 3
+<< Test-kernel: SBI implementation Id: 4
+<< Test-kernel: SBI implementation version: 200
+<< Test-kernel: Device mvendorid: 0
+<< Test-kernel: Device marchid: 0
+<< Test-kernel: Device mimpid: 0
+>> Test-kernel: Testing SBI instruction emulation
+<< Test-kernel: Current time: 17fc45
+<< Test-kernel: Time after operation: 187678
+>> Test-kernel: Trigger illegal exception
+<< Test-kernel: Value of scause: Exception(IllegalInstruction)
+<< Test-kernel: Illegal exception delegate success
+>> Stop hart 3, return value 0
+>> Hart 0 state return value: 0
+>> Hart 1 state return value: 4
+>> Hart 2 state return value: 4
+>> Hart 3 state return value: 1
+<< Test-kernel: test for hart 0 success, wake another hart
+>> Wake hart 1, sbi return value 0
+>> Start test for hart 1, retentive suspend return value 0
+>> Wake hart 2, sbi return value 0
+<< The parameter passed to hart 2 resume is: 0x4567890a
+>> Start hart 3 with parameter 0x12345678
+>> SBI return value: 0
+<< The parameter passed to hart 3 start is: 0x12345678
+<< Test-kernel: All hart SBI test SUCCESS, shutdown
+```
+
+## Run test kernel
+
+### Requirements
+
+You should have `cargo-binutils` and `llvm-tools-preview` installed.
+
+```
+cargo install cargo-binutils
+rustup component add llvm-tools-preview
+```
+
+### Run
+
+Run with:
+
+```shell
+cargo test
+```
+
+It will run RustSBI-QEMU with a test kernel. The test kernel will test all SBI functions, 
+its command emulation and other features. If it succeeds, there would be output like:
+
+```
+running 1 test
+    Finished dev [unoptimized + debuginfo] target(s) in 0.14s
+   Compiling test-kernel v0.1.0 (D:\RustProjects\rustsbi-qemu\test-kernel)
+    Finished dev [unoptimized + debuginfo] target(s) in 0.61s
+test run_test_kernel ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.31s
+```
+
+## Notes
+
+1. How to enable hypervisor H extension on QEMU?
+
+You should use these following line of parameters:
+
+```rust
+    command.args(&["-cpu", "rv64,x-h=true"]);
+```
+
+... to enable H extension on QEMU software.
+
+## License 
+
+This project is licensed under Mulan PSL v2.
+
+```text
+Copyright (c) 2021-2022 RustSBI Team
+RustSBI-QEMU 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.
+```
diff --git a/bootloader/rustsbi-qemu/rust-toolchain.toml b/bootloader/rustsbi-qemu/rust-toolchain.toml
new file mode 100644
index 0000000000000000000000000000000000000000..5d56faf9ae08cb604e06df9aa6281e2b51ce5809
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "nightly"
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/.cargo/config.toml b/bootloader/rustsbi-qemu/rustsbi-qemu/.cargo/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..86e09f1652b0a244cccbc2f2f23e6d3adc64f4e9
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/.cargo/config.toml
@@ -0,0 +1,2 @@
+[build]
+target = "riscv64imac-unknown-none-elf"
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/Cargo.toml b/bootloader/rustsbi-qemu/rustsbi-qemu/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..85493d6e127cc8142d9b2e4f7b51117ff78a791b
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "rustsbi-qemu"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rustsbi = "0.2.1"
+buddy_system_allocator = "0.8"
+lazy_static = { version = "1", features = ["spin_no_std"] }
+spin = "0.9"
+riscv = { git = "https://github.com/rust-embedded/riscv", rev = "dc0bc37e", features = ["inline-asm"] }
+device_tree = { git = "https://github.com/rcore-os/device_tree-rs/" }
+embedded-hal = "0.2.6"
+nb = "1"
+bitflags = "1"
+bit_field = "0.10"
+hashbrown = "0.11"
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/build.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9575c5462ef03dac6c5619f5e06145b182eec19d
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/build.rs
@@ -0,0 +1,4 @@
+fn main() {
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rustc-link-arg=-Trustsbi-qemu/src/linker64.ld");
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/clint.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/clint.rs
new file mode 100644
index 0000000000000000000000000000000000000000..969b257769b83a730407f98d19a62f1d33df17b4
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/clint.rs
@@ -0,0 +1,72 @@
+#![allow(dead_code)]
+
+use rustsbi::{HartMask, Ipi, Timer};
+// 这部分其实是运行时提供的,不应该做到实现库里面
+use rustsbi::SbiRet;
+
+pub struct Clint {
+    base: usize,
+}
+
+impl Clint {
+    #[inline]
+    pub fn new(base: *mut u8) -> Clint {
+        Clint {
+            base: base as usize,
+        }
+    }
+
+    #[inline]
+    pub fn get_mtime(&self) -> u64 {
+        unsafe {
+            let base = self.base as *mut u8;
+            core::ptr::read_volatile(base.add(0xbff8) as *mut u64)
+        }
+    }
+
+    #[inline]
+    pub fn set_timer(&self, hart_id: usize, instant: u64) {
+        unsafe {
+            let base = self.base as *mut u8;
+            core::ptr::write_volatile((base.offset(0x4000) as *mut u64).add(hart_id), instant);
+        }
+    }
+
+    #[inline]
+    pub fn send_soft(&self, hart_id: usize) {
+        unsafe {
+            let base = self.base as *mut u8;
+            core::ptr::write_volatile((base as *mut u32).add(hart_id), 1);
+        }
+    }
+
+    #[inline]
+    pub fn clear_soft(&self, hart_id: usize) {
+        unsafe {
+            let base = self.base as *mut u8;
+            core::ptr::write_volatile((base as *mut u32).add(hart_id), 0);
+        }
+    }
+}
+
+impl Ipi for Clint {
+    #[inline]
+    fn send_ipi_many(&self, hart_mask: HartMask) -> SbiRet {
+        // println!("[rustsbi] send ipi many, {:?}", hart_mask);
+        let num_harts = *crate::count_harts::NUM_HARTS.lock();
+        for i in 0..num_harts {
+            if hart_mask.has_bit(i) {
+                self.send_soft(i);
+            }
+        }
+        SbiRet::ok(0)
+    }
+}
+
+impl Timer for Clint {
+    #[inline]
+    fn set_timer(&self, time_value: u64) {
+        let this_mhartid = riscv::register::mhartid::read();
+        self.set_timer(this_mhartid, time_value);
+    }
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/count_harts.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/count_harts.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7b24054830c79b07c236e3c4b3eb58143aa3ccfb
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/count_harts.rs
@@ -0,0 +1,54 @@
+use device_tree::{DeviceTree, Node};
+
+const DEVICE_TREE_MAGIC: u32 = 0xD00DFEED;
+
+lazy_static::lazy_static! {
+    // 最大的硬件线程编号;只在启动时写入,跨核软中断发生时读取
+    pub static ref NUM_HARTS: spin::Mutex<usize> = spin::Mutex::new(8);
+}
+
+pub unsafe fn init_hart_count(dtb_pa: usize) {
+    *NUM_HARTS.lock() = count_harts(dtb_pa)
+}
+
+#[repr(C)]
+struct DtbHeader {
+    magic: u32,
+    size: u32,
+}
+
+unsafe fn count_harts(dtb_pa: usize) -> usize {
+    let header = &*(dtb_pa as *const DtbHeader);
+    // from_be 是大小端序的转换(from big endian)
+    let magic = u32::from_be(header.magic);
+    if magic == DEVICE_TREE_MAGIC {
+        let size = u32::from_be(header.size);
+        // 拷贝数据,加载并遍历
+        let data = core::slice::from_raw_parts(dtb_pa as *const u8, size as usize);
+        if let Ok(dt) = DeviceTree::load(data) {
+            if let Some(cpu_map) = dt.find("/cpus/cpu-map") {
+                return enumerate_cpu_map(cpu_map);
+            }
+        }
+    }
+    // 如果DTB的结构不对(读不到/cpus/cpu-map),返回默认的8个核
+    let ans = 8;
+    println!("[rustsbi-dtb] Could not read '/cpus/cpu-map' from 'dtb_pa' device tree root; assuming {} cores", ans);
+    ans
+}
+
+// 遍历“cpu_map”结构
+// 这个结构的子结构是“处理核簇”(cluster)
+// 每个“处理核簇”的子结构分别表示一个处理器核
+fn enumerate_cpu_map(cpu_map_node: &Node) -> usize {
+    let mut tot = 0;
+    for cluster_node in cpu_map_node.children.iter() {
+        let name = &cluster_node.name;
+        let count = cluster_node.children.iter().count();
+        // 会输出:Hart count: cluster0 with 2 cores
+        // 在justfile的“threads := "2"”处更改
+        println!("[rustsbi-dtb] Hart count: {} with {} cores", name, count);
+        tot += count;
+    }
+    tot
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/execute.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/execute.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ca4ef3ac83af6793e58a5f13c0812ba98b5be0dc
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/execute.rs
@@ -0,0 +1,146 @@
+use core::{
+    ops::{Generator, GeneratorState},
+    pin::Pin,
+};
+
+use riscv::register::{mcause, mie, mip, scause::{Exception, Trap}};
+use riscv::register::scause::Interrupt;
+
+use crate::feature;
+use crate::prv_mem::{self, SupervisorPointer};
+use crate::qemu_hsm::{HsmCommand, pause, QemuHsm};
+use crate::runtime::{MachineTrap, Runtime, SupervisorContext};
+
+pub fn execute_supervisor(supervisor_mepc: usize, hart_id: usize, a1: usize, hsm: QemuHsm) -> ! {
+    let mut rt = Runtime::new_sbi_supervisor(supervisor_mepc, hart_id, a1);
+    hsm.record_current_start_finished();
+    loop {
+        match Pin::new(&mut rt).resume(()) {
+            GeneratorState::Yielded(MachineTrap::SbiCall()) => {
+                let ctx = rt.context_mut();
+                let param = [ctx.a0, ctx.a1, ctx.a2, ctx.a3, ctx.a4, ctx.a5];
+                let ans = rustsbi::ecall(ctx.a7, ctx.a6, param);
+                if ans.error == 0x233 {
+                    // hart non-retentive resume
+                    if let Some(HsmCommand::Start(start_paddr, opaque)) = hsm.last_command() {
+                        unsafe {
+                            riscv::register::satp::write(0);
+                            riscv::register::sstatus::clear_sie();
+                        }
+                        hsm.record_current_start_finished();
+                        ctx.mstatus = riscv::register::mstatus::read(); // get from modified sstatus
+                        ctx.a0 = hart_id;
+                        ctx.a1 = opaque;
+                        ctx.mepc = start_paddr;
+                    }
+                } else {
+                    ctx.a0 = ans.error;
+                    ctx.a1 = ans.value;
+                    ctx.mepc = ctx.mepc.wrapping_add(4);
+                }
+            }
+            GeneratorState::Yielded(MachineTrap::IllegalInstruction()) => {
+                let ctx = rt.context_mut();
+                let ptr: SupervisorPointer<usize> = SupervisorPointer::cast(ctx.mepc);
+                let deref_ans = unsafe { prv_mem::try_read(ptr) };
+                let ins = match deref_ans {
+                    Ok(ins) => ins,
+                    Err(e) => fail_cant_read_exception_address(ctx, e),
+                };
+                if !emulate_illegal_instruction(ctx, ins) {
+                    unsafe {
+                        if feature::should_transfer_trap(ctx) {
+                            feature::do_transfer_trap(
+                                ctx,
+                                Trap::Exception(Exception::IllegalInstruction),
+                            )
+                        } else {
+                            fail_illegal_instruction(ctx, ins)
+                        }
+                    }
+                }
+            }
+            GeneratorState::Yielded(MachineTrap::MachineTimer()) => unsafe {
+                mip::set_stimer();
+                mie::clear_mtimer();
+            },
+            GeneratorState::Yielded(MachineTrap::MachineSoft()) => match hsm.last_command() {
+                Some(HsmCommand::Start(_start_paddr, _opaque)) => {
+                    panic!("rustsbi-qemu: illegal state")
+                }
+                Some(HsmCommand::Stop) => {
+                    // no hart stop command in qemu, record stop state and pause
+                    hsm.record_current_stop_finished();
+                    pause();
+                    if let Some(HsmCommand::Start(start_paddr, opaque)) = hsm.last_command() {
+                        // Resuming from a non-retentive suspend state is relatively more involved and requires software
+                        // to restore various hart registers and CSRs for all privilege modes.
+                        // Upon resuming from non-retentive suspend state, the hart will jump to supervisor-mode at address
+                        // specified by `resume_addr` with specific registers values described in the table below:
+                        //
+                        // | Register Name | Register Value
+                        // |:--------------|:--------------
+                        // | `satp`        | 0
+                        // | `sstatus.SIE` | 0
+                        // | a0            | hartid
+                        // | a1            | `opaque` parameter
+                        unsafe {
+                            riscv::register::satp::write(0);
+                            riscv::register::sstatus::clear_sie();
+                        }
+                        hsm.record_current_start_finished();
+                        let ctx = rt.context_mut();
+                        ctx.mstatus = riscv::register::mstatus::read(); // get from modified sstatus
+                        ctx.a0 = hart_id;
+                        ctx.a1 = opaque;
+                        ctx.mepc = start_paddr;
+                    }
+                }
+                None => unsafe {
+                    // machine software interrupt but no HSM commands - delegate to S mode;
+                    let ctx = rt.context_mut();
+                    let clint = crate::clint::Clint::new(0x2000000 as *mut u8);
+                    clint.clear_soft(hart_id); // Clear IPI
+                    if feature::should_transfer_trap(ctx) {
+                        feature::do_transfer_trap(
+                            ctx,
+                            Trap::Interrupt(Interrupt::SupervisorSoft),
+                        )
+                    } else {
+                        panic!("rustsbi-qemu: machine soft interrupt with no hart state monitor command")
+                    }
+                },
+            },
+            GeneratorState::Complete(()) => {
+                use rustsbi::Reset;
+                crate::test_device::SiFiveTest.system_reset(
+                    rustsbi::reset::RESET_TYPE_SHUTDOWN,
+                    rustsbi::reset::RESET_REASON_NO_REASON,
+                );
+            }
+        }
+    }
+}
+
+#[inline]
+fn emulate_illegal_instruction(ctx: &mut SupervisorContext, ins: usize) -> bool {
+    if feature::emulate_rdtime(ctx, ins) {
+        return true;
+    }
+    false
+}
+
+// Illegal instruction occurred in M level
+fn fail_illegal_instruction(ctx: &mut SupervisorContext, ins: usize) -> ! {
+    #[cfg(target_pointer_width = "64")]
+    panic!("invalid instruction from machine level, mepc: {:016x?}, instruction: {:016x?}, context: {:016x?}", ctx.mepc, ins, ctx);
+    #[cfg(target_pointer_width = "32")]
+    panic!("invalid instruction from machine level, mepc: {:08x?}, instruction: {:08x?}, context: {:08x?}", ctx.mepc, ins, ctx);
+}
+
+fn fail_cant_read_exception_address(ctx: &mut SupervisorContext, cause: mcause::Exception) -> ! {
+    #[cfg(target_pointer_width = "64")]
+    panic!("can't read exception address, cause: {:?}, mepc: {:016x?}, context: {:016x?}", cause, ctx.mepc, ctx);
+    #[cfg(target_pointer_width = "32")]
+    panic!("can't read exception address, cause: {:?}, mepc: {:08x?}, context: {:08x?}", cause, ctx.mepc, ctx);
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/feature.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/feature.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6bde26d49c6a3945123bed67719d0d4fb340fcae
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/feature.rs
@@ -0,0 +1,5 @@
+mod emulate_rdtime;
+mod transfer_trap;
+
+pub use emulate_rdtime::emulate_rdtime;
+pub use transfer_trap::{do_transfer_trap, should_transfer_trap};
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/feature/emulate_rdtime.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/feature/emulate_rdtime.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a76a52c52a84820da81b59b827f7cfc56164da62
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/feature/emulate_rdtime.rs
@@ -0,0 +1,27 @@
+use crate::clint;
+use crate::runtime::SupervisorContext;
+
+#[inline]
+pub fn emulate_rdtime(ctx: &mut SupervisorContext, ins: usize) -> bool {
+    return if ins & 0xFFFFF07F == 0xC0102073 {
+        let rd = ((ins >> 7) & 0b1_1111) as u8;
+        let clint = clint::Clint::new(0x2000000 as *mut u8);
+        let time_usize = clint.get_mtime() as usize;
+        set_register_xi(ctx, rd, time_usize);
+        ctx.mepc = ctx.mepc.wrapping_add(4); // skip rdtime instruction
+        true
+    } else {
+        false // is not a rdtime instruction
+    };
+}
+
+#[inline]
+fn set_register_xi(ctx: &mut SupervisorContext, i: u8, data: usize) {
+    let registers = unsafe { &mut *(ctx as *mut _ as *mut [usize; 31]) };
+    assert!(i <= 31, "i should be valid register target");
+    if i == 0 {
+        // x0, don't modify
+        return;
+    }
+    registers[(i - 1) as usize] = data;
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/feature/transfer_trap.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/feature/transfer_trap.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ddd598ac67bbf847114d025e9b26fcde5df11f23
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/feature/transfer_trap.rs
@@ -0,0 +1,32 @@
+use riscv::register::{
+    mstatus::{self, MPP, SPP},
+    mtval, scause, sepc, stval, stvec,
+};
+
+use crate::runtime::SupervisorContext;
+
+#[inline]
+pub unsafe fn should_transfer_trap(ctx: &mut SupervisorContext) -> bool {
+    ctx.mstatus.mpp() != MPP::Machine
+}
+
+#[inline]
+pub unsafe fn do_transfer_trap(ctx: &mut SupervisorContext, cause: scause::Trap) {
+    // 设置S层异常原因为:非法指令
+    scause::set(cause);
+    // 填写异常指令的指令内容
+    stval::write(mtval::read());
+    // 填写S层需要返回到的地址,这里的mepc会被随后的代码覆盖掉。mepc已经处理了中断向量的问题
+    sepc::write(ctx.mepc);
+    // 设置中断位
+    mstatus::set_mpp(MPP::Supervisor);
+    mstatus::set_spp(SPP::Supervisor);
+    if mstatus::read().sie() {
+        mstatus::set_spie()
+    }
+    mstatus::clear_sie();
+    ctx.mstatus = mstatus::read();
+    // 设置返回地址,返回到S层
+    // 注意,无论是Direct还是Vectored模式,所有异常的向量偏移都是0,不需要处理中断向量,跳转到入口地址即可
+    ctx.mepc = stvec::read().address();
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/hart_csr_utils.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/hart_csr_utils.rs
new file mode 100644
index 0000000000000000000000000000000000000000..57e4136162bea6880807e168ed20ebe14be12802
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/hart_csr_utils.rs
@@ -0,0 +1,304 @@
+use alloc::format;
+use alloc::vec::Vec;
+use bit_field::BitField;
+use riscv::register::{
+    medeleg, mideleg,
+    misa::{self, MXL},
+};
+
+pub fn print_hart_csrs() {
+    print_misa();
+    print_mideleg();
+    print_medeleg();
+    print_pmp();
+}
+
+#[inline]
+fn print_misa() {
+    let isa = misa::read();
+    if let Some(isa) = isa {
+        let mxl_str = match isa.mxl() {
+            MXL::XLEN32 => "RV32",
+            MXL::XLEN64 => "RV64",
+            MXL::XLEN128 => "RV128",
+        };
+        print!("[rustsbi] misa: {}", mxl_str);
+        for ext in 'A'..='Z' {
+            if isa.has_extension(ext) {
+                print!("{}", ext);
+            }
+        }
+        println!("");
+    }
+}
+
+#[inline]
+fn print_mideleg() {
+    let mideleg = mideleg::read();
+    let mut delegs = Vec::new();
+    if mideleg.usoft() {
+        delegs.push("usoft")
+    }
+    if mideleg.utimer() {
+        delegs.push("utimer")
+    }
+    if mideleg.uext() {
+        delegs.push("uext")
+    }
+    if mideleg.ssoft() {
+        delegs.push("ssoft")
+    }
+    if mideleg.stimer() {
+        delegs.push("stimer")
+    }
+    if mideleg.sext() {
+        delegs.push("sext")
+    }
+    println!(
+        "[rustsbi] mideleg: {} ({:#x})",
+        delegs.join(", "),
+        mideleg.bits()
+    );
+}
+
+#[inline]
+fn print_medeleg() {
+    let medeleg = medeleg::read();
+    let mut delegs = Vec::new();
+    if medeleg.instruction_misaligned() {
+        delegs.push("ima")
+    }
+    if medeleg.instruction_fault() {
+        delegs.push("ia") // instruction access
+    }
+    if medeleg.illegal_instruction() {
+        delegs.push("illinsn")
+    }
+    if medeleg.breakpoint() {
+        delegs.push("bkpt")
+    }
+    if medeleg.load_misaligned() {
+        delegs.push("lma")
+    }
+    if medeleg.load_fault() {
+        delegs.push("la") // load access
+    }
+    if medeleg.store_misaligned() {
+        delegs.push("sma")
+    }
+    if medeleg.store_fault() {
+        delegs.push("sa") // store access
+    }
+    if medeleg.user_env_call() {
+        delegs.push("uecall")
+    }
+    if medeleg.supervisor_env_call() {
+        delegs.push("secall")
+    }
+    if medeleg.machine_env_call() {
+        delegs.push("mecall")
+    }
+    if medeleg.instruction_page_fault() {
+        delegs.push("ipage")
+    }
+    if medeleg.load_page_fault() {
+        delegs.push("lpage")
+    }
+    if medeleg.store_page_fault() {
+        delegs.push("spage")
+    }
+    println!(
+        "[rustsbi] medeleg: {} ({:#x})",
+        delegs.join(", "),
+        medeleg.bits()
+    );
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline]
+fn print_pmp() {
+    let pmps = unsafe { pmps::<16>() };
+    for (i, (pmpicfg, pmpiaddr)) in pmps.iter().enumerate() {
+        let pmpicfg = PmpCfg::from(*pmpicfg);
+        let range = match pmpicfg.a() {
+            AddressMatching::Off => continue,
+            AddressMatching::Tor => (0, (1 << (55 + 1)) - 1), // max pmp bits = 55
+            AddressMatching::Na4 => ((*pmpiaddr as u128) << 2, ((*pmpiaddr as u128) << 2) + 4),
+            AddressMatching::Napot => napot_pmpaddr_cfg(*pmpiaddr as u128),
+        };
+        let range = format!("{:#x} ..= {:#x}", range.0, range.1);
+        let privilege = format!(
+            "{}{}{}",
+            if pmpicfg.r() { "r" } else { "-" },
+            if pmpicfg.w() { "w" } else { "-" },
+            if pmpicfg.x() { "x" } else { "-" },
+        );
+        let l = if pmpicfg.l() { "l, " } else { "" };
+        println!("[rustsbi] pmp{}: {} ({}{})", i, range, privilege, l);
+    }
+}
+
+fn napot_pmpaddr_cfg(input: u128) -> (u128, u128) {
+    let trailing_ones = input.trailing_ones();
+    if trailing_ones == 0 {
+        return (input, input);
+    }
+    let mask = (1 << trailing_ones) - 1;
+    ((input - mask) << 2, ((input + 1) << 2) - 1)
+}
+
+struct PmpCfg {
+    bits: u8,
+}
+
+impl From<u8> for PmpCfg {
+    fn from(bits: u8) -> PmpCfg {
+        PmpCfg { bits }
+    }
+}
+
+impl PmpCfg {
+    #[inline]
+    pub fn r(&self) -> bool {
+        self.bits.get_bit(0)
+    }
+    #[inline]
+    pub fn w(&self) -> bool {
+        self.bits.get_bit(1)
+    }
+    #[inline]
+    pub fn x(&self) -> bool {
+        self.bits.get_bit(2)
+    }
+    #[inline]
+    pub fn a(&self) -> AddressMatching {
+        match self.bits.get_bits(3..5) {
+            0 => AddressMatching::Off,
+            1 => AddressMatching::Tor,
+            2 => AddressMatching::Na4,
+            3 => AddressMatching::Napot,
+            _ => unreachable!(),
+        }
+    }
+    #[inline]
+    pub fn l(&self) -> bool {
+        self.bits.get_bit(7)
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum AddressMatching {
+    Off,
+    Tor,
+    Na4,
+    Napot,
+}
+
+// 1.12中,L=64;1.11中,L=16。
+// 0..16 => pmpcfg[0, 2]
+// 0..64 => pmpcfg[0, 2, 4, 6, .., 14]
+#[inline]
+unsafe fn pmps<const L: usize>() -> [(u8, usize); L] {
+    assert!(L < 64, "in pmpxcfg, x should be in [0, 64)");
+    let xlen: usize = core::mem::size_of::<usize>() * 8;
+    let cfgs_in_pmpcfg: usize = xlen / 8;
+    let pmpcfg_max_id: usize = L / cfgs_in_pmpcfg;
+    let mut ans = [(0, 0); L];
+    for i in (0..pmpcfg_max_id).step_by(xlen / 32) {
+        let pmpcfgi = pmpcfg_r(i).to_le_bytes();
+        for j in 0..cfgs_in_pmpcfg {
+            let pmpaddr_id = i * 4 + j;
+            let pmpaddri = pmpaddr_r(pmpaddr_id);
+            ans[pmpaddr_id] = (pmpcfgi[j], pmpaddri);
+        }
+    }
+    ans
+}
+
+// 1.12版本中,pmpcfg总共有16个,其中64位下只能访问偶数个,32位下可以访问所有寄存器
+// 1.11版本中,pmpcfg只有4个。有些模拟器最多只支持4个pmp寄存器,大于4的编号会出错。
+#[inline]
+unsafe fn pmpcfg_r(pmpcfg_id: usize) -> usize {
+    assert!(pmpcfg_id <= 15, "pmpcfg id should be in [0, 15]");
+    let ans: usize;
+    core::arch::asm!(
+    // tmp <- 1的地址;len <- csrr和j指令的长度和
+    "la     {tmp}, 1f
+    la      {len}, 2f
+    sub     {len}, {len}, {tmp}",
+    // tmp <- tmp + id * len(csrr + j)
+    "mul    {id}, {id}, {len}
+    add     {tmp}, {tmp}, {id}
+    jr      {tmp}",
+"1:  csrr   {ans}, 0x3A0", "j   1f",
+"2:  csrr   {ans}, 0x3A1", "j   1f",
+    "csrr   {ans}, 0x3A2", "j   1f",
+    "csrr   {ans}, 0x3A3", "j   1f",
+    "csrr   {ans}, 0x3A4", "j   1f",
+    "csrr   {ans}, 0x3A5", "j   1f",
+    "csrr   {ans}, 0x3A6", "j   1f",
+    "csrr   {ans}, 0x3A7", "j   1f",
+    "csrr   {ans}, 0x3A8", "j   1f",
+    "csrr   {ans}, 0x3A9", "j   1f",
+    "csrr   {ans}, 0x3AA", "j   1f",
+    "csrr   {ans}, 0x3AB", "j   1f",
+    "csrr   {ans}, 0x3AC", "j   1f",
+    "csrr   {ans}, 0x3AD", "j   1f",
+    "csrr   {ans}, 0x3AE", "j   1f",
+    "csrr   {ans}, 0x3AF", "j   1f",
+"1:", 
+    id = in(reg) pmpcfg_id, tmp = out(reg) _, len = out(reg) _, ans = out(reg) ans);
+    ans
+}
+
+// 1.12中有63个,但1.11中只有15个。个别模拟器需要注意,详见上文
+#[inline]
+unsafe fn pmpaddr_r(pmpaddr_id: usize) -> usize {
+    assert!(pmpaddr_id <= 63, "pmpcfg id should be in [0, 63]");
+    let ans: usize;
+    core::arch::asm!(
+    // tmp <- 1的地址;len <- csrr和j指令的长度和
+    "la     {tmp}, 1f
+    la      {len}, 2f
+    sub     {len}, {len}, {tmp}",
+    // tmp <- tmp + id * len(csrr + j)
+    "mul    {id}, {id}, {len}
+    add     {tmp}, {tmp}, {id}
+    jr      {tmp}",
+"1:  csrr   {ans}, 0x3B0", "j   1f",
+"2:  csrr   {ans}, 0x3B1", "j   1f",
+    "csrr   {ans}, 0x3B2", "j   1f", "csrr   {ans}, 0x3B3", "j   1f",
+    "csrr   {ans}, 0x3B4", "j   1f", "csrr   {ans}, 0x3B5", "j   1f",
+    "csrr   {ans}, 0x3B6", "j   1f", "csrr   {ans}, 0x3B7", "j   1f",
+    "csrr   {ans}, 0x3B8", "j   1f", "csrr   {ans}, 0x3B9", "j   1f",
+    "csrr   {ans}, 0x3BA", "j   1f", "csrr   {ans}, 0x3BB", "j   1f",
+    "csrr   {ans}, 0x3BC", "j   1f", "csrr   {ans}, 0x3BD", "j   1f",
+    "csrr   {ans}, 0x3BE", "j   1f", "csrr   {ans}, 0x3BF", "j   1f",
+    "csrr   {ans}, 0x3C0", "j   1f", "csrr   {ans}, 0x3C1", "j   1f",
+    "csrr   {ans}, 0x3C2", "j   1f", "csrr   {ans}, 0x3C3", "j   1f",
+    "csrr   {ans}, 0x3C4", "j   1f", "csrr   {ans}, 0x3C5", "j   1f",
+    "csrr   {ans}, 0x3C6", "j   1f", "csrr   {ans}, 0x3C7", "j   1f",
+    "csrr   {ans}, 0x3C8", "j   1f", "csrr   {ans}, 0x3C9", "j   1f",
+    "csrr   {ans}, 0x3CA", "j   1f", "csrr   {ans}, 0x3CB", "j   1f",
+    "csrr   {ans}, 0x3CC", "j   1f", "csrr   {ans}, 0x3CD", "j   1f",
+    "csrr   {ans}, 0x3CE", "j   1f", "csrr   {ans}, 0x3CF", "j   1f",
+    "csrr   {ans}, 0x3D0", "j   1f", "csrr   {ans}, 0x3D1", "j   1f",
+    "csrr   {ans}, 0x3D2", "j   1f", "csrr   {ans}, 0x3D3", "j   1f",
+    "csrr   {ans}, 0x3D4", "j   1f", "csrr   {ans}, 0x3D5", "j   1f",
+    "csrr   {ans}, 0x3D6", "j   1f", "csrr   {ans}, 0x3D7", "j   1f",
+    "csrr   {ans}, 0x3D8", "j   1f", "csrr   {ans}, 0x3D9", "j   1f",
+    "csrr   {ans}, 0x3DA", "j   1f", "csrr   {ans}, 0x3DB", "j   1f",
+    "csrr   {ans}, 0x3DC", "j   1f", "csrr   {ans}, 0x3DD", "j   1f",
+    "csrr   {ans}, 0x3DE", "j   1f", "csrr   {ans}, 0x3DF", "j   1f",
+    "csrr   {ans}, 0x3E0", "j   1f", "csrr   {ans}, 0x3E1", "j   1f",
+    "csrr   {ans}, 0x3E2", "j   1f", "csrr   {ans}, 0x3E3", "j   1f",
+    "csrr   {ans}, 0x3E4", "j   1f", "csrr   {ans}, 0x3E5", "j   1f",
+    "csrr   {ans}, 0x3E6", "j   1f", "csrr   {ans}, 0x3E7", "j   1f",
+    "csrr   {ans}, 0x3E8", "j   1f", "csrr   {ans}, 0x3E9", "j   1f",
+    "csrr   {ans}, 0x3EA", "j   1f", "csrr   {ans}, 0x3EB", "j   1f",
+    "csrr   {ans}, 0x3EC", "j   1f", "csrr   {ans}, 0x3ED", "j   1f",
+    "csrr   {ans}, 0x3EE", "j   1f", "csrr   {ans}, 0x3EF", "j   1f",
+"1:", 
+    id = in(reg) pmpaddr_id, tmp = out(reg) _, len = out(reg) _, ans = out(reg) ans);
+    ans
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/linker64.ld b/bootloader/rustsbi-qemu/rustsbi-qemu/src/linker64.ld
new file mode 100644
index 0000000000000000000000000000000000000000..c45b890ff0fce500678d06313f9cb801c4efc6fa
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/linker64.ld
@@ -0,0 +1,48 @@
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+BASE_ADDRESS = 0x80000000;
+
+SECTIONS
+{
+    . = BASE_ADDRESS;
+    skernel = .;
+
+    stext = .;
+    .text : {
+        *(.text.entry)
+        *(.text .text.*)
+    }
+
+    . = ALIGN(4);
+    etext = .;
+    srodata = .;
+    .rodata : {
+        *(.rodata .rodata.*)
+        *(.srodata .srodata.*)
+    }
+
+    . = ALIGN(4);
+    erodata = .;
+    sdata = .;
+    .data : {
+        *(.data .data.*)
+        *(.sdata .sdata.*)
+    }
+
+    . = ALIGN(4);
+    edata = .;
+    .bss : {
+        *(.bss.uninit)
+        sbss = .;
+        *(.bss .bss.*)
+        *(.sbss .sbss.*)
+    }
+
+    . = ALIGN(4);
+    ebss = .;
+    ekernel = .;
+
+    /DISCARD/ : {
+        *(.eh_frame)
+    }
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/main.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a0eb9161c01b65ce387e687757147b4cae219e73
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/main.rs
@@ -0,0 +1,239 @@
+#![no_std]
+#![no_main]
+#![feature(naked_functions)]
+#![feature(asm_sym, asm_const)]
+#![feature(generator_trait)]
+#![feature(default_alloc_error_handler)]
+
+extern crate alloc;
+#[macro_use]
+extern crate rustsbi;
+
+use core::arch::asm;
+use core::panic::PanicInfo;
+
+use buddy_system_allocator::LockedHeap;
+
+mod clint;
+mod count_harts;
+mod execute;
+mod feature;
+mod hart_csr_utils;
+mod ns16550a;
+mod prv_mem;
+mod qemu_hsm;
+mod qemu_pmu;
+mod runtime;
+mod test_device;
+
+const PER_HART_STACK_SIZE: usize = 4 * 4096; // 16KiB
+const SBI_STACK_SIZE: usize = 8 * PER_HART_STACK_SIZE; // assume 8 cores in QEMU
+#[link_section = ".bss.uninit"]
+static mut SBI_STACK: [u8; SBI_STACK_SIZE] = [0; SBI_STACK_SIZE];
+
+const SBI_HEAP_SIZE: usize = 64 * 1024; // 64KiB
+#[link_section = ".bss.uninit"]
+static mut HEAP_SPACE: [u8; SBI_HEAP_SIZE] = [0; SBI_HEAP_SIZE];
+#[global_allocator]
+static SBI_HEAP: LockedHeap<32> = LockedHeap::empty();
+
+#[cfg_attr(not(test), panic_handler)]
+#[allow(unused)]
+fn panic(info: &PanicInfo) -> ! {
+    let hart_id = riscv::register::mhartid::read();
+    // 输出的信息大概是“[rustsbi-panic] hart 0 panicked at ...”
+    println!("[rustsbi-panic] hart {} {}", hart_id, info);
+    println!("[rustsbi-panic] system shutdown scheduled due to RustSBI panic");
+    use rustsbi::Reset;
+    test_device::SiFiveTest.system_reset(
+        rustsbi::reset::RESET_TYPE_SHUTDOWN,
+        rustsbi::reset::RESET_REASON_SYSTEM_FAILURE,
+    );
+    loop {}
+}
+
+lazy_static::lazy_static! {
+    pub static ref HSM: qemu_hsm::QemuHsm = qemu_hsm::QemuHsm::new();
+}
+
+extern "C" fn rust_main(hartid: usize, opqaue: usize) -> ! {
+    runtime::init();
+    if hartid == 0 {
+        init_heap();
+        init_legacy_stdio();
+        init_clint();
+        init_test_device();
+        println!(
+            "[rustsbi] RustSBI version {}, adapting to RISC-V SBI v0.3",
+            rustsbi::VERSION
+        );
+        println!("{}", rustsbi::LOGO);
+        println!(
+            "[rustsbi] Implementation: RustSBI-QEMU Version {}",
+            env!("CARGO_PKG_VERSION")
+        );
+        unsafe { count_harts::init_hart_count(opqaue) };
+        // initialize hsm module
+        rustsbi::init_hsm(HSM.clone());
+    } else {
+        qemu_hsm::pause();
+    }
+    delegate_interrupt_exception();
+    set_pmp();
+    unsafe {
+        // enable wake by ipi
+        riscv::register::mstatus::set_mie();
+    }
+    if hartid == 0 {
+        // print hart csr configuration
+        hart_csr_utils::print_hart_csrs();
+        // start other harts
+        let clint = clint::Clint::new(0x2000000 as *mut u8);
+        let num_harts = *{ count_harts::NUM_HARTS.lock() };
+        for target_hart_id in 0..num_harts {
+            if target_hart_id != 0 {
+                clint.send_soft(target_hart_id);
+            }
+        }
+        println!("[rustsbi] enter supervisor 0x80200000");
+    }
+    // start SBI environment
+    execute::execute_supervisor(0x80200000, hartid, opqaue, HSM.clone());
+}
+
+fn init_heap() {
+    unsafe {
+        SBI_HEAP
+            .lock()
+            .init(HEAP_SPACE.as_ptr() as usize, SBI_HEAP_SIZE)
+    }
+}
+
+fn init_legacy_stdio() {
+    let serial = ns16550a::Ns16550a::new(0x10000000, 0, 11_059_200, 115200);
+    use rustsbi::legacy_stdio::init_legacy_stdio_embedded_hal;
+    init_legacy_stdio_embedded_hal(serial);
+}
+
+fn init_clint() {
+    let clint = clint::Clint::new(0x2000000 as *mut u8);
+    use rustsbi::init_ipi;
+    init_ipi(clint);
+    let clint = clint::Clint::new(0x2000000 as *mut u8);
+    use rustsbi::init_timer;
+    init_timer(clint);
+}
+
+fn init_test_device() {
+    use rustsbi::init_reset;
+    init_reset(test_device::SiFiveTest);
+}
+
+// 委托中断;把S的中断全部委托给S层
+fn delegate_interrupt_exception() {
+    use riscv::register::{medeleg, mideleg, mie};
+    unsafe {
+        mideleg::set_sext();
+        mideleg::set_stimer();
+        mideleg::set_ssoft();
+        mideleg::set_uext();
+        mideleg::set_utimer();
+        mideleg::set_usoft();
+        medeleg::set_instruction_misaligned();
+        medeleg::set_breakpoint();
+        medeleg::set_user_env_call();
+        medeleg::set_instruction_page_fault();
+        medeleg::set_load_page_fault();
+        medeleg::set_store_page_fault();
+        medeleg::set_instruction_fault();
+        medeleg::set_load_fault();
+        medeleg::set_store_fault();
+        mie::set_mext();
+        // 不打开mie::set_mtimer
+        mie::set_msoft();
+    }
+}
+
+fn set_pmp() {
+    // todo: 根据QEMU的loader device等等,设置这里的权限配置
+    // read fdt tree value, parse, and calculate proper pmp configuration for this device tree (issue #7)
+    // integrate with `count_harts`
+    //
+    // Qemu MMIO config ref: https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c#L46
+    //
+    // About PMP:
+    //
+    // CSR: pmpcfg0(0x3A0)~pmpcfg15(0x3AF); pmpaddr0(0x3B0)~pmpaddr63(0x3EF)
+    // pmpcfg packs pmp entries each of which is of 8-bit
+    // on RV64 only even pmpcfg CSRs(0,2,...,14) are available, each of which contains 8 PMP
+    // entries
+    // every pmp entry and its corresponding pmpaddr describe a pmp region
+    //
+    // layout of PMP entries:
+    // ------------------------------------------------------
+    //  7   |   [5:6]   |   [3:4]   |   2   |   1   |   0   |
+    //  L   |   0(WARL) |   A       |   X   |   W   |   R   |
+    // ------------------------------------------------------
+    // A = OFF(0), disabled;
+    // A = TOR(top of range, 1), match address y so that pmpaddr_{i-1}<=y<pmpaddr_i irrespective of
+    // the value pmp entry i-1
+    // A = NA4(naturally aligned 4-byte region, 2), only support a 4-byte pmp region
+    // A = NAPOT(naturally aligned power-of-two region, 3), support a >=8-byte pmp region
+    // When using NAPOT to match a address range [S,S+L), then the pmpaddr_i should be set to (S>>2)|((L>>2)-1)
+    let calc_pmpaddr = |start_addr: usize, length: usize| {
+        (start_addr >> 2) | ((length >> 2) - 1) 
+    };
+    let mut pmpcfg0: usize = 0;
+    // pmp region 0: RW, A=NAPOT, address range {0x1000_1000, 0x1000}, VIRT_VIRTIO
+    //                            address range {0x1000_0000, 0x100}, VIRT_UART0
+    //                            aligned address range {0x1000_0000, 0x2000}
+    pmpcfg0 |= 0b11011; 
+    let pmpaddr0 = calc_pmpaddr(0x1000_0000, 0x2000);
+    // pmp region 1: RW, A=NAPOT, address range {0x200_0000, 0x1_0000}, VIRT_CLINT 
+    pmpcfg0 |= 0b11011 << 8;
+    let pmpaddr1 = calc_pmpaddr(0x200_0000, 0x1_0000);
+    // pmp region 2: RW, A=NAPOT, address range {0xC00_0000, 0x40_0000}, VIRT_PLIC
+    // VIRT_PLIC_SIZE = 0x20_0000 + 0x1000 * harts, thus supports up to 512 harts
+    pmpcfg0 |= 0b11011 << 16;
+    let pmpaddr2 = calc_pmpaddr(0xC00_0000, 0x40_0000);
+    // pmp region 3: RWX, A=NAPOT, address range {0x8000_0000, 0x1000_0000}, VIRT_DRAM
+    pmpcfg0 |= 0b11111 << 24;
+    let pmpaddr3 = calc_pmpaddr(0x8000_0000, 0x1000_0000);
+    unsafe {
+        core::arch::asm!("csrw  pmpcfg0, {}",
+             "csrw  pmpaddr0, {}",
+             "csrw  pmpaddr1, {}",
+             "csrw  pmpaddr2, {}",
+             "csrw  pmpaddr3, {}",
+             "sfence.vma",
+             in(reg) pmpcfg0,
+             in(reg) pmpaddr0,
+             in(reg) pmpaddr1,
+             in(reg) pmpaddr2,
+             in(reg) pmpaddr3,
+        ); 
+    }
+}
+
+#[naked]
+#[link_section = ".text.entry"]
+#[export_name = "_start"]
+unsafe extern "C" fn entry(_a0: usize, _a1: usize) -> ! {
+    asm!(
+    // 1. set sp
+    // sp = bootstack + (hartid + 1) * HART_STACK_SIZE
+    "
+    la      sp, {stack}
+    li      t0, {per_hart_stack_size}
+    addi    t1, a0, 1
+1:  add     sp, sp, t0
+    addi    t1, t1, -1
+    bnez    t1, 1b
+    ",
+    // 2. jump to rust_main (absolute address)
+    "j      {rust_main}",
+    per_hart_stack_size = const PER_HART_STACK_SIZE,
+    stack = sym SBI_STACK,
+    rust_main = sym rust_main,
+    options(noreturn))
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/ns16550a.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/ns16550a.rs
new file mode 100644
index 0000000000000000000000000000000000000000..cbf36f673ff48564a33178d2aca1c3642b6d523e
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/ns16550a.rs
@@ -0,0 +1,95 @@
+use core::convert::Infallible;
+use core::ptr::{read_volatile, write_volatile};
+use embedded_hal::serial::{Read, Write};
+
+pub struct Ns16550a {
+    base: usize,
+    shift: usize,
+}
+
+impl Ns16550a {
+    pub fn new(base: usize, shift: usize, clk: u64, baud: u64) -> Self {
+        // init process; ref: MeowSBI/utils/uart.rs
+        unsafe {
+            write_volatile((base + (offsets::IER << shift)) as *mut u8, 0);
+            write_volatile((base + (offsets::LCR << shift)) as *mut u8, 0x80); // DLAB
+
+            let latch = clk / (16 * baud);
+            write_volatile((base + (offsets::DLL << shift)) as *mut u8, latch as u8);
+            write_volatile(
+                (base + (offsets::DLH << shift)) as *mut u8,
+                (latch >> 8) as u8,
+            );
+
+            write_volatile((base + (offsets::LCR << shift)) as *mut u8, 3); // WLEN8 & !DLAB
+
+            write_volatile((base + (offsets::MCR << shift)) as *mut u8, 0);
+            
+            write_volatile((base + (offsets::FCR << shift)) as *mut u8, 0x7); // FIFO enable + FIFO reset
+            write_volatile((base + (offsets::IER << shift)) as *mut u8, 1); // RX IE ENABLE
+            // No interrupt for now fuck...
+        }
+        // init finished
+        Self { base, shift }
+    }
+}
+
+impl Read<u8> for Ns16550a {
+    // 其实是可能出错的,overrun啊,这些
+    type Error = Infallible;
+
+    fn read(&mut self) -> nb::Result<u8, Self::Error> {
+        let pending =
+            unsafe { read_volatile((self.base + (offsets::LSR << self.shift)) as *const u8) }
+                & masks::DR;
+        if pending != 0 {
+            let word =
+                unsafe { read_volatile((self.base + (offsets::RBR << self.shift)) as *const u8) };
+            Ok(word)
+        } else {
+            Err(nb::Error::WouldBlock)
+        }
+    }
+}
+
+impl Write<u8> for Ns16550a {
+    type Error = Infallible;
+
+    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
+        // 写,但是不刷新
+        unsafe { write_volatile((self.base + (offsets::THR << self.shift)) as *mut u8, word) };
+        Ok(())
+    }
+
+    fn flush(&mut self) -> nb::Result<(), Self::Error> {
+        let pending =
+            unsafe { read_volatile((self.base + (offsets::LSR << self.shift)) as *const u8) }
+                & masks::THRE;
+        if pending != 0 {
+            // 发送已经结束了
+            Ok(())
+        } else {
+            // 发送还没有结束,继续等
+            Err(nb::Error::WouldBlock)
+        }
+    }
+}
+
+mod offsets {
+    pub const RBR: usize = 0x0;
+    pub const THR: usize = 0x0;
+
+    pub const IER: usize = 0x1;
+    pub const FCR: usize = 0x2;
+    pub const LCR: usize = 0x3;
+    pub const MCR: usize = 0x4;
+    pub const LSR: usize = 0x5;
+
+    pub const DLL: usize = 0x0;
+    pub const DLH: usize = 0x1;
+}
+
+mod masks {
+    pub const THRE: u8 = 1 << 5;
+    pub const DR: u8 = 1;
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/prv_mem.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/prv_mem.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ffab00e8eff40025b1d61878e86d6af5395a17e9
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/prv_mem.rs
@@ -0,0 +1,251 @@
+//! Privileged memory access module
+//!
+//! Reading from privileged mode memory does not need to iterate page table tree from software;
+//! instead, this module makes use of `mstatus.MPRV` bit (17-th bit of `mstatus`) to read memory
+//! from privileged modes under machine level.
+//!
+//! This module is useful when implementation need to process SBI calls with memory addresses
+//! as parameters.
+// Code ref: https://github.com/luojia65/zihai/blob/adb4e69ca1a4118a4de634c0682e34b67810cb0c/zihai/src/detect.rs
+
+use core::arch::asm;
+use core::mem::{self, MaybeUninit};
+
+use riscv::register::{mcause::{Exception, Mcause, Trap}, mcause, mstatus, mtvec::{self, Mtvec, TrapMode}};
+
+/// Pointer at supervisor level
+///
+/// These pointers cannot dereference directly from machine level. Instead, you may use
+/// function `try_read` to get data from them.
+#[derive(Debug)]
+pub struct SupervisorPointer<T> {
+    inner: *const T,
+}
+
+impl<T> SupervisorPointer<T> {
+    /// Cast a supervisor parameter into a supervisor pointer
+    ///
+    /// This is a safe function for creation of a raw pointer; deref it will be unsafe.
+    pub fn cast(supervisor_param: usize) -> Self {
+        SupervisorPointer {
+            inner: supervisor_param as *const _,
+        }
+    }
+}
+
+/// Reads the supervisor memory value, or fail if any exception occurred.
+///
+/// This function will invoke multiple instructions including reads, write, enabling
+/// or disabling `mstatus.MPRV` bit. After they are executed, the value is typically returned
+/// on stack or register with type `T`.
+pub unsafe fn try_read<T>(src: SupervisorPointer<T>) -> Result<T, mcause::Exception> {
+    let mut ans: MaybeUninit<T> = MaybeUninit::uninit();
+    if mstatus::read().mprv() {
+        panic!("rustsbi-qemu: mprv should be cleared before try_read")
+    }
+    for idx in (0..mem::size_of::<T>()).step_by(mem::size_of::<u32>()) {
+        let nr = with_detect_trap(0, || asm!(
+        "li     {mprv_bit}, (1 << 17)",
+        "csrs   mstatus, {mprv_bit}",
+        "lw     {word}, 0({in_s_addr})",
+        "csrc   mstatus, {mprv_bit}",
+        "sw     {word}, 0({out_m_addr})",
+        mprv_bit = out(reg) _,
+        word = out(reg) _,
+        in_s_addr = in(reg) src.inner.cast::<u8>().add(idx),
+        out_m_addr = in(reg) ans.as_mut_ptr().cast::<u8>().add(idx),
+        options(nostack),
+        ));
+        if nr != 0 {
+            return Err(Exception::from(nr))
+        }
+    }
+    Ok(ans.assume_init())
+}
+
+// Tries to execute all instructions defined in clojure `f`.
+// If resulted in an exception, this function returns its exception id.
+//
+// This function is useful to detect if an instruction exists on current environment.
+#[inline]
+fn with_detect_trap(param: usize, f: impl FnOnce()) -> usize {
+    // disable interrupts and handle exceptions only
+    let (mie, mtvec, tp) = unsafe { init_detect_trap(param) };
+    // run detection inner
+    f();
+    // restore trap handler and enable interrupts
+    let ans = unsafe { restore_detect_trap(mie, mtvec, tp) };
+    // return the answer
+    ans
+}
+
+// rust trap handler for detect exceptions
+extern "C" fn rust_detect_trap(trap_frame: &mut TrapFrame) {
+    // store returned exception id value into tp register
+    // specially: illegal instruction => 2
+    trap_frame.tp = trap_frame.mcause.bits();
+    // if illegal instruction, skip current instruction
+    match trap_frame.mcause.cause() {
+        Trap::Exception(_) => {
+            let mut insn_bits = riscv_illegal_instruction_bits((trap_frame.mtval & 0xFFFF) as u16);
+            if insn_bits == 0 {
+                let insn_half = unsafe { *(trap_frame.mepc as *const u16) };
+                insn_bits = riscv_illegal_instruction_bits(insn_half);
+            }
+            // skip current instruction
+            trap_frame.mepc = trap_frame.mepc.wrapping_add(insn_bits);
+        }
+        Trap::Interrupt(_) => unreachable!(), // filtered out for mie == false
+    }
+}
+
+// Gets risc-v instruction bits from illegal instruction stval value, or 0 if unknown
+#[inline]
+fn riscv_illegal_instruction_bits(insn: u16) -> usize {
+    if insn == 0 {
+        return 0; // mtval[0..16] == 0, unknown
+    }
+    if insn & 0b11 != 0b11 {
+        return 2; // 16-bit
+    }
+    if insn & 0b11100 != 0b11100 {
+        return 4; // 32-bit
+    }
+    // FIXME: add >= 48-bit instructions in the future if we need to proceed with such instructions
+    return 0; // >= 48-bit, unknown from this function by now
+}
+
+// Initialize environment for trap detection and filter in exception only
+#[inline]
+unsafe fn init_detect_trap(param: usize) -> (bool, Mtvec, usize) {
+    // clear mie to handle exception only
+    let stored_mie = mstatus::read().mie();
+    mstatus::clear_mie();
+    // use detect trap handler to handle exceptions
+    let stored_mtvec = mtvec::read();
+    let mut trap_addr = on_detect_trap as usize;
+    if trap_addr & 0b1 != 0 {
+        trap_addr += 0b1;
+    }
+    mtvec::write(trap_addr, TrapMode::Direct);
+    // store tp register. tp will be used to load parameter and store return value
+    let stored_tp: usize;
+    asm!("mv  {}, tp", "mv  tp, {}", out(reg) stored_tp, in(reg) param, options(nomem, nostack));
+    // returns preserved previous hardware states
+    (stored_mie, stored_mtvec, stored_tp)
+}
+
+// Restore previous hardware states before trap detection
+#[inline]
+unsafe fn restore_detect_trap(mie: bool, mtvec: Mtvec, tp: usize) -> usize {
+    // read the return value from tp register, and restore tp value
+    let ans: usize;
+    asm!("mv  {}, tp", "mv  tp, {}", out(reg) ans, in(reg) tp, options(nomem, nostack));
+    // restore trap vector settings
+    asm!("csrw  mtvec, {}", in(reg) mtvec.bits(), options(nomem, nostack));
+    // enable interrupts
+    if mie {
+        mstatus::set_mie();
+    };
+    ans
+}
+
+// Trap frame for instruction exception detection
+#[repr(C)]
+struct TrapFrame {
+    ra: usize,
+    tp: usize,
+    a0: usize,
+    a1: usize,
+    a2: usize,
+    a3: usize,
+    a4: usize,
+    a5: usize,
+    a6: usize,
+    a7: usize,
+    t0: usize,
+    t1: usize,
+    t2: usize,
+    t3: usize,
+    t4: usize,
+    t5: usize,
+    t6: usize,
+    mstatus: usize,
+    mepc: usize,
+    mcause: Mcause,
+    mtval: usize,
+}
+
+// Assembly trap handler for instruction detection.
+//
+// This trap handler shares the same stack from its prospective caller,
+// the caller must ensure it has abundant stack size for a trap handler.
+//
+// This function should not be used in conventional trap handling,
+// as it does not preserve a special trap stack, and it's designed to
+// handle exceptions only rather than interrupts.
+#[naked]
+unsafe extern "C" fn on_detect_trap() -> ! {
+    asm!(
+    ".p2align 2",
+    "addi   sp, sp, -8*21",
+    "sd     ra, 0*8(sp)",
+    "sd     tp, 1*8(sp)",
+    "sd     a0, 2*8(sp)",
+    "sd     a1, 3*8(sp)",
+    "sd     a2, 4*8(sp)",
+    "sd     a3, 5*8(sp)",
+    "sd     a4, 6*8(sp)",
+    "sd     a5, 7*8(sp)",
+    "sd     a6, 8*8(sp)",
+    "sd     a7, 9*8(sp)",
+    "sd     t0, 10*8(sp)",
+    "sd     t1, 11*8(sp)",
+    "sd     t2, 12*8(sp)",
+    "sd     t3, 13*8(sp)",
+    "sd     t4, 14*8(sp)",
+    "sd     t5, 15*8(sp)",
+    "sd     t6, 16*8(sp)",
+    "csrr   t0, mstatus",
+    "sd     t0, 17*8(sp)",
+    "csrr   t1, mepc",
+    "sd     t1, 18*8(sp)",
+    "csrr   t2, mcause",
+    "sd     t2, 19*8(sp)",
+    "csrr   t3, mtval",
+    "sd     t3, 20*8(sp)",
+    "mv     a0, sp",
+    "li     t4, (1 << 17)", // clear mstatus.mprv
+    "csrc   mstatus, t4",
+    "call   {rust_detect_trap}",
+    "ld     t0, 17*8(sp)",
+    "csrw   mstatus, t0",
+    "ld     t1, 18*8(sp)",
+    "csrw   mepc, t1",
+    "ld     t2, 19*8(sp)",
+    "csrw   mcause, t2",
+    "ld     t3, 20*8(sp)",
+    "csrw   mtval, t3",
+    "ld     ra, 0*8(sp)",
+    "ld     tp, 1*8(sp)",
+    "ld     a0, 2*8(sp)",
+    "ld     a1, 3*8(sp)",
+    "ld     a2, 4*8(sp)",
+    "ld     a3, 5*8(sp)",
+    "ld     a4, 6*8(sp)",
+    "ld     a5, 7*8(sp)",
+    "ld     a6, 8*8(sp)",
+    "ld     a7, 9*8(sp)",
+    "ld     t0, 10*8(sp)",
+    "ld     t1, 11*8(sp)",
+    "ld     t2, 12*8(sp)",
+    "ld     t3, 13*8(sp)",
+    "ld     t4, 14*8(sp)",
+    "ld     t5, 15*8(sp)",
+    "ld     t6, 16*8(sp)",
+    "addi   sp, sp, 8*21",
+    "sret",
+    rust_detect_trap = sym rust_detect_trap,
+    options(noreturn),
+    )
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/qemu_hsm.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/qemu_hsm.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3f8cfc747a8beb8ae8f3e35e37d670316d5ea4df
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/qemu_hsm.rs
@@ -0,0 +1,336 @@
+//! Hart state monitor designed for QEMU
+
+use alloc::sync::Arc;
+use core::sync::atomic::{AtomicU8, Ordering};
+
+use hashbrown::HashMap;
+use riscv::register::mstatus::{self, MPP};
+use rustsbi::SbiRet;
+
+// RISC-V SBI Hart State Monitor states
+#[allow(unused)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[repr(u8)]
+enum HsmState {
+    /// The hart is physically powered-up and executing normally.
+    Started = 0,
+    /// The hart is not executing in supervisor-mode or any lower privilege mode.
+    /// It is probably powered-down by the SBI implementation if the underlying platform has a mechanism
+    /// to physically power-down harts.
+    Stopped = 1,
+    /// Some other hart has requested to start (or power-up) the hart from the STOPPED state
+    /// and the SBI implementation is still working to get the hart in the STARTED state.
+    StartPending = 2,
+    /// The hart has requested to stop (or power-down) itself from the STARTED state
+    /// and the SBI implementation is still working to get the hart in the STOPPED state.
+    StopPending = 3,
+    /// This hart is in a platform specific suspend (or low power) state.
+    Suspended = 4,
+    /// The hart has requested to put itself in a platform specific low power state from the STARTED state
+    /// and the SBI implementation is still working to get the hart in the platform specific SUSPENDED state.
+    SuspendPending = 5,
+    /// An interrupt or platform specific hardware event has caused the hart to resume normal execution from
+    /// the SUSPENDED state and the SBI implementation is still working to get the hart in the STARTED state.
+    ResumePending = 6,
+}
+
+// RustSBI-QEMU hart state monitor structure. It stores hart states for all harts,
+// and last command (see HsmCommand) when hart is requested to proceed HSM functions.
+//
+// RustSBI-QEMU makes use of machine software interrupt. Functions should modify `state` to
+// XxxPending before the actual procedure began. Then, caller should store next command structure
+// to `last_command`, and use IPI to invoke software interrupt on machine level.
+//
+// When target hart received machine software interrupt, it should read and proceed command
+// from `last_command`. Then, after command execution makes progress, it should modify
+// `state` variable to mark that the HSM function has taken effect.
+//
+// These functions above are defined as asynchronous procedures. That means it returns before
+// actual procedure has finished. There are functions to read its current state when the target hart
+// is still in transition or after the transition is done. These functions may read from `last_command`
+// variable at any time.
+#[derive(Clone)]
+pub struct QemuHsm {
+    state: Arc<spin::Mutex<HashMap<usize, AtomicU8>>>,
+    last_command: Arc<spin::Mutex<HashMap<usize, HsmCommand>>>,
+}
+
+// RustSBI-QEMU HSM command, these commands apply to a remote given hart.
+//
+// Should be stored with hart id before software interrupt is invoked.
+// After software interrupt is received, the target hart should handle with HSM command structure
+// and run corresponding HSM procedures.
+//
+// By current version of SBI specification, suspend command only apply to current hart,
+// thus RustSBI does not use remote HSM command in this case.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum HsmCommand {
+    Start(usize, usize),
+    Stop,
+}
+
+impl QemuHsm {
+    // creates a RustSBI-QEMU hsm structure.
+    pub fn new() -> Self {
+        Self {
+            state: Arc::new(spin::Mutex::new(HashMap::new())),
+            last_command: Arc::new(spin::Mutex::new(HashMap::new())),
+        }
+    }
+    // Return last command by current hart id.
+    // This function is used in software interrupt handler to check which HSM function should we execute.
+    pub(crate) fn last_command(&self) -> Option<HsmCommand> {
+        let hart_id = riscv::register::mhartid::read();
+        let last_command_lock = self.last_command.lock();
+        let ans = last_command_lock.get(&hart_id).map(|c| *c);
+        drop(last_command_lock);
+        ans
+    }
+    // Record that current hart id is marked as `Stopped` state.
+    // It is used in interrupt handler, when hart stop command is received. Before this function,
+    // the target hart is making preparations to stop; it records state and must stop immediately after
+    // this function is called.
+    pub(crate) fn record_current_stop_finished(&self) {
+        let hart_id = riscv::register::mhartid::read();
+        self.state
+            .lock()
+            .entry(hart_id)
+            .insert(AtomicU8::new(HsmState::Stopped as u8));
+    }
+    // Record that current hart id is marked as `Started` state.
+    // It is used when hart stop command is received in interrupt handler.
+    // The target hart (when in interrupt handler) is prepared to start, it marks itself into 'started',
+    // and should jump to target address right away.
+    pub(crate) fn record_current_start_finished(&self) {
+        let hart_id = riscv::register::mhartid::read();
+        self.state
+            .lock()
+            .entry(hart_id)
+            .insert(AtomicU8::new(HsmState::Started as u8));
+    }
+}
+
+// Adapt RustSBI interface to RustSBI-QEMU's QemuHsm.
+impl rustsbi::Hsm for QemuHsm {
+    // The supervisor software above RustSBI has called SBI environment to start a given `hart_id`
+    // to address `start_addr` with parameter `opaque`.
+    fn hart_start(&self, hart_id: usize, start_addr: usize, opaque: usize) -> SbiRet {
+        // previous privileged mode should be user or supervisor; start from machine mode is not supported
+        let mpp = mstatus::read().mpp();
+        if mpp != MPP::Supervisor && mpp != MPP::User {
+            return SbiRet::invalid_param();
+        }
+        // try to modify state to start hart
+        let mut state_lock = self.state.lock();
+        let current_state = state_lock
+            .entry(hart_id)
+            .or_insert(AtomicU8::new(HsmState::Stopped as u8))
+            .compare_exchange(
+                HsmState::Stopped as u8,
+                HsmState::StartPending as u8,
+                Ordering::AcqRel,
+                Ordering::Acquire,
+            );
+        // proceed with invalid hart states.
+        // - the given hartid is already started, the compare exchange should fail and suggests current state as `Started`,
+        // function should return error as already available.
+        if current_state == Err(HsmState::Started as u8) {
+            return SbiRet::already_available();
+        }
+        // - otherwise return invalid parameter, this may be caused for hart is already transitioning from started state
+        if current_state != Ok(HsmState::Stopped as u8) {
+            return SbiRet::invalid_param();
+        }
+        // todo: check start address
+        /* SBI_ERR_INVALID_ADDRESS: start_addr is not valid possibly due to following reasons:
+         * It is not a valid physical address.
+         * The address is prohibited by PMP to run in supervisor mode. */
+        // fill in the parameter
+        let mut config_lock = self.last_command.lock();
+        config_lock
+            .entry(hart_id)
+            .insert(HsmCommand::Start(start_addr, opaque));
+        drop(config_lock);
+        drop(state_lock);
+        // now, start the target hart
+        let clint = crate::clint::Clint::new(0x2000000 as *mut u8);
+        clint.send_soft(hart_id); // this does not block the current function
+                                  // The following process is going to be handled in software interrupt handler, and
+                                  // the function returns immediately as starting a hart is defined as an asynchronous procedure.
+        SbiRet::ok(0)
+    }
+    fn hart_stop(&self, hart_id: usize) -> SbiRet {
+        // try to set current target hart state to stop pending
+        let mut state_lock = self.state.lock();
+        let current_state = state_lock
+            .entry(hart_id)
+            .or_insert(AtomicU8::new(HsmState::Stopped as u8))
+            .compare_exchange(
+                HsmState::Started as u8,
+                HsmState::StopPending as u8,
+                Ordering::AcqRel,
+                Ordering::Acquire,
+            );
+        // check current hart state
+        if current_state.is_err() {
+            return SbiRet::failed(); // illegal state
+        }
+        // fill in the parameter
+        let mut config_lock = self.last_command.lock();
+        config_lock.entry(hart_id).insert(HsmCommand::Stop);
+        drop(config_lock);
+        drop(state_lock);
+        // stop the target hart
+        let clint = crate::clint::Clint::new(0x2000000 as *mut u8);
+        clint.send_soft(hart_id);
+        SbiRet::ok(0)
+    }
+    fn hart_get_status(&self, hart_id: usize) -> SbiRet {
+        self.state.lock().get(&hart_id).map_or(
+            SbiRet::invalid_param(), // not in `state` map structure, the given hart id is invalid
+            |a| SbiRet::ok(a.load(Ordering::Relaxed) as usize),
+        )
+    }
+    // Supervisor requested current hart to suspend.
+    //
+    // In RustSBI-QEMU, if `suspend_type` is retentive, it pauses the current hart; `resume_addr`
+    // and `opaque` is not used.
+    // Otherwise, the current hart discards current supervisor context, and returns to another
+    //  `resume_addr` with parameter `opaque`.
+    fn hart_suspend(&self, suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
+        match suspend_type {
+            // Resuming from a retentive suspend state is straight forward and the supervisor-mode software
+            // will see SBI suspend call return without any failures.
+            SUSPEND_RETENTIVE => {
+                // try to set current target hart state to stop pending
+                let hart_id = riscv::register::mhartid::read();
+                let mut state_lock = self.state.lock();
+                let current_state = state_lock
+                    .entry(hart_id)
+                    .or_insert(AtomicU8::new(HsmState::Stopped as u8))
+                    .compare_exchange(
+                        HsmState::Started as u8,
+                        HsmState::SuspendPending as u8,
+                        Ordering::AcqRel,
+                        Ordering::Acquire,
+                    );
+                // check current hart state
+                if current_state.is_err() {
+                    return SbiRet::failed(); // illegal state
+                }
+                drop(state_lock);
+                // actual suspend begin
+                suspend_current_hart(&self); // pause and wait for machine level ipi
+                                             // mark current hart as started
+                let mut state_lock = self.state.lock();
+                state_lock
+                    .entry(hart_id)
+                    .insert(AtomicU8::new(HsmState::Started as u8));
+                drop(state_lock);
+                SbiRet::ok(0)
+            }
+            // Resuming from a non-retentive suspend state is relatively more involved and requires software
+            // to restore various hart registers and CSRs for all privilege modes.
+            SUSPEND_NON_RETENTIVE => {
+                // try to set current target hart state to stop pending
+                let hart_id = riscv::register::mhartid::read();
+                let mut state_lock = self.state.lock();
+                let current_state = state_lock
+                    .entry(hart_id)
+                    .or_insert(AtomicU8::new(HsmState::Stopped as u8))
+                    .compare_exchange(
+                        HsmState::Started as u8,
+                        HsmState::SuspendPending as u8,
+                        Ordering::AcqRel,
+                        Ordering::Acquire,
+                    );
+                // check current hart state
+                if current_state.is_err() {
+                    return SbiRet::failed(); // illegal state
+                }
+                drop(state_lock);
+                // retentive suspend
+                suspend_current_hart(&self);
+                // begin wake process
+                // send start command to runtime of current hart
+                let mut config_lock = self.last_command.lock();
+                config_lock
+                    .entry(hart_id)
+                    .insert(HsmCommand::Start(resume_addr, opaque));
+                drop(config_lock);
+                SbiRet {
+                    error: 0x233,
+                    value: 0x0,
+                } // unreachable, the runtime identifies start command and perform the hart resume
+            }
+            // There could be other platform specific suspend types; RustSBI-QEMU does not define any
+            // platform suspend types. It gives SBI return value as not supported.
+            _ => SbiRet::not_supported(),
+        }
+    }
+}
+
+const SUSPEND_RETENTIVE: u32 = 0x00000000;
+const SUSPEND_NON_RETENTIVE: u32 = 0x80000000;
+
+// Suspend current hart and record resume state when wake
+pub fn suspend_current_hart(hsm: &QemuHsm) {
+    use crate::clint::Clint;
+    use riscv::asm::wfi;
+    use riscv::register::{mhartid, mie, mip};
+    let hart_id = mhartid::read();
+    let clint = Clint::new(0x2000000 as *mut u8);
+    clint.clear_soft(hart_id); // Clear IPI
+    unsafe { mip::clear_msoft() }; // clear machine software interrupt flag
+    let prev_msoft = mie::read().msoft();
+    unsafe { mie::set_msoft() }; // Start listening for software interrupts
+                                 // mark current state as suspended
+    let mut state_lock = hsm.state.lock();
+    state_lock
+        .entry(hart_id)
+        .insert(AtomicU8::new(HsmState::Suspended as u8));
+    drop(state_lock);
+    // actual suspended process
+    loop {
+        unsafe { wfi() };
+        if mip::read().msoft() {
+            break;
+        }
+    }
+    // mark current state as resume pending
+    let mut state_lock = hsm.state.lock();
+    state_lock
+        .entry(hart_id)
+        .insert(AtomicU8::new(HsmState::ResumePending as u8));
+    drop(state_lock);
+    // resume
+    if !prev_msoft {
+        unsafe { mie::clear_msoft() }; // Stop listening for software interrupts
+    }
+    clint.clear_soft(hart_id); // Clear IPI
+}
+
+// Pause current hart, wake through inter-processor interrupt
+pub fn pause() {
+    use crate::clint::Clint;
+    use riscv::asm::wfi;
+    use riscv::register::{mhartid, mie, mip};
+    unsafe {
+        let hartid = mhartid::read();
+        let clint = Clint::new(0x2000000 as *mut u8);
+        clint.clear_soft(hartid); // Clear IPI
+        mip::clear_msoft(); // clear machine software interrupt flag
+        let prev_msoft = mie::read().msoft();
+        mie::set_msoft(); // Start listening for software interrupts
+        loop {
+            wfi();
+            if mip::read().msoft() {
+                break;
+            }
+        }
+        if !prev_msoft {
+            mie::clear_msoft(); // Stop listening for software interrupts
+        }
+        clint.clear_soft(hartid); // Clear IPI
+    }
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/qemu_pmu.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/qemu_pmu.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/runtime.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/runtime.rs
new file mode 100644
index 0000000000000000000000000000000000000000..67a57ac3d73b826b4313ead152a3b0621739cb19
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/runtime.rs
@@ -0,0 +1,280 @@
+use core::{
+    arch::asm,
+    ops::{Generator, GeneratorState},
+    pin::Pin,
+};
+use riscv::register::{
+    mcause::{self, Exception, Interrupt, Trap},
+    mstatus::{self, Mstatus, MPP},
+    mtval,
+    mtvec::{self, TrapMode},
+};
+
+pub fn init() {
+    let mut addr = from_supervisor_save as usize;
+    if addr & 0x2 != 0 {
+        addr += 0x2; // 必须对齐到4个字节
+    }
+    unsafe { mtvec::write(addr, TrapMode::Direct) };
+}
+
+pub struct Runtime {
+    context: SupervisorContext,
+}
+
+impl Runtime {
+    pub fn new_sbi_supervisor(supervisor_mepc: usize, a0: usize, a1: usize) -> Self {
+        let context: SupervisorContext = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+        let mut ans = Runtime { context };
+        ans.prepare_supervisor(supervisor_mepc);
+        ans.context.a0 = a0;
+        ans.context.a1 = a1;
+        ans
+    }
+
+    fn reset(&mut self) {
+        unsafe { mstatus::set_mpp(MPP::Supervisor) };
+        self.context.mstatus = mstatus::read();
+        self.context.machine_stack = 0x2333333366666666; // 将会被resume函数覆盖
+    }
+
+    // 在处理异常的时候,使用context_mut得到运行时当前用户的上下文,可以改变上下文的内容
+    pub fn context_mut(&mut self) -> &mut SupervisorContext {
+        &mut self.context
+    }
+
+    pub fn prepare_supervisor(&mut self, new_mepc: usize) {
+        self.reset();
+        self.context.mepc = new_mepc;
+    }
+}
+
+impl Generator for Runtime {
+    type Yield = MachineTrap;
+    type Return = ();
+    fn resume(mut self: Pin<&mut Self>, _arg: ()) -> GeneratorState<Self::Yield, Self::Return> {
+        unsafe { do_resume(&mut self.context as *mut _) };
+        let mtval = mtval::read();
+        let trap = match mcause::read().cause() {
+            Trap::Exception(Exception::SupervisorEnvCall) => MachineTrap::SbiCall(),
+            Trap::Exception(Exception::IllegalInstruction) => MachineTrap::IllegalInstruction(),
+            Trap::Interrupt(Interrupt::MachineTimer) => MachineTrap::MachineTimer(),
+            Trap::Interrupt(Interrupt::MachineSoft) => MachineTrap::MachineSoft(),
+            e => panic!(
+                "unhandled exception: {:?}! mtval: {:#x?}, ctx: {:#x?}",
+                e, mtval, self.context
+            ),
+        };
+        GeneratorState::Yielded(trap)
+    }
+}
+
+#[repr(C)]
+pub enum MachineTrap {
+    SbiCall(),
+    IllegalInstruction(),
+    MachineTimer(),
+    MachineSoft(),
+}
+
+#[derive(Debug)]
+#[repr(C)]
+pub struct SupervisorContext {
+    pub ra: usize, // 0
+    pub sp: usize,
+    pub gp: usize,
+    pub tp: usize,
+    pub t0: usize,
+    pub t1: usize,
+    pub t2: usize,
+    pub s0: usize,
+    pub s1: usize,
+    pub a0: usize,
+    pub a1: usize,
+    pub a2: usize,
+    pub a3: usize,
+    pub a4: usize,
+    pub a5: usize,
+    pub a6: usize,
+    pub a7: usize,
+    pub s2: usize,
+    pub s3: usize,
+    pub s4: usize,
+    pub s5: usize,
+    pub s6: usize,
+    pub s7: usize,
+    pub s8: usize,
+    pub s9: usize,
+    pub s10: usize,
+    pub s11: usize,
+    pub t3: usize,
+    pub t4: usize,
+    pub t5: usize,
+    pub t6: usize,            // 30
+    pub mstatus: Mstatus,     // 31
+    pub mepc: usize,          // 32
+    pub machine_stack: usize, // 33
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn do_resume(_supervisor_context: *mut SupervisorContext) {
+    asm!("j     {from_machine_save}", from_machine_save = sym from_machine_save, options(noreturn))
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn from_machine_save(_supervisor_context: *mut SupervisorContext) -> ! {
+    asm!( // sp:机器栈顶
+        "addi   sp, sp, -15*8", // sp:机器栈顶
+        // 进入函数之前,已经保存了调用者寄存器,应当保存被调用者寄存器
+        "sd     ra, 0*8(sp)
+        sd      gp, 1*8(sp)
+        sd      tp, 2*8(sp)
+        sd      s0, 3*8(sp)
+        sd      s1, 4*8(sp)
+        sd      s2, 5*8(sp)
+        sd      s3, 6*8(sp)
+        sd      s4, 7*8(sp)
+        sd      s5, 8*8(sp)
+        sd      s6, 9*8(sp)
+        sd      s7, 10*8(sp)
+        sd      s8, 11*8(sp)
+        sd      s9, 12*8(sp)
+        sd      s10, 13*8(sp)
+        sd      s11, 14*8(sp)", 
+        // a0:特权级上下文
+        "j      {to_supervisor_restore}",
+        to_supervisor_restore = sym to_supervisor_restore,
+        options(noreturn)
+    )
+}
+
+#[naked]
+#[link_section = ".text"]
+pub unsafe extern "C" fn to_supervisor_restore(_supervisor_context: *mut SupervisorContext) -> ! {
+    asm!(
+        // a0:特权级上下文
+        "sd     sp, 33*8(a0)", // 机器栈顶放进特权级上下文
+        "csrw   mscratch, a0", // 新mscratch:特权级上下文
+        // mscratch:特权级上下文
+        "mv     sp, a0", // 新sp:特权级上下文
+        "ld     t0, 31*8(sp)
+        ld      t1, 32*8(sp)
+        csrw    mstatus, t0
+        csrw    mepc, t1",
+        "ld     ra, 0*8(sp)
+        ld      gp, 2*8(sp)
+        ld      tp, 3*8(sp)
+        ld      t0, 4*8(sp)
+        ld      t1, 5*8(sp)
+        ld      t2, 6*8(sp)
+        ld      s0, 7*8(sp)
+        ld      s1, 8*8(sp)
+        ld      a0, 9*8(sp)
+        ld      a1, 10*8(sp)
+        ld      a2, 11*8(sp)
+        ld      a3, 12*8(sp)
+        ld      a4, 13*8(sp)
+        ld      a5, 14*8(sp)
+        ld      a6, 15*8(sp)
+        ld      a7, 16*8(sp)
+        ld      s2, 17*8(sp)
+        ld      s3, 18*8(sp)
+        ld      s4, 19*8(sp)
+        ld      s5, 20*8(sp)
+        ld      s6, 21*8(sp)
+        ld      s7, 22*8(sp)
+        ld      s8, 23*8(sp)
+        ld      s9, 24*8(sp)
+        ld     s10, 25*8(sp)
+        ld     s11, 26*8(sp)
+        ld      t3, 27*8(sp)
+        ld      t4, 28*8(sp)
+        ld      t5, 29*8(sp)
+        ld      t6, 30*8(sp)",
+        "ld     sp, 1*8(sp)", // 新sp:特权级栈
+        // sp:特权级栈, mscratch:特权级上下文
+        "mret",
+        options(noreturn)
+    )
+}
+
+// 中断开始
+
+#[naked]
+#[link_section = ".text"]
+pub unsafe extern "C" fn from_supervisor_save() -> ! {
+    asm!( // sp:特权级栈,mscratch:特权级上下文
+        ".p2align 2",
+        "csrrw  sp, mscratch, sp", // 新mscratch:特权级栈, 新sp:特权级上下文
+        "sd     ra, 0*8(sp)
+        sd      gp, 2*8(sp)
+        sd      tp, 3*8(sp)
+        sd      t0, 4*8(sp)
+        sd      t1, 5*8(sp)
+        sd      t2, 6*8(sp)
+        sd      s0, 7*8(sp)
+        sd      s1, 8*8(sp)
+        sd      a0, 9*8(sp)
+        sd      a1, 10*8(sp)
+        sd      a2, 11*8(sp)
+        sd      a3, 12*8(sp)
+        sd      a4, 13*8(sp)
+        sd      a5, 14*8(sp)
+        sd      a6, 15*8(sp)
+        sd      a7, 16*8(sp)
+        sd      s2, 17*8(sp)
+        sd      s3, 18*8(sp)
+        sd      s4, 19*8(sp)
+        sd      s5, 20*8(sp)
+        sd      s6, 21*8(sp)
+        sd      s7, 22*8(sp)
+        sd      s8, 23*8(sp)
+        sd      s9, 24*8(sp)
+        sd     s10, 25*8(sp)
+        sd     s11, 26*8(sp)
+        sd      t3, 27*8(sp)
+        sd      t4, 28*8(sp)
+        sd      t5, 29*8(sp)
+        sd      t6, 30*8(sp)",
+        "csrr   t0, mstatus
+        sd      t0, 31*8(sp)",
+        "csrr   t1, mepc
+        sd      t1, 32*8(sp)",
+        // mscratch:特权级栈,sp:特权级上下文
+        "csrrw  t2, mscratch, sp", // 新mscratch:特权级上下文,t2:特权级栈
+        "sd     t2, 1*8(sp)", // 保存特权级栈
+        "j      {to_machine_restore}",
+        to_machine_restore = sym to_machine_restore,
+        options(noreturn)
+    )
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn to_machine_restore() -> ! {
+    asm!(
+        // mscratch:特权级上下文
+        "csrr   sp, mscratch", // sp:特权级上下文
+        "ld     sp, 33*8(sp)", // sp:机器栈
+        "ld     ra, 0*8(sp)
+        ld      gp, 1*8(sp)
+        ld      tp, 2*8(sp)
+        ld      s0, 3*8(sp)
+        ld      s1, 4*8(sp)
+        ld      s2, 5*8(sp)
+        ld      s3, 6*8(sp)
+        ld      s4, 7*8(sp)
+        ld      s5, 8*8(sp)
+        ld      s6, 9*8(sp)
+        ld      s7, 10*8(sp)
+        ld      s8, 11*8(sp)
+        ld      s9, 12*8(sp)
+        ld      s10, 13*8(sp)
+        ld      s11, 14*8(sp)",
+        "addi   sp, sp, 15*8", // sp:机器栈顶
+        "jr     ra",           // 其实就是ret
+        options(noreturn)
+    )
+}
diff --git a/bootloader/rustsbi-qemu/rustsbi-qemu/src/test_device.rs b/bootloader/rustsbi-qemu/rustsbi-qemu/src/test_device.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d108e8bc4fa463dbf2df54b46cee6d92c8208478
--- /dev/null
+++ b/bootloader/rustsbi-qemu/rustsbi-qemu/src/test_device.rs
@@ -0,0 +1,45 @@
+// SiFive Test virtual device
+//
+// This is a test finisher memory mapped device used to exit simulation
+//
+// Ref: https://github.com/qemu/qemu/blob/master/hw/misc/sifive_test.c
+use rustsbi::{
+    reset::{
+        RESET_REASON_NO_REASON, RESET_REASON_SYSTEM_FAILURE, RESET_TYPE_COLD_REBOOT,
+        RESET_TYPE_SHUTDOWN, RESET_TYPE_WARM_REBOOT,
+    },
+    Reset, SbiRet,
+};
+
+// Zero sized structure for a static write-only device
+pub struct SiFiveTest;
+
+// Write these values to perform test device operations
+const TEST_FAIL: u32 = 0x3333;
+const TEST_PASS: u32 = 0x5555;
+const TEST_RESET: u32 = 0x7777;
+
+// On most QEMU host platforms, exit code for a general error is 1
+const QEMU_ERR_EXIT_CODE: u32 = 1;
+
+impl Reset for SiFiveTest {
+    fn system_reset(&self, reset_type: usize, reset_reason: usize) -> SbiRet {
+        const VIRT_TEST: *mut u32 = 0x10_0000 as *mut u32;
+        let value = match reset_type {
+            RESET_TYPE_SHUTDOWN => match reset_reason {
+                RESET_REASON_NO_REASON => TEST_PASS,
+                RESET_REASON_SYSTEM_FAILURE => TEST_FAIL | (QEMU_ERR_EXIT_CODE << 16),
+                // pass unknown reason from [2, 0xFFFF] to qemu return value output
+                // reason if reason <= 0xFFFF => TEST_FAIL | (((reason & 0xFFFF) as u32) << 16),
+                _ => return SbiRet::invalid_param(),
+            },
+            RESET_TYPE_COLD_REBOOT => TEST_RESET,
+            RESET_TYPE_WARM_REBOOT => TEST_RESET,
+            _ => return SbiRet::invalid_param(),
+        };
+        unsafe {
+            core::ptr::write_volatile(VIRT_TEST, value);
+        }
+        unreachable!()
+    }
+}
diff --git a/bootloader/rustsbi-qemu/test-kernel/.cargo/config.toml b/bootloader/rustsbi-qemu/test-kernel/.cargo/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..9b7792ee58da2ae453f4677a90e20869e880e9b8
--- /dev/null
+++ b/bootloader/rustsbi-qemu/test-kernel/.cargo/config.toml
@@ -0,0 +1,9 @@
+[target.riscv64imac-unknown-none-elf]
+rustflags = [
+    "-C", "link-arg=-Tlinker64.ld",
+]
+
+[target.riscv32imac-unknown-none-elf]
+rustflags = [
+    "-C", "link-arg=-Tlinker32.ld",
+]
diff --git a/bootloader/rustsbi-qemu/test-kernel/Cargo.toml b/bootloader/rustsbi-qemu/test-kernel/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..f0444b9560f84fc296e84b31bf4f55f91e6bef13
--- /dev/null
+++ b/bootloader/rustsbi-qemu/test-kernel/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "test-kernel"
+version = "0.1.0"
+authors = ["luojia65 <me@luojia.cc>"]
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+riscv = "0.6"
+spin = "0.7"
+lazy_static = { version = "1", features = ["spin_no_std"] }
+buddy_system_allocator = "0.8"
diff --git a/bootloader/rustsbi-qemu/test-kernel/build.rs b/bootloader/rustsbi-qemu/test-kernel/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..de1858b510a5994a0150aa08777e51888b2927c2
--- /dev/null
+++ b/bootloader/rustsbi-qemu/test-kernel/build.rs
@@ -0,0 +1,23 @@
+use std::env;
+use std::fs;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
+
+    // Put the linker script somewhere the linker can find it
+    fs::File::create(out_dir.join("linker64.ld"))
+        .unwrap()
+        .write_all(include_bytes!("src/linker64.ld"))
+        .unwrap();
+    fs::File::create(out_dir.join("linker32.ld"))
+        .unwrap()
+        .write_all(include_bytes!("src/linker32.ld"))
+        .unwrap();
+    println!("cargo:rustc-link-search={}", out_dir.display());
+
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rerun-if-changed=src/linker64.ld");
+    println!("cargo:rerun-if-changed=src/linker32.ld");
+}
diff --git a/bootloader/rustsbi-qemu/test-kernel/src/console.rs b/bootloader/rustsbi-qemu/test-kernel/src/console.rs
new file mode 100644
index 0000000000000000000000000000000000000000..922308d3dbf6431e56b226eefd3e00eca9a8119d
--- /dev/null
+++ b/bootloader/rustsbi-qemu/test-kernel/src/console.rs
@@ -0,0 +1,40 @@
+use crate::sbi::*;
+use core::fmt::{self, Write};
+use spin::Mutex;
+
+struct Stdout;
+
+impl Write for Stdout {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        let mut buffer = [0u8; 4];
+        for c in s.chars() {
+            for code_point in c.encode_utf8(&mut buffer).as_bytes().iter() {
+                console_putchar(*code_point as usize);
+            }
+        }
+        Ok(())
+    }
+}
+
+#[allow(unused)]
+pub fn print(args: fmt::Arguments) {
+    STDOUT.lock().write_fmt(args).unwrap();
+}
+
+lazy_static::lazy_static! {
+    static ref STDOUT: Mutex<Stdout> = Mutex::new(Stdout);
+}
+
+#[macro_export]
+macro_rules! print {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+    }
+}
+
+#[macro_export]
+macro_rules! println {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+    }
+}
diff --git a/bootloader/rustsbi-qemu/test-kernel/src/linker32.ld b/bootloader/rustsbi-qemu/test-kernel/src/linker32.ld
new file mode 100644
index 0000000000000000000000000000000000000000..8606bfae2b43cc3130394982012ce36a090b568f
--- /dev/null
+++ b/bootloader/rustsbi-qemu/test-kernel/src/linker32.ld
@@ -0,0 +1,38 @@
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x80400000;
+
+SECTIONS
+{
+    /* Load the kernel at this address: "." means the current address */
+    . = BASE_ADDRESS;
+    start = .;
+
+    .text : ALIGN(4K) {
+        _stext = .;
+        *(.text.entry)
+        *(.text .text.*)
+        _etext = .;
+    }
+
+    .rodata : ALIGN(4K) {
+        _srodata = .;
+        *(.rodata .rodata.*)
+        _erodata = .;
+    }
+
+    .data : ALIGN(4K) {
+        _sdata = .;
+        *(.data .data.*)
+        _edata = .;
+    }
+
+    .bss (NOLOAD) : ALIGN(4K)  {
+        _sbss = .;
+        *(.sbss .bss .bss.*)
+        _ebss = .;
+    }
+
+    PROVIDE(end = .);
+}
diff --git a/bootloader/rustsbi-qemu/test-kernel/src/linker64.ld b/bootloader/rustsbi-qemu/test-kernel/src/linker64.ld
new file mode 100644
index 0000000000000000000000000000000000000000..29621953d106dd1881c0553230072f3032a86a61
--- /dev/null
+++ b/bootloader/rustsbi-qemu/test-kernel/src/linker64.ld
@@ -0,0 +1,43 @@
+/* Copy from bbl-ucore : https://ring00.github.io/bbl-ucore      */
+
+/* Simple linker script for the ucore kernel.
+   See the GNU ld 'info' manual ("info ld") to learn the syntax. */
+
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x80200000;
+
+SECTIONS
+{
+    /* Load the kernel at this address: "." means the current address */
+    . = BASE_ADDRESS;
+    start = .;
+
+    .text : ALIGN(4K) {
+        _stext = .;
+        *(.text.entry)
+        *(.text .text.*)
+        _etext = .;
+    }
+
+    .rodata : ALIGN(4K) {
+        _srodata = .;
+        *(.rodata .rodata.*)
+        _erodata = .;
+    }
+
+    .data : ALIGN(4K) {
+        _sdata = .;
+        *(.data .data.*)
+        _edata = .;
+    }
+
+    .bss (NOLOAD) : ALIGN(4K)  {
+        _sbss = .;
+        *(.sbss .bss .bss.*)
+        _ebss = .;
+    }
+
+    PROVIDE(end = .);
+}
diff --git a/bootloader/rustsbi-qemu/test-kernel/src/main.rs b/bootloader/rustsbi-qemu/test-kernel/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..bc605f6ee5b15995515fb8fda70feea8e2977722
--- /dev/null
+++ b/bootloader/rustsbi-qemu/test-kernel/src/main.rs
@@ -0,0 +1,325 @@
+// A test kernel to test RustSBI function on all platforms
+#![feature(naked_functions, asm_sym, asm_const)]
+#![feature(default_alloc_error_handler)]
+#![no_std]
+#![no_main]
+
+use core::arch::asm;
+use core::panic::PanicInfo;
+
+use riscv::register::{scause::{self, Exception, Trap}, sepc, /*sie, sstatus, */stvec::{self, TrapMode}};
+use riscv::register::scause::Interrupt;
+
+#[macro_use]
+mod console;
+mod mm;
+mod sbi;
+
+pub extern "C" fn rust_main(hartid: usize, dtb_pa: usize) -> ! {
+    unsafe { asm!("mv tp, {}", in(reg) hartid, options(nomem, nostack)) }; // tp == hartid
+    let mut start_trap_addr = start_trap as usize;
+    if start_trap_addr & 0b10 != 0 {
+        start_trap_addr += 0b10;
+    }
+    if hartid == 0 {
+        // initialization
+        mm::init_heap();
+    }
+    if hartid == 0 {
+        println!(
+            "<< Test-kernel: Hart id = {}, DTB physical address = {:#x}",
+            hartid, dtb_pa
+        );
+        test_base_extension();
+        test_sbi_ins_emulation();
+        unsafe { stvec::write(start_trap_addr, TrapMode::Direct) };
+        println!(">> Test-kernel: Trigger illegal exception");
+        unsafe { asm!("csrw mcycle, x0") }; // mcycle cannot be written, this is always a 4-byte illegal instruction
+    }
+    if hartid == 0 {
+        let sbi_ret = sbi::hart_stop(3);
+        println!(">> Stop hart 3, return value {:?}", sbi_ret);
+        for i in 0..5 {
+            let sbi_ret = sbi::hart_get_status(i);
+            println!(">> Hart {} state return value: {:?}", i, sbi_ret);
+        }
+    } else if hartid == 1 {
+        let sbi_ret = sbi::hart_suspend(0x00000000, 0, 0);
+        println!(
+            ">> Start test for hart {}, retentive suspend return value {:?}",
+            hartid, sbi_ret
+        );
+    } else if hartid == 2 {
+        /* resume_addr should be physical address, and here pa == va */
+        let sbi_ret = sbi::hart_suspend(0x80000000, hart_2_resume as usize, 0x4567890a);
+        println!(">> Error for non-retentive suspend: {:?}", sbi_ret);
+        loop {}
+    } else if hartid == 4 {
+        // unsafe { stvec::write(start_trap_addr, TrapMode::Direct) };
+        // unsafe { sstatus::set_sie() };
+        // unsafe { sie::set_ssoft() };
+        // loop {} // wait for S-IPI
+        // println!(">> Test-kernel: SBI S-IPI delegation success");
+        // println!("<< Test-kernel: All hart SBI test SUCCESS, shutdown");
+        loop {} // todo: S-IPI
+    } else {
+        // hartid == 3
+        loop {}
+    }
+    if hartid == 0 {
+        println!(
+            "<< Test-kernel: test for hart {} success, wake another hart",
+            hartid
+        );
+        let sbi_ret = sbi::send_ipi(0b10, 0); // wake hart 1
+        println!(">> Wake hart 1, sbi return value {:?}", sbi_ret);
+        loop {} // wait for machine shutdown
+    } else if hartid == 1 {
+        // send software IPI to activate hart 2
+        let sbi_ret = sbi::send_ipi(0b1, 2);
+        println!(">> Wake hart 2, sbi return value {:?}", sbi_ret);
+        loop {}
+    } else {
+        // hartid == 2 || hartid == 3 || hartid == 4
+        unreachable!()
+    }
+}
+
+extern "C" fn hart_2_resume(hart_id: usize, param: usize) {
+    println!(
+        "<< The parameter passed to hart {} resume is: {:#x}",
+        hart_id, param
+    );
+    let param = 0x12345678;
+    println!(">> Start hart 3 with parameter {:#x}", param);
+    /* start_addr should be physical address, and here pa == va */
+    let sbi_ret = sbi::hart_start(3, hart_3_start as usize, param);
+    println!(">> SBI return value: {:?}", sbi_ret);
+    loop {} // wait for machine shutdown
+}
+
+extern "C" fn hart_3_start(hart_id: usize, param: usize) {
+    println!(
+        "<< The parameter passed to hart {} start is: {:#x}",
+        hart_id, param
+    );
+    println!("<< Test-kernel: All hart SBI test SUCCESS, shutdown");
+    sbi::shutdown()
+    // todo: S-IPI
+    // println!(">> Send IPI to hart 4, should delegate IPI to S-level");
+    // let _ = sbi::send_ipi(0b1, 4); // IPI to hart 4
+    // loop {} // wait for machine shutdown
+}
+
+fn test_base_extension() {
+    println!(">> Test-kernel: Testing base extension");
+    let base_version = sbi::probe_extension(sbi::EXTENSION_BASE);
+    if base_version == 0 {
+        println!("!! Test-kernel: no base extension probed; SBI call returned value '0'");
+        println!(
+            "!! Test-kernel: This SBI implementation may only have legacy extension implemented"
+        );
+        println!("!! Test-kernel: SBI test FAILED due to no base extension found");
+        sbi::shutdown()
+    }
+    println!("<< Test-kernel: Base extension version: {:x}", base_version);
+    println!(
+        "<< Test-kernel: SBI specification version: {:x}",
+        sbi::get_spec_version()
+    );
+    println!(
+        "<< Test-kernel: SBI implementation Id: {:x}",
+        sbi::get_sbi_impl_id()
+    );
+    println!(
+        "<< Test-kernel: SBI implementation version: {:x}",
+        sbi::get_sbi_impl_version()
+    );
+    println!(
+        "<< Test-kernel: Device mvendorid: {:x}",
+        sbi::get_mvendorid()
+    );
+    println!("<< Test-kernel: Device marchid: {:x}", sbi::get_marchid());
+    println!("<< Test-kernel: Device mimpid: {:x}", sbi::get_mimpid());
+}
+
+fn test_sbi_ins_emulation() {
+    println!(">> Test-kernel: Testing SBI instruction emulation");
+    let time_start = riscv::register::time::read64();
+    println!("<< Test-kernel: Current time: {:x}", time_start);
+    let time_end = riscv::register::time::read64();
+    if time_end > time_start {
+        println!("<< Test-kernel: Time after operation: {:x}", time_end);
+    } else {
+        println!("!! Test-kernel: SBI test FAILED due to incorrect time counter");
+        sbi::shutdown()
+    }
+}
+
+extern "C" fn rust_trap_exception(trap_frame: &mut TrapFrame) {
+    if trap_frame.tp == 0 {
+        let cause = scause::read().cause();
+        println!("<< Test-kernel: Value of scause: {:?}", cause);
+        if cause != Trap::Exception(Exception::IllegalInstruction) {
+            println!("!! Test-kernel: Wrong cause associated to illegal instruction");
+            sbi::shutdown()
+        }
+        println!("<< Test-kernel: Illegal exception delegate success");
+        sepc::write(sepc::read().wrapping_add(4));
+    } else if trap_frame.tp == 4 {
+        if scause::read().cause() != Trap::Interrupt(Interrupt::SupervisorSoft) {
+            println!("!! Test-kernel: Wrong cause associated to S-IPI delegation");
+            sbi::shutdown()
+        }
+    } else {
+        println!("!! Test-kernel: hart {} should not trap", trap_frame.tp);
+        println!("!! Test-kernel: SBI test FAILED for this hart should not trap");
+        sbi::shutdown()
+    }
+}
+
+#[cfg_attr(not(test), panic_handler)]
+#[allow(unused)]
+fn panic(info: &PanicInfo) -> ! {
+    println!("!! Test-kernel: {}", info);
+    println!("!! Test-kernel: SBI test FAILED due to panic");
+    sbi::reset(sbi::RESET_TYPE_SHUTDOWN, sbi::RESET_REASON_SYSTEM_FAILURE);
+    loop {}
+}
+
+const BOOT_STACK_SIZE: usize = 4096 * 4 * 8;
+
+static mut BOOT_STACK: [u8; BOOT_STACK_SIZE] = [0; BOOT_STACK_SIZE];
+
+#[naked]
+#[link_section = ".text.entry"]
+#[export_name = "_start"]
+unsafe extern "C" fn entry() -> ! {
+    asm!("
+    # 1. set sp
+    # sp = bootstack + (hartid + 1) * 0x10000
+    add     t0, a0, 1
+    slli    t0, t0, 14
+1:  auipc   sp, %pcrel_hi({boot_stack})
+    addi    sp, sp, %pcrel_lo(1b)
+    add     sp, sp, t0
+
+    # 2. jump to rust_main (absolute address)
+1:  auipc   t0, %pcrel_hi({rust_main})
+    addi    t0, t0, %pcrel_lo(1b)
+    jr      t0
+    ", 
+    boot_stack = sym BOOT_STACK,
+    rust_main = sym rust_main,
+    options(noreturn))
+}
+
+#[cfg(target_pointer_width = "128")]
+macro_rules! define_store_load {
+    () => {
+        ".altmacro
+        .macro STORE reg, offset
+            sq  \\reg, \\offset* {REGBYTES} (sp)
+        .endm
+        .macro LOAD reg, offset
+            lq  \\reg, \\offset* {REGBYTES} (sp)
+        .endm"
+    };
+}
+
+#[cfg(target_pointer_width = "64")]
+macro_rules! define_store_load {
+    () => {
+        ".altmacro
+        .macro STORE reg, offset
+            sd  \\reg, \\offset* {REGBYTES} (sp)
+        .endm
+        .macro LOAD reg, offset
+            ld  \\reg, \\offset* {REGBYTES} (sp)
+        .endm"
+    };
+}
+
+#[cfg(target_pointer_width = "32")]
+macro_rules! define_store_load {
+    () => {
+        ".altmacro
+        .macro STORE reg, offset
+            sw  \\reg, \\offset* {REGBYTES} (sp)
+        .endm
+        .macro LOAD reg, offset
+            lw  \\reg, \\offset* {REGBYTES} (sp)
+        .endm"
+    };
+}
+
+#[naked]
+#[link_section = ".text"]
+unsafe extern "C" fn start_trap() {
+    asm!(define_store_load!(), "
+    .p2align 2
+    addi    sp, sp, -17 * {REGBYTES}
+    STORE   ra, 0
+    STORE   t0, 1
+    STORE   t1, 2
+    STORE   t2, 3
+    STORE   t3, 4
+    STORE   t4, 5
+    STORE   t5, 6
+    STORE   t6, 7
+    STORE   a0, 8
+    STORE   a1, 9
+    STORE   a2, 10
+    STORE   a3, 11
+    STORE   a4, 12
+    STORE   a5, 13
+    STORE   a6, 14
+    STORE   a7, 15
+    STORE   tp, 16
+    mv      a0, sp
+    call    {rust_trap_exception}
+    LOAD    ra, 0
+    LOAD    t0, 1
+    LOAD    t1, 2
+    LOAD    t2, 3
+    LOAD    t3, 4
+    LOAD    t4, 5
+    LOAD    t5, 6
+    LOAD    t6, 7
+    LOAD    a0, 8
+    LOAD    a1, 9
+    LOAD    a2, 10
+    LOAD    a3, 11
+    LOAD    a4, 12
+    LOAD    a5, 13
+    LOAD    a6, 14
+    LOAD    a7, 15
+    LOAD    tp, 16
+    addi    sp, sp, 17 * {REGBYTES}
+    sret
+    ",
+    REGBYTES = const core::mem::size_of::<usize>(),
+    rust_trap_exception = sym rust_trap_exception,
+    options(noreturn))
+}
+
+#[repr(C)]
+struct TrapFrame {
+    ra: usize,
+    t0: usize,
+    t1: usize,
+    t2: usize,
+    t3: usize,
+    t4: usize,
+    t5: usize,
+    t6: usize,
+    a0: usize,
+    a1: usize,
+    a2: usize,
+    a3: usize,
+    a4: usize,
+    a5: usize,
+    a6: usize,
+    a7: usize,
+    tp: usize,
+}
\ No newline at end of file
diff --git a/bootloader/rustsbi-qemu/test-kernel/src/mm.rs b/bootloader/rustsbi-qemu/test-kernel/src/mm.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6aee4fd6b479c052aa9f20865ef250badfb57c78
--- /dev/null
+++ b/bootloader/rustsbi-qemu/test-kernel/src/mm.rs
@@ -0,0 +1,11 @@
+use buddy_system_allocator::LockedHeap;
+
+const HEAP_SIZE: usize = 64 * 1024; // 64KiB
+#[link_section = ".bss.uninit"]
+static mut HEAP_SPACE: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
+#[global_allocator]
+static HEAP: LockedHeap<32> = LockedHeap::empty();
+
+pub fn init_heap() {
+    unsafe { HEAP.lock().init(HEAP_SPACE.as_ptr() as usize, HEAP_SIZE) }
+}
diff --git a/bootloader/rustsbi-qemu/test-kernel/src/sbi.rs b/bootloader/rustsbi-qemu/test-kernel/src/sbi.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ece80c57afd5c0bf1f09b9a5373357eccf5d0aa5
--- /dev/null
+++ b/bootloader/rustsbi-qemu/test-kernel/src/sbi.rs
@@ -0,0 +1,291 @@
+#![allow(unused)]
+use core::arch::asm;
+use core::fmt;
+
+pub const EXTENSION_BASE: usize = 0x10;
+pub const EXTENSION_TIMER: usize = 0x54494D45;
+pub const EXTENSION_IPI: usize = 0x735049;
+pub const EXTENSION_RFENCE: usize = 0x52464E43;
+pub const EXTENSION_HSM: usize = 0x48534D;
+pub const EXTENSION_SRST: usize = 0x53525354;
+
+const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0;
+const FUNCTION_BASE_GET_SBI_IMPL_ID: usize = 0x1;
+const FUNCTION_BASE_GET_SBI_IMPL_VERSION: usize = 0x2;
+const FUNCTION_BASE_PROBE_EXTENSION: usize = 0x3;
+const FUNCTION_BASE_GET_MVENDORID: usize = 0x4;
+const FUNCTION_BASE_GET_MARCHID: usize = 0x5;
+const FUNCTION_BASE_GET_MIMPID: usize = 0x6;
+
+#[repr(C)]
+pub struct SbiRet {
+    /// Error number
+    pub error: usize,
+    /// Result value
+    pub value: usize,
+}
+
+const SBI_SUCCESS: usize = 0;
+const SBI_ERR_FAILED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-1));
+const SBI_ERR_NOT_SUPPORTED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-2));
+const SBI_ERR_INVALID_PARAM: usize = usize::from_ne_bytes(isize::to_ne_bytes(-3));
+const SBI_ERR_DENIED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-4));
+const SBI_ERR_INVALID_ADDRESS: usize = usize::from_ne_bytes(isize::to_ne_bytes(-5));
+const SBI_ERR_ALREADY_AVAILABLE: usize = usize::from_ne_bytes(isize::to_ne_bytes(-6));
+const SBI_ERR_ALREADY_STARTED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-7));
+const SBI_ERR_ALREADY_STOPPED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-8));
+
+impl fmt::Debug for SbiRet {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self.error {
+            SBI_SUCCESS => write!(f, "{:?}", self.value),
+            SBI_ERR_FAILED => write!(f, "<SBI call failed>"),
+            SBI_ERR_NOT_SUPPORTED => write!(f, "<SBI feature not supported>"),
+            SBI_ERR_INVALID_PARAM => write!(f, "<SBI invalid parameter>"),
+            SBI_ERR_DENIED => write!(f, "<SBI denied>"),
+            SBI_ERR_INVALID_ADDRESS => write!(f, "<SBI invalid address>"),
+            SBI_ERR_ALREADY_AVAILABLE => write!(f, "<SBI already available>"),
+            SBI_ERR_ALREADY_STARTED => write!(f, "<SBI already started>"),
+            SBI_ERR_ALREADY_STOPPED => write!(f, "<SBI already stopped>"),
+            unknown => write!(f, "[SBI Unknown error: {}]", unknown),
+        }
+    }
+}
+
+#[inline]
+pub fn get_spec_version() -> usize {
+    sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION).value
+}
+
+#[inline]
+pub fn get_sbi_impl_id() -> usize {
+    sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_ID).value
+}
+
+#[inline]
+pub fn get_sbi_impl_version() -> usize {
+    sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_SBI_IMPL_VERSION).value
+}
+
+#[inline]
+pub fn probe_extension(extension_id: usize) -> usize {
+    sbi_call_1(EXTENSION_BASE, FUNCTION_BASE_PROBE_EXTENSION, extension_id).value
+}
+
+#[inline]
+pub fn get_mvendorid() -> usize {
+    sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_MVENDORID).value
+}
+
+#[inline]
+pub fn get_marchid() -> usize {
+    sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_MARCHID).value
+}
+
+#[inline]
+pub fn get_mimpid() -> usize {
+    sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_MIMPID).value
+}
+
+const FUNCTION_SYSTEM_RESET: usize = 0x0;
+
+pub const RESET_TYPE_SHUTDOWN: usize = 0x0000_0000;
+pub const RESET_TYPE_COLD_REBOOT: usize = 0x0000_0001;
+pub const RESET_TYPE_WARM_REBOOT: usize = 0x0000_0002;
+pub const RESET_REASON_NO_REASON: usize = 0x0000_0000;
+pub const RESET_REASON_SYSTEM_FAILURE: usize = 0x0000_0001;
+
+#[inline]
+pub fn reset(reset_type: usize, reset_reason: usize) -> SbiRet {
+    sbi_call_2(
+        EXTENSION_SRST,
+        FUNCTION_SYSTEM_RESET,
+        reset_type,
+        reset_reason,
+    )
+}
+
+pub fn shutdown() -> ! {
+    sbi_call_2(
+        EXTENSION_SRST,
+        FUNCTION_SYSTEM_RESET,
+        RESET_TYPE_SHUTDOWN,
+        RESET_REASON_NO_REASON,
+    );
+    unreachable!()
+}
+
+#[inline(always)]
+fn sbi_call_legacy(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
+    let ret;
+    match () {
+        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
+        () => unsafe {
+            asm!(
+                "ecall",
+                in("a0") arg0, in("a1") arg1, in("a2") arg2,
+                in("a7") which,
+                lateout("a0") ret,
+            )
+        },
+        #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
+        () => {
+            drop((which, arg0, arg1, arg2));
+            unimplemented!("not RISC-V instruction set architecture")
+        }
+    };
+    ret
+}
+
+const SBI_SET_TIMER: usize = 0;
+const SBI_CONSOLE_PUTCHAR: usize = 1;
+const SBI_CONSOLE_GETCHAR: usize = 2;
+const SBI_CLEAR_IPI: usize = 3;
+const SBI_SEND_IPI: usize = 4;
+const SBI_REMOTE_FENCE_I: usize = 5;
+const SBI_REMOTE_SFENCE_VMA: usize = 6;
+const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
+const SBI_SHUTDOWN: usize = 8;
+
+pub fn console_putchar(c: usize) {
+    sbi_call_legacy(SBI_CONSOLE_PUTCHAR, c, 0, 0);
+}
+
+pub fn console_getchar() -> usize {
+    sbi_call_legacy(SBI_CONSOLE_GETCHAR, 0, 0, 0)
+}
+
+pub fn set_timer(time: usize) {
+    sbi_call_legacy(SBI_SET_TIMER, time, 0, 0);
+}
+
+const FUNCTION_IPI_SEND_IPI: usize = 0x0;
+
+pub fn send_ipi(hart_mask: usize, hart_mask_base: usize) -> SbiRet {
+    sbi_call_2(
+        EXTENSION_IPI,
+        FUNCTION_IPI_SEND_IPI,
+        hart_mask,
+        hart_mask_base,
+    )
+}
+
+const FUNCTION_HSM_HART_START: usize = 0x0;
+const FUNCTION_HSM_HART_STOP: usize = 0x1;
+const FUNCTION_HSM_HART_GET_STATUS: usize = 0x2;
+const FUNCTION_HSM_HART_SUSPEND: usize = 0x3;
+
+pub fn hart_start(hartid: usize, start_addr: usize, opaque: usize) -> SbiRet {
+    sbi_call_3(
+        EXTENSION_HSM,
+        FUNCTION_HSM_HART_START,
+        hartid,
+        start_addr,
+        opaque,
+    )
+}
+
+pub fn hart_stop(hartid: usize) -> SbiRet {
+    sbi_call_1(EXTENSION_HSM, FUNCTION_HSM_HART_STOP, hartid)
+}
+
+pub fn hart_get_status(hartid: usize) -> SbiRet {
+    sbi_call_1(EXTENSION_HSM, FUNCTION_HSM_HART_GET_STATUS, hartid)
+}
+
+pub fn hart_suspend(suspend_type: u32, resume_addr: usize, opaque: usize) -> SbiRet {
+    sbi_call_3(
+        EXTENSION_HSM,
+        FUNCTION_HSM_HART_SUSPEND,
+        suspend_type as usize,
+        resume_addr,
+        opaque,
+    )
+}
+
+#[inline(always)]
+fn sbi_call_0(extension: usize, function: usize) -> SbiRet {
+    let (error, value);
+    match () {
+        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
+        () => unsafe {
+            asm!(
+                "ecall",
+                in("a6") function, in("a7") extension,
+                lateout("a0") error, lateout("a1") value,
+            )
+        },
+        #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
+        () => {
+            drop((extension, function));
+            unimplemented!("not RISC-V instruction set architecture")
+        }
+    };
+    SbiRet { error, value }
+}
+
+#[inline(always)]
+fn sbi_call_1(extension: usize, function: usize, arg0: usize) -> SbiRet {
+    let (error, value);
+    match () {
+        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
+        () => unsafe {
+            asm!(
+                "ecall",
+                in("a0") arg0,
+                in("a6") function, in("a7") extension,
+                lateout("a0") error, lateout("a1") value,
+            )
+        },
+        #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
+        () => {
+            drop((extension, function, arg0));
+            unimplemented!("not RISC-V instruction set architecture")
+        }
+    };
+    SbiRet { error, value }
+}
+
+#[inline(always)]
+fn sbi_call_2(extension: usize, function: usize, arg0: usize, arg1: usize) -> SbiRet {
+    let (error, value);
+    match () {
+        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
+        () => unsafe {
+            asm!(
+                "ecall",
+                in("a0") arg0, in("a1") arg1,
+                in("a6") function, in("a7") extension,
+                lateout("a0") error, lateout("a1") value,
+            )
+        },
+        #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
+        () => {
+            drop((extension, function, arg0, arg1));
+            unimplemented!("not RISC-V instruction set architecture")
+        }
+    };
+    SbiRet { error, value }
+}
+
+#[inline(always)]
+fn sbi_call_3(extension: usize, function: usize, arg0: usize, arg1: usize, arg2: usize) -> SbiRet {
+    let (error, value);
+    match () {
+        #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
+        () => unsafe {
+            asm!(
+                "ecall",
+                in("a0") arg0, in("a1") arg1, in("a2") arg2,
+                in("a6") function, in("a7") extension,
+                lateout("a0") error, lateout("a1") value,
+            )
+        },
+        #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
+        () => {
+            drop((extension, function, arg0, arg1, arg2));
+            unimplemented!("not RISC-V instruction set architecture")
+        }
+    };
+    SbiRet { error, value }
+}
diff --git a/bootloader/rustsbi-qemu/xtask/Cargo.toml b/bootloader/rustsbi-qemu/xtask/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..205feff876339eda1174a26f1bead18820b47ac0
--- /dev/null
+++ b/bootloader/rustsbi-qemu/xtask/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "xtask"
+version = "0.1.0"
+authors = ["Luo Jia <me@luojia.cc>"]
+description = "interactive cargo runner"
+edition = "2018"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = "2"
+ctrlc = "3.2"
diff --git a/bootloader/rustsbi-qemu/xtask/src/main.rs b/bootloader/rustsbi-qemu/xtask/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d155acb9ad81a0f4a50d3fba1e985fc0877593df
--- /dev/null
+++ b/bootloader/rustsbi-qemu/xtask/src/main.rs
@@ -0,0 +1,355 @@
+#[macro_use]
+extern crate clap;
+
+use std::{
+    env,
+    path::{Path, PathBuf},
+    process::{self, Command, Stdio},
+};
+
+
+// 不要修改DEFAULT_TARGET;如果你需要编译到别的目标,请使用--target编译选项!
+const DEFAULT_TARGET: &'static str = "riscv64imac-unknown-none-elf";
+
+#[derive(Debug)]
+struct XtaskEnv {
+    compile_mode: CompileMode,
+}
+
+#[derive(Debug)]
+enum CompileMode {
+    Debug,
+    Release,
+}
+
+fn main() {
+    let matches = clap_app!(xtask =>
+        (version: crate_version!())
+        (author: crate_authors!())
+        (about: crate_description!())
+        (@subcommand make =>
+            (about: "Build project")
+            (@arg release: --release "Build artifacts in release mode, with optimizations")
+        )
+        (@subcommand asm =>
+            (about: "View asm code for project")
+            (@arg release: --release "Build artifacts in release mode, with optimizations")
+        )
+        (@subcommand size =>
+            (about: "View size for project")
+            (@arg release: --release "Build artifacts in release mode, with optimizations")
+        )
+        (@subcommand qemu =>
+            (about: "Run QEMU")
+            (@arg release: --release "Build artifacts in release mode, with optimizations")
+        )
+        (@subcommand debug =>
+            (about: "Debug with QEMU and GDB stub")
+        )
+        (@subcommand gdb =>
+            (about: "Run GDB debugger")
+        )
+    )
+    .get_matches();
+    let mut xtask_env = XtaskEnv {
+        compile_mode: CompileMode::Debug,
+    };
+    eprintln!("xtask: mode: {:?}", xtask_env.compile_mode);
+    if let Some(matches) = matches.subcommand_matches("make") {
+        if matches.is_present("release") {
+            xtask_env.compile_mode = CompileMode::Release;
+        }
+        xtask_build_sbi(&xtask_env);
+        xtask_binary_sbi(&xtask_env);
+        xtask_build_test_kernel(&xtask_env);
+        xtask_binary_test_kernel(&xtask_env);
+    } else if let Some(matches) = matches.subcommand_matches("qemu") {
+        if matches.is_present("release") {
+            xtask_env.compile_mode = CompileMode::Release;
+        }
+        xtask_build_sbi(&xtask_env);
+        xtask_binary_sbi(&xtask_env);
+        xtask_build_test_kernel(&xtask_env);
+        xtask_binary_test_kernel(&xtask_env);
+        xtask_qemu_run(&xtask_env);
+    } else if let Some(_matches) = matches.subcommand_matches("debug") {
+        xtask_build_sbi(&xtask_env);
+        xtask_binary_sbi(&xtask_env);
+        xtask_build_test_kernel(&xtask_env);
+        xtask_binary_test_kernel(&xtask_env);
+        xtask_qemu_debug(&xtask_env);
+    } else if let Some(matches) = matches.subcommand_matches("asm") {
+        if matches.is_present("release") {
+            xtask_env.compile_mode = CompileMode::Release;
+        }
+        xtask_build_sbi(&xtask_env);
+        xtask_asm_sbi(&xtask_env);
+    } else if let Some(matches) = matches.subcommand_matches("size") {
+        if matches.is_present("release") {
+            xtask_env.compile_mode = CompileMode::Release;
+        }
+        xtask_build_sbi(&xtask_env);
+        xtask_size_sbi(&xtask_env);
+    } else if let Some(_matches) = matches.subcommand_matches("gdb") {
+        xtask_gdb(&xtask_env);
+    } else {
+        eprintln!("Use `cargo qemu` to run, `cargo xtask --help` for help")
+    }
+}
+
+fn xtask_build_sbi(xtask_env: &XtaskEnv) {
+    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
+    let mut command = Command::new(cargo);
+    command.current_dir(project_root().join("rustsbi-qemu"));
+    command.arg("build");
+    match xtask_env.compile_mode {
+        CompileMode::Debug => {}
+        CompileMode::Release => {
+            command.arg("--release");
+        }
+    }
+    command.args(&["--package", "rustsbi-qemu"]);
+    command.args(&["--target", DEFAULT_TARGET]);
+    let status = command.status().unwrap();
+    if !status.success() {
+        println!("cargo build failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_build_test_kernel(xtask_env: &XtaskEnv) {
+    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
+    let mut command = Command::new(cargo);
+    command.current_dir(project_root().join("test-kernel"));
+    command.arg("build");
+    match xtask_env.compile_mode {
+        CompileMode::Debug => {}
+        CompileMode::Release => {
+            command.arg("--release");
+        }
+    }
+    command.args(&["--package", "test-kernel"]);
+    command.args(&["--target", DEFAULT_TARGET]);
+    let status = command.status().unwrap();
+    if !status.success() {
+        println!("cargo build failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_asm_sbi(xtask_env: &XtaskEnv) {
+    // @{{objdump}} -D {{test-kernel-elf}} | less
+    let objdump = check_tool("objdump").expect("Objdump tool not found");
+    Command::new(objdump)
+        .current_dir(dist_dir(xtask_env))
+        .arg("-d")
+        .arg("rustsbi-qemu")
+        .status()
+        .unwrap();
+}
+
+fn xtask_size_sbi(xtask_env: &XtaskEnv) {
+    // @{{size}} -A -x {{test-kernel-elf}}
+    let size = check_tool("size").expect("Size tool not found");
+    Command::new(size)
+        .current_dir(dist_dir(xtask_env))
+        .arg("-A")
+        .arg("-x")
+        .arg("rustsbi-qemu")
+        .status()
+        .unwrap();
+}
+
+fn xtask_binary_sbi(xtask_env: &XtaskEnv) {
+    /*
+        objdump := "riscv64-unknown-elf-objdump"
+    objcopy := "rust-objcopy --binary-architecture=riscv64"
+
+    build: firmware
+        @{{objcopy}} {{test-kernel-elf}} --strip-all -O binary {{test-kernel-bin}}
+     */
+    let objcopy = check_tool("objcopy").expect("Objcopy tool not found");
+    let status = Command::new(objcopy)
+        .current_dir(dist_dir(xtask_env))
+        .arg("rustsbi-qemu")
+        .arg("--binary-architecture=riscv64")
+        .arg("--strip-all")
+        .args(&["-O", "binary", "rustsbi-qemu.bin"])
+        .status()
+        .unwrap();
+
+    if !status.success() {
+        println!("objcopy binary failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_binary_test_kernel(xtask_env: &XtaskEnv) {
+    let objcopy = check_tool("objcopy").expect("Objcopy tool not found");
+    let status = Command::new(objcopy)
+        .current_dir(dist_dir(xtask_env))
+        .arg("test-kernel")
+        .arg("--binary-architecture=riscv64")
+        .arg("--strip-all")
+        .args(&["-O", "binary", "test-kernel.bin"])
+        .status()
+        .unwrap();
+
+    if !status.success() {
+        println!("objcopy binary failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_qemu_run(xtask_env: &XtaskEnv) {
+    /*
+    qemu: build
+    @qemu-system-riscv64 \
+            -machine virt \
+            -nographic \
+            -bios none \
+            -device loader,file={{rustsbi-bin}},addr=0x80000000 \
+            -device loader,file={{test-kernel-bin}},addr=0x80200000 \
+            -smp threads={{threads}}
+    */
+    let status = Command::new("qemu-system-riscv64")
+        .current_dir(dist_dir(xtask_env))
+        .args(&["-machine", "virt"])
+        .args(&["-bios", "rustsbi-qemu.bin"])
+        .args(&["-kernel", "test-kernel.bin"])
+        .args(&["-smp", "8"]) // 8 cores
+        .arg("-nographic")
+        .status()
+        .unwrap();
+
+    if !status.success() {
+        println!("qemu failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_qemu_debug(xtask_env: &XtaskEnv) {
+    let status = Command::new("qemu-system-riscv64")
+        .current_dir(dist_dir(xtask_env))
+        .args(&["-machine", "virt"])
+        .args(&["-bios", "rustsbi-qemu.bin"])
+        .args(&["-kernel", "test-kernel.bin"])
+        .args(&["-smp", "8"]) // 8 cores
+        .arg("-nographic")
+        .args(&["-gdb", "tcp::1234", "-S"])
+        .status()
+        .unwrap();
+
+    if !status.success() {
+        println!("qemu failed");
+        process::exit(1);
+    }
+}
+
+fn xtask_gdb(xtask_env: &XtaskEnv) {
+    let mut command = Command::new("riscv64-unknown-elf-gdb");
+    command.current_dir(dist_dir(xtask_env));
+    command.args(&["--eval-command", "file rustsbi-qemu"]);
+    command.args(&["--eval-command", "target remote localhost:1234"]);
+    command.arg("-q");
+
+    ctrlc::set_handler(move || {
+        // when ctrl-c, don't exit gdb
+    }).expect("disable Ctrl-C exit");
+
+    let status = command.status().expect("run program");
+    if !status.success() {
+        println!("debug failed");
+        process::exit(1);
+    }
+}
+
+fn project_root() -> PathBuf {
+    Path::new(&env!("CARGO_MANIFEST_DIR"))
+        .ancestors()
+        .nth(1)
+        .unwrap()
+        .to_path_buf()
+}
+
+fn dist_dir(xtask_env: &XtaskEnv) -> PathBuf {
+    let mut path_buf = project_root().join("target").join(DEFAULT_TARGET);
+    path_buf = match xtask_env.compile_mode {
+        CompileMode::Debug => path_buf.join("debug"),
+        CompileMode::Release => path_buf.join("release"),
+    };
+    path_buf
+}
+
+fn check_tool<S: AsRef<str>>(tool: S) -> Option<String> {
+    // check the `rust-x` tool
+    if let Ok(status) = Command::new(format!("rust-{}", tool.as_ref()))
+        .arg("--version")
+        .stdout(Stdio::null())
+        .status()
+    {
+        if status.success() {
+            return Some(format!("rust-{}", tool.as_ref()));
+        }
+    }
+    // check the `riscv64-linux-gnu-x` tool
+    if let Ok(status) = Command::new(format!("riscv64-linux-gnu-{}", tool.as_ref()))
+        .arg("--version")
+        .stdout(Stdio::null())
+        .status()
+    {
+        if status.success() {
+            return Some(format!("riscv64-linux-gnu-{}", tool.as_ref()));
+        }
+    }
+    // check `riscv64-unknown-elf-x` tool
+    if let Ok(status) = Command::new(format!("riscv64-unknown-elf-{}", tool.as_ref()))
+        .arg("--version")
+        .stdout(Stdio::null())
+        .status()
+    {
+        if status.success() {
+            return Some(format!("riscv64-unknown-elf-{}", tool.as_ref()));
+        }
+    }
+    println!(
+        "
+No binutils found, try install using:
+
+    rustup component add llvm-tools-preview
+    cargo install cargo-binutils"
+    );
+    return None;
+}
+
+#[test]
+fn run_test_kernel() {
+    let xtask_env = XtaskEnv {
+        compile_mode: CompileMode::Debug,
+    };
+    xtask_build_sbi(&xtask_env);
+    xtask_binary_sbi(&xtask_env);
+    xtask_build_test_kernel(&xtask_env);
+    xtask_binary_test_kernel(&xtask_env);
+    let child = Command::new("qemu-system-riscv64")
+        .current_dir(dist_dir(&xtask_env))
+        .args(&["-machine", "virt"])
+        .args(&["-bios", "rustsbi-qemu.bin"])
+        .args(&["-kernel", "test-kernel.bin"])
+        .args(&["-smp", "8"]) // 8 cores
+        .arg("-nographic")
+        .stdout(process::Stdio::piped())
+        .spawn()
+        .expect("spawn child process");
+    let output = child.wait_with_output().expect("wait on child");
+    let string = String::from_utf8(output.stdout).expect("utf-8 output");
+    println!("{}", string);
+    let last_line = string.lines().last();
+    assert!(last_line.is_some(), "some outuput");
+    assert_eq!(
+        last_line.unwrap(),
+        "<< Test-kernel: All hart SBI test SUCCESS, shutdown",
+        "success output"
+    );
+    assert!(output.status.success(), "success exit code");
+}