├── .cargo └── config.toml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── bootloader └── rustsbi-qemu.bin ├── docs ├── exception.md ├── guest-os.md └── images │ ├── debug1.jpg │ ├── hypocaust.png │ ├── layout.png │ └── run1.jpg ├── rust-toolchain.toml └── src ├── boards └── qemu.rs ├── console.rs ├── constants ├── csr.rs ├── layout.rs └── mod.rs ├── debug ├── backtrace.rs ├── mod.rs └── pagedebug.rs ├── device_emu ├── mod.rs ├── plic.rs ├── uart.rs └── virtio.rs ├── guest ├── context.rs ├── mod.rs ├── pmap.rs ├── sbi.rs └── switch.rs ├── hypervisor ├── fdt.rs ├── hyp_alloc │ ├── frame_allocator.rs │ ├── heap_allocator.rs │ └── mod.rs ├── mod.rs ├── shared.rs └── trap │ ├── context.rs │ ├── device.rs │ ├── forward.rs │ ├── inst_fault.rs │ ├── mod.rs │ ├── page_fault.rs │ └── trap.S ├── lang_items.rs ├── linker.ld ├── main.rs ├── mm ├── memory_region.rs ├── memory_set.rs └── mod.rs ├── page_table ├── address.rs ├── mod.rs ├── pte.rs ├── sv39.rs ├── sv48.rs └── sv57.rs ├── sbi.rs ├── sync ├── mod.rs ├── mutex.rs └── up.rs └── timer.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "riscv64gc-unknown-none-elf" 3 | 4 | [target.riscv64gc-unknown-none-elf] 5 | rustflags = [ 6 | "-Clink-arg=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes" 7 | ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | hyper.S 3 | guest.S 4 | guest_kernel 5 | .gdb_history 6 | fs.img 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "minikernel"] 2 | path = minikernel 3 | url = git@github.com:KuangjuX/minikernel.git 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.20" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "arrayvec" 16 | version = "0.7.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.1.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 25 | 26 | [[package]] 27 | name = "bare-metal" 28 | version = "0.2.5" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" 31 | dependencies = [ 32 | "rustc_version", 33 | ] 34 | 35 | [[package]] 36 | name = "bit_field" 37 | version = "0.10.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 40 | 41 | [[package]] 42 | name = "bitflags" 43 | version = "1.3.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 46 | 47 | [[package]] 48 | name = "buddy_system_allocator" 49 | version = "0.6.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "b4e85e760e105b46ae0bd1236578793c6c147ae7463fe95c8350296b8bfcb830" 52 | dependencies = [ 53 | "spin 0.7.1", 54 | ] 55 | 56 | [[package]] 57 | name = "byteorder" 58 | version = "1.4.3" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 61 | 62 | [[package]] 63 | name = "cfg-if" 64 | version = "1.0.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 67 | 68 | [[package]] 69 | name = "fdt" 70 | version = "0.1.5" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" 73 | 74 | [[package]] 75 | name = "hypocaust" 76 | version = "0.1.0" 77 | dependencies = [ 78 | "arrayvec", 79 | "bitflags", 80 | "buddy_system_allocator", 81 | "fdt", 82 | "lazy_static", 83 | "riscv", 84 | "riscv-decode", 85 | "sbi-rt", 86 | "spin 0.9.4", 87 | "virtio-drivers", 88 | "xmas-elf", 89 | ] 90 | 91 | [[package]] 92 | name = "lazy_static" 93 | version = "1.4.0" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 96 | dependencies = [ 97 | "spin 0.5.2", 98 | ] 99 | 100 | [[package]] 101 | name = "lock_api" 102 | version = "0.4.9" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 105 | dependencies = [ 106 | "autocfg", 107 | "scopeguard", 108 | ] 109 | 110 | [[package]] 111 | name = "log" 112 | version = "0.4.17" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 115 | dependencies = [ 116 | "cfg-if", 117 | ] 118 | 119 | [[package]] 120 | name = "memchr" 121 | version = "2.5.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 124 | 125 | [[package]] 126 | name = "proc-macro2" 127 | version = "1.0.51" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" 130 | dependencies = [ 131 | "unicode-ident", 132 | ] 133 | 134 | [[package]] 135 | name = "quote" 136 | version = "1.0.23" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 139 | dependencies = [ 140 | "proc-macro2", 141 | ] 142 | 143 | [[package]] 144 | name = "regex" 145 | version = "1.7.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" 148 | dependencies = [ 149 | "aho-corasick", 150 | "memchr", 151 | "regex-syntax", 152 | ] 153 | 154 | [[package]] 155 | name = "regex-syntax" 156 | version = "0.6.28" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 159 | 160 | [[package]] 161 | name = "riscv" 162 | version = "0.6.0" 163 | source = "git+https://github.com/rcore-os/riscv#11d43cf7cccb3b62a3caaf3e07a1db7449588f9a" 164 | dependencies = [ 165 | "bare-metal", 166 | "bit_field", 167 | "bitflags", 168 | "log", 169 | "riscv-target", 170 | ] 171 | 172 | [[package]] 173 | name = "riscv-decode" 174 | version = "0.2.1" 175 | source = "git+https://github.com/KuangjuX/riscv-decode.git#0e346d0ad041987023caa6f7f832185ed568630b" 176 | 177 | [[package]] 178 | name = "riscv-target" 179 | version = "0.1.2" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" 182 | dependencies = [ 183 | "lazy_static", 184 | "regex", 185 | ] 186 | 187 | [[package]] 188 | name = "rustc_version" 189 | version = "0.2.3" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 192 | dependencies = [ 193 | "semver", 194 | ] 195 | 196 | [[package]] 197 | name = "sbi-rt" 198 | version = "0.0.2" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "8c113c53291db8ac141e01f43224ed488b8d6001ab66737b82e04695a43a42b7" 201 | dependencies = [ 202 | "sbi-spec", 203 | ] 204 | 205 | [[package]] 206 | name = "sbi-spec" 207 | version = "0.0.4" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "6d4027cf9bb591a9fd0fc0e283be6165c5abe96cb73e9f0e24738c227f425377" 210 | dependencies = [ 211 | "static_assertions", 212 | ] 213 | 214 | [[package]] 215 | name = "scopeguard" 216 | version = "1.1.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 219 | 220 | [[package]] 221 | name = "semver" 222 | version = "0.9.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 225 | dependencies = [ 226 | "semver-parser", 227 | ] 228 | 229 | [[package]] 230 | name = "semver-parser" 231 | version = "0.7.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 234 | 235 | [[package]] 236 | name = "spin" 237 | version = "0.5.2" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 240 | 241 | [[package]] 242 | name = "spin" 243 | version = "0.7.1" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162" 246 | 247 | [[package]] 248 | name = "spin" 249 | version = "0.9.4" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" 252 | dependencies = [ 253 | "lock_api", 254 | ] 255 | 256 | [[package]] 257 | name = "static_assertions" 258 | version = "1.1.0" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 261 | 262 | [[package]] 263 | name = "syn" 264 | version = "1.0.107" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" 267 | dependencies = [ 268 | "proc-macro2", 269 | "quote", 270 | "unicode-ident", 271 | ] 272 | 273 | [[package]] 274 | name = "unicode-ident" 275 | version = "1.0.6" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" 278 | 279 | [[package]] 280 | name = "virtio-drivers" 281 | version = "0.3.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "aedaf1192446dc383b6bfb02d6a47813078932a96c0589e42b87ce684b934c40" 284 | dependencies = [ 285 | "bitflags", 286 | "log", 287 | "zerocopy", 288 | ] 289 | 290 | [[package]] 291 | name = "xmas-elf" 292 | version = "0.7.0" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "e74de9a366f6ab8c405fa6b371d9ac24943921fa14b3d64afcb202065c405f11" 295 | dependencies = [ 296 | "zero", 297 | ] 298 | 299 | [[package]] 300 | name = "zero" 301 | version = "0.1.3" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784" 304 | 305 | [[package]] 306 | name = "zerocopy" 307 | version = "0.6.1" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236" 310 | dependencies = [ 311 | "byteorder", 312 | "zerocopy-derive", 313 | ] 314 | 315 | [[package]] 316 | name = "zerocopy-derive" 317 | version = "0.3.2" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "6505e6815af7de1746a08f69c69606bb45695a17149517680f3b2149713b19a3" 320 | dependencies = [ 321 | "proc-macro2", 322 | "quote", 323 | "syn", 324 | ] 325 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hypocaust" 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 | riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } 10 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 11 | buddy_system_allocator = "0.6" 12 | bitflags = "1.2.1" 13 | xmas-elf = "0.7.0" 14 | riscv-decode = { git = "https://github.com/KuangjuX/riscv-decode.git" } 15 | sbi-rt = "0.0.2" 16 | spin = "0.9.4" 17 | arrayvec = { version = "0.7.2", default-features = false } 18 | virtio-drivers = { version = "0.3.0" } 19 | fdt = { version = "0.1.5" } 20 | 21 | [features] 22 | embed_guest_kernel = [] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ChengXiang Qi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET := riscv64gc-unknown-none-elf 2 | MODE := debug 3 | KERNEL_ELF := target/$(TARGET)/$(MODE)/hypocaust 4 | KERNEL_BIN := target/$(TARGET)/$(MODE)/hypocaust.bin 5 | CPUS := 1 6 | 7 | BOARD := qemu 8 | 9 | GDB := gdb-multiarch 10 | 11 | FS_IMG := fs.img 12 | 13 | # 客户操作系统 14 | GUEST_KERNEL_ELF := ./guest_kernel 15 | # GUEST_KERNEL_BIN := minikernel/target/$(TARGET)/$(MODE)/minikernel.bin 16 | 17 | GUEST_KERNEL_FEATURE:=$(if $(GUEST_KERNEL_ELF), --features embed_guest_kernel, ) 18 | 19 | OBJDUMP := rust-objdump --arch-name=riscv64 20 | OBJCOPY := rust-objcopy --binary-architecture=riscv64 21 | 22 | QEMU := qemu-system-riscv64 23 | BOOTLOADER := bootloader/rustsbi-qemu.bin 24 | 25 | KERNEL_ENTRY_PA := 0x80200000 26 | 27 | QEMUOPTS = --machine virt -m 3G -bios $(BOOTLOADER) -nographic 28 | QEMUOPTS +=-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) 29 | QEMUOPTS +=-drive file=$(FS_IMG),if=none,format=raw,id=x0 30 | QEMUOPTS +=-device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 31 | 32 | 33 | 34 | 35 | $(GUEST_KERNEL_ELF): 36 | cd minikernel/user && cargo build --release 37 | cd minikernel && cargo build && cp target/$(TARGET)/$(MODE)/minikernel ../guest_kernel 38 | 39 | # $(GUEST_KERNEL_BIN): $(GUEST_KERNEL_ELF) 40 | # $(OBJCOPY) $(GUEST_KERNEL_ELF) --strip-all -O binary $@ 41 | 42 | build: $(GUEST_KERNEL_ELF) $(FS_IMG) 43 | cargo build $(GUEST_KERNEL_FEATURE) 44 | 45 | $(KERNEL_BIN): build 46 | $(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@ 47 | 48 | 49 | 50 | qemu: $(KERNEL_BIN) 51 | $(QEMU) $(QEMUOPTS) 52 | 53 | clean: 54 | cargo clean 55 | cd minikernel && cargo clean 56 | cd minikernel/user && cargo clean 57 | rm guest_kernel *.S $(FS_IMG) 58 | 59 | qemu-gdb: $(KERNEL_ELF) 60 | $(QEMU) $(QEMUOPTS) -S -gdb tcp::1234 61 | 62 | gdb: $(KERNEL_ELF) 63 | $(GDB) $(KERNEL_ELF) 64 | 65 | debug: $(KERNEL_BIN) 66 | @tmux new-session -d \ 67 | "$(QEMU) $(QEMUOPTS) -s -S" && \ 68 | tmux split-window -h "$(GDB) -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \ 69 | tmux -2 attach-session -d 70 | 71 | asm: 72 | riscv64-unknown-elf-objdump -d target/riscv64gc-unknown-none-elf/debug/hypocaust > hyper.S 73 | riscv64-unknown-elf-objdump -d guest_kernel > guest.S 74 | 75 | 76 | $(FS_IMG): 77 | cd minikernel && make fs-img 78 | cp minikernel/user/target/$(TARGET)/release/fs.img ./ 79 | # touch fs.img -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hypocaust 2 | **Note:** 3 | Currently developing the hardware-assisted virtualization project [**hypocaust-2**](https://github.com/KuangjuX/hypocaust-2) 4 | 5 | ## Overview 6 | **Hypocaust** is an experimental S-mode trap and emulate type-1 hypervisor for RISC-V. It is currently targeted at QEMU's virt machine type. It can run on [RustSBI](https://github.com/rustsbi/rustsbi) currently. It can boot and run `minikernel` now. 7 | 8 | 9 | 10 | ![](docs/images/hypocaust.png) 11 | ![](docs/images/run1.jpg) 12 | ![](docs/images/debug1.jpg) 13 | 14 | ## Environment 15 | - QEMU 7.0.0 16 | - rust 1.66.0 17 | 18 | ## Build 19 | ``` 20 | git clone https://github.com/KuangjuX/hypocaust.git 21 | cd hypocaust 22 | git submodule update --init 23 | 24 | make qemu 25 | ``` 26 | 27 | ## Memory Region 28 | - DRAM Memory Region: 0x80000000 - 0x140000000 3GB 29 | - hypervisor: 128MB 30 | - Guest Kernel: 128MB 31 | 32 | ### Hypervisor Memory Region 33 | | HVA Start | HVA End | HPA Start | HPA End | Memory Region | 34 | | --------------| ----------- | -------------- | ------------ | ------------- | 35 | | 0x80000000 | 0x80200000 | 0x80000000 | 0x80200000 |RustSBI | 36 | | 0x80200000 | 0xC0000000 | 0x80200000 | 0x88000000 |hypervisor | 37 | | 0x88000000 | 0x8FFFFFFF | 0x88000000 | 0x8FFFFFFF | Guest Kernel 1 | 38 | | 0x90000000 | 0x97FFFFFF | 0x90000000 | 0x97FFFFFF | Guest Kernel 2 | 39 | | 0x98000000 | 0x9FFFFFFF | 0x98000000 | 0x9FFFFFFF | Guest Kernel 3 | 40 | | 0x100000000 | 0x17FFFFFFF | 0x100000000| 0x17FFFFFFF| Guest Kernel 1 Shadow Page Table | 41 | 42 | ### Resvered Memory Region 43 | | VA Start | VA End | Memory Region | 44 | | ---------|--------| -------------- | 45 | | 0xFFFFFFFFFFFFF000 | 0xFFFFFFFFFFFFFFFF | Trampoline | 46 | | 0xFFFFFFFFFFFFE000 | 0xFFFFFFFFFFFFEFFF | Trap Context | 47 | 48 | ### Guest Kernel Memory Region 49 | | GVA | GPA | HVA | Memory Region | 50 | | ---- | ---- | ---- | ---- | 51 | | 0x80000000 - 0x87FFFFFF | 0x80000000 - 0x87FFFFFF | 0x88000000 - 0x8FFFFFFF | Guest Kernel 1 | 52 | | 0x80000000 - 0x87FFFFFF | 0x80000000 - 0x87FFFFFF | 0x90000000 - 0x97FFFFFF | Guest Kernel 2| 53 | | 0x80000000 - 0x87FFFFFF | 0x80000000 - 0x87FFFFFF | 0x98000000 - 0x9FFFFFFF | Guest Kernel 3 | 54 | 55 | ![](docs/images/layout.png) 56 | 57 | ## Supported Platforms 58 | - QEMU virt machine type 59 | 60 | ## RoadMap 61 | - [x] Load guest kernel && Run guest kernel 62 | - [x] Trap and emulate of privileged instructions(CSR related and SFENCE>VMA) 63 | - [x] Shadow page tables 64 | - [x] Synchronize guest page table & shadow page table 65 | - [x] Foward Expections & Interrupts 66 | - [x] Timers 67 | - [ ] Serial IO emulate 68 | - [ ] Handle external interrupts 69 | - [ ] Expose and/or emulate peripherals 70 | - [ ] passthrough virtio block and networkd devices 71 | - [ ] multicore supported 72 | - [ ] multiguest supported 73 | 74 | ## References 75 | - [rcore-os/rCore-Tutorial-v3](https://github.com/rcore-os/rCore-Tutorial-v3) 76 | - [mit-pdos/RVirt](https://github.com/mit-pdos/RVirt) 77 | 78 | ## Questions 79 | See also: 80 | - [Synchronize satp & SPT](https://github.com/KuangjuX/hypocaust/issues/1) 81 | 82 | ## License 83 | MIT License 84 | 85 | Copyright (c) 2022 ChengXiang Qi 86 | 87 | Permission is hereby granted, free of charge, to any person obtaining a copy 88 | of this software and associated documentation files (the "Software"), to deal 89 | in the Software without restriction, including without limitation the rights 90 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 91 | copies of the Software, and to permit persons to whom the Software is 92 | furnished to do so, subject to the following conditions: 93 | 94 | The above copyright notice and this permission notice shall be included in all 95 | copies or substantial portions of the Software. 96 | 97 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 98 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 99 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 100 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 101 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 102 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 103 | SOFTWARE. 104 | 105 | -------------------------------------------------------------------------------- /bootloader/rustsbi-qemu.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust/3a2fcf6b4b22d218d42ecf59042bc329c59872e6/bootloader/rustsbi-qemu.bin -------------------------------------------------------------------------------- /docs/exception.md: -------------------------------------------------------------------------------- 1 | # 用户态异常 -------------------------------------------------------------------------------- /docs/guest-os.md: -------------------------------------------------------------------------------- 1 | # 一些关于 Guest OS 的改动说明 2 | - 内核的启动地址从 0x8000_0000 开始 3 | - 内核使用 sv39 页表模式,使用低 256G 虚拟内存 4 | - 内核对于 DMA 的绕过 MMU,不经过影子页表,需要自行进行地址转换 5 | - 对页表释放时需要使用 `sb`指令 -------------------------------------------------------------------------------- /docs/images/debug1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust/3a2fcf6b4b22d218d42ecf59042bc329c59872e6/docs/images/debug1.jpg -------------------------------------------------------------------------------- /docs/images/hypocaust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust/3a2fcf6b4b22d218d42ecf59042bc329c59872e6/docs/images/hypocaust.png -------------------------------------------------------------------------------- /docs/images/layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust/3a2fcf6b4b22d218d42ecf59042bc329c59872e6/docs/images/layout.png -------------------------------------------------------------------------------- /docs/images/run1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust/3a2fcf6b4b22d218d42ecf59042bc329c59872e6/docs/images/run1.jpg -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" -------------------------------------------------------------------------------- /src/boards/qemu.rs: -------------------------------------------------------------------------------- 1 | //! Constants used in rCore for qemu 2 | 3 | pub const CLOCK_FREQ: usize = 12500000; 4 | 5 | pub const MMIO: &[(usize, usize)] = &[ 6 | (0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine 7 | (0x1000_1000, 0x00_1000), // Virtio Block in virt machine 8 | ]; 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/console.rs: -------------------------------------------------------------------------------- 1 | //! SBI console driver, for text output 2 | 3 | use crate::sbi::console_putchar; 4 | use core::fmt::{self, Write}; 5 | 6 | struct Stdout; 7 | 8 | impl Write for Stdout { 9 | fn write_str(&mut self, s: &str) -> fmt::Result { 10 | for c in s.chars() { 11 | console_putchar(c as usize); 12 | } 13 | Ok(()) 14 | } 15 | } 16 | 17 | pub fn print(args: fmt::Arguments) { 18 | Stdout.write_fmt(args).unwrap(); 19 | } 20 | 21 | #[macro_export] 22 | /// print string macro 23 | macro_rules! print { 24 | ($fmt: literal $(, $($arg: tt)+)?) => { 25 | $crate::console::print(format_args!($fmt $(, $($arg)+)?)); 26 | } 27 | } 28 | 29 | #[macro_export] 30 | /// println string macro 31 | macro_rules! println { 32 | ($fmt: literal $(, $($arg: tt)+)?) => { 33 | $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); 34 | } 35 | } 36 | 37 | #[macro_export] 38 | macro_rules! hdebug { 39 | ($fmt: literal $(, $($arg: tt)+)?) => { 40 | $crate::console::print(format_args!(concat!("[Hypervisor] ", $fmt, "\n") $(, $($arg)+)?)); 41 | } 42 | } 43 | 44 | #[macro_export] 45 | macro_rules! hwarning { 46 | ($fmt: literal $(, $($arg: tt)+)?) => { 47 | $crate::console::print(format_args!(concat!("[Warning] ", $fmt, "\n") $(, $($arg)+)?)); 48 | } 49 | } 50 | 51 | #[macro_export] 52 | macro_rules! htracking { 53 | ($fmt: literal $(, $($arg: tt)+)?) => { 54 | $crate::console::print(format_args!(concat!("[Tracking] ", $fmt, "\n") $(, $($arg)+)?)); 55 | } 56 | } 57 | 58 | #[macro_export] 59 | macro_rules! herror { 60 | ($fmt: literal $(, $($arg: tt)+)?) => { 61 | $crate::console::print(format_args!(concat!("\x1b[1;31m[Error] ", $fmt, "\x1b[0m\n") $(, $($arg)+)?)); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/constants/csr.rs: -------------------------------------------------------------------------------- 1 | pub const ustatus: usize = 0x000; 2 | pub const uie: usize = 0x004; 3 | pub const utvec: usize = 0x005; 4 | pub const uscratch: usize = 0x040; 5 | pub const uepc: usize = 0x041; 6 | pub const ucause: usize = 0x042; 7 | pub const utval: usize = 0x043; 8 | pub const uip: usize = 0x044; 9 | pub const fflags: usize = 0x001; 10 | pub const frm: usize = 0x002; 11 | pub const fcsr: usize = 0x003; 12 | pub const cycle: usize = 0xc00; 13 | pub const time: usize = 0xc01; 14 | pub const instret: usize = 0xc02; 15 | pub const hpmcounter3: usize = 0xc03; 16 | pub const hpmcounter4: usize = 0xc04; 17 | pub const hpmcounter5: usize = 0xc05; 18 | pub const hpmcounter6: usize = 0xc06; 19 | pub const hpmcounter7: usize = 0xc07; 20 | pub const hpmcounter8: usize = 0xc08; 21 | pub const hpmcounter9: usize = 0xc09; 22 | pub const hpmcounter10: usize = 0xc0a; 23 | pub const hpmcounter11: usize = 0xc0b; 24 | pub const hpmcounter12: usize = 0xc0c; 25 | pub const hpmcounter13: usize = 0xc0d; 26 | pub const hpmcounter14: usize = 0xc0e; 27 | pub const hpmcounter15: usize = 0xc0f; 28 | pub const hpmcounter16: usize = 0xc10; 29 | pub const hpmcounter17: usize = 0xc11; 30 | pub const hpmcounter18: usize = 0xc12; 31 | pub const hpmcounter19: usize = 0xc13; 32 | pub const hpmcounter20: usize = 0xc14; 33 | pub const hpmcounter21: usize = 0xc15; 34 | pub const hpmcounter22: usize = 0xc16; 35 | pub const hpmcounter23: usize = 0xc17; 36 | pub const hpmcounter24: usize = 0xc18; 37 | pub const hpmcounter25: usize = 0xc19; 38 | pub const hpmcounter26: usize = 0xc1a; 39 | pub const hpmcounter27: usize = 0xc1b; 40 | pub const hpmcounter28: usize = 0xc1c; 41 | pub const hpmcounter29: usize = 0xc1d; 42 | pub const hpmcounter30: usize = 0xc1e; 43 | pub const hpmcounter31: usize = 0xc1f; 44 | pub const cycleh: usize = 0xc80; 45 | pub const timeh: usize = 0xc81; 46 | pub const instreth: usize = 0xc82; 47 | pub const hpmcounter3h: usize = 0xc83; 48 | pub const hpmcounter4h: usize = 0xc84; 49 | pub const hpmcounter5h: usize = 0xc85; 50 | pub const hpmcounter6h: usize = 0xc86; 51 | pub const hpmcounter7h: usize = 0xc87; 52 | pub const hpmcounter8h: usize = 0xc88; 53 | pub const hpmcounter9h: usize = 0xc89; 54 | pub const hpmcounter10h: usize = 0xc8a; 55 | pub const hpmcounter11h: usize = 0xc8b; 56 | pub const hpmcounter12h: usize = 0xc8c; 57 | pub const hpmcounter13h: usize = 0xc8d; 58 | pub const hpmcounter14h: usize = 0xc8e; 59 | pub const hpmcounter15h: usize = 0xc8f; 60 | pub const hpmcounter16h: usize = 0xc90; 61 | pub const hpmcounter17h: usize = 0xc91; 62 | pub const hpmcounter18h: usize = 0xc92; 63 | pub const hpmcounter19h: usize = 0xc93; 64 | pub const hpmcounter20h: usize = 0xc94; 65 | pub const hpmcounter21h: usize = 0xc95; 66 | pub const hpmcounter22h: usize = 0xc96; 67 | pub const hpmcounter23h: usize = 0xc97; 68 | pub const hpmcounter24h: usize = 0xc98; 69 | pub const hpmcounter25h: usize = 0xc99; 70 | pub const hpmcounter26h: usize = 0xc9a; 71 | pub const hpmcounter27h: usize = 0xc9b; 72 | pub const hpmcounter28h: usize = 0xc9c; 73 | pub const hpmcounter29h: usize = 0xc9d; 74 | pub const hpmcounter30h: usize = 0xc9e; 75 | pub const hpmcounter31h: usize = 0xc9f; 76 | pub const mcycle: usize = 0xb00; 77 | pub const minstret: usize = 0xb02; 78 | pub const mcycleh: usize = 0xb80; 79 | pub const minstreth: usize = 0xb82; 80 | pub const mvendorid: usize = 0xf11; 81 | pub const marchid: usize = 0xf12; 82 | pub const mimpid: usize = 0xf13; 83 | pub const mhartid: usize = 0xf14; 84 | pub const mstatus: usize = 0x300; 85 | pub const misa: usize = 0x301; 86 | pub const medeleg: usize = 0x302; 87 | pub const mideleg: usize = 0x303; 88 | pub const mie: usize = 0x304; 89 | pub const mtvec: usize = 0x305; 90 | pub const mcounteren: usize = 0x306; 91 | pub const mtvt: usize = 0x307; 92 | pub const mucounteren: usize = 0x320; 93 | pub const mscounteren: usize = 0x321; 94 | pub const mscratch: usize = 0x340; 95 | pub const mepc: usize = 0x341; 96 | pub const mcause: usize = 0x342; 97 | pub const mbadaddr: usize = 0x343; 98 | pub const mtval: usize = 0x343; 99 | pub const mip: usize = 0x344; 100 | pub const mnxti: usize = 0x345; 101 | pub const mintstatus: usize = 0x346; 102 | pub const mscratchcsw: usize = 0x348; 103 | pub const sstatus: usize = 0x100; 104 | pub const sedeleg: usize = 0x102; 105 | pub const sideleg: usize = 0x103; 106 | pub const sie: usize = 0x104; 107 | pub const stvec: usize = 0x105; 108 | pub const scounteren: usize = 0x106; 109 | pub const stvt: usize = 0x107; 110 | pub const sscratch: usize = 0x140; 111 | pub const sepc: usize = 0x141; 112 | pub const scause: usize = 0x142; 113 | pub const sbadaddr: usize = 0x143; 114 | pub const stval: usize = 0x143; 115 | pub const sip: usize = 0x144; 116 | pub const snxti: usize = 0x145; 117 | pub const sintstatus: usize = 0x146; 118 | pub const sscratchcsw: usize = 0x148; 119 | pub const sptbr: usize = 0x180; 120 | pub const satp: usize = 0x180; 121 | pub const pmpcfg0: usize = 0x3a0; 122 | pub const pmpcfg1: usize = 0x3a1; 123 | pub const pmpcfg2: usize = 0x3a2; 124 | pub const pmpcfg3: usize = 0x3a3; 125 | pub const pmpaddr0: usize = 0x3b0; 126 | pub const pmpaddr1: usize = 0x3b1; 127 | pub const pmpaddr2: usize = 0x3b2; 128 | pub const pmpaddr3: usize = 0x3b3; 129 | pub const pmpaddr4: usize = 0x3b4; 130 | pub const pmpaddr5: usize = 0x3b5; 131 | pub const pmpaddr6: usize = 0x3b6; 132 | pub const pmpaddr7: usize = 0x3b7; 133 | pub const pmpaddr8: usize = 0x3b8; 134 | pub const pmpaddr9: usize = 0x3b9; 135 | pub const pmpaddr10: usize = 0x3ba; 136 | pub const pmpaddr11: usize = 0x3bb; 137 | pub const pmpaddr12: usize = 0x3bc; 138 | pub const pmpaddr13: usize = 0x3bd; 139 | pub const pmpaddr14: usize = 0x3be; 140 | pub const pmpaddr15: usize = 0x3bf; 141 | pub const tselect: usize = 0x7a0; 142 | pub const tdata1: usize = 0x7a1; 143 | pub const tdata2: usize = 0x7a2; 144 | pub const tdata3: usize = 0x7a3; 145 | pub const dcsr: usize = 0x7b0; 146 | pub const dpc: usize = 0x7b1; 147 | pub const dscratch: usize = 0x7b2; 148 | pub const mhpmcounter3: usize = 0xb03; 149 | pub const mhpmcounter4: usize = 0xb04; 150 | pub const mhpmcounter5: usize = 0xb05; 151 | pub const mhpmcounter6: usize = 0xb06; 152 | pub const mhpmcounter7: usize = 0xb07; 153 | pub const mhpmcounter8: usize = 0xb08; 154 | pub const mhpmcounter9: usize = 0xb09; 155 | pub const mhpmcounter10: usize = 0xb0a; 156 | pub const mhpmcounter11: usize = 0xb0b; 157 | pub const mhpmcounter12: usize = 0xb0c; 158 | pub const mhpmcounter13: usize = 0xb0d; 159 | pub const mhpmcounter14: usize = 0xb0e; 160 | pub const mhpmcounter15: usize = 0xb0f; 161 | pub const mhpmcounter16: usize = 0xb10; 162 | pub const mhpmcounter17: usize = 0xb11; 163 | pub const mhpmcounter18: usize = 0xb12; 164 | pub const mhpmcounter19: usize = 0xb13; 165 | pub const mhpmcounter20: usize = 0xb14; 166 | pub const mhpmcounter21: usize = 0xb15; 167 | pub const mhpmcounter22: usize = 0xb16; 168 | pub const mhpmcounter23: usize = 0xb17; 169 | pub const mhpmcounter24: usize = 0xb18; 170 | pub const mhpmcounter25: usize = 0xb19; 171 | pub const mhpmcounter26: usize = 0xb1a; 172 | pub const mhpmcounter27: usize = 0xb1b; 173 | pub const mhpmcounter28: usize = 0xb1c; 174 | pub const mhpmcounter29: usize = 0xb1d; 175 | pub const mhpmcounter30: usize = 0xb1e; 176 | pub const mhpmcounter31: usize = 0xb1f; 177 | pub const mhpmevent3: usize = 0x323; 178 | pub const mhpmevent4: usize = 0x324; 179 | pub const mhpmevent5: usize = 0x325; 180 | pub const mhpmevent6: usize = 0x326; 181 | pub const mhpmevent7: usize = 0x327; 182 | pub const mhpmevent8: usize = 0x328; 183 | pub const mhpmevent9: usize = 0x329; 184 | pub const mhpmevent10: usize = 0x32a; 185 | pub const mhpmevent11: usize = 0x32b; 186 | pub const mhpmevent12: usize = 0x32c; 187 | pub const mhpmevent13: usize = 0x32d; 188 | pub const mhpmevent14: usize = 0x32e; 189 | pub const mhpmevent15: usize = 0x32f; 190 | pub const mhpmevent16: usize = 0x330; 191 | pub const mhpmevent17: usize = 0x331; 192 | pub const mhpmevent18: usize = 0x332; 193 | pub const mhpmevent19: usize = 0x333; 194 | pub const mhpmevent20: usize = 0x334; 195 | pub const mhpmevent21: usize = 0x335; 196 | pub const mhpmevent22: usize = 0x336; 197 | pub const mhpmevent23: usize = 0x337; 198 | pub const mhpmevent24: usize = 0x338; 199 | pub const mhpmevent25: usize = 0x339; 200 | pub const mhpmevent26: usize = 0x33a; 201 | pub const mhpmevent27: usize = 0x33b; 202 | pub const mhpmevent28: usize = 0x33c; 203 | pub const mhpmevent29: usize = 0x33d; 204 | pub const mhpmevent30: usize = 0x33e; 205 | pub const mhpmevent31: usize = 0x33f; 206 | pub const mhpmcounter3h: usize = 0xb83; 207 | pub const mhpmcounter4h: usize = 0xb84; 208 | pub const mhpmcounter5h: usize = 0xb85; 209 | pub const mhpmcounter6h: usize = 0xb86; 210 | pub const mhpmcounter7h: usize = 0xb87; 211 | pub const mhpmcounter8h: usize = 0xb88; 212 | pub const mhpmcounter9h: usize = 0xb89; 213 | pub const mhpmcounter10h: usize = 0xb8a; 214 | pub const mhpmcounter11h: usize = 0xb8b; 215 | pub const mhpmcounter12h: usize = 0xb8c; 216 | pub const mhpmcounter13h: usize = 0xb8d; 217 | pub const mhpmcounter14h: usize = 0xb8e; 218 | pub const mhpmcounter15h: usize = 0xb8f; 219 | pub const mhpmcounter16h: usize = 0xb90; 220 | pub const mhpmcounter17h: usize = 0xb91; 221 | pub const mhpmcounter18h: usize = 0xb92; 222 | pub const mhpmcounter19h: usize = 0xb93; 223 | pub const mhpmcounter20h: usize = 0xb94; 224 | pub const mhpmcounter21h: usize = 0xb95; 225 | pub const mhpmcounter22h: usize = 0xb96; 226 | pub const mhpmcounter23h: usize = 0xb97; 227 | pub const mhpmcounter24h: usize = 0xb98; 228 | pub const mhpmcounter25h: usize = 0xb99; 229 | pub const mhpmcounter26h: usize = 0xb9a; 230 | pub const mhpmcounter27h: usize = 0xb9b; 231 | pub const mhpmcounter28h: usize = 0xb9c; 232 | pub const mhpmcounter29h: usize = 0xb9d; 233 | pub const mhpmcounter30h: usize = 0xb9e; 234 | pub const mhpmcounter31h: usize = 0xb9f; 235 | 236 | pub mod sie { 237 | /// software interrupts enable 238 | pub const SSIE: usize = 1 << 1; 239 | /// timer interrupts enable 240 | pub const STIE: usize = 1 << 5; 241 | /// external interrupts enable 242 | pub const SEIE: usize = 1 << 9; 243 | 244 | pub const SSIE_BIT: usize = 1; 245 | pub const STIE_BIT: usize = 5; 246 | pub const SEIE_BIT: usize = 9; 247 | } 248 | 249 | pub mod sip { 250 | /// software interrupts pending 251 | pub const SSIP: usize = 1 << 1; 252 | /// timer interrupts pending 253 | pub const STIP: usize = 1 << 5; 254 | /// external interrupts pending 255 | pub const SEIP: usize = 1 << 9; 256 | 257 | pub const SSIP_BIT: usize = 1; 258 | pub const STIP_BIT: usize = 5; 259 | pub const SEIP_BIT: usize = 9; 260 | } 261 | 262 | pub mod status { 263 | pub const STATUS_UIE: usize = 1 << 0; 264 | /// enable or disable all interupts in supervisor mode. 265 | pub const STATUS_SIE: usize = 1 << 1; 266 | pub const STATUS_UPIE: usize = 1 << 4; 267 | /// indicate whether supervisor interrupts were enabled prior to trapping into 268 | /// supervisor mode. When a trap is taken into supervisor mode, SPIE is set to SIE, 269 | /// and SIE is set to 0. When an SRET instruction is executed, SIE is set to SPIE, 270 | /// then SPIE is set to 1. 271 | pub const STATUS_SPIE: usize = 1 << 5; 272 | pub const STATUS_SPP: usize = 1 << 8; 273 | pub const STATUS_FS: usize = 3 << 13; 274 | pub const STATUS_XS: usize = 3 << 15; 275 | pub const STATUS_SUM: usize = 1 << 18; 276 | pub const STATUS_MXR: usize = 1 << 19; 277 | pub const STATUS_SD: usize = 1 << 63; 278 | 279 | pub const STATUS_SIE_BIT: usize = 1; 280 | 281 | pub const STATUS_SPIE_BIT: usize = 5; 282 | 283 | pub const STATUS_SPP_BIT: usize = 8; 284 | } -------------------------------------------------------------------------------- /src/constants/layout.rs: -------------------------------------------------------------------------------- 1 | //! Constants used in rCore 2 | 3 | pub const USER_STACK_SIZE: usize = 4096 * 2; 4 | pub const KERNEL_STACK_SIZE: usize = 4096 * 4; 5 | pub const KERNEL_HEAP_SIZE: usize = 0x30_0000; 6 | pub const MEMORY_START: usize = 0x80000000; 7 | pub const MEMORY_END: usize = 0x88000000; 8 | pub const PAGE_SIZE: usize = 0x1000; 9 | pub const PAGE_SIZE_BITS: usize = 0xc; 10 | 11 | /// 每个内核拥有 128 M 的空间 12 | pub const KERNEL_SPACE: usize = 128 * 1024 * 1024; 13 | 14 | pub const SPT_PA_START_1: usize = 0x10000_0000; 15 | pub const SPT_PA_END_1: usize = SPT_PA_START_1 + KERNEL_SPACE; 16 | 17 | // 客户操作系统内存映射 18 | pub const GUEST_KERNEL_PHY_START_1: usize = 0x8800_0000; 19 | pub const GUEST_KERNEL_PHY_END_1: usize = 0x9000_0000; 20 | pub const GUEST_KERNEL_VIRT_START: usize = 0x8000_0000; 21 | pub const GUEST_KERNEL_VIRT_END: usize = 0x8800_0000; 22 | 23 | pub const GUEST_KERNEL_PHY_START_2: usize = GUEST_KERNEL_PHY_START_1 + KERNEL_SPACE; 24 | pub const GUEST_KERNEL_VIRT_START_2: usize = 0x8000_0000; 25 | 26 | pub const GUEST_KERNEL_PHY_START_3: usize = GUEST_KERNEL_PHY_START_2 + KERNEL_SPACE; 27 | pub const GUEST_KERNEL_VIRT_START_3: usize = 0x8000_0000; 28 | 29 | pub const GUEST_KERNEL_OFFSET_1: usize = 0x800_0000; 30 | 31 | /// 测试内核的跳板页和 Trap Context 的地址 32 | pub const GUEST_MAX_VA: usize = 1 << (9 + 9 + 9 + 12 - 1); 33 | pub const GUEST_TRAMPOLINE: usize = GUEST_MAX_VA - PAGE_SIZE; 34 | pub const GUEST_TRAP_CONTEXT: usize = GUEST_TRAMPOLINE - PAGE_SIZE; 35 | 36 | 37 | /// 虚拟地址最高页为跳板页 38 | pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; 39 | /// 中断切换上下文 40 | pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE; 41 | /// Return (bottom, top) of a kernel stack in kernel space. 42 | pub fn kernel_stack_position(app_id: usize) -> (usize, usize) { 43 | let top = TRAMPOLINE - app_id * (KERNEL_STACK_SIZE + PAGE_SIZE); 44 | let bottom = top - KERNEL_STACK_SIZE; 45 | (bottom, top) 46 | } 47 | 48 | pub use crate::board::{CLOCK_FREQ, MMIO}; 49 | -------------------------------------------------------------------------------- /src/constants/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod layout; 2 | pub mod csr; -------------------------------------------------------------------------------- /src/debug/backtrace.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::layout::{TRAMPOLINE, TRAP_CONTEXT}; 2 | use crate::hypervisor::trap::TrapContext; 3 | use crate::page_table::{VirtPageNum, PageTable}; 4 | 5 | use super::PageDebug; 6 | 7 | #[allow(unused)] 8 | pub fn print_guest_backtrace(spt: &P, satp: usize, ctx: &TrapContext) { 9 | let pc = ctx.sepc; 10 | let mut ra = ctx.x[1]; 11 | let mut sp = ctx.x[2]; 12 | let mut fp = ctx.x[8]; 13 | 14 | let mut old_fp = 0; 15 | while old_fp != fp { 16 | hdebug!("ra -> {:#x}", ra); 17 | ra = match fp.checked_sub(8) { 18 | Some(va) => { 19 | let vpn = VirtPageNum::from(va >> 12); 20 | let offset = va & 0xfff; 21 | match spt.translate(vpn) { 22 | Some(ppn) => { 23 | let pa = offset + (ppn.ppn().0 << 12); 24 | unsafe{ core::ptr::read(pa as *const usize) } 25 | } 26 | None => break 27 | } 28 | }, 29 | None => break, 30 | }; 31 | 32 | old_fp = fp; 33 | 34 | fp = match fp.checked_sub(16) { 35 | Some(va) => { 36 | let vpn = VirtPageNum::from(va >> 12); 37 | let offset = va & 0xfff; 38 | match spt.translate(vpn) { 39 | Some(ppn) => { 40 | let pa = offset + (ppn.ppn().0 << 12); 41 | unsafe{ core::ptr::read(pa as *const usize) } 42 | } 43 | None => break 44 | } 45 | }, 46 | None => break, 47 | }; 48 | } 49 | } 50 | 51 | #[allow(unused)] 52 | pub fn print_hypervisor_backtrace(ctx: &TrapContext) { 53 | let mut ra = ctx.x[1]; 54 | let mut fp = ctx.x[8]; 55 | let mut old_fp = 0; 56 | while old_fp != fp { 57 | hdebug!("ra -> {:#x}", ra); 58 | ra = match fp.checked_sub(8) { 59 | Some(addr) => { 60 | if (addr >= 0x8020_0000 && addr <= 0x8800_0000) || (addr >= TRAP_CONTEXT && addr <= TRAMPOLINE) { 61 | unsafe{ core::ptr::read(addr as *const usize) } 62 | }else{ 63 | break; 64 | } 65 | }, 66 | None => break, 67 | }; 68 | 69 | old_fp = fp; 70 | 71 | fp = match fp.checked_sub(16) { 72 | Some(addr) => { 73 | if (addr >= 0x8020_0000 && addr <= 0x8800_0000) || (addr >= TRAP_CONTEXT && addr <= TRAMPOLINE) { 74 | unsafe{ core::ptr::read(addr as *const usize) } 75 | }else{ 76 | break; 77 | } 78 | }, 79 | None => break, 80 | }; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/debug/mod.rs: -------------------------------------------------------------------------------- 1 | mod backtrace; 2 | mod pagedebug; 3 | 4 | pub use backtrace::{ print_guest_backtrace, print_hypervisor_backtrace }; 5 | pub use pagedebug::PageDebug; 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/debug/pagedebug.rs: -------------------------------------------------------------------------------- 1 | use crate::page_table::{PageTableEntry, PageTableSv39, PhysPageNum, VirtPageNum, PageTable}; 2 | use crate::constants::layout::GUEST_TRAP_CONTEXT; 3 | use crate::hypervisor::trap::TrapContext; 4 | 5 | pub trait PageDebug { 6 | fn print_page_table(&self); 7 | fn print_guest_page_table(&self); 8 | fn print_trap_context(&self); 9 | } 10 | 11 | impl PageDebug for PageTableSv39 { 12 | fn print_page_table(&self) { 13 | let root_pte_array = self.root_ppn().get_pte_array(); 14 | hdebug!("print page table: "); 15 | print_page_table(root_pte_array, 3); 16 | } 17 | 18 | fn print_guest_page_table(&self) { 19 | let root_pte_array = self.root_ppn().get_pte_array(); 20 | hdebug!("print guest page table: "); 21 | print_guest_page_table(root_pte_array, 3); 22 | } 23 | 24 | fn print_trap_context(&self) { 25 | let trap_ctx_ppn = self.translate(VirtPageNum::from(GUEST_TRAP_CONTEXT >> 12)).unwrap().ppn().0; 26 | hdebug!("trap ctx ppn: {:#x}", trap_ctx_ppn); 27 | unsafe{ 28 | let trap_ctx = &*((trap_ctx_ppn << 12) as *const TrapContext); 29 | for i in 0..trap_ctx.x.len() { 30 | hdebug!("x{} -> {:#x}", i, trap_ctx.x[i]); 31 | } 32 | hdebug!("sepc -> {:#x}", trap_ctx.sepc); 33 | hdebug!("sstatus -> {:#x}", trap_ctx.sstatus.bits()); 34 | } 35 | } 36 | 37 | } 38 | 39 | pub fn print_page_table(pte_array: &[PageTableEntry], level: u8) { 40 | if level == 0 { return; } 41 | for i in 0..512 { 42 | let pte = pte_array[i]; 43 | if pte.is_valid() { 44 | for _ in 0..(3 - level) { 45 | print!(" "); 46 | } 47 | println!("{}: {:#x} {:?}", i, pte.ppn().0, pte.flags()); 48 | } 49 | if pte.is_valid() { 50 | assert!(level != 0); 51 | let pte_array = pte.ppn().get_pte_array(); 52 | print_page_table(pte_array, level - 1); 53 | } 54 | } 55 | } 56 | 57 | pub fn print_guest_page_table(pte_array: &[PageTableEntry], level: u8) { 58 | if level == 0 { return; } 59 | for i in 0..512 { 60 | let pte = pte_array[i]; 61 | if pte.is_valid() { 62 | for _ in 0..(3 - level) { 63 | print!(" "); 64 | } 65 | println!("{}: {:#x} {:?}", i, pte.ppn().0, pte.flags()); 66 | } 67 | if pte.is_valid() { 68 | assert!(level != 0); 69 | let ppn = PhysPageNum::from(((pte.ppn().0 << 12) + 0x800_0000) >> 12); 70 | let pte_array = ppn.get_pte_array(); 71 | print_guest_page_table(pte_array, level - 1); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/device_emu/mod.rs: -------------------------------------------------------------------------------- 1 | mod uart; 2 | mod plic; 3 | mod virtio; 4 | pub use uart::Uart; 5 | pub use plic::HostPlic; 6 | pub use virtio::{ VirtIO, is_device_access }; 7 | 8 | 9 | /// Software emulated device used in VMM 10 | pub struct VirtDevice { 11 | pub qemu_virt_tester: qemu_virt::QemuVirtTester, 12 | pub uart: Uart 13 | } 14 | 15 | impl VirtDevice { 16 | pub fn new(guest_id: usize) -> Self { 17 | Self { 18 | qemu_virt_tester: qemu_virt::QemuVirtTester::new(), 19 | uart: Uart::new(guest_id) 20 | } 21 | } 22 | 23 | } 24 | 25 | 26 | 27 | mod qemu_virt { 28 | use crate::mm::MemoryRegion; 29 | /// Software emulated qemu virt test 30 | pub struct QemuVirtTester { 31 | pub mmregs: MemoryRegion 32 | } 33 | 34 | impl QemuVirtTester { 35 | pub fn new() -> Self { 36 | Self { 37 | mmregs: MemoryRegion::new(0x10_0000, 0x1000) 38 | } 39 | } 40 | 41 | pub fn in_region(&self, addr: usize) -> bool { 42 | self.mmregs.in_region(addr) 43 | } 44 | 45 | pub fn base(&self) -> usize { 46 | self.mmregs.base() 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/device_emu/plic.rs: -------------------------------------------------------------------------------- 1 | use crate::mm::MemoryRegion; 2 | 3 | 4 | 5 | /// ref: https://github.com/mit-pdos/RVirt/blob/HEAD/src/context.rs 6 | /// hypervisor emulated plic for guest 7 | pub struct HostPlic { 8 | pub claim_clear: MemoryRegion 9 | } 10 | 11 | impl HostPlic { 12 | pub fn claim_and_clear(&mut self) -> u32 { 13 | let claim = self.claim_clear[0]; 14 | // 设置内存屏障 15 | core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); 16 | self.claim_clear[0] = claim; 17 | claim 18 | } 19 | } -------------------------------------------------------------------------------- /src/device_emu/uart.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::ArrayVec; 2 | 3 | pub struct Uart { 4 | pub dlab: bool, 5 | 6 | pub divisor_latch: u16, 7 | pub interrupt_enable: u8, 8 | 9 | pub next_interrupt_time: usize, 10 | 11 | pub input_fifo: [u8; 16], 12 | pub input_bytes_ready: usize, 13 | 14 | pub line_buffer: ArrayVec, 15 | pub guest_id: usize 16 | } 17 | 18 | impl Uart { 19 | pub const fn new(guest_id: usize) -> Self { 20 | Self{ 21 | dlab: false, 22 | interrupt_enable: 0, 23 | divisor_latch: 1, 24 | next_interrupt_time: 0, 25 | input_fifo: [0; 16], 26 | input_bytes_ready: 0, 27 | line_buffer: ArrayVec::new_const(), 28 | guest_id 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/device_emu/virtio.rs: -------------------------------------------------------------------------------- 1 | //! refs: https://github.com/mit-pdos/RVirt/blob/HEAD/src/virtio.rs 2 | 3 | use arrayvec::ArrayVec; 4 | 5 | use crate::mm::MemoryRegion; 6 | 7 | 8 | pub const MAX_QUEUES: usize = 4; 9 | pub const MAX_DEVICES: usize = 4; 10 | pub const MAX_PAGES: usize = MAX_DEVICES * MAX_QUEUES; 11 | 12 | pub struct VirtIO { 13 | pub devices: ArrayVec, 14 | pub queue_guest_pages: ArrayVec 15 | } 16 | 17 | #[derive(Copy, Clone)] 18 | pub struct Queue { 19 | /// Address guest thinks queue is mapped at 20 | guest_pa: usize, 21 | /// Address queue is actually mapped at 22 | host_pa: usize, 23 | /// Number of entries in queue 24 | size: usize 25 | } 26 | 27 | pub enum Device { 28 | Passthrough { 29 | /// Virtual Queue Index, offset=0x30 30 | queue_sel: u32, 31 | queues: [Queue; MAX_QUEUES], 32 | device_registers: MemoryRegion 33 | }, 34 | Unmapped 35 | } 36 | 37 | impl Device { 38 | pub unsafe fn new(host_base_address: usize) -> Self { 39 | Device::Passthrough { 40 | queue_sel: 0, 41 | queues: [Queue { guest_pa: 0, host_pa: 0, size: 0}; MAX_QUEUES], 42 | device_registers: MemoryRegion::new(host_base_address, 0x1000) 43 | } 44 | } 45 | } 46 | 47 | pub fn is_device_access(guest_pa: usize) -> bool { 48 | guest_pa >= 0x1000_1000 && guest_pa < 0x1000_2000 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/guest/context.rs: -------------------------------------------------------------------------------- 1 | use riscv::addr::BitField; 2 | 3 | 4 | use crate::hypervisor::trap::trap_return; 5 | use crate::constants::csr::status::{STATUS_SIE_BIT, STATUS_SPIE_BIT, STATUS_SPP_BIT}; 6 | use crate::page_table::PageTable; 7 | use crate::debug::PageDebug; 8 | use super::pmap::ShadowPageTables; 9 | 10 | 11 | pub struct ControlRegisters { 12 | // sedeleg: usize, -- Hard-wired to zero 13 | // sideleg: usize, -- Hard-wired to zero 14 | pub sstatus: usize, 15 | /// 中断使能寄存器 16 | pub sie: usize, 17 | /// 中断代理寄存器 18 | pub sip: usize, 19 | pub stvec: usize, 20 | // scounteren: usize, -- Hard-wired to zero 21 | pub sscratch: usize, 22 | pub sepc: usize, 23 | pub scause: usize, 24 | pub stval: usize, 25 | pub satp: usize, 26 | /// 用于设置 Guest OS 时钟中断 27 | pub mtimecmp: usize 28 | } 29 | 30 | impl ControlRegisters { 31 | pub const fn new() -> Self { 32 | Self { 33 | sstatus: 0, 34 | stvec: 0, 35 | sie: 0, 36 | sip: 0, 37 | sscratch: 0, 38 | sepc: 0, 39 | scause: 0, 40 | stval: 0, 41 | satp: 0, 42 | mtimecmp: core::usize::MAX 43 | } 44 | } 45 | } 46 | 47 | pub struct ShadowState { 48 | pub csrs: ControlRegisters, 49 | /// 影子页表 50 | pub shadow_page_tables: ShadowPageTables

, 51 | /// 是否发生中断 52 | pub interrupt: bool, 53 | /// 连续切换页表次数 54 | pub conseutive_satp_switch_count: usize 55 | } 56 | 57 | impl

ShadowState

where P: PageTable + PageDebug { 58 | pub const fn new() -> Self { 59 | Self { 60 | csrs: ControlRegisters::new(), 61 | shadow_page_tables: ShadowPageTables::new(), 62 | interrupt: false, 63 | conseutive_satp_switch_count: 0 64 | } 65 | } 66 | 67 | 68 | 69 | 70 | /// ref: riscv-privileged 71 | /// The `SPIE` bit indicates whether supervisor interrupts were enabled prior to 72 | /// trapping into supervisor mode. When a trap is taken into supervisor mode, `SPIE` is set 73 | /// to 0. When an `SRET` instruction is executed, `SIE` is set to `SPIE`, then `SPIE` is set to 1. 74 | pub fn push_sie(&mut self) { 75 | self.csrs.sstatus.set_bit(STATUS_SPIE_BIT, self.csrs.sstatus.get_bit(STATUS_SIE_BIT)); 76 | self.csrs.sstatus.set_bit(STATUS_SIE_BIT, false); 77 | } 78 | 79 | /// ref: riscv-privileged 80 | /// The `SPIE` bit indicates whether supervisor interrupts were enabled prior to 81 | /// trapping into supervisor mode. When a trap is taken into supervisor mode, `SPIE` is set 82 | /// to 0. When an `SRET` instruction is executed, `SIE` is set to `SPIE`, then `SPIE` is set to 1. 83 | pub fn pop_sie(&mut self) { 84 | if !self.csrs.sstatus.get_bit(STATUS_SIE_BIT) && self.csrs.sstatus.get_bit(STATUS_SPIE_BIT) { 85 | self.interrupt = true; 86 | } 87 | self.csrs.sstatus.set_bit(STATUS_SIE_BIT, self.csrs.sstatus.get_bit(STATUS_SPIE_BIT)); 88 | self.csrs.sstatus.set_bit(STATUS_SPIE_BIT, true); 89 | } 90 | 91 | pub fn smode(&self) -> bool { 92 | self.csrs.sstatus.get_bit(STATUS_SPP_BIT) 93 | } 94 | // 是否开启分页 95 | pub fn paged(&self) -> bool { self.csrs.satp != 0 } 96 | 97 | 98 | } 99 | 100 | 101 | 102 | 103 | 104 | 105 | #[repr(C)] 106 | /// task context structure containing some registers 107 | pub struct TaskContext { 108 | /// return address ( e.g. __restore ) of __switch ASM function 109 | ra: usize, 110 | /// kernel stack pointer of app 111 | sp: usize, 112 | /// callee saved registers: s 0..11 113 | s: [usize; 12], 114 | } 115 | 116 | impl TaskContext { 117 | /// init task context 118 | pub fn zero_init() -> Self { 119 | Self { 120 | ra: 0, 121 | sp: 0, 122 | s: [0; 12], 123 | } 124 | } 125 | /// set Task Context{__restore ASM funciton: trap_return, sp: kstack_ptr, s: s_0..12} 126 | pub fn goto_trap_return(kstack_ptr: usize) -> Self { 127 | Self { 128 | ra: trap_return as usize, 129 | sp: kstack_ptr, 130 | s: [0; 12], 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/guest/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::csr::sie::{SEIE, STIE, SSIE, STIE_BIT}; 2 | use crate::constants::csr::sip::SSIP; 3 | use crate::constants::csr::status::STATUS_SIE_BIT; 4 | use crate::debug::PageDebug; 5 | use crate::hypervisor::HYPERVISOR_MEMORY; 6 | use crate::page_table::{VirtAddr, PhysPageNum, PageTable}; 7 | use crate::mm::{MemorySet, MapPermission}; 8 | use crate::hypervisor::trap::{TrapContext, trap_handler}; 9 | use crate::constants::layout::{TRAP_CONTEXT, kernel_stack_position, GUEST_KERNEL_VIRT_START}; 10 | use crate::constants::csr; 11 | use crate::device_emu::VirtDevice; 12 | 13 | 14 | pub mod switch; 15 | pub mod context; 16 | mod pmap; 17 | pub mod sbi; 18 | 19 | use context::TaskContext; 20 | use riscv::addr::BitField; 21 | 22 | pub use self::context::ShadowState; 23 | pub use self::pmap::{ ShadowPageTables, PageTableRoot, gpa2hpa, hpa2gpa }; 24 | 25 | /// Guest Kernel 结构体 26 | pub struct GuestKernel { 27 | pub memory_set: MemorySet

, 28 | pub trap_cx_ppn: PhysPageNum, 29 | pub task_cx: TaskContext, 30 | pub shadow_state: ShadowState

, 31 | pub guest_id: usize, 32 | /// Guest OS 是否运行在 S mode 33 | pub smode: bool, 34 | /// Virtual emulated device in qemu 35 | pub virt_device: VirtDevice, 36 | } 37 | 38 | impl

GuestKernel

where P: PageDebug + PageTable { 39 | pub fn new(memory_set: MemorySet

, guest_id: usize) -> Self { 40 | // 获取中断上下文的物理地址 41 | let mut hypervisor_memory = HYPERVISOR_MEMORY.exclusive_access(); 42 | let trap_cx_ppn = memory_set 43 | .translate(VirtAddr::from(TRAP_CONTEXT).into()) 44 | .unwrap() 45 | .ppn(); 46 | // 获取内核栈地址 47 | let (kernel_stack_bottom, kernel_stack_top) = kernel_stack_position(guest_id); 48 | // 将内核栈地址进行映射 49 | hypervisor_memory.insert_framed_area( 50 | kernel_stack_bottom.into(), 51 | kernel_stack_top.into(), 52 | MapPermission::R | MapPermission::W, 53 | ); 54 | let mut guest_kernel = Self { 55 | memory_set, 56 | trap_cx_ppn, 57 | task_cx: TaskContext::goto_trap_return(kernel_stack_top), 58 | shadow_state: ShadowState::new(), 59 | guest_id, 60 | smode: true, 61 | virt_device: VirtDevice::new(guest_id), 62 | }; 63 | // 设置 Guest OS `sstatus` 的 `SPP` 64 | let mut sstatus = riscv::register::sstatus::read(); 65 | sstatus.set_spp(riscv::register::sstatus::SPP::Supervisor); 66 | guest_kernel.shadow_state.csrs.sstatus = sstatus.bits(); 67 | // 获取中断上下文的地址 68 | let trap_cx : &mut TrapContext = guest_kernel.trap_cx_ppn.get_mut(); 69 | *trap_cx = TrapContext::app_init_context( 70 | GUEST_KERNEL_VIRT_START, 71 | 0, 72 | hypervisor_memory.token(), 73 | kernel_stack_top, 74 | trap_handler as usize, 75 | ); 76 | guest_kernel 77 | } 78 | 79 | /// 根据 `PageTableRoot` mode 来获取对应的 shadow page table token 80 | pub fn get_user_token(&self) -> usize { 81 | match self.shadow() { 82 | PageTableRoot::GPA => self.memory_set.token(), 83 | PageTableRoot::GVA => if self.shadow_state.csrs.satp == self.shadow_state.shadow_page_tables.guest_satp.unwrap() 84 | { self.shadow_state.shadow_page_tables.page_tables[1].unwrap() } 85 | else{ self.shadow_state.shadow_page_tables.page_tables[2].unwrap() }, 86 | PageTableRoot::UVA => self.shadow_state.shadow_page_tables.page_tables[2].unwrap(), 87 | } 88 | } 89 | 90 | /// 用来检查应当使用哪一级的影子页表 91 | pub fn shadow(&self) -> PageTableRoot { 92 | if (self.shadow_state.csrs.satp >> 60) & 0xf == 0 { 93 | PageTableRoot::GPA 94 | }else if !self.shadow_state.smode() { 95 | PageTableRoot::UVA 96 | }else { 97 | PageTableRoot::GVA 98 | } 99 | } 100 | 101 | pub fn get_csr(&self, csr: usize) -> usize { 102 | let shadow_state = &self.shadow_state; 103 | match csr { 104 | csr::sstatus => shadow_state.csrs.sstatus, 105 | csr::stvec => shadow_state.csrs.stvec, 106 | csr::sie => shadow_state.csrs.sie, 107 | csr::sscratch => shadow_state.csrs.sscratch, 108 | csr::sepc => shadow_state.csrs.sepc, 109 | csr::scause => shadow_state.csrs.scause, 110 | csr::stval => shadow_state.csrs.stval, 111 | csr::satp => shadow_state.csrs.satp, 112 | _ => unreachable!(), 113 | } 114 | } 115 | 116 | pub fn set_csr(&mut self, csr: usize, val: usize) { 117 | let shadow_state = &mut self.shadow_state; 118 | match csr { 119 | csr::sstatus => { 120 | if val.get_bit(STATUS_SIE_BIT) { 121 | // Enabling interruots might casue one to happen right away 122 | shadow_state.interrupt = true; 123 | } 124 | shadow_state.csrs.sstatus = val 125 | } 126 | csr::stvec => shadow_state.csrs.stvec = val, 127 | csr::sie => { 128 | let value = val & (SEIE | STIE | SSIE); 129 | if !shadow_state.csrs.sie & value != 0{ 130 | shadow_state.interrupt = true; 131 | } 132 | if value.get_bit(STIE_BIT) { 133 | unsafe{ riscv::register::sie::set_stimer() }; 134 | } 135 | shadow_state.csrs.sie = val; 136 | } 137 | csr::sip => { 138 | if val & SSIP != 0 { 139 | shadow_state.interrupt = true; 140 | } 141 | shadow_state.csrs.sip = (shadow_state.csrs.sip & !SSIP) | (val & SSIP); 142 | } 143 | csr::sscratch => shadow_state.csrs.sscratch = val, 144 | csr::sepc => shadow_state.csrs.sepc = val, 145 | csr::scause => shadow_state.csrs.scause = val, 146 | csr::stval => shadow_state.csrs.stval = val, 147 | csr::satp => { 148 | let satp = val; 149 | match (satp >> 60) & 0xf { 150 | 0 => shadow_state.csrs.satp = satp, 151 | 8 => { 152 | // 获取 guest kernel 153 | shadow_state.csrs.satp = satp; 154 | self.make_shadow_page_table(satp); 155 | } 156 | _ => panic!("Install page table with unsupported mode?") 157 | } 158 | } 159 | _ => unreachable!() 160 | } 161 | } 162 | 163 | 164 | } 165 | 166 | -------------------------------------------------------------------------------- /src/guest/pmap.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::{VecDeque, BTreeMap}; 2 | use alloc::vec::Vec; 3 | use core::cell::UnsafeCell; 4 | 5 | use crate::debug::PageDebug; 6 | use crate::device_emu::is_device_access; 7 | use crate::hypervisor::HYPERVISOR_MEMORY; 8 | use crate::page_table::{PageTable, VirtPageNum, PageTableEntry, PhysPageNum, PTEFlags}; 9 | use crate::constants::layout::{GUEST_KERNEL_VIRT_START, TRAMPOLINE, TRAP_CONTEXT}; 10 | 11 | use super::GuestKernel; 12 | 13 | /// 内存信息,用于帮助做地址映射 14 | #[allow(unused)] 15 | mod segment_layout { 16 | pub const HART_SEGMENT_SIZE: usize = 128 * 1024 * 1024; 17 | pub const SPT_OFFSET: usize = 0x10000_0000 - 0x8000_0000; 18 | } 19 | 20 | 21 | 22 | /// 页表(影子页表类型) 23 | #[derive(Copy, Clone, Eq, PartialEq)] 24 | pub enum PageTableRoot { 25 | /// Guest Physical Address 26 | GPA, 27 | /// Guest Virtual Address 28 | GVA, 29 | /// User Virtual Address 30 | UVA 31 | } 32 | 33 | pub struct ShadowPageTables { 34 | /// all shadow page tables (satp, spt) 35 | pub spts: UnsafeCell>, 36 | /// guest kernel installed shadow page table 37 | pub page_tables: [Option; 3], 38 | /// kernel guest page table token 39 | pub guest_satp: Option 40 | } 41 | 42 | impl

ShadowPageTables

where P: PageDebug + PageTable { 43 | pub const fn new() -> Self { 44 | Self { 45 | spts: UnsafeCell::new(BTreeMap::new()), 46 | page_tables: [None; 3], 47 | guest_satp: None 48 | } 49 | } 50 | 51 | pub fn spts(&self) -> &mut BTreeMap { 52 | unsafe{ &mut *self.spts.get() } 53 | } 54 | 55 | pub fn push(&self, satp: usize, spt: P) { 56 | let inner = self.spts(); 57 | inner.insert(satp, spt); 58 | } 59 | 60 | 61 | pub fn shadow_page_table(&self, satp: usize) -> Option<&mut P> { 62 | let inner = self.spts(); 63 | inner.get_mut(&satp) 64 | } 65 | 66 | pub fn guest_page_table(&self) -> Option<&mut P> { 67 | let inner = self.spts(); 68 | if let Some(guest_satp) = self.guest_satp { 69 | inner.get_mut(&guest_satp) 70 | }else{ 71 | None 72 | } 73 | } 74 | 75 | pub fn install_root(&mut self, spt_token: usize, mode: PageTableRoot) { 76 | match mode { 77 | PageTableRoot::GPA => self.page_tables[0] = Some(spt_token), 78 | PageTableRoot::GVA => self.page_tables[1] = Some(spt_token), 79 | PageTableRoot::UVA => self.page_tables[2] = Some(spt_token) 80 | } 81 | } 82 | 83 | } 84 | 85 | pub fn gpa2hpa(va: usize, hart_id: usize) -> usize { 86 | va + (hart_id + 1) * segment_layout::HART_SEGMENT_SIZE 87 | } 88 | 89 | pub fn hpa2gpa(pa: usize, hart_id: usize) -> usize { 90 | pa - (hart_id + 1) * segment_layout::HART_SEGMENT_SIZE 91 | } 92 | 93 | pub fn gpt2spt(va: usize, hart_id: usize) -> usize { 94 | va + segment_layout::SPT_OFFSET + hart_id * segment_layout::HART_SEGMENT_SIZE 95 | } 96 | 97 | pub fn page_table_mode(page_table: P, hart_id: usize) -> PageTableRoot { 98 | if page_table.translate_guest(VirtPageNum::from(GUEST_KERNEL_VIRT_START >> 12), hart_id).is_some() { 99 | return PageTableRoot::GVA 100 | } 101 | PageTableRoot::UVA 102 | } 103 | 104 | 105 | 106 | fn update_pte_readonly(vpn: VirtPageNum, spt: &mut P) -> bool { 107 | if let Some(pte) = spt.find_pte(vpn) { 108 | if pte.writable() | pte.executable() { 109 | *pte = PageTableEntry::new(pte.ppn(), PTEFlags::R | PTEFlags::U | PTEFlags::V); 110 | } 111 | true 112 | }else{ 113 | false 114 | } 115 | } 116 | 117 | fn clear_page_table(spt: &mut P, va: usize, hart_id: usize) { 118 | let mut drop = true; 119 | let guest_ppn = PhysPageNum::from(gpa2hpa(va, hart_id) >> 12); 120 | let guest_ptes = guest_ppn.get_pte_array(); 121 | guest_ptes.iter().for_each(|&pte| { 122 | if pte.bits != 0 { drop = false; } 123 | }); 124 | if drop { 125 | // htracking!("Drop the page table guest ppn -> {:#x}", guest_ppn.0); 126 | // 将影子页表设置为可读可写 127 | if let Some(spt_pte) = spt.find_pte(VirtPageNum::from(va >> 12)) { 128 | *spt_pte = PageTableEntry::new(spt_pte.ppn(), PTEFlags::R | PTEFlags::W | PTEFlags::U | PTEFlags::V); 129 | } 130 | } 131 | } 132 | 133 | /// 收集所有页表的虚拟页号 134 | pub fn collect_page_table_vpns(hart_id: usize, satp: usize) -> Vec { 135 | let guest_root_pa = (satp & 0xfff_ffff_ffff) << 12; 136 | 137 | // 遍历所有页表项 138 | let mut queue = VecDeque::new(); 139 | let mut buffer = Vec::new(); 140 | // 非叶子所在的虚拟页号 141 | let mut non_leaf_vpns = Vec::new(); 142 | let vpn = VirtPageNum::from(guest_root_pa >> 12); 143 | queue.push_back(vpn); 144 | 145 | for walk in 0..3 { 146 | // 遍历三级页表 147 | while !queue.is_empty() { 148 | // 获得 guest pte 的虚拟页号 149 | let guest_page_table_vpn = queue.pop_front().unwrap(); 150 | // 收集所有非叶子节点 `vpn`,用于设置为只读 151 | non_leaf_vpns.push(guest_page_table_vpn); 152 | // 获得 guest pte 的物理页号 153 | let guest_page_table_ppn = PhysPageNum::from(gpa2hpa(guest_page_table_vpn.0 << 12, hart_id) >> 12); 154 | // 获得 guest pte 页表项内容 155 | let guest_ptes = guest_page_table_ppn.get_pte_array(); 156 | for guest_pte in guest_ptes.iter(){ 157 | if guest_pte.is_valid() && walk < 2 { 158 | // 非叶子页表项 159 | buffer.push(VirtPageNum::from(guest_pte.ppn().0)); 160 | }else if guest_pte.is_valid() && walk == 2 { 161 | } 162 | } 163 | } 164 | while !buffer.is_empty() { 165 | queue.push_back(buffer.pop().unwrap()); 166 | } 167 | } 168 | non_leaf_vpns 169 | 170 | } 171 | 172 | pub fn synchronize_page_table(hart_id: usize, satp: usize) { 173 | let guest_root_pa = (satp & 0xfff_ffff_ffff) << 12; 174 | 175 | // 遍历所有页表项 176 | let mut queue = VecDeque::new(); 177 | let mut buffer = Vec::new(); 178 | let vpn = VirtPageNum::from(guest_root_pa >> 12); 179 | queue.push_back(vpn); 180 | 181 | for walk in 0..3 { 182 | // 遍历三级页表 183 | while !queue.is_empty() { 184 | // 获得 guest pte 的虚拟页号 185 | let guest_page_table_vpn = queue.pop_front().unwrap(); 186 | // 收集所有非叶子节点 `vpn`,用于设置为只读 187 | let host_page_table_ppn = PhysPageNum::from(gpt2spt(guest_page_table_vpn.0 << 12, hart_id) >> 12); 188 | // 获得 guest pte 的物理页号 189 | let guest_page_table_ppn = PhysPageNum::from(gpa2hpa(guest_page_table_vpn.0 << 12, hart_id) >> 12); 190 | // 获得 guest pte 页表项内容 191 | let guest_ptes = guest_page_table_ppn.get_pte_array(); 192 | // 获得 host pte 页表项内容 193 | let host_ptes = host_page_table_ppn.get_pte_array(); 194 | for (index, guest_pte) in guest_ptes.iter().enumerate() { 195 | if guest_pte.is_valid() && walk < 2 { 196 | // 非叶子页表项 197 | buffer.push(VirtPageNum::from(guest_pte.ppn().0)); 198 | // 构造 host pte 199 | let host_pte = PageTableEntry::new(PhysPageNum::from(gpt2spt(guest_pte.ppn().0 << 12, hart_id) >> 12) , guest_pte.flags()); 200 | host_ptes[index] = host_pte; 201 | }else if guest_pte.is_valid() && walk == 2 { 202 | let host_pte = PageTableEntry::new(PhysPageNum::from(gpa2hpa(guest_pte.ppn().0 << 12, hart_id) >> 12) , guest_pte.flags() | PTEFlags::U); 203 | host_ptes[index] = host_pte; 204 | } 205 | } 206 | } 207 | while !buffer.is_empty() { 208 | queue.push_back(buffer.pop().unwrap()); 209 | } 210 | } 211 | } 212 | 213 | /// 用于初始化影子页表同步所有页表项(仅在最开始时使用) 214 | pub fn initialize_shadow_page_table(hart_id: usize, satp: usize, mode: PageTableRoot, guest_spt: Option<&mut P>) -> Option

{ 215 | let guest_root_pa = (satp & 0xfff_ffff_ffff) << 12; 216 | let host_root_pa = gpt2spt(guest_root_pa, hart_id); 217 | // 获取 `guest SPT` 218 | let mut empty_spt = P::from_token(0); 219 | let guest_spt = match mode { 220 | PageTableRoot::GVA => { &mut empty_spt }, 221 | PageTableRoot::UVA => if let Some(spt) = guest_spt { spt } else { panic!() } 222 | _ => unreachable!() 223 | }; 224 | // 遍历所有页表项 225 | let mut queue = VecDeque::new(); 226 | let mut buffer = Vec::new(); 227 | // 非叶子所在的虚拟页号 228 | let mut non_leaf_vpns = Vec::new(); 229 | let vpn = VirtPageNum::from(guest_root_pa >> 12); 230 | queue.push_back(vpn); 231 | for walk in 0..3 { 232 | // 遍历三级页表 233 | while !queue.is_empty() { 234 | // 获得 guest pte 的虚拟页号 235 | let guest_page_table_vpn = queue.pop_front().unwrap(); 236 | // 收集所有非叶子节点 `vpn`,用于设置为只读 237 | non_leaf_vpns.push(guest_page_table_vpn); 238 | let host_page_table_ppn = PhysPageNum::from(gpt2spt(guest_page_table_vpn.0 << 12, hart_id) >> 12); 239 | // 获得 guest pte 的物理页号 240 | let guest_page_table_ppn = PhysPageNum::from(gpa2hpa(guest_page_table_vpn.0 << 12, hart_id) >> 12); 241 | // 获得 guest pte 页表项内容 242 | let guest_ptes = guest_page_table_ppn.get_pte_array(); 243 | // 获得 host pte 页表项内容 244 | let host_ptes = host_page_table_ppn.get_pte_array(); 245 | for (index, guest_pte) in guest_ptes.iter().enumerate() { 246 | if guest_pte.is_valid() && walk < 2 { 247 | // 非叶子页表项 248 | buffer.push(VirtPageNum::from(guest_pte.ppn().0)); 249 | // 构造 host pte 250 | let host_pte = PageTableEntry::new(PhysPageNum::from(gpt2spt(guest_pte.ppn().0 << 12, hart_id) >> 12) , guest_pte.flags()); 251 | host_ptes[index] = host_pte; 252 | }else if guest_pte.is_valid() && walk == 2 { 253 | let host_pte; 254 | if !is_device_access(guest_pte.ppn().0 << 12) { 255 | host_pte = PageTableEntry::new(PhysPageNum::from(gpa2hpa(guest_pte.ppn().0 << 12, hart_id) >> 12) , guest_pte.flags() | PTEFlags::U); 256 | }else{ 257 | host_pte = PageTableEntry::new(PhysPageNum::from(guest_pte.ppn().0) , guest_pte.flags() | PTEFlags::U); 258 | } 259 | host_ptes[index] = host_pte; 260 | } 261 | } 262 | } 263 | while !buffer.is_empty() { 264 | queue.push_back(buffer.pop().unwrap()); 265 | } 266 | } 267 | let mut host_shadow_page_table = PageTable::from_ppn(PhysPageNum::from(host_root_pa >> 12)); 268 | non_leaf_vpns.iter().for_each(|&vpn| { 269 | match mode { 270 | PageTableRoot::GVA => { 271 | update_pte_readonly(vpn, &mut host_shadow_page_table); 272 | }, 273 | PageTableRoot::UVA => { 274 | update_pte_readonly(vpn, guest_spt); 275 | }, 276 | _ => unreachable!() 277 | } 278 | }); 279 | Some(host_shadow_page_table) 280 | } 281 | 282 | 283 | 284 | 285 | impl

GuestKernel

where P: PageDebug + PageTable { 286 | /// GPA -> HPA 287 | pub fn translate_guest_paddr(&self, paddr: usize) -> Option { 288 | let offset = paddr & 0xfff; 289 | let vpn: VirtPageNum = VirtPageNum::from(paddr >> 12); 290 | let pte = self.translate_guest_ppte(vpn); 291 | if let Some(pte) = pte { 292 | return Some((pte.ppn(). 0 << 12) + offset) 293 | } 294 | None 295 | } 296 | 297 | /// GVA -> HPA 298 | pub fn translate_guest_vaddr(&self, vaddr: usize) -> Option { 299 | let offset = vaddr & 0xfff; 300 | let vpn = VirtPageNum::from(vaddr >> 12); 301 | let pte = self.translate_guest_vpte(vpn); 302 | if let Some(pte) = pte { 303 | return Some((pte.ppn(). 0 << 12) + offset) 304 | } 305 | None 306 | } 307 | 308 | pub fn translate_guest_ppte(&self, vpn: VirtPageNum) -> Option { 309 | self.memory_set.translate(vpn) 310 | } 311 | 312 | pub fn translate_guest_vpte(&self, vpn: VirtPageNum) -> Option { 313 | if let Some(spt) = self.shadow_state.shadow_page_tables.shadow_page_table(self.shadow_state.csrs.satp) { 314 | // 由于 GHA 与 GPA 是同等映射的,因此翻译成的物理地址可以直接当虚拟地址用 315 | spt.translate(vpn) 316 | }else{ 317 | // hwarning!("translate guest va from GPA mode?"); 318 | self.translate_guest_ppte(vpn) 319 | } 320 | } 321 | 322 | pub fn translate_valid_guest_vaddr(&self, vaddr: usize) -> Option { 323 | let offset = vaddr & 0xfff; 324 | let vpn = VirtPageNum::from(vaddr >> 12); 325 | let pte = self.translate_guest_vpte(vpn); 326 | if let Some(pte) = pte { 327 | if !pte.is_valid(){ return None } 328 | return Some((pte.ppn(). 0 << 12) + offset) 329 | } 330 | None 331 | } 332 | 333 | /// 根据 satp 构建影子页表 334 | /// 需要将 GVA -> HPA 335 | pub fn make_shadow_page_table(&mut self, satp: usize) { 336 | // 根据 satp 获取 guest kernel 根页表的物理地址 337 | let hart_id = self.guest_id; 338 | let root_gpa = (satp & 0xfff_ffff_ffff) << 12; 339 | let root_hppn = PhysPageNum::from(gpa2hpa(root_gpa, hart_id) >> 12); 340 | let gpt = P::from_ppn(root_hppn); 341 | 342 | let hypervisor_memory = HYPERVISOR_MEMORY.exclusive_access(); 343 | if self.shadow_state.shadow_page_tables.shadow_page_table(satp).is_none() { 344 | // 如果影子页表中没有发现,新建影子页表 345 | let mut spt; 346 | let mode; 347 | // 根据页表是否可读内核地址空间判断是 `GVA` 还是 `UVA` 348 | match page_table_mode(gpt.clone(), hart_id) { 349 | PageTableRoot::GVA => { 350 | // 将 mode 设置为 `GVA` 351 | mode = PageTableRoot::GVA; 352 | spt = initialize_shadow_page_table::

(hart_id, satp, mode, None).unwrap(); 353 | self.shadow_state.shadow_page_tables.guest_satp = Some(satp); 354 | 355 | assert_eq!(spt.translate(VirtPageNum::from(0x10001)).unwrap().ppn().0, 0x10001); 356 | } 357 | PageTableRoot::UVA => { 358 | // 将 mode 设置为 `UVA` 359 | mode = PageTableRoot::UVA; 360 | // 同步 guest spt,即将用户页表设置为只读 361 | let guest_spt = self.shadow_state.shadow_page_tables.guest_page_table().unwrap(); 362 | spt = initialize_shadow_page_table::

(hart_id, satp, mode, Some(guest_spt)).unwrap(); 363 | 364 | } 365 | _ => unreachable!() 366 | } 367 | 368 | // 为 `SPT` 映射跳板页 369 | // 无论是 guest spt 还是 user spt 都要映射跳板页与 Trap Context 370 | let trampoline_hppn = hypervisor_memory.translate(VirtPageNum::from(TRAMPOLINE >> 12)).unwrap().ppn(); 371 | spt.map(VirtPageNum::from(TRAMPOLINE >> 12), trampoline_hppn, PTEFlags::R | PTEFlags::X); 372 | 373 | let trapctx_hvpn = VirtPageNum::from(self.translate_guest_paddr(TRAP_CONTEXT).unwrap() >> 12); 374 | let trapctx_hppn = hypervisor_memory.translate(trapctx_hvpn).unwrap().ppn(); 375 | spt.map(VirtPageNum::from(TRAP_CONTEXT >> 12), trapctx_hppn, PTEFlags::R | PTEFlags::W); 376 | 377 | // hdebug!("Make new SPT(satp -> {:#x}, spt -> {:#x}) ", satp, spt.token()); 378 | self.shadow_state.shadow_page_tables.install_root(spt.token(), mode); 379 | self.shadow_state.shadow_page_tables.push(satp, spt); 380 | }else{ 381 | // 如果存在的话,根据 `guest page table` 更新 `guest os SPT` 只读项 382 | let guest_spt = self.shadow_state.shadow_page_tables.guest_page_table().unwrap(); 383 | match page_table_mode(gpt.clone(), hart_id) { 384 | PageTableRoot::GVA => { 385 | // os 的内存映射几乎不会改变,因此在切换页表时不需要同步 386 | self.shadow_state.conseutive_satp_switch_count += 1; 387 | // 切换的页表为 `guest os page table` 388 | // 需要重新遍历所有页表项,并将其设置为只读 389 | collect_page_table_vpns::

(hart_id, satp).iter().for_each(|&vpn| { 390 | update_pte_readonly(vpn, guest_spt); 391 | }); 392 | }, 393 | PageTableRoot::UVA => { 394 | collect_page_table_vpns::

(hart_id, satp).iter().for_each(|&vpn| { 395 | update_pte_readonly(vpn, guest_spt); 396 | }); 397 | // 需要更新用户态页表 398 | synchronize_page_table::

(hart_id, satp); 399 | let spt = &mut self.shadow_state.shadow_page_tables.shadow_page_table(satp).unwrap(); 400 | // 为 `SPT` 映射跳板页 401 | let trampoline_hppn = hypervisor_memory.translate(VirtPageNum::from(TRAMPOLINE >> 12)).unwrap().ppn(); 402 | if let Some(pte) = spt.translate(VirtPageNum::from(TRAMPOLINE >> 12)) { 403 | if !pte.is_valid() { 404 | htracking!("user remap trampoline"); 405 | spt.map(VirtPageNum::from(TRAMPOLINE >> 12), trampoline_hppn, PTEFlags::R | PTEFlags::X); 406 | } 407 | }else{ 408 | htracking!("user remap trampoline"); 409 | spt.map(VirtPageNum::from(TRAMPOLINE >> 12), trampoline_hppn, PTEFlags::R | PTEFlags::X); 410 | } 411 | 412 | let trapctx_hvpn = VirtPageNum::from(self.translate_guest_paddr(TRAP_CONTEXT).unwrap() >> 12); 413 | let trapctx_hppn = hypervisor_memory.translate(trapctx_hvpn).unwrap().ppn(); 414 | if let Some(pte) = spt.translate(VirtPageNum::from(TRAP_CONTEXT >> 12)) { 415 | if !pte.is_valid() { 416 | htracking!("user remap trap context"); 417 | spt.map(VirtPageNum::from(TRAP_CONTEXT >> 12), trapctx_hppn, PTEFlags::R | PTEFlags::W); 418 | } 419 | }else{ 420 | htracking!("user remap trap context"); 421 | spt.map(VirtPageNum::from(TRAP_CONTEXT >> 12), trapctx_hppn, PTEFlags::R | PTEFlags::W); 422 | } 423 | self.shadow_state.shadow_page_tables.install_root(spt.token(), PageTableRoot::UVA); 424 | }, 425 | _ => unreachable!() 426 | } 427 | } 428 | } 429 | 430 | 431 | 432 | pub fn synchronize_page_table(&mut self, va: usize, pte: PageTableEntry) { 433 | let hart_id = self.guest_id; 434 | // 获取对应影子页表的地址 435 | let host_pa = gpt2spt(va, hart_id); 436 | let host_ppn = PhysPageNum::from(host_pa >> 12); 437 | // 获得影子页表 438 | let guest_spt = self.shadow_state.shadow_page_tables.guest_page_table().unwrap(); 439 | if va % core::mem::size_of::() != 0 { 440 | panic!("Page Table Entry aligned?"); 441 | }else if va % core::mem::size_of::() == 0 && pte.bits == 0 { 442 | // 页表项对齐且物理页号为 0, 写入 `u8` 443 | unsafe{ core::ptr::write(host_pa as *mut usize, pte.bits as usize) }; 444 | // 消除页表映射,将页表内存修改为可读可写 445 | clear_page_table(guest_spt, va, hart_id); 446 | }else { 447 | // 如果页表项对齐且物理页号不为零表示进行页表映射 448 | let index = (host_pa & 0xfff) / core::mem::size_of::(); 449 | let pte_array = host_ppn.get_pte_array(); 450 | if pte.is_valid() && (pte.readable() | pte.writable() | pte.executable()) { 451 | // 叶子节点 452 | let new_ppn = PhysPageNum::from(gpa2hpa(pte.ppn().0 << 12, hart_id) >> 12); 453 | let new_flags = pte.flags() | PTEFlags::U; 454 | let new_pte = PageTableEntry::new(new_ppn, new_flags); 455 | pte_array[index] = new_pte; 456 | let vpn = VirtPageNum::from(va >> 12); 457 | if let Some(pte) = guest_spt.translate(vpn) { 458 | if pte.writable() | pte.executable() { 459 | htracking!("Allocate page table, ppn: {:#x}", vpn.0); 460 | update_pte_readonly(vpn, guest_spt); 461 | } 462 | }else{ 463 | panic!() 464 | } 465 | 466 | }else if pte.is_valid() && !(pte.readable() | pte.writable() | pte.executable()) { 467 | // 非叶子节点 468 | // 获取非叶子节点的偏移 469 | let new_ppn = PhysPageNum::from(gpt2spt(pte.ppn().0 << 12, hart_id) >> 12); 470 | let new_flags = pte.flags(); 471 | let new_pte = PageTableEntry::new(new_ppn, new_flags); 472 | pte_array[index] = new_pte; 473 | // 判断当前页面是否设置为只读 474 | let vpn = VirtPageNum::from(va >> 12); 475 | if let Some(pte) = guest_spt.translate(vpn) { 476 | if pte.writable() | pte.executable() { 477 | htracking!("Allocate page table, ppn: {:#x}", vpn.0); 478 | update_pte_readonly(vpn, guest_spt); 479 | } 480 | }else{ 481 | unreachable!() 482 | } 483 | }else{ 484 | unreachable!() 485 | } 486 | } 487 | } 488 | 489 | } 490 | 491 | -------------------------------------------------------------------------------- /src/guest/sbi.rs: -------------------------------------------------------------------------------- 1 | pub const SBI_SET_TIMER: usize = 0; 2 | pub const SBI_CONSOLE_PUTCHAR: usize = 1; 3 | pub const SBI_CONSOLE_GETCHAR: usize = 2; 4 | pub const SBI_CLEAR_IPI: usize = 3; 5 | pub const SBI_SEND_IPI: usize = 4; 6 | pub const SBI_REMOTE_FENCE_I: usize = 5; 7 | pub const SBI_REMOTE_SFENCE_VMA: usize = 6; 8 | pub const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7; 9 | pub const SBI_SHUTDOWN: usize = 8; -------------------------------------------------------------------------------- /src/guest/switch.rs: -------------------------------------------------------------------------------- 1 | //! Rust wrapper around `__switch`. 2 | //! 3 | //! Switching to a different task's context happens here. The actual 4 | //! implementation must not be in Rust and (essentially) has to be in assembly 5 | //! language (Do you know why?), so this module really is just a wrapper around 6 | //! `switch.S`. 7 | 8 | // core::arch::global_asm!(include_str!("switch.S")); 9 | use super::TaskContext; 10 | 11 | 12 | #[no_mangle] 13 | #[naked] 14 | pub unsafe extern "C" fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext) { 15 | core::arch::asm!( 16 | "sd sp,8(a0)", 17 | "sd ra,0(a0)", 18 | "sd s0,16(a0)", 19 | "sd s1,24(a0)", 20 | "sd s2,32(a0)", 21 | "sd s3,40(a0)", 22 | "sd s4,48(a0)", 23 | "sd s5,56(a0)", 24 | "sd s6,64(a0)", 25 | "sd s7,72(a0)", 26 | "sd s8,80(a0)", 27 | "sd s9,88(a0)", 28 | "sd s10,96(a0)", 29 | "sd s11,104(a0)", 30 | "ld ra,0(a1)", 31 | "ld s0,16(a1)", 32 | "ld s1,24(a1)", 33 | "ld s2,32(a1)", 34 | "ld s3,40(a1)", 35 | "ld s4,48(a1)", 36 | "ld s5,56(a1)", 37 | "ld s6,64(a1)", 38 | "ld s7,72(a1)", 39 | "ld s8,80(a1)", 40 | "ld s9,88(a1)", 41 | "ld s10,96(a1)", 42 | "ld s11,104(a1)", 43 | "ld sp,8(a1)", 44 | "ret", 45 | options(noreturn) 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/hypervisor/fdt.rs: -------------------------------------------------------------------------------- 1 | ///! ref: https://github.com/mit-pdos/RVirt/blob/HEAD/src/fdt.rs 2 | 3 | use arrayvec::ArrayVec; 4 | use fdt::Fdt; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct Device { 8 | pub base_address: usize, 9 | pub size: usize 10 | } 11 | 12 | #[derive(Clone, Debug, Default)] 13 | pub struct MachineMeta{ 14 | pub physical_memory_offset: usize, 15 | pub physical_memory_size: usize, 16 | 17 | pub virtio: ArrayVec 18 | } 19 | 20 | impl MachineMeta { 21 | pub fn parse(dtb: usize) -> Self { 22 | let fdt = unsafe{ Fdt::from_ptr(dtb as *const u8) }.unwrap(); 23 | let memory = fdt.memory(); 24 | let mut meta = MachineMeta::default(); 25 | for region in memory.regions() { 26 | meta.physical_memory_offset = region.starting_address as usize; 27 | meta.physical_memory_size = region.size.unwrap(); 28 | } 29 | // 发现 virtio mmio 设备 30 | for node in fdt.find_all_nodes("/soc/virtio_mmio") { 31 | if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { 32 | let paddr = reg.starting_address as usize; 33 | let size = reg.size.unwrap(); 34 | let vaddr = paddr; 35 | unsafe{ 36 | let header = vaddr as *const u32; 37 | let device_id_addr = header.add(2); 38 | let device_id = core::ptr::read_volatile(device_id_addr); 39 | if device_id != 0 { 40 | hdebug!("virtio mmio addr: {:#x}, size: {:#x}", paddr, size); 41 | meta.virtio.push( 42 | Device { base_address: paddr, size } 43 | ) 44 | } 45 | } 46 | } 47 | } 48 | meta.virtio.sort_unstable_by_key(|v| v.base_address); 49 | meta 50 | } 51 | } -------------------------------------------------------------------------------- /src/hypervisor/hyp_alloc/frame_allocator.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of [`FrameAllocator`] which 2 | //! controls all the frames in the operating system. 3 | 4 | use crate::page_table::{PhysPageNum, PhysAddr}; 5 | use crate::constants::layout::MEMORY_END; 6 | use crate::sync::UPSafeCell; 7 | use alloc::vec::Vec; 8 | use core::fmt::{self, Debug, Formatter}; 9 | use lazy_static::*; 10 | 11 | /// manage a frame which has the same lifecycle as the tracker 12 | #[derive(Clone)] 13 | pub struct FrameTracker { 14 | pub ppn: PhysPageNum, 15 | } 16 | 17 | impl FrameTracker { 18 | pub fn new(ppn: PhysPageNum) -> Self { 19 | // page cleaning 20 | let bytes_array = ppn.get_bytes_array(); 21 | for i in bytes_array { 22 | *i = 0; 23 | } 24 | Self { ppn } 25 | } 26 | } 27 | 28 | impl Debug for FrameTracker { 29 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 30 | f.write_fmt(format_args!("FrameTracker:PPN={:#x}", self.ppn.0)) 31 | } 32 | } 33 | 34 | impl Drop for FrameTracker { 35 | fn drop(&mut self) { 36 | frame_dealloc(self.ppn); 37 | } 38 | } 39 | 40 | trait FrameAllocator { 41 | fn new() -> Self; 42 | fn alloc(&mut self) -> Option; 43 | fn dealloc(&mut self, ppn: PhysPageNum); 44 | } 45 | 46 | /// an implementation for frame allocator 47 | pub struct StackFrameAllocator { 48 | current: usize, 49 | end: usize, 50 | recycled: Vec, 51 | } 52 | 53 | impl StackFrameAllocator { 54 | pub fn init(&mut self, l: PhysPageNum, r: PhysPageNum) { 55 | self.current = l.0; 56 | self.end = r.0; 57 | } 58 | } 59 | impl FrameAllocator for StackFrameAllocator { 60 | fn new() -> Self { 61 | Self { 62 | current: 0, 63 | end: 0, 64 | recycled: Vec::new(), 65 | } 66 | } 67 | fn alloc(&mut self) -> Option { 68 | if let Some(ppn) = self.recycled.pop() { 69 | Some(ppn.into()) 70 | } else if self.current == self.end { 71 | None 72 | } else { 73 | self.current += 1; 74 | Some((self.current - 1).into()) 75 | } 76 | } 77 | fn dealloc(&mut self, ppn: PhysPageNum) { 78 | let ppn = ppn.0; 79 | // validity check 80 | if ppn >= self.current || self.recycled.iter().any(|&v| v == ppn) { 81 | panic!("Frame ppn={:#x} has not been allocated!", ppn); 82 | } 83 | // recycle 84 | self.recycled.push(ppn); 85 | } 86 | } 87 | 88 | type FrameAllocatorImpl = StackFrameAllocator; 89 | 90 | lazy_static! { 91 | /// frame allocator instance through lazy_static! 92 | pub static ref FRAME_ALLOCATOR: UPSafeCell = 93 | unsafe { UPSafeCell::new(FrameAllocatorImpl::new()) }; 94 | } 95 | 96 | /// initiate the frame allocator using `einitrd` and `MEMORY_END` 97 | pub fn init_frame_allocator() { 98 | extern "C" { 99 | fn einitrd(); 100 | } 101 | FRAME_ALLOCATOR.exclusive_access().init( 102 | PhysAddr::from(einitrd as usize).ceil(), 103 | PhysAddr::from(MEMORY_END).floor(), 104 | ); 105 | } 106 | 107 | /// allocate a frame 108 | pub fn frame_alloc() -> Option { 109 | FRAME_ALLOCATOR 110 | .exclusive_access() 111 | .alloc() 112 | .map(FrameTracker::new) 113 | } 114 | 115 | /// deallocate a frame 116 | pub fn frame_dealloc(ppn: PhysPageNum) { 117 | FRAME_ALLOCATOR.exclusive_access().dealloc(ppn); 118 | } 119 | 120 | #[allow(unused)] 121 | /// a simple test for frame allocator 122 | pub fn frame_allocator_test() { 123 | let mut v: Vec = Vec::new(); 124 | for i in 0..5 { 125 | let frame = frame_alloc().unwrap(); 126 | println!("{:?}", frame); 127 | v.push(frame); 128 | } 129 | v.clear(); 130 | for i in 0..5 { 131 | let frame = frame_alloc().unwrap(); 132 | println!("{:?}", frame); 133 | v.push(frame); 134 | } 135 | drop(v); 136 | println!("frame_allocator_test passed!"); 137 | } 138 | -------------------------------------------------------------------------------- /src/hypervisor/hyp_alloc/heap_allocator.rs: -------------------------------------------------------------------------------- 1 | //! The global allocator 2 | 3 | use crate::constants::layout::KERNEL_HEAP_SIZE; 4 | use buddy_system_allocator::LockedHeap; 5 | 6 | #[global_allocator] 7 | /// heap allocator instance 8 | static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); 9 | 10 | #[alloc_error_handler] 11 | /// panic when heap allocation error occurs 12 | pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { 13 | panic!("Heap allocation error, layout = {:?}", layout); 14 | } 15 | 16 | /// heap space ([u8; KERNEL_HEAP_SIZE]) 17 | static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE]; 18 | 19 | /// initiate heap allocator 20 | pub fn init_heap() { 21 | unsafe { 22 | HEAP_ALLOCATOR 23 | .lock() 24 | .init(HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE); 25 | } 26 | } 27 | 28 | #[allow(unused)] 29 | pub fn heap_test() { 30 | use alloc::boxed::Box; 31 | use alloc::vec::Vec; 32 | extern "C" { 33 | fn sbss(); 34 | fn ebss(); 35 | } 36 | let bss_range = sbss as usize..ebss as usize; 37 | let a = Box::new(5); 38 | assert_eq!(*a, 5); 39 | assert!(bss_range.contains(&(a.as_ref() as *const _ as usize))); 40 | drop(a); 41 | let mut v: Vec = Vec::new(); 42 | for i in 0..500 { 43 | v.push(i); 44 | } 45 | for (i, val) in v.iter().take(500).enumerate() { 46 | assert_eq!(*val, i); 47 | } 48 | assert!(bss_range.contains(&(v.as_ptr() as usize))); 49 | drop(v); 50 | println!("heap_test passed!"); 51 | } 52 | -------------------------------------------------------------------------------- /src/hypervisor/hyp_alloc/mod.rs: -------------------------------------------------------------------------------- 1 | mod frame_allocator; 2 | mod heap_allocator; 3 | 4 | pub use frame_allocator::{frame_alloc, frame_dealloc, FrameTracker}; 5 | 6 | /// initiate heap allocator, frame allocator and kernel space 7 | pub fn heap_init() { 8 | heap_allocator::init_heap(); 9 | frame_allocator::init_frame_allocator(); 10 | } -------------------------------------------------------------------------------- /src/hypervisor/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use spin::Mutex; 3 | 4 | 5 | use crate::constants::layout::TRAP_CONTEXT; 6 | use crate::guest::GuestKernel; 7 | use crate::page_table::{PageTable, PageTableSv39, VirtPageNum}; 8 | use crate::debug::PageDebug; 9 | use crate::guest::context::TaskContext; 10 | use crate::guest::switch::__switch; 11 | 12 | pub use self::hyp_alloc::FrameTracker; 13 | pub use self::fdt::MachineMeta; 14 | pub use self::shared::HYPERVISOR_MEMORY; 15 | use self::trap::TrapContext; 16 | 17 | 18 | 19 | pub mod hyp_alloc; 20 | pub mod trap; 21 | pub mod fdt; 22 | pub mod shared; 23 | 24 | pub struct Hypervisor { 25 | pub meta: MachineMeta, 26 | pub guests: Vec>, 27 | pub guest_run_id: usize 28 | } 29 | 30 | 31 | pub static HYPOCAUST: Mutex>> = Mutex::new(None); 32 | 33 | impl Hypervisor

{ 34 | pub fn create_guest() { 35 | 36 | } 37 | pub fn run_guest(&self, guest_id: usize) -> ! { 38 | let guest_kernel = &self.guests[guest_id]; 39 | let task_cx_ptr = &guest_kernel.task_cx as *const TaskContext; 40 | let mut _unused = TaskContext::zero_init(); 41 | hdebug!("run guest kernel {}......", guest_id); 42 | // before this, we should drop local variables that must be dropped manually 43 | unsafe { 44 | __switch(&mut _unused as *mut _, task_cx_ptr); 45 | } 46 | panic!("unreachable in run_first_task!"); 47 | } 48 | 49 | pub fn add_guest(&mut self, guest: GuestKernel

) { 50 | self.guests.push(guest); 51 | } 52 | 53 | pub fn current_user_token(&self) -> usize { 54 | let guest = &self.guests[self.guest_run_id]; 55 | guest.get_user_token() 56 | } 57 | 58 | pub fn current_trap_cx(&mut self) -> &'static mut TrapContext { 59 | let guest = &mut self.guests[self.guest_run_id]; 60 | guest.memory_set.translate(VirtPageNum::from(TRAP_CONTEXT >> 12)).unwrap().ppn().get_mut() 61 | } 62 | 63 | pub fn current_guest(&mut self) -> &mut GuestKernel

{ 64 | &mut self.guests[self.guest_run_id] 65 | } 66 | } 67 | 68 | 69 | 70 | pub fn initialize_vmm(meta: MachineMeta) { 71 | unsafe{ HYPOCAUST.force_unlock(); } 72 | let old = HYPOCAUST.lock().replace( 73 | Hypervisor{ 74 | meta, 75 | guests: Vec::new(), 76 | guest_run_id: 0 77 | } 78 | ); 79 | core::mem::forget(old); 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/hypervisor/shared.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use lazy_static::lazy_static; 3 | 4 | use crate::mm::MemorySet; 5 | use crate::page_table::PageTableSv39; 6 | use crate::sync::UPSafeCell; 7 | 8 | lazy_static! { 9 | pub static ref HYPERVISOR_MEMORY: Arc>> = Arc::new(unsafe{ UPSafeCell::new(MemorySet::::new_kernel())}); 10 | } -------------------------------------------------------------------------------- /src/hypervisor/trap/context.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of [`TrapContext`] 2 | 3 | use riscv::register::sstatus::{self, Sstatus, SPP}; 4 | 5 | #[repr(C)] 6 | #[derive(Debug)] 7 | /// trap context structure containing sstatus, sepc and registers 8 | pub struct TrapContext { 9 | /// general regs[0..31] 10 | pub x: [usize; 32], 11 | /// CSR sstatus 12 | pub sstatus: Sstatus, 13 | /// CSR sepc 14 | pub sepc: usize, 15 | /// Addr of Page Table 16 | pub kernel_satp: usize, 17 | /// kernel stack 18 | pub kernel_sp: usize, 19 | /// Addr of trap_handler function 20 | pub trap_handler: usize, 21 | } 22 | 23 | impl TrapContext { 24 | /// set stack pointer to x_2 reg (sp) 25 | pub fn set_sp(&mut self, sp: usize) { 26 | self.x[2] = sp; 27 | } 28 | /// init app context 29 | pub fn app_init_context( 30 | entry: usize, 31 | sp: usize, 32 | kernel_satp: usize, 33 | kernel_sp: usize, 34 | trap_handler: usize, 35 | ) -> Self { 36 | let mut sstatus = sstatus::read(); // CSR sstatus 37 | sstatus.set_spp(SPP::User); //previous privilege mode: user mode 38 | let mut cx = Self { 39 | x: [0; 32], 40 | sstatus, 41 | sepc: entry, // entry point of app 42 | kernel_satp, // addr of page table 43 | kernel_sp, // kernel stack 44 | trap_handler, // addr of trap_handler function 45 | }; 46 | cx.set_sp(sp); // app's user stack pointer 47 | cx // return initial Trap Context of app 48 | } 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/hypervisor/trap/device.rs: -------------------------------------------------------------------------------- 1 | use riscv::addr::BitField; 2 | 3 | use crate::constants::csr::sie::STIE_BIT; 4 | use crate::constants::csr::sip::STIP_BIT; 5 | use crate::page_table::PageTable; 6 | use crate::debug::PageDebug; 7 | use crate::guest::GuestKernel; 8 | use crate::sbi::set_timer; 9 | use crate::timer::get_default_timer; 10 | use crate::timer::get_time; 11 | 12 | use super::TrapContext; 13 | use super::decode_instruction_at_address; 14 | 15 | 16 | pub fn handle_qemu_virt(guest: &mut GuestKernel

, ctx: &mut TrapContext) { 17 | let (len, inst) = decode_instruction_at_address(guest, ctx.sepc); 18 | if let Some(inst) = inst { 19 | match inst { 20 | riscv_decode::Instruction::Sw(i) => { 21 | let rs1 = i.rs1() as usize; 22 | let rs2 = i.rs2() as usize; 23 | let offset: isize = if i.imm() > 2048 { ((0b1111 << 12) | i.imm()) as i16 as isize }else{ i.imm() as isize }; 24 | let vaddr = (ctx.x[rs1] as isize + offset) as usize; 25 | let value = ctx.x[rs2]; 26 | guest.virt_device.qemu_virt_tester.mmregs[vaddr] = value as u32; 27 | } 28 | _ => panic!("stval: {:#x}", ctx.sepc) 29 | } 30 | } 31 | ctx.sepc += len; 32 | } 33 | 34 | /// 时钟中断处理函数 35 | pub fn handle_time_interrupt(guest: &mut GuestKernel

) { 36 | let time = get_time(); 37 | let mut next = time + get_default_timer(); 38 | if guest.shadow_state.csrs.sie.get_bit(STIE_BIT) { 39 | if guest.shadow_state.csrs.mtimecmp <= time { 40 | // 表明此时 Guest OS 发生中断 41 | guest.shadow_state.interrupt = true; 42 | // 设置 sip 寄存器 43 | guest.shadow_state.csrs.sip.set_bit(STIP_BIT, true); 44 | }else{ 45 | // 未发生中断,设置下次中断 46 | next = next.min(guest.shadow_state.csrs.mtimecmp) 47 | } 48 | } 49 | // 设置下次中断 50 | set_timer(next); 51 | } 52 | 53 | #[inline(always)] 54 | pub fn is_device_access(guest_pa: usize) -> bool { 55 | guest_pa >= 0x1000_1000 && guest_pa < 0x1000_1000 + 1000 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/hypervisor/trap/forward.rs: -------------------------------------------------------------------------------- 1 | use riscv::addr::BitField; 2 | use riscv::register::{scause, stval}; 3 | 4 | use crate::constants::csr::sie::SSIE_BIT; 5 | use crate::constants::csr::sip::{SEIP_BIT, STIP_BIT}; 6 | use crate::constants::csr::status::{STATUS_SIE_BIT, STATUS_SPP_BIT}; 7 | use crate::page_table::PageTable; 8 | use crate::debug::PageDebug; 9 | use crate::guest::GuestKernel; 10 | use super::TrapContext; 11 | 12 | /// 检测 Guest OS 是否发生中断,若有则进行转发 13 | pub fn maybe_forward_interrupt(guest: &mut GuestKernel

, ctx: &mut TrapContext) { 14 | // 没有发生中断,返回 15 | if !guest.shadow_state.interrupt { return } 16 | let state = &mut guest.shadow_state; 17 | // 当前状态处于用户态,且开启中断并有中断正在等待 18 | if (!state.smode() && state.csrs.sstatus.get_bit(STATUS_SIE_BIT)) && (state.csrs.sie & state.csrs.sip != 0) { 19 | // hdebug!("forward timer interrupt: sepc -> {:#x}", ctx.sepc); 20 | let cause = if state.csrs.sip.get_bit(SEIP_BIT) { 9 } 21 | else if state.csrs.sip.get_bit(STIP_BIT) { 5 } 22 | else if state.csrs.sip.get_bit(SSIE_BIT) { 1 } 23 | else{ unreachable!() }; 24 | 25 | state.csrs.scause = (1 << 63) | cause; 26 | state.csrs.stval = 0; 27 | state.csrs.sepc = ctx.sepc; 28 | state.push_sie(); 29 | // 设置 sstatus 指向 S mode 30 | state.csrs.sstatus.set_bit(STATUS_SPP_BIT, true); 31 | ctx.sepc = state.csrs.stvec; 32 | }else{ 33 | state.interrupt = false; 34 | } 35 | } 36 | 37 | /// 向 guest kernel 转发异常 38 | pub fn forward_exception(guest: &mut GuestKernel

, ctx: &mut TrapContext) { 39 | let state = &mut guest.shadow_state; 40 | state.csrs.scause = scause::read().code(); 41 | state.csrs.sepc = ctx.sepc; 42 | state.csrs.stval = stval::read(); 43 | // 设置 sstatus 指向 S mode 44 | state.csrs.sstatus.set_bit(STATUS_SPP_BIT, true); 45 | ctx.sepc = state.csrs.stvec; 46 | // 将当前中断上下文修改为中断处理地址,以便陷入内核处理 47 | match guest.shadow_state.smode() { 48 | true => {}, 49 | false => {} 50 | } 51 | } -------------------------------------------------------------------------------- /src/hypervisor/trap/inst_fault.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | use riscv::addr::BitField; 4 | use riscv::register::scause; 5 | 6 | use super::TrapContext; 7 | use super::forward_exception; 8 | use crate::debug::PageDebug; 9 | use crate::constants::csr::sip::STIP_BIT; 10 | use crate::constants::csr::status::STATUS_SPP_BIT; 11 | use crate::page_table::PageTable; 12 | use crate::sbi::{ console_putchar, set_timer, console_getchar, shutdown }; 13 | use crate::guest::sbi::{ SBI_CONSOLE_GETCHAR, SBI_CONSOLE_PUTCHAR, SBI_SET_TIMER, SBI_SHUTDOWN }; 14 | use crate::guest::GuestKernel; 15 | 16 | 17 | 18 | /// 处理特权级指令问题 19 | pub fn ifault(guest: &mut GuestKernel

, ctx: &mut TrapContext) { 20 | let (len, inst) = decode_instruction_at_address(guest, ctx.sepc); 21 | if let Some(inst) = inst { 22 | match inst { 23 | riscv_decode::Instruction::Ecall => { 24 | match ctx.x[17] { 25 | SBI_SET_TIMER => { 26 | let stime = ctx.x[10]; 27 | guest.shadow_state.csrs.mtimecmp = stime; 28 | set_timer(stime); 29 | guest.shadow_state.csrs.sip.set_bit(STIP_BIT, false); 30 | } 31 | SBI_CONSOLE_PUTCHAR => { 32 | let c = ctx.x[10]; 33 | console_putchar(c); 34 | } 35 | SBI_CONSOLE_GETCHAR => { 36 | let c = console_getchar(); 37 | ctx.x[10] = c; 38 | } 39 | SBI_SHUTDOWN => shutdown(), 40 | _ => { 41 | // hdebug!("forward exception: sepc -> {:#x}", ctx.sepc); 42 | forward_exception(guest, ctx); 43 | return; 44 | } 45 | } 46 | }, 47 | riscv_decode::Instruction::Csrrc(i) => { 48 | let mask = ctx.x[i.rs1() as usize]; 49 | let csr = i.csr() as usize; 50 | let rd = i.rd() as usize; 51 | let val = guest.get_csr(csr); 52 | if mask != 0 { 53 | guest.set_csr(csr, val & !mask); 54 | } 55 | ctx.x[rd] = val; 56 | } 57 | riscv_decode::Instruction::Csrrs(i) => { 58 | let mask = ctx.x[i.rs1() as usize]; 59 | let csr = i.csr() as usize; 60 | let rd = i.rd() as usize; 61 | let val = guest.get_csr(csr); 62 | if mask != 0 { 63 | guest.set_csr(csr, val | mask); 64 | } 65 | ctx.x[rd] = val; 66 | } 67 | // 写 CSR 指令 68 | riscv_decode::Instruction::Csrrw(i) => { 69 | let prev = guest.get_csr(i.csr() as usize); 70 | // 向 Shadow CSR 写入 71 | let val = ctx.x[i.rs1() as usize]; 72 | guest.set_csr(i.csr() as usize, val); 73 | ctx.x[i.rd() as usize] = prev; 74 | }, 75 | riscv_decode::Instruction::Csrrwi(i) => { 76 | let prev = guest.get_csr(i.csr() as usize); 77 | guest.set_csr(i.csr() as usize, i.zimm() as usize); 78 | ctx.x[i.rd() as usize] = prev; 79 | } 80 | riscv_decode::Instruction::Csrrsi(i) => { 81 | let prev = guest.get_csr(i.csr() as usize); 82 | let mask = i.zimm() as usize; 83 | if mask != 0 { 84 | guest.set_csr(i.csr() as usize, prev | mask); 85 | } 86 | ctx.x[i.rd() as usize] = prev; 87 | }, 88 | riscv_decode::Instruction::Csrrci(i) => { 89 | let prev = guest.get_csr(i.csr() as usize); 90 | let mask = i.zimm() as usize; 91 | if mask != 0 { 92 | guest.set_csr(i.csr() as usize, prev & !mask); 93 | } 94 | ctx.x[i.rd() as usize] = prev; 95 | } 96 | riscv_decode::Instruction::Sret => { 97 | guest.shadow_state.pop_sie(); 98 | ctx.sepc = guest.get_csr(crate::constants::csr::sepc); 99 | guest.shadow_state.csrs.sstatus.set_bit(STATUS_SPP_BIT, false); 100 | if !guest.shadow_state.smode() { 101 | guest.shadow_state.interrupt = true; 102 | } 103 | // hdebug!("sret: spec -> {:#x}", ctx.sepc); 104 | return; 105 | } 106 | riscv_decode::Instruction::SfenceVma(i) => { 107 | if i.rs1() == 0 { 108 | // unsafe{ core::arch::asm!("sfence.vma") }; 109 | }else{ 110 | unimplemented!() 111 | } 112 | } 113 | riscv_decode::Instruction::Wfi => {} 114 | _ => { 115 | let paddr = guest.translate_guest_vaddr(ctx.sepc).unwrap(); 116 | let inst = unsafe{ core::ptr::read(paddr as *const u32) }; 117 | panic!("Unrecognized instruction, sepc: {:#x}, scause: {:?}, inst: {:#x}", ctx.sepc, scause::read().cause(), inst) 118 | } 119 | } 120 | }else{ 121 | forward_exception(guest, ctx) 122 | } 123 | ctx.sepc += len; 124 | } 125 | 126 | /// decode instruction from Guest OS address 127 | pub fn decode_instruction_at_address(guest: &GuestKernel

, addr: usize) -> (usize, Option) { 128 | let paddr = guest.translate_guest_vaddr(addr).unwrap(); 129 | let i1 = unsafe{ core::ptr::read(paddr as *const u16) }; 130 | let len = riscv_decode::instruction_length(i1); 131 | let inst = match len { 132 | 2 => i1 as u32, 133 | 4 => unsafe{ core::ptr::read(paddr as *const u32) }, 134 | _ => unreachable!() 135 | }; 136 | (len, riscv_decode::decode(inst).ok()) 137 | } 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /src/hypervisor/trap/mod.rs: -------------------------------------------------------------------------------- 1 | //! Trap handling functionality 2 | //! 3 | //! For rCore, we have a single trap entry point, namely `__alltraps`. At 4 | //! initialization in [`init()`], we set the `stvec` CSR to point to it. 5 | //! 6 | //! All traps go through `__alltraps`, which is defined in `trap.S`. The 7 | //! assembly language code does just enough work restore the kernel space 8 | //! context, ensuring that Rust code safely runs, and transfers control to 9 | //! [`trap_handler()`]. 10 | //! 11 | //! It then calls different functionality based on what exactly the exception 12 | //! was. For example, timer interrupts trigger task preemption, and syscalls go 13 | //! to [`syscall()`]. 14 | mod context; 15 | mod inst_fault; 16 | mod page_fault; 17 | mod device; 18 | mod forward; 19 | 20 | use crate::constants::layout::{TRAMPOLINE, TRAP_CONTEXT}; 21 | use crate::debug::print_hypervisor_backtrace; 22 | use crate::hypervisor::HYPOCAUST; 23 | 24 | use core::arch::{asm, global_asm}; 25 | use riscv::register::{ 26 | mtvec::TrapMode, 27 | scause::{self, Exception, Interrupt, Trap}, 28 | sie, stval, stvec, sepc, sscratch 29 | }; 30 | pub use context::TrapContext; 31 | use self::inst_fault::{ifault, decode_instruction_at_address}; 32 | use self::page_fault::handle_page_fault; 33 | use self::device::{ handle_qemu_virt, handle_time_interrupt }; 34 | use self::forward::{forward_exception, maybe_forward_interrupt}; 35 | 36 | 37 | global_asm!(include_str!("trap.S")); 38 | 39 | 40 | 41 | /// initialize CSR `stvec` as the entry of `__alltraps` 42 | pub fn init() { 43 | set_kernel_trap_entry(); 44 | } 45 | 46 | fn set_kernel_trap_entry() { 47 | extern "C" { 48 | fn __alltraps(); 49 | fn __alltraps_k(); 50 | } 51 | let __alltraps_k_va = __alltraps_k as usize - __alltraps as usize + TRAMPOLINE; 52 | unsafe { 53 | stvec::write(__alltraps_k_va, TrapMode::Direct); 54 | sscratch::write(trap_from_kernel as usize); 55 | } 56 | } 57 | 58 | fn set_user_trap_entry() { 59 | unsafe { 60 | stvec::write(TRAMPOLINE as usize, TrapMode::Direct); 61 | } 62 | } 63 | 64 | /// enable timer interrupt in sie CSR 65 | pub fn enable_timer_interrupt() { 66 | unsafe { sie::set_stimer(); } 67 | } 68 | 69 | pub fn disable_timer_interrupt() { 70 | unsafe{ sie::clear_stimer(); } 71 | } 72 | 73 | 74 | #[no_mangle] 75 | /// handle an interrupt, exception, or system call from user space 76 | pub fn trap_handler() -> ! { 77 | set_kernel_trap_entry(); 78 | unsafe{ HYPOCAUST.force_unlock(); } 79 | let mut hypervisor = HYPOCAUST.lock(); 80 | let hypervisor = {&mut *hypervisor}.as_mut().unwrap(); 81 | let ctx = hypervisor.current_trap_cx(); 82 | let scause = scause::read(); 83 | let stval = stval::read(); 84 | // get guest kernel 85 | let guest = hypervisor.current_guest(); 86 | match scause.cause() { 87 | Trap::Exception(Exception::UserEnvCall) => { 88 | ifault(guest, ctx); 89 | }, 90 | Trap::Exception(Exception::Breakpoint) => { 91 | ifault(guest, ctx); 92 | } 93 | Trap::Exception(Exception::StorePageFault) => { 94 | if !handle_page_fault(guest, ctx) { 95 | htracking!("forward page exception sepc -> {:#x}", ctx.sepc); 96 | forward_exception(guest, ctx); 97 | } 98 | } 99 | Trap::Exception(Exception::IllegalInstruction) => { 100 | ifault(guest, ctx); 101 | } 102 | Trap::Interrupt(Interrupt::SupervisorTimer) => { 103 | handle_time_interrupt(guest); 104 | // 可能转发中断 105 | maybe_forward_interrupt(guest, ctx); 106 | }, 107 | _ => { 108 | panic!( 109 | "Unsupported trap {:?}, stval = {:#x} spec: {:#x} smode -> {}!", 110 | scause.cause(), 111 | stval, 112 | ctx.sepc, 113 | guest.shadow_state.smode() 114 | ); 115 | } 116 | } 117 | drop(hypervisor); 118 | trap_return(); 119 | } 120 | 121 | #[no_mangle] 122 | /// set the new addr of __restore asm function in TRAMPOLINE page, 123 | /// set the reg a0 = trap_cx_ptr, reg a1 = phy addr of usr page table, 124 | /// finally, jump to new addr of __restore asm function 125 | pub fn trap_return() -> ! { 126 | set_user_trap_entry(); 127 | let trap_cx_ptr = TRAP_CONTEXT; 128 | unsafe{ HYPOCAUST.force_unlock(); } 129 | let hypervisor = HYPOCAUST.lock(); 130 | let hypervisor = {&*hypervisor}.as_ref().unwrap(); 131 | let user_satp = hypervisor.current_user_token(); 132 | extern "C" { 133 | fn __alltraps(); 134 | fn __restore(); 135 | } 136 | let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE; 137 | unsafe { 138 | asm!( 139 | "fence.i", 140 | "jr {restore_va}", // jump to new addr of __restore asm function 141 | restore_va = in(reg) restore_va, 142 | in("a0") trap_cx_ptr, // a0 = virt addr of Trap Context 143 | in("a1") user_satp, // a1 = phy addr of usr page table 144 | options(noreturn) 145 | ); 146 | } 147 | } 148 | 149 | #[no_mangle] 150 | pub fn trap_from_kernel(_trap_cx: &TrapContext) -> ! { 151 | print_hypervisor_backtrace(_trap_cx); 152 | let scause= scause::read(); 153 | let sepc = sepc::read(); 154 | match scause.cause() { 155 | Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::LoadFault) | Trap::Exception(Exception::LoadPageFault)=> { 156 | let stval = stval::read(); 157 | panic!("scause: {:?}, sepc: {:#x}, stval: {:#x}", scause.cause(), _trap_cx.sepc, stval); 158 | }, 159 | _ => { panic!("scause: {:?}, spec: {:#x}, stval: {:#x}", scause.cause(), sepc, stval::read())} 160 | } 161 | } 162 | 163 | 164 | -------------------------------------------------------------------------------- /src/hypervisor/trap/page_fault.rs: -------------------------------------------------------------------------------- 1 | use riscv::register::stval; 2 | 3 | use crate::page_table::{PageTable, PageTableEntry}; 4 | use crate::debug::{PageDebug, print_guest_backtrace}; 5 | use crate::guest::{GuestKernel, gpa2hpa, PageTableRoot}; 6 | use super::{ decode_instruction_at_address, handle_qemu_virt}; 7 | 8 | use super::TrapContext; 9 | 10 | pub fn handle_page_fault(guest: &mut GuestKernel

, ctx: &mut TrapContext) -> bool { 11 | let shadow = guest.shadow(); 12 | if shadow == PageTableRoot::GPA { 13 | hdebug!("Page fault without paging enabled?"); 14 | return false; 15 | } 16 | if shadow == PageTableRoot::UVA { 17 | // 用户态触发异常,进行转发 18 | hwarning!("Page fault from U mode?"); 19 | return false; 20 | } 21 | 22 | let guest_va = stval::read(); 23 | if guest_va % core::mem::size_of::() != 0 { 24 | hwarning!("guest va: {:#x}, sepc: {:#x}", guest_va, ctx.sepc); 25 | print_guest_backtrace::

(&guest.shadow_state.shadow_page_tables.guest_page_table().unwrap(), guest.shadow_state.csrs.satp, ctx) 26 | } 27 | assert_eq!(guest_va % core::mem::size_of::(), 0); 28 | let sepc = ctx.sepc; 29 | let (len, inst) = decode_instruction_at_address(guest, sepc); 30 | // 处理 `MMIO` 31 | if guest.virt_device.qemu_virt_tester.in_region(guest_va){ 32 | handle_qemu_virt(guest, ctx); 33 | ctx.sepc += len; 34 | return true; 35 | } 36 | 37 | let mut pte = 0; 38 | if let Some(_translation) = guest.translate_guest_vaddr(guest_va) { 39 | // 获得翻译后的物理地址 40 | if let Some(inst) = inst { 41 | match inst { 42 | riscv_decode::Instruction::Sd(i) => { 43 | let rs1 = i.rs1() as usize; 44 | let rs2 = i.rs2() as usize; 45 | let offset: isize = if i.imm() > 2048 { ((0b1111 << 12) | i.imm()) as i16 as isize }else{ i.imm() as isize }; 46 | let vaddr = (ctx.x[rs1] as isize + offset) as usize; 47 | assert_eq!(vaddr, guest_va); 48 | pte = ctx.x[rs2]; 49 | }, 50 | riscv_decode::Instruction::Sb(_) | riscv_decode::Instruction::Sw(_) => { 51 | panic!("Unsporrted instruction sepc -> {:#x}, stval: {:#x}", ctx.sepc, stval::read()); 52 | } 53 | _ => { return false } 54 | } 55 | } 56 | let pte = PageTableEntry{ bits: pte }; 57 | let guest_pte_addr = gpa2hpa(guest_va, guest.guest_id); 58 | if guest_pte_addr >= 0x4000000000 { 59 | print_guest_backtrace(guest.shadow_state.shadow_page_tables.guest_page_table().unwrap(), guest.shadow_state.csrs.satp, ctx); 60 | panic!("guest va -> {:#x}, guest_pte_addr: {:#x}, sepc: {:#x}, translation: {:#x}", guest_va, guest_pte_addr, ctx.sepc, _translation); 61 | } 62 | unsafe{ core::ptr::write(guest_pte_addr as *mut usize, pte.bits)} 63 | 64 | guest.synchronize_page_table(guest_va, pte); 65 | ctx.sepc += len; 66 | return true; 67 | } 68 | false 69 | } -------------------------------------------------------------------------------- /src/hypervisor/trap/trap.S: -------------------------------------------------------------------------------- 1 | .altmacro 2 | .macro SAVE_GP n 3 | sd x\n, \n*8(sp) 4 | .endm 5 | .macro LOAD_GP n 6 | ld x\n, \n*8(sp) 7 | .endm 8 | .section .text.trampoline 9 | .globl __alltraps 10 | .globl __restore 11 | .globl __alltraps_k 12 | .globl __restore_k 13 | .align 2 14 | __alltraps: 15 | csrrw sp, sscratch, sp 16 | # now sp->*TrapContext in user space, sscratch->user stack 17 | # save other general purpose registers 18 | sd x1, 1*8(sp) 19 | # skip sp(x2), we will save it later 20 | sd x3, 3*8(sp) 21 | # skip tp(x4), application does not use it 22 | # save x5~x31 23 | .set n, 5 24 | .rept 27 25 | SAVE_GP %n 26 | .set n, n+1 27 | .endr 28 | # we can use t0/t1/t2 freely, because they have been saved in TrapContext 29 | csrr t0, sstatus 30 | csrr t1, sepc 31 | sd t0, 32*8(sp) 32 | sd t1, 33*8(sp) 33 | # read user stack from sscratch and save it in TrapContext 34 | csrr t2, sscratch 35 | sd t2, 2*8(sp) 36 | # load kernel_satp into t0 37 | ld t0, 34*8(sp) 38 | # load trap_handler into t1 39 | ld t1, 36*8(sp) 40 | # move to kernel_sp 41 | ld sp, 35*8(sp) 42 | # switch to kernel space 43 | csrw satp, t0 44 | sfence.vma 45 | # jump to trap_handler 46 | jr t1 47 | 48 | __restore: 49 | # a0: *TrapContext in user space(Constant); a1: user space token 50 | # switch to user space 51 | csrw satp, a1 52 | sfence.vma 53 | csrw sscratch, a0 54 | mv sp, a0 55 | # now sp points to TrapContext in user space, start restoring based on it 56 | # restore sstatus/sepc 57 | ld t0, 32*8(sp) 58 | ld t1, 33*8(sp) 59 | csrw sstatus, t0 60 | csrw sepc, t1 61 | # restore general purpose registers except x0/sp/tp 62 | ld x1, 1*8(sp) 63 | ld x3, 3*8(sp) 64 | .set n, 5 65 | .rept 27 66 | LOAD_GP %n 67 | .set n, n+1 68 | .endr 69 | # back to user stack 70 | ld sp, 2*8(sp) 71 | sret 72 | 73 | .align 2 74 | __alltraps_k: 75 | addi sp, sp, -34*8 76 | sd x1, 1*8(sp) 77 | sd x3, 3*8(sp) 78 | .set n, 5 79 | .rept 27 80 | SAVE_GP %n 81 | .set n, n+1 82 | .endr 83 | csrr t0, sstatus 84 | csrr t1, sepc 85 | sd t0, 32*8(sp) 86 | sd t1, 33*8(sp) 87 | mv a0, sp 88 | csrr t2, sscratch 89 | jalr t2 90 | 91 | __restore_k: 92 | ld t0, 32*8(sp) 93 | ld t1, 33*8(sp) 94 | csrw sstatus, t0 95 | csrw sepc, t1 96 | ld x1, 1*8(sp) 97 | ld x3, 3*8(sp) 98 | .set n, 5 99 | .rept 27 100 | LOAD_GP %n 101 | .set n, n+1 102 | .endr 103 | addi sp, sp, 34*8 104 | sret -------------------------------------------------------------------------------- /src/lang_items.rs: -------------------------------------------------------------------------------- 1 | //! The panic handler 2 | 3 | use crate::sbi::shutdown; 4 | use core::panic::PanicInfo; 5 | 6 | #[panic_handler] 7 | /// panic handler 8 | fn panic(info: &PanicInfo) -> ! { 9 | if let Some(location) = info.location() { 10 | println!( 11 | "\x1b[1;31m[hypervisor] Panicked at {}:{} {}\x1b[0m", 12 | location.file(), 13 | location.line(), 14 | info.message().unwrap() 15 | ); 16 | } else { 17 | println!("[kernel] Panicked: {}", info.message().unwrap()); 18 | } 19 | shutdown() 20 | } 21 | -------------------------------------------------------------------------------- /src/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | ENTRY(_start) 3 | BASE_ADDRESS = 0x80200000; 4 | 5 | SECTIONS 6 | { 7 | . = BASE_ADDRESS; 8 | skernel = .; 9 | 10 | stext = .; 11 | .text : { 12 | *(.text.entry) 13 | . = ALIGN(4K); 14 | strampoline = .; 15 | *(.text.trampoline); 16 | . = ALIGN(4K); 17 | *(.text .text.*) 18 | } 19 | 20 | . = ALIGN(4K); 21 | etext = .; 22 | srodata = .; 23 | .rodata : { 24 | *(.rodata .rodata.*) 25 | *(.srodata .srodata.*) 26 | } 27 | 28 | . = ALIGN(4K); 29 | erodata = .; 30 | sdata = .; 31 | .data : { 32 | *(.data .data.*) 33 | *(.sdata .sdata.*) 34 | } 35 | 36 | . = ALIGN(4K); 37 | edata = .; 38 | sbss_with_stack = .; 39 | .bss : { 40 | *(.bss.stack) 41 | sbss = .; 42 | *(.bss .bss.*) 43 | *(.sbss .sbss.*) 44 | } 45 | 46 | . = ALIGN(4K); 47 | ebss = .; 48 | ekernel = .; 49 | 50 | . = ALIGN(4K); 51 | sinitrd = .; 52 | .initrd : { 53 | *(.initrd) 54 | } 55 | einitrd = .; 56 | 57 | /DISCARD/ : { 58 | *(.eh_frame) 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! The main module and entrypoint 2 | #![no_std] 3 | #![no_main] 4 | #![feature(panic_info_message)] 5 | #![feature(alloc_error_handler)] 6 | #![feature(core_intrinsics)] 7 | #![allow(non_upper_case_globals)] 8 | #![allow(dead_code)] 9 | #![deny(warnings)] 10 | #![feature(naked_functions)] 11 | #![feature(asm_const)] 12 | 13 | 14 | extern crate alloc; 15 | 16 | #[macro_use] 17 | extern crate bitflags; 18 | 19 | #[path = "boards/qemu.rs"] 20 | mod board; 21 | 22 | #[macro_use] 23 | mod console; 24 | mod constants; 25 | mod lang_items; 26 | mod page_table; 27 | mod sbi; 28 | mod sync; 29 | mod timer; 30 | mod guest; 31 | mod debug; 32 | mod mm; 33 | mod device_emu; 34 | mod hypervisor; 35 | 36 | 37 | 38 | use crate::constants::layout::PAGE_SIZE; 39 | use crate::guest::GuestKernel; 40 | use crate::hypervisor::HYPOCAUST; 41 | use crate::mm::MemorySet; 42 | 43 | // use fdt::Fdt; 44 | 45 | #[link_section = ".initrd"] 46 | #[cfg(feature = "embed_guest_kernel")] 47 | static GUEST_KERNEL: [u8;include_bytes!("../guest_kernel").len()] = 48 | *include_bytes!("../guest_kernel"); 49 | 50 | #[cfg(not(feature = "embed_guest_kernel"))] 51 | static GUEST_KERNEL: [u8; 0] = []; 52 | 53 | const BOOT_STACK_SIZE: usize = 16 * PAGE_SIZE; 54 | 55 | #[link_section = ".bss.stack"] 56 | /// hypocaust boot stack 57 | static BOOT_STACK: [u8; BOOT_STACK_SIZE] = [0u8; BOOT_STACK_SIZE]; 58 | 59 | #[link_section = ".text.entry"] 60 | #[export_name = "_start"] 61 | #[naked] 62 | /// hypocaust entrypoint 63 | pub unsafe extern "C" fn start() -> ! { 64 | core::arch::asm!( 65 | // prepare stack 66 | "la sp, {boot_stack}", 67 | "li t2, {boot_stack_size}", 68 | "addi t3, a0, 1", 69 | "mul t2, t2, t3", 70 | "add sp, sp, t2", 71 | // enter hentry 72 | "call hentry", 73 | boot_stack = sym BOOT_STACK, 74 | boot_stack_size = const BOOT_STACK_SIZE, 75 | options(noreturn) 76 | ) 77 | } 78 | 79 | /// clear BSS segment 80 | fn clear_bss() { 81 | extern "C" { 82 | fn sbss(); 83 | fn ebss(); 84 | } 85 | unsafe { 86 | core::slice::from_raw_parts_mut(sbss as usize as *mut u8, ebss as usize - sbss as usize) 87 | .fill(0); 88 | } 89 | } 90 | 91 | #[no_mangle] 92 | pub fn hentry(hart_id: usize, device_tree_blob: usize) -> ! { 93 | if hart_id == 0{ 94 | clear_bss(); 95 | hdebug!("Hello Hypocaust"); 96 | hdebug!("hart_id: {}, device tree blob: {:#x}", hart_id, device_tree_blob); 97 | let meta = hypervisor::fdt::MachineMeta::parse(device_tree_blob); 98 | // 初始化堆及帧分配器 99 | hypervisor::hyp_alloc::heap_init(); 100 | hypervisor::initialize_vmm(meta); 101 | let mut hypervisor = HYPOCAUST.lock(); 102 | let hypervisor = {&mut *hypervisor}.as_mut().unwrap(); 103 | let guest_kernel_memory = MemorySet::new_guest_kernel(&GUEST_KERNEL); 104 | // 初始化虚拟内存 105 | mm::vm_init(&guest_kernel_memory); 106 | hypervisor::trap::init(); 107 | // 测试重映射 108 | mm::remap_test(); 109 | // 测试 guest kernel 内存映射 110 | mm::guest_kernel_test(); 111 | // 开启时钟中断 112 | hypervisor::trap::enable_timer_interrupt(); 113 | timer::set_default_next_trigger(); 114 | // 创建用户态的 guest kernel 内存空间 115 | let user_guest_kernel_memory = MemorySet::create_user_guest_kernel(&guest_kernel_memory); 116 | let guest = GuestKernel::new(user_guest_kernel_memory, 0); 117 | // 开始运行 guest kernel 118 | hypervisor.add_guest(guest); 119 | hypervisor.run_guest(0) 120 | }else{ 121 | unreachable!() 122 | } 123 | } 124 | 125 | -------------------------------------------------------------------------------- /src/mm/memory_region.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::ops::{Index, IndexMut}; 3 | pub struct MemoryRegion{ 4 | ptr: *mut T, 5 | base_address: usize, 6 | length_bytes: usize 7 | } 8 | 9 | unsafe impl Send for MemoryRegion {} 10 | 11 | impl MemoryRegion { 12 | pub fn new(addr: usize, length: usize) -> Self { 13 | assert_eq!(length % mem::size_of::(), 0); 14 | Self{ 15 | ptr: addr as *mut T, 16 | base_address: addr, 17 | length_bytes: length 18 | } 19 | } 20 | 21 | pub fn base(&self) -> usize { self.base_address } 22 | 23 | pub fn len(&self) -> usize { self.length_bytes } 24 | 25 | pub fn in_region(&self, addr: usize) -> bool { 26 | addr >= self.base_address && addr < self.base_address + self.length_bytes 27 | } 28 | } 29 | 30 | impl Index for MemoryRegion { 31 | type Output = T; 32 | fn index(&self, index: usize) -> &T { 33 | assert_eq!(index % mem::size_of::(), 0); 34 | assert!(index >= self.base_address); 35 | 36 | let offset = index - self.base_address; 37 | assert!(offset < self.length_bytes); 38 | unsafe{ &*(self.ptr.add(offset / mem::size_of::())) } 39 | } 40 | } 41 | 42 | impl IndexMut for MemoryRegion { 43 | fn index_mut(&mut self, index: usize) -> &mut T { 44 | assert_eq!(index % mem::size_of::(), 0); 45 | assert!(index >= self.base_address); 46 | 47 | let offset = index - self.base_address; 48 | assert!(offset < self.length_bytes); 49 | unsafe{ &mut *(self.ptr.add(offset / mem::size_of::())) } 50 | } 51 | } -------------------------------------------------------------------------------- /src/mm/memory_set.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of [`MapArea`] and [`MemorySet`]. 2 | 3 | use crate::hypervisor::hyp_alloc::{FrameTracker, frame_alloc}; 4 | use crate::hypervisor::HYPERVISOR_MEMORY; 5 | use crate::page_table::{PTEFlags, PageTable, PageTableEntry}; 6 | use crate::page_table::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum}; 7 | use crate::page_table::{StepByOne, VPNRange, PPNRange}; 8 | use crate::constants::layout::{ 9 | PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT, GUEST_KERNEL_PHY_START_1, 10 | GUEST_KERNEL_VIRT_START, MEMORY_END, MMIO, 11 | GUEST_KERNEL_VIRT_END, GUEST_KERNEL_PHY_END_1, SPT_PA_START_1, SPT_PA_END_1 12 | }; 13 | use alloc::collections::BTreeMap; 14 | use alloc::vec::Vec; 15 | use core::arch::asm; 16 | use core::marker::PhantomData; 17 | use riscv::register::satp; 18 | 19 | extern "C" { 20 | fn stext(); 21 | fn etext(); 22 | fn srodata(); 23 | fn erodata(); 24 | fn sdata(); 25 | fn edata(); 26 | fn sbss_with_stack(); 27 | fn ebss(); 28 | fn ekernel(); 29 | fn strampoline(); 30 | fn sinitrd(); 31 | fn einitrd(); 32 | } 33 | 34 | 35 | /// memory set structure, controls virtual-memory space 36 | pub struct MemorySet { 37 | page_table: P, 38 | areas: Vec>, 39 | } 40 | 41 | impl

MemorySet

where P: PageTable { 42 | pub fn new_bare() -> Self { 43 | Self { 44 | page_table: PageTable::new(), 45 | areas: Vec::new(), 46 | } 47 | } 48 | pub fn token(&self) -> usize { 49 | self.page_table.token() 50 | } 51 | 52 | pub fn page_table(&self) -> &P { 53 | &self.page_table 54 | } 55 | /// Assume that no conflicts. 56 | pub fn insert_framed_area( 57 | &mut self, 58 | start_va: VirtAddr, 59 | end_va: VirtAddr, 60 | permission: MapPermission, 61 | ) { 62 | self.push( 63 | MapArea::new(start_va, end_va, None, None, MapType::Framed, permission), 64 | None, 65 | ); 66 | } 67 | 68 | /// 将内存区域 push 到页表中,并映射内存区域 69 | fn push(&mut self, mut map_area: MapArea

, data: Option<&[u8]>) { 70 | map_area.map(&mut self.page_table); 71 | if let Some(data) = data { 72 | map_area.copy_data(&mut self.page_table, data); 73 | } 74 | self.areas.push(map_area); 75 | } 76 | /// Mention that trampoline is not collected by areas. 77 | fn map_trampoline(&mut self) { 78 | self.page_table.map( 79 | VirtAddr::from(TRAMPOLINE).into(), 80 | PhysAddr::from(strampoline as usize).into(), 81 | PTEFlags::R | PTEFlags::X, 82 | ); 83 | } 84 | 85 | /// Without kernel stacks. 86 | /// 内核虚拟地址映射 87 | /// 映射了内核代码段和数据段以及跳板页,没有映射内核栈 88 | pub fn new_kernel() -> Self { 89 | let mut memory_set = Self::new_bare(); 90 | // map trampoline 91 | memory_set.map_trampoline(); 92 | // map kernel sections 93 | memory_set.push( 94 | MapArea::new( 95 | (stext as usize).into(), 96 | (etext as usize).into(), 97 | Some((stext as usize).into()), 98 | Some((etext as usize).into()), 99 | MapType::Linear, 100 | MapPermission::R | MapPermission::X, 101 | ), 102 | None, 103 | ); 104 | memory_set.push( 105 | MapArea::new( 106 | (srodata as usize).into(), 107 | (erodata as usize).into(), 108 | Some((srodata as usize).into()), 109 | Some((erodata as usize).into()), 110 | MapType::Linear, 111 | MapPermission::R, 112 | ), 113 | None, 114 | ); 115 | memory_set.push( 116 | MapArea::new( 117 | (sdata as usize).into(), 118 | (edata as usize).into(), 119 | Some((sdata as usize).into()), 120 | Some((edata as usize).into()), 121 | MapType::Linear, 122 | MapPermission::R | MapPermission::W, 123 | ), 124 | None, 125 | ); 126 | memory_set.push( 127 | MapArea::new( 128 | (sbss_with_stack as usize).into(), 129 | (ebss as usize).into(), 130 | Some((sbss_with_stack as usize).into()), 131 | Some((ebss as usize).into()), 132 | MapType::Linear, 133 | MapPermission::R | MapPermission::W, 134 | ), 135 | None, 136 | ); 137 | memory_set.push( 138 | MapArea::new( 139 | (ekernel as usize).into(), 140 | MEMORY_END.into(), 141 | Some((ekernel as usize).into()), 142 | Some(MEMORY_END.into()), 143 | MapType::Linear, 144 | MapPermission::R | MapPermission::W, 145 | ), 146 | None, 147 | ); 148 | 149 | // 影子页表映射区域 150 | memory_set.push( 151 | MapArea::new( 152 | VirtAddr::from(SPT_PA_START_1), 153 | VirtAddr::from(SPT_PA_END_1), 154 | Some(PhysAddr::from(SPT_PA_START_1)), 155 | Some(PhysAddr::from(SPT_PA_END_1)), 156 | MapType::Linear, 157 | MapPermission::R | MapPermission::W 158 | ), 159 | None 160 | ); 161 | 162 | for pair in MMIO { 163 | memory_set.push( 164 | MapArea::new( 165 | (*pair).0.into(), 166 | ((*pair).0 + (*pair).1).into(), 167 | Some((*pair).0.into()), 168 | Some(((*pair).0 + (*pair).1).into()), 169 | MapType::Linear, 170 | MapPermission::R | MapPermission::W, 171 | ), 172 | None, 173 | ); 174 | } 175 | 176 | memory_set 177 | } 178 | 179 | /// 创建用户态的 Guest Kernel 内存空间 180 | pub fn create_user_guest_kernel(guest_kernel_memory: &Self) -> Self { 181 | let mut memory_set = Self::new_bare(); 182 | // 代码段:可读可执行 183 | // 数据段:可读 184 | // 所有段映射用户空间 185 | for area in guest_kernel_memory.areas.iter() { 186 | let mut user_area = area.clone(); 187 | // 添加用户标志 188 | user_area.map_perm |= MapPermission::U; 189 | memory_set.push(user_area.clone(), None); 190 | } 191 | // 创建跳板页映射 192 | memory_set.map_trampoline(); 193 | // 映射 Trap Context 194 | memory_set.push( 195 | MapArea::new( 196 | TRAP_CONTEXT.into(), 197 | TRAMPOLINE.into(), 198 | None, 199 | None, 200 | MapType::Framed, 201 | MapPermission::R | MapPermission::W 202 | ), 203 | None, 204 | ); 205 | 206 | for pair in MMIO { 207 | memory_set.push( 208 | MapArea::new( 209 | (*pair).0.into(), 210 | ((*pair).0 + (*pair).1).into(), 211 | Some((*pair).0.into()), 212 | Some(((*pair).0 + (*pair).1).into()), 213 | MapType::Linear, 214 | MapPermission::R | MapPermission::W | MapPermission::U, 215 | ), 216 | None, 217 | ); 218 | } 219 | memory_set 220 | } 221 | 222 | pub fn new_guest_kernel(guest_kernel_data: &[u8]) -> Self { 223 | let mut memory_set = Self::new_bare(); 224 | let elf = xmas_elf::ElfFile::new(guest_kernel_data).unwrap(); 225 | let elf_header = elf.header; 226 | let magic = elf_header.pt1.magic; 227 | assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!"); 228 | let ph_count = elf_header.pt2.ph_count(); 229 | // 物理内存,从 0x8800_0000 开始 230 | // 虚拟内存,从 0x8000_0000 开始 231 | let mut paddr = GUEST_KERNEL_PHY_START_1 as *mut u8; 232 | let mut last_paddr = GUEST_KERNEL_PHY_START_1 as *mut u8; 233 | for i in 0..ph_count { 234 | let ph = elf.program_header(i).unwrap(); 235 | if ph.get_type().unwrap() == xmas_elf::program::Type::Load { 236 | let start_va: VirtAddr = (ph.virtual_addr() as usize).into(); 237 | let end_va: VirtAddr = ((ph.virtual_addr() + ph.mem_size()) as usize).into(); 238 | let mut map_perm = MapPermission { bits: 0 }; 239 | let ph_flags = ph.flags(); 240 | if ph_flags.is_read() { 241 | map_perm |= MapPermission::R; 242 | } 243 | if ph_flags.is_write() { 244 | map_perm |= MapPermission::W; 245 | } 246 | if ph_flags.is_execute() { 247 | map_perm |= MapPermission::X; 248 | } 249 | // 将内存拷贝到对应的物理内存上 250 | unsafe{ 251 | core::ptr::copy(guest_kernel_data.as_ptr().add(ph.offset() as usize), paddr, ph.file_size() as usize); 252 | let page_align_size = ((ph.mem_size() as usize + PAGE_SIZE - 1) >> 12) << 12; 253 | paddr = paddr.add(page_align_size); 254 | } 255 | 256 | let map_area = MapArea::new( 257 | start_va, 258 | end_va, 259 | Some(PhysAddr(last_paddr as usize)), 260 | Some(PhysAddr(paddr as usize)), 261 | MapType::Linear, 262 | map_perm 263 | ); 264 | last_paddr = paddr; 265 | memory_set.push(map_area, None); 266 | } 267 | 268 | } 269 | let offset = paddr as usize - GUEST_KERNEL_PHY_START_1; 270 | // 映射其他物理内存 271 | memory_set.push(MapArea::new( 272 | VirtAddr(offset + GUEST_KERNEL_VIRT_START), 273 | VirtAddr(GUEST_KERNEL_VIRT_END), 274 | Some(PhysAddr(paddr as usize)), 275 | Some(PhysAddr(GUEST_KERNEL_PHY_END_1)), 276 | MapType::Linear, 277 | MapPermission::R | MapPermission::W 278 | ), 279 | None 280 | ); 281 | 282 | memory_set 283 | } 284 | 285 | /// 加载客户操作系统 286 | pub fn hyper_load_guest_kernel(&mut self, guest_kernel_memory: &Self) { 287 | for area in guest_kernel_memory.areas.iter() { 288 | // 修改虚拟地址与物理地址相同 289 | let ppn_range = area.ppn_range.unwrap(); 290 | let start_pa: PhysAddr = ppn_range.get_start().into(); 291 | let end_pa: PhysAddr = ppn_range.get_end().into(); 292 | let start_va: usize = start_pa.into(); 293 | let end_va: usize= end_pa.into(); 294 | let new_area = MapArea::new( 295 | start_va.into(), 296 | end_va.into(), 297 | Some(start_pa), 298 | Some(end_pa), 299 | area.map_type, 300 | area.map_perm 301 | ); 302 | self.push(new_area, None); 303 | } 304 | } 305 | 306 | 307 | /// 激活根页表 308 | pub fn activate(&self) { 309 | let satp = self.page_table.token(); 310 | unsafe { 311 | satp::write(satp); 312 | asm!("sfence.vma"); 313 | } 314 | } 315 | 316 | /// 将虚拟页号翻译成页表项 317 | pub fn translate(&self, vpn: VirtPageNum) -> Option { 318 | self.page_table.translate(vpn) 319 | } 320 | } 321 | 322 | /// map area structure, controls a contiguous piece of virtual memory 323 | #[derive(Clone)] 324 | pub struct MapArea { 325 | pub vpn_range: VPNRange, 326 | pub ppn_range: Option, 327 | pub data_frames: BTreeMap, 328 | pub map_type: MapType, 329 | pub map_perm: MapPermission, 330 | _marker: PhantomData

331 | } 332 | 333 | impl

MapArea

where P: PageTable { 334 | pub fn new( 335 | start_va: VirtAddr, 336 | end_va: VirtAddr, 337 | start_pa: Option, 338 | end_pa: Option, 339 | map_type: MapType, 340 | map_perm: MapPermission, 341 | ) -> Self { 342 | let start_vpn: VirtPageNum = start_va.floor(); 343 | let end_vpn: VirtPageNum = end_va.ceil(); 344 | if let (Some(start_pa), Some(end_pa)) = (start_pa, end_pa) { 345 | let start_ppn = start_pa.floor(); 346 | let end_ppn = end_pa.ceil(); 347 | return Self { 348 | vpn_range: VPNRange::new(start_vpn, end_vpn), 349 | ppn_range: Some(PPNRange::new(start_ppn, end_ppn)), 350 | data_frames: BTreeMap::new(), 351 | map_type, 352 | map_perm, 353 | _marker: PhantomData 354 | } 355 | } 356 | Self{ 357 | vpn_range: VPNRange::new(start_vpn, end_vpn), 358 | ppn_range: None, 359 | data_frames: BTreeMap::new(), 360 | map_type, 361 | map_perm, 362 | _marker: PhantomData 363 | } 364 | } 365 | pub fn map_one(&mut self, page_table: &mut P, vpn: VirtPageNum, ppn_: Option) { 366 | let ppn: PhysPageNum; 367 | match self.map_type { 368 | // 线性映射 369 | MapType::Linear => { 370 | ppn = ppn_.unwrap(); 371 | }, 372 | MapType::Framed => { 373 | let frame = frame_alloc().unwrap(); 374 | ppn = frame.ppn; 375 | self.data_frames.insert(vpn, frame); 376 | } 377 | } 378 | let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap(); 379 | page_table.map(vpn, ppn, pte_flags); 380 | } 381 | #[allow(unused)] 382 | pub fn unmap_one(&mut self, page_table: &mut P, vpn: VirtPageNum) { 383 | if self.map_type == MapType::Framed { 384 | self.data_frames.remove(&vpn); 385 | } 386 | page_table.unmap(vpn); 387 | } 388 | pub fn map(&mut self, page_table: &mut P) { 389 | let vpn_range = self.vpn_range; 390 | if let Some(ppn_range) = self.ppn_range { 391 | let ppn_start: usize = ppn_range.get_start().into(); 392 | let ppn_end: usize = ppn_range.get_end().into(); 393 | let vpn_start: usize = vpn_range.get_start().into(); 394 | let vpn_end: usize = vpn_range.get_end().into(); 395 | assert_eq!(ppn_end - ppn_start, vpn_end - vpn_start); 396 | let mut ppn = ppn_range.get_start(); 397 | let mut vpn = vpn_range.get_start(); 398 | loop { 399 | self.map_one(page_table, vpn, Some(ppn)); 400 | ppn.step(); 401 | vpn.step(); 402 | if ppn == ppn_range.get_end() && vpn == vpn_range.get_end() { 403 | break; 404 | } 405 | } 406 | }else{ 407 | for vpn in self.vpn_range { 408 | self.map_one(page_table, vpn, None) 409 | } 410 | } 411 | } 412 | #[allow(unused)] 413 | pub fn unmap(&mut self, page_table: &mut P) { 414 | for vpn in self.vpn_range { 415 | self.unmap_one(page_table, vpn); 416 | } 417 | } 418 | /// data: start-aligned but maybe with shorter length 419 | /// assume that all frames were cleared before 420 | pub fn copy_data(&mut self, page_table: &mut P, data: &[u8]) { 421 | assert_eq!(self.map_type, MapType::Framed); 422 | let mut start: usize = 0; 423 | let mut current_vpn = self.vpn_range.get_start(); 424 | let len = data.len(); 425 | loop { 426 | let src = &data[start..len.min(start + PAGE_SIZE)]; 427 | let dst = &mut page_table 428 | .translate(current_vpn) 429 | .unwrap() 430 | .ppn() 431 | .get_bytes_array()[..src.len()]; 432 | dst.copy_from_slice(src); 433 | start += PAGE_SIZE; 434 | if start >= len { 435 | break; 436 | } 437 | current_vpn.step(); 438 | } 439 | } 440 | 441 | } 442 | 443 | #[derive(Copy, Clone, PartialEq, Debug)] 444 | /// map type for memory set: identical or framed 445 | pub enum MapType { 446 | Framed, 447 | Linear 448 | } 449 | 450 | bitflags! { 451 | /// map permission corresponding to that in pte: `R W X U` 452 | pub struct MapPermission: u8 { 453 | const R = 1 << 1; 454 | const W = 1 << 2; 455 | const X = 1 << 3; 456 | const U = 1 << 4; 457 | } 458 | } 459 | 460 | #[allow(unused)] 461 | pub fn remap_test() { 462 | let mut kernel_space = HYPERVISOR_MEMORY.exclusive_access(); 463 | let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into(); 464 | let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into(); 465 | let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into(); 466 | 467 | assert!(!kernel_space 468 | .page_table 469 | .translate(mid_text.floor()) 470 | .unwrap() 471 | .writable(),); 472 | assert!(!kernel_space 473 | .page_table 474 | .translate(mid_rodata.floor()) 475 | .unwrap() 476 | .writable(),); 477 | assert!(!kernel_space 478 | .page_table 479 | .translate(mid_data.floor()) 480 | .unwrap() 481 | .executable(),); 482 | // 测试 guest ketnel 483 | hdebug!("remap test passed!"); 484 | } 485 | 486 | #[allow(unused)] 487 | pub fn guest_kernel_test() { 488 | use crate::constants::layout::GUEST_KERNEL_PHY_START_1; 489 | let mut kernel_space = HYPERVISOR_MEMORY.exclusive_access(); 490 | 491 | let guest_kernel_text: VirtAddr = GUEST_KERNEL_PHY_START_1.into(); 492 | 493 | assert!(kernel_space.page_table.translate(guest_kernel_text.floor()).unwrap().executable()); 494 | assert!(kernel_space.page_table.translate(guest_kernel_text.floor()).unwrap().readable()); 495 | // 尝试读数据 496 | unsafe{ 497 | core::ptr::read(GUEST_KERNEL_PHY_START_1 as *const u32); 498 | } 499 | // 测试 guest ketnel 500 | hdebug!("guest kernel test passed!"); 501 | } 502 | 503 | -------------------------------------------------------------------------------- /src/mm/mod.rs: -------------------------------------------------------------------------------- 1 | mod memory_set; 2 | mod memory_region; 3 | 4 | pub use memory_set::{ remap_test, guest_kernel_test }; 5 | pub use memory_set::{MapPermission, MemorySet}; 6 | pub use memory_region::MemoryRegion; 7 | 8 | use crate::hypervisor::HYPERVISOR_MEMORY; 9 | use crate::page_table::PageTableSv39; 10 | 11 | pub fn vm_init(guest_kernel_memory: &MemorySet) { 12 | let mut hypervisor_memory = HYPERVISOR_MEMORY.exclusive_access(); 13 | hypervisor_memory.hyper_load_guest_kernel(guest_kernel_memory); 14 | hypervisor_memory.activate(); 15 | } -------------------------------------------------------------------------------- /src/page_table/address.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of physical and virtual address and page number. 2 | 3 | use super::PageTableEntry; 4 | use crate::constants::layout::{PAGE_SIZE, PAGE_SIZE_BITS}; 5 | use core::fmt::{self, Debug, Formatter}; 6 | 7 | /// physical address 8 | const PA_WIDTH_SV39: usize = 56; 9 | const VA_WIDTH_SV39: usize = 39; 10 | const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS; 11 | const VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS; 12 | 13 | /// Definitions 14 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 15 | pub struct PhysAddr(pub usize); 16 | 17 | /// virtual address 18 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 19 | pub struct VirtAddr(pub usize); 20 | 21 | /// physical page number 22 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 23 | pub struct PhysPageNum(pub usize); 24 | 25 | /// virtual page number 26 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 27 | pub struct VirtPageNum(pub usize); 28 | 29 | /// Debugging 30 | 31 | impl Debug for VirtAddr { 32 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 33 | f.write_fmt(format_args!("VA:{:#x}", self.0)) 34 | } 35 | } 36 | impl Debug for VirtPageNum { 37 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 38 | f.write_fmt(format_args!("VPN:{:#x}", self.0)) 39 | } 40 | } 41 | impl Debug for PhysAddr { 42 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 43 | f.write_fmt(format_args!("PA:{:#x}", self.0)) 44 | } 45 | } 46 | impl Debug for PhysPageNum { 47 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 48 | f.write_fmt(format_args!("PPN:{:#x}", self.0)) 49 | } 50 | } 51 | 52 | /// T: {PhysAddr, VirtAddr, PhysPageNum, VirtPageNum} 53 | /// T -> usize: T.0 54 | /// usize -> T: usize.into() 55 | 56 | impl From for PhysAddr { 57 | fn from(v: usize) -> Self { 58 | Self(v & ((1 << PA_WIDTH_SV39) - 1)) 59 | } 60 | } 61 | impl From for PhysPageNum { 62 | fn from(v: usize) -> Self { 63 | Self(v & ((1 << PPN_WIDTH_SV39) - 1)) 64 | } 65 | } 66 | impl From for VirtAddr { 67 | fn from(v: usize) -> Self { 68 | Self(v & ((1 << VA_WIDTH_SV39) - 1)) 69 | } 70 | } 71 | impl From for VirtPageNum { 72 | fn from(v: usize) -> Self { 73 | Self(v & ((1 << VPN_WIDTH_SV39) - 1)) 74 | } 75 | } 76 | impl From for usize { 77 | fn from(v: PhysAddr) -> Self { 78 | v.0 79 | } 80 | } 81 | impl From for usize { 82 | fn from(v: PhysPageNum) -> Self { 83 | v.0 84 | } 85 | } 86 | impl From for usize { 87 | fn from(v: VirtAddr) -> Self { 88 | if v.0 >= (1 << (VA_WIDTH_SV39 - 1)) { 89 | v.0 | (!((1 << VA_WIDTH_SV39) - 1)) 90 | } else { 91 | v.0 92 | } 93 | } 94 | } 95 | impl From for usize { 96 | fn from(v: VirtPageNum) -> Self { 97 | v.0 98 | } 99 | } 100 | 101 | impl VirtAddr { 102 | pub fn floor(&self) -> VirtPageNum { 103 | VirtPageNum(self.0 / PAGE_SIZE) 104 | } 105 | pub fn ceil(&self) -> VirtPageNum { 106 | VirtPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) 107 | } 108 | pub fn page_offset(&self) -> usize { 109 | self.0 & (PAGE_SIZE - 1) 110 | } 111 | pub fn aligned(&self) -> bool { 112 | self.page_offset() == 0 113 | } 114 | } 115 | impl From for VirtPageNum { 116 | fn from(v: VirtAddr) -> Self { 117 | assert_eq!(v.page_offset(), 0); 118 | v.floor() 119 | } 120 | } 121 | impl From for VirtAddr { 122 | fn from(v: VirtPageNum) -> Self { 123 | Self(v.0 << PAGE_SIZE_BITS) 124 | } 125 | } 126 | impl PhysAddr { 127 | pub fn floor(&self) -> PhysPageNum { 128 | PhysPageNum(self.0 / PAGE_SIZE) 129 | } 130 | pub fn ceil(&self) -> PhysPageNum { 131 | PhysPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) 132 | } 133 | pub fn page_offset(&self) -> usize { 134 | self.0 & (PAGE_SIZE - 1) 135 | } 136 | pub fn aligned(&self) -> bool { 137 | self.page_offset() == 0 138 | } 139 | } 140 | impl From for PhysPageNum { 141 | fn from(v: PhysAddr) -> Self { 142 | assert_eq!(v.page_offset(), 0); 143 | v.floor() 144 | } 145 | } 146 | impl From for PhysAddr { 147 | fn from(v: PhysPageNum) -> Self { 148 | Self(v.0 << PAGE_SIZE_BITS) 149 | } 150 | } 151 | 152 | impl VirtPageNum { 153 | pub fn indexes(&self) -> [usize; 3] { 154 | let mut vpn = self.0; 155 | let mut idx = [0usize; 3]; 156 | for i in (0..3).rev() { 157 | idx[i] = vpn & 511; 158 | vpn >>= 9; 159 | } 160 | idx 161 | } 162 | } 163 | 164 | impl PhysPageNum { 165 | /// 暂时的实现 166 | pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] { 167 | let pa: PhysAddr = (*self).into(); 168 | unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) } 169 | } 170 | pub fn get_bytes_array(&self) -> &'static mut [u8] { 171 | let pa: PhysAddr = (*self).into(); 172 | unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut u8, 4096) } 173 | } 174 | pub fn get_mut(&self) -> &'static mut T { 175 | let pa: PhysAddr = (*self).into(); 176 | unsafe { (pa.0 as *mut T).as_mut().unwrap() } 177 | } 178 | } 179 | 180 | pub trait StepByOne { 181 | fn step(&mut self); 182 | } 183 | impl StepByOne for VirtPageNum { 184 | fn step(&mut self) { 185 | self.0 += 1; 186 | } 187 | } 188 | 189 | impl StepByOne for PhysPageNum { 190 | fn step(&mut self) { 191 | self.0 += 1; 192 | } 193 | } 194 | 195 | #[derive(Copy, Clone)] 196 | /// a simple range structure for type T 197 | pub struct SimpleRange 198 | where 199 | T: StepByOne + Copy + PartialEq + PartialOrd + Debug, 200 | { 201 | l: T, 202 | r: T, 203 | } 204 | impl SimpleRange 205 | where 206 | T: StepByOne + Copy + PartialEq + PartialOrd + Debug, 207 | { 208 | pub fn new(start: T, end: T) -> Self { 209 | assert!(start <= end, "start {:?} > end {:?}!", start, end); 210 | Self { l: start, r: end } 211 | } 212 | pub fn get_start(&self) -> T { 213 | self.l 214 | } 215 | pub fn get_end(&self) -> T { 216 | self.r 217 | } 218 | } 219 | impl IntoIterator for SimpleRange 220 | where 221 | T: StepByOne + Copy + PartialEq + PartialOrd + Debug, 222 | { 223 | type Item = T; 224 | type IntoIter = SimpleRangeIterator; 225 | fn into_iter(self) -> Self::IntoIter { 226 | SimpleRangeIterator::new(self.l, self.r) 227 | } 228 | } 229 | /// iterator for the simple range structure 230 | pub struct SimpleRangeIterator 231 | where 232 | T: StepByOne + Copy + PartialEq + PartialOrd + Debug, 233 | { 234 | current: T, 235 | end: T, 236 | } 237 | impl SimpleRangeIterator 238 | where 239 | T: StepByOne + Copy + PartialEq + PartialOrd + Debug, 240 | { 241 | pub fn new(l: T, r: T) -> Self { 242 | Self { current: l, end: r } 243 | } 244 | } 245 | impl Iterator for SimpleRangeIterator 246 | where 247 | T: StepByOne + Copy + PartialEq + PartialOrd + Debug, 248 | { 249 | type Item = T; 250 | fn next(&mut self) -> Option { 251 | if self.current == self.end { 252 | None 253 | } else { 254 | let t = self.current; 255 | self.current.step(); 256 | Some(t) 257 | } 258 | } 259 | } 260 | 261 | /// a simple range structure for virtual page number 262 | pub type VPNRange = SimpleRange; 263 | pub type PPNRange = SimpleRange; 264 | #[allow(unused)] 265 | pub type PageRange = SimpleRange; 266 | -------------------------------------------------------------------------------- /src/page_table/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | mod address; 3 | mod pte; 4 | mod sv39; 5 | mod sv48; 6 | mod sv57; 7 | 8 | use alloc::vec::Vec; 9 | use crate::guest::gpa2hpa; 10 | 11 | pub use address::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum}; 12 | pub use address::{StepByOne, VPNRange, PPNRange, PageRange}; 13 | pub use sv39::{translated_byte_buffer, PageTableSv39}; 14 | pub use pte::{PageTableEntry, PTEFlags}; 15 | 16 | pub trait PageTable: Clone { 17 | fn new() -> Self; 18 | fn from_token(satp: usize) -> Self; 19 | fn from_ppn(ppn: PhysPageNum) -> Self; 20 | fn root_ppn(&self) -> PhysPageNum; 21 | fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry>; 22 | fn find_pte(&self, vpn: VirtPageNum) -> Option<&mut PageTableEntry>; 23 | fn find_guest_pte(&self, vpn: VirtPageNum, hart_id: usize) -> Option<&mut PageTableEntry>; 24 | #[allow(unused)] 25 | fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags); 26 | #[allow(unused)] 27 | fn unmap(&mut self, vpn: VirtPageNum); 28 | fn translate(&self, vpn: VirtPageNum) -> Option; 29 | #[allow(unused)] 30 | fn translate_guest(&self, vpn: VirtPageNum, hart_id: usize) -> Option; 31 | fn token(&self) -> usize; 32 | /// page walk,并返回所有 `walk` 过的所有页表项 33 | fn walk_page_table usize>(root: usize, va: usize, read_pte: R) -> Option; 34 | } 35 | 36 | 37 | #[allow(unused)] 38 | pub enum PageError { 39 | 40 | } 41 | 42 | /// The page sizes supported by RISC -V 43 | #[allow(unused)] 44 | #[repr(u64)] 45 | #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Hash, Ord)] 46 | pub enum PageSize { 47 | /// Page 48 | Size4K = 4 * 1024, 49 | /// Mega 50 | Size2M = 2 * 1024 * 1024, 51 | /// Giga 52 | Size1G = 1024 * 1204 * 1024, 53 | /// Tera 54 | Size512G = 512 * 1024 * 1024 * 1024 55 | } 56 | 57 | 58 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 59 | pub enum PageTableLevel { 60 | Level4KB, 61 | Level2MB, 62 | Level1GB, 63 | } 64 | 65 | #[derive(Debug)] 66 | pub struct PteWrapper { 67 | pub addr: usize, 68 | pub pte: PageTableEntry, 69 | pub level: PageTableLevel 70 | } 71 | 72 | #[derive(Debug)] 73 | pub struct PageWalk { 74 | pub path: Vec, 75 | pub pa: usize 76 | } 77 | 78 | #[derive(Debug)] 79 | pub struct AddressTranslation { 80 | pub pte: PageTableEntry, 81 | pub pte_addr: usize, 82 | pub guest_pa: usize, 83 | pub level: PageTableLevel, 84 | pub page_walk: PageWalk 85 | } 86 | 87 | 88 | /// 将 guest vaddr 翻译为 host paddr,并返回 `AddressTranslation` 89 | pub fn translate_guest_address(hart_id: usize, root_page_table: usize, va: usize) -> Option { 90 | P::walk_page_table(root_page_table, va, |va|{ 91 | let pa = gpa2hpa(va, hart_id); 92 | unsafe{ core::ptr::read(pa as *const usize) } 93 | }).map(|t| { 94 | AddressTranslation { 95 | pte: t.path[t.path.len() - 1].pte, 96 | pte_addr: t.path[t.path.len() - 1].addr, 97 | level: t.path[t.path.len() - 1].level, 98 | guest_pa: t.pa, 99 | page_walk: t 100 | } 101 | }) 102 | } 103 | -------------------------------------------------------------------------------- /src/page_table/pte.rs: -------------------------------------------------------------------------------- 1 | use bitflags::*; 2 | use crate::page_table::{PhysPageNum}; 3 | 4 | bitflags! { 5 | /// page table entry flags 6 | pub struct PTEFlags: u8 { 7 | const V = 1 << 0; 8 | const R = 1 << 1; 9 | const W = 1 << 2; 10 | const X = 1 << 3; 11 | const U = 1 << 4; 12 | const G = 1 << 5; 13 | const A = 1 << 6; 14 | const D = 1 << 7; 15 | } 16 | } 17 | 18 | #[derive(Copy, Clone, PartialEq, Debug)] 19 | #[repr(C)] 20 | /// page table entry structure 21 | pub struct PageTableEntry { 22 | pub bits: usize, 23 | } 24 | 25 | impl PageTableEntry { 26 | pub fn new(ppn: PhysPageNum, flags: PTEFlags) -> Self { 27 | PageTableEntry { 28 | bits: ppn.0 << 10 | flags.bits as usize, 29 | } 30 | } 31 | pub fn empty() -> Self { 32 | PageTableEntry { bits: 0 } 33 | } 34 | pub fn ppn(&self) -> PhysPageNum { 35 | (self.bits >> 10 & ((1usize << 44) - 1)).into() 36 | } 37 | pub fn flags(&self) -> PTEFlags { 38 | PTEFlags::from_bits((self.bits & 0x3ff) as u8 ).unwrap() 39 | } 40 | pub fn is_valid(&self) -> bool { 41 | (self.flags() & PTEFlags::V) != PTEFlags::empty() 42 | } 43 | pub fn readable(&self) -> bool { 44 | (self.flags() & PTEFlags::R) != PTEFlags::empty() 45 | } 46 | pub fn writable(&self) -> bool { 47 | (self.flags() & PTEFlags::W) != PTEFlags::empty() 48 | } 49 | pub fn executable(&self) -> bool { 50 | (self.flags() & PTEFlags::X) != PTEFlags::empty() 51 | } 52 | 53 | pub fn is_user(&self) -> bool { 54 | (self.flags() & PTEFlags::U) != PTEFlags::empty() 55 | } 56 | 57 | pub fn is_global(&self) -> bool { 58 | (self.flags() & PTEFlags::G) != PTEFlags::empty() 59 | } 60 | 61 | pub fn dirty(&self) -> bool { 62 | (self.flags() & PTEFlags::D) != PTEFlags::empty() 63 | } 64 | 65 | pub fn accessed(&self) -> bool { 66 | (self.flags() & PTEFlags::A) != PTEFlags::empty() 67 | } 68 | } -------------------------------------------------------------------------------- /src/page_table/sv39.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of [`PageTableEntry`] and [`PageTable`]. 2 | 3 | use super::{PhysPageNum, StepByOne, VirtAddr, VirtPageNum, PTEFlags, PageTableEntry, PageTable, PageTableLevel, PteWrapper, PageWalk}; 4 | use crate::guest::gpa2hpa; 5 | use crate::hypervisor::hyp_alloc::{FrameTracker, frame_alloc}; 6 | use alloc::vec; 7 | use alloc::vec::Vec; 8 | 9 | 10 | pub struct PageWalkSv39 { 11 | pub path: [PageTableEntry; 3], 12 | pub pa: usize 13 | } 14 | 15 | /// page table structure 16 | #[derive(Clone)] 17 | pub struct PageTableSv39 { 18 | root_ppn: PhysPageNum, 19 | frames: Vec, 20 | } 21 | 22 | /// Assume that it won't oom when creating/mapping. 23 | impl PageTable for PageTableSv39 { 24 | fn new() -> Self { 25 | let frame = frame_alloc().unwrap(); 26 | PageTableSv39 { 27 | root_ppn: frame.ppn, 28 | frames: vec![frame], 29 | } 30 | } 31 | /// Temporarily used to get arguments from user space. 32 | fn from_token(satp: usize) -> Self { 33 | Self { 34 | root_ppn: PhysPageNum::from(satp & ((1usize << 44) - 1)), 35 | frames: Vec::new(), 36 | } 37 | } 38 | 39 | fn from_ppn(ppn: PhysPageNum) -> Self { 40 | Self { 41 | root_ppn: ppn, 42 | frames: Vec::new() 43 | } 44 | } 45 | 46 | fn root_ppn(&self) -> PhysPageNum { 47 | self.root_ppn 48 | } 49 | 50 | fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> { 51 | let idxs = vpn.indexes(); 52 | let mut ppn = self.root_ppn; 53 | let mut result: Option<&mut PageTableEntry> = None; 54 | for (i, idx) in idxs.iter().enumerate() { 55 | let pte = &mut ppn.get_pte_array()[*idx]; 56 | if i == 2 { 57 | result = Some(pte); 58 | break; 59 | } 60 | if !pte.is_valid() { 61 | let frame = frame_alloc().unwrap(); 62 | *pte = PageTableEntry::new(frame.ppn, PTEFlags::V); 63 | self.frames.push(frame); 64 | } 65 | ppn = pte.ppn(); 66 | } 67 | result 68 | } 69 | 70 | fn find_pte(&self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> { 71 | let idxs = vpn.indexes(); 72 | let mut ppn = self.root_ppn; 73 | let mut result: Option<&mut PageTableEntry> = None; 74 | for (i, idx) in idxs.iter().enumerate() { 75 | let pte = &mut ppn.get_pte_array()[*idx]; 76 | if i == 2 { 77 | result = Some(pte); 78 | break; 79 | } 80 | if !pte.is_valid() { 81 | return None; 82 | } 83 | ppn = pte.ppn(); 84 | } 85 | result 86 | } 87 | 88 | fn find_guest_pte(&self, vpn: VirtPageNum, hart_id: usize) -> Option<&mut PageTableEntry> { 89 | let idxs = vpn.indexes(); 90 | let mut ppn = self.root_ppn; 91 | let mut result: Option<&mut PageTableEntry> = None; 92 | for (i, idx) in idxs.iter().enumerate() { 93 | let pte; 94 | if i == 0{ pte = &mut ppn.get_pte_array()[*idx]; } 95 | else{ 96 | ppn = PhysPageNum::from(gpa2hpa(ppn.0 << 12, hart_id) >> 12); 97 | pte = &mut ppn.get_pte_array()[*idx]; 98 | } 99 | if i == 2 { 100 | result = Some(pte); 101 | break; 102 | } 103 | if !pte.is_valid() { 104 | return None; 105 | } 106 | ppn = pte.ppn(); 107 | } 108 | result 109 | } 110 | 111 | #[allow(unused)] 112 | fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) { 113 | let pte = self.find_pte_create(vpn).unwrap(); 114 | assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn); 115 | *pte = PageTableEntry::new(ppn, flags | PTEFlags::V); 116 | } 117 | #[allow(unused)] 118 | fn unmap(&mut self, vpn: VirtPageNum) { 119 | let pte = self.find_pte(vpn).unwrap(); 120 | assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn); 121 | *pte = PageTableEntry::empty(); 122 | } 123 | 124 | fn translate(&self, vpn: VirtPageNum) -> Option { 125 | self.find_pte(vpn).map(|pte| *pte) 126 | } 127 | 128 | #[allow(unused)] 129 | fn translate_guest(&self, vpn: VirtPageNum, hart_id: usize) -> Option { 130 | self.find_guest_pte(vpn, hart_id).map(|pte| *pte) 131 | } 132 | 133 | fn token(&self) -> usize { 134 | 8usize << 60 | self.root_ppn.0 135 | } 136 | 137 | fn walk_page_table usize>(root: usize, va: usize, read_pte: R) -> Option { 138 | let mut path = Vec::new(); 139 | let mut page_table = root; 140 | for level in 0..3 { 141 | let pte_index = (va >> (30 - 9 * level)) & 0x1ff; 142 | let pte_addr = page_table + pte_index * 8; 143 | let pte = read_pte(pte_addr); 144 | let level = match level { 145 | 0 => PageTableLevel::Level1GB, 146 | 1 => PageTableLevel::Level2MB, 147 | 2 => PageTableLevel::Level4KB, 148 | _ => unreachable!(), 149 | }; 150 | let pte = PageTableEntry{ bits: pte}; 151 | path.push(PteWrapper{ addr: pte_addr, pte, level}); 152 | 153 | if !pte.is_valid() || (pte.writable() && !pte.readable()){ return None; } 154 | else if pte.readable() | pte.executable() { 155 | let pa = match level { 156 | PageTableLevel::Level4KB => ((pte.bits >> 10) << 12) | (va & 0xfff), 157 | PageTableLevel::Level2MB => ((pte.bits >> 19) << 21) | (va & 0x1fffff), 158 | PageTableLevel::Level1GB => ((pte.bits >> 28) << 30) | (va & 0x3fffffff), 159 | }; 160 | return Some(super::PageWalk { path, pa}); 161 | }else{ 162 | page_table = (pte.bits >> 10) << 12; 163 | } 164 | } 165 | None 166 | } 167 | } 168 | 169 | /// translate a pointer to a mutable u8 Vec through page table 170 | pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> { 171 | let page_table = PageTableSv39::from_token(token); 172 | let mut start = ptr as usize; 173 | let end = start + len; 174 | let mut v = Vec::new(); 175 | while start < end { 176 | let start_va = VirtAddr::from(start); 177 | let mut vpn = start_va.floor(); 178 | let ppn = page_table.translate(vpn).unwrap().ppn(); 179 | vpn.step(); 180 | let mut end_va: VirtAddr = vpn.into(); 181 | end_va = end_va.min(VirtAddr::from(end)); 182 | if end_va.page_offset() == 0 { 183 | v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]); 184 | } else { 185 | v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]); 186 | } 187 | start = end_va.into(); 188 | } 189 | v 190 | } 191 | -------------------------------------------------------------------------------- /src/page_table/sv48.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust/3a2fcf6b4b22d218d42ecf59042bc329c59872e6/src/page_table/sv48.rs -------------------------------------------------------------------------------- /src/page_table/sv57.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust/3a2fcf6b4b22d218d42ecf59042bc329c59872e6/src/page_table/sv57.rs -------------------------------------------------------------------------------- /src/sbi.rs: -------------------------------------------------------------------------------- 1 | //! SBI call wrappers 2 | 3 | use core::arch::asm; 4 | 5 | 6 | const SBI_CONSOLE_PUTCHAR: usize = 1; 7 | const SBI_CONSOLE_GETCHAR: usize = 2; 8 | 9 | 10 | #[inline(always)] 11 | /// general sbi call 12 | fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { 13 | let mut ret; 14 | unsafe { 15 | asm!( 16 | "li x16, 0", 17 | "ecall", 18 | inlateout("x10") arg0 => ret, 19 | in("x11") arg1, 20 | in("x12") arg2, 21 | in("x17") which, 22 | ); 23 | } 24 | ret 25 | } 26 | 27 | /// use sbi call to putchar in console (qemu uart handler) 28 | pub fn console_putchar(c: usize) { 29 | sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0); 30 | } 31 | 32 | /// use sbi call to getchar from console (qemu uart handler) 33 | pub fn console_getchar() -> usize { 34 | sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0) 35 | } 36 | 37 | pub fn set_timer(stime: usize) { 38 | sbi_rt::set_timer(stime as u64); 39 | } 40 | 41 | /// use sbi call to shutdown the kernel 42 | pub fn shutdown() -> ! { 43 | sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::SystemFailure); 44 | unreachable!() 45 | } 46 | -------------------------------------------------------------------------------- /src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | //! Synchronization and interior mutability primitives 2 | 3 | mod up; 4 | mod mutex; 5 | 6 | pub use up::UPSafeCell; 7 | -------------------------------------------------------------------------------- /src/sync/mutex.rs: -------------------------------------------------------------------------------- 1 | pub trait Mutex: Sync + Send { 2 | fn lock(&self); 3 | fn unlock(&self); 4 | } -------------------------------------------------------------------------------- /src/sync/up.rs: -------------------------------------------------------------------------------- 1 | //! Uniprocessor interior mutability primitives 2 | 3 | use core::cell::{RefCell, RefMut}; 4 | 5 | /// Wrap a static data structure inside it so that we are 6 | /// able to access it without any `unsafe`. 7 | /// 8 | /// We should only use it in uniprocessor. 9 | /// 10 | /// In order to get mutable reference of inner data, call 11 | /// `exclusive_access`. 12 | pub struct UPSafeCell { 13 | /// inner data 14 | inner: RefCell, 15 | } 16 | 17 | unsafe impl Sync for UPSafeCell {} 18 | 19 | impl UPSafeCell { 20 | /// User is responsible to guarantee that inner struct is only used in 21 | /// uniprocessor. 22 | pub unsafe fn new(value: T) -> Self { 23 | Self { 24 | inner: RefCell::new(value), 25 | } 26 | } 27 | /// Exclusive access inner data in UPSafeCell. Panic if the data has been borrowed. 28 | pub fn exclusive_access(&self) -> RefMut<'_, T> { 29 | self.inner.borrow_mut() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | //! RISC-V timer-related functionality 2 | 3 | use crate::constants::layout::CLOCK_FREQ; 4 | use riscv::register::time; 5 | use crate::sbi::set_timer; 6 | 7 | const TICKS_PER_SEC: usize = 100; 8 | const MSEC_PER_SEC: usize = 1000; 9 | 10 | pub fn get_default_timer() -> usize { 11 | CLOCK_FREQ / TICKS_PER_SEC 12 | } 13 | 14 | pub fn get_time() -> usize { 15 | time::read() 16 | } 17 | 18 | /// get current time in microseconds 19 | pub fn get_time_ms() -> usize { 20 | time::read() / (CLOCK_FREQ / MSEC_PER_SEC) 21 | } 22 | 23 | pub fn set_next_trigger(stimer: usize) { 24 | set_timer(stimer); 25 | } 26 | 27 | /// set the next timer interrupt 28 | pub fn set_default_next_trigger() { 29 | set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC); 30 | } 31 | --------------------------------------------------------------------------------