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"); +}