├── .cargo └── config.toml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── bootloader ├── fw_payload.bin └── rustsbi-qemu.bin ├── docs ├── boot.md ├── guest_page_table.md ├── image │ └── G-state-Sv39.jpg └── trap.md ├── guest ├── hyperbench │ ├── hyperbench │ ├── hyperbench.dtb │ └── hyperbench.dts ├── linux │ ├── linux.bin │ ├── linux.dts │ ├── linux.elf │ └── patches │ │ ├── 0001-hypocaust-2-riscv-mm-Disable-l4-and-l5-page-table.patch │ │ ├── 0002-hypocaust-2-riscv-sbi-Disable-remote-fence.patch │ │ └── 0003-hypocaust-2-riscv-defconfig-Disable-perf-events.patch ├── rCore-Tutorial-v3 │ ├── .gitignore │ ├── fs.img │ ├── rCore-Tutorial-v3.bin │ ├── rCore-Tutorial-v3.dts │ └── rCore-Tutorial-v3.elf ├── rtthread │ ├── .gitignore │ ├── rtthread.dts │ └── rtthread.elf └── u-boot │ ├── u-boot.bin │ ├── u-boot.dtb │ ├── u-boot.dts │ ├── u-boot.elf │ ├── uboot.bin │ └── uboot.elf ├── rust-toolchain.toml ├── scripts ├── hyperbench.sh ├── linux.sh ├── rCore-Tutorial-v3.sh ├── rt-thread.sh └── u-boot.sh └── src ├── boards └── qemu.rs ├── console.rs ├── constants.rs ├── detect.rs ├── device_emu ├── mod.rs └── plic.rs ├── drivers ├── iommu │ ├── core.rs │ ├── device_directory.rs │ ├── mod.rs │ └── registers.rs └── mod.rs ├── error.rs ├── guest ├── context.rs ├── guest.S ├── mod.rs ├── sbi.rs ├── trap.S ├── vcpu.rs └── vmexit.rs ├── hyp_alloc ├── frame_allocator.rs ├── heap_allocator.rs └── mod.rs ├── hypervisor.rs ├── lang_items.rs ├── linker-qemu.ld ├── main.rs ├── mm ├── memory_set.rs └── mod.rs ├── page_table ├── address.rs ├── mod.rs ├── pte.rs └── sv39.rs ├── sbi.rs └── sync ├── mod.rs └── up.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 | ] 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | guest.bin 3 | guest.elf 4 | .gdb_history 5 | /guest.S 6 | /hyper.S 7 | guest.dtb 8 | -------------------------------------------------------------------------------- /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 = "cfg-if" 58 | version = "1.0.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 61 | 62 | [[package]] 63 | name = "fdt" 64 | version = "0.1.5" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" 67 | 68 | [[package]] 69 | name = "hypocaust-2" 70 | version = "0.1.0" 71 | dependencies = [ 72 | "arrayvec", 73 | "bitflags", 74 | "buddy_system_allocator", 75 | "fdt", 76 | "memoffset", 77 | "riscv", 78 | "riscv-decode", 79 | "sbi-rt", 80 | "spin 0.9.5", 81 | "tock-registers", 82 | "xmas-elf", 83 | ] 84 | 85 | [[package]] 86 | name = "lazy_static" 87 | version = "1.4.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 90 | 91 | [[package]] 92 | name = "lock_api" 93 | version = "0.4.9" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 96 | dependencies = [ 97 | "autocfg", 98 | "scopeguard", 99 | ] 100 | 101 | [[package]] 102 | name = "log" 103 | version = "0.4.17" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 106 | dependencies = [ 107 | "cfg-if", 108 | ] 109 | 110 | [[package]] 111 | name = "memchr" 112 | version = "2.5.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 115 | 116 | [[package]] 117 | name = "memoffset" 118 | version = "0.8.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" 121 | dependencies = [ 122 | "autocfg", 123 | ] 124 | 125 | [[package]] 126 | name = "regex" 127 | version = "1.7.1" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 130 | dependencies = [ 131 | "aho-corasick", 132 | "memchr", 133 | "regex-syntax", 134 | ] 135 | 136 | [[package]] 137 | name = "regex-syntax" 138 | version = "0.6.28" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 141 | 142 | [[package]] 143 | name = "riscv" 144 | version = "0.6.0" 145 | source = "git+https://github.com/rcore-os/riscv#11d43cf7cccb3b62a3caaf3e07a1db7449588f9a" 146 | dependencies = [ 147 | "bare-metal", 148 | "bit_field", 149 | "bitflags", 150 | "log", 151 | "riscv-target", 152 | ] 153 | 154 | [[package]] 155 | name = "riscv-decode" 156 | version = "0.2.1" 157 | source = "git+https://github.com/KuangjuX/riscv-decode.git#0e346d0ad041987023caa6f7f832185ed568630b" 158 | 159 | [[package]] 160 | name = "riscv-target" 161 | version = "0.1.2" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" 164 | dependencies = [ 165 | "lazy_static", 166 | "regex", 167 | ] 168 | 169 | [[package]] 170 | name = "rustc_version" 171 | version = "0.2.3" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 174 | dependencies = [ 175 | "semver", 176 | ] 177 | 178 | [[package]] 179 | name = "sbi-rt" 180 | version = "0.0.2" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "8c113c53291db8ac141e01f43224ed488b8d6001ab66737b82e04695a43a42b7" 183 | dependencies = [ 184 | "sbi-spec", 185 | ] 186 | 187 | [[package]] 188 | name = "sbi-spec" 189 | version = "0.0.4" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "6d4027cf9bb591a9fd0fc0e283be6165c5abe96cb73e9f0e24738c227f425377" 192 | dependencies = [ 193 | "static_assertions", 194 | ] 195 | 196 | [[package]] 197 | name = "scopeguard" 198 | version = "1.1.0" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 201 | 202 | [[package]] 203 | name = "semver" 204 | version = "0.9.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 207 | dependencies = [ 208 | "semver-parser", 209 | ] 210 | 211 | [[package]] 212 | name = "semver-parser" 213 | version = "0.7.0" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 216 | 217 | [[package]] 218 | name = "spin" 219 | version = "0.7.1" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162" 222 | 223 | [[package]] 224 | name = "spin" 225 | version = "0.9.5" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" 228 | dependencies = [ 229 | "lock_api", 230 | ] 231 | 232 | [[package]] 233 | name = "static_assertions" 234 | version = "1.1.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 237 | 238 | [[package]] 239 | name = "tock-registers" 240 | version = "0.8.1" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" 243 | 244 | [[package]] 245 | name = "xmas-elf" 246 | version = "0.7.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "e74de9a366f6ab8c405fa6b371d9ac24943921fa14b3d64afcb202065c405f11" 249 | dependencies = [ 250 | "zero", 251 | ] 252 | 253 | [[package]] 254 | name = "zero" 255 | version = "0.1.3" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784" 258 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hypocaust-2" 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 | buddy_system_allocator = "0.6" 11 | bitflags = "1.2.1" 12 | xmas-elf = "0.7.0" 13 | sbi-rt = "0.0.2" 14 | spin = "0.9.4" 15 | riscv-decode = { git = "https://github.com/KuangjuX/riscv-decode.git" } 16 | fdt = { version = "0.1.5" } 17 | arrayvec = { version = "0.7.2", default-features = false } 18 | memoffset = { version = ">=0.6.5", features = ["unstable_const"] } 19 | tock-registers = { version = "0.8.1" } 20 | 21 | 22 | [features] 23 | embed_guest_kernel = [] -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | 4 | ARG WORKSPACE=/root 5 | 6 | 7 | 8 | # 0. Install general tools 9 | ARG DEBIAN_FRONTEND=noninteractive 10 | RUN apt-get update && \ 11 | apt-get install -y \ 12 | curl \ 13 | git \ 14 | python3 \ 15 | wget vim 16 | RUN apt install -y autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \ 17 | gawk build-essential bison flex texinfo gperf libtool patchutils bc \ 18 | zlib1g-dev libexpat-dev git \ 19 | libglib2.0-dev libfdt-dev libpixman-1-dev \ 20 | libncurses5-dev libncursesw5-dev 21 | 22 | 23 | # 1. Download riscv64 toolchains 24 | WORKDIR ${WORKSPACE} 25 | RUN git clone https://gitee.com/mirrors/riscv-gnu-toolchain 26 | 27 | # 1.1 Install riscv toolchain 28 | WORKDIR ${WORKSPACE}/riscv-gnu-toolchain 29 | RUN git rm qemu && \ 30 | git submodule update --init --recursive 31 | RUN ./configure --prefix=/opt/riscv64 && \ 32 | make linux -j10 33 | 34 | 35 | # Ready to go 36 | WORKDIR ${HOME} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 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-2 4 | KERNEL_BIN := target/$(TARGET)/$(MODE)/hypocaust-2.bin 5 | CPUS := 1 6 | 7 | PLATFORM ?= rt-thread 8 | 9 | BOARD := qemu 10 | 11 | GDB := gdb-multiarch 12 | 13 | FS_IMG := fs.img 14 | 15 | ROOTFS := /Users/kuangjux/software/kernel_workspace/rootfs.img 16 | 17 | ifeq ($(PLATFORM), rCore-Tutorial-v3) 18 | QEMUOPTS = --machine virt -m 3G -bios $(BOOTLOADER) -nographic 19 | QEMUOPTS +=-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) 20 | QEMUOPTS +=-drive file=guest/rCore-Tutorial-v3/fs.img,if=none,format=raw,id=x0 21 | QEMUOPTS +=-device virtio-blk-device,drive=x0 22 | QEMUOPTS +=-device virtio-gpu-device 23 | QEMUOPTS +=-device virtio-keyboard-device 24 | QEMUOPTS +=-device virtio-mouse-device 25 | QEMUOPTS +=-device virtio-net-device,netdev=net0 26 | QEMUOPTS +=-netdev user,id=net0,hostfwd=udp::6200-:2000 27 | # QEMUOPTS += -machine dumpdtb=rCore-Tutorial-v3.dtb 28 | else ifeq ($(PLATFORM), rt-thread) 29 | QEMUOPTS = --machine virt -m 3G -bios $(BOOTLOADER) -nographic 30 | QEMUOPTS +=-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) 31 | QEMUOPTS +=-drive if=none,file=guest/rtthread/sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 32 | QEMUOPTS +=-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 33 | QEMUOPTS +=-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0 34 | # QEMUOPTS += -machine dumpdtb=rtthread.dtb 35 | else ifeq ($(PLATFORM), linux) 36 | QEMUOPTS = --machine virt -m 3G -bios default -nographic 37 | QEMUOPTS +=-kernel $(KERNEL_BIN) 38 | QEMUOPTS +=-drive file=$(ROOTFS),format=raw,id=hd0 39 | QEMUOPTS +=-device virtio-blk-device,drive=hd0 40 | QEMUOPTS +=-append "root=/dev/vda rw console=ttyS0" 41 | else ifeq ($(PLATFORM), u-boot) 42 | QEMUOPTS = --machine virt -m 3G -bios $(BOOTLOADER) -nographic 43 | QEMUOPTS +=-kernel $(KERNEL_BIN) 44 | QEMUOPTS +=-drive file=$(ROOTFS),format=raw,id=hd0 45 | QEMUOPTS +=-device virtio-blk-device,drive=hd0 46 | QEMUOPTS +=-append "root=/dev/vda rw console=ttyS0" 47 | # QEMUOPTS +=-machine dumpdtb=linux.dtb 48 | # else ifeq($(PLATFORM), bare-linux) 49 | # QEMUOPTS = --machine virt -m 3G -bios $(BOOTLOADER) -nographic 50 | # QEMUOPTS +=-kernel guest.bin 51 | # QEMUOPTS +=-drive file=$(ROOTFS),format=raw,id=hd0 52 | # QEMUOPTS +=-device virtio-blk-device,drive=hd0 53 | # QEMUOPTS +=-append "root=/dev/vda rw console=ttyS0" 54 | else ifeq($(PLATFORM), hyperbench) 55 | QEMUOPTS = --machine virt -m 3G -bios $(BOOTLOADER) -nographic 56 | QEMUOPTS +=-kernel $(KERNEL_BIN) 57 | endif 58 | 59 | GUEST_KERNEL_ELF := guest.elf 60 | GUEST_KERNEL_FEATURE:=$(if $(GUEST_KERNEL_ELF), --features embed_guest_kernel, ) 61 | 62 | OBJDUMP := rust-objdump --arch-name=riscv64 63 | OBJCOPY := rust-objcopy --binary-architecture=riscv64 64 | 65 | QEMUPATH ?= ~/software/qemu/qemu-7.1.0/build/ 66 | QEMU := $(QEMUPATH)qemu-system-riscv64 67 | BOOTLOADER := bootloader/rustsbi-qemu.bin 68 | 69 | KERNEL_ENTRY_PA := 0x80200000 70 | 71 | 72 | 73 | 74 | build: $(GUEST) 75 | cp src/linker-qemu.ld src/linker.ld 76 | cargo build $(GUEST_KERNEL_FEATURE) 77 | rm src/linker.ld 78 | 79 | $(KERNEL_BIN): build 80 | $(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@ 81 | 82 | 83 | 84 | qemu: $(KERNEL_BIN) 85 | $(QEMU) $(QEMUOPTS) 86 | 87 | clean: 88 | rm $(FS_IMG) 89 | cargo clean 90 | rm $(GUEST) 91 | cd guest && cargo clean 92 | 93 | qemu-gdb: $(KERNEL_ELF) 94 | $(QEMU) $(QEMUOPTS) -S -gdb tcp::1234 95 | 96 | gdb: $(KERNEL_ELF) 97 | $(GDB) $(KERNEL_ELF) 98 | 99 | debug: $(KERNEL_BIN) 100 | @tmux new-session -d \ 101 | "$(QEMU) $(QEMUOPTS) -s -S" && \ 102 | tmux split-window -h "$(GDB) -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \ 103 | tmux -2 attach-session -d 104 | 105 | asm: 106 | riscv64-unknown-elf-objdump -d target/riscv64gc-unknown-none-elf/debug/hypocaust-2 > hyper.S 107 | riscv64-unknown-elf-objdump -d guest.elf > guest.S 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hypocaust-2 2 | ## Overview 3 | Hypocaust-2 is an experimental type-1 hypervisor with H extension run on RISC-V machine. It depends on the RISC -V H extension, which currently runs on QEMU 7.1.0 or above. It is the successor of the [hypocaust](https://github.com/KuangjuX/hypocaust) project. 4 | 5 | 6 | My plan is to build a high-performance riscv64 hypervisor that physically maps the cpu cores, so there is no need to schedule guests in the hypervisor. In addition, the passthrough method for IO devices has achieved good performance. 7 | 8 | The purpose of this project is to run on bare metal or embedded devices, but it is not ruled out that kvm technology will be used and run on linux in the future. 9 | 10 | [![asciicast](https://asciinema.org/a/564050.png)](https://asciinema.org/a/564050) 11 | 12 | 13 | 14 | ## Environment 15 | - QEMU 7.1.0 16 | - RustSBI-QEMU Prereleased 2023-02-01 17 | - Rust 1.66.0 18 | 19 | 20 | ## Examples 21 | 22 | ### rCore-Tutorial-v3 23 | ``` 24 | ./srcipts/rCore-Tutorial-v3.sh && make qemu PLATFORM=rCore-Tutorial-v3 25 | ``` 26 | 27 | ### RT-Thread 28 | ``` 29 | ./srcipts/rt-thread.sh && make qemu PLATFORM=rt-thread 30 | ``` 31 | 32 | ### Linux 33 | **Toolchains:** 34 | ``` 35 | $ sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \ 36 | gawk build-essential bison flex texinfo gperf libtool patchutils bc \ 37 | zlib1g-dev libexpat-dev git \ 38 | libglib2.0-dev libfdt-dev libpixman-1-dev \ 39 | libncurses5-dev libncursesw5-dev 40 | 41 | # install riscv linux toolchain 42 | $ git clone https://gitee.com/mirrors/riscv-gnu-toolchain --depth=1 43 | $ cd riscv-gnu-toolchain 44 | $ git rm qemu 45 | $ git submodule update --init --recursive 46 | $ ./configure --prefix=/opt/riscv64 --with-arch=rv64imac --with-abi=lp64 47 | $ sudo make linux -j8 48 | $ export PATH=$PATH:/opt/riscv64/bin 49 | ``` 50 | 51 | **Build Linux:** 52 | ``` 53 | $ git clone https://github.com/torvalds/linux -b v6.2 54 | $ cd linux 55 | $ git am ${HYPOCAUST_2_DIR}/guest/linux/patches/*.patch 56 | $ make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig 57 | $ make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- menuconfig 58 | $ make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- all -j8 59 | ``` 60 | 61 | **Run bare Linux on qemu:** 62 | ``` 63 | $ qemu-system-riscv64 -M virt -m 256M -nographic -bios $(BOOTLOADER)/rustsbi-qemu.bin -kernel $(linux)/arch/riscv/boot/Image 64 | ``` 65 | 66 | **Docker Command(MacOS/Windows):** 67 | ``` 68 | # run docker container, mount workspace in docker 69 | 70 | docker run -itd --name riscv-env --privileged -v {WORKSPACE}:/workspace riscv-gnu-toolchain /bin/bash 71 | 72 | # run docker 73 | docker exec -it riscv-env /bin/bash 74 | ``` 75 | 76 | **Make rootfs:** 77 | ``` 78 | git clone https://gitee.com/mirrors/busyboxsource.git 79 | cd busyboxsource 80 | 81 | # Select: Settings -> Build Options -> Build static binary 82 | CROSS_COMPILE=riscv64-unknown-linux-gnu- make menuconfig 83 | 84 | ## Build && Install 85 | CROSS_COMPILE=riscv64-unknown-linux-gnu- make -j10 86 | CROSS_COMPILE=riscv64-unknown-linux-gnu- make install 87 | 88 | # Make minimal root file system 89 | cd ../ 90 | qemu-img create rootfs.img 1g 91 | mkfs.ext4 rootfs.img 92 | 93 | # mount file system && copy busybox 94 | mkdir rootfs 95 | mount -o loop rootfs.img rootfs 96 | cd rootfs 97 | cp -r ../busyboxsource/_install/* . 98 | mkdir proc dev tec etc/init.d 99 | 100 | cd etc/init.d/ 101 | touch rcS 102 | vim rcS 103 | 104 | ##### 105 | #!/bin/sh 106 | mount -t proc none /proc 107 | mount -t sysfs none /sys 108 | /sbin/mdev -s 109 | ##### 110 | 111 | chmod +x rcS 112 | 113 | umount rootfs 114 | 115 | qemu-system-riscv64 -M virt -m 256M -nographic -bios {BOOTLOADR} -kernel {KERNEL_ELF} -drive file=rootfs.img,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -append "root=/dev/vda rw console=ttyS0" 116 | 117 | ``` 118 | 119 | ## RoadMap 120 | - [x] Load guest elf image. 121 | - [x] Jump guest loaded to a VM while enabling guest physical address translation by `hgatp`. 122 | - [x] Run a tiny kernel that does not require any external hardware like disk devices. 123 | - [x] Handle read/write requests for CSRs from a guest 124 | - [x] Handle SBI calls(currently only `console_putchar`, `console_getchar` and `set_timer` and `base` related) 125 | - [x] Guest enable paging & setup 2-stage page table translation. 126 | - [x] Jump VU mode and run user applications 127 | - [x] Timers 128 | - [x] Passthrough virtio block device 129 | - [x] Configure hypervisor and guest memory addresses and peripheral space mapping by device tree. 130 | - [x] Emulate PLIC && Forward interrupts 131 | - [x] Expose and/or emulate peripherals 132 | - [x] run rCore-Tutorial-v3 133 | - [x] run RT-Thread 134 | - [x] run Linux 135 | - [ ] IOMMU enabled 136 | - [ ] AIA support 137 | - [ ] multicore supported 138 | - [ ] multiguest supported 139 | 140 | ## Features 141 | ### Doamin Isolation 142 | - [ ] VCPU and Host Interrupt Affinity 143 | - [ ] Spatial and Temporal Memory Isolation 144 | 145 | ### Device Virtualization 146 | - [ ] Pass-through device support(enable IOMMU) 147 | - [ ] Block device virtualization 148 | - [ ] Network device virtualization 149 | - [ ] Input device virtualization 150 | - [ ] Display device virtualization 151 | 152 | ## Configuration 153 | ### Device Tree 154 | 155 | Two types of device tree(DT): 156 | 1. **Host DT:** 157 | - Device tree which describes underlying host HW to hypocaust-2 158 | - Used by hypocaust-2 at boot-time 159 | 160 | 2. **Guest DT:** 161 | - Device tree which dscribes Guest virtual HW to hypocaust-2 162 | - Used by hypocaust-2 to create Guest 163 | 164 | ## Tips 165 | - When the hypervisor is initialized, it is necessary to write the `hcounteren` register to all 1, because it is possible to read the `time` register in VU mode or VS mode.(refs: The counter-enable register `hcounteren` is a 32-bit register that controls the availability of the hardware performance monitoring counters to the guest virtual machine. 166 | When the CY, TM, IR, or HPMn bit in the hcounteren register is clear, attempts to read the 167 | cycle, time, instret, or hpmcountern register while V=1 will cause a virtual instruction exception 168 | if the same bit in mcounteren is 1. When one of these bits is set, access to the corresponding register 169 | is permitted when V=1, unless prevented for some other reason. In VU-mode, a counter is not 170 | readable unless the applicable bits are set in both `hcounteren` and `scounteren`. 171 | `hcounteren` must be implemented. However, any of the bits may be read-only zero, indicating 172 | reads to the corresponding counter will cause an exception when V=1. Hence, they are effectively 173 | WARL fields.) 174 | - When the hypervisor initializes the memory for the guest, it needs to set all the mapping flags of the guest memory to RWX, although it needs to be modified in the end. Otherwise, when the guest allocates memory for the application, it will not be executable, causing `InstructionGuestPageFault`. 175 | - The hypervisor currently does not support IOMMU, so it is necessary to have all its memory configured with identify mapping when guest wishes to use a DMA device. 176 | 177 | ## Design Docs 178 | - [Trap Design](docs/trap.md) 179 | - [Guest Page Table Design](docs/guest_page_table.md) 180 | 181 | ## References 182 | - [hypocaust](https://github.com/KuangjuX/hypocaust) 183 | - [rustyvisor](https://github.com/stemnic/rustyvisor) 184 | - [bao-hypervisor](https://github.com/bao-project/bao-hypervisor) 185 | - [salus](https://github.com/rivosinc/salus) 186 | 187 | ## Relative Links 188 | - [QEMU 运行 RISC-V linux 总结](http://www.icfgblog.com/index.php/software/324.html) 189 | - [QEMU 启动方式分析(1): QEMU 及 RISC-V 启动流程简介](https://gitee.com/YJMSTR/riscv-linux/blob/master/articles/20220816-introduction-to-qemu-and-riscv-upstream-boot-flow.md#https://gitee.com/link?target=https%3A%2F%2Ftinylab.org%2Friscv-uefi-part1%2F) 190 | - [QEMU 启动方式分析(2): QEMU virt 平台下通过 OpenSBI + U-Boot 引导 RISC-V 64 Linux](https://gitee.com/YJMSTR/riscv-linux/blob/master/articles/20220823-boot-riscv-linux-kernel-with-uboot-on-qemu-virt-machine.md) 191 | 192 | - [Virtual Memory Layout on RISC-V Linux](https://www.kernel.org/doc/html/latest/riscv/vm-layout.html) -------------------------------------------------------------------------------- /bootloader/fw_payload.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/bootloader/fw_payload.bin -------------------------------------------------------------------------------- /bootloader/rustsbi-qemu.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/bootloader/rustsbi-qemu.bin -------------------------------------------------------------------------------- /docs/boot.md: -------------------------------------------------------------------------------- 1 | # Hypocaust-2 启动流程 -------------------------------------------------------------------------------- /docs/guest_page_table.md: -------------------------------------------------------------------------------- 1 | # Guest Page Table Design 2 | 3 | ## Theory 4 | Guest 物理地址翻译和 supervisor 物理地址翻译有所不同, Guest 物理地址翻译被 `hgatp` 寄存器所控制. 5 | 6 | 当 `hgatp` 为 0 时,和 supervisor 物理地址翻译相同,此时对于 guest 物理内存没有物理内存保护. 当使用 **Sv32x4**,**Sv39x4** 等等的时候, **G-stage** 地址转换通常是基于页面虚拟地址转换方案的变体.并且地址宽度被拓宽 2 位(34,41,50,59).为了填充额外的两位,根页表应当是 16 KiB 大小.为了匹配更大的大小,根页表也必须 16 KiB 对齐. 7 | 8 | ![](image/G-state-Sv39.jpg) 9 | 10 | 上图是 Sv39 的物理地址结构,除了根页表是 16KiB 之外,其他都相同,并且 Address[63: 41] 必须是 0,否则将会发生 guest page fault. 11 | 12 | Sv32x4,Sv39x4,Sv48x4,Sv57x4 的 guest 物理地址转换与 supervisor 的物理地址转换相同,除了: 13 | - `hgatp` 替代通常的 `satp` 14 | - 启用翻译的特权及模式必须是 VS-mode 或者 VU-mode 15 | - 检查 U 位,始终取当前特权模式为 U mode 16 | - 使用 **Guest Page Fault** 代替常规的 **Page Fault** 17 | -------------------------------------------------------------------------------- /docs/image/G-state-Sv39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/docs/image/G-state-Sv39.jpg -------------------------------------------------------------------------------- /docs/trap.md: -------------------------------------------------------------------------------- 1 | # Trap Design 2 | 3 | ## 由 hypervisor 切换到 guest 4 | - 切换 `hgatp` 寄存器并且 `hfence.gvma` 用来开启两阶段页表翻译 5 | - 设置 `hsatus` 寄存器的 `SPV` 位为 1 表明返回 VS mode 6 | - 设置 `sstatus` 寄存器的 `SPP` 位为 1 表明返回 S mode 7 | - 设置 `sepc` 用来标志返回入口 8 | - 切换 `sp` 和 `sscratch`,使其全部指向 Trap Context 9 | - 恢复上下文 10 | - sret 返回 guest 执行 11 | 12 | ## 由 guest 切换到 hypervisor 13 | - 首先交换 `sscratch` 和 `sp` 寄存器的值,此时 `sscratch` 存储的是 guest 栈指针地址,`sp` 存储的是 trap context 地址 14 | - 保存上下文到 trap context 内存区域 15 | - 将 `sp` 寄存器切换到内核栈并跳转到 trap 处理函数(在 H 扩展情况下不需要切换页表) 16 | 17 | 由于此时在 guest 和 hypervisor 之间不再需要切换页表了,有一些事情需要注意: 18 | - hypervisor 需要单独映射 Trap Context Page,而 guest 则不需要进行映射 19 | - guest 内核栈的虚拟地址与 Trap Context page 的地址不能重合,否则会破坏 Trap Context 的结构 -------------------------------------------------------------------------------- /guest/hyperbench/hyperbench: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/hyperbench/hyperbench -------------------------------------------------------------------------------- /guest/hyperbench/hyperbench.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/hyperbench/hyperbench.dtb -------------------------------------------------------------------------------- /guest/hyperbench/hyperbench.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | #address-cells = <0x02>; 5 | #size-cells = <0x02>; 6 | compatible = "riscv-virtio"; 7 | model = "riscv-virtio,qemu"; 8 | 9 | fw-cfg@10100000 { 10 | dma-coherent; 11 | reg = <0x00 0x10100000 0x00 0x18>; 12 | compatible = "qemu,fw-cfg-mmio"; 13 | }; 14 | 15 | flash@20000000 { 16 | bank-width = <0x04>; 17 | reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>; 18 | compatible = "cfi-flash"; 19 | }; 20 | 21 | chosen { 22 | stdout-path = "/soc/uart@10000000"; 23 | }; 24 | 25 | platform@4000000 { 26 | interrupt-parent = <0x03>; 27 | ranges = <0x00 0x00 0x4000000 0x2000000>; 28 | #address-cells = <0x01>; 29 | #size-cells = <0x01>; 30 | compatible = "qemu,platform\0simple-bus"; 31 | }; 32 | 33 | memory@90000000 { 34 | device_type = "memory"; 35 | reg = <0x00 0x90200000 0x00 0x8000000>; 36 | }; 37 | 38 | cpus { 39 | #address-cells = <0x01>; 40 | #size-cells = <0x00>; 41 | timebase-frequency = <0x989680>; 42 | 43 | cpu@0 { 44 | phandle = <0x01>; 45 | device_type = "cpu"; 46 | reg = <0x00>; 47 | status = "okay"; 48 | compatible = "riscv"; 49 | riscv,isa = "rv64imafdch_zicsr_zifencei_zba_zbb_zbc_zbs"; 50 | mmu-type = "riscv,sv48"; 51 | 52 | interrupt-controller { 53 | #interrupt-cells = <0x01>; 54 | interrupt-controller; 55 | compatible = "riscv,cpu-intc"; 56 | phandle = <0x02>; 57 | }; 58 | }; 59 | 60 | cpu-map { 61 | 62 | cluster0 { 63 | 64 | core0 { 65 | cpu = <0x01>; 66 | }; 67 | }; 68 | }; 69 | }; 70 | 71 | soc { 72 | #address-cells = <0x02>; 73 | #size-cells = <0x02>; 74 | compatible = "simple-bus"; 75 | ranges; 76 | 77 | rtc@101000 { 78 | interrupts = <0x0b>; 79 | interrupt-parent = <0x03>; 80 | reg = <0x00 0x101000 0x00 0x1000>; 81 | compatible = "google,goldfish-rtc"; 82 | }; 83 | 84 | uart@10000000 { 85 | interrupts = <0x0a>; 86 | interrupt-parent = <0x03>; 87 | clock-frequency = "\08@"; 88 | reg = <0x00 0x10000000 0x00 0x100>; 89 | compatible = "ns16550a"; 90 | }; 91 | 92 | poweroff { 93 | value = <0x5555>; 94 | offset = <0x00>; 95 | regmap = <0x04>; 96 | compatible = "syscon-poweroff"; 97 | }; 98 | 99 | reboot { 100 | value = <0x7777>; 101 | offset = <0x00>; 102 | regmap = <0x04>; 103 | compatible = "syscon-reboot"; 104 | }; 105 | 106 | test@100000 { 107 | phandle = <0x04>; 108 | reg = <0x00 0x100000 0x00 0x1000>; 109 | compatible = "sifive,test1\0sifive,test0\0syscon"; 110 | }; 111 | 112 | pci@30000000 { 113 | interrupt-map-mask = <0x1800 0x00 0x00 0x07>; 114 | interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>; 115 | ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; 116 | reg = <0x00 0x30000000 0x00 0x10000000>; 117 | dma-coherent; 118 | bus-range = <0x00 0xff>; 119 | linux,pci-domain = <0x00>; 120 | device_type = "pci"; 121 | compatible = "pci-host-ecam-generic"; 122 | #size-cells = <0x02>; 123 | #interrupt-cells = <0x01>; 124 | #address-cells = <0x03>; 125 | }; 126 | 127 | virtio_mmio@10008000 { 128 | interrupts = <0x08>; 129 | interrupt-parent = <0x03>; 130 | reg = <0x00 0x10008000 0x00 0x1000>; 131 | compatible = "virtio,mmio"; 132 | }; 133 | 134 | virtio_mmio@10007000 { 135 | interrupts = <0x07>; 136 | interrupt-parent = <0x03>; 137 | reg = <0x00 0x10007000 0x00 0x1000>; 138 | compatible = "virtio,mmio"; 139 | }; 140 | 141 | virtio_mmio@10006000 { 142 | interrupts = <0x06>; 143 | interrupt-parent = <0x03>; 144 | reg = <0x00 0x10006000 0x00 0x1000>; 145 | compatible = "virtio,mmio"; 146 | }; 147 | 148 | virtio_mmio@10005000 { 149 | interrupts = <0x05>; 150 | interrupt-parent = <0x03>; 151 | reg = <0x00 0x10005000 0x00 0x1000>; 152 | compatible = "virtio,mmio"; 153 | }; 154 | 155 | virtio_mmio@10004000 { 156 | interrupts = <0x04>; 157 | interrupt-parent = <0x03>; 158 | reg = <0x00 0x10004000 0x00 0x1000>; 159 | compatible = "virtio,mmio"; 160 | }; 161 | 162 | virtio_mmio@10003000 { 163 | interrupts = <0x03>; 164 | interrupt-parent = <0x03>; 165 | reg = <0x00 0x10003000 0x00 0x1000>; 166 | compatible = "virtio,mmio"; 167 | }; 168 | 169 | virtio_mmio@10002000 { 170 | interrupts = <0x02>; 171 | interrupt-parent = <0x03>; 172 | reg = <0x00 0x10002000 0x00 0x1000>; 173 | compatible = "virtio,mmio"; 174 | }; 175 | 176 | virtio_mmio@10001000 { 177 | interrupts = <0x01>; 178 | interrupt-parent = <0x03>; 179 | reg = <0x00 0x10001000 0x00 0x1000>; 180 | compatible = "virtio,mmio"; 181 | }; 182 | 183 | plic@c000000 { 184 | phandle = <0x03>; 185 | riscv,ndev = <0x60>; 186 | reg = <0x00 0xc000000 0x00 0x600000>; 187 | interrupts-extended = <0x02 0x0b 0x02 0x09>; 188 | interrupt-controller; 189 | compatible = "sifive,plic-1.0.0\0riscv,plic0"; 190 | #interrupt-cells = <0x01>; 191 | }; 192 | 193 | clint@2000000 { 194 | interrupts-extended = <0x02 0x03 0x02 0x07>; 195 | reg = <0x00 0x2000000 0x00 0x10000>; 196 | compatible = "sifive,clint0\0riscv,clint0"; 197 | }; 198 | }; 199 | }; -------------------------------------------------------------------------------- /guest/linux/linux.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/linux/linux.bin -------------------------------------------------------------------------------- /guest/linux/linux.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | #address-cells = <0x02>; 5 | #size-cells = <0x02>; 6 | compatible = "riscv-virtio"; 7 | model = "riscv-virtio,qemu"; 8 | 9 | fw-cfg@10100000 { 10 | dma-coherent; 11 | reg = <0x00 0x10100000 0x00 0x18>; 12 | compatible = "qemu,fw-cfg-mmio"; 13 | }; 14 | 15 | flash@20000000 { 16 | bank-width = <0x04>; 17 | reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>; 18 | compatible = "cfi-flash"; 19 | }; 20 | 21 | chosen { 22 | bootargs = "root=/dev/vda rw console=ttyS0"; 23 | stdout-path = "/soc/uart@10000000"; 24 | }; 25 | 26 | platform@4000000 { 27 | interrupt-parent = <0x03>; 28 | ranges = <0x00 0x00 0x4000000 0x2000000>; 29 | #address-cells = <0x01>; 30 | #size-cells = <0x01>; 31 | compatible = "qemu,platform\0simple-bus"; 32 | }; 33 | 34 | memory@90000000 { 35 | device_type = "memory"; 36 | reg = <0x00 0x90000000 0x00 0x8000000>; 37 | }; 38 | 39 | cpus { 40 | #address-cells = <0x01>; 41 | #size-cells = <0x00>; 42 | timebase-frequency = <0x989680>; 43 | 44 | cpu@0 { 45 | phandle = <0x01>; 46 | device_type = "cpu"; 47 | reg = <0x00>; 48 | status = "okay"; 49 | compatible = "riscv"; 50 | riscv,isa = "rv64ima"; 51 | mmu-type = "riscv,sv39"; 52 | 53 | interrupt-controller { 54 | #interrupt-cells = <0x01>; 55 | interrupt-controller; 56 | compatible = "riscv,cpu-intc"; 57 | phandle = <0x02>; 58 | }; 59 | }; 60 | 61 | cpu-map { 62 | 63 | cluster0 { 64 | 65 | core0 { 66 | cpu = <0x01>; 67 | }; 68 | }; 69 | }; 70 | }; 71 | 72 | soc { 73 | #address-cells = <0x02>; 74 | #size-cells = <0x02>; 75 | compatible = "simple-bus"; 76 | ranges; 77 | 78 | rtc@101000 { 79 | interrupts = <0x0b>; 80 | interrupt-parent = <0x03>; 81 | reg = <0x00 0x101000 0x00 0x1000>; 82 | compatible = "google,goldfish-rtc"; 83 | }; 84 | 85 | uart@10000000 { 86 | interrupts = <0x0a>; 87 | interrupt-parent = <0x03>; 88 | clock-frequency = "\08@"; 89 | reg = <0x00 0x10000000 0x00 0x100>; 90 | compatible = "ns16550a"; 91 | }; 92 | 93 | poweroff { 94 | value = <0x5555>; 95 | offset = <0x00>; 96 | regmap = <0x04>; 97 | compatible = "syscon-poweroff"; 98 | }; 99 | 100 | reboot { 101 | value = <0x7777>; 102 | offset = <0x00>; 103 | regmap = <0x04>; 104 | compatible = "syscon-reboot"; 105 | }; 106 | 107 | test@100000 { 108 | phandle = <0x04>; 109 | reg = <0x00 0x100000 0x00 0x1000>; 110 | compatible = "sifive,test1\0sifive,test0\0syscon"; 111 | }; 112 | 113 | pci@30000000 { 114 | interrupt-map-mask = <0x1800 0x00 0x00 0x07>; 115 | interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>; 116 | ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; 117 | reg = <0x00 0x30000000 0x00 0x10000000>; 118 | dma-coherent; 119 | bus-range = <0x00 0xff>; 120 | linux,pci-domain = <0x00>; 121 | device_type = "pci"; 122 | compatible = "pci-host-ecam-generic"; 123 | #size-cells = <0x02>; 124 | #interrupt-cells = <0x01>; 125 | #address-cells = <0x03>; 126 | }; 127 | 128 | virtio_mmio@10008000 { 129 | interrupts = <0x08>; 130 | interrupt-parent = <0x03>; 131 | reg = <0x00 0x10008000 0x00 0x1000>; 132 | compatible = "virtio,mmio"; 133 | }; 134 | 135 | virtio_mmio@10007000 { 136 | interrupts = <0x07>; 137 | interrupt-parent = <0x03>; 138 | reg = <0x00 0x10007000 0x00 0x1000>; 139 | compatible = "virtio,mmio"; 140 | }; 141 | 142 | virtio_mmio@10006000 { 143 | interrupts = <0x06>; 144 | interrupt-parent = <0x03>; 145 | reg = <0x00 0x10006000 0x00 0x1000>; 146 | compatible = "virtio,mmio"; 147 | }; 148 | 149 | virtio_mmio@10005000 { 150 | interrupts = <0x05>; 151 | interrupt-parent = <0x03>; 152 | reg = <0x00 0x10005000 0x00 0x1000>; 153 | compatible = "virtio,mmio"; 154 | }; 155 | 156 | virtio_mmio@10004000 { 157 | interrupts = <0x04>; 158 | interrupt-parent = <0x03>; 159 | reg = <0x00 0x10004000 0x00 0x1000>; 160 | compatible = "virtio,mmio"; 161 | }; 162 | 163 | virtio_mmio@10003000 { 164 | interrupts = <0x03>; 165 | interrupt-parent = <0x03>; 166 | reg = <0x00 0x10003000 0x00 0x1000>; 167 | compatible = "virtio,mmio"; 168 | }; 169 | 170 | virtio_mmio@10002000 { 171 | interrupts = <0x02>; 172 | interrupt-parent = <0x03>; 173 | reg = <0x00 0x10002000 0x00 0x1000>; 174 | compatible = "virtio,mmio"; 175 | }; 176 | 177 | virtio_mmio@10001000 { 178 | interrupts = <0x01>; 179 | interrupt-parent = <0x03>; 180 | reg = <0x00 0x10001000 0x00 0x1000>; 181 | compatible = "virtio,mmio"; 182 | }; 183 | 184 | plic@c000000 { 185 | phandle = <0x03>; 186 | riscv,ndev = <0x60>; 187 | reg = <0x00 0xc000000 0x00 0x600000>; 188 | interrupts-extended = <0x02 0x0b 0x02 0x09>; 189 | interrupt-controller; 190 | compatible = "sifive,plic-1.0.0\0riscv,plic0"; 191 | #interrupt-cells = <0x01>; 192 | }; 193 | 194 | clint@2000000 { 195 | interrupts-extended = <0x02 0x03 0x02 0x07>; 196 | reg = <0x00 0x2000000 0x00 0x10000>; 197 | compatible = "sifive,clint0\0riscv,clint0"; 198 | }; 199 | }; 200 | }; 201 | -------------------------------------------------------------------------------- /guest/linux/linux.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/linux/linux.elf -------------------------------------------------------------------------------- /guest/linux/patches/0001-hypocaust-2-riscv-mm-Disable-l4-and-l5-page-table.patch: -------------------------------------------------------------------------------- 1 | From a9800728f4d46805f403e8f6253c4951ff94ad01 Mon Sep 17 00:00:00 2001 2 | From: Tan En De 3 | Date: Thu, 30 Nov 2023 11:33:19 +0800 4 | Subject: [hypocaust-2] riscv: mm: Disable l4 and l5 page table 5 | 6 | Currently, hypocaust-2 does not support l4 and l5 page table. 7 | Without this commit, the following error will be observed: 8 | ``` 9 | [Error] inst addr: 0xffffffff804538d0 10 | [hypervisor] Panicked at src/guest/vmexit.rs:144 err: TranslationError 11 | ``` 12 | 13 | Signed-off-by: Tan En De 14 | --- 15 | arch/riscv/mm/init.c | 20 +++++++++++--------- 16 | 1 file changed, 11 insertions(+), 9 deletions(-) 17 | 18 | diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c 19 | index 478d6763a01a..edcafd96c156 100644 20 | --- a/arch/riscv/mm/init.c 21 | +++ b/arch/riscv/mm/init.c 22 | @@ -785,15 +785,17 @@ static __init void set_satp_mode(void) 23 | hw_satp = csr_swap(CSR_SATP, 0ULL); 24 | local_flush_tlb_all(); 25 | 26 | - if (hw_satp != identity_satp) { 27 | - if (!check_l4) { 28 | - disable_pgtable_l5(); 29 | - check_l4 = true; 30 | - memset(early_pg_dir, 0, PAGE_SIZE); 31 | - goto retry; 32 | - } 33 | - disable_pgtable_l4(); 34 | - } 35 | + /* if (hw_satp != identity_satp) { */ 36 | + /* if (!check_l4) { */ 37 | + /* disable_pgtable_l5(); */ 38 | + /* check_l4 = true; */ 39 | + /* memset(early_pg_dir, 0, PAGE_SIZE); */ 40 | + /* goto retry; */ 41 | + /* } */ 42 | + /* disable_pgtable_l4(); */ 43 | + /* } */ 44 | + disable_pgtable_l5(); 45 | + disable_pgtable_l4(); 46 | 47 | memset(early_pg_dir, 0, PAGE_SIZE); 48 | memset(early_p4d, 0, PAGE_SIZE); 49 | -- 50 | 2.34.1 51 | 52 | -------------------------------------------------------------------------------- /guest/linux/patches/0002-hypocaust-2-riscv-sbi-Disable-remote-fence.patch: -------------------------------------------------------------------------------- 1 | From 3ae263a264752ffc937cd176812bf0c04e0abeae Mon Sep 17 00:00:00 2001 2 | From: Tan En De 3 | Date: Thu, 30 Nov 2023 12:20:56 +0800 4 | Subject: [hypocaust-2] riscv: sbi: Disable remote fence 5 | 6 | Currently, hypocaust-2 does not support remote fence. 7 | Without this commit, the following error will happen: 8 | ``` 9 | [hypervisor] Panicked at src/guest/sbi.rs:56 Unsupported SBI call id 0x52464e43 10 | ``` 11 | 12 | Signed-off-by: Tan En De 13 | --- 14 | arch/riscv/kernel/sbi.c | 8 ++++---- 15 | 1 file changed, 4 insertions(+), 4 deletions(-) 16 | 17 | diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c 18 | index 5c87db8fdff2..c4a8fe7b5e69 100644 19 | --- a/arch/riscv/kernel/sbi.c 20 | +++ b/arch/riscv/kernel/sbi.c 21 | @@ -228,8 +228,8 @@ static int __sbi_rfence_v01(int fid, const struct cpumask *cpu_mask, 22 | unsigned long start, unsigned long size, 23 | unsigned long arg4, unsigned long arg5) 24 | { 25 | - pr_warn("remote fence extension is not available in SBI v%lu.%lu\n", 26 | - sbi_major_version(), sbi_minor_version()); 27 | + /* pr_warn("remote fence extension is not available in SBI v%lu.%lu\n", */ 28 | + /* sbi_major_version(), sbi_minor_version()); */ 29 | 30 | return 0; 31 | } 32 | @@ -678,8 +678,8 @@ void __init sbi_init(void) 33 | __sbi_send_ipi = __sbi_send_ipi_v01; 34 | } 35 | if (sbi_probe_extension(SBI_EXT_RFENCE) > 0) { 36 | - __sbi_rfence = __sbi_rfence_v02; 37 | - pr_info("SBI RFENCE extension detected\n"); 38 | + __sbi_rfence = __sbi_rfence_v01; 39 | + /* pr_info("SBI RFENCE extension detected\n"); */ 40 | } else { 41 | __sbi_rfence = __sbi_rfence_v01; 42 | } 43 | -- 44 | 2.34.1 45 | 46 | -------------------------------------------------------------------------------- /guest/linux/patches/0003-hypocaust-2-riscv-defconfig-Disable-perf-events.patch: -------------------------------------------------------------------------------- 1 | From 1824297bc60a1b99b146fc6a98755d0b17b2713d Mon Sep 17 00:00:00 2001 2 | From: Tan En De 3 | Date: Thu, 30 Nov 2023 12:26:29 +0800 4 | Subject: [hypocaust-2] riscv: defconfig: Disable perf events 5 | 6 | Currently, hypocaust-2 does not support PMU-related SBI call. 7 | Without this commit, the following error will be observed: 8 | ``` 9 | [hypervisor] Panicked at src/guest/sbi.rs:56 Unsupported SBI call id 0x504d55 10 | ``` 11 | 12 | Signed-off-by: Tan En De 13 | --- 14 | arch/riscv/configs/defconfig | 1 + 15 | 1 file changed, 1 insertion(+) 16 | 17 | diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig 18 | index 128dcf4c0814..491494ea7187 100644 19 | --- a/arch/riscv/configs/defconfig 20 | +++ b/arch/riscv/configs/defconfig 21 | @@ -218,3 +218,4 @@ CONFIG_RCU_EQS_DEBUG=y 22 | # CONFIG_FTRACE is not set 23 | # CONFIG_RUNTIME_TESTING_MENU is not set 24 | CONFIG_MEMTEST=y 25 | +CONFIG_PERF_EVENTS=n 26 | -- 27 | 2.34.1 28 | 29 | -------------------------------------------------------------------------------- /guest/rCore-Tutorial-v3/.gitignore: -------------------------------------------------------------------------------- 1 | *.dtb -------------------------------------------------------------------------------- /guest/rCore-Tutorial-v3/fs.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/rCore-Tutorial-v3/fs.img -------------------------------------------------------------------------------- /guest/rCore-Tutorial-v3/rCore-Tutorial-v3.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/rCore-Tutorial-v3/rCore-Tutorial-v3.bin -------------------------------------------------------------------------------- /guest/rCore-Tutorial-v3/rCore-Tutorial-v3.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | #address-cells = <0x02>; 5 | #size-cells = <0x02>; 6 | compatible = "riscv-virtio"; 7 | model = "riscv-virtio,qemu"; 8 | 9 | fw-cfg@10100000 { 10 | dma-coherent; 11 | reg = <0x00 0x10100000 0x00 0x18>; 12 | compatible = "qemu,fw-cfg-mmio"; 13 | }; 14 | 15 | flash@20000000 { 16 | bank-width = <0x04>; 17 | reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>; 18 | compatible = "cfi-flash"; 19 | }; 20 | 21 | chosen { 22 | stdout-path = "/soc/uart@10000000"; 23 | }; 24 | 25 | platform@4000000 { 26 | interrupt-parent = <0x03>; 27 | ranges = <0x00 0x00 0x4000000 0x2000000>; 28 | #address-cells = <0x01>; 29 | #size-cells = <0x01>; 30 | compatible = "qemu,platform\0simple-bus"; 31 | }; 32 | 33 | memory@90000000 { 34 | device_type = "memory"; 35 | reg = <0x00 0x90200000 0x00 0x8000000>; 36 | }; 37 | 38 | cpus { 39 | #address-cells = <0x01>; 40 | #size-cells = <0x00>; 41 | timebase-frequency = <0x989680>; 42 | 43 | cpu@0 { 44 | phandle = <0x01>; 45 | device_type = "cpu"; 46 | reg = <0x00>; 47 | status = "okay"; 48 | compatible = "riscv"; 49 | riscv,isa = "rv64imafdch_zicsr_zifencei_zba_zbb_zbc_zbs"; 50 | mmu-type = "riscv,sv48"; 51 | 52 | interrupt-controller { 53 | #interrupt-cells = <0x01>; 54 | interrupt-controller; 55 | compatible = "riscv,cpu-intc"; 56 | phandle = <0x02>; 57 | }; 58 | }; 59 | 60 | cpu-map { 61 | 62 | cluster0 { 63 | 64 | core0 { 65 | cpu = <0x01>; 66 | }; 67 | }; 68 | }; 69 | }; 70 | 71 | soc { 72 | #address-cells = <0x02>; 73 | #size-cells = <0x02>; 74 | compatible = "simple-bus"; 75 | ranges; 76 | 77 | rtc@101000 { 78 | interrupts = <0x0b>; 79 | interrupt-parent = <0x03>; 80 | reg = <0x00 0x101000 0x00 0x1000>; 81 | compatible = "google,goldfish-rtc"; 82 | }; 83 | 84 | uart@10000000 { 85 | interrupts = <0x0a>; 86 | interrupt-parent = <0x03>; 87 | clock-frequency = "\08@"; 88 | reg = <0x00 0x10000000 0x00 0x100>; 89 | compatible = "ns16550a"; 90 | }; 91 | 92 | poweroff { 93 | value = <0x5555>; 94 | offset = <0x00>; 95 | regmap = <0x04>; 96 | compatible = "syscon-poweroff"; 97 | }; 98 | 99 | reboot { 100 | value = <0x7777>; 101 | offset = <0x00>; 102 | regmap = <0x04>; 103 | compatible = "syscon-reboot"; 104 | }; 105 | 106 | test@100000 { 107 | phandle = <0x04>; 108 | reg = <0x00 0x100000 0x00 0x1000>; 109 | compatible = "sifive,test1\0sifive,test0\0syscon"; 110 | }; 111 | 112 | pci@30000000 { 113 | interrupt-map-mask = <0x1800 0x00 0x00 0x07>; 114 | interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>; 115 | ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; 116 | reg = <0x00 0x30000000 0x00 0x10000000>; 117 | dma-coherent; 118 | bus-range = <0x00 0xff>; 119 | linux,pci-domain = <0x00>; 120 | device_type = "pci"; 121 | compatible = "pci-host-ecam-generic"; 122 | #size-cells = <0x02>; 123 | #interrupt-cells = <0x01>; 124 | #address-cells = <0x03>; 125 | }; 126 | 127 | virtio_mmio@10008000 { 128 | interrupts = <0x08>; 129 | interrupt-parent = <0x03>; 130 | reg = <0x00 0x10008000 0x00 0x1000>; 131 | compatible = "virtio,mmio"; 132 | }; 133 | 134 | virtio_mmio@10007000 { 135 | interrupts = <0x07>; 136 | interrupt-parent = <0x03>; 137 | reg = <0x00 0x10007000 0x00 0x1000>; 138 | compatible = "virtio,mmio"; 139 | }; 140 | 141 | virtio_mmio@10006000 { 142 | interrupts = <0x06>; 143 | interrupt-parent = <0x03>; 144 | reg = <0x00 0x10006000 0x00 0x1000>; 145 | compatible = "virtio,mmio"; 146 | }; 147 | 148 | virtio_mmio@10005000 { 149 | interrupts = <0x05>; 150 | interrupt-parent = <0x03>; 151 | reg = <0x00 0x10005000 0x00 0x1000>; 152 | compatible = "virtio,mmio"; 153 | }; 154 | 155 | virtio_mmio@10004000 { 156 | interrupts = <0x04>; 157 | interrupt-parent = <0x03>; 158 | reg = <0x00 0x10004000 0x00 0x1000>; 159 | compatible = "virtio,mmio"; 160 | }; 161 | 162 | virtio_mmio@10003000 { 163 | interrupts = <0x03>; 164 | interrupt-parent = <0x03>; 165 | reg = <0x00 0x10003000 0x00 0x1000>; 166 | compatible = "virtio,mmio"; 167 | }; 168 | 169 | virtio_mmio@10002000 { 170 | interrupts = <0x02>; 171 | interrupt-parent = <0x03>; 172 | reg = <0x00 0x10002000 0x00 0x1000>; 173 | compatible = "virtio,mmio"; 174 | }; 175 | 176 | virtio_mmio@10001000 { 177 | interrupts = <0x01>; 178 | interrupt-parent = <0x03>; 179 | reg = <0x00 0x10001000 0x00 0x1000>; 180 | compatible = "virtio,mmio"; 181 | }; 182 | 183 | plic@c000000 { 184 | phandle = <0x03>; 185 | riscv,ndev = <0x60>; 186 | reg = <0x00 0xc000000 0x00 0x600000>; 187 | interrupts-extended = <0x02 0x0b 0x02 0x09>; 188 | interrupt-controller; 189 | compatible = "sifive,plic-1.0.0\0riscv,plic0"; 190 | #interrupt-cells = <0x01>; 191 | }; 192 | 193 | clint@2000000 { 194 | interrupts-extended = <0x02 0x03 0x02 0x07>; 195 | reg = <0x00 0x2000000 0x00 0x10000>; 196 | compatible = "sifive,clint0\0riscv,clint0"; 197 | }; 198 | }; 199 | }; -------------------------------------------------------------------------------- /guest/rCore-Tutorial-v3/rCore-Tutorial-v3.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/rCore-Tutorial-v3/rCore-Tutorial-v3.elf -------------------------------------------------------------------------------- /guest/rtthread/.gitignore: -------------------------------------------------------------------------------- 1 | sd.bin 2 | *.dtb -------------------------------------------------------------------------------- /guest/rtthread/rtthread.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | #address-cells = <0x02>; 5 | #size-cells = <0x02>; 6 | compatible = "riscv-virtio"; 7 | model = "riscv-virtio,qemu"; 8 | 9 | fw-cfg@10100000 { 10 | dma-coherent; 11 | reg = <0x00 0x10100000 0x00 0x18>; 12 | compatible = "qemu,fw-cfg-mmio"; 13 | }; 14 | 15 | flash@20000000 { 16 | bank-width = <0x04>; 17 | reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>; 18 | compatible = "cfi-flash"; 19 | }; 20 | 21 | chosen { 22 | stdout-path = "/soc/uart@10000000"; 23 | }; 24 | 25 | platform@4000000 { 26 | interrupt-parent = <0x03>; 27 | ranges = <0x00 0x00 0x4000000 0x2000000>; 28 | #address-cells = <0x01>; 29 | #size-cells = <0x01>; 30 | compatible = "qemu,platform\0simple-bus"; 31 | }; 32 | 33 | memory@90000000 { 34 | device_type = "memory"; 35 | reg = <0x00 0x90002000 0x00 0x1000000>; 36 | }; 37 | 38 | cpus { 39 | #address-cells = <0x01>; 40 | #size-cells = <0x00>; 41 | timebase-frequency = <0x989680>; 42 | 43 | cpu@0 { 44 | phandle = <0x01>; 45 | device_type = "cpu"; 46 | reg = <0x00>; 47 | status = "okay"; 48 | compatible = "riscv"; 49 | riscv,isa = "rv64imafdch_zicsr_zifencei_zba_zbb_zbc_zbs"; 50 | mmu-type = "riscv,sv48"; 51 | 52 | interrupt-controller { 53 | #interrupt-cells = <0x01>; 54 | interrupt-controller; 55 | compatible = "riscv,cpu-intc"; 56 | phandle = <0x02>; 57 | }; 58 | }; 59 | 60 | cpu-map { 61 | 62 | cluster0 { 63 | 64 | core0 { 65 | cpu = <0x01>; 66 | }; 67 | }; 68 | }; 69 | }; 70 | 71 | soc { 72 | #address-cells = <0x02>; 73 | #size-cells = <0x02>; 74 | compatible = "simple-bus"; 75 | ranges; 76 | 77 | rtc@101000 { 78 | interrupts = <0x0b>; 79 | interrupt-parent = <0x03>; 80 | reg = <0x00 0x101000 0x00 0x1000>; 81 | compatible = "google,goldfish-rtc"; 82 | }; 83 | 84 | uart@10000000 { 85 | interrupts = <0x0a>; 86 | interrupt-parent = <0x03>; 87 | clock-frequency = "\08@"; 88 | reg = <0x00 0x10000000 0x00 0x100>; 89 | compatible = "ns16550a"; 90 | }; 91 | 92 | poweroff { 93 | value = <0x5555>; 94 | offset = <0x00>; 95 | regmap = <0x04>; 96 | compatible = "syscon-poweroff"; 97 | }; 98 | 99 | reboot { 100 | value = <0x7777>; 101 | offset = <0x00>; 102 | regmap = <0x04>; 103 | compatible = "syscon-reboot"; 104 | }; 105 | 106 | test@100000 { 107 | phandle = <0x04>; 108 | reg = <0x00 0x100000 0x00 0x1000>; 109 | compatible = "sifive,test1\0sifive,test0\0syscon"; 110 | }; 111 | 112 | pci@30000000 { 113 | interrupt-map-mask = <0x1800 0x00 0x00 0x07>; 114 | interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>; 115 | ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; 116 | reg = <0x00 0x30000000 0x00 0x10000000>; 117 | dma-coherent; 118 | bus-range = <0x00 0xff>; 119 | linux,pci-domain = <0x00>; 120 | device_type = "pci"; 121 | compatible = "pci-host-ecam-generic"; 122 | #size-cells = <0x02>; 123 | #interrupt-cells = <0x01>; 124 | #address-cells = <0x03>; 125 | }; 126 | 127 | virtio_mmio@10008000 { 128 | interrupts = <0x08>; 129 | interrupt-parent = <0x03>; 130 | reg = <0x00 0x10008000 0x00 0x1000>; 131 | compatible = "virtio,mmio"; 132 | }; 133 | 134 | virtio_mmio@10007000 { 135 | interrupts = <0x07>; 136 | interrupt-parent = <0x03>; 137 | reg = <0x00 0x10007000 0x00 0x1000>; 138 | compatible = "virtio,mmio"; 139 | }; 140 | 141 | virtio_mmio@10006000 { 142 | interrupts = <0x06>; 143 | interrupt-parent = <0x03>; 144 | reg = <0x00 0x10006000 0x00 0x1000>; 145 | compatible = "virtio,mmio"; 146 | }; 147 | 148 | virtio_mmio@10005000 { 149 | interrupts = <0x05>; 150 | interrupt-parent = <0x03>; 151 | reg = <0x00 0x10005000 0x00 0x1000>; 152 | compatible = "virtio,mmio"; 153 | }; 154 | 155 | virtio_mmio@10004000 { 156 | interrupts = <0x04>; 157 | interrupt-parent = <0x03>; 158 | reg = <0x00 0x10004000 0x00 0x1000>; 159 | compatible = "virtio,mmio"; 160 | }; 161 | 162 | virtio_mmio@10003000 { 163 | interrupts = <0x03>; 164 | interrupt-parent = <0x03>; 165 | reg = <0x00 0x10003000 0x00 0x1000>; 166 | compatible = "virtio,mmio"; 167 | }; 168 | 169 | virtio_mmio@10002000 { 170 | interrupts = <0x02>; 171 | interrupt-parent = <0x03>; 172 | reg = <0x00 0x10002000 0x00 0x1000>; 173 | compatible = "virtio,mmio"; 174 | }; 175 | 176 | virtio_mmio@10001000 { 177 | interrupts = <0x01>; 178 | interrupt-parent = <0x03>; 179 | reg = <0x00 0x10001000 0x00 0x1000>; 180 | compatible = "virtio,mmio"; 181 | }; 182 | 183 | plic@c000000 { 184 | phandle = <0x03>; 185 | riscv,ndev = <0x60>; 186 | reg = <0x00 0xc000000 0x00 0x600000>; 187 | interrupts-extended = <0x02 0x0b 0x02 0x09>; 188 | interrupt-controller; 189 | compatible = "sifive,plic-1.0.0\0riscv,plic0"; 190 | #interrupt-cells = <0x01>; 191 | }; 192 | 193 | clint@2000000 { 194 | interrupts-extended = <0x02 0x03 0x02 0x07>; 195 | reg = <0x00 0x2000000 0x00 0x10000>; 196 | compatible = "sifive,clint0\0riscv,clint0"; 197 | }; 198 | }; 199 | }; 200 | -------------------------------------------------------------------------------- /guest/rtthread/rtthread.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/rtthread/rtthread.elf -------------------------------------------------------------------------------- /guest/u-boot/u-boot.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/u-boot/u-boot.bin -------------------------------------------------------------------------------- /guest/u-boot/u-boot.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/u-boot/u-boot.dtb -------------------------------------------------------------------------------- /guest/u-boot/u-boot.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | #address-cells = <0x02>; 5 | #size-cells = <0x02>; 6 | compatible = "riscv-virtio"; 7 | model = "riscv-virtio,qemu"; 8 | 9 | fw-cfg@10100000 { 10 | dma-coherent; 11 | reg = <0x00 0x10100000 0x00 0x18>; 12 | compatible = "qemu,fw-cfg-mmio"; 13 | }; 14 | 15 | flash@20000000 { 16 | bank-width = <0x04>; 17 | reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>; 18 | compatible = "cfi-flash"; 19 | }; 20 | 21 | chosen { 22 | stdout-path = "/soc/uart@10000000"; 23 | }; 24 | 25 | platform@4000000 { 26 | interrupt-parent = <0x03>; 27 | ranges = <0x00 0x00 0x4000000 0x2000000>; 28 | #address-cells = <0x01>; 29 | #size-cells = <0x01>; 30 | compatible = "qemu,platform\0simple-bus"; 31 | }; 32 | 33 | memory@90000000 { 34 | device_type = "memory"; 35 | reg = <0x00 0x90200000 0x00 0x1000000>; 36 | }; 37 | 38 | cpus { 39 | #address-cells = <0x01>; 40 | #size-cells = <0x00>; 41 | timebase-frequency = <0x989680>; 42 | 43 | cpu@0 { 44 | phandle = <0x01>; 45 | device_type = "cpu"; 46 | reg = <0x00>; 47 | status = "okay"; 48 | compatible = "riscv"; 49 | riscv,isa = "rv64imafdch_zicsr_zifencei_zba_zbb_zbc_zbs"; 50 | mmu-type = "riscv,sv48"; 51 | 52 | interrupt-controller { 53 | #interrupt-cells = <0x01>; 54 | interrupt-controller; 55 | compatible = "riscv,cpu-intc"; 56 | phandle = <0x02>; 57 | }; 58 | }; 59 | 60 | cpu-map { 61 | 62 | cluster0 { 63 | 64 | core0 { 65 | cpu = <0x01>; 66 | }; 67 | }; 68 | }; 69 | }; 70 | 71 | soc { 72 | #address-cells = <0x02>; 73 | #size-cells = <0x02>; 74 | compatible = "simple-bus"; 75 | ranges; 76 | 77 | rtc@101000 { 78 | interrupts = <0x0b>; 79 | interrupt-parent = <0x03>; 80 | reg = <0x00 0x101000 0x00 0x1000>; 81 | compatible = "google,goldfish-rtc"; 82 | }; 83 | 84 | uart@10000000 { 85 | interrupts = <0x0a>; 86 | interrupt-parent = <0x03>; 87 | clock-frequency = "\08@"; 88 | reg = <0x00 0x10000000 0x00 0x100>; 89 | compatible = "ns16550a"; 90 | }; 91 | 92 | poweroff { 93 | value = <0x5555>; 94 | offset = <0x00>; 95 | regmap = <0x04>; 96 | compatible = "syscon-poweroff"; 97 | }; 98 | 99 | reboot { 100 | value = <0x7777>; 101 | offset = <0x00>; 102 | regmap = <0x04>; 103 | compatible = "syscon-reboot"; 104 | }; 105 | 106 | test@100000 { 107 | phandle = <0x04>; 108 | reg = <0x00 0x100000 0x00 0x1000>; 109 | compatible = "sifive,test1\0sifive,test0\0syscon"; 110 | }; 111 | 112 | pci@30000000 { 113 | interrupt-map-mask = <0x1800 0x00 0x00 0x07>; 114 | interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>; 115 | ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>; 116 | reg = <0x00 0x30000000 0x00 0x10000000>; 117 | dma-coherent; 118 | bus-range = <0x00 0xff>; 119 | linux,pci-domain = <0x00>; 120 | device_type = "pci"; 121 | compatible = "pci-host-ecam-generic"; 122 | #size-cells = <0x02>; 123 | #interrupt-cells = <0x01>; 124 | #address-cells = <0x03>; 125 | }; 126 | 127 | virtio_mmio@10008000 { 128 | interrupts = <0x08>; 129 | interrupt-parent = <0x03>; 130 | reg = <0x00 0x10008000 0x00 0x1000>; 131 | compatible = "virtio,mmio"; 132 | }; 133 | 134 | virtio_mmio@10007000 { 135 | interrupts = <0x07>; 136 | interrupt-parent = <0x03>; 137 | reg = <0x00 0x10007000 0x00 0x1000>; 138 | compatible = "virtio,mmio"; 139 | }; 140 | 141 | virtio_mmio@10006000 { 142 | interrupts = <0x06>; 143 | interrupt-parent = <0x03>; 144 | reg = <0x00 0x10006000 0x00 0x1000>; 145 | compatible = "virtio,mmio"; 146 | }; 147 | 148 | virtio_mmio@10005000 { 149 | interrupts = <0x05>; 150 | interrupt-parent = <0x03>; 151 | reg = <0x00 0x10005000 0x00 0x1000>; 152 | compatible = "virtio,mmio"; 153 | }; 154 | 155 | virtio_mmio@10004000 { 156 | interrupts = <0x04>; 157 | interrupt-parent = <0x03>; 158 | reg = <0x00 0x10004000 0x00 0x1000>; 159 | compatible = "virtio,mmio"; 160 | }; 161 | 162 | virtio_mmio@10003000 { 163 | interrupts = <0x03>; 164 | interrupt-parent = <0x03>; 165 | reg = <0x00 0x10003000 0x00 0x1000>; 166 | compatible = "virtio,mmio"; 167 | }; 168 | 169 | virtio_mmio@10002000 { 170 | interrupts = <0x02>; 171 | interrupt-parent = <0x03>; 172 | reg = <0x00 0x10002000 0x00 0x1000>; 173 | compatible = "virtio,mmio"; 174 | }; 175 | 176 | virtio_mmio@10001000 { 177 | interrupts = <0x01>; 178 | interrupt-parent = <0x03>; 179 | reg = <0x00 0x10001000 0x00 0x1000>; 180 | compatible = "virtio,mmio"; 181 | }; 182 | 183 | plic@c000000 { 184 | phandle = <0x03>; 185 | riscv,ndev = <0x60>; 186 | reg = <0x00 0xc000000 0x00 0x600000>; 187 | interrupts-extended = <0x02 0x0b 0x02 0x09>; 188 | interrupt-controller; 189 | compatible = "sifive,plic-1.0.0\0riscv,plic0"; 190 | #interrupt-cells = <0x01>; 191 | }; 192 | 193 | clint@2000000 { 194 | interrupts-extended = <0x02 0x03 0x02 0x07>; 195 | reg = <0x00 0x2000000 0x00 0x10000>; 196 | compatible = "sifive,clint0\0riscv,clint0"; 197 | }; 198 | }; 199 | }; 200 | -------------------------------------------------------------------------------- /guest/u-boot/u-boot.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/u-boot/u-boot.elf -------------------------------------------------------------------------------- /guest/u-boot/uboot.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/u-boot/uboot.bin -------------------------------------------------------------------------------- /guest/u-boot/uboot.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/guest/u-boot/uboot.elf -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" -------------------------------------------------------------------------------- /scripts/hyperbench.sh: -------------------------------------------------------------------------------- 1 | dtc -I dts -O dtb -o ./guest/hyperbench/hyperbench.dtb ./guest/hyperbench/hyperbench.dts 2 | cp ./guest/hyperbench/hyperbench ./guest.bin 3 | cp ./guest/hyperbench/hyperbench.dtb ./guest.dtb -------------------------------------------------------------------------------- /scripts/linux.sh: -------------------------------------------------------------------------------- 1 | dtc -I dts -O dtb -o ./guest.dtb ./guest/linux/linux.dts 2 | cp ./guest/linux/linux.bin ./guest.bin 3 | cp ./guest/linux/linux.elf ./guest.elf -------------------------------------------------------------------------------- /scripts/rCore-Tutorial-v3.sh: -------------------------------------------------------------------------------- 1 | dtc -I dts -O dtb -o ./guest/rCore-Tutorial-v3/rCore-Tutorial-v3.dtb ./guest/rCore-Tutorial-v3/rCore-Tutorial-v3.dts 2 | cp ./guest/rCore-Tutorial-v3/rCore-Tutorial-v3.elf ./guest.elf 3 | rust-objcopy --binary-architecture=riscv64 --strip-all -O binary ./guest/rCore-Tutorial-v3/rCore-Tutorial-v3.elf ./guest.bin 4 | cp ./guest/rCore-Tutorial-v3/rCore-Tutorial-v3.dtb ./guest.dtb -------------------------------------------------------------------------------- /scripts/rt-thread.sh: -------------------------------------------------------------------------------- 1 | dtc -I dts -O dtb -o ./guest/rtthread/rtthread.dtb ./guest/rtthread/rtthread.dts 2 | cp ./guest/rtthread/rtthread.elf ./guest.elf 3 | cp ./guest/rtthread/rtthread.dtb ./guest.dtb 4 | 5 | if [ ! -f "./guest/rtthread/sd.bin" ]; then 6 | dd if=/dev/zero of=./guest/rtthread/sd.bin bs=1024 count=65536 7 | fi -------------------------------------------------------------------------------- /scripts/u-boot.sh: -------------------------------------------------------------------------------- 1 | dtc ./guest/u-boot/u-boot/u-boot.dts ./guest/u-boot/u-boot/u-boot.dtb 2 | cp ./guest/u-boot/u-boot.elf ./guest.elf 3 | cp ./guest/u-boot/u-boot.dtb ./guest.dtb -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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!("\x1b[1;32m[Tracking] ", $fmt, "\x1b[0m\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 | -------------------------------------------------------------------------------- /src/constants.rs: -------------------------------------------------------------------------------- 1 | pub const PAGE_SIZE: usize = 0x1000; 2 | pub const PAGE_SIZE_BITS: usize = 0xc; 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 | 7 | 8 | pub const MAX_GUESTS: usize = 4; 9 | pub const MAX_GUEST_HARTS: usize = 16; 10 | /// Number of contexts for the PLIC. Value is twice the max number of harts because each hart will 11 | /// have on M-mode context and one S-mode context. 12 | pub const MAX_CONTEXTS: usize = 16 * 2; 13 | 14 | pub use crate::board::CLOCK_FREQ; 15 | 16 | pub mod layout { 17 | use super::PAGE_SIZE; 18 | 19 | pub const MEMORY_START: usize = 0x8000_0000; 20 | pub const MEMORY_END: usize = 0x8800_0000; 21 | 22 | /// 跳板页虚拟地址 23 | pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; 24 | 25 | /// 上下文切换数据存储虚拟地址 26 | pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE; 27 | 28 | pub const GUEST_START_PA: usize = 0x9020_0000; 29 | pub const GUEST_START_VA: usize = 0x9020_0000; 30 | 31 | pub const GUEST_DEFAULT_SIZE: usize = 128 * 1024 * 1024; 32 | 33 | pub const GUEST_DTB_ADDR: usize = 0x9000_0000; 34 | 35 | pub use crate::board::MMIO; 36 | } 37 | 38 | pub mod csr { 39 | pub const ustatus: usize = 0x000; 40 | pub const uie: usize = 0x004; 41 | pub const utvec: usize = 0x005; 42 | pub const uscratch: usize = 0x040; 43 | pub const uepc: usize = 0x041; 44 | pub const ucause: usize = 0x042; 45 | pub const utval: usize = 0x043; 46 | pub const uip: usize = 0x044; 47 | pub const fflags: usize = 0x001; 48 | pub const frm: usize = 0x002; 49 | pub const fcsr: usize = 0x003; 50 | pub const cycle: usize = 0xc00; 51 | pub const time: usize = 0xc01; 52 | pub const instret: usize = 0xc02; 53 | pub const hpmcounter3: usize = 0xc03; 54 | pub const hpmcounter4: usize = 0xc04; 55 | pub const hpmcounter5: usize = 0xc05; 56 | pub const hpmcounter6: usize = 0xc06; 57 | pub const hpmcounter7: usize = 0xc07; 58 | pub const hpmcounter8: usize = 0xc08; 59 | pub const hpmcounter9: usize = 0xc09; 60 | pub const hpmcounter10: usize = 0xc0a; 61 | pub const hpmcounter11: usize = 0xc0b; 62 | pub const hpmcounter12: usize = 0xc0c; 63 | pub const hpmcounter13: usize = 0xc0d; 64 | pub const hpmcounter14: usize = 0xc0e; 65 | pub const hpmcounter15: usize = 0xc0f; 66 | pub const hpmcounter16: usize = 0xc10; 67 | pub const hpmcounter17: usize = 0xc11; 68 | pub const hpmcounter18: usize = 0xc12; 69 | pub const hpmcounter19: usize = 0xc13; 70 | pub const hpmcounter20: usize = 0xc14; 71 | pub const hpmcounter21: usize = 0xc15; 72 | pub const hpmcounter22: usize = 0xc16; 73 | pub const hpmcounter23: usize = 0xc17; 74 | pub const hpmcounter24: usize = 0xc18; 75 | pub const hpmcounter25: usize = 0xc19; 76 | pub const hpmcounter26: usize = 0xc1a; 77 | pub const hpmcounter27: usize = 0xc1b; 78 | pub const hpmcounter28: usize = 0xc1c; 79 | pub const hpmcounter29: usize = 0xc1d; 80 | pub const hpmcounter30: usize = 0xc1e; 81 | pub const hpmcounter31: usize = 0xc1f; 82 | pub const cycleh: usize = 0xc80; 83 | pub const timeh: usize = 0xc81; 84 | pub const instreth: usize = 0xc82; 85 | pub const hpmcounter3h: usize = 0xc83; 86 | pub const hpmcounter4h: usize = 0xc84; 87 | pub const hpmcounter5h: usize = 0xc85; 88 | pub const hpmcounter6h: usize = 0xc86; 89 | pub const hpmcounter7h: usize = 0xc87; 90 | pub const hpmcounter8h: usize = 0xc88; 91 | pub const hpmcounter9h: usize = 0xc89; 92 | pub const hpmcounter10h: usize = 0xc8a; 93 | pub const hpmcounter11h: usize = 0xc8b; 94 | pub const hpmcounter12h: usize = 0xc8c; 95 | pub const hpmcounter13h: usize = 0xc8d; 96 | pub const hpmcounter14h: usize = 0xc8e; 97 | pub const hpmcounter15h: usize = 0xc8f; 98 | pub const hpmcounter16h: usize = 0xc90; 99 | pub const hpmcounter17h: usize = 0xc91; 100 | pub const hpmcounter18h: usize = 0xc92; 101 | pub const hpmcounter19h: usize = 0xc93; 102 | pub const hpmcounter20h: usize = 0xc94; 103 | pub const hpmcounter21h: usize = 0xc95; 104 | pub const hpmcounter22h: usize = 0xc96; 105 | pub const hpmcounter23h: usize = 0xc97; 106 | pub const hpmcounter24h: usize = 0xc98; 107 | pub const hpmcounter25h: usize = 0xc99; 108 | pub const hpmcounter26h: usize = 0xc9a; 109 | pub const hpmcounter27h: usize = 0xc9b; 110 | pub const hpmcounter28h: usize = 0xc9c; 111 | pub const hpmcounter29h: usize = 0xc9d; 112 | pub const hpmcounter30h: usize = 0xc9e; 113 | pub const hpmcounter31h: usize = 0xc9f; 114 | pub const mcycle: usize = 0xb00; 115 | pub const minstret: usize = 0xb02; 116 | pub const mcycleh: usize = 0xb80; 117 | pub const minstreth: usize = 0xb82; 118 | pub const mvendorid: usize = 0xf11; 119 | pub const marchid: usize = 0xf12; 120 | pub const mimpid: usize = 0xf13; 121 | pub const mhartid: usize = 0xf14; 122 | pub const mstatus: usize = 0x300; 123 | pub const misa: usize = 0x301; 124 | pub const medeleg: usize = 0x302; 125 | pub const mideleg: usize = 0x303; 126 | pub const mie: usize = 0x304; 127 | pub const mtvec: usize = 0x305; 128 | pub const mcounteren: usize = 0x306; 129 | pub const mtvt: usize = 0x307; 130 | pub const mucounteren: usize = 0x320; 131 | pub const mscounteren: usize = 0x321; 132 | pub const mscratch: usize = 0x340; 133 | pub const mepc: usize = 0x341; 134 | pub const mcause: usize = 0x342; 135 | pub const mbadaddr: usize = 0x343; 136 | pub const mtval: usize = 0x343; 137 | pub const mip: usize = 0x344; 138 | pub const mnxti: usize = 0x345; 139 | pub const mintstatus: usize = 0x346; 140 | pub const mscratchcsw: usize = 0x348; 141 | pub const sstatus: usize = 0x100; 142 | pub const sedeleg: usize = 0x102; 143 | pub const sideleg: usize = 0x103; 144 | pub const sie: usize = 0x104; 145 | pub const stvec: usize = 0x105; 146 | pub const scounteren: usize = 0x106; 147 | pub const stvt: usize = 0x107; 148 | pub const sscratch: usize = 0x140; 149 | pub const sepc: usize = 0x141; 150 | pub const scause: usize = 0x142; 151 | pub const sbadaddr: usize = 0x143; 152 | pub const stval: usize = 0x143; 153 | pub const sip: usize = 0x144; 154 | pub const snxti: usize = 0x145; 155 | pub const sintstatus: usize = 0x146; 156 | pub const sscratchcsw: usize = 0x148; 157 | pub const sptbr: usize = 0x180; 158 | pub const satp: usize = 0x180; 159 | pub const pmpcfg0: usize = 0x3a0; 160 | pub const pmpcfg1: usize = 0x3a1; 161 | pub const pmpcfg2: usize = 0x3a2; 162 | pub const pmpcfg3: usize = 0x3a3; 163 | pub const pmpaddr0: usize = 0x3b0; 164 | pub const pmpaddr1: usize = 0x3b1; 165 | pub const pmpaddr2: usize = 0x3b2; 166 | pub const pmpaddr3: usize = 0x3b3; 167 | pub const pmpaddr4: usize = 0x3b4; 168 | pub const pmpaddr5: usize = 0x3b5; 169 | pub const pmpaddr6: usize = 0x3b6; 170 | pub const pmpaddr7: usize = 0x3b7; 171 | pub const pmpaddr8: usize = 0x3b8; 172 | pub const pmpaddr9: usize = 0x3b9; 173 | pub const pmpaddr10: usize = 0x3ba; 174 | pub const pmpaddr11: usize = 0x3bb; 175 | pub const pmpaddr12: usize = 0x3bc; 176 | pub const pmpaddr13: usize = 0x3bd; 177 | pub const pmpaddr14: usize = 0x3be; 178 | pub const pmpaddr15: usize = 0x3bf; 179 | pub const tselect: usize = 0x7a0; 180 | pub const tdata1: usize = 0x7a1; 181 | pub const tdata2: usize = 0x7a2; 182 | pub const tdata3: usize = 0x7a3; 183 | pub const dcsr: usize = 0x7b0; 184 | pub const dpc: usize = 0x7b1; 185 | pub const dscratch: usize = 0x7b2; 186 | pub const mhpmcounter3: usize = 0xb03; 187 | pub const mhpmcounter4: usize = 0xb04; 188 | pub const mhpmcounter5: usize = 0xb05; 189 | pub const mhpmcounter6: usize = 0xb06; 190 | pub const mhpmcounter7: usize = 0xb07; 191 | pub const mhpmcounter8: usize = 0xb08; 192 | pub const mhpmcounter9: usize = 0xb09; 193 | pub const mhpmcounter10: usize = 0xb0a; 194 | pub const mhpmcounter11: usize = 0xb0b; 195 | pub const mhpmcounter12: usize = 0xb0c; 196 | pub const mhpmcounter13: usize = 0xb0d; 197 | pub const mhpmcounter14: usize = 0xb0e; 198 | pub const mhpmcounter15: usize = 0xb0f; 199 | pub const mhpmcounter16: usize = 0xb10; 200 | pub const mhpmcounter17: usize = 0xb11; 201 | pub const mhpmcounter18: usize = 0xb12; 202 | pub const mhpmcounter19: usize = 0xb13; 203 | pub const mhpmcounter20: usize = 0xb14; 204 | pub const mhpmcounter21: usize = 0xb15; 205 | pub const mhpmcounter22: usize = 0xb16; 206 | pub const mhpmcounter23: usize = 0xb17; 207 | pub const mhpmcounter24: usize = 0xb18; 208 | pub const mhpmcounter25: usize = 0xb19; 209 | pub const mhpmcounter26: usize = 0xb1a; 210 | pub const mhpmcounter27: usize = 0xb1b; 211 | pub const mhpmcounter28: usize = 0xb1c; 212 | pub const mhpmcounter29: usize = 0xb1d; 213 | pub const mhpmcounter30: usize = 0xb1e; 214 | pub const mhpmcounter31: usize = 0xb1f; 215 | pub const mhpmevent3: usize = 0x323; 216 | pub const mhpmevent4: usize = 0x324; 217 | pub const mhpmevent5: usize = 0x325; 218 | pub const mhpmevent6: usize = 0x326; 219 | pub const mhpmevent7: usize = 0x327; 220 | pub const mhpmevent8: usize = 0x328; 221 | pub const mhpmevent9: usize = 0x329; 222 | pub const mhpmevent10: usize = 0x32a; 223 | pub const mhpmevent11: usize = 0x32b; 224 | pub const mhpmevent12: usize = 0x32c; 225 | pub const mhpmevent13: usize = 0x32d; 226 | pub const mhpmevent14: usize = 0x32e; 227 | pub const mhpmevent15: usize = 0x32f; 228 | pub const mhpmevent16: usize = 0x330; 229 | pub const mhpmevent17: usize = 0x331; 230 | pub const mhpmevent18: usize = 0x332; 231 | pub const mhpmevent19: usize = 0x333; 232 | pub const mhpmevent20: usize = 0x334; 233 | pub const mhpmevent21: usize = 0x335; 234 | pub const mhpmevent22: usize = 0x336; 235 | pub const mhpmevent23: usize = 0x337; 236 | pub const mhpmevent24: usize = 0x338; 237 | pub const mhpmevent25: usize = 0x339; 238 | pub const mhpmevent26: usize = 0x33a; 239 | pub const mhpmevent27: usize = 0x33b; 240 | pub const mhpmevent28: usize = 0x33c; 241 | pub const mhpmevent29: usize = 0x33d; 242 | pub const mhpmevent30: usize = 0x33e; 243 | pub const mhpmevent31: usize = 0x33f; 244 | pub const mhpmcounter3h: usize = 0xb83; 245 | pub const mhpmcounter4h: usize = 0xb84; 246 | pub const mhpmcounter5h: usize = 0xb85; 247 | pub const mhpmcounter6h: usize = 0xb86; 248 | pub const mhpmcounter7h: usize = 0xb87; 249 | pub const mhpmcounter8h: usize = 0xb88; 250 | pub const mhpmcounter9h: usize = 0xb89; 251 | pub const mhpmcounter10h: usize = 0xb8a; 252 | pub const mhpmcounter11h: usize = 0xb8b; 253 | pub const mhpmcounter12h: usize = 0xb8c; 254 | pub const mhpmcounter13h: usize = 0xb8d; 255 | pub const mhpmcounter14h: usize = 0xb8e; 256 | pub const mhpmcounter15h: usize = 0xb8f; 257 | pub const mhpmcounter16h: usize = 0xb90; 258 | pub const mhpmcounter17h: usize = 0xb91; 259 | pub const mhpmcounter18h: usize = 0xb92; 260 | pub const mhpmcounter19h: usize = 0xb93; 261 | pub const mhpmcounter20h: usize = 0xb94; 262 | pub const mhpmcounter21h: usize = 0xb95; 263 | pub const mhpmcounter22h: usize = 0xb96; 264 | pub const mhpmcounter23h: usize = 0xb97; 265 | pub const mhpmcounter24h: usize = 0xb98; 266 | pub const mhpmcounter25h: usize = 0xb99; 267 | pub const mhpmcounter26h: usize = 0xb9a; 268 | pub const mhpmcounter27h: usize = 0xb9b; 269 | pub const mhpmcounter28h: usize = 0xb9c; 270 | pub const mhpmcounter29h: usize = 0xb9d; 271 | pub const mhpmcounter30h: usize = 0xb9e; 272 | pub const mhpmcounter31h: usize = 0xb9f; 273 | 274 | pub enum VirtualzationMode { 275 | Host = 0, 276 | Guest = 1, 277 | } 278 | 279 | #[derive(PartialEq)] 280 | pub enum CpuMode { 281 | M = 0b11, 282 | S = 0b01, 283 | U = 0b00 284 | } 285 | 286 | pub enum PrevisorMode { 287 | U, 288 | HS, 289 | M, 290 | VU, 291 | VS 292 | } 293 | 294 | pub mod hedeleg { 295 | use core::arch::asm; 296 | 297 | pub const INST_ADDR_MISALIGN: usize = 1 << 0; 298 | pub const INST_ACCESSS_FAULT: usize = 1 << 1; 299 | pub const ILLEGAL_INST: usize = 1 << 2; 300 | pub const BREAKPOINT: usize = 1 << 3; 301 | pub const LOAD_ADDR_MISALIGNED: usize = 1 << 4; 302 | pub const LOAD_ACCESS_FAULT: usize = 1 << 5; 303 | pub const STORE_ADDR_MISALIGNED: usize = 1 << 6; 304 | pub const STORE_ACCESS_FAULT: usize = 1 << 7; 305 | pub const ENV_CALL_FROM_U_OR_VU: usize = 1 << 8; 306 | pub const ENV_CALL_FROM_HS: usize = 1 << 9; 307 | pub const ENV_CALL_FROM_VS: usize = 1 << 10; 308 | pub const ENV_CALL_FROM_M: usize = 1 << 11; 309 | pub const INST_PAGE_FAULT: usize = 1 << 12; 310 | pub const LOAD_PAGE_FAULT: usize = 1 << 13; 311 | pub const STORE_PAGE_FAULT: usize = 1 << 15; 312 | pub const INST_GUEST_PAGE_FAULT: usize = 1 << 20; 313 | pub const LOAD_GUEST_PAGE_FAULT: usize = 1 << 21; 314 | pub const VIRTUAL_INST: usize = 1 << 22; 315 | pub const STORE_GUEST_PAGE_FAULT: usize = 1 << 23; 316 | 317 | pub unsafe fn write(hedeleg: usize) { 318 | asm!( 319 | "csrw hedeleg, {}", 320 | in(reg) hedeleg 321 | ) 322 | } 323 | } 324 | 325 | pub mod hideleg { 326 | use core::arch::asm; 327 | pub const VSSIP: usize = 1 << 2; 328 | pub const VSTIP: usize = 1 << 6; 329 | pub const VSEIP: usize = 1 << 10; 330 | pub unsafe fn write(hideleg: usize) { 331 | asm!( 332 | "csrw hideleg, {}", 333 | in(reg) hideleg 334 | ) 335 | } 336 | } 337 | 338 | pub mod hcounteren { 339 | use core::arch::asm; 340 | 341 | pub unsafe fn write(hcounteren: u32) { 342 | asm!( 343 | "csrw hcounteren, {}", 344 | in(reg) hcounteren 345 | ) 346 | } 347 | } 348 | 349 | 350 | pub mod sip { 351 | /// software interrupts pending 352 | pub const SSIP: usize = 1 << 1; 353 | /// timer interrupts pending 354 | pub const STIP: usize = 1 << 5; 355 | /// external interrupts pending 356 | pub const SEIP: usize = 1 << 9; 357 | } 358 | 359 | } 360 | 361 | pub mod riscv_regs { 362 | #[derive(Default)] 363 | #[repr(C)] 364 | pub struct GeneralPurposeRegisters([u64; 32]); 365 | 366 | /// Index of risc-v general purpose registers in `GeneralPurposeRegisters`. 367 | #[repr(u32)] 368 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 369 | pub enum GprIndex { 370 | Zero = 0, 371 | RA, 372 | SP, 373 | GP, 374 | TP, 375 | T0, 376 | T1, 377 | T2, 378 | S0, 379 | S1, 380 | A0, 381 | A1, 382 | A2, 383 | A3, 384 | A4, 385 | A5, 386 | A6, 387 | A7, 388 | S2, 389 | S3, 390 | S4, 391 | S5, 392 | S6, 393 | S7, 394 | S8, 395 | S9, 396 | S10, 397 | S11, 398 | T3, 399 | T4, 400 | T5, 401 | T6, 402 | } 403 | 404 | impl GprIndex { 405 | pub fn from_raw(raw: u32) -> Option { 406 | use GprIndex::*; 407 | let index = match raw { 408 | 0 => Zero, 409 | 1 => RA, 410 | 2 => SP, 411 | 3 => GP, 412 | 4 => TP, 413 | 5 => T0, 414 | 6 => T1, 415 | 7 => T2, 416 | 8 => S0, 417 | 9 => S1, 418 | 10 => A0, 419 | 11 => A1, 420 | 12 => A2, 421 | 13 => A3, 422 | 14 => A4, 423 | 15 => A5, 424 | 16 => A6, 425 | 17 => A7, 426 | 18 => S2, 427 | 19 => S3, 428 | 20 => S4, 429 | 21 => S5, 430 | 22 => S6, 431 | 23 => S7, 432 | 24 => S8, 433 | 25 => S9, 434 | 26 => S10, 435 | 27 => S11, 436 | 28 => T3, 437 | 29 => T4, 438 | 30 => T5, 439 | 31 => T6, 440 | _ => { 441 | return None; 442 | } 443 | }; 444 | Some(index) 445 | } 446 | } 447 | 448 | impl GeneralPurposeRegisters { 449 | /// Returns the value of the given register. 450 | pub fn reg(&self, reg_index: GprIndex) -> u64 { 451 | self.0[reg_index as usize] 452 | } 453 | 454 | /// Sets the value of the given register. 455 | pub fn set_reg(&mut self, reg_index: GprIndex, val: u64) { 456 | if reg_index == GprIndex::Zero { 457 | return; 458 | } 459 | 460 | self.0[reg_index as usize] = val; 461 | } 462 | 463 | /// Returns the argument registers. 464 | /// This is avoids many calls when an SBI handler needs all of the argmuent regs. 465 | pub fn a_regs(&self) -> &[u64] { 466 | &self.0[GprIndex::A0 as usize..=GprIndex::A7 as usize] 467 | } 468 | 469 | /// Returns the arguments register as a mutable. 470 | pub fn a_regs_mut(&mut self) -> &mut [u64] { 471 | &mut self.0[GprIndex::A0 as usize..=GprIndex::A7 as usize] 472 | } 473 | } 474 | 475 | } -------------------------------------------------------------------------------- /src/detect.rs: -------------------------------------------------------------------------------- 1 | //! ref: https://github.com/luojia65/zihai/blob/main/zihai/src/detect.rs 2 | 3 | use core::arch::asm; 4 | use riscv::register::{ 5 | scause::{Exception, Scause, Trap}, 6 | sstatus, 7 | stvec::{self, Stvec, TrapMode}, 8 | }; 9 | 10 | // Detect if hypervisor extension exists on current hart environment 11 | // 12 | // This function tries to read hgatp and returns false if the read operation failed. 13 | pub fn detect_h_extension() -> bool { 14 | // run detection by trap on csrr instruction. 15 | let ans = with_detect_trap(0, || unsafe { 16 | asm!("csrr {}, 0x680", out(reg) _, options(nomem, nostack)); // 0x680 => hgatp 17 | }); 18 | // return the answer from output flag. 0 => success, 2 => failed, illegal instruction 19 | ans != 2 20 | } 21 | 22 | // Tries to execute all instructions defined in clojure `f`. 23 | // If resulted in an exception, this function returns its exception id. 24 | // 25 | // This function is useful to detect if an instruction exists on current environment. 26 | #[inline] 27 | fn with_detect_trap(param: usize, f: impl FnOnce()) -> usize { 28 | // disable interrupts and handle exceptions only 29 | let (sie, stvec, tp) = unsafe { init_detect_trap(param) }; 30 | // run detection inner 31 | f(); 32 | // restore trap handler and enable interrupts 33 | let ans = unsafe { restore_detect_trap(sie, stvec, tp) }; 34 | // return the answer 35 | ans 36 | } 37 | 38 | // rust trap handler for detect exceptions 39 | extern "C" fn rust_detect_trap(trap_frame: &mut TrapFrame) { 40 | // store returned exception id value into tp register 41 | // specially: illegal instruction => 2 42 | trap_frame.tp = trap_frame.scause.bits(); 43 | // if illegal instruction, skip current instruction 44 | match trap_frame.scause.cause() { 45 | Trap::Exception(Exception::IllegalInstruction) => { 46 | let mut insn_bits = riscv_illegal_insn_bits((trap_frame.stval & 0xFFFF) as u16); 47 | if insn_bits == 0 { 48 | let insn_half = unsafe { *(trap_frame.sepc as *const u16) }; 49 | insn_bits = riscv_illegal_insn_bits(insn_half); 50 | } 51 | // skip current instruction 52 | trap_frame.sepc = trap_frame.sepc.wrapping_add(insn_bits); 53 | } 54 | Trap::Exception(_) => unreachable!(), // FIXME: unexpected instruction errors 55 | Trap::Interrupt(_) => unreachable!(), // filtered out for sie == false 56 | } 57 | } 58 | 59 | // Gets risc-v instruction bits from illegal instruction stval value, or 0 if unknown 60 | #[inline] 61 | fn riscv_illegal_insn_bits(insn: u16) -> usize { 62 | if insn == 0 { 63 | return 0; // stval[0..16] == 0, unknown 64 | } 65 | if insn & 0b11 != 0b11 { 66 | return 2; // 16-bit 67 | } 68 | if insn & 0b11100 != 0b11100 { 69 | return 4; // 32-bit 70 | } 71 | // FIXME: add >= 48-bit instructions in the future if we need to detect such instrucions 72 | return 0; // >= 48-bit, unknown from this function by now 73 | } 74 | 75 | // Initialize environment for trap detection and filter in exception only 76 | #[inline] 77 | unsafe fn init_detect_trap(param: usize) -> (bool, Stvec, usize) { 78 | // clear SIE to handle exception only 79 | let stored_sie = sstatus::read().sie(); 80 | sstatus::clear_sie(); 81 | // use detect trap handler to handle exceptions 82 | let stored_stvec = stvec::read(); 83 | let mut trap_addr = on_detect_trap as usize; 84 | if trap_addr & 0b1 != 0 { 85 | trap_addr += 0b1; 86 | } 87 | stvec::write(trap_addr, TrapMode::Direct); 88 | // store tp register. tp will be used to load parameter and store return value 89 | let stored_tp: usize; 90 | asm!("mv {}, tp", "mv tp, {}", out(reg) stored_tp, in(reg) param, options(nomem, nostack)); 91 | // returns preserved previous hardware states 92 | (stored_sie, stored_stvec, stored_tp) 93 | } 94 | 95 | // Restore previous hardware states before trap detection 96 | #[inline] 97 | unsafe fn restore_detect_trap(sie: bool, stvec: Stvec, tp: usize) -> usize { 98 | // read the return value from tp register, and restore tp value 99 | let ans: usize; 100 | asm!("mv {}, tp", "mv tp, {}", out(reg) ans, in(reg) tp, options(nomem, nostack)); 101 | // restore trap vector settings 102 | asm!("csrw stvec, {}", in(reg) stvec.bits(), options(nomem, nostack)); 103 | // enable interrupts 104 | if sie { 105 | sstatus::set_sie(); 106 | }; 107 | ans 108 | } 109 | 110 | // Trap frame for instruction exception detection 111 | #[repr(C)] 112 | struct TrapFrame { 113 | ra: usize, 114 | tp: usize, 115 | a0: usize, 116 | a1: usize, 117 | a2: usize, 118 | a3: usize, 119 | a4: usize, 120 | a5: usize, 121 | a6: usize, 122 | a7: usize, 123 | t0: usize, 124 | t1: usize, 125 | t2: usize, 126 | t3: usize, 127 | t4: usize, 128 | t5: usize, 129 | t6: usize, 130 | sstatus: usize, 131 | sepc: usize, 132 | scause: Scause, 133 | stval: usize, 134 | } 135 | 136 | // Assembly trap handler for instruction detection. 137 | // 138 | // This trap handler shares the same stack from its prospective caller, 139 | // the caller must ensure it has abundant stack size for a trap handler. 140 | // 141 | // This function should not be used in conventional trap handling, 142 | // as it does not preserve a special trap stack, and it's designed to 143 | // handle exceptions only rather than interrupts. 144 | #[naked] 145 | unsafe extern "C" fn on_detect_trap() -> ! { 146 | asm!( 147 | ".p2align 2", 148 | "addi sp, sp, -8*21", 149 | "sd ra, 0*8(sp)", 150 | "sd tp, 1*8(sp)", 151 | "sd a0, 2*8(sp)", 152 | "sd a1, 3*8(sp)", 153 | "sd a2, 4*8(sp)", 154 | "sd a3, 5*8(sp)", 155 | "sd a4, 6*8(sp)", 156 | "sd a5, 7*8(sp)", 157 | "sd a6, 8*8(sp)", 158 | "sd a7, 9*8(sp)", 159 | "sd t0, 10*8(sp)", 160 | "sd t1, 11*8(sp)", 161 | "sd t2, 12*8(sp)", 162 | "sd t3, 13*8(sp)", 163 | "sd t4, 14*8(sp)", 164 | "sd t5, 15*8(sp)", 165 | "sd t6, 16*8(sp)", 166 | "csrr t0, sstatus", 167 | "sd t0, 17*8(sp)", 168 | "csrr t1, sepc", 169 | "sd t1, 18*8(sp)", 170 | "csrr t2, scause", 171 | "sd t2, 19*8(sp)", 172 | "csrr t3, stval", 173 | "sd t3, 20*8(sp)", 174 | "mv a0, sp", 175 | "call {rust_detect_trap}", 176 | "ld t0, 17*8(sp)", 177 | "csrw sstatus, t0", 178 | "ld t1, 18*8(sp)", 179 | "csrw sepc, t1", 180 | "ld t2, 19*8(sp)", 181 | "csrw scause, t2", 182 | "ld t3, 20*8(sp)", 183 | "csrw stval, t3", 184 | "ld ra, 0*8(sp)", 185 | "ld tp, 1*8(sp)", 186 | "ld a0, 2*8(sp)", 187 | "ld a1, 3*8(sp)", 188 | "ld a2, 4*8(sp)", 189 | "ld a3, 5*8(sp)", 190 | "ld a4, 6*8(sp)", 191 | "ld a5, 7*8(sp)", 192 | "ld a6, 8*8(sp)", 193 | "ld a7, 9*8(sp)", 194 | "ld t0, 10*8(sp)", 195 | "ld t1, 11*8(sp)", 196 | "ld t2, 12*8(sp)", 197 | "ld t3, 13*8(sp)", 198 | "ld t4, 14*8(sp)", 199 | "ld t5, 15*8(sp)", 200 | "ld t6, 16*8(sp)", 201 | "addi sp, sp, 8*21", 202 | "sret", 203 | rust_detect_trap = sym rust_detect_trap, 204 | options(noreturn), 205 | ) 206 | } 207 | -------------------------------------------------------------------------------- /src/device_emu/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod plic; -------------------------------------------------------------------------------- /src/device_emu/plic.rs: -------------------------------------------------------------------------------- 1 | use riscv::register::hvip; 2 | use riscv_decode::Instruction; 3 | 4 | use crate::guest::vmexit::TrapContext; 5 | use crate::{ 6 | constants::MAX_CONTEXTS, guest::page_table::GuestPageTable, hypervisor::HostVmm, 7 | page_table::PageTable, 8 | }; 9 | use crate::{VmmError, VmmResult}; 10 | 11 | pub const PLIC_OFFSET: &[(usize, usize)] = &[ 12 | (0x0, 0x1000), // Interrupt priority 13 | (0x1000, 0x1000), // Interrupt Pending 14 | (0x2000, 0x200000 - 0x2000), // Enable 15 | (0x200000, 0x600000 - 0x200000), // threshold/claim/complete 16 | ]; 17 | 18 | pub struct PlicState { 19 | pub base_addr: usize, 20 | pub claim_complete: [u32; MAX_CONTEXTS], 21 | } 22 | 23 | impl PlicState { 24 | pub fn new(base_addr: usize) -> Self { 25 | Self { 26 | base_addr, 27 | claim_complete: [0u32; MAX_CONTEXTS], 28 | } 29 | } 30 | } 31 | 32 | impl HostVmm { 33 | pub fn handle_plic_access( 34 | &mut self, 35 | ctx: &mut TrapContext, 36 | guest_pa: usize, 37 | instrution: Instruction, 38 | ) -> VmmResult { 39 | let host_plic = self.host_plic.as_mut().unwrap(); 40 | let offset = guest_pa.wrapping_sub(host_plic.base_addr); 41 | // threshold/claim/complete 42 | if offset >= 0x200000 && offset < 0x200000 + 0x1000 * MAX_CONTEXTS { 43 | let hart = (offset - 0x200000) / 0x1000; 44 | let index = ((offset - 0x200000) & 0xfff) >> 2; 45 | if index == 0 { 46 | // threshold 47 | match instrution { 48 | Instruction::Sw(i) => { 49 | // guest write threshold register to plic core 50 | let value = ctx.x[i.rs2() as usize] as u32; 51 | // hdebug!("PLIC write addr@{:#x} -> {:#x}", guest_pa, value); 52 | // todo: guest pa -> host pa 53 | // htracking!( 54 | // "write PLIC threshold reg, addr: {:#x}, value: {:#x}", 55 | // guest_pa, 56 | // value 57 | // ); 58 | unsafe { 59 | core::ptr::write_volatile(guest_pa as *mut u32, value); 60 | } 61 | } 62 | _ => return Err(VmmError::UnexpectedInst), 63 | } 64 | } else if index == 1 { 65 | // claim/complete 66 | // htracking!("claim/complete"); 67 | match instrution { 68 | Instruction::Lw(i) => { 69 | // guest read claim from plic core 70 | // htracking!( 71 | // "PLIC read addr@{:#x} -> {:#x}", 72 | // guest_pa, 73 | // host_plic.claim_complete[hart] 74 | // ); 75 | ctx.x[i.rd() as usize] = host_plic.claim_complete[hart] as usize; 76 | } 77 | Instruction::Sw(i) => { 78 | // guest write complete to plic core 79 | let value = ctx.x[i.rs2() as usize] as u32; 80 | // htracking!("Write plic addr@:{:#x} -> {:#x}", guest_pa, value); 81 | // todo: guest pa -> host pa 82 | unsafe { 83 | core::ptr::write_volatile(guest_pa as *mut u32, value); 84 | } 85 | host_plic.claim_complete[hart] = 0; 86 | unsafe { 87 | hvip::clear_vseip(); 88 | } 89 | } 90 | _ => return Err(VmmError::UnexpectedInst), 91 | } 92 | } 93 | } else { 94 | panic!("Invalid address: {:#x}", guest_pa); 95 | } 96 | Ok(()) 97 | } 98 | } 99 | 100 | #[inline(always)] 101 | pub fn is_plic_access(addr: usize) -> bool { 102 | // let host_vmm = unsafe{ HOST_VMM.get().unwrap().lock() }; 103 | // let plic = if let Some(plic) = &host_vmm.host_machine.plic { 104 | // plic 105 | // }else{ 106 | // return false 107 | // }; 108 | // if addr >= plic.base_address && addr <= plic.base_address + plic.size { return true } 109 | // else{ return false } 110 | 111 | // TODO: use guest machine base address 112 | return addr >= 0x0c00_0000 && addr < 0x1000_0000; 113 | } 114 | -------------------------------------------------------------------------------- /src/drivers/iommu/core.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KuangjuX/hypocaust-2/4da6018bbac5111af8ac814a31ff3e79c94c9e73/src/drivers/iommu/core.rs -------------------------------------------------------------------------------- /src/drivers/iommu/device_directory.rs: -------------------------------------------------------------------------------- 1 | 2 | // Maximum number of device ID bits used by the IOMMU. 3 | const DEVICE_ID_BITS: usize = 24; 4 | // Number of bits used to index into the leaf table. 5 | const LEAF_INDEX_BITS: usize = 6; 6 | // Number of bits used to index into intermediate tables. 7 | const NON_LEAF_INDEX_BITS: usize = 9; 8 | 9 | /// The device ID. Used to index into the device directory table. For PCI devices behind an IOMMU 10 | /// this is equivalent to the requester ID of the PCI device (i.e. the bits of the B/D/F). 11 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 12 | pub struct DeviceId(u32); 13 | 14 | impl DeviceId { 15 | /// Creates a new `DeviceId` from the raw `val`. 16 | pub fn new(val: u32) -> Option { 17 | if (val & !((1 << DEVICE_ID_BITS) - 1)) == 0 { 18 | Some(Self(val)) 19 | } else { 20 | None 21 | } 22 | } 23 | 24 | /// Returns the raw bits of this `DeviceId`. 25 | pub fn bits(&self) -> u32 { 26 | self.0 27 | } 28 | 29 | // Returns the bits from this `DeviceId` used to index at `level`. 30 | fn level_index_bits(&self, level: usize) -> usize { 31 | if level == 0 { 32 | (self.0 as usize) & ((1 << LEAF_INDEX_BITS) - 1) 33 | } else { 34 | let shift = LEAF_INDEX_BITS + NON_LEAF_INDEX_BITS * (level - 1); 35 | ((self.0 as usize) >> shift) & ((1 << NON_LEAF_INDEX_BITS) - 1) 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/drivers/iommu/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | mod device_directory; 3 | mod registers; 4 | mod core; 5 | 6 | #[cfg(test)] 7 | mod tests { 8 | 9 | } -------------------------------------------------------------------------------- /src/drivers/iommu/registers.rs: -------------------------------------------------------------------------------- 1 | 2 | use tock_registers::register_bitfields; 3 | use tock_registers::registers::{ReadOnly, ReadWrite}; 4 | 5 | 6 | // IOMMU register definitions; see https://github.com/riscv-non-isa/riscv-iommu. 7 | 8 | register_bitfields![u64, 9 | pub Capabilities [ 10 | Version OFFSET(0) NUMBITS(8), 11 | Sv32 OFFSET(8) NUMBITS(1), 12 | Sv39 OFFSET(9) NUMBITS(1), 13 | Sv48 OFFSET(10) NUMBITS(1), 14 | Sv57 OFFSET(11) NUMBITS(1), 15 | Sv32x4 OFFSET(16) NUMBITS(1), 16 | Sv39x4 OFFSET(17) NUMBITS(1), 17 | Sv48x4 OFFSET(18) NUMBITS(1), 18 | Sv57x4 OFFSET(19) NUMBITS(1), 19 | MsiFlat OFFSET(22) NUMBITS(1), 20 | MsiMrif OFFSET(23) NUMBITS(1), 21 | ], 22 | 23 | pub DirectoryPointer [ 24 | Mode OFFSET(0) NUMBITS(4), 25 | Busy OFFSET(4) NUMBITS(1), 26 | Ppn OFFSET(10) NUMBITS(44), 27 | ], 28 | 29 | pub QueueBase [ 30 | Log2SzMinus1 OFFSET(0) NUMBITS(5), 31 | Ppn OFFSET(10) NUMBITS(44), 32 | ], 33 | ]; 34 | 35 | register_bitfields![u32, 36 | pub CqControl [ 37 | Enable OFFSET(0) NUMBITS(1), 38 | InterruptEnable OFFSET(1) NUMBITS(1), 39 | MemoryFault OFFSET(8) NUMBITS(1), 40 | CommandTimeout OFFSET(9) NUMBITS(1), 41 | CommandIllegal OFFSET(10) NUMBITS(1), 42 | On OFFSET(16) NUMBITS(1), 43 | Busy OFFSET(17) NUMBITS(1), 44 | ], 45 | ]; 46 | 47 | /// The IOMMU register set. 48 | #[repr(C)] 49 | pub struct IommuRegisters { 50 | pub capabilities: ReadOnly, 51 | pub fctrl: ReadWrite, 52 | _reserved0: u32, 53 | pub ddtp: ReadWrite, 54 | pub cqb: ReadWrite, 55 | pub cqh: ReadOnly, 56 | pub cqt: ReadWrite, 57 | pub fqb: ReadWrite, 58 | pub fqh: ReadWrite, 59 | pub fqt: ReadOnly, 60 | pub pqb: ReadWrite, 61 | pub pqh: ReadWrite, 62 | pub pqt: ReadOnly, 63 | pub cqcsr: ReadWrite, 64 | pub fqcsr: ReadWrite, 65 | pub pqcsr: ReadWrite, 66 | pub ipsr: ReadWrite, 67 | // Includes debug/performance counter registers which we don't care about at the moment. 68 | _reserved1: [u32; 1002], 69 | } 70 | 71 | 72 | pub fn _assert_register_layout() { 73 | assert_eq!(core::mem::size_of::(), 4096) 74 | } -------------------------------------------------------------------------------- /src/drivers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod iommu; -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum VmmError { 3 | NotSupported, 4 | NoFound, 5 | Unimplemented, 6 | TranslationError, 7 | DeviceNotFound, 8 | PseudoInst, 9 | DecodeInstError, 10 | UnexpectedInst 11 | } 12 | 13 | pub type VmmResult = Result; -------------------------------------------------------------------------------- /src/guest/context.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::riscv_regs::{ GeneralPurposeRegisters, GprIndex }; 2 | use memoffset::offset_of; 3 | use core::mem::size_of; 4 | use core::arch::global_asm; 5 | 6 | use riscv::register::{ 7 | sstatus::{self, Sstatus, SPP }, 8 | hstatus::{self, Hstatus } 9 | }; 10 | 11 | #[repr(C)] 12 | #[derive(Debug)] 13 | /// trap context structure containing sstatus, sepc and registers 14 | pub struct TrapContext { 15 | /// general regs[0..31] 16 | pub x: [usize; 32], 17 | /// CSR sstatus 18 | pub sstatus: Sstatus, 19 | /// CSR sepc 20 | pub sepc: usize, 21 | /// Addr of Page Table 22 | pub hgatp: usize, 23 | /// kernel stack 24 | pub kernel_sp: usize, 25 | /// Addr of trap_handler function 26 | pub trap_handler: usize, 27 | /// CSR hstatus 28 | pub hstatus: Hstatus 29 | } 30 | 31 | impl TrapContext { 32 | /// set stack pointer to x_2 reg (sp) 33 | pub fn set_sp(&mut self, sp: usize) { 34 | self.x[2] = sp; 35 | } 36 | 37 | /// init guest trap context 38 | pub fn initialize_context( 39 | entry: usize, 40 | sp: usize, 41 | hgatp: usize, 42 | kernel_sp: usize, 43 | trap_handler: usize, 44 | ) -> Self { 45 | let mut sstatus = sstatus::read(); 46 | // 这里需要注意,进入 VS 态的时候需要将 sstatus 的 SPP 设置为 Supervisor 47 | sstatus.set_spp(SPP::Supervisor); 48 | let mut hstatus = hstatus::read(); 49 | hstatus.set_spv(true); 50 | let mut cx = Self { 51 | x: [0; 32], 52 | sstatus, 53 | sepc: entry, // entry point of app 54 | hgatp, // addr of page table 55 | kernel_sp, // kernel stack 56 | trap_handler, // addr of trap_handler function 57 | hstatus 58 | }; 59 | cx.set_sp(sp); // app's user stack pointer 60 | cx // return initial Trap Context of app 61 | } 62 | } 63 | 64 | /// Hypervisor GPR and CSR state which must be saved/restored when entering/exiting virtualization. 65 | #[derive(Default)] 66 | #[repr(C)] 67 | struct HypervisorCpuState { 68 | gprs: GeneralPurposeRegisters, 69 | sstatus: u64, 70 | hstatus: u64, 71 | scounteren: u64, 72 | stvec: u64, 73 | sscratch: u64, 74 | } 75 | 76 | /// Guest GPR and CSR state which must be saved/restored when exiting/entering virtualization. 77 | #[derive(Default)] 78 | #[repr(C)] 79 | struct GuestCpuState { 80 | gprs: GeneralPurposeRegisters, 81 | sstatus: u64, 82 | hstatus: u64, 83 | scounteren: u64, 84 | sepc: u64, 85 | } 86 | 87 | /// The CSRs that are only in effect when virtualization is enabled (V=1) and must be saved and 88 | /// restored whenever we switch between VMs. 89 | #[derive(Default)] 90 | #[repr(C)] 91 | pub struct GuestVsCsrs { 92 | htimedelta: u64, 93 | vsstatus: u64, 94 | vsie: u64, 95 | vstvec: u64, 96 | vsscratch: u64, 97 | vsepc: u64, 98 | vscause: u64, 99 | vstval: u64, 100 | vsatp: u64, 101 | vstimecmp: u64, 102 | } 103 | 104 | /// Virtualized HS-level CSRs that are used to emulate (part of) the hypervisor extension for the 105 | /// guest. 106 | #[derive(Default)] 107 | #[repr(C)] 108 | pub struct GuestVirtualHsCsrs { 109 | hie: u64, 110 | hgeie: u64, 111 | hgatp: u64, 112 | } 113 | 114 | /// CSRs written on an exit from virtualization that are used by the hypervisor to determine the cause 115 | /// of the trap. 116 | #[derive(Default, Clone)] 117 | #[repr(C)] 118 | pub struct VmCpuTrapState { 119 | pub scause: u64, 120 | pub stval: u64, 121 | pub htval: u64, 122 | pub htinst: u64, 123 | } 124 | 125 | /// (v)CPU register state that must be saved or restored when entering/exiting a VM or switching 126 | /// between VMs. 127 | #[derive(Default)] 128 | #[repr(C)] 129 | struct VmCpuRegisters { 130 | // CPU state that's shared between our's and the guest's execution environment. Saved/restored 131 | // when entering/exiting a VM. 132 | hyp_regs: HypervisorCpuState, 133 | guest_regs: GuestCpuState, 134 | 135 | // CPU state that only applies when V=1, e.g. the VS-level CSRs. Saved/restored on activation of 136 | // the vCPU. 137 | vs_csrs: GuestVsCsrs, 138 | 139 | // Virtualized HS-level CPU state. 140 | virtual_hs_csrs: GuestVirtualHsCsrs, 141 | 142 | // Read on VM exit. 143 | trap_csrs: VmCpuTrapState, 144 | } 145 | 146 | // The vCPU context switch, defined in guest.S 147 | extern "C" { 148 | fn _run_guest(state: *mut VmCpuRegisters); 149 | } 150 | 151 | #[allow(dead_code)] 152 | const fn hyp_gpr_offset(index: GprIndex) -> usize { 153 | offset_of!(VmCpuRegisters, hyp_regs) 154 | + offset_of!(HypervisorCpuState, gprs) 155 | + (index as usize) * size_of::() 156 | } 157 | 158 | #[allow(dead_code)] 159 | const fn guest_gpr_offset(index: GprIndex) -> usize { 160 | offset_of!(VmCpuRegisters, guest_regs) 161 | + offset_of!(GuestCpuState, gprs) 162 | + (index as usize) * size_of::() 163 | } 164 | 165 | macro_rules! hyp_csr_offset { 166 | ($reg:tt) => { 167 | offset_of!(VmCpuRegisters, hyp_regs) + offset_of!(HypervisorCpuState, $reg) 168 | }; 169 | } 170 | 171 | macro_rules! guest_csr_offset { 172 | ($reg:tt) => { 173 | offset_of!(VmCpuRegisters, guest_regs) + offset_of!(GuestCpuState, $reg) 174 | }; 175 | } 176 | 177 | global_asm!( 178 | include_str!("guest.S"), 179 | hyp_ra = const hyp_gpr_offset(GprIndex::RA), 180 | hyp_gp = const hyp_gpr_offset(GprIndex::GP), 181 | hyp_tp = const hyp_gpr_offset(GprIndex::TP), 182 | hyp_s0 = const hyp_gpr_offset(GprIndex::S0), 183 | hyp_s1 = const hyp_gpr_offset(GprIndex::S1), 184 | hyp_a1 = const hyp_gpr_offset(GprIndex::A1), 185 | hyp_a2 = const hyp_gpr_offset(GprIndex::A2), 186 | hyp_a3 = const hyp_gpr_offset(GprIndex::A3), 187 | hyp_a4 = const hyp_gpr_offset(GprIndex::A4), 188 | hyp_a5 = const hyp_gpr_offset(GprIndex::A5), 189 | hyp_a6 = const hyp_gpr_offset(GprIndex::A6), 190 | hyp_a7 = const hyp_gpr_offset(GprIndex::A7), 191 | hyp_s2 = const hyp_gpr_offset(GprIndex::S2), 192 | hyp_s3 = const hyp_gpr_offset(GprIndex::S3), 193 | hyp_s4 = const hyp_gpr_offset(GprIndex::S4), 194 | hyp_s5 = const hyp_gpr_offset(GprIndex::S5), 195 | hyp_s6 = const hyp_gpr_offset(GprIndex::S6), 196 | hyp_s7 = const hyp_gpr_offset(GprIndex::S7), 197 | hyp_s8 = const hyp_gpr_offset(GprIndex::S8), 198 | hyp_s9 = const hyp_gpr_offset(GprIndex::S9), 199 | hyp_s10 = const hyp_gpr_offset(GprIndex::S10), 200 | hyp_s11 = const hyp_gpr_offset(GprIndex::S11), 201 | hyp_sp = const hyp_gpr_offset(GprIndex::SP), 202 | hyp_sstatus = const hyp_csr_offset!(sstatus), 203 | hyp_hstatus = const hyp_csr_offset!(hstatus), 204 | hyp_scounteren = const hyp_csr_offset!(scounteren), 205 | hyp_stvec = const hyp_csr_offset!(stvec), 206 | hyp_sscratch = const hyp_csr_offset!(sscratch), 207 | guest_ra = const guest_gpr_offset(GprIndex::RA), 208 | guest_gp = const guest_gpr_offset(GprIndex::GP), 209 | guest_tp = const guest_gpr_offset(GprIndex::TP), 210 | guest_s0 = const guest_gpr_offset(GprIndex::S0), 211 | guest_s1 = const guest_gpr_offset(GprIndex::S1), 212 | guest_a0 = const guest_gpr_offset(GprIndex::A0), 213 | guest_a1 = const guest_gpr_offset(GprIndex::A1), 214 | guest_a2 = const guest_gpr_offset(GprIndex::A2), 215 | guest_a3 = const guest_gpr_offset(GprIndex::A3), 216 | guest_a4 = const guest_gpr_offset(GprIndex::A4), 217 | guest_a5 = const guest_gpr_offset(GprIndex::A5), 218 | guest_a6 = const guest_gpr_offset(GprIndex::A6), 219 | guest_a7 = const guest_gpr_offset(GprIndex::A7), 220 | guest_s2 = const guest_gpr_offset(GprIndex::S2), 221 | guest_s3 = const guest_gpr_offset(GprIndex::S3), 222 | guest_s4 = const guest_gpr_offset(GprIndex::S4), 223 | guest_s5 = const guest_gpr_offset(GprIndex::S5), 224 | guest_s6 = const guest_gpr_offset(GprIndex::S6), 225 | guest_s7 = const guest_gpr_offset(GprIndex::S7), 226 | guest_s8 = const guest_gpr_offset(GprIndex::S8), 227 | guest_s9 = const guest_gpr_offset(GprIndex::S9), 228 | guest_s10 = const guest_gpr_offset(GprIndex::S10), 229 | guest_s11 = const guest_gpr_offset(GprIndex::S11), 230 | guest_t0 = const guest_gpr_offset(GprIndex::T0), 231 | guest_t1 = const guest_gpr_offset(GprIndex::T1), 232 | guest_t2 = const guest_gpr_offset(GprIndex::T2), 233 | guest_t3 = const guest_gpr_offset(GprIndex::T3), 234 | guest_t4 = const guest_gpr_offset(GprIndex::T4), 235 | guest_t5 = const guest_gpr_offset(GprIndex::T5), 236 | guest_t6 = const guest_gpr_offset(GprIndex::T6), 237 | guest_sp = const guest_gpr_offset(GprIndex::SP), 238 | 239 | guest_sstatus = const guest_csr_offset!(sstatus), 240 | guest_hstatus = const guest_csr_offset!(hstatus), 241 | guest_scounteren = const guest_csr_offset!(scounteren), 242 | guest_sepc = const guest_csr_offset!(sepc), 243 | 244 | ); 245 | 246 | -------------------------------------------------------------------------------- /src/guest/guest.S: -------------------------------------------------------------------------------- 1 | 2 | 3 | .section .text.trampoline 4 | .global _run_guest 5 | _run_guest: 6 | /* Save hypervisor state */ 7 | 8 | /* Save hypervisor GPRs (except T0-T6 and a0, which is GuestInfo and stashed in sscratch) */ 9 | sd ra, ({hyp_ra})(a0) 10 | sd gp, ({hyp_gp})(a0) 11 | sd tp, ({hyp_tp})(a0) 12 | sd s0, ({hyp_s0})(a0) 13 | sd s1, ({hyp_s1})(a0) 14 | sd a1, ({hyp_a1})(a0) 15 | sd a2, ({hyp_a2})(a0) 16 | sd a3, ({hyp_a3})(a0) 17 | sd a4, ({hyp_a4})(a0) 18 | sd a5, ({hyp_a5})(a0) 19 | sd a6, ({hyp_a6})(a0) 20 | sd a7, ({hyp_a7})(a0) 21 | sd s2, ({hyp_s2})(a0) 22 | sd s3, ({hyp_s3})(a0) 23 | sd s4, ({hyp_s4})(a0) 24 | sd s5, ({hyp_s5})(a0) 25 | sd s6, ({hyp_s6})(a0) 26 | sd s7, ({hyp_s7})(a0) 27 | sd s8, ({hyp_s8})(a0) 28 | sd s9, ({hyp_s9})(a0) 29 | sd s10, ({hyp_s10})(a0) 30 | sd s11, ({hyp_s11})(a0) 31 | sd sp, ({hyp_sp})(a0) 32 | 33 | /* Swap in guest CSRs. */ 34 | ld t1, ({guest_sstatus})(a0) 35 | csrrw t1, sstatus, t1 36 | sd t1, ({hyp_sstatus})(a0) 37 | 38 | ld t1, ({guest_hstatus})(a0) 39 | csrrw t1, hstatus, t1 40 | sd t1, ({hyp_hstatus})(a0) 41 | 42 | ld t1, ({guest_scounteren})(a0) 43 | csrrw t1, scounteren, t1 44 | sd t1, ({hyp_scounteren})(a0) 45 | 46 | ld t1, ({guest_sepc})(a0) 47 | csrw sepc, t1 48 | 49 | /* Set stvec so that hypervisor resumes after the sret when the guest exits. */ 50 | la t1, _guest_exit 51 | csrrw t1, stvec, t1 52 | sd t1, ({hyp_stvec})(a0) 53 | 54 | /* Save sscratch and replace with pointer to GuestInfo. */ 55 | csrrw t1, sscratch, a0 56 | sd t1, ({hyp_sscratch})(a0) 57 | 58 | /* Restore the gprs from this GuestInfo */ 59 | ld ra, ({guest_ra})(a0) 60 | ld gp, ({guest_gp})(a0) 61 | ld tp, ({guest_tp})(a0) 62 | ld s0, ({guest_s0})(a0) 63 | ld s1, ({guest_s1})(a0) 64 | ld a1, ({guest_a1})(a0) 65 | ld a2, ({guest_a2})(a0) 66 | ld a3, ({guest_a3})(a0) 67 | ld a4, ({guest_a4})(a0) 68 | ld a5, ({guest_a5})(a0) 69 | ld a6, ({guest_a6})(a0) 70 | ld a7, ({guest_a7})(a0) 71 | ld s2, ({guest_s2})(a0) 72 | ld s3, ({guest_s3})(a0) 73 | ld s4, ({guest_s4})(a0) 74 | ld s5, ({guest_s5})(a0) 75 | ld s6, ({guest_s6})(a0) 76 | ld s7, ({guest_s7})(a0) 77 | ld s8, ({guest_s8})(a0) 78 | ld s9, ({guest_s9})(a0) 79 | ld s10, ({guest_s10})(a0) 80 | ld s11, ({guest_s11})(a0) 81 | ld t0, ({guest_t0})(a0) 82 | ld t1, ({guest_t1})(a0) 83 | ld t2, ({guest_t2})(a0) 84 | ld t3, ({guest_t3})(a0) 85 | ld t4, ({guest_t4})(a0) 86 | ld t5, ({guest_t5})(a0) 87 | ld t6, ({guest_t6})(a0) 88 | ld sp, ({guest_sp})(a0) 89 | ld a0, ({guest_a0})(a0) 90 | 91 | sret 92 | 93 | .align 2 94 | _guest_exit: 95 | /* Pull GuestInfo out of sscratch, swapping with guest's a0 */ 96 | csrrw a0, sscratch, a0 97 | 98 | /* Save guest GPRs. */ 99 | sd ra, ({guest_ra})(a0) 100 | sd gp, ({guest_gp})(a0) 101 | sd tp, ({guest_tp})(a0) 102 | sd s0, ({guest_s0})(a0) 103 | sd s1, ({guest_s1})(a0) 104 | sd a1, ({guest_a1})(a0) 105 | sd a2, ({guest_a2})(a0) 106 | sd a3, ({guest_a3})(a0) 107 | sd a4, ({guest_a4})(a0) 108 | sd a5, ({guest_a5})(a0) 109 | sd a6, ({guest_a6})(a0) 110 | sd a7, ({guest_a7})(a0) 111 | sd s2, ({guest_s2})(a0) 112 | sd s3, ({guest_s3})(a0) 113 | sd s4, ({guest_s4})(a0) 114 | sd s5, ({guest_s5})(a0) 115 | sd s6, ({guest_s6})(a0) 116 | sd s7, ({guest_s7})(a0) 117 | sd s8, ({guest_s8})(a0) 118 | sd s9, ({guest_s9})(a0) 119 | sd s10, ({guest_s10})(a0) 120 | sd s11, ({guest_s11})(a0) 121 | sd t0, ({guest_t0})(a0) 122 | sd t1, ({guest_t1})(a0) 123 | sd t2, ({guest_t2})(a0) 124 | sd t3, ({guest_t3})(a0) 125 | sd t4, ({guest_t4})(a0) 126 | sd t5, ({guest_t5})(a0) 127 | sd t6, ({guest_t6})(a0) 128 | sd sp, ({guest_sp})(a0) 129 | 130 | /* Save Guest a0 after recovering from sscratch. */ 131 | csrr t0, sscratch 132 | sd t0, ({guest_a0})(a0) 133 | 134 | _restore_csrs: 135 | /* Swap in hypervisor CSRs. */ 136 | ld t1, ({hyp_sstatus})(a0) 137 | csrrw t1, sstatus, t1 138 | sd t1, ({guest_sstatus})(a0) 139 | 140 | ld t1, ({hyp_hstatus})(a0) 141 | csrrw t1, hstatus, t1 142 | sd t1, ({guest_hstatus})(a0) 143 | 144 | ld t1, ({hyp_scounteren})(a0) 145 | csrrw t1, scounteren, t1 146 | sd t1, ({guest_scounteren})(a0) 147 | 148 | ld t1, ({hyp_stvec})(a0) 149 | csrw stvec, t1 150 | 151 | ld t1, ({hyp_sscratch})(a0) 152 | csrw sscratch, t1 153 | 154 | /* Save guest EPC. */ 155 | csrr t1, sepc 156 | sd t1, ({guest_sepc})(a0) 157 | 158 | /* Restore hypervisor GPRs. */ 159 | ld ra, ({hyp_ra})(a0) 160 | ld gp, ({hyp_gp})(a0) 161 | ld tp, ({hyp_tp})(a0) 162 | ld s0, ({hyp_s0})(a0) 163 | ld s1, ({hyp_s1})(a0) 164 | ld a1, ({hyp_a1})(a0) 165 | ld a2, ({hyp_a2})(a0) 166 | ld a3, ({hyp_a3})(a0) 167 | ld a4, ({hyp_a4})(a0) 168 | ld a5, ({hyp_a5})(a0) 169 | ld a6, ({hyp_a6})(a0) 170 | ld a7, ({hyp_a7})(a0) 171 | ld s2, ({hyp_s2})(a0) 172 | ld s3, ({hyp_s3})(a0) 173 | ld s4, ({hyp_s4})(a0) 174 | ld s5, ({hyp_s5})(a0) 175 | ld s6, ({hyp_s6})(a0) 176 | ld s7, ({hyp_s7})(a0) 177 | ld s8, ({hyp_s8})(a0) 178 | ld s9, ({hyp_s9})(a0) 179 | ld s10, ({hyp_s10})(a0) 180 | ld s11, ({hyp_s11})(a0) 181 | ld sp, ({hyp_sp})(a0) 182 | 183 | ret 184 | 185 | -------------------------------------------------------------------------------- /src/guest/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::layout::{TRAP_CONTEXT, GUEST_START_VA}; 2 | use crate::hypervisor::fdt::MachineMeta; 3 | use crate::mm::{ GuestMemorySet, MemorySet }; 4 | use crate::hypervisor::{ stack::hstack_alloc}; 5 | use vmexit::{TrapContext, trap_handler}; 6 | 7 | use self::page_table::GuestPageTable; 8 | use self::vcpu::VCpu; 9 | pub use sbi::SbiRet; 10 | 11 | mod context; 12 | mod vcpu; 13 | mod sbi; 14 | pub mod vmexit; 15 | 16 | 17 | pub struct Guest { 18 | pub guest_machine: MachineMeta, 19 | /// guest memory set 20 | pub gpm: GuestMemorySet, 21 | /// guest id 22 | pub guest_id: usize, 23 | /// virtual cpu status 24 | pub vcpu: VCpu 25 | } 26 | 27 | impl Guest { 28 | pub fn new(guest_id: usize, gpm: GuestMemorySet, guest_machine: MachineMeta) -> Self { 29 | // 分配 hypervisor 内核栈 30 | let hstack = hstack_alloc(guest_id); 31 | let hstack_top = hstack.get_top(); 32 | // 获取 trap context 33 | let trap_ctx: &mut TrapContext = unsafe{ (TRAP_CONTEXT as *mut TrapContext).as_mut().unwrap() }; 34 | // 初始化 trap context 的环境 35 | // 包括入口地址/栈寄存器/satp/内核栈寄存器/trap处理地址 36 | *trap_ctx = TrapContext::initialize_context( 37 | GUEST_START_VA, 38 | 0, 39 | gpm.token(), 40 | hstack_top, 41 | trap_handler as usize 42 | ); 43 | Self { 44 | guest_id, 45 | gpm, 46 | guest_machine, 47 | vcpu: VCpu::new(guest_id), 48 | } 49 | } 50 | 51 | 52 | pub fn run(&mut self) { 53 | todo!() 54 | } 55 | } 56 | 57 | 58 | pub mod page_table { 59 | use crate::page_table::PageTable; 60 | 61 | pub trait GuestPageTable: PageTable { 62 | fn new_guest() -> Self; 63 | } 64 | } 65 | 66 | pub mod pmap { 67 | use riscv_decode::Instruction; 68 | 69 | use crate::{mm::{MemorySet, GuestMemorySet}, page_table::translate_guest_va}; 70 | use super::page_table::GuestPageTable; 71 | // use riscv_decode; 72 | 73 | #[allow(unused)] 74 | mod segment_layout { 75 | pub const GUEST_SEGMENT_SIZE: usize = 128 * 1024 * 1024; 76 | } 77 | 78 | pub fn gpa2hpa(va: usize, guest_id: usize) -> usize { 79 | va + guest_id * segment_layout::GUEST_SEGMENT_SIZE 80 | } 81 | 82 | pub fn hpa2gpa(pa: usize, guest_id: usize) -> usize { 83 | pa - guest_id * segment_layout::GUEST_SEGMENT_SIZE 84 | } 85 | 86 | pub fn two_stage_translation(guest_id: usize, guest_va: usize, vsatp: usize, gpm: &GuestMemorySet) -> Option { 87 | let guest_root = (vsatp & 0x3ff_ffff_ffff) << 12; 88 | let guest_pa; 89 | if guest_root != 0 { 90 | if let Some(translation) = translate_guest_va::(guest_id, guest_root, guest_va) { 91 | guest_pa = translation.guest_pa; 92 | // htracking!("guest pa: {:#x}", guest_pa); 93 | }else{ 94 | return None 95 | } 96 | }else{ 97 | guest_pa = guest_va; 98 | } 99 | if let Some(host_va) = gpm.translate_va(guest_pa) { 100 | Some(host_va) 101 | }else{ 102 | return None 103 | } 104 | 105 | } 106 | 107 | pub fn fast_two_stage_translation(guest_id: usize, guest_va: usize, vsatp: usize) -> Option { 108 | let guest_root = (vsatp & 0x3ff_ffff_ffff) << 12; 109 | let guest_pa; 110 | if guest_root != 0 { 111 | if let Some(translation) = translate_guest_va::(guest_id, guest_root, guest_va) { 112 | guest_pa = translation.guest_pa; 113 | // htracking!("guest pa: {:#x}", guest_pa); 114 | }else{ 115 | return None 116 | } 117 | }else{ 118 | guest_pa = guest_va; 119 | } 120 | Some(guest_pa) 121 | 122 | } 123 | 124 | 125 | pub fn decode_inst_at_addr(host_va: usize) -> (usize, Option) { 126 | let i1 = unsafe{ core::ptr::read(host_va as *const u16) }; 127 | let len = riscv_decode::instruction_length(i1); 128 | let inst = match len { 129 | 2 => i1 as u32, 130 | 4 => unsafe{ core::ptr::read(host_va as *const u32) }, 131 | _ => unreachable!() 132 | }; 133 | (len, riscv_decode::decode(inst).ok()) 134 | } 135 | 136 | /// decode risc-v instruction, return (inst len, inst) 137 | pub fn decode_inst(inst: usize) -> (usize, Option) { 138 | let i1 = inst as u16; 139 | let len = riscv_decode::instruction_length(i1); 140 | let inst = match len { 141 | 2 => i1 as u32, 142 | 4 => inst as u32, 143 | _ => unreachable!() 144 | }; 145 | (len, riscv_decode::decode(inst).ok()) 146 | } 147 | } 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /src/guest/sbi.rs: -------------------------------------------------------------------------------- 1 | use super::vmexit::TrapContext; 2 | use crate::constants::riscv_regs::GprIndex; 3 | use crate::sbi::leagcy::SBI_SET_TIMER; 4 | use crate::sbi::{ 5 | console_getchar, console_putchar, set_timer, SBI_CONSOLE_GETCHAR, SBI_CONSOLE_PUTCHAR, 6 | SBI_ERR_NOT_SUPPORTED, SBI_EXTID_BASE, SBI_EXTID_TIME, SBI_GET_MARCHID_FID, SBI_GET_MIMPID_FID, 7 | SBI_GET_MVENDORID_FID, SBI_GET_SBI_IMPL_ID_FID, SBI_GET_SBI_IMPL_VERSION_FID, 8 | SBI_GET_SBI_SPEC_VERSION_FID, SBI_PROBE_EXTENSION_FID, SBI_SET_TIMER_FID, SBI_SUCCESS, 9 | }; 10 | use crate::VmmResult; 11 | use sbi_rt; 12 | 13 | use riscv::register::{hvip, sie}; 14 | pub struct SbiRet { 15 | error: usize, 16 | value: usize, 17 | } 18 | 19 | #[inline(always)] 20 | pub(crate) fn sbi_call_1(eid: usize, fid: usize, arg0: usize) -> SbiRet { 21 | let (error, value); 22 | unsafe { 23 | core::arch::asm!( 24 | "ecall", 25 | in("a7") eid, 26 | in("a6") fid, 27 | inlateout("a0") arg0 => error, 28 | lateout("a1") value, 29 | ); 30 | } 31 | SbiRet { error, value } 32 | } 33 | 34 | pub fn sbi_vs_handler(ctx: &mut TrapContext) -> VmmResult { 35 | let ext_id: usize = ctx.x[GprIndex::A7 as usize]; 36 | let fid: usize = ctx.x[GprIndex::A6 as usize]; 37 | let sbi_ret; 38 | 39 | match ext_id { 40 | SBI_EXTID_BASE => sbi_ret = sbi_base_handler(fid, ctx), 41 | SBI_EXTID_TIME => sbi_ret = sbi_time_handler(ctx.x[GprIndex::A0 as usize], fid), 42 | SBI_CONSOLE_PUTCHAR => sbi_ret = sbi_console_putchar_handler(ctx.x[GprIndex::A0 as usize]), 43 | SBI_CONSOLE_GETCHAR => sbi_ret = sbi_console_getchar_handler(), 44 | SBI_SET_TIMER => sbi_ret = sbi_legacy_set_time(ctx.x[GprIndex::A0 as usize]), 45 | _ => panic!("Unsupported SBI call id {:#x}", ext_id), 46 | } 47 | ctx.x[GprIndex::A0 as usize] = sbi_ret.error; 48 | ctx.x[GprIndex::A1 as usize] = sbi_ret.value; 49 | 50 | Ok(()) 51 | } 52 | 53 | pub fn sbi_base_handler(fid: usize, ctx: &TrapContext) -> SbiRet { 54 | let mut sbi_ret = SbiRet { 55 | error: SBI_SUCCESS, 56 | value: 0, 57 | }; 58 | match fid { 59 | SBI_GET_SBI_SPEC_VERSION_FID => { 60 | sbi_ret = sbi_call_1(SBI_EXTID_BASE, fid, 0); 61 | htracking!("GetSepcificationVersion: {}", sbi_ret.value); 62 | } 63 | SBI_GET_SBI_IMPL_ID_FID => { 64 | sbi_ret.value = sbi_rt::get_sbi_impl_id(); 65 | htracking!("GetImplementationId: {}", sbi_ret.value); 66 | } 67 | SBI_GET_SBI_IMPL_VERSION_FID => { 68 | sbi_ret.value = sbi_rt::get_sbi_impl_version(); 69 | htracking!("GetImplementationVersion: {}", sbi_ret.value); 70 | } 71 | SBI_PROBE_EXTENSION_FID => { 72 | let extension = ctx.x[GprIndex::A0 as usize]; 73 | sbi_ret = sbi_call_1(SBI_EXTID_BASE, fid, extension); 74 | htracking!("ProbeExtension: {}", sbi_ret.value); 75 | } 76 | SBI_GET_MVENDORID_FID => { 77 | sbi_ret.value = sbi_rt::get_mvendorid(); 78 | htracking!("GetVendorId: {}", sbi_ret.value); 79 | } 80 | SBI_GET_MARCHID_FID => { 81 | sbi_ret.value = sbi_rt::get_marchid(); 82 | htracking!("GetArchId: {}", sbi_ret.value); 83 | } 84 | SBI_GET_MIMPID_FID => { 85 | sbi_ret.value = sbi_rt::get_mimpid(); 86 | htracking!("GetMimpId: {}", sbi_ret.value); 87 | } 88 | _ => panic!("sbi base handler fid: {}", fid), 89 | } 90 | sbi_ret 91 | } 92 | 93 | pub fn sbi_console_putchar_handler(c: usize) -> SbiRet { 94 | console_putchar(c); 95 | return SbiRet { 96 | error: SBI_SUCCESS, 97 | value: 0, 98 | }; 99 | } 100 | 101 | pub fn sbi_console_getchar_handler() -> SbiRet { 102 | let c = console_getchar(); 103 | return SbiRet { 104 | error: SBI_SUCCESS, 105 | value: c, 106 | }; 107 | } 108 | 109 | pub fn sbi_time_handler(stime: usize, fid: usize) -> SbiRet { 110 | let mut sbi_ret = SbiRet { 111 | error: SBI_SUCCESS, 112 | value: 0, 113 | }; 114 | if fid != SBI_SET_TIMER_FID { 115 | sbi_ret.error = SBI_ERR_NOT_SUPPORTED as usize; 116 | return sbi_ret; 117 | } 118 | 119 | // htracking!("set timer: {}", stime); 120 | set_timer(stime); 121 | unsafe { 122 | // clear guest timer interrupt pending 123 | hvip::clear_vstip(); 124 | // enable timer interrupt 125 | sie::set_stimer(); 126 | } 127 | return sbi_ret; 128 | } 129 | 130 | // pub fn sbi_rfence_handler(fid: usize) { 131 | 132 | // } 133 | 134 | pub fn sbi_legacy_set_time(stime: usize) -> SbiRet { 135 | let sbi_ret = SbiRet { 136 | error: SBI_SUCCESS, 137 | value: 0, 138 | }; 139 | set_timer(stime); 140 | unsafe { 141 | // clear guest timer interrupt pending 142 | hvip::clear_vstip(); 143 | // enable timer interrupt 144 | sie::set_stimer(); 145 | } 146 | return sbi_ret; 147 | } 148 | -------------------------------------------------------------------------------- /src/guest/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 | 15 | __alltraps: 16 | # 交换 sp 和 sscratch 寄存器,此时 sp 寄存器是 TrapContext 的地址, 17 | # sp 是 guest 地址 18 | csrrw sp, sscratch, sp 19 | sd x1, 1*8(sp) 20 | # skip sp(x2), we will save it later 21 | sd x3, 3*8(sp) 22 | # skip tp(x4), application does not use it 23 | # save x5~x31 24 | .set n, 5 25 | .rept 27 26 | SAVE_GP %n 27 | .set n, n+1 28 | .endr 29 | # we can use t0/t1/t2 freely, because they have been saved in TrapContext 30 | csrr t0, sstatus 31 | csrr t1, sepc 32 | # 将 sstatus 和 sepc 存储在 32*8(trap ctx) 和 33*8(trap ctx) 的位置 33 | sd t0, 32*8(sp) 34 | sd t1, 33*8(sp) 35 | # 将 guest stack 寄存器保存 36 | csrr t2, sscratch 37 | sd t2, 2*8(sp) 38 | # 加载 hgatp 到 t0 寄存器 39 | ld t0, 34*8(sp) 40 | # 加载 hypervisor trap handler 地址到 t1 寄存器 41 | ld t1, 36*8(sp) 42 | # 存储 hstatus 寄存器 43 | csrr t0, hstatus 44 | sd t0, 37*8(sp) 45 | # 切换栈寄存器 46 | ld sp, 35*8(sp) 47 | # 由 VS guest 跳转到 HS hypervisor, 不需要切换页表 48 | # 跳转到 trap 处理函数 49 | jr t1 50 | 51 | # a0: trap context addr 52 | __restore: 53 | # 注意,此时不需要进行页表切换,此时仍在 HS 态,使用 satp 进行 MMU 地址翻译 54 | # 将 sscratch 和 sp 全部写入 a0(Trap Context) 55 | csrw sscratch, a0 56 | mv sp, a0 57 | # now sp points to TrapContext in user space, start restoring based on it 58 | # restore sstatus/sepc 59 | ld t0, 32*8(sp) 60 | ld t1, 33*8(sp) 61 | csrw sstatus, t0 62 | csrw sepc, t1 63 | # 恢复 hstatus 寄存器 64 | ld t0, 37*8(sp) 65 | csrw hstatus, t0 66 | # restore general purpose registers except x0/sp/tp 67 | ld x1, 1*8(sp) 68 | ld x3, 3*8(sp) 69 | .set n, 5 70 | .rept 27 71 | LOAD_GP %n 72 | .set n, n+1 73 | .endr 74 | # back to user stack 75 | ld sp, 2*8(sp) 76 | sret 77 | 78 | .align 2 79 | __alltraps_k: 80 | addi sp, sp, -34*8 81 | sd x1, 1*8(sp) 82 | sd x3, 3*8(sp) 83 | .set n, 5 84 | .rept 27 85 | SAVE_GP %n 86 | .set n, n+1 87 | .endr 88 | csrr t0, sstatus 89 | csrr t1, sepc 90 | sd t0, 32*8(sp) 91 | sd t1, 33*8(sp) 92 | mv a0, sp 93 | csrr t2, sscratch 94 | jalr t2 95 | 96 | __restore_k: 97 | ld t0, 32*8(sp) 98 | ld t1, 33*8(sp) 99 | csrw sstatus, t0 100 | csrw sepc, t1 101 | ld x1, 1*8(sp) 102 | ld x3, 3*8(sp) 103 | .set n, 5 104 | .rept 27 105 | LOAD_GP %n 106 | .set n, n+1 107 | .endr 108 | addi sp, sp, 34*8 109 | sret -------------------------------------------------------------------------------- /src/guest/vcpu.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::VecDeque; 2 | 3 | pub struct VCpu { 4 | pub hart: usize, 5 | /// pending interrupts 6 | pub pending_events: VecDeque 7 | } 8 | 9 | impl VCpu { 10 | pub fn new(hart: usize) -> Self { 11 | Self{ 12 | hart, 13 | pending_events: VecDeque::new() 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/guest/vmexit.rs: -------------------------------------------------------------------------------- 1 | use core::arch::{asm, global_asm}; 2 | 3 | use crate::constants::layout::{GUEST_DTB_ADDR, TRAMPOLINE, TRAP_CONTEXT}; 4 | use crate::device_emu::plic::is_plic_access; 5 | use crate::guest::page_table::GuestPageTable; 6 | use crate::guest::pmap::{decode_inst, two_stage_translation}; 7 | use crate::hypervisor::{HostVmm, HOST_VMM}; 8 | use crate::page_table::{PageTable, PageTableSv39}; 9 | use crate::{VmmError, VmmResult}; 10 | 11 | use riscv::register::scause::{Exception, Interrupt, Trap}; 12 | use riscv::register::{ 13 | hgatp, htinst, htval, hvip, scause, sepc, sie, sscratch, stval, stvec, vsatp, vstvec, 14 | }; 15 | 16 | pub use super::context::TrapContext; 17 | use super::pmap::fast_two_stage_translation; 18 | use super::sbi::sbi_vs_handler; 19 | 20 | global_asm!(include_str!("trap.S")); 21 | 22 | /// initialize CSR `stvec` as the entry of `__alltraps` 23 | pub fn trap_init() { 24 | set_kernel_trap_entry(); 25 | } 26 | 27 | /// enable timer interrupt in sie CSR 28 | pub fn enable_timer_interrupt() { 29 | unsafe { 30 | sie::set_stimer(); 31 | } 32 | } 33 | 34 | pub fn disable_timer_interrupt() { 35 | unsafe { 36 | sie::clear_stimer(); 37 | } 38 | } 39 | 40 | fn set_kernel_trap_entry() { 41 | extern "C" { 42 | fn __alltraps(); 43 | fn __alltraps_k(); 44 | } 45 | let __alltraps_k_va = __alltraps_k as usize - __alltraps as usize + TRAMPOLINE; 46 | unsafe { 47 | stvec::write(__alltraps_k_va, stvec::TrapMode::Direct); 48 | sscratch::write(trap_from_kernel as usize); 49 | } 50 | } 51 | 52 | fn set_user_trap_entry() { 53 | unsafe { 54 | stvec::write(TRAMPOLINE as usize, stvec::TrapMode::Direct); 55 | } 56 | } 57 | 58 | fn privileged_inst_handler(_ctx: &mut TrapContext) -> VmmResult { 59 | todo!() 60 | } 61 | 62 | pub fn guest_page_fault_handler( 63 | host_vmm: &mut HostVmm, 64 | ctx: &mut TrapContext, 65 | ) -> VmmResult { 66 | let addr = htval::read() << 2; 67 | if is_plic_access(addr) { 68 | let mut inst = htinst::read(); 69 | if inst == 0 { 70 | // If htinst does not provide information about the trap, 71 | // we must read the instruction from guest's memory manually 72 | let inst_addr = ctx.sepc; 73 | // let gpm = &host_vmm.guests[host_vmm.guest_id].as_ref().unwrap().gpm; 74 | if let Some(host_inst_addr) = fast_two_stage_translation::( 75 | host_vmm.guest_id, 76 | inst_addr, 77 | vsatp::read().bits(), 78 | ) { 79 | inst = unsafe { core::ptr::read(host_inst_addr as *const usize) }; 80 | } else { 81 | herror!("inst addr: {:#x}", inst_addr); 82 | return Err(VmmError::TranslationError); 83 | } 84 | } else if inst == 0x3020 || inst == 0x3000 { 85 | // TODO: we should reinject this in the guest as a fault access 86 | herror!("fault on 1st stage page table walk"); 87 | return Err(VmmError::PseudoInst); 88 | } else { 89 | // If htinst is valid and is not a pseudo instructon make sure 90 | // the opcode is valid even if it was a compressed instruction, 91 | // but before save the real instruction size. 92 | } 93 | let (len, inst) = decode_inst(inst); 94 | if let Some(inst) = inst { 95 | // htracking!("inst: {:?}", inst); 96 | host_vmm.handle_plic_access(ctx, addr, inst)?; 97 | ctx.sepc += len; 98 | } else { 99 | return Err(VmmError::DecodeInstError); 100 | } 101 | Ok(()) 102 | } else { 103 | herror!("addr: {:#x}, sepc: {:#x}", addr, ctx.sepc); 104 | Err(VmmError::DeviceNotFound) 105 | // todo: handle other device 106 | } 107 | } 108 | 109 | /// handle interrupt request(current only external interrupt) 110 | pub fn handle_irq( 111 | host_vmm: &mut HostVmm, 112 | _ctx: &mut TrapContext, 113 | ) { 114 | // TODO: handle other irq 115 | // check external interrupt && handle 116 | let host_plic = host_vmm.host_plic.as_mut().unwrap(); 117 | // get current guest context id 118 | let context_id = 2 * host_vmm.guest_id + 1; 119 | let claim_and_complete_addr = host_plic.base_addr + 0x0020_0004 + 0x1000 * context_id; 120 | let irq = unsafe { core::ptr::read(claim_and_complete_addr as *const u32) }; 121 | host_plic.claim_complete[context_id] = irq; 122 | 123 | // set external interrupt pending, which trigger guest interrupt 124 | unsafe { hvip::set_vseip() }; 125 | 126 | // set irq pending in host vmm 127 | host_vmm.irq_pending = true; 128 | } 129 | 130 | /// forward exception by setting `vsepc` & `vscause` 131 | pub fn forward_exception(ctx: &mut TrapContext) { 132 | unsafe { 133 | asm!( 134 | "csrw vsepc, {sepc}", 135 | "csrw vscause, {scause}", 136 | sepc = in(reg) ctx.sepc, 137 | scause = in(reg) scause::read().bits() 138 | ) 139 | } 140 | ctx.sepc = vstvec::read().bits(); 141 | } 142 | 143 | pub fn handle_internal_vmm_error(err: VmmError) { 144 | panic!("err: {:?}", err); 145 | } 146 | 147 | #[no_mangle] 148 | #[allow(unreachable_code)] 149 | pub unsafe fn trap_handler() -> ! { 150 | set_kernel_trap_entry(); 151 | let ctx = (TRAP_CONTEXT as *mut TrapContext).as_mut().unwrap(); 152 | let scause = scause::read(); 153 | let host_vmm = HOST_VMM.get_mut().unwrap(); 154 | let mut host_vmm = host_vmm.lock(); 155 | let mut err = None; 156 | match scause.cause() { 157 | Trap::Exception(Exception::UserEnvCall) => { 158 | panic!("U-mode/VU-mode env call from VS-mode?"); 159 | } 160 | Trap::Exception(Exception::VirtualSupervisorEnvCall) => { 161 | if let Err(vmm_err) = sbi_vs_handler(ctx) { 162 | err = Some(vmm_err); 163 | } 164 | ctx.sepc += 4; 165 | } 166 | Trap::Exception(Exception::VirtualInstruction) => { 167 | if let Err(vmm_err) = privileged_inst_handler(ctx) { 168 | err = Some(vmm_err); 169 | } 170 | } 171 | Trap::Exception(Exception::InstructionGuestPageFault) => { 172 | let host_vmm = unsafe { HOST_VMM.get().unwrap().lock() }; 173 | let guest_id = host_vmm.guest_id; 174 | let gpm = &host_vmm.guests[guest_id].as_ref().unwrap().gpm; 175 | if let Some(host_va) = 176 | two_stage_translation(guest_id, ctx.sepc, vsatp::read().bits(), gpm) 177 | { 178 | herror!("host va: {:#x}", host_va); 179 | } else { 180 | herror!("Fail to translate exception pc."); 181 | } 182 | panic!( 183 | "InstructionGuestPageFault: sepc -> {:#x}, hgatp -> {:#x}", 184 | ctx.sepc, 185 | hgatp::read().bits() 186 | ); 187 | } 188 | Trap::Exception(Exception::LoadGuestPageFault) 189 | | Trap::Exception(Exception::StoreGuestPageFault) => { 190 | if let Err(vmm_err) = guest_page_fault_handler(&mut host_vmm, ctx) { 191 | err = Some(vmm_err); 192 | } 193 | host_vmm.guest_page_falut += 1; 194 | if host_vmm.guest_page_falut % 1000 == 0 { 195 | htracking!( 196 | "guest page fault: {}, addr: {:#x}", 197 | host_vmm.guest_page_falut, 198 | htval::read() << 2 199 | ); 200 | } 201 | } 202 | Trap::Interrupt(Interrupt::SupervisorExternal) => { 203 | handle_irq(&mut host_vmm, ctx); 204 | host_vmm.external_irq += 1; 205 | // htracking!("external irq: {}", host_vmm.external_irq); 206 | } 207 | Trap::Interrupt(Interrupt::SupervisorTimer) => { 208 | // set guest timer interrupt pending 209 | hvip::set_vstip(); 210 | // disable timer interrupt 211 | sie::clear_stimer(); 212 | host_vmm.timer_irq += 1; 213 | // if host_vmm.timer_irq % 1000 == 0 { 214 | // htracking!("timer irq: {}", host_vmm.timer_irq); 215 | // } 216 | } 217 | _ => forward_exception(ctx), 218 | } 219 | drop(host_vmm); 220 | if let Some(err) = err { 221 | // TODO: handler vmm error 222 | handle_internal_vmm_error(err) 223 | } 224 | switch_to_guest() 225 | } 226 | 227 | pub unsafe fn hart_entry_1() -> ! { 228 | set_user_trap_entry(); 229 | // get guest context 230 | let ctx = (TRAP_CONTEXT as *mut TrapContext).as_mut().unwrap(); 231 | 232 | // hgatp: set page table for guest physical address translation 233 | if riscv::register::hgatp::read().bits() != ctx.hgatp { 234 | let hgatp = riscv::register::hgatp::Hgatp::from_bits(ctx.hgatp); 235 | hgatp.write(); 236 | core::arch::riscv64::hfence_gvma_all(); 237 | assert_eq!(hgatp.bits(), riscv::register::hgatp::read().bits()); 238 | } 239 | hart_entry_2() 240 | } 241 | 242 | /// first enter guest, pass dtb 243 | #[naked] 244 | pub unsafe extern "C" fn hart_entry_2() -> ! { 245 | core::arch::asm!( 246 | "fence.i", 247 | "li a0, {trap_context}", 248 | "csrw sscratch, a0", 249 | "mv sp, a0", 250 | "ld t0, 32*8(sp)", 251 | "ld t1, 33*8(sp)", 252 | "csrw sstatus, t0", 253 | "csrw sepc, t1", 254 | "ld t0, 37*8(sp)", 255 | "csrw hstatus, t0", 256 | "ld x1, 1*8(sp)", 257 | "ld x3, 3*8(sp)", 258 | "ld x5, 5*8(sp)", 259 | "ld x6, 6*8(sp)", 260 | "ld x7, 7*8(sp)", 261 | "ld x8, 8*8(sp)", 262 | "ld x9, 9*8(sp)", 263 | "ld x10, 10*8(sp)", 264 | "ld x11, 11*8(sp)", 265 | "ld x12, 12*8(sp)", 266 | "ld x13, 13*8(sp)", 267 | "ld x14, 14*8(sp)", 268 | "ld x15, 15*8(sp)", 269 | "ld x16, 16*8(sp)", 270 | "ld x17, 17*8(sp)", 271 | "ld x18, 18*8(sp)", 272 | "ld x19, 19*8(sp)", 273 | "ld x20, 20*8(sp)", 274 | "ld x21, 21*8(sp)", 275 | "ld x22, 22*8(sp)", 276 | "ld x23, 23*8(sp)", 277 | "ld x24, 24*8(sp)", 278 | "ld x25, 25*8(sp)", 279 | "ld x26, 26*8(sp)", 280 | "ld x27, 27*8(sp)", 281 | "ld x28, 28*8(sp)", 282 | "ld x29, 29*8(sp)", 283 | "ld x30, 30*8(sp)", 284 | "ld x31, 31*8(sp)", 285 | "ld sp, 2*8(sp)", 286 | "li a1, {guest_dtb}", 287 | "sret", 288 | trap_context = const TRAP_CONTEXT, 289 | guest_dtb = const GUEST_DTB_ADDR, 290 | options(noreturn) 291 | ) 292 | } 293 | 294 | #[no_mangle] 295 | /// set the new addr of __restore asm function in TRAMPOLINE page, 296 | /// set the reg a0 = trap_cx_ptr, reg a1 = phy addr of usr page table, 297 | /// finally, jump to new addr of __restore asm function 298 | pub unsafe fn switch_to_guest() -> ! { 299 | set_user_trap_entry(); 300 | // get guest context 301 | let ctx = (TRAP_CONTEXT as *mut TrapContext).as_mut().unwrap(); 302 | // hdebug!("ctx sp: {:#x}, scause: {:?}", ctx.x[2], scause::read().cause()); 303 | 304 | // hgatp: set page table for guest physical address translation 305 | if riscv::register::hgatp::read().bits() != ctx.hgatp { 306 | let hgatp = riscv::register::hgatp::Hgatp::from_bits(ctx.hgatp); 307 | hgatp.write(); 308 | core::arch::riscv64::hfence_gvma_all(); 309 | assert_eq!(hgatp.bits(), riscv::register::hgatp::read().bits()); 310 | } 311 | 312 | extern "C" { 313 | fn __alltraps(); 314 | fn __restore(); 315 | } 316 | let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE; 317 | unsafe { 318 | asm!( 319 | "fence.i", 320 | "jr {restore_va}", // jump to new addr of __restore asm function 321 | restore_va = in(reg) restore_va, 322 | in("a0") TRAP_CONTEXT, // a0 = virt addr of Trap Context 323 | options(noreturn) 324 | ); 325 | } 326 | } 327 | 328 | #[no_mangle] 329 | pub fn trap_from_kernel(_trap_cx: &TrapContext) -> ! { 330 | let scause = scause::read(); 331 | let sepc = sepc::read(); 332 | match scause.cause() { 333 | Trap::Exception(Exception::StoreFault) 334 | | Trap::Exception(Exception::LoadFault) 335 | | Trap::Exception(Exception::LoadPageFault) => { 336 | let stval = stval::read(); 337 | panic!( 338 | "scause: {:?}, sepc: {:#x}, stval: {:#x}", 339 | scause.cause(), 340 | _trap_cx.sepc, 341 | stval 342 | ); 343 | } 344 | _ => { 345 | panic!( 346 | "scause: {:?}, spec: {:#x}, stval: {:#x}", 347 | scause.cause(), 348 | sepc, 349 | stval::read() 350 | ) 351 | } 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /src/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 alloc::vec::Vec; 7 | use spin::{Once, Mutex}; 8 | use core::fmt::{self, Debug, Formatter}; 9 | 10 | /// manage a frame which has the same lifecycle as the tracker 11 | #[derive(Clone)] 12 | pub struct FrameTracker { 13 | pub ppn: PhysPageNum, 14 | } 15 | 16 | impl FrameTracker { 17 | pub fn new(ppn: PhysPageNum) -> Self { 18 | // page cleaning 19 | let bytes_array = ppn.get_bytes_array(); 20 | for i in bytes_array { 21 | *i = 0; 22 | } 23 | Self { ppn } 24 | } 25 | } 26 | 27 | impl Debug for FrameTracker { 28 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 29 | f.write_fmt(format_args!("FrameTracker:PPN={:#x}", self.ppn.0)) 30 | } 31 | } 32 | 33 | impl Drop for FrameTracker { 34 | fn drop(&mut self) { 35 | frame_dealloc(self.ppn); 36 | } 37 | } 38 | 39 | trait FrameAllocator { 40 | fn new() -> Self; 41 | fn alloc(&mut self) -> Option; 42 | fn dealloc(&mut self, ppn: PhysPageNum); 43 | } 44 | 45 | /// an implementation for frame allocator 46 | pub struct StackFrameAllocator { 47 | current: usize, 48 | end: usize, 49 | recycled: Vec, 50 | } 51 | 52 | impl StackFrameAllocator { 53 | pub fn init(&mut self, l: PhysPageNum, r: PhysPageNum) { 54 | self.current = l.0; 55 | self.end = r.0; 56 | } 57 | } 58 | impl FrameAllocator for StackFrameAllocator { 59 | fn new() -> Self { 60 | Self { 61 | current: 0, 62 | end: 0, 63 | recycled: Vec::new(), 64 | } 65 | } 66 | fn alloc(&mut self) -> Option { 67 | if let Some(ppn) = self.recycled.pop() { 68 | Some(ppn.into()) 69 | } else if self.current == self.end { 70 | None 71 | } else { 72 | self.current += 1; 73 | Some((self.current - 1).into()) 74 | } 75 | } 76 | fn dealloc(&mut self, ppn: PhysPageNum) { 77 | let ppn = ppn.0; 78 | // validity check 79 | if ppn >= self.current || self.recycled.iter().any(|&v| v == ppn) { 80 | panic!("Frame ppn={:#x} has not been allocated!", ppn); 81 | } 82 | // recycle 83 | self.recycled.push(ppn); 84 | } 85 | } 86 | 87 | type FrameAllocatorImpl = StackFrameAllocator; 88 | 89 | 90 | 91 | pub static mut FRAME_ALLOCATOR: Once> = Once::new(); 92 | 93 | /// initiate the frame allocator using `ekernel` and `MEMORY_END` 94 | pub fn init_frame_allocator() { 95 | extern "C" { 96 | fn ekernel(); 97 | } 98 | unsafe{ 99 | FRAME_ALLOCATOR.call_once(|| { 100 | let mut frame_allocator = FrameAllocatorImpl::new(); 101 | frame_allocator.init( 102 | PhysAddr::from(ekernel as usize).ceil(), 103 | PhysAddr::from(MEMORY_END).floor(), 104 | ); 105 | Mutex::new(frame_allocator) 106 | }); 107 | } 108 | } 109 | 110 | /// allocate a frame 111 | pub fn frame_alloc() -> Option { 112 | // FRAME_ALLOCATOR 113 | // .exclusive_access() 114 | // .alloc() 115 | // .map(FrameTracker::new) 116 | unsafe{ 117 | let mut frame_allocator = FRAME_ALLOCATOR.get_mut(); 118 | let mut frame_allocator = frame_allocator.as_mut().unwrap().lock(); 119 | frame_allocator.alloc().map(FrameTracker::new) 120 | } 121 | } 122 | 123 | /// deallocate a frame 124 | pub fn frame_dealloc(ppn: PhysPageNum) { 125 | unsafe{ 126 | let mut frame_allocator = FRAME_ALLOCATOR.get_mut(); 127 | let mut frame_allocator = frame_allocator.as_mut().unwrap().lock(); 128 | frame_allocator.dealloc(ppn); 129 | } 130 | } 131 | 132 | #[allow(unused)] 133 | /// a simple test for frame allocator 134 | pub fn frame_allocator_test() { 135 | let mut v: Vec = Vec::new(); 136 | for i in 0..5 { 137 | let frame = frame_alloc().unwrap(); 138 | println!("{:?}", frame); 139 | v.push(frame); 140 | } 141 | v.clear(); 142 | for i in 0..5 { 143 | let frame = frame_alloc().unwrap(); 144 | println!("{:?}", frame); 145 | v.push(frame); 146 | } 147 | drop(v); 148 | println!("frame_allocator_test passed!"); 149 | } 150 | -------------------------------------------------------------------------------- /src/hyp_alloc/heap_allocator.rs: -------------------------------------------------------------------------------- 1 | //! The global allocator 2 | 3 | use crate::constants::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 | } -------------------------------------------------------------------------------- /src/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 | hdebug!("Heap initialize finished!"); 11 | } -------------------------------------------------------------------------------- /src/hypervisor.rs: -------------------------------------------------------------------------------- 1 | pub mod stack { 2 | use crate::{constants::{ 3 | PAGE_SIZE, KERNEL_STACK_SIZE, 4 | layout::TRAP_CONTEXT 5 | }, mm::MapPermission}; 6 | use crate::mm::MemorySet; 7 | use super::HOST_VMM; 8 | pub struct HypervisorStack(pub usize); 9 | 10 | pub fn hstack_position(guest_id: usize) -> (usize, usize) { 11 | let top = TRAP_CONTEXT - guest_id * (KERNEL_STACK_SIZE + PAGE_SIZE); 12 | let bottom = top - KERNEL_STACK_SIZE; 13 | (bottom, top) 14 | } 15 | 16 | pub fn hstack_alloc(guest_id: usize) -> HypervisorStack { 17 | let (hstack_bottom, hstack_top) = hstack_position(guest_id); 18 | hdebug!("allocated hstack: [{:#x}: {:#x})",hstack_bottom, hstack_top); 19 | let mut host_vmm = unsafe{ HOST_VMM.get_mut().unwrap().lock() }; 20 | host_vmm.hpm.insert_framed_area( 21 | hstack_bottom.into(), 22 | hstack_top.into(), 23 | MapPermission::R | MapPermission::W 24 | ); 25 | HypervisorStack(guest_id) 26 | } 27 | 28 | impl HypervisorStack { 29 | pub fn get_top(&self) -> usize { 30 | let (_, hstack_top) = hstack_position(self.0); 31 | hstack_top 32 | } 33 | } 34 | 35 | } 36 | 37 | pub mod fdt { 38 | ///! ref: https://github.com/mit-pdos/RVirt/blob/HEAD/src/fdt.rs 39 | 40 | use arrayvec::ArrayVec; 41 | use fdt::Fdt; 42 | 43 | #[derive(Clone, Debug)] 44 | pub struct Device { 45 | pub base_address: usize, 46 | pub size: usize 47 | } 48 | 49 | #[derive(Clone, Debug, Default)] 50 | pub struct MachineMeta{ 51 | pub physical_memory_offset: usize, 52 | pub physical_memory_size: usize, 53 | 54 | pub virtio: ArrayVec, 55 | 56 | pub test_finisher_address: Option, 57 | 58 | pub uart: Option, 59 | 60 | pub clint: Option, 61 | 62 | pub plic: Option, 63 | 64 | pub pci: Option, 65 | } 66 | 67 | impl MachineMeta { 68 | pub fn parse(dtb: usize) -> Self { 69 | let fdt = unsafe{ Fdt::from_ptr(dtb as *const u8) }.unwrap(); 70 | let memory = fdt.memory(); 71 | let mut meta = MachineMeta::default(); 72 | for region in memory.regions() { 73 | meta.physical_memory_offset = region.starting_address as usize; 74 | meta.physical_memory_size = region.size.unwrap(); 75 | } 76 | // probe virtio mmio device 77 | for node in fdt.find_all_nodes("/soc/virtio_mmio") { 78 | if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { 79 | let paddr = reg.starting_address as usize; 80 | let size = reg.size.unwrap(); 81 | hdebug!("virtio mmio addr: {:#x}, size: {:#x}", paddr, size); 82 | meta.virtio.push( 83 | Device { base_address: paddr, size } 84 | ) 85 | } 86 | } 87 | meta.virtio.sort_unstable_by_key(|v| v.base_address); 88 | 89 | // probe virt test 90 | for node in fdt.find_all_nodes("/soc/test") { 91 | if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { 92 | let base_addr = reg.starting_address as usize; 93 | let size = reg.size.unwrap(); 94 | hdebug!("test addr: {:#x}, size: {:#x}", base_addr, size); 95 | meta.test_finisher_address = Some(Device { base_address: base_addr, size}); 96 | } 97 | } 98 | 99 | // probe uart device 100 | for node in fdt.find_all_nodes("/soc/uart") { 101 | if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { 102 | let base_addr = reg.starting_address as usize; 103 | let size = reg.size.unwrap(); 104 | hdebug!("UART addr: {:#x}, size: {:#x}", base_addr, size); 105 | meta.uart = Some(Device { base_address: base_addr, size}); 106 | } 107 | } 108 | 109 | // probe clint(core local interrupter) 110 | for node in fdt.find_all_nodes("/soc/clint") { 111 | if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { 112 | let base_addr = reg.starting_address as usize; 113 | let size = reg.size.unwrap(); 114 | hdebug!("CLINT addr: {:#x}, size: {:#x}", base_addr, size); 115 | meta.clint = Some(Device { base_address: base_addr, size}); 116 | } 117 | } 118 | 119 | // probe plic 120 | for node in fdt.find_all_nodes("/soc/plic") { 121 | if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { 122 | let base_addr = reg.starting_address as usize; 123 | let size = reg.size.unwrap(); 124 | hdebug!("PLIC addr: {:#x}, size: {:#x}", base_addr, size); 125 | meta.plic = Some(Device { base_address: base_addr, size}); 126 | } 127 | } 128 | 129 | for node in fdt.find_all_nodes("/soc/pci") { 130 | if let Some(reg) = node.reg().and_then(|mut reg| reg.next()) { 131 | let base_addr = reg.starting_address as usize; 132 | let size = reg.size.unwrap(); 133 | hdebug!("PCI addr: {:#x}, size: {:#x}", base_addr, size); 134 | meta.pci = Some(Device { base_address: base_addr, size}); 135 | } 136 | } 137 | 138 | meta 139 | } 140 | } 141 | 142 | 143 | } 144 | 145 | 146 | use arrayvec::ArrayVec; 147 | use riscv::register::{ hvip, sie }; 148 | use spin::{ Once, Mutex }; 149 | use crate::constants::MAX_GUESTS; 150 | use crate::constants::csr::{hedeleg, hideleg, hcounteren}; 151 | use crate::device_emu::plic::PlicState; 152 | use crate::guest::{ page_table::GuestPageTable, Guest }; 153 | use crate::page_table::{ PageTable, PageTableSv39 }; 154 | use crate::mm::HostMemorySet; 155 | 156 | use self::fdt::MachineMeta; 157 | 158 | 159 | pub static mut HOST_VMM: Once>> = Once::new(); 160 | 161 | pub struct HostVmm { 162 | pub host_machine: MachineMeta, 163 | /// hypervisor memory 164 | pub hpm: HostMemorySet

, 165 | /// all guest structs 166 | pub guests: ArrayVec>, MAX_GUESTS>, 167 | /// current run guest id(single core) 168 | pub guest_id: usize, 169 | /// hypervisor emulated plic 170 | pub host_plic: Option, 171 | 172 | pub irq_pending: bool, 173 | 174 | pub timer_irq: usize, 175 | pub external_irq: usize, 176 | pub guest_page_falut: usize, 177 | } 178 | 179 | pub fn add_guest_queue(guest: Guest) { 180 | let host_vmm = unsafe{ HOST_VMM.get_mut().unwrap() }; 181 | let mut host_vmm = host_vmm.lock(); 182 | let guest_id = guest.guest_id; 183 | assert!(guest_id < MAX_GUESTS); 184 | host_vmm.guests[guest_id] = Some(guest); 185 | } 186 | 187 | 188 | pub unsafe fn init_vmm(hpm: HostMemorySet, host_machine: MachineMeta) { 189 | // hedeleg: delegate some synchronous exceptions 190 | hedeleg::write( 191 | hedeleg::INST_ADDR_MISALIGN | 192 | hedeleg::BREAKPOINT | 193 | hedeleg::ENV_CALL_FROM_U_OR_VU | 194 | hedeleg::INST_PAGE_FAULT | 195 | hedeleg::LOAD_PAGE_FAULT | 196 | hedeleg::STORE_PAGE_FAULT 197 | ); 198 | 199 | // hideleg: delegate all interrupts 200 | hideleg::write( 201 | hideleg::VSEIP | 202 | hideleg::VSSIP | 203 | hideleg::VSTIP 204 | ); 205 | 206 | // hvip: clear all interrupts 207 | hvip::clear_vseip(); 208 | hvip::clear_vssip(); 209 | hvip::clear_vstip(); 210 | 211 | // When the hypervisor is initialized, it is necessary to write the `hcounteren` register to all 1, because it is possible to read the `time` register in VU mode or VS mode.(refs: The counter-enable register `hcounteren` is a 32-bit register that controls the availability of the hardware performance monitoring counters to the guest virtual machine. 212 | // When the CY, TM, IR, or HPMn bit in the hcounteren register is clear, attempts to read the 213 | // cycle, time, instret, or hpmcountern register while V=1 will cause a virtual instruction exception 214 | // if the same bit in mcounteren is 1. When one of these bits is set, access to the corresponding register 215 | // is permitted when V=1, unless prevented for some other reason. In VU-mode, a counter is not 216 | // readable unless the applicable bits are set in both `hcounteren` and `scounteren`. 217 | // `hcounteren` must be implemented. However, any of the bits may be read-only zero, indicating 218 | // reads to the corresponding counter will cause an exception when V=1. Hence, they are effectively 219 | // WARL fields.) 220 | hcounteren::write(0xffff_ffff); 221 | 222 | // enable all interupts 223 | sie::set_sext(); 224 | sie::set_ssoft(); 225 | sie::set_stimer(); 226 | 227 | core::arch::asm!( 228 | "csrw vsatp, 0" 229 | ); 230 | 231 | // initialize HOST_VMM 232 | HOST_VMM.call_once(|| { 233 | let mut guests: ArrayVec>, MAX_GUESTS> = ArrayVec::new_const(); 234 | for _ in 0..MAX_GUESTS{ 235 | guests.push(None) 236 | } 237 | 238 | let host_plic; 239 | if let Some(plic) = host_machine.clone().plic { 240 | host_plic = Some(PlicState::new(plic.base_address)); 241 | }else{ 242 | host_plic = None; 243 | } 244 | Mutex::new( 245 | HostVmm { 246 | host_machine, 247 | hpm, 248 | guests, 249 | guest_id: 0, 250 | host_plic, 251 | irq_pending: false, 252 | timer_irq: 0, 253 | external_irq: 0, 254 | guest_page_falut: 0 255 | } 256 | ) 257 | }); 258 | 259 | hdebug!("Initialize hypervisor environment"); 260 | 261 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /src/linker-qemu.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 | . = 0x90000000; 51 | .dtb : { 52 | *(.dtb) 53 | } 54 | 55 | . = 0x90200000; 56 | .initrd : { 57 | *(.initrd) 58 | } 59 | 60 | /DISCARD/ : { 61 | *(.eh_frame) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![deny(warnings)] 4 | #![allow(non_upper_case_globals, dead_code)] 5 | #![feature( 6 | panic_info_message, 7 | alloc_error_handler, 8 | core_intrinsics, 9 | naked_functions, 10 | asm_const, 11 | stdsimd, 12 | riscv_ext_intrinsics 13 | )] 14 | 15 | #[macro_use] 16 | extern crate bitflags; 17 | 18 | extern crate alloc; 19 | 20 | #[path = "boards/qemu.rs"] 21 | mod board; 22 | 23 | #[macro_use] 24 | mod console; 25 | mod constants; 26 | mod detect; 27 | mod device_emu; 28 | mod drivers; 29 | mod error; 30 | mod guest; 31 | mod hyp_alloc; 32 | mod hypervisor; 33 | mod lang_items; 34 | mod mm; 35 | mod page_table; 36 | mod sbi; 37 | mod sync; 38 | 39 | use crate::constants::layout::{GUEST_DEFAULT_SIZE, GUEST_START_PA}; 40 | use crate::constants::PAGE_SIZE; 41 | use crate::guest::vmexit::hart_entry_1; 42 | use crate::guest::Guest; 43 | use crate::hypervisor::{add_guest_queue, init_vmm, HOST_VMM}; 44 | use crate::mm::{GuestMemorySet, HostMemorySet}; 45 | use crate::page_table::PageTableSv39; 46 | 47 | pub use error::{VmmError, VmmResult}; 48 | 49 | #[link_section = ".dtb"] 50 | pub static GUEST_DTB: [u8; include_bytes!("../guest.dtb").len()] = *include_bytes!("../guest.dtb"); 51 | 52 | #[link_section = ".initrd"] 53 | #[cfg(feature = "embed_guest_kernel")] 54 | static GUEST: [u8; include_bytes!("../guest.bin").len()] = *include_bytes!("../guest.bin"); 55 | 56 | #[link_section = ".initrd"] 57 | #[cfg(not(feature = "embed_guest_kernel"))] 58 | static GUEST: [u8; 0] = []; 59 | 60 | /// hypervisor boot stack size 61 | const BOOT_STACK_SIZE: usize = 16 * PAGE_SIZE; 62 | 63 | #[link_section = ".bss.stack"] 64 | /// hypervisor boot stack 65 | static BOOT_STACK: [u8; BOOT_STACK_SIZE] = [0u8; BOOT_STACK_SIZE]; 66 | 67 | #[link_section = ".text.entry"] 68 | #[export_name = "_start"] 69 | #[naked] 70 | /// hypervisor entrypoint 71 | pub unsafe extern "C" fn start() -> ! { 72 | core::arch::asm!( 73 | // prepare stack 74 | "la sp, {boot_stack}", 75 | "li t2, {boot_stack_size}", 76 | "addi t3, a0, 1", 77 | "mul t2, t2, t3", 78 | "add sp, sp, t2", 79 | // enter hentry 80 | "call hentry", 81 | boot_stack = sym BOOT_STACK, 82 | boot_stack_size = const BOOT_STACK_SIZE, 83 | options(noreturn) 84 | ) 85 | } 86 | 87 | /// clear BSS segment 88 | fn clear_bss() { 89 | extern "C" { 90 | fn sbss(); 91 | fn ebss(); 92 | } 93 | unsafe { 94 | core::slice::from_raw_parts_mut(sbss as usize as *mut u8, ebss as usize - sbss as usize) 95 | .fill(0); 96 | } 97 | } 98 | 99 | #[no_mangle] 100 | unsafe fn hentry(hart_id: usize, dtb: usize) -> ! { 101 | if hart_id == 0 { 102 | clear_bss(); 103 | hdebug!( 104 | "guest entry: {:#x}, guest size: {:#x}", 105 | GUEST.as_ptr() as usize, 106 | GUEST.len() 107 | ); 108 | hdebug!("guest dtb addr: {:#x}", GUEST_DTB.as_ptr() as usize); 109 | hdebug!("Hello Hypocaust-2!"); 110 | hdebug!("hart id: {}, dtb: {:#x}", hart_id, dtb); 111 | // detect h extension 112 | if sbi_rt::probe_extension(sbi_rt::Hsm).is_unavailable() { 113 | panic!("no HSM extension exist on current SBI environment"); 114 | } 115 | if !detect::detect_h_extension() { 116 | panic!("no RISC-V hypervisor H extension on current environment") 117 | } 118 | hdebug!("Hypocaust-2 > running with hardware RISC-V H ISA acceration!"); 119 | 120 | // initialize heap 121 | hyp_alloc::heap_init(); 122 | hdebug!("host dtb: {:#x}", dtb); 123 | let machine = hypervisor::fdt::MachineMeta::parse(dtb); 124 | // parse guest fdt 125 | hdebug!("guest dtb: {:#x}", GUEST_DTB.as_ptr() as usize); 126 | let guest_machine = hypervisor::fdt::MachineMeta::parse(GUEST_DTB.as_ptr() as usize); 127 | // initialize vmm 128 | let hpm = HostMemorySet::::new_host_vmm(&machine); 129 | init_vmm(hpm, machine); 130 | // create guest memory set 131 | let gpm = GuestMemorySet::::new_guest_without_load(&guest_machine); 132 | 133 | let mut host_vmm = HOST_VMM.get_mut().unwrap().lock(); 134 | host_vmm.hpm.map_guest(GUEST_START_PA, GUEST_DEFAULT_SIZE); 135 | drop(host_vmm); 136 | // hypervisor enable paging 137 | mm::enable_paging(); 138 | // trap init 139 | guest::vmexit::trap_init(); 140 | // memory translation test 141 | mm::remap_test(); 142 | // create guest struct 143 | let guest = Guest::new(0, gpm, guest_machine); 144 | add_guest_queue(guest); 145 | hdebug!("Jump to guest......"); 146 | hart_entry_1() 147 | } else { 148 | unreachable!() 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/mm/memory_set.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of [`MapArea`] and [`MemorySet`]. 2 | 3 | use super::MemorySet; 4 | use crate::constants::{ 5 | layout::{GUEST_START_PA, GUEST_START_VA, MEMORY_END, TRAMPOLINE, TRAP_CONTEXT}, 6 | PAGE_SIZE, 7 | }; 8 | use crate::guest::page_table::GuestPageTable; 9 | use crate::hyp_alloc::{frame_alloc, FrameTracker}; 10 | use crate::hypervisor::{fdt::MachineMeta, HOST_VMM}; 11 | use crate::page_table::{PPNRange, StepByOne, VPNRange}; 12 | use crate::page_table::{PTEFlags, PageTable}; 13 | use crate::page_table::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum}; 14 | use alloc::collections::BTreeMap; 15 | use alloc::vec::Vec; 16 | use core::arch::asm; 17 | use core::marker::PhantomData; 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 | /// memory set structure, controls virtual-memory space 35 | pub struct HostMemorySet { 36 | pub page_table: P, 37 | pub areas: Vec>, 38 | } 39 | 40 | pub struct GuestMemorySet { 41 | pub page_table: G, 42 | pub areas: Vec>, 43 | } 44 | 45 | impl HostMemorySet

{ 46 | pub fn new_bare() -> Self { 47 | Self { 48 | page_table: PageTable::new(), 49 | areas: Vec::new(), 50 | } 51 | } 52 | 53 | /// Without kernel stacks. 54 | /// 内核虚拟地址映射 55 | /// 映射了内核代码段和数据段以及跳板页,没有映射内核栈 56 | pub fn new_host_vmm(machine: &MachineMeta) -> Self { 57 | let mut hpm = Self::new_bare(); 58 | // map trampoline 59 | hpm.map_trampoline(); 60 | 61 | // 这里注意了,需要单独映射 Trap Context,因为在上下文切换时 62 | // 我们是不切换页表的 63 | hpm.push( 64 | MapArea::new( 65 | TRAP_CONTEXT.into(), 66 | TRAMPOLINE.into(), 67 | None, 68 | None, 69 | MapType::Framed, 70 | MapPermission::R | MapPermission::W, 71 | ), 72 | None, 73 | ); 74 | 75 | // map kernel sections 76 | hpm.push( 77 | MapArea::new( 78 | (stext as usize).into(), 79 | (etext as usize).into(), 80 | Some((stext as usize).into()), 81 | Some((etext as usize).into()), 82 | MapType::Linear, 83 | MapPermission::R | MapPermission::X, 84 | ), 85 | None, 86 | ); 87 | 88 | hpm.push( 89 | MapArea::new( 90 | (srodata as usize).into(), 91 | (erodata as usize).into(), 92 | Some((srodata as usize).into()), 93 | Some((erodata as usize).into()), 94 | MapType::Linear, 95 | MapPermission::R, 96 | ), 97 | None, 98 | ); 99 | 100 | hpm.push( 101 | MapArea::new( 102 | (sdata as usize).into(), 103 | (edata as usize).into(), 104 | Some((sdata as usize).into()), 105 | Some((edata as usize).into()), 106 | MapType::Linear, 107 | MapPermission::R | MapPermission::W, 108 | ), 109 | None, 110 | ); 111 | 112 | hpm.push( 113 | MapArea::new( 114 | (sbss_with_stack as usize).into(), 115 | (ebss as usize).into(), 116 | Some((sbss_with_stack as usize).into()), 117 | Some((ebss as usize).into()), 118 | MapType::Linear, 119 | MapPermission::R | MapPermission::W, 120 | ), 121 | None, 122 | ); 123 | 124 | hpm.push( 125 | MapArea::new( 126 | (ekernel as usize).into(), 127 | MEMORY_END.into(), 128 | Some((ekernel as usize).into()), 129 | Some(MEMORY_END.into()), 130 | MapType::Linear, 131 | MapPermission::R | MapPermission::W, 132 | ), 133 | None, 134 | ); 135 | 136 | if let Some(test) = &machine.test_finisher_address { 137 | hpm.push( 138 | MapArea::new( 139 | test.base_address.into(), 140 | (test.base_address + test.size).into(), 141 | Some(test.base_address.into()), 142 | Some((test.base_address + test.size).into()), 143 | MapType::Linear, 144 | MapPermission::R | MapPermission::W, 145 | ), 146 | None, 147 | ); 148 | } 149 | 150 | for virtio_dev in machine.virtio.iter() { 151 | hpm.push( 152 | MapArea::new( 153 | virtio_dev.base_address.into(), 154 | (virtio_dev.base_address + virtio_dev.size).into(), 155 | Some(virtio_dev.base_address.into()), 156 | Some((virtio_dev.base_address + virtio_dev.size).into()), 157 | MapType::Linear, 158 | MapPermission::R | MapPermission::W, 159 | ), 160 | None, 161 | ) 162 | } 163 | 164 | if let Some(plic) = &machine.plic { 165 | hpm.push( 166 | MapArea::new( 167 | plic.base_address.into(), 168 | (plic.base_address + plic.size).into(), 169 | Some(plic.base_address.into()), 170 | Some((plic.base_address + plic.size).into()), 171 | MapType::Linear, 172 | MapPermission::R | MapPermission::W, 173 | ), 174 | None, 175 | ); 176 | } 177 | hpm 178 | } 179 | 180 | /// 激活根页表 181 | pub fn activate(&self) { 182 | let satp = self.page_table.token(); 183 | unsafe { 184 | asm!( 185 | "csrw satp, {satp}", 186 | "sfence.vma", 187 | satp = in(reg) satp 188 | ); 189 | } 190 | } 191 | 192 | pub fn map_guest(&mut self, start_pa: usize, gpm_size: usize) { 193 | self.push( 194 | MapArea::new( 195 | start_pa.into(), 196 | (start_pa + gpm_size).into(), 197 | Some(start_pa.into()), 198 | Some((start_pa + gpm_size).into()), 199 | MapType::Linear, 200 | MapPermission::R | MapPermission::W, 201 | ), 202 | None, 203 | ); 204 | } 205 | 206 | /// 加载客户操作系统 207 | pub fn map_gpm(&mut self, gpm: &GuestMemorySet) { 208 | for area in gpm.areas.iter() { 209 | // 修改虚拟地址与物理地址相同 210 | let ppn_range = area.ppn_range.unwrap(); 211 | let start_pa: PhysAddr = ppn_range.get_start().into(); 212 | let end_pa: PhysAddr = ppn_range.get_end().into(); 213 | let start_va: usize = start_pa.into(); 214 | let end_va: usize = end_pa.into(); 215 | let new_area = MapArea::new( 216 | start_va.into(), 217 | end_va.into(), 218 | Some(start_pa), 219 | Some(end_pa), 220 | area.map_type, 221 | area.map_perm, 222 | ); 223 | self.push(new_area, None); 224 | } 225 | } 226 | } 227 | 228 | impl GuestMemorySet { 229 | /// 为 guest page table 新建根页表 230 | /// 需要分配 16 KiB 对齐的页表 231 | pub fn new_guest_bare() -> Self { 232 | Self { 233 | page_table: GuestPageTable::new_guest(), 234 | areas: Vec::new(), 235 | } 236 | } 237 | 238 | pub fn new_guest(guest_data: &[u8], gpm_size: usize, guest_machine: &MachineMeta) -> Self { 239 | let mut gpm = Self::new_guest_bare(); 240 | let elf = xmas_elf::ElfFile::new(guest_data).unwrap(); 241 | let elf_header = elf.header; 242 | let magic = elf_header.pt1.magic; 243 | assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!"); 244 | let ph_count = elf_header.pt2.ph_count(); 245 | let mut paddr = GUEST_START_PA as *mut u8; 246 | let mut last_paddr = GUEST_START_PA as *mut u8; 247 | for i in 0..ph_count { 248 | let ph = elf.program_header(i).unwrap(); 249 | if ph.get_type().unwrap() == xmas_elf::program::Type::Load { 250 | let start_va: VirtAddr = (ph.virtual_addr() as usize).into(); 251 | let end_va: VirtAddr = ((ph.virtual_addr() + ph.mem_size()) as usize).into(); 252 | hdebug!("va: [{:#x}: {:#x})", start_va.0, end_va.0); 253 | let mut map_perm = MapPermission::U; 254 | let ph_flags = ph.flags(); 255 | if ph_flags.is_read() { 256 | map_perm |= MapPermission::R; 257 | } 258 | if ph_flags.is_write() { 259 | map_perm |= MapPermission::W; 260 | } 261 | if ph_flags.is_execute() { 262 | map_perm |= MapPermission::X; 263 | } 264 | // 将内存拷贝到对应的物理内存上 265 | unsafe { 266 | core::ptr::copy( 267 | guest_data.as_ptr().add(ph.offset() as usize), 268 | paddr, 269 | ph.file_size() as usize, 270 | ); 271 | let page_align_size = ((ph.mem_size() as usize + PAGE_SIZE - 1) >> 12) << 12; 272 | paddr = paddr.add(page_align_size); 273 | } 274 | 275 | let map_area = MapArea::new( 276 | start_va, 277 | end_va, 278 | Some(PhysAddr(last_paddr as usize)), 279 | Some(PhysAddr(paddr as usize)), 280 | MapType::Linear, 281 | map_perm, 282 | ); 283 | hdebug!( 284 | "va: [{:#x}: {:#x}], pa: [{:#x}: {:#x}]", 285 | start_va.0, 286 | end_va.0, 287 | last_paddr as usize, 288 | paddr as usize 289 | ); 290 | last_paddr = paddr; 291 | gpm.push(map_area, None); 292 | } 293 | } 294 | let offset = paddr as usize - GUEST_START_PA; 295 | 296 | let guest_end_pa = GUEST_START_PA + gpm_size; 297 | let guest_end_va = GUEST_START_VA + gpm_size; 298 | // 映射其他物理内存 299 | gpm.push( 300 | MapArea::new( 301 | VirtAddr(offset + GUEST_START_VA), 302 | VirtAddr(guest_end_va), 303 | Some(PhysAddr(paddr as usize)), 304 | Some(PhysAddr(guest_end_pa)), 305 | MapType::Linear, 306 | MapPermission::R | MapPermission::W | MapPermission::U | MapPermission::X, 307 | ), 308 | None, 309 | ); 310 | hdebug!( 311 | "guest va -> [{:#x}: {:#x}), guest pa -> [{:#x}: {:#x})", 312 | GUEST_START_VA, 313 | guest_end_va, 314 | GUEST_START_PA, 315 | guest_end_pa 316 | ); 317 | 318 | gpm.map_trampoline(); 319 | 320 | // map qemu test 321 | if let Some(test) = &guest_machine.test_finisher_address { 322 | gpm.push( 323 | MapArea::new( 324 | test.base_address.into(), 325 | (test.base_address + test.size).into(), 326 | Some(test.base_address.into()), 327 | Some((test.base_address + test.size).into()), 328 | MapType::Linear, 329 | MapPermission::R | MapPermission::W | MapPermission::U, 330 | ), 331 | None, 332 | ); 333 | } 334 | 335 | // map virtio device 336 | for virtio_dev in guest_machine.virtio.iter() { 337 | gpm.push( 338 | MapArea::new( 339 | virtio_dev.base_address.into(), 340 | (virtio_dev.base_address + virtio_dev.size).into(), 341 | Some(virtio_dev.base_address.into()), 342 | Some((virtio_dev.base_address + virtio_dev.size).into()), 343 | MapType::Linear, 344 | MapPermission::R | MapPermission::W | MapPermission::U, 345 | ), 346 | None, 347 | ) 348 | } 349 | 350 | if let Some(uart) = &guest_machine.uart { 351 | gpm.push( 352 | MapArea::new( 353 | uart.base_address.into(), 354 | (uart.base_address + uart.size).into(), 355 | Some(uart.base_address.into()), 356 | Some((uart.base_address + uart.size).into()), 357 | MapType::Linear, 358 | MapPermission::R | MapPermission::W | MapPermission::U, 359 | ), 360 | None, 361 | ); 362 | } 363 | 364 | if let Some(clint) = &guest_machine.clint { 365 | gpm.push( 366 | MapArea::new( 367 | clint.base_address.into(), 368 | (clint.base_address + clint.size).into(), 369 | Some(clint.base_address.into()), 370 | Some((clint.base_address + clint.size).into()), 371 | MapType::Linear, 372 | MapPermission::R | MapPermission::W | MapPermission::U, 373 | ), 374 | None, 375 | ); 376 | } 377 | 378 | if let Some(plic) = &guest_machine.plic { 379 | gpm.push( 380 | MapArea::new( 381 | plic.base_address.into(), 382 | (plic.base_address).into(), 383 | Some(plic.base_address.into()), 384 | Some((plic.base_address).into()), 385 | MapType::Linear, 386 | MapPermission::R | MapPermission::W | MapPermission::U, 387 | ), 388 | None, 389 | ); 390 | } 391 | 392 | gpm 393 | } 394 | 395 | pub fn new_guest_without_load(guest_machine: &MachineMeta) -> Self { 396 | let mut gpm = Self::new_guest_bare(); 397 | 398 | htracking!( 399 | "map guest: [{:#x}: {:#x}]", 400 | guest_machine.physical_memory_offset, 401 | guest_machine.physical_memory_offset + guest_machine.physical_memory_size 402 | ); 403 | gpm.push( 404 | MapArea::new( 405 | VirtAddr(guest_machine.physical_memory_offset - 0x20_0000), 406 | VirtAddr(guest_machine.physical_memory_offset + guest_machine.physical_memory_size), 407 | Some(PhysAddr(guest_machine.physical_memory_offset - 0x20_0000)), 408 | Some(PhysAddr( 409 | guest_machine.physical_memory_offset + guest_machine.physical_memory_size, 410 | )), 411 | MapType::Linear, 412 | MapPermission::R | MapPermission::W | MapPermission::U | MapPermission::X, 413 | ), 414 | None, 415 | ); 416 | hdebug!( 417 | "guest va -> [{:#x}: {:#x}), guest pa -> [{:#x}: {:#x})", 418 | guest_machine.physical_memory_offset, 419 | guest_machine.physical_memory_offset + guest_machine.physical_memory_size, 420 | guest_machine.physical_memory_offset, 421 | guest_machine.physical_memory_offset + guest_machine.physical_memory_size 422 | ); 423 | 424 | gpm.map_trampoline(); 425 | 426 | // map qemu test 427 | if let Some(test) = &guest_machine.test_finisher_address { 428 | gpm.push( 429 | MapArea::new( 430 | test.base_address.into(), 431 | (test.base_address + test.size + 0x1000).into(), 432 | Some(test.base_address.into()), 433 | Some((test.base_address + test.size + 0x1000).into()), 434 | MapType::Linear, 435 | MapPermission::R | MapPermission::W | MapPermission::U | MapPermission::X, 436 | ), 437 | None, 438 | ); 439 | } 440 | 441 | // map virtio device 442 | for virtio_dev in guest_machine.virtio.iter() { 443 | gpm.push( 444 | MapArea::new( 445 | virtio_dev.base_address.into(), 446 | (virtio_dev.base_address + virtio_dev.size).into(), 447 | Some(virtio_dev.base_address.into()), 448 | Some((virtio_dev.base_address + virtio_dev.size).into()), 449 | MapType::Linear, 450 | MapPermission::R | MapPermission::W | MapPermission::U, 451 | ), 452 | None, 453 | ) 454 | } 455 | 456 | if let Some(uart) = &guest_machine.uart { 457 | gpm.push( 458 | MapArea::new( 459 | uart.base_address.into(), 460 | (uart.base_address + uart.size).into(), 461 | Some(uart.base_address.into()), 462 | Some((uart.base_address + uart.size).into()), 463 | MapType::Linear, 464 | MapPermission::R | MapPermission::W | MapPermission::U, 465 | ), 466 | None, 467 | ); 468 | } 469 | 470 | if let Some(clint) = &guest_machine.clint { 471 | gpm.push( 472 | MapArea::new( 473 | clint.base_address.into(), 474 | (clint.base_address + clint.size).into(), 475 | Some(clint.base_address.into()), 476 | Some((clint.base_address + clint.size).into()), 477 | MapType::Linear, 478 | MapPermission::R | MapPermission::W | MapPermission::U, 479 | ), 480 | None, 481 | ); 482 | } 483 | 484 | if let Some(plic) = &guest_machine.plic { 485 | gpm.push( 486 | MapArea::new( 487 | plic.base_address.into(), 488 | (plic.base_address + 0x0020_0000).into(), 489 | Some(plic.base_address.into()), 490 | Some((plic.base_address + 0x0020_0000).into()), 491 | MapType::Linear, 492 | MapPermission::R | MapPermission::W | MapPermission::U, 493 | ), 494 | None, 495 | ); 496 | } 497 | 498 | if let Some(pci) = &guest_machine.pci { 499 | gpm.push( 500 | MapArea::new( 501 | pci.base_address.into(), 502 | (pci.base_address + 0x0020_0000).into(), 503 | Some(pci.base_address.into()), 504 | Some((pci.base_address + 0x0020_0000).into()), 505 | MapType::Linear, 506 | MapPermission::R | MapPermission::W | MapPermission::U, 507 | ), 508 | None, 509 | ); 510 | } 511 | 512 | gpm 513 | } 514 | } 515 | 516 | /// map area structure, controls a contiguous piece of virtual memory 517 | #[derive(Clone)] 518 | pub struct MapArea { 519 | pub vpn_range: VPNRange, 520 | pub ppn_range: Option, 521 | pub data_frames: BTreeMap, 522 | pub map_type: MapType, 523 | pub map_perm: MapPermission, 524 | _marker: PhantomData

, 525 | } 526 | 527 | impl

MapArea

528 | where 529 | P: PageTable, 530 | { 531 | pub fn new( 532 | start_va: VirtAddr, 533 | end_va: VirtAddr, 534 | start_pa: Option, 535 | end_pa: Option, 536 | map_type: MapType, 537 | map_perm: MapPermission, 538 | ) -> Self { 539 | let start_vpn: VirtPageNum = start_va.floor(); 540 | let end_vpn: VirtPageNum = end_va.ceil(); 541 | if let (Some(start_pa), Some(end_pa)) = (start_pa, end_pa) { 542 | let start_ppn = start_pa.floor(); 543 | let end_ppn = end_pa.ceil(); 544 | return Self { 545 | vpn_range: VPNRange::new(start_vpn, end_vpn), 546 | ppn_range: Some(PPNRange::new(start_ppn, end_ppn)), 547 | data_frames: BTreeMap::new(), 548 | map_type, 549 | map_perm, 550 | _marker: PhantomData, 551 | }; 552 | } 553 | Self { 554 | vpn_range: VPNRange::new(start_vpn, end_vpn), 555 | ppn_range: None, 556 | data_frames: BTreeMap::new(), 557 | map_type, 558 | map_perm, 559 | _marker: PhantomData, 560 | } 561 | } 562 | pub fn map_one(&mut self, page_table: &mut P, vpn: VirtPageNum, ppn_: Option) { 563 | let ppn: PhysPageNum; 564 | match self.map_type { 565 | // 线性映射 566 | MapType::Linear => { 567 | ppn = ppn_.unwrap(); 568 | } 569 | MapType::Framed => { 570 | let frame = frame_alloc().unwrap(); 571 | ppn = frame.ppn; 572 | self.data_frames.insert(vpn, frame); 573 | } 574 | } 575 | let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap(); 576 | page_table.map(vpn, ppn, pte_flags); 577 | } 578 | #[allow(unused)] 579 | pub fn unmap_one(&mut self, page_table: &mut P, vpn: VirtPageNum) { 580 | if self.map_type == MapType::Framed { 581 | self.data_frames.remove(&vpn); 582 | } 583 | page_table.unmap(vpn); 584 | } 585 | pub fn map(&mut self, page_table: &mut P) { 586 | let vpn_range = self.vpn_range; 587 | if let Some(ppn_range) = self.ppn_range { 588 | let ppn_start: usize = ppn_range.get_start().into(); 589 | let ppn_end: usize = ppn_range.get_end().into(); 590 | let vpn_start: usize = vpn_range.get_start().into(); 591 | let vpn_end: usize = vpn_range.get_end().into(); 592 | assert_eq!(ppn_end - ppn_start, vpn_end - vpn_start); 593 | let mut ppn = ppn_range.get_start(); 594 | let mut vpn = vpn_range.get_start(); 595 | loop { 596 | self.map_one(page_table, vpn, Some(ppn)); 597 | ppn.step(); 598 | vpn.step(); 599 | if ppn == ppn_range.get_end() && vpn == vpn_range.get_end() { 600 | break; 601 | } 602 | } 603 | } else { 604 | for vpn in self.vpn_range { 605 | self.map_one(page_table, vpn, None) 606 | } 607 | } 608 | } 609 | #[allow(unused)] 610 | pub fn unmap(&mut self, page_table: &mut P) { 611 | for vpn in self.vpn_range { 612 | self.unmap_one(page_table, vpn); 613 | } 614 | } 615 | /// data: start-aligned but maybe with shorter length 616 | /// assume that all frames were cleared before 617 | pub fn copy_data(&mut self, page_table: &mut P, data: &[u8]) { 618 | assert_eq!(self.map_type, MapType::Framed); 619 | let mut start: usize = 0; 620 | let mut current_vpn = self.vpn_range.get_start(); 621 | let len = data.len(); 622 | loop { 623 | let src = &data[start..len.min(start + PAGE_SIZE)]; 624 | let dst = &mut page_table 625 | .translate(current_vpn) 626 | .unwrap() 627 | .ppn() 628 | .get_bytes_array()[..src.len()]; 629 | dst.copy_from_slice(src); 630 | start += PAGE_SIZE; 631 | if start >= len { 632 | break; 633 | } 634 | current_vpn.step(); 635 | } 636 | } 637 | } 638 | 639 | #[derive(Copy, Clone, PartialEq, Debug)] 640 | /// map type for memory set: identical or framed 641 | pub enum MapType { 642 | Framed, 643 | Linear, 644 | } 645 | 646 | bitflags! { 647 | /// map permission corresponding to that in pte: `R W X U` 648 | pub struct MapPermission: u8 { 649 | const R = 1 << 1; 650 | const W = 1 << 2; 651 | const X = 1 << 3; 652 | const U = 1 << 4; 653 | } 654 | } 655 | 656 | #[allow(unused)] 657 | pub fn remap_test() { 658 | let host_vmm = unsafe { HOST_VMM.get().unwrap().lock() }; 659 | let kernel_space = &host_vmm.hpm; 660 | let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into(); 661 | let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into(); 662 | let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into(); 663 | 664 | assert!(!kernel_space 665 | .page_table 666 | .translate(mid_text.floor()) 667 | .unwrap() 668 | .writable(),); 669 | assert!(!kernel_space 670 | .page_table 671 | .translate(mid_rodata.floor()) 672 | .unwrap() 673 | .writable(),); 674 | assert!(!kernel_space 675 | .page_table 676 | .translate(mid_data.floor()) 677 | .unwrap() 678 | .executable(),); 679 | unsafe { core::ptr::read(TRAMPOLINE as *const usize) }; 680 | // 测试 guest ketnel 681 | hdebug!("remap test passed!"); 682 | drop(host_vmm); 683 | } 684 | 685 | // #[allow(unused)] 686 | // pub fn guest_kernel_test() { 687 | // use crate::constants::layout::GUEST_KERNEL_PHY_START_1; 688 | // let mut kernel_space = HYPERVISOR_MEMORY.exclusive_access(); 689 | 690 | // let guest_kernel_text: VirtAddr = GUEST_KERNEL_PHY_START_1.into(); 691 | 692 | // assert!(kernel_space.page_table.translate(guest_kernel_text.floor()).unwrap().executable()); 693 | // assert!(kernel_space.page_table.translate(guest_kernel_text.floor()).unwrap().readable()); 694 | // // 尝试读数据 695 | // unsafe{ 696 | // core::ptr::read(GUEST_KERNEL_PHY_START_1 as *const u32); 697 | // } 698 | // // 测试 guest ketnel 699 | // hdebug!("guest kernel test passed!"); 700 | // } 701 | -------------------------------------------------------------------------------- /src/mm/mod.rs: -------------------------------------------------------------------------------- 1 | mod memory_set; 2 | 3 | pub use memory_set::{HostMemorySet, GuestMemorySet, MapArea, remap_test, MapPermission}; 4 | 5 | use memory_set::MapType; 6 | use crate::guest::page_table::GuestPageTable; 7 | use crate::page_table::{VirtAddr, PageTable, VirtPageNum, PageTableEntry, PhysAddr, PTEFlags}; 8 | use crate::constants::layout::TRAMPOLINE; 9 | use crate::hypervisor::HOST_VMM; 10 | 11 | pub fn enable_paging() { 12 | let host_vmm = unsafe{ HOST_VMM.get().unwrap().lock() }; 13 | host_vmm.hpm.activate(); 14 | drop(host_vmm); 15 | hdebug!("Hypervisor enable paging!"); 16 | } 17 | 18 | pub trait MemorySet{ 19 | fn token(&self) -> usize; 20 | fn insert_framed_area( 21 | &mut self, 22 | start_va: VirtAddr, 23 | end_va: VirtAddr, 24 | permission: MapPermission, 25 | ); 26 | fn push( 27 | &mut self, 28 | map_area: MapArea

, 29 | data: Option<&[u8]> 30 | ); 31 | 32 | fn map_trampoline(&mut self); 33 | fn translate(&self, vpn: VirtPageNum) -> Option; 34 | fn translate_va(&self, va: usize) -> Option; 35 | } 36 | 37 | impl MemorySet

for HostMemorySet

{ 38 | fn token(&self) -> usize { 39 | self.page_table.token() 40 | } 41 | 42 | /// Assume that no conflicts. 43 | fn insert_framed_area( 44 | &mut self, 45 | start_va: VirtAddr, 46 | end_va: VirtAddr, 47 | permission: MapPermission, 48 | ) { 49 | self.push( 50 | MapArea::new(start_va, end_va, None, None, MapType::Framed, permission), 51 | None, 52 | ); 53 | } 54 | 55 | /// 将内存区域 push 到页表中,并映射内存区域 56 | fn push(&mut self, mut map_area: MapArea

, data: Option<&[u8]>) { 57 | map_area.map(&mut self.page_table); 58 | if let Some(data) = data { 59 | map_area.copy_data(&mut self.page_table, data); 60 | } 61 | self.areas.push(map_area); 62 | } 63 | 64 | /// Mention that trampoline is not collected by areas. 65 | fn map_trampoline(&mut self) { 66 | extern "C" { 67 | fn strampoline(); 68 | } 69 | self.page_table.map( 70 | VirtAddr::from(TRAMPOLINE).into(), 71 | PhysAddr::from(strampoline as usize).into(), 72 | PTEFlags::R | PTEFlags::X, 73 | ); 74 | } 75 | 76 | 77 | /// 将虚拟页号翻译成页表项 78 | fn translate(&self, vpn: VirtPageNum) -> Option { 79 | self.page_table.translate(vpn) 80 | } 81 | 82 | fn translate_va(&self, va: usize) -> Option { 83 | self.page_table.translate_va(va) 84 | } 85 | } 86 | 87 | impl MemorySet

for GuestMemorySet

{ 88 | fn token(&self) -> usize { 89 | self.page_table.token() 90 | } 91 | 92 | /// Assume that no conflicts. 93 | fn insert_framed_area( 94 | &mut self, 95 | start_va: VirtAddr, 96 | end_va: VirtAddr, 97 | permission: MapPermission, 98 | ) { 99 | self.push( 100 | MapArea::new(start_va, end_va, None, None, MapType::Framed, permission), 101 | None, 102 | ); 103 | } 104 | 105 | /// 将内存区域 push 到页表中,并映射内存区域 106 | fn push(&mut self, mut map_area: MapArea

, data: Option<&[u8]>) { 107 | map_area.map(&mut self.page_table); 108 | if let Some(data) = data { 109 | map_area.copy_data(&mut self.page_table, data); 110 | } 111 | self.areas.push(map_area); 112 | } 113 | 114 | /// Mention that trampoline is not collected by areas. 115 | fn map_trampoline(&mut self) { 116 | extern "C" { 117 | fn strampoline(); 118 | } 119 | self.page_table.map( 120 | VirtAddr::from(TRAMPOLINE).into(), 121 | PhysAddr::from(strampoline as usize).into(), 122 | PTEFlags::R | PTEFlags::X, 123 | ); 124 | } 125 | 126 | /// 将虚拟页号翻译成页表项 127 | fn translate(&self, vpn: VirtPageNum) -> Option { 128 | self.page_table.translate(vpn) 129 | } 130 | 131 | fn translate_va(&self, va: usize) -> Option { 132 | self.page_table.translate_va(va) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/page_table/address.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of physical and virtual address and page number. 2 | 3 | use super::PageTableEntry; 4 | use crate::constants::{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; -------------------------------------------------------------------------------- /src/page_table/mod.rs: -------------------------------------------------------------------------------- 1 | mod address; 2 | mod pte; 3 | mod sv39; 4 | 5 | use alloc::vec::Vec; 6 | 7 | pub use pte::{ PTEFlags, PageTableEntry }; 8 | pub use address::{ PhysPageNum, VirtPageNum, PhysAddr, VirtAddr, StepByOne, VPNRange, PPNRange }; 9 | pub use sv39::PageTableSv39; 10 | 11 | use crate::guest::page_table::GuestPageTable; 12 | use crate::guest::pmap::gpa2hpa; 13 | 14 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 15 | pub enum PageTableLevel { 16 | Level4KB, 17 | Level2MB, 18 | Level1GB 19 | } 20 | 21 | #[derive(Debug)] 22 | pub struct PteWrapper { 23 | pub addr: usize, 24 | pub pte: PageTableEntry, 25 | pub level: PageTableLevel 26 | } 27 | 28 | #[derive(Debug)] 29 | pub struct PageWalk { 30 | pub path: Vec, 31 | pub pa: usize 32 | } 33 | 34 | #[derive(Debug)] 35 | pub struct AddressTranslation { 36 | pub pte: PageTableEntry, 37 | pub pte_addr: usize, 38 | pub guest_pa: usize, 39 | pub level: PageTableLevel, 40 | pub page_walk: PageWalk 41 | } 42 | 43 | 44 | 45 | pub trait PageTable: Clone { 46 | /// build new bare page table 47 | fn new() -> Self; 48 | /// build page table from 49 | fn from_token(satp: usize) -> Self; 50 | /// map virt page into phys page 51 | fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags); 52 | /// unmap virt page 53 | fn unmap(&mut self, vpn: VirtPageNum); 54 | /// page walk and renturn all walked ptes 55 | fn walk_page_table usize>(root: usize, va: usize, read_pte: R) -> Option; 56 | /// translate virt page into physical page 57 | fn translate(&self, vpn: VirtPageNum) -> Option; 58 | /// translate virt address into physical address 59 | fn translate_va(&self, va: usize) -> Option; 60 | /// get page table root token 61 | fn token(&self) -> usize; 62 | } 63 | 64 | pub fn translate_guest_va(guest_id: usize, root: usize, guest_va: usize) -> Option { 65 | P::walk_page_table(root, guest_va, |va| { 66 | let pa = gpa2hpa(va, guest_id); 67 | // htracking!("pa: {:#x}", pa); 68 | unsafe{ core::ptr::read(pa as *const usize) } 69 | }).map(|t| { 70 | AddressTranslation { 71 | pte: t.path[t.path.len() - 1].pte, 72 | pte_addr: t.path[t.path.len() - 1].addr, 73 | level: t.path[t.path.len() - 1].level, 74 | guest_pa: t.pa, 75 | page_walk: t 76 | } 77 | }) 78 | } -------------------------------------------------------------------------------- /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 | use crate::guest::page_table::GuestPageTable; 2 | use crate::hyp_alloc::{ FrameTracker, frame_alloc }; 3 | 4 | use super::{ PhysPageNum, VirtPageNum, PageTable, PageTableLevel, PTEFlags, PageTableEntry, PteWrapper, PageWalk }; 5 | 6 | use alloc::vec::Vec; 7 | use alloc::vec; 8 | 9 | #[derive(Clone)] 10 | pub struct PageTableSv39 { 11 | pub root_ppn: PhysPageNum, 12 | frames: Vec 13 | } 14 | 15 | impl PageTableSv39 { 16 | fn find_pte(&self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> { 17 | let idxs = vpn.indexes(); 18 | let mut ppn = self.root_ppn; 19 | let mut result: Option<&mut PageTableEntry> = None; 20 | for (i, idx) in idxs.iter().enumerate() { 21 | let pte = &mut ppn.get_pte_array()[*idx]; 22 | if i == 2 { 23 | result = Some(pte); 24 | break; 25 | } 26 | if !pte.is_valid() { 27 | return None; 28 | } 29 | ppn = pte.ppn(); 30 | } 31 | result 32 | } 33 | 34 | fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> { 35 | let idxs = vpn.indexes(); 36 | let mut ppn = self.root_ppn; 37 | let mut result: Option<&mut PageTableEntry> = None; 38 | for (i, idx) in idxs.iter().enumerate() { 39 | let pte = &mut ppn.get_pte_array()[*idx]; 40 | if i == 2 { 41 | result = Some(pte); 42 | break; 43 | } 44 | if !pte.is_valid() { 45 | let frame = frame_alloc().unwrap(); 46 | *pte = PageTableEntry::new(frame.ppn, PTEFlags::V); 47 | self.frames.push(frame); 48 | } 49 | ppn = pte.ppn(); 50 | } 51 | result 52 | } 53 | } 54 | 55 | 56 | 57 | impl GuestPageTable for PageTableSv39 { 58 | /// 新建 guest 根目录页表,需要分配 16 KiB 的内存 59 | /// 并且 16 KiB 内存对齐 60 | fn new_guest() -> Self { 61 | let mut frames = vec![]; 62 | let mut root_frame = frame_alloc().unwrap(); 63 | while root_frame.ppn.0 & 0x3 != 0 { 64 | hdebug!("page {:#x} was allocated, but is does not follow 16KiB buundary.", root_frame.ppn.0); 65 | frames.push(root_frame); 66 | root_frame = frame_alloc().unwrap(); 67 | } 68 | hdebug!("Guest root page table: {:#x}", root_frame.ppn.0); 69 | let root_ppn = root_frame.ppn; 70 | frames.push(root_frame); 71 | for _ in 0..3 { 72 | frames.push(frame_alloc().unwrap()); 73 | } 74 | Self { 75 | root_ppn: root_ppn, 76 | frames 77 | } 78 | } 79 | } 80 | 81 | impl PageTable for PageTableSv39 { 82 | fn new() -> Self { 83 | let frame = frame_alloc().unwrap(); 84 | PageTableSv39 { 85 | root_ppn: frame.ppn, 86 | frames: vec![frame], 87 | } 88 | } 89 | /// Temporarily used to get arguments from user space. 90 | fn from_token(satp: usize) -> Self { 91 | Self { 92 | root_ppn: PhysPageNum::from(satp & ((1usize << 44) - 1)), 93 | frames: Vec::new(), 94 | } 95 | } 96 | 97 | fn token(&self) -> usize { 98 | 8usize << 60 | self.root_ppn.0 99 | } 100 | 101 | #[allow(unused)] 102 | fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) { 103 | let pte = self.find_pte_create(vpn).unwrap(); 104 | assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn); 105 | *pte = PageTableEntry::new(ppn, flags | PTEFlags::V); 106 | } 107 | 108 | #[allow(unused)] 109 | fn unmap(&mut self, vpn: VirtPageNum) { 110 | let pte = self.find_pte(vpn).unwrap(); 111 | assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn); 112 | *pte = PageTableEntry::empty(); 113 | } 114 | 115 | fn translate(&self, vpn: VirtPageNum) -> Option { 116 | self.find_pte(vpn).map(|pte| *pte) 117 | } 118 | 119 | fn translate_va(&self, va: usize) -> Option { 120 | let offset = va & 0xfff; 121 | let vpn = VirtPageNum::from(va >> 12); 122 | if let Some(pte) = self.translate(vpn) { 123 | Some((pte.ppn().0 << 12) + offset) 124 | }else{ 125 | None 126 | } 127 | } 128 | 129 | fn walk_page_table usize>(root: usize, va: usize, read_pte: R) -> Option { 130 | let mut path = Vec::new(); 131 | let mut page_table = root; 132 | for level in 0..3 { 133 | let pte_index = (va >> (30 - 9 * level)) & 0x1ff; 134 | let pte_addr = page_table + pte_index * 8; 135 | let pte = read_pte(pte_addr); 136 | let level = match level { 137 | 0 => PageTableLevel::Level1GB, 138 | 1 => PageTableLevel::Level2MB, 139 | 2 => PageTableLevel::Level4KB, 140 | _ => unreachable!(), 141 | }; 142 | let pte = PageTableEntry{ bits: pte }; 143 | path.push(PteWrapper{ addr: pte_addr, pte, level}); 144 | 145 | if !pte.is_valid() || (pte.writable() && !pte.readable()){ return None; } 146 | else if pte.readable() | pte.executable() { 147 | let pa = match level { 148 | PageTableLevel::Level4KB => ((pte.bits >> 10) << 12) | (va & 0xfff), 149 | PageTableLevel::Level2MB => ((pte.bits >> 19) << 21) | (va & 0x1fffff), 150 | PageTableLevel::Level1GB => ((pte.bits >> 28) << 30) | (va & 0x3fffffff), 151 | }; 152 | return Some(super::PageWalk { path, pa}); 153 | }else{ 154 | page_table = (pte.bits >> 10) << 12; 155 | } 156 | } 157 | None 158 | } 159 | 160 | } -------------------------------------------------------------------------------- /src/sbi.rs: -------------------------------------------------------------------------------- 1 | //! SBI call wrappers 2 | 3 | use core::arch::asm; 4 | 5 | 6 | 7 | pub const SBI_CONSOLE_PUTCHAR: usize = 1; 8 | pub const SBI_CONSOLE_GETCHAR: usize = 2; 9 | 10 | pub mod leagcy { 11 | pub const SBI_SET_TIMER: usize = 0; 12 | } 13 | 14 | pub const SBI_SUCCESS: usize = 0; 15 | pub const SBI_ERR_FAILUER: isize = -1; 16 | pub const SBI_ERR_NOT_SUPPORTED: isize = -2; 17 | pub const SBI_ERR_INAVLID_PARAM: isize = -3; 18 | pub const SBI_ERR_DENIED: isize = -4; 19 | pub const SBI_ERR_INVALID_ADDRESS: isize = -5; 20 | pub const SBI_ERR_ALREADY_AVAILABLE: isize = -6; 21 | 22 | pub const SBI_EXTID_BASE: usize = 0x10; 23 | pub const SBI_GET_SBI_SPEC_VERSION_FID: usize = 0; 24 | pub const SBI_GET_SBI_IMPL_ID_FID: usize = 1; 25 | pub const SBI_GET_SBI_IMPL_VERSION_FID: usize = 2; 26 | pub const SBI_PROBE_EXTENSION_FID: usize = 3; 27 | pub const SBI_GET_MVENDORID_FID: usize = 4; 28 | pub const SBI_GET_MARCHID_FID: usize = 5; 29 | pub const SBI_GET_MIMPID_FID: usize = 6; 30 | 31 | pub const SBI_EXTID_TIME: usize = 0x54494D45; 32 | pub const SBI_SET_TIMER_FID: usize = 0x0; 33 | 34 | pub const SBI_EXTID_IPI: usize = 0x735049; 35 | pub const SBI_SEND_IPI_FID: usize = 0x0; 36 | 37 | pub const SBI_EXTID_HSM: usize = 0x48534D; 38 | pub const SBI_HART_START_FID: usize = 0; 39 | pub const SBI_HART_STOP_FID: usize = 1; 40 | pub const SBI_HART_STATUS_FID: usize = 2; 41 | 42 | pub const SBI_EXTID_RFNC: usize = 0x52464E43; 43 | pub const SBI_REMOTE_FENCE_I_FID: usize = 0; 44 | pub const SBI_REMOTE_SFENCE_VMA_FID: usize = 1; 45 | pub const SBI_REMOTE_SFENCE_VMA_ASID_FID: usize = 2; 46 | pub const SBI_REMOTE_HFENCE_GVMA_FID: usize = 3; 47 | pub const SBI_REMOTE_HFENCE_GVMA_VMID_FID: usize = 4; 48 | pub const SBI_REMOTE_HFENCE_VVMA_FIDL: usize = 5; 49 | pub const SBI_REMOTE_HFENCE_VVMA_ASID_FID: usize = 6; 50 | 51 | 52 | #[inline(always)] 53 | /// general sbi call 54 | fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { 55 | let mut ret; 56 | unsafe { 57 | asm!( 58 | "li x16, 0", 59 | "ecall", 60 | inlateout("x10") arg0 => ret, 61 | in("x11") arg1, 62 | in("x12") arg2, 63 | in("x17") which, 64 | ); 65 | } 66 | ret 67 | } 68 | 69 | 70 | /// use sbi call to putchar in console (qemu uart handler) 71 | pub fn console_putchar(c: usize) { 72 | sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0); 73 | } 74 | 75 | /// use sbi call to getchar from console (qemu uart handler) 76 | pub fn console_getchar() -> usize { 77 | sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0) 78 | } 79 | 80 | pub fn set_timer(stime: usize) { 81 | sbi_rt::set_timer(stime as u64); 82 | } 83 | 84 | /// use sbi call to shutdown the kernel 85 | pub fn shutdown() -> ! { 86 | sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::SystemFailure); 87 | unreachable!() 88 | } 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | mod up; 2 | 3 | pub use up::UPSafeCell; -------------------------------------------------------------------------------- /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 | } --------------------------------------------------------------------------------