├── .cargo └── config.toml ├── .github └── workflows │ ├── release.yml │ └── workflow.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── bench-kernel ├── Cargo.toml ├── build.rs └── src │ └── main.rs ├── hsm-cell ├── Cargo.toml └── src │ └── lib.rs ├── rust-toolchain.toml ├── rustsbi-qemu ├── Cargo.toml ├── build.rs └── src │ ├── clint.rs │ ├── dbcn.rs │ ├── device_tree.rs │ ├── hart_csr_utils.rs │ ├── main.rs │ ├── qemu_test.rs │ ├── riscv_spec.rs │ ├── trap_stack.rs │ ├── trap_vec.rs │ └── uart16550.rs ├── test-kernel ├── Cargo.toml ├── build.rs └── src │ └── main.rs └── xtask ├── Cargo.toml └── src └── main.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --release --" 3 | make = "xtask make" 4 | asm = "xtask asm" 5 | qemu = "xtask qemu" 6 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | branches: 5 | - main 6 | tags: 7 | - v*.*.* 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | matrix: 14 | mode: [release, debug] 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Cache Dependencies 20 | uses: Swatinem/rust-cache@v2 21 | with: 22 | key: ${{ matrix.mode }} 23 | 24 | - name: Build (release mode) 25 | if: matrix.mode == 'release' 26 | run: cargo make 27 | 28 | - name: Build (debug mode) 29 | if: matrix.mode == 'debug' 30 | run: cargo make --debug 31 | 32 | - name: Compress and Rename Artifact 33 | run: | 34 | gzip -c target/riscv64imac-unknown-none-elf/${{ matrix.mode }}/rustsbi-qemu.bin > rustsbi-qemu-${{ matrix.mode }}.gz 35 | zip rustsbi-qemu-${{ matrix.mode }}.zip target/riscv64imac-unknown-none-elf/${{ matrix.mode }}/rustsbi-qemu.bin 36 | 37 | - name: Upload Artifacts 38 | uses: actions/upload-artifact@v2 39 | with: 40 | name: rustsbi-qemu-${{ matrix.mode }} 41 | path: | 42 | rustsbi-qemu-${{ matrix.mode }}.gz 43 | rustsbi-qemu-${{ matrix.mode }}.zip 44 | 45 | release: 46 | runs-on: ubuntu-latest 47 | needs: build 48 | steps: 49 | - uses: actions/checkout@v3 50 | 51 | - name: Download Artifacts 52 | uses: actions/download-artifact@v2 53 | with: 54 | path: artifacts 55 | 56 | - name: List Artifacts 57 | run: ls -R ./artifacts 58 | 59 | - name: Set current date as environment variable 60 | run: echo "CURRENT_DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV 61 | 62 | - name: Check if pre-release 63 | id: check 64 | run: | 65 | if [[ $GITHUB_REF =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 66 | echo "PRE_RELEASE=false" >> $GITHUB_OUTPUT 67 | else 68 | echo "PRE_RELEASE=true" >> $GITHUB_OUTPUT 69 | fi 70 | 71 | - name: Get Changelog 72 | id: changelog-reader 73 | uses: mindsers/changelog-reader-action@v2.0.0 74 | with: 75 | version: ${{ (steps.check.outputs.PRE_RELEASE && 'Unreleased') || github.ref_name }} 76 | 77 | - name: Update Unreleased Tag 78 | if: ${{ steps.changelog-reader.outputs.version == 'Unreleased' }} 79 | uses: richardsimko/update-tag@v1 80 | with: 81 | tag_name: Unreleased 82 | env: 83 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 84 | 85 | - name: Create Release 86 | uses: softprops/action-gh-release@v1 87 | with: 88 | tag_name: ${{ steps.changelog-reader.outputs.version }} 89 | name: ${{ (github.ref_type == 'tag' && steps.changelog-reader.outputs.version) || format('Prereleased {0}', env.CURRENT_DATE) }} 90 | body: ${{ steps.changelog-reader.outputs.changes }} 91 | prerelease: ${{ steps.changelog-reader.outputs.status == 'unreleased' }} 92 | target_commitish: ${{ github.sha }} 93 | files: | 94 | artifacts/**/* 95 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-20.04 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Check format 15 | run: cargo fmt --all -- --check 16 | 17 | - name: Clippy 18 | run: cargo clippy 19 | 20 | test: 21 | needs: build 22 | runs-on: ubuntu-20.04 23 | strategy: 24 | matrix: 25 | base-img: [slim] 26 | qemu-version: [6.2.0, 7.0.0, 7.1.0, 7.2.0, 8.0.5, 8.1.5, 8.2.2] 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - name: Login to GitHub Container Registry 31 | uses: docker/login-action@v2 32 | with: 33 | registry: ghcr.io 34 | username: ${{ github.repository_owner }} 35 | password: ${{ secrets.GITHUB_TOKEN }} 36 | 37 | - name: Pull image 38 | run: docker pull ghcr.io/rustsbi/qemu:${{ matrix.base-img }}-${{ matrix.qemu-version }}-rv64 39 | 40 | - name: Test 41 | uses: addnab/docker-run-action@v3 42 | with: 43 | image: ghcr.io/rustsbi/qemu:${{ matrix.base-img }}-${{ matrix.qemu-version }}-rv64 44 | options: -v ${{ github.workspace }}:/work 45 | run: | 46 | rustup component add llvm-tools-preview 47 | rustup target add riscv64imac-unknown-none-elf 48 | cd /work 49 | cargo test 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.*/* 2 | !/.github/* 3 | !/.cargo/* 4 | !/.vscode/settings.json 5 | 6 | /target 7 | /*.asm 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Prevent "can't find crate for `test`" error on no_std 3 | // Ref: https://github.com/rust-lang/vscode-rust/issues/729 4 | "rust-analyzer.cargo.target": "riscv64imac-unknown-none-elf", 5 | "rust-analyzer.checkOnSave.allTargets": false, 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | 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). 6 | 7 | ## [Unreleased] 8 | 9 | ### Added 10 | 11 | - Hint spin loop in hart state monitor module 12 | - Add crate *bench-kernel* to workspace for sbi call bench 13 | - Add SBI DBCN extension support 14 | 15 | ### Modified 16 | 17 | - Use derive macro based RustSBI interface, with separate functions for legacy stdio 18 | - Update rustsbi to version 0.4.0-alpha.1 19 | - Update sbi-spec to version 0.0.7 20 | - Update sbi-rt to version 0.0.3 21 | - Update sbi-testing to version 0.0.3-alpha.2 22 | - Use crate *fast-trap* version 0.0.1 in rustsbi-qemu for trap handling 23 | - Use crate *rcore-console* version 0.0.0 in rustsbi-qemu and test-kernel for `print!` and `println!` 24 | - Use crate *aclint* version 0.0.0 in rustsbi-qemu for aclint structs 25 | - Use crate *os-xtask-utils* version 0.0.0 in xtask builder 26 | - Use crate *sifive-test-device* version 0.0.0 instead of qemu-exit 27 | - Use crate *uart16550* version 0.0.1 for 16550 definition 28 | - Use `wfi` for suspend and stop without enable mie 29 | - Remove crate *once_cell* from dependencies 30 | 31 | ### Fixed 32 | 33 | - Xtask will now print error when system does not have qemu installed 34 | - Fix dtb parsing for qemu 7.2 35 | 36 | ## [0.1.1] - 2022-03-23 37 | 38 | ### Added 39 | 40 | - Adapts to RustSBI version 0.2.2, RISC-V SBI version 1.0.0 ratified 41 | - Handle possible failure of deref virtual address by machine trap detection 42 | 43 | ### Modified 44 | 45 | - Use Rust Edition 2021 46 | - Modify test kernel message 47 | 48 | ## [0.1.0] - 2022-02-13 49 | 50 | ### Added 51 | 52 | - Adapts to RustSBI version 0.2.0 53 | - Implement SBI non-retentive resume procedure 54 | - PMP updates, use stabilized core::arch::asm! macro, thanks to @wyfcyx 55 | - Fixes on usage of CLINT peripheral, thanks to @duskmoon314 56 | - Numerous fixes to HSM module implementation, more documents 57 | 58 | [Unreleased]: https://github.com/rustsbi/rustsbi-qemu/compare/v0.1.1...HEAD 59 | [0.1.1]: https://github.com/rustsbi/rustsbi-qemu/compare/v0.1.0...v0.1.1 60 | [0.1.0]: https://github.com/rustsbi/rustsbi-qemu/releases/tag/v0.1.0 61 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aclint" 7 | version = "0.0.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a01ba40421eca6c4f1afcedd8465fba6d9e5ef8e0e13060d0141e4cded4ab4a" 10 | 11 | [[package]] 12 | name = "anstream" 13 | version = "0.6.11" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" 16 | dependencies = [ 17 | "anstyle", 18 | "anstyle-parse", 19 | "anstyle-query", 20 | "anstyle-wincon", 21 | "colorchoice", 22 | "utf8parse", 23 | ] 24 | 25 | [[package]] 26 | name = "anstyle" 27 | version = "1.0.6" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" 30 | 31 | [[package]] 32 | name = "anstyle-parse" 33 | version = "0.2.3" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 36 | dependencies = [ 37 | "utf8parse", 38 | ] 39 | 40 | [[package]] 41 | name = "anstyle-query" 42 | version = "1.0.2" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 45 | dependencies = [ 46 | "windows-sys", 47 | ] 48 | 49 | [[package]] 50 | name = "anstyle-wincon" 51 | version = "3.0.2" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 54 | dependencies = [ 55 | "anstyle", 56 | "windows-sys", 57 | ] 58 | 59 | [[package]] 60 | name = "autocfg" 61 | version = "1.1.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 64 | 65 | [[package]] 66 | name = "bench-kernel" 67 | version = "0.0.1" 68 | dependencies = [ 69 | "rcore-console", 70 | "riscv 0.10.1", 71 | "sbi-rt", 72 | "uart16550", 73 | ] 74 | 75 | [[package]] 76 | name = "bit_field" 77 | version = "0.10.2" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 80 | 81 | [[package]] 82 | name = "clap" 83 | version = "4.5.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" 86 | dependencies = [ 87 | "clap_builder", 88 | "clap_derive", 89 | ] 90 | 91 | [[package]] 92 | name = "clap_builder" 93 | version = "4.5.0" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" 96 | dependencies = [ 97 | "anstream", 98 | "anstyle", 99 | "clap_lex", 100 | "strsim", 101 | ] 102 | 103 | [[package]] 104 | name = "clap_derive" 105 | version = "4.5.0" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" 108 | dependencies = [ 109 | "heck", 110 | "proc-macro2", 111 | "quote", 112 | "syn", 113 | ] 114 | 115 | [[package]] 116 | name = "clap_lex" 117 | version = "0.7.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 120 | 121 | [[package]] 122 | name = "colorchoice" 123 | version = "1.0.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 126 | 127 | [[package]] 128 | name = "critical-section" 129 | version = "1.1.2" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" 132 | 133 | [[package]] 134 | name = "dtb-walker" 135 | version = "0.2.0-alpha.3" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "9404d41caa1aa659f7be44d5a902e318c0672900822fe9ca41d9e38c14b52332" 138 | 139 | [[package]] 140 | name = "embedded-hal" 141 | version = "0.2.7" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 144 | dependencies = [ 145 | "nb 0.1.3", 146 | "void", 147 | ] 148 | 149 | [[package]] 150 | name = "embedded-hal" 151 | version = "1.0.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 154 | 155 | [[package]] 156 | name = "fast-trap" 157 | version = "0.0.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "7fbe69badc2e0dc98ad2787648fa140b5772d24b49e9a6b180a67e1348f7544c" 160 | 161 | [[package]] 162 | name = "heck" 163 | version = "0.4.1" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 166 | 167 | [[package]] 168 | name = "hsm-cell" 169 | version = "0.1.0" 170 | dependencies = [ 171 | "sbi-spec", 172 | ] 173 | 174 | [[package]] 175 | name = "lock_api" 176 | version = "0.4.11" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 179 | dependencies = [ 180 | "autocfg", 181 | "scopeguard", 182 | ] 183 | 184 | [[package]] 185 | name = "log" 186 | version = "0.4.20" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 189 | 190 | [[package]] 191 | name = "nb" 192 | version = "0.1.3" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 195 | dependencies = [ 196 | "nb 1.1.0", 197 | ] 198 | 199 | [[package]] 200 | name = "nb" 201 | version = "1.1.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 204 | 205 | [[package]] 206 | name = "once_cell" 207 | version = "1.19.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 210 | 211 | [[package]] 212 | name = "os-xtask-utils" 213 | version = "0.0.0" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "f5e9cccd7bf690840399e09e283b7f5aa219886dc5770c083beee2adca0ffabe" 216 | dependencies = [ 217 | "once_cell", 218 | ] 219 | 220 | [[package]] 221 | name = "proc-macro2" 222 | version = "1.0.88" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" 225 | dependencies = [ 226 | "unicode-ident", 227 | ] 228 | 229 | [[package]] 230 | name = "quote" 231 | version = "1.0.35" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 234 | dependencies = [ 235 | "proc-macro2", 236 | ] 237 | 238 | [[package]] 239 | name = "rcore-console" 240 | version = "0.0.0" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "63aae49a6d2e6fd69821507a979b5871e4c47dc3abc9066347fa5c4a51a73dd6" 243 | dependencies = [ 244 | "log", 245 | "spin", 246 | ] 247 | 248 | [[package]] 249 | name = "riscv" 250 | version = "0.10.1" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "aa3145d2fae3778b1e31ec2e827b228bdc6abd9b74bb5705ba46dcb82069bc4f" 253 | dependencies = [ 254 | "bit_field", 255 | "critical-section", 256 | "embedded-hal 0.2.7", 257 | ] 258 | 259 | [[package]] 260 | name = "riscv" 261 | version = "0.11.1" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" 264 | dependencies = [ 265 | "critical-section", 266 | "embedded-hal 1.0.0", 267 | ] 268 | 269 | [[package]] 270 | name = "rustsbi" 271 | version = "0.4.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "44c13763120794ed11d64bac885fb31d384ae385c3287b0697711b97affbf8ab" 274 | dependencies = [ 275 | "riscv 0.11.1", 276 | "rustsbi-macros", 277 | "sbi-spec", 278 | ] 279 | 280 | [[package]] 281 | name = "rustsbi-macros" 282 | version = "0.0.2" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "a71347da9582cc6b6f3652c7d2c06516c9555690b3738ecdff7e84297f4e17fc" 285 | dependencies = [ 286 | "proc-macro2", 287 | "quote", 288 | "syn", 289 | ] 290 | 291 | [[package]] 292 | name = "rustsbi-qemu" 293 | version = "0.2.0-alpha.3" 294 | dependencies = [ 295 | "aclint", 296 | "dtb-walker", 297 | "fast-trap", 298 | "hsm-cell", 299 | "rcore-console", 300 | "riscv 0.10.1", 301 | "rustsbi", 302 | "sbi-spec", 303 | "sifive-test-device", 304 | "spin", 305 | "uart16550", 306 | ] 307 | 308 | [[package]] 309 | name = "sbi-rt" 310 | version = "0.0.3" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "7fbaa69be1eedc61c426e6d489b2260482e928b465360576900d52d496a58bd0" 313 | dependencies = [ 314 | "sbi-spec", 315 | ] 316 | 317 | [[package]] 318 | name = "sbi-spec" 319 | version = "0.0.7" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "e6e36312fb5ddc10d08ecdc65187402baba4ac34585cb9d1b78522ae2358d890" 322 | 323 | [[package]] 324 | name = "sbi-testing" 325 | version = "0.0.3-alpha.2" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "135c0f1ce07ede77a7e1c3daff35d20d37b54fd1037ac02ab9595c231518531e" 328 | dependencies = [ 329 | "log", 330 | "riscv 0.11.1", 331 | "sbi-rt", 332 | "sbi-spec", 333 | ] 334 | 335 | [[package]] 336 | name = "scopeguard" 337 | version = "1.2.0" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 340 | 341 | [[package]] 342 | name = "sifive-test-device" 343 | version = "0.0.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "ba50a6fd7cb5cdb2645fb93fb2bbae7d8d78390677a889bdcfaf13c3d29286d0" 346 | 347 | [[package]] 348 | name = "spin" 349 | version = "0.9.8" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 352 | dependencies = [ 353 | "lock_api", 354 | ] 355 | 356 | [[package]] 357 | name = "strsim" 358 | version = "0.11.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" 361 | 362 | [[package]] 363 | name = "syn" 364 | version = "2.0.48" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 367 | dependencies = [ 368 | "proc-macro2", 369 | "quote", 370 | "unicode-ident", 371 | ] 372 | 373 | [[package]] 374 | name = "test-kernel" 375 | version = "0.2.0" 376 | dependencies = [ 377 | "dtb-walker", 378 | "log", 379 | "rcore-console", 380 | "riscv 0.10.1", 381 | "sbi-testing", 382 | "spin", 383 | "uart16550", 384 | ] 385 | 386 | [[package]] 387 | name = "uart16550" 388 | version = "0.0.1" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "939f6f9ccad815fe3efca8fd06f2ec1620c0387fb1bca2b231b61ce710bffb9b" 391 | 392 | [[package]] 393 | name = "unicode-ident" 394 | version = "1.0.12" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 397 | 398 | [[package]] 399 | name = "utf8parse" 400 | version = "0.2.1" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 403 | 404 | [[package]] 405 | name = "void" 406 | version = "1.0.2" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 409 | 410 | [[package]] 411 | name = "windows-sys" 412 | version = "0.52.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 415 | dependencies = [ 416 | "windows-targets", 417 | ] 418 | 419 | [[package]] 420 | name = "windows-targets" 421 | version = "0.52.0" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 424 | dependencies = [ 425 | "windows_aarch64_gnullvm", 426 | "windows_aarch64_msvc", 427 | "windows_i686_gnu", 428 | "windows_i686_msvc", 429 | "windows_x86_64_gnu", 430 | "windows_x86_64_gnullvm", 431 | "windows_x86_64_msvc", 432 | ] 433 | 434 | [[package]] 435 | name = "windows_aarch64_gnullvm" 436 | version = "0.52.0" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 439 | 440 | [[package]] 441 | name = "windows_aarch64_msvc" 442 | version = "0.52.0" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 445 | 446 | [[package]] 447 | name = "windows_i686_gnu" 448 | version = "0.52.0" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 451 | 452 | [[package]] 453 | name = "windows_i686_msvc" 454 | version = "0.52.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 457 | 458 | [[package]] 459 | name = "windows_x86_64_gnu" 460 | version = "0.52.0" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 463 | 464 | [[package]] 465 | name = "windows_x86_64_gnullvm" 466 | version = "0.52.0" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 469 | 470 | [[package]] 471 | name = "windows_x86_64_msvc" 472 | version = "0.52.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" 475 | 476 | [[package]] 477 | name = "xtask" 478 | version = "0.2.0" 479 | dependencies = [ 480 | "clap", 481 | "os-xtask-utils", 482 | ] 483 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["rustsbi-qemu", "hsm-cell", "test-kernel", "bench-kernel", "xtask"] 3 | default-members = ["xtask"] 4 | resolver = "2" 5 | 6 | [profile.release] 7 | opt-level = 3 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 木兰宽松许可证, 第2版 2 | 3 | 2020年1月 http://license.coscl.org.cn/MulanPSL2 4 | 5 | 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 6 | 7 | 0. 定义 8 | 9 | “软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 10 | 11 | “贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 12 | 13 | “贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 14 | 15 | “法人实体” 是指提交贡献的机构及其“关联实体”。 16 | 17 | “关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 18 | 19 | 1. 授予版权许可 20 | 21 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 22 | 23 | 2. 授予专利许可 24 | 25 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 26 | 27 | 3. 无商标许可 28 | 29 | “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 30 | 31 | 4. 分发限制 32 | 33 | 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 34 | 35 | 5. 免责声明与责任限制 36 | 37 | “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 38 | 39 | 6. 语言 40 | 41 | “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 42 | 43 | 条款结束 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QEMU support from RustSBI 2 | 3 | RustSBI is designed as a library to craft a bootable binary or ELF file. However, QEMU provides us a way to load ELF 4 | file and implement simple SBI directly, thus RustSBI provides a bootable ELF file for this platform. 5 | 6 | ## Try it out! 7 | 8 | To prepare for environment, you should have Rust compiler and QEMU installed. 9 | You may install Rust by [rustup](https://rustup.rs) or using vendor provided rustc and cargo packages. 10 | To install QEMU, your may need to use package manager (e.g. apt, dnf etc.) from system distribution 11 | to get a proper QEMU software package. 12 | 13 | After environment prepared, compile and run with: 14 | 15 | ```shell 16 | cargo qemu 17 | ``` 18 | 19 | When running `cargo qemu`, the test kernel will build and run. Expected output should be: 20 | 21 | ```plaintext 22 | [rustsbi] RustSBI version 0.4.0-alpha.1, adapting to RISC-V SBI v1.0.0 23 | .______ __ __ _______.___________. _______..______ __ 24 | | _ \ | | | | / | | / || _ \ | | 25 | | |_) | | | | | | (----`---| |----`| (----`| |_) || | 26 | | / | | | | \ \ | | \ \ | _ < | | 27 | | |\ \----.| `--' |.----) | | | .----) | | |_) || | 28 | | _| `._____| \______/ |_______/ |__| |_______/ |______/ |__| 29 | [rustsbi] Implementation : RustSBI-QEMU Version 0.2.0-alpha.2 30 | [rustsbi] Platform Name : riscv-virtio,qemu 31 | [rustsbi] Platform SMP : 8 32 | [rustsbi] Platform Memory : 0x80000000..0x88000000 33 | [rustsbi] Boot HART : 6 34 | [rustsbi] Device Tree Region : 0x87e00000..0x87e01a8e 35 | [rustsbi] Firmware Address : 0x80000000 36 | [rustsbi] Supervisor Address : 0x80200000 37 | [rustsbi] pmp01: 0x00000000..0x80000000 (-wr) 38 | [rustsbi] pmp02: 0x80000000..0x80200000 (---) 39 | [rustsbi] pmp03: 0x80200000..0x88000000 (xwr) 40 | [rustsbi] pmp04: 0x88000000..0x00000000 (-wr) 41 | 42 | _____ _ _ __ _ 43 | |_ _|__ ___| |_ | |/ /___ _ __ _ __ ___| | 44 | | |/ _ \/ __| __| | ' // _ \ '__| '_ \ / _ \ | 45 | | | __/\__ \ |_ | . \ __/ | | | | | __/ | 46 | |_|\___||___/\__| |_|\_\___|_| |_| |_|\___|_| 47 | ================================================ 48 | | boot hart id | 6 | 49 | | smp | 8 | 50 | | timebase frequency | 10000000 Hz | 51 | | dtb physical address | 0x87e00000 | 52 | ------------------------------------------------ 53 | [ INFO] Testing `Base` 54 | [ INFO] sbi spec version = 2.0 55 | [ INFO] sbi impl = RustSBI 56 | [ INFO] sbi impl version = 0x400 57 | [ INFO] sbi extensions = [Base, TIME, sPI, HSM, SRST] 58 | [ INFO] mvendor id = 0x0 59 | [ INFO] march id = 0x70200 60 | [ INFO] mimp id = 0x70200 61 | [ INFO] Sbi `Base` test pass 62 | [ INFO] Testing `TIME` 63 | [ INFO] read time register successfully, set timer +1s 64 | [ INFO] timer interrupt delegate successfully 65 | [ INFO] Sbi `TIME` test pass 66 | [ INFO] Testing `sPI` 67 | [ INFO] send ipi successfully 68 | [ INFO] Sbi `sPI` test pass 69 | [ INFO] Testing `HSM` 70 | [ INFO] Testing harts: [0, 1, 2, 3] 71 | [DEBUG] hart 0 started 72 | [DEBUG] hart 0 suspended nonretentive 73 | [DEBUG] hart 1 started 74 | [DEBUG] hart 1 suspended nonretentive 75 | [DEBUG] hart 2 started 76 | [DEBUG] hart 2 suspended nonretentive 77 | [DEBUG] hart 3 started 78 | [DEBUG] hart 3 suspended nonretentive 79 | [DEBUG] hart 0 resumed 80 | [DEBUG] hart 0 suspended retentive 81 | [DEBUG] hart 0 stopped 82 | [DEBUG] hart 1 resumed 83 | [DEBUG] hart 1 suspended retentive 84 | [DEBUG] hart 1 stopped 85 | [DEBUG] hart 2 resumed 86 | [DEBUG] hart 2 suspended retentive 87 | [DEBUG] hart 2 stopped 88 | [DEBUG] hart 3 resumed 89 | [DEBUG] hart 3 suspended retentive 90 | [DEBUG] hart 3 stopped 91 | [ INFO] Testing Pass: [0, 1, 2, 3] 92 | [ INFO] Testing harts: [4, 5, 7] 93 | [DEBUG] hart 4 started 94 | [DEBUG] hart 4 suspended nonretentive 95 | [DEBUG] hart 5 started 96 | [DEBUG] hart 5 suspended nonretentive 97 | [DEBUG] hart 7 started 98 | [DEBUG] hart 7 suspended nonretentive 99 | [DEBUG] hart 4 resumed 100 | [DEBUG] hart 4 suspended retentive 101 | [DEBUG] hart 4 stopped 102 | [DEBUG] hart 5 resumed 103 | [DEBUG] hart 5 suspended retentive 104 | [DEBUG] hart 5 stopped 105 | [DEBUG] hart 7 resumed 106 | [DEBUG] hart 7 suspended retentive 107 | [DEBUG] hart 7 stopped 108 | [ INFO] Testing Pass: [4, 5, 7] 109 | [ INFO] Sbi `HSM` test pass 110 | [ INFO] Testing `DBCN` 111 | Hello, world! 112 | [ INFO] writing slice successfully 113 | [ INFO] reading 0 bytes from console 114 | [ INFO] Sbi `DBCN` test pass 115 | ``` 116 | 117 | ## Run test kernel 118 | 119 | ### Requirements 120 | 121 | You should have `cargo-binutils` installed. 122 | 123 | ```shell 124 | cargo install cargo-binutils 125 | ``` 126 | 127 | ### Run 128 | 129 | Run with: 130 | 131 | ```shell 132 | cargo test 133 | ``` 134 | 135 | It will run RustSBI-QEMU with a test kernel. The test kernel will test all SBI functions, 136 | its command emulation and other features. If it succeeds, there would be output like: 137 | 138 | ```plaintext 139 | running 1 test 140 | Finished dev [unoptimized + debuginfo] target(s) in 0.14s 141 | Compiling test-kernel v0.1.0 (D:\RustProjects\rustsbi-qemu\test-kernel) 142 | Finished dev [unoptimized + debuginfo] target(s) in 0.61s 143 | test run_test_kernel ... ok 144 | 145 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.31s 146 | ``` 147 | 148 | ## Notes 149 | 150 | 1. What kind of kernel does this project support? 151 | 152 | The rustsbi-qemu project supports raw binary kernels for educational or 153 | competition use. This project itself is only a showcase example illustrating how 154 | implementations should use RustSBI, it does not include a Linux boot support. 155 | You may visit downstream bootloader projects for a Linux capable bootloader. 156 | 157 | 2. How to enable hypervisor H extension on QEMU? 158 | 159 | You should use these following line of parameters: 160 | 161 | ```rust 162 | command.args(&["-cpu", "rv64,x-h=true"]); 163 | ``` 164 | 165 | ... to enable H extension on QEMU software. 166 | 167 | The H extension is enabled by default when QEMU version >= 7.0.0. 168 | 169 | 3. What is the minimum supported Rust version of this package? 170 | 171 | You should build RustSBI-QEMU on nightly at least `rustc 1.66.0-nightly (a24a020e6 2022-10-18)`. 172 | 173 | ## License 174 | 175 | This project is licensed under Mulan PSL v2. 176 | 177 | ```plaintext 178 | Copyright (c) 2021-2023 RustSBI Team 179 | RustSBI-QEMU is licensed under Mulan PSL v2. 180 | You can use this software according to the terms and conditions of the Mulan PSL v2. 181 | You may obtain a copy of Mulan PSL v2 at: 182 | http://license.coscl.org.cn/MulanPSL2 183 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 184 | EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 185 | MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 186 | See the Mulan PSL v2 for more details. 187 | ``` 188 | -------------------------------------------------------------------------------- /bench-kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bench-kernel" 3 | version = "0.0.1" 4 | edition = "2021" 5 | authors = ["YdrMaster "] 6 | publish = false 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | sbi-rt = "0.0.3" 12 | riscv = "0.10.1" 13 | uart16550 = "0.0.1" 14 | rcore-console = "0.0.0" 15 | -------------------------------------------------------------------------------- /bench-kernel/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(&ld, LINKER).unwrap(); 6 | println!("cargo:rerun-if-changed=build.rs"); 7 | println!("cargo:rerun-if-env-changed=LOG"); 8 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 9 | } 10 | 11 | const LINKER: &[u8] = b" 12 | OUTPUT_ARCH(riscv) 13 | ENTRY(_start) 14 | MEMORY { 15 | DRAM : ORIGIN = 0x80200000, LENGTH = 64M 16 | } 17 | SECTIONS { 18 | .text : { 19 | *(.text.entry) 20 | *(.text .text.*) 21 | } > DRAM 22 | .rodata : { 23 | *(.rodata .rodata.*) 24 | *(.srodata .srodata.*) 25 | } > DRAM 26 | .data : { 27 | *(.data .data.*) 28 | *(.sdata .sdata.*) 29 | } > DRAM 30 | .bss (NOLOAD) : { 31 | *(.bss.uninit) 32 | . = ALIGN(8); 33 | sbss = .; 34 | *(.bss .bss.*) 35 | *(.sbss .sbss.*) 36 | . = ALIGN(8); 37 | ebss = .; 38 | } > DRAM 39 | /DISCARD/ : { 40 | *(.eh_frame) 41 | } 42 | }"; 43 | -------------------------------------------------------------------------------- /bench-kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(naked_functions, asm_const)] 4 | #![deny(warnings)] 5 | 6 | use rcore_console::log; 7 | use riscv::register::*; 8 | use sbi_rt::*; 9 | use uart16550::Uart16550; 10 | 11 | #[naked] 12 | #[no_mangle] 13 | #[link_section = ".text.entry"] 14 | unsafe extern "C" fn _start(hartid: usize, device_tree_paddr: usize) -> ! { 15 | const STACK_SIZE: usize = 16384; // 16 KiB 16 | 17 | #[link_section = ".bss.uninit"] 18 | static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE]; 19 | 20 | core::arch::asm!( 21 | "la sp, {stack} + {stack_size}", 22 | "j {main}", 23 | stack_size = const STACK_SIZE, 24 | stack = sym STACK, 25 | main = sym rust_main, 26 | options(noreturn), 27 | ) 28 | } 29 | 30 | extern "C" fn rust_main(hartid: usize, _dtb_pa: usize) -> ! { 31 | extern "C" { 32 | static mut sbss: u64; 33 | static mut ebss: u64; 34 | } 35 | unsafe { 36 | let mut ptr = sbss as *mut u64; 37 | let end = ebss as *mut u64; 38 | while ptr < end { 39 | ptr.write_volatile(0); 40 | ptr = ptr.offset(1); 41 | } 42 | } 43 | // 初始化打印 44 | unsafe { UART = Uart16550Map(0x1000_0000 as _) }; 45 | rcore_console::init_console(&Console); 46 | rcore_console::set_log_level(option_env!("LOG")); 47 | rcore_console::test_log(); 48 | 49 | // 测试调用延迟 50 | let t0 = time::read(); 51 | 52 | for _ in 0..100_0000 { 53 | let _ = sbi_rt::get_spec_version(); 54 | } 55 | 56 | let t1 = time::read(); 57 | log::info!("spec_version duration = {}", t1 - t0); 58 | 59 | // 测试调用延迟 60 | let t0 = time::read(); 61 | 62 | for _ in 0..100_0000 { 63 | let _ = sbi_rt::get_marchid(); 64 | } 65 | 66 | let t1 = time::read(); 67 | log::info!("marchid duration = {}", t1 - t0); 68 | 69 | // 打开软中断 70 | unsafe { sie::set_ssoft() }; 71 | // 测试中断响应延迟 72 | let t0 = time::read(); 73 | for _ in 0..100_0000 { 74 | unsafe { 75 | sstatus::set_sie(); 76 | core::arch::asm!( 77 | " la {0}, 1f 78 | csrw stvec, {0} 79 | mv a0, a2 80 | mv a1, zero 81 | ecall 82 | 0: wfi 83 | j 0b 84 | .align 2 85 | 1: csrci sip, {ssip} 86 | ", 87 | out(reg) _, 88 | ssip = const 1 << 1, 89 | in("a7") 0x735049, 90 | in("a6") 0, 91 | in("a0") 0, 92 | in("a1") 0, 93 | in("a2") 1 << hartid, 94 | options(nomem), 95 | ); 96 | } 97 | } 98 | 99 | let t1 = time::read(); 100 | log::info!("ipi duration = {}", t1 - t0); 101 | 102 | system_reset(Shutdown, NoReason); 103 | unreachable!() 104 | } 105 | 106 | #[panic_handler] 107 | fn panic(info: &core::panic::PanicInfo) -> ! { 108 | log::error!("{info}"); 109 | system_reset(Shutdown, SystemFailure); 110 | loop {} 111 | } 112 | 113 | struct Console; 114 | static mut UART: Uart16550Map = Uart16550Map(core::ptr::null()); 115 | 116 | pub struct Uart16550Map(*const Uart16550); 117 | 118 | unsafe impl Sync for Uart16550Map {} 119 | 120 | impl Uart16550Map { 121 | #[inline] 122 | pub fn get(&self) -> &Uart16550 { 123 | unsafe { &*self.0 } 124 | } 125 | } 126 | 127 | impl rcore_console::Console for Console { 128 | #[inline] 129 | fn put_char(&self, c: u8) { 130 | unsafe { UART.get().write(core::slice::from_ref(&c)) }; 131 | } 132 | 133 | #[inline] 134 | fn put_str(&self, s: &str) { 135 | unsafe { UART.get().write(s.as_bytes()) }; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /hsm-cell/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hsm-cell" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sbi-spec = "0.0.7" 10 | 11 | [lib] 12 | name = "hsm_cell" 13 | test = false 14 | bench = false 15 | -------------------------------------------------------------------------------- /hsm-cell/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 硬件线程状态和受状态保护的线程间共享数据。 2 | 3 | #![no_std] 4 | #![deny(warnings, missing_docs)] 5 | 6 | use core::{ 7 | cell::UnsafeCell, 8 | hint::spin_loop, 9 | sync::atomic::{AtomicUsize, Ordering}, 10 | }; 11 | use sbi_spec::hsm::*; 12 | 13 | /// 硬件线程状态和受状态保护的线程间共享数据。 14 | pub struct HsmCell { 15 | status: AtomicUsize, 16 | val: UnsafeCell>, 17 | } 18 | 19 | /// 当前硬件线程的共享对象。 20 | pub struct LocalHsmCell<'a, T>(&'a HsmCell); 21 | 22 | /// 任意硬件线程的共享对象。 23 | pub struct RemoteHsmCell<'a, T>(&'a HsmCell); 24 | 25 | unsafe impl Sync for HsmCell {} 26 | unsafe impl Send for HsmCell {} 27 | 28 | const HART_STATE_START_PENDING_EXT: usize = usize::MAX; 29 | 30 | impl HsmCell { 31 | /// 创建一个新的共享对象。 32 | pub const fn new() -> Self { 33 | Self { 34 | status: AtomicUsize::new(hart_state::STOPPED), 35 | val: UnsafeCell::new(None), 36 | } 37 | } 38 | 39 | /// 从当前硬件线程的状态中获取线程间共享对象。 40 | /// 41 | /// # Safety 42 | /// 43 | /// 用户需要确保对象属于当前硬件线程。 44 | #[inline] 45 | pub unsafe fn local(&self) -> LocalHsmCell<'_, T> { 46 | LocalHsmCell(self) 47 | } 48 | 49 | /// 取出共享对象。 50 | #[inline] 51 | pub fn remote(&self) -> RemoteHsmCell<'_, T> { 52 | RemoteHsmCell(self) 53 | } 54 | } 55 | 56 | impl LocalHsmCell<'_, T> { 57 | /// 从启动挂起状态的硬件线程取出共享数据,并将其状态设置为启动,如果成功返回取出的数据,否则返回当前状态。 58 | #[inline] 59 | pub fn start(&self) -> Result { 60 | loop { 61 | match self.0.status.compare_exchange( 62 | hart_state::START_PENDING, 63 | hart_state::STARTED, 64 | Ordering::AcqRel, 65 | Ordering::Relaxed, 66 | ) { 67 | Ok(_) => break Ok(unsafe { (*self.0.val.get()).take().unwrap() }), 68 | Err(HART_STATE_START_PENDING_EXT) => spin_loop(), 69 | Err(s) => break Err(s), 70 | } 71 | } 72 | } 73 | 74 | /// 关闭。 75 | #[inline] 76 | pub fn stop(&self) { 77 | self.0.status.store(hart_state::STOPPED, Ordering::Release) 78 | } 79 | 80 | /// 关闭。 81 | #[inline] 82 | pub fn suspend(&self) { 83 | self.0 84 | .status 85 | .store(hart_state::SUSPENDED, Ordering::Relaxed) 86 | } 87 | 88 | /// 关闭。 89 | #[inline] 90 | pub fn resume(&self) { 91 | self.0.status.store(hart_state::STARTED, Ordering::Relaxed) 92 | } 93 | } 94 | 95 | impl RemoteHsmCell<'_, T> { 96 | /// 向关闭状态的硬件线程传入共享数据,并将其状态设置为启动挂起,返回是否放入成功。 97 | #[inline] 98 | pub fn start(self, t: T) -> bool { 99 | if self 100 | .0 101 | .status 102 | .compare_exchange( 103 | hart_state::STOPPED, 104 | HART_STATE_START_PENDING_EXT, 105 | Ordering::Acquire, 106 | Ordering::Relaxed, 107 | ) 108 | .is_ok() 109 | { 110 | unsafe { *self.0.val.get() = Some(t) }; 111 | self.0 112 | .status 113 | .store(hart_state::START_PENDING, Ordering::Release); 114 | true 115 | } else { 116 | false 117 | } 118 | } 119 | 120 | /// 取出当前状态。 121 | #[inline] 122 | pub fn sbi_get_status(&self) -> usize { 123 | match self.0.status.load(Ordering::Relaxed) { 124 | HART_STATE_START_PENDING_EXT => hart_state::START_PENDING, 125 | normal => normal, 126 | } 127 | } 128 | 129 | /// 判断这个 HART 能否接收 IPI。 130 | #[inline] 131 | pub fn allow_ipi(&self) -> bool { 132 | matches!( 133 | self.0.status.load(Ordering::Relaxed), 134 | hart_state::STARTED | hart_state::SUSPENDED 135 | ) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | profile = "minimal" 3 | channel = "nightly" 4 | components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] 5 | targets = ["riscv64imac-unknown-none-elf"] 6 | -------------------------------------------------------------------------------- /rustsbi-qemu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustsbi-qemu" 3 | description = "RustSBI support package for QEMU" 4 | version = "0.2.0-alpha.3" 5 | authors = [ 6 | "Luo Jia ", 7 | "YdrMaster ", 8 | "Campbell He ", 9 | "Yifan Wu ", 10 | ] 11 | license = "MulanPSL-2.0 OR MIT" 12 | readme = "README.md" 13 | keywords = ["riscv", "sbi", "rustsbi"] 14 | categories = ["os", "embedded", "hardware-support", "no-std"] 15 | edition = "2021" 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [dependencies] 20 | rustsbi = { version = "0.4.0", features = ["machine"] } 21 | sbi-spec = { version = "0.0.7", features = ["legacy"] } 22 | riscv = "0.10.1" 23 | spin = "0.9" 24 | rcore-console = "0.0.0" 25 | aclint = "0.0.0" 26 | sifive-test-device = "0.0.0" 27 | dtb-walker = "=0.2.0-alpha.3" 28 | uart16550 = "0.0.1" 29 | 30 | hsm-cell = { path = "../hsm-cell" } 31 | fast-trap = { version = "=0.0.1", features = ["riscv-m"] } 32 | -------------------------------------------------------------------------------- /rustsbi-qemu/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = &PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(ld, LINKER).unwrap(); 6 | println!("cargo:rerun-if-changed=build.rs"); 7 | println!("cargo:rerun-if-env-changed=LOG"); 8 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 9 | } 10 | 11 | const LINKER: &[u8] = b" 12 | OUTPUT_ARCH(riscv) 13 | ENTRY(_start) 14 | MEMORY { 15 | DRAM : ORIGIN = 0x80000000, LENGTH = 2M 16 | } 17 | SECTIONS { 18 | .text : { 19 | *(.text.entry) 20 | *(.text .text.*) 21 | } > DRAM 22 | .rodata : { 23 | *(.rodata .rodata.*) 24 | *(.srodata .srodata.*) 25 | } > DRAM 26 | .data : { 27 | *(.data .data.*) 28 | *(.sdata .sdata.*) 29 | } > DRAM 30 | .bss (NOLOAD) : { 31 | *(.bss.uninit) 32 | . = ALIGN(8); 33 | sbss = .; 34 | *(.bss .bss.*) 35 | *(.sbss .sbss.*) 36 | . = ALIGN(8); 37 | ebss = .; 38 | } > DRAM 39 | /DISCARD/ : { 40 | *(.eh_frame) 41 | } 42 | }"; 43 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/clint.rs: -------------------------------------------------------------------------------- 1 | use crate::{hart_id, trap_stack::remote_hsm}; 2 | use aclint::SifiveClint; 3 | use core::{ 4 | ptr::null_mut, 5 | sync::atomic::{AtomicPtr, Ordering}, 6 | }; 7 | use rustsbi::{HartMask, Ipi, SbiRet, Timer}; 8 | 9 | pub(crate) struct Clint; 10 | 11 | pub(crate) static CLINT: AtomicPtr = AtomicPtr::new(null_mut()); 12 | 13 | pub(crate) fn init(base: usize) { 14 | CLINT.store(base as _, Ordering::Release); 15 | } 16 | 17 | impl Ipi for Clint { 18 | #[inline] 19 | fn send_ipi(&self, hart_mask: HartMask) -> SbiRet { 20 | for i in 0..crate::NUM_HART_MAX { 21 | if hart_mask.has_bit(i) && remote_hsm(i).map_or(false, |hsm| hsm.allow_ipi()) { 22 | set_msip(i); 23 | } 24 | } 25 | SbiRet::success(0) 26 | } 27 | } 28 | 29 | impl Timer for Clint { 30 | #[inline] 31 | fn set_timer(&self, time_value: u64) { 32 | unsafe { 33 | riscv::register::mip::clear_stimer(); 34 | (*CLINT.load(Ordering::Relaxed)).write_mtimecmp(hart_id(), time_value); 35 | } 36 | } 37 | } 38 | 39 | #[inline] 40 | pub fn set_msip(hart_idx: usize) { 41 | unsafe { &*CLINT.load(Ordering::Relaxed) }.set_msip(hart_idx); 42 | } 43 | 44 | #[inline] 45 | pub fn clear_msip() { 46 | unsafe { &*CLINT.load(Ordering::Relaxed) }.clear_msip(hart_id()); 47 | } 48 | 49 | #[inline] 50 | pub fn clear() { 51 | loop { 52 | if let Some(clint) = unsafe { CLINT.load(Ordering::Relaxed).as_ref() } { 53 | clint.clear_msip(hart_id()); 54 | clint.write_mtimecmp(hart_id(), u64::MAX); 55 | break; 56 | } else { 57 | continue; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/dbcn.rs: -------------------------------------------------------------------------------- 1 | use crate::uart16550; 2 | use core::ops::Range; 3 | use rustsbi::{Console, Physical, SbiRet}; 4 | use spin::Once; 5 | 6 | pub(crate) struct DBCN(Range); 7 | 8 | static INSTANCE: Once = Once::new(); 9 | 10 | pub(crate) fn init(memory: Range) { 11 | INSTANCE.call_once(|| DBCN(memory)); 12 | } 13 | 14 | pub(crate) fn get() -> &'static DBCN { 15 | INSTANCE.wait() 16 | } 17 | 18 | impl Console for DBCN { 19 | fn write(&self, bytes: Physical<&[u8]>) -> SbiRet { 20 | let start = bytes.phys_addr_lo(); 21 | let end = start + bytes.num_bytes(); 22 | if self.0.contains(&start) && self.0.contains(&(end - 1)) { 23 | let buf = unsafe { core::slice::from_raw_parts(start as *const u8, bytes.num_bytes()) }; 24 | SbiRet::success(uart16550::UART.lock().get().write(buf)) 25 | } else { 26 | SbiRet::invalid_param() 27 | } 28 | } 29 | 30 | fn read(&self, bytes: Physical<&mut [u8]>) -> SbiRet { 31 | let start = bytes.phys_addr_lo(); 32 | let end = start + bytes.num_bytes(); 33 | if self.0.contains(&start) && self.0.contains(&(end - 1)) { 34 | let buf = 35 | unsafe { core::slice::from_raw_parts_mut(start as *mut u8, bytes.num_bytes()) }; 36 | SbiRet::success(uart16550::UART.lock().get().read(buf)) 37 | } else { 38 | SbiRet::invalid_param() 39 | } 40 | } 41 | 42 | #[inline] 43 | fn write_byte(&self, byte: u8) -> SbiRet { 44 | let uart = uart16550::UART.lock(); 45 | loop { 46 | if uart.get().write(&[byte]) == 1 { 47 | return SbiRet::success(0); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/device_tree.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::{Display, Formatter, Result}, 3 | ops::Range, 4 | }; 5 | 6 | /// 从设备树采集的板信息。 7 | pub(crate) struct BoardInfo { 8 | pub dtb: Range, 9 | pub model: StringInline<128>, 10 | pub smp: usize, 11 | pub mem: Range, 12 | pub uart: Range, 13 | pub test: Range, 14 | pub clint: Range, 15 | } 16 | 17 | /// 在栈上存储有限长度字符串。 18 | pub(crate) struct StringInline(usize, [u8; N]); 19 | 20 | impl Display for StringInline { 21 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 22 | write!(f, "{}", unsafe { 23 | core::str::from_utf8_unchecked(&self.1[..self.0]) 24 | }) 25 | } 26 | } 27 | 28 | /// 解析设备树。 29 | pub(crate) fn parse(opaque: usize) -> BoardInfo { 30 | use dtb_walker::{Dtb, DtbObj, HeaderError as E, Property, Str, WalkOperation::*}; 31 | const CPUS: &str = "cpus"; 32 | const MEMORY: &str = "memory"; 33 | const SOC: &str = "soc"; 34 | const UART: &str = "uart"; 35 | const SERIAL: &str = "serial"; 36 | const TEST: &str = "test"; 37 | const CLINT: &str = "clint"; 38 | 39 | let mut ans = BoardInfo { 40 | dtb: opaque..opaque, 41 | model: StringInline(0, [0u8; 128]), 42 | smp: 0, 43 | mem: 0..0, 44 | uart: 0..0, 45 | test: 0..0, 46 | clint: 0..0, 47 | }; 48 | let dtb = unsafe { 49 | Dtb::from_raw_parts_filtered(opaque as _, |e| { 50 | matches!(e, E::Misaligned(4) | E::LastCompVersion(_)) 51 | }) 52 | } 53 | .unwrap(); 54 | ans.dtb.end += dtb.total_size(); 55 | dtb.walk(|ctx, obj| match obj { 56 | DtbObj::SubNode { name } => { 57 | let current = ctx.name(); 58 | if ctx.is_root() { 59 | if name == Str::from(CPUS) || name == Str::from(SOC) || name.starts_with(MEMORY) { 60 | StepInto 61 | } else { 62 | StepOver 63 | } 64 | } else if current == Str::from(SOC) { 65 | if name.starts_with(UART) 66 | || name.starts_with(SERIAL) 67 | || name.starts_with(TEST) 68 | || name.starts_with(CLINT) 69 | { 70 | StepInto 71 | } else { 72 | StepOver 73 | } 74 | } else { 75 | if current == Str::from(CPUS) && name.starts_with("cpu@") { 76 | ans.smp += 1; 77 | } 78 | StepOver 79 | } 80 | } 81 | DtbObj::Property(Property::Model(model)) if ctx.is_root() => { 82 | ans.model.0 = model.as_bytes().len(); 83 | ans.model.1[..ans.model.0].copy_from_slice(model.as_bytes()); 84 | StepOver 85 | } 86 | DtbObj::Property(Property::Reg(mut reg)) => { 87 | let node = ctx.name(); 88 | if node.starts_with(UART) || node.starts_with(SERIAL) { 89 | ans.uart = reg.next().unwrap(); 90 | StepOut 91 | } else if node.starts_with(TEST) { 92 | ans.test = reg.next().unwrap(); 93 | StepOut 94 | } else if node.starts_with(CLINT) { 95 | ans.clint = reg.next().unwrap(); 96 | StepOut 97 | } else if node.starts_with(MEMORY) { 98 | ans.mem = reg.next().unwrap(); 99 | StepOut 100 | } else { 101 | StepOver 102 | } 103 | } 104 | DtbObj::Property(_) => StepOver, 105 | }); 106 | 107 | ans 108 | } 109 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/hart_csr_utils.rs: -------------------------------------------------------------------------------- 1 | pub(crate) fn print_pmps() { 2 | const ITEM_PER_CFG: usize = core::mem::size_of::(); 3 | const CFG_STEP: usize = ITEM_PER_CFG / core::mem::size_of::(); 4 | 5 | let mut i_cfg = 0; 6 | while i_cfg < 4 { 7 | let base = i_cfg * core::mem::size_of::(); 8 | let mut cfg = pmpcfg(i_cfg); 9 | for i_addr in 0..ITEM_PER_CFG { 10 | match (cfg >> 3) & 0b11 { 11 | 0b00 => {} 12 | 0b01 => dump_pmp( 13 | base + i_addr, 14 | pmpaddr(base + i_addr - 1) << 2, 15 | pmpaddr(base + i_addr) << 2, 16 | cfg, 17 | ), 18 | 0b10 => { 19 | let s = pmpaddr(base + i_addr); 20 | dump_pmp(base + i_addr, s << 2, (s + 1) << 2, cfg); 21 | } 22 | 0b11 => { 23 | let addr = pmpaddr(base + i_addr); 24 | let len = 1usize << (addr.trailing_ones() + 2); 25 | let s = (addr & !(len - 1)) << 2; 26 | let e = s + len; 27 | dump_pmp(base + i_addr, s, e, cfg); 28 | } 29 | _ => unreachable!(), 30 | }; 31 | cfg >>= 8; 32 | } 33 | i_cfg += CFG_STEP; 34 | } 35 | } 36 | 37 | #[inline] 38 | fn dump_pmp(i: usize, s: usize, e: usize, cfg: usize) { 39 | println!( 40 | "[rustsbi] pmp{i:02}: {s:#010x}..{e:#010x} ({}{}{})", 41 | if cfg & 0b100 != 0 { "x" } else { "-" }, 42 | if cfg & 0b010 != 0 { "w" } else { "-" }, 43 | if cfg & 0b001 != 0 { "r" } else { "-" }, 44 | ); 45 | } 46 | 47 | fn pmpcfg(i: usize) -> usize { 48 | use riscv::register::*; 49 | match i { 50 | 0 => pmpcfg0::read().bits, 51 | #[cfg(target_arch = "riscv32")] 52 | 1 => pmpcfg1::read().bits, 53 | 2 => pmpcfg2::read().bits, 54 | #[cfg(target_arch = "riscv32")] 55 | 3 => pmpcfg3::read().bits, 56 | _ => todo!(), 57 | } 58 | } 59 | 60 | fn pmpaddr(i: usize) -> usize { 61 | use riscv::register::*; 62 | match i { 63 | 0x0 => pmpaddr0::read(), 64 | 0x1 => pmpaddr1::read(), 65 | 0x2 => pmpaddr2::read(), 66 | 0x3 => pmpaddr3::read(), 67 | 0x4 => pmpaddr4::read(), 68 | 0x5 => pmpaddr5::read(), 69 | 0x6 => pmpaddr6::read(), 70 | 0x7 => pmpaddr7::read(), 71 | 0x8 => pmpaddr8::read(), 72 | 0x9 => pmpaddr9::read(), 73 | 0xa => pmpaddr10::read(), 74 | 0xb => pmpaddr11::read(), 75 | 0xc => pmpaddr12::read(), 76 | 0xd => pmpaddr13::read(), 77 | 0xe => pmpaddr14::read(), 78 | 0xf => pmpaddr15::read(), 79 | _ => todo!(), 80 | } 81 | } 82 | 83 | // pub(crate) fn print_hart_csrs() { 84 | // print_misa(); 85 | // print_mideleg(); 86 | // print_medeleg(); 87 | // } 88 | 89 | // #[inline] 90 | // fn print_misa() { 91 | // let isa = misa::read(); 92 | // if let Some(isa) = isa { 93 | // let mxl_str = match isa.mxl() { 94 | // MXL::XLEN32 => "RV32", 95 | // MXL::XLEN64 => "RV64", 96 | // MXL::XLEN128 => "RV128", 97 | // }; 98 | // print!("[rustsbi] misa: {mxl_str}"); 99 | // for ext in 'A'..='Z' { 100 | // if isa.has_extension(ext) { 101 | // print!("{ext}"); 102 | // } 103 | // } 104 | // println!(); 105 | // } 106 | // } 107 | 108 | // #[inline] 109 | // fn print_mideleg() { 110 | // let mideleg = mideleg::read(); 111 | // let mut delegs = Vec::new(); 112 | // if mideleg.usoft() { 113 | // delegs.push("usoft") 114 | // } 115 | // if mideleg.utimer() { 116 | // delegs.push("utimer") 117 | // } 118 | // if mideleg.uext() { 119 | // delegs.push("uext") 120 | // } 121 | // if mideleg.ssoft() { 122 | // delegs.push("ssoft") 123 | // } 124 | // if mideleg.stimer() { 125 | // delegs.push("stimer") 126 | // } 127 | // if mideleg.sext() { 128 | // delegs.push("sext") 129 | // } 130 | // println!( 131 | // "[rustsbi] mideleg: {} ({:#x})", 132 | // delegs.join(", "), 133 | // mideleg.bits() 134 | // ); 135 | // } 136 | 137 | // #[inline] 138 | // fn print_medeleg() { 139 | // let medeleg = medeleg::read(); 140 | // let mut delegs = Vec::new(); 141 | // if medeleg.instruction_misaligned() { 142 | // delegs.push("ima") 143 | // } 144 | // if medeleg.instruction_fault() { 145 | // delegs.push("ia") // instruction access 146 | // } 147 | // if medeleg.illegal_instruction() { 148 | // delegs.push("illinsn") 149 | // } 150 | // if medeleg.breakpoint() { 151 | // delegs.push("bkpt") 152 | // } 153 | // if medeleg.load_misaligned() { 154 | // delegs.push("lma") 155 | // } 156 | // if medeleg.load_fault() { 157 | // delegs.push("la") // load access 158 | // } 159 | // if medeleg.store_misaligned() { 160 | // delegs.push("sma") 161 | // } 162 | // if medeleg.store_fault() { 163 | // delegs.push("sa") // store access 164 | // } 165 | // if medeleg.user_env_call() { 166 | // delegs.push("uecall") 167 | // } 168 | // if medeleg.supervisor_env_call() { 169 | // delegs.push("secall") 170 | // } 171 | // if medeleg.machine_env_call() { 172 | // delegs.push("mecall") 173 | // } 174 | // if medeleg.instruction_page_fault() { 175 | // delegs.push("ipage") 176 | // } 177 | // if medeleg.load_page_fault() { 178 | // delegs.push("lpage") 179 | // } 180 | // if medeleg.store_page_fault() { 181 | // delegs.push("spage") 182 | // } 183 | // println!( 184 | // "[rustsbi] medeleg: {} ({:#x})", 185 | // delegs.join(", "), 186 | // medeleg.bits() 187 | // ); 188 | // } 189 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(naked_functions, asm_const)] 4 | #![deny(warnings)] 5 | 6 | mod clint; 7 | mod dbcn; 8 | mod device_tree; 9 | mod hart_csr_utils; 10 | mod qemu_test; 11 | mod riscv_spec; 12 | mod trap_stack; 13 | mod trap_vec; 14 | mod uart16550; 15 | 16 | mod constants { 17 | /// 特权软件入口。 18 | pub(crate) const SUPERVISOR_ENTRY: usize = 0x8020_0000; 19 | /// 每个硬件线程设置 16KiB 栈空间。 20 | pub(crate) const LEN_STACK_PER_HART: usize = 16 * 1024; 21 | /// qemu-virt 最多 8 个硬件线程。 22 | pub(crate) const NUM_HART_MAX: usize = 8; 23 | } 24 | 25 | #[macro_use] 26 | extern crate rcore_console; 27 | 28 | use constants::*; 29 | use core::{ 30 | arch::asm, 31 | mem::MaybeUninit, 32 | sync::atomic::{AtomicBool, Ordering}, 33 | }; 34 | use device_tree::BoardInfo; 35 | use fast_trap::{FastContext, FastResult}; 36 | use riscv_spec::*; 37 | use rustsbi::{RustSBI, SbiRet}; 38 | use spin::Once; 39 | use trap_stack::{local_hsm, local_remote_hsm, remote_hsm}; 40 | use trap_vec::trap_vec; 41 | 42 | /// 入口。 43 | /// 44 | /// # Safety 45 | /// 46 | /// 裸函数。 47 | #[naked] 48 | #[no_mangle] 49 | #[link_section = ".text.entry"] 50 | unsafe extern "C" fn _start() -> ! { 51 | asm!( 52 | " call {locate_stack} 53 | call {rust_main} 54 | j {trap} 55 | ", 56 | locate_stack = sym trap_stack::locate, 57 | rust_main = sym rust_main, 58 | trap = sym trap_vec, 59 | options(noreturn), 60 | ) 61 | } 62 | 63 | /// rust 入口。 64 | extern "C" fn rust_main(hartid: usize, opaque: usize) { 65 | static GENESIS: AtomicBool = AtomicBool::new(true); 66 | static BOARD_INFO: Once = Once::new(); 67 | 68 | // 全局初始化过程 69 | if GENESIS.swap(false, Ordering::AcqRel) { 70 | extern "C" { 71 | static mut sbss: u64; 72 | static mut ebss: u64; 73 | } 74 | unsafe { 75 | let mut ptr = sbss as *mut u64; 76 | let end = ebss as *mut u64; 77 | while ptr < end { 78 | ptr.write_volatile(0); 79 | ptr = ptr.offset(1); 80 | } 81 | } 82 | // 解析设备树 83 | let board_info = BOARD_INFO.call_once(|| device_tree::parse(opaque)); 84 | // 初始化外设 85 | uart16550::init(board_info.uart.start); 86 | rcore_console::init_console(&Console); 87 | rcore_console::set_log_level(option_env!("LOG")); 88 | clint::init(board_info.clint.start); 89 | qemu_test::init(board_info.test.start); 90 | dbcn::init(SUPERVISOR_ENTRY..board_info.mem.end); 91 | // 打印启动信息 92 | print!( 93 | "\ 94 | [rustsbi] RustSBI version {ver_sbi}, adapting to RISC-V SBI v2.0.0 95 | {logo} 96 | [rustsbi] Implementation : RustSBI-QEMU Version {ver_impl} 97 | [rustsbi] Platform Name : {model} 98 | [rustsbi] Platform SMP : {smp} 99 | [rustsbi] Platform Memory : {mem:#x?} 100 | [rustsbi] Boot HART : {hartid} 101 | [rustsbi] Device Tree Region : {dtb:#x?} 102 | [rustsbi] Firmware Address : {firmware:#x} 103 | [rustsbi] Supervisor Address : {SUPERVISOR_ENTRY:#x} 104 | ", 105 | ver_sbi = rustsbi::VERSION, 106 | logo = rustsbi::LOGO, 107 | ver_impl = env!("CARGO_PKG_VERSION"), 108 | model = board_info.model, 109 | smp = board_info.smp, 110 | mem = board_info.mem, 111 | dtb = board_info.dtb, 112 | firmware = _start as usize, 113 | ); 114 | // 初始化 SBI 115 | unsafe { 116 | SBI = MaybeUninit::new(FixedRustSBI { 117 | clint: &clint::Clint, 118 | hsm: Hsm, 119 | reset: qemu_test::get(), 120 | dbcn: dbcn::get(), 121 | }); 122 | } 123 | // 设置并打印 pmp 124 | set_pmp(board_info); 125 | hart_csr_utils::print_pmps(); 126 | // 设置陷入栈 127 | trap_stack::prepare_for_trap(); 128 | // 设置内核入口 129 | local_remote_hsm().start(Supervisor { 130 | start_addr: SUPERVISOR_ENTRY, 131 | opaque, 132 | }); 133 | } else { 134 | // 设置 pmp 135 | set_pmp(BOARD_INFO.wait()); 136 | // 设置陷入栈 137 | trap_stack::prepare_for_trap(); 138 | } 139 | // 清理 clint 140 | clint::clear(); 141 | // 准备启动调度 142 | unsafe { 143 | asm!("csrw mideleg, {}", in(reg) !0); 144 | asm!("csrw medeleg, {}", in(reg) !0); 145 | asm!("csrw mcounteren, {}", in(reg) !0); 146 | use riscv::register::{medeleg, mtvec}; 147 | medeleg::clear_supervisor_env_call(); 148 | medeleg::clear_machine_env_call(); 149 | mtvec::write(trap_vec as _, mtvec::TrapMode::Vectored); 150 | } 151 | } 152 | 153 | #[inline(always)] 154 | fn hart_id() -> usize { 155 | riscv::register::mhartid::read() 156 | } 157 | 158 | /// 设置 PMP。 159 | fn set_pmp(board_info: &BoardInfo) { 160 | use riscv::register::*; 161 | let mem = &board_info.mem; 162 | unsafe { 163 | pmpcfg0::set_pmp(0, Range::OFF, Permission::NONE, false); 164 | pmpaddr0::write(0); 165 | // 外设 166 | pmpcfg0::set_pmp(1, Range::TOR, Permission::RW, false); 167 | pmpaddr1::write(mem.start >> 2); 168 | // SBI 169 | pmpcfg0::set_pmp(2, Range::TOR, Permission::NONE, false); 170 | pmpaddr2::write(SUPERVISOR_ENTRY >> 2); 171 | // 主存 172 | pmpcfg0::set_pmp(3, Range::TOR, Permission::RWX, false); 173 | pmpaddr3::write(mem.end >> 2); 174 | // 其他 175 | pmpcfg0::set_pmp(4, Range::TOR, Permission::RW, false); 176 | pmpaddr4::write(1 << (usize::BITS - 1)); 177 | } 178 | } 179 | 180 | extern "C" fn fast_handler( 181 | mut ctx: FastContext, 182 | a1: usize, 183 | a2: usize, 184 | a3: usize, 185 | a4: usize, 186 | a5: usize, 187 | a6: usize, 188 | a7: usize, 189 | ) -> FastResult { 190 | use riscv::register::{ 191 | mcause::{self, Exception as E, Trap as T}, 192 | mtval, satp, sstatus, 193 | }; 194 | 195 | #[inline] 196 | fn boot(mut ctx: FastContext, start_addr: usize, opaque: usize) -> FastResult { 197 | unsafe { 198 | sstatus::clear_sie(); 199 | satp::write(0); 200 | } 201 | ctx.regs().a[0] = hart_id(); 202 | ctx.regs().a[1] = opaque; 203 | ctx.regs().pc = start_addr; 204 | ctx.call(2) 205 | } 206 | loop { 207 | match local_hsm().start() { 208 | Ok(supervisor) => { 209 | mstatus::update(|bits| { 210 | *bits &= !mstatus::MPP; 211 | *bits |= mstatus::MPIE | mstatus::MPP_SUPERVISOR; 212 | }); 213 | mie::write(mie::MSIE | mie::MTIE); 214 | break boot(ctx, supervisor.start_addr, supervisor.opaque); 215 | } 216 | Err(rustsbi::spec::hsm::HART_STOP) => { 217 | mie::write(mie::MSIE); 218 | unsafe { riscv::asm::wfi() }; 219 | clint::clear_msip(); 220 | } 221 | _ => match mcause::read().cause() { 222 | // SBI call 223 | T::Exception(E::SupervisorEnvCall) => { 224 | use sbi_spec::{base, hsm, legacy}; 225 | let mut ret = unsafe { SBI.assume_init_mut() }.handle_ecall( 226 | a7, 227 | a6, 228 | [ctx.a0(), a1, a2, a3, a4, a5], 229 | ); 230 | if ret.is_ok() { 231 | match (a7, a6) { 232 | // 关闭 233 | (hsm::EID_HSM, hsm::HART_STOP) => continue, 234 | // 不可恢复挂起 235 | (hsm::EID_HSM, hsm::HART_SUSPEND) 236 | if matches!(ctx.a0() as u32, hsm::suspend_type::NON_RETENTIVE) => 237 | { 238 | break boot(ctx, a1, a2); 239 | } 240 | // legacy console 探测 241 | (base::EID_BASE, base::PROBE_EXTENSION) 242 | if matches!( 243 | ctx.a0(), 244 | legacy::LEGACY_CONSOLE_PUTCHAR | legacy::LEGACY_CONSOLE_GETCHAR 245 | ) => 246 | { 247 | ret.value = 1; 248 | } 249 | _ => {} 250 | } 251 | } else { 252 | match a7 { 253 | legacy::LEGACY_CONSOLE_PUTCHAR => { 254 | print!("{}", ctx.a0() as u8 as char); 255 | ret.error = 0; 256 | ret.value = a1; 257 | } 258 | legacy::LEGACY_CONSOLE_GETCHAR => { 259 | let mut c = 0u8; 260 | let uart = uart16550::UART.lock(); 261 | loop { 262 | if uart.get().read(core::slice::from_mut(&mut c)) == 1 { 263 | ret.error = c as _; 264 | ret.value = a1; 265 | break; 266 | } 267 | } 268 | } 269 | _ => {} 270 | } 271 | } 272 | ctx.regs().a = [ret.error, ret.value, a2, a3, a4, a5, a6, a7]; 273 | mepc::next(); 274 | break ctx.restore(); 275 | } 276 | // 其他陷入 277 | trap => { 278 | println!( 279 | " 280 | ----------------------------- 281 | > trap: {trap:?} 282 | > mstatus: {:#018x} 283 | > mepc: {:#018x} 284 | > mtval: {:#018x} 285 | ----------------------------- 286 | ", 287 | mstatus::read(), 288 | mepc::read(), 289 | mtval::read() 290 | ); 291 | panic!("stopped with unsupported trap") 292 | } 293 | }, 294 | } 295 | } 296 | } 297 | 298 | #[panic_handler] 299 | fn panic(info: &core::panic::PanicInfo) -> ! { 300 | use rustsbi::{ 301 | spec::srst::{RESET_REASON_SYSTEM_FAILURE, RESET_TYPE_SHUTDOWN}, 302 | Reset, 303 | }; 304 | // 输出的信息大概是“[rustsbi-panic] hart 0 panicked at ...” 305 | println!("[rustsbi-panic] hart {} {info}", hart_id()); 306 | println!("[rustsbi-panic] system shutdown scheduled due to RustSBI panic"); 307 | qemu_test::get().system_reset(RESET_TYPE_SHUTDOWN, RESET_REASON_SYSTEM_FAILURE); 308 | unreachable!() 309 | } 310 | 311 | /// 特权软件信息。 312 | #[derive(Debug)] 313 | struct Supervisor { 314 | start_addr: usize, 315 | opaque: usize, 316 | } 317 | 318 | struct Console; 319 | 320 | impl rcore_console::Console for Console { 321 | #[inline] 322 | fn put_char(&self, c: u8) { 323 | let uart = uart16550::UART.lock(); 324 | while uart.get().write(&[c]) == 0 { 325 | core::hint::spin_loop(); 326 | } 327 | } 328 | 329 | #[inline] 330 | fn put_str(&self, s: &str) { 331 | let uart = uart16550::UART.lock(); 332 | let mut bytes = s.as_bytes(); 333 | while !bytes.is_empty() { 334 | let count = uart.get().write(bytes); 335 | bytes = &bytes[count..]; 336 | } 337 | } 338 | } 339 | 340 | static mut SBI: MaybeUninit = MaybeUninit::uninit(); 341 | 342 | #[derive(RustSBI)] 343 | struct FixedRustSBI<'a> { 344 | #[rustsbi(ipi, timer)] 345 | clint: &'a clint::Clint, 346 | hsm: Hsm, 347 | reset: &'a qemu_test::QemuTest, 348 | dbcn: &'a dbcn::DBCN, 349 | } 350 | 351 | struct Hsm; 352 | 353 | impl rustsbi::Hsm for Hsm { 354 | fn hart_start(&self, hartid: usize, start_addr: usize, opaque: usize) -> SbiRet { 355 | match remote_hsm(hartid) { 356 | Some(remote) => { 357 | if remote.start(Supervisor { start_addr, opaque }) { 358 | clint::set_msip(hartid); 359 | SbiRet::success(0) 360 | } else { 361 | SbiRet::already_started() 362 | } 363 | } 364 | None => SbiRet::invalid_param(), 365 | } 366 | } 367 | 368 | #[inline] 369 | fn hart_stop(&self) -> SbiRet { 370 | local_hsm().stop(); 371 | SbiRet::success(0) 372 | } 373 | 374 | #[inline] 375 | fn hart_get_status(&self, hartid: usize) -> SbiRet { 376 | match remote_hsm(hartid) { 377 | Some(remote) => SbiRet::success(remote.sbi_get_status()), 378 | None => SbiRet::invalid_param(), 379 | } 380 | } 381 | 382 | fn hart_suspend(&self, suspend_type: u32, _resume_addr: usize, _opaque: usize) -> SbiRet { 383 | use rustsbi::spec::hsm::suspend_type::{NON_RETENTIVE, RETENTIVE}; 384 | if matches!(suspend_type, NON_RETENTIVE | RETENTIVE) { 385 | local_hsm().suspend(); 386 | unsafe { riscv::asm::wfi() }; 387 | local_hsm().resume(); 388 | SbiRet::success(0) 389 | } else { 390 | SbiRet::not_supported() 391 | } 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/qemu_test.rs: -------------------------------------------------------------------------------- 1 | use rustsbi::{ 2 | spec::srst::{ 3 | RESET_REASON_NO_REASON, RESET_REASON_SYSTEM_FAILURE, RESET_TYPE_COLD_REBOOT, 4 | RESET_TYPE_SHUTDOWN, RESET_TYPE_WARM_REBOOT, 5 | }, 6 | Reset, SbiRet, 7 | }; 8 | use sifive_test_device::SifiveTestDevice; 9 | use spin::Once; 10 | 11 | pub(crate) struct QemuTest(usize); 12 | 13 | static TEST: Once = Once::new(); 14 | 15 | pub(crate) fn init(base: usize) { 16 | TEST.call_once(|| QemuTest(base)); 17 | } 18 | 19 | pub(crate) fn get() -> &'static QemuTest { 20 | TEST.wait() 21 | } 22 | 23 | impl Reset for QemuTest { 24 | fn system_reset(&self, reset_type: u32, reset_reason: u32) -> SbiRet { 25 | let test = unsafe { &*(TEST.wait().0 as *const SifiveTestDevice) }; 26 | match reset_type { 27 | RESET_TYPE_SHUTDOWN => match reset_reason { 28 | RESET_REASON_NO_REASON => test.pass(), 29 | RESET_REASON_SYSTEM_FAILURE => test.fail(-1 as _), 30 | value => test.fail(value as _), 31 | }, 32 | RESET_TYPE_COLD_REBOOT | RESET_TYPE_WARM_REBOOT => { 33 | // test.reset(); 34 | todo!() 35 | } 36 | _ => SbiRet::invalid_param(), 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/riscv_spec.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused, missing_docs)] 2 | 3 | pub mod mie { 4 | use core::arch::asm; 5 | 6 | pub const SSIE: usize = 1 << 1; 7 | pub const VSSIE: usize = 1 << 2; 8 | pub const MSIE: usize = 1 << 3; 9 | pub const STIE: usize = 1 << 5; 10 | pub const VSTIE: usize = 1 << 6; 11 | pub const MTIE: usize = 1 << 7; 12 | pub const SEIE: usize = 1 << 9; 13 | pub const VSEIE: usize = 1 << 10; 14 | pub const MEIE: usize = 1 << 11; 15 | pub const SGEIE: usize = 1 << 12; 16 | 17 | #[inline(always)] 18 | pub fn write(bits: usize) { 19 | unsafe { asm!("csrw mie, {}", in(reg) bits, options(nomem)) }; 20 | } 21 | } 22 | 23 | pub mod mstatus { 24 | use core::arch::asm; 25 | 26 | pub const SIE: usize = 1 << 1; 27 | pub const MIE: usize = 1 << 3; 28 | pub const SPIE: usize = 1 << 5; 29 | pub const MPIE: usize = 1 << 7; 30 | pub const SPP: usize = 1 << 8; 31 | pub const VS: usize = 3 << 9; 32 | pub const MPP: usize = 3 << 11; 33 | pub const FS: usize = 3 << 13; 34 | pub const XS: usize = 3 << 15; 35 | pub const MPRV: usize = 1 << 17; 36 | pub const SUM: usize = 1 << 18; 37 | pub const MXR: usize = 1 << 19; 38 | pub const TVM: usize = 1 << 20; 39 | pub const TW: usize = 1 << 21; 40 | pub const TSR: usize = 1 << 22; 41 | pub const UXL: usize = 3 << 32; 42 | pub const SXL: usize = 3 << 34; 43 | pub const SBE: usize = 1 << 36; 44 | pub const MBE: usize = 1 << 37; 45 | pub const SD: usize = 1 << 63; 46 | 47 | pub const MPP_MACHINE: usize = 3 << 11; 48 | pub const MPP_SUPERVISOR: usize = 1 << 11; 49 | pub const MPP_USER: usize = 0 << 11; 50 | 51 | pub fn update(f: impl FnOnce(&mut usize)) { 52 | let mut bits: usize; 53 | unsafe { asm!("csrr {}, mstatus", out(reg) bits, options(nomem)) }; 54 | f(&mut bits); 55 | unsafe { asm!("csrw mstatus, {}", in(reg) bits, options(nomem)) }; 56 | } 57 | 58 | #[inline(always)] 59 | pub fn read() -> usize { 60 | let bits: usize; 61 | unsafe { asm!("csrr {}, mstatus", out(reg) bits, options(nomem)) }; 62 | bits 63 | } 64 | } 65 | 66 | pub mod mepc { 67 | use core::arch::asm; 68 | 69 | #[inline(always)] 70 | pub fn next() { 71 | unsafe { 72 | asm!( 73 | " csrr {0}, mepc 74 | addi {0}, {0}, 4 75 | csrw mepc, {0} 76 | ", 77 | out(reg) _, 78 | options(nomem), 79 | ) 80 | } 81 | } 82 | 83 | #[inline(always)] 84 | pub fn read() -> usize { 85 | let bits: usize; 86 | unsafe { asm!("csrr {}, mepc", out(reg) bits, options(nomem)) }; 87 | bits 88 | } 89 | 90 | #[inline(always)] 91 | pub fn write(bits: usize) { 92 | unsafe { asm!("csrw mepc, {}", in(reg) bits, options(nomem)) }; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/trap_stack.rs: -------------------------------------------------------------------------------- 1 | use crate::{fast_handler, hart_id, Supervisor, LEN_STACK_PER_HART, NUM_HART_MAX}; 2 | use core::{mem::forget, ptr::NonNull}; 3 | use fast_trap::{FlowContext, FreeTrapStack}; 4 | use hsm_cell::{HsmCell, LocalHsmCell, RemoteHsmCell}; 5 | 6 | /// 栈空间。 7 | #[link_section = ".bss.uninit"] 8 | static mut ROOT_STACK: [Stack; NUM_HART_MAX] = [Stack::ZERO; NUM_HART_MAX]; 9 | 10 | /// 定位每个 hart 的栈。 11 | #[naked] 12 | pub(crate) unsafe extern "C" fn locate() { 13 | core::arch::asm!( 14 | " la sp, {stack} 15 | li t0, {per_hart_stack_size} 16 | csrr t1, mhartid 17 | addi t1, t1, 1 18 | 1: add sp, sp, t0 19 | addi t1, t1, -1 20 | bnez t1, 1b 21 | call t1, {move_stack} 22 | ret 23 | ", 24 | per_hart_stack_size = const LEN_STACK_PER_HART, 25 | stack = sym ROOT_STACK, 26 | move_stack = sym fast_trap::reuse_stack_for_trap, 27 | options(noreturn), 28 | ) 29 | } 30 | 31 | /// 预备陷入栈。 32 | pub(crate) fn prepare_for_trap() { 33 | unsafe { ROOT_STACK.get_unchecked_mut(hart_id()).load_as_stack() }; 34 | } 35 | 36 | /// 获取此 hart 的 local hsm 对象。 37 | pub(crate) fn local_hsm() -> LocalHsmCell<'static, Supervisor> { 38 | unsafe { 39 | ROOT_STACK 40 | .get_unchecked_mut(hart_id()) 41 | .hart_context() 42 | .hsm 43 | .local() 44 | } 45 | } 46 | 47 | /// 获取此 hart 的 remote hsm 对象。 48 | pub(crate) fn local_remote_hsm() -> RemoteHsmCell<'static, Supervisor> { 49 | unsafe { 50 | ROOT_STACK 51 | .get_unchecked_mut(hart_id()) 52 | .hart_context() 53 | .hsm 54 | .remote() 55 | } 56 | } 57 | 58 | /// 获取任意 hart 的 remote hsm 对象。 59 | pub(crate) fn remote_hsm(hart_id: usize) -> Option> { 60 | unsafe { 61 | ROOT_STACK 62 | .get_mut(hart_id) 63 | .map(|x| x.hart_context().hsm.remote()) 64 | } 65 | } 66 | 67 | /// 类型化栈。 68 | /// 69 | /// 每个硬件线程拥有一个满足这样条件的内存块。 70 | /// 这个内存块的底部放着硬件线程状态 [`HartContext`],顶部用于陷入处理,中间是这个硬件线程的栈空间。 71 | /// 不需要 M 态线程,每个硬件线程只有这一个栈。 72 | #[repr(C, align(128))] 73 | struct Stack([u8; LEN_STACK_PER_HART]); 74 | 75 | impl Stack { 76 | /// 零初始化以避免加载。 77 | const ZERO: Self = Self([0; LEN_STACK_PER_HART]); 78 | 79 | /// 从栈上取出硬件线程状态。 80 | #[inline] 81 | fn hart_context(&mut self) -> &mut HartContext { 82 | unsafe { &mut *self.0.as_mut_ptr().cast() } 83 | } 84 | 85 | fn load_as_stack(&'static mut self) { 86 | let hart = self.hart_context(); 87 | let context_ptr = hart.context_ptr(); 88 | hart.init(); 89 | let range = self.0.as_ptr_range(); 90 | forget( 91 | FreeTrapStack::new( 92 | range.start as usize..range.end as usize, 93 | |_| {}, 94 | context_ptr, 95 | fast_handler, 96 | ) 97 | .unwrap() 98 | .load(), 99 | ); 100 | } 101 | } 102 | 103 | /// 硬件线程上下文。 104 | struct HartContext { 105 | /// 陷入上下文。 106 | trap: FlowContext, 107 | hsm: HsmCell, 108 | } 109 | 110 | impl HartContext { 111 | #[inline] 112 | fn init(&mut self) { 113 | self.hsm = HsmCell::new(); 114 | } 115 | 116 | #[inline] 117 | fn context_ptr(&mut self) -> NonNull { 118 | unsafe { NonNull::new_unchecked(&mut self.trap) } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/trap_vec.rs: -------------------------------------------------------------------------------- 1 | use crate::clint::CLINT; 2 | use aclint::SifiveClint as Clint; 3 | use core::arch::asm; 4 | use fast_trap::trap_entry; 5 | 6 | /// 中断向量表 7 | /// 8 | /// # Safety 9 | /// 10 | /// 裸函数。 11 | #[naked] 12 | pub(crate) unsafe extern "C" fn trap_vec() { 13 | asm!( 14 | ".align 2", 15 | ".option push", 16 | ".option norvc", 17 | "j {default}", // exception 18 | "j {default}", // supervisor software 19 | "j {default}", // reserved 20 | "j {msoft} ", // machine software 21 | "j {default}", // reserved 22 | "j {default}", // supervisor timer 23 | "j {default}", // reserved 24 | "j {mtimer}", // machine timer 25 | "j {default}", // reserved 26 | "j {default}", // supervisor external 27 | "j {default}", // reserved 28 | "j {default}", // machine external 29 | ".option pop", 30 | default = sym trap_entry, 31 | mtimer = sym mtimer, 32 | msoft = sym msoft, 33 | options(noreturn) 34 | ) 35 | } 36 | 37 | /// machine timer 中断代理 38 | /// 39 | /// # Safety 40 | /// 41 | /// 裸函数。 42 | #[naked] 43 | unsafe extern "C" fn mtimer() { 44 | asm!( 45 | // 换栈: 46 | // sp : M sp 47 | // mscratch: S sp 48 | " csrrw sp, mscratch, sp", 49 | // 保护 50 | " addi sp, sp, -4*8 51 | sd ra, 0*8(sp) 52 | sd a0, 1*8(sp) 53 | sd a1, 2*8(sp) 54 | sd a2, 3*8(sp) 55 | ", 56 | // 清除 mtimecmp 57 | " la a0, {clint_ptr} 58 | ld a0, (a0) 59 | csrr a1, mhartid 60 | addi a2, zero, -1 61 | call {set_mtimecmp} 62 | ", 63 | // 设置 stip 64 | " li a0, {mip_stip} 65 | csrrs zero, mip, a0 66 | ", 67 | // 恢复 68 | " ld ra, 0*8(sp) 69 | ld a0, 1*8(sp) 70 | ld a1, 2*8(sp) 71 | ld a2, 3*8(sp) 72 | addi sp, sp, 4*8 73 | ", 74 | // 换栈: 75 | // sp : S sp 76 | // mscratch: M sp 77 | " csrrw sp, mscratch, sp", 78 | // 返回 79 | " mret", 80 | mip_stip = const 1 << 5, 81 | clint_ptr = sym CLINT, 82 | // Clint::write_mtimecmp_naked(&self, hart_idx, val) 83 | set_mtimecmp = sym Clint::write_mtimecmp_naked, 84 | options(noreturn) 85 | ) 86 | } 87 | 88 | /// machine soft 中断代理 89 | /// 90 | /// # Safety 91 | /// 92 | /// 裸函数。 93 | #[naked] 94 | unsafe extern "C" fn msoft() { 95 | asm!( 96 | // 换栈: 97 | // sp : M sp 98 | // mscratch: S sp 99 | " csrrw sp, mscratch, sp", 100 | // 保护 101 | " addi sp, sp, -3*8 102 | sd ra, 0*8(sp) 103 | sd a0, 1*8(sp) 104 | sd a1, 2*8(sp) 105 | ", 106 | // 清除 msip 设置 ssip 107 | " la a0, {clint_ptr} 108 | ld a0, (a0) 109 | csrr a1, mhartid 110 | call {clear_msip} 111 | csrrsi zero, mip, 1 << 1 112 | ", 113 | // 恢复 114 | " ld ra, 0*8(sp) 115 | ld a0, 1*8(sp) 116 | ld a1, 2*8(sp) 117 | addi sp, sp, 3*8 118 | ", 119 | // 换栈: 120 | // sp : S sp 121 | // mscratch: M sp 122 | " csrrw sp, mscratch, sp", 123 | // 返回 124 | " mret", 125 | clint_ptr = sym CLINT, 126 | // Clint::clear_msip_naked(&self, hart_idx) 127 | clear_msip = sym Clint::clear_msip_naked, 128 | options(noreturn) 129 | ) 130 | } 131 | -------------------------------------------------------------------------------- /rustsbi-qemu/src/uart16550.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::null; 2 | use spin::lock_api::Mutex; 3 | use uart16550::Uart16550; 4 | 5 | pub(crate) static UART: Mutex = Mutex::new(Uart16550Map(null())); 6 | 7 | pub(crate) fn init(base: usize) { 8 | *UART.lock() = Uart16550Map(base as _); 9 | } 10 | 11 | pub struct Uart16550Map(*const Uart16550); 12 | 13 | unsafe impl Send for Uart16550Map {} 14 | unsafe impl Sync for Uart16550Map {} 15 | 16 | impl Uart16550Map { 17 | #[inline] 18 | pub fn get(&self) -> &Uart16550 { 19 | unsafe { &*self.0 } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test-kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-kernel" 3 | version = "0.2.0" 4 | authors = ["Luo Jia ", "YdrMaster "] 5 | edition = "2021" 6 | publish = false 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | sbi-testing = { version = "0.0.3-alpha.2", features = ["log"] } 12 | log = "0.4" 13 | riscv = "0.10.1" 14 | spin = "0.9" 15 | uart16550 = "0.0.1" 16 | rcore-console = "0.0.0" 17 | dtb-walker = "=0.2.0-alpha.3" 18 | -------------------------------------------------------------------------------- /test-kernel/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::{env, fs, path::PathBuf}; 3 | 4 | let ld = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld"); 5 | fs::write(&ld, LINKER).unwrap(); 6 | println!("cargo:rerun-if-changed=build.rs"); 7 | println!("cargo:rerun-if-env-changed=LOG"); 8 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 9 | } 10 | 11 | const LINKER: &[u8] = b" 12 | OUTPUT_ARCH(riscv) 13 | ENTRY(_start) 14 | MEMORY { 15 | DRAM : ORIGIN = 0x80200000, LENGTH = 64M 16 | } 17 | SECTIONS { 18 | .text : { 19 | *(.text.entry) 20 | *(.text .text.*) 21 | } > DRAM 22 | .rodata : { 23 | *(.rodata .rodata.*) 24 | *(.srodata .srodata.*) 25 | } > DRAM 26 | .data : { 27 | *(.data .data.*) 28 | *(.sdata .sdata.*) 29 | } > DRAM 30 | .bss (NOLOAD) : { 31 | *(.bss.uninit) 32 | . = ALIGN(8); 33 | sbss = .; 34 | *(.bss .bss.*) 35 | *(.sbss .sbss.*) 36 | . = ALIGN(8); 37 | ebss = .; 38 | } > DRAM 39 | /DISCARD/ : { 40 | *(.eh_frame) 41 | } 42 | }"; 43 | -------------------------------------------------------------------------------- /test-kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(naked_functions, asm_const)] 4 | #![deny(warnings)] 5 | 6 | #[macro_use] 7 | extern crate rcore_console; 8 | 9 | use core::{arch::asm, ptr::null}; 10 | use sbi_testing::sbi; 11 | use uart16550::Uart16550; 12 | 13 | /// 内核入口。 14 | /// 15 | /// # Safety 16 | /// 17 | /// 裸函数。 18 | #[naked] 19 | #[no_mangle] 20 | #[link_section = ".text.entry"] 21 | unsafe extern "C" fn _start(hartid: usize, device_tree_paddr: usize) -> ! { 22 | const STACK_SIZE: usize = 16384; // 16 KiB 23 | 24 | #[link_section = ".bss.uninit"] 25 | static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE]; 26 | 27 | asm!( 28 | "la sp, {stack} + {stack_size}", 29 | "j {main}", 30 | stack_size = const STACK_SIZE, 31 | stack = sym STACK, 32 | main = sym rust_main, 33 | options(noreturn), 34 | ) 35 | } 36 | 37 | extern "C" fn rust_main(hartid: usize, dtb_pa: usize) -> ! { 38 | extern "C" { 39 | static mut sbss: u64; 40 | static mut ebss: u64; 41 | } 42 | unsafe { 43 | let mut ptr = sbss as *mut u64; 44 | let end = ebss as *mut u64; 45 | while ptr < end { 46 | ptr.write_volatile(0); 47 | ptr = ptr.offset(1); 48 | } 49 | } 50 | let BoardInfo { 51 | smp, 52 | frequency, 53 | uart, 54 | } = BoardInfo::parse(dtb_pa); 55 | unsafe { UART = Uart16550Map(uart as _) }; 56 | rcore_console::init_console(&Console); 57 | rcore_console::set_log_level(option_env!("LOG")); 58 | println!( 59 | r" 60 | _____ _ _ __ _ 61 | |_ _|__ ___| |_ | |/ /___ _ __ _ __ ___| | 62 | | |/ _ \/ __| __| | ' // _ \ '__| '_ \ / _ \ | 63 | | | __/\__ \ |_ | . \ __/ | | | | | __/ | 64 | |_|\___||___/\__| |_|\_\___|_| |_| |_|\___|_| 65 | ================================================ 66 | | boot hart id | {hartid:20} | 67 | | smp | {smp:20} | 68 | | timebase frequency | {frequency:17} Hz | 69 | | dtb physical address | {dtb_pa:#20x} | 70 | ------------------------------------------------" 71 | ); 72 | let testing = sbi_testing::Testing { 73 | hartid, 74 | hart_mask: (1 << smp) - 1, 75 | hart_mask_base: 0, 76 | delay: frequency, 77 | }; 78 | if testing.test() { 79 | sbi::system_reset(sbi::Shutdown, sbi::NoReason); 80 | } else { 81 | sbi::system_reset(sbi::Shutdown, sbi::SystemFailure); 82 | } 83 | unreachable!() 84 | } 85 | 86 | #[cfg_attr(not(test), panic_handler)] 87 | fn panic(info: &core::panic::PanicInfo) -> ! { 88 | let (hart_id, pc): (usize, usize); 89 | unsafe { asm!("mv {}, tp", out(reg) hart_id) }; 90 | unsafe { asm!("auipc {}, 0", out(reg) pc) }; 91 | println!("[test-kernel-panic] hart {hart_id} {info}"); 92 | println!("[test-kernel-panic] pc = {pc:#x}"); 93 | println!("[test-kernel-panic] SBI test FAILED due to panic"); 94 | sbi::system_reset(sbi::Shutdown, sbi::SystemFailure); 95 | loop {} 96 | } 97 | 98 | struct BoardInfo { 99 | smp: usize, 100 | frequency: u64, 101 | uart: usize, 102 | } 103 | 104 | impl BoardInfo { 105 | fn parse(dtb_pa: usize) -> Self { 106 | use dtb_walker::{Dtb, DtbObj, HeaderError as E, Property, Str, WalkOperation::*}; 107 | 108 | let mut ans = Self { 109 | smp: 0, 110 | frequency: 0, 111 | uart: 0, 112 | }; 113 | unsafe { 114 | Dtb::from_raw_parts_filtered(dtb_pa as _, |e| { 115 | matches!(e, E::Misaligned(4) | E::LastCompVersion(_)) 116 | }) 117 | } 118 | .unwrap() 119 | .walk(|ctx, obj| match obj { 120 | DtbObj::SubNode { name } => { 121 | if ctx.is_root() && (name == Str::from("cpus") || name == Str::from("soc")) { 122 | StepInto 123 | } else if ctx.name() == Str::from("cpus") && name.starts_with("cpu@") { 124 | ans.smp += 1; 125 | StepOver 126 | } else if ctx.name() == Str::from("soc") 127 | && (name.starts_with("uart") || name.starts_with("serial")) 128 | { 129 | StepInto 130 | } else { 131 | StepOver 132 | } 133 | } 134 | DtbObj::Property(Property::Reg(mut reg)) => { 135 | if ctx.name().starts_with("uart") || ctx.name().starts_with("serial") { 136 | ans.uart = reg.next().unwrap().start; 137 | } 138 | StepOut 139 | } 140 | DtbObj::Property(Property::General { name, value }) => { 141 | if ctx.name() == Str::from("cpus") && name == Str::from("timebase-frequency") { 142 | ans.frequency = match *value { 143 | [a, b, c, d] => u32::from_be_bytes([a, b, c, d]) as _, 144 | [a, b, c, d, e, f, g, h] => u64::from_be_bytes([a, b, c, d, e, f, g, h]), 145 | _ => unreachable!(), 146 | }; 147 | } 148 | StepOver 149 | } 150 | DtbObj::Property(_) => StepOver, 151 | }); 152 | ans 153 | } 154 | } 155 | 156 | struct Console; 157 | static mut UART: Uart16550Map = Uart16550Map(null()); 158 | 159 | pub struct Uart16550Map(*const Uart16550); 160 | 161 | unsafe impl Sync for Uart16550Map {} 162 | 163 | impl Uart16550Map { 164 | #[inline] 165 | pub fn get(&self) -> &Uart16550 { 166 | unsafe { &*self.0 } 167 | } 168 | } 169 | 170 | impl rcore_console::Console for Console { 171 | #[inline] 172 | fn put_char(&self, c: u8) { 173 | unsafe { UART.get().write(core::slice::from_ref(&c)) }; 174 | } 175 | 176 | #[inline] 177 | fn put_str(&self, s: &str) { 178 | unsafe { UART.get().write(s.as_bytes()) }; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.2.0" 4 | authors = ["Luo Jia ", "YdrMaster "] 5 | description = "Interactive cargo runner, to build, analyze and test RustSBI-Qemu." 6 | edition = "2021" 7 | publish = false 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | clap = { version = "4.5", features = ["derive"] } 13 | os-xtask-utils = "0.0.0" 14 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate clap; 3 | 4 | use clap::Parser; 5 | use os_xtask_utils::{BinUtil, Cargo, CommandExt, Qemu}; 6 | use std::{ 7 | fs, io, 8 | path::{Path, PathBuf}, 9 | process, 10 | sync::OnceLock, 11 | }; 12 | 13 | fn project() -> &'static Path { 14 | static PROJECT: OnceLock<&'static Path> = OnceLock::new(); 15 | PROJECT.get_or_init(|| Path::new(std::env!("CARGO_MANIFEST_DIR")).parent().unwrap()) 16 | } 17 | 18 | #[derive(Parser)] 19 | #[clap(name = "RustSBI-Qemu")] 20 | #[clap(version, about, long_about = None)] 21 | struct Cli { 22 | #[clap(subcommand)] 23 | command: Commands, 24 | } 25 | 26 | #[derive(Subcommand)] 27 | enum Commands { 28 | /// Make this project 29 | Make(BuildArgs), 30 | /// Dump assembly code of RustSBI-QEMU 31 | Asm(AsmArgs), 32 | /// Run RustSBI-QEMU and test-kernel in QEMU 33 | Qemu(QemuArgs), 34 | } 35 | 36 | fn main() { 37 | use Commands::*; 38 | match Cli::parse().command { 39 | Make(args) => { 40 | args.make(package(args.kernel.as_ref()), true); 41 | } 42 | Asm(args) => args.dump(), 43 | Qemu(args) => args.run(), 44 | } 45 | } 46 | 47 | #[derive(Args, Default)] 48 | struct BuildArgs { 49 | /// With supervisor. 50 | #[clap(short, long)] 51 | kernel: Option, 52 | /// Log level. 53 | #[clap(long)] 54 | log: Option, 55 | /// Build in debug mode. 56 | #[clap(long)] 57 | debug: bool, 58 | } 59 | 60 | impl BuildArgs { 61 | fn make(&self, package: &str, binary: bool) -> PathBuf { 62 | let target = "riscv64imac-unknown-none-elf"; 63 | Cargo::build() 64 | .package(package) 65 | .optional(&self.log, |cargo, log| { 66 | cargo.env("LOG", log); 67 | }) 68 | .conditional(!self.debug, |cargo| { 69 | cargo.release(); 70 | }) 71 | .target(target) 72 | .invoke(); 73 | let elf = project() 74 | .join("target") 75 | .join(target) 76 | .join(if self.debug { "debug" } else { "release" }) 77 | .join(package); 78 | if binary { 79 | let bin = elf.with_extension("bin"); 80 | BinUtil::objcopy() 81 | .arg(elf) 82 | .arg("--strip-all") 83 | .args(["-O", "binary"]) 84 | .arg(&bin) 85 | .invoke(); 86 | bin 87 | } else { 88 | elf 89 | } 90 | } 91 | } 92 | 93 | #[derive(Args)] 94 | struct AsmArgs { 95 | #[clap(flatten)] 96 | build: BuildArgs, 97 | /// Output file. 98 | #[clap(short, long)] 99 | output: Option, 100 | } 101 | 102 | impl AsmArgs { 103 | /// 如果没有设置 `kernel`,将 `rustsbi-qemu` 反汇编,并保存到指定位置。 104 | /// 105 | /// 如果设置了 `kernel` 是 'test' 或 'test-kernel',将 `test-kernel` 反汇编,并保存到指定位置。 106 | /// 107 | /// 如果设置了 `kernel` 但不是 'test' 或 'test-kernel',将 `kernel` 指定的二进制文件反汇编,并保存到指定位置。 108 | fn dump(self) { 109 | let elf = self.build.make(package(self.build.kernel.as_ref()), false); 110 | let out = project().join(self.output.unwrap_or(format!( 111 | "{}.asm", 112 | elf.file_stem().unwrap().to_string_lossy() 113 | ))); 114 | println!("Asm file dumps to '{}'.", out.display()); 115 | fs::write(out, BinUtil::objdump().arg(elf).arg("-d").output().stdout).unwrap(); 116 | } 117 | } 118 | 119 | #[derive(Args, Default)] 120 | struct QemuArgs { 121 | #[clap(flatten)] 122 | build: BuildArgs, 123 | /// Which sbi to use, open or rust. 124 | #[clap(long)] 125 | sbi: Option, 126 | /// Number of hart (SMP for Symmetrical Multiple Processor). 127 | #[clap(long)] 128 | smp: Option, 129 | /// Port for gdb to connect. If set, qemu will block and wait gdb to connect. 130 | #[clap(long)] 131 | gdb: Option, 132 | } 133 | 134 | impl QemuArgs { 135 | fn run(mut self) { 136 | let sbi = self.sbi.take().unwrap_or_else(|| "rust".into()); 137 | let sbi = match sbi.to_lowercase().as_str() { 138 | "rust" | "rustsbi" => self.build.make("rustsbi-qemu", true), 139 | "open" | "opensbi" => PathBuf::from("default"), 140 | _ => panic!(), 141 | }; 142 | let kernel = self.build.kernel.take().unwrap_or_else(|| "test".into()); 143 | let kernel = match kernel.to_lowercase().as_str() { 144 | "test" | "test-kernel" => self.build.make("test-kernel", true), 145 | "bench" | "bench-kernel" => self.build.make("bench-kernel", true), 146 | _ => panic!(), 147 | }; 148 | let status = Qemu::system("riscv64") 149 | .args(["-machine", "virt"]) 150 | .arg("-nographic") 151 | .arg("-bios") 152 | .arg(sbi) 153 | .arg("-kernel") 154 | .arg(kernel) 155 | .args(["-serial", "mon:stdio"]) 156 | .args(["-smp", &self.smp.unwrap_or(8).to_string()]) 157 | .optional(&self.gdb, |qemu, gdb| { 158 | qemu.args(["-S", "-gdb", &format!("tcp::{gdb}")]); 159 | }) 160 | .as_mut() 161 | .status(); 162 | if let Err(e) = status { 163 | if e.kind() == io::ErrorKind::NotFound { 164 | println!("xtask: QEMU command not found. Does your system have QEMU installed and environment variable configured?"); 165 | println!("xtask: error: {e}"); 166 | } else { 167 | println!("xtask: error: {e}"); 168 | } 169 | process::exit(1); 170 | } 171 | } 172 | } 173 | 174 | fn package>(name: Option) -> &'static str { 175 | if let Some(t) = name { 176 | match t.as_ref().to_lowercase().as_str() { 177 | "test" | "test-kernel" => "test-kernel", 178 | "bench" | "bench-kernel" => "bench-kernel", 179 | "rustsbi" | "rustsbi-qemu" => "rustsbi-qemu", 180 | _ => panic!(), 181 | } 182 | } else { 183 | "rustsbi-qemu" 184 | } 185 | } 186 | 187 | #[test] 188 | fn test() { 189 | QemuArgs::default().run(); 190 | } 191 | --------------------------------------------------------------------------------