├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Makefile ├── README.md ├── gdb_init_commands ├── guest ├── .cargo │ └── config.toml ├── Cargo.toml ├── build.rs ├── rust-toolchain.toml └── src │ ├── boot.S │ ├── boot.rs │ ├── debug.rs │ ├── kernel.S │ ├── kernel.rs │ ├── main.rs │ ├── memlayout.rs │ ├── paging.rs │ ├── uart.rs │ └── util.rs └── hypervisor ├── .cargo └── config.toml ├── Cargo.toml ├── build.rs ├── rust-toolchain.toml └── src ├── boot.S ├── boot.rs ├── clint.rs ├── count_harts.rs ├── debug.rs ├── global_const.rs ├── guest.rs ├── hypervisor.S ├── hypervisor.rs ├── m_mode_calls.rs ├── main.rs ├── memlayout.rs ├── mkernel.S ├── mkernel.rs ├── paging.rs ├── plic.rs ├── riscv.rs ├── riscv ├── csr.rs ├── csr │ ├── cpumode.rs │ ├── hcontext.rs │ ├── hcounteren.rs │ ├── hedeleg.rs │ ├── henvcfg.rs │ ├── hgatp.rs │ ├── hgeie.rs │ ├── hgeip.rs │ ├── hideleg.rs │ ├── hie.rs │ ├── hip.rs │ ├── hstatus.rs │ ├── htimedelta.rs │ ├── htinst.rs │ ├── htval.rs │ ├── hvip.rs │ ├── macros.rs │ ├── mcause.rs │ ├── medeleg.rs │ ├── mepc.rs │ ├── mhartid.rs │ ├── mideleg.rs │ ├── mie.rs │ ├── mip.rs │ ├── misa.rs │ ├── mstatus.rs │ ├── mtvec.rs │ ├── satp.rs │ ├── scause.rs │ ├── scounteren.rs │ ├── sepc.rs │ ├── sie.rs │ ├── sip.rs │ ├── sscratch.rs │ ├── sstatus.rs │ ├── stval.rs │ ├── stvec.rs │ ├── vsatp.rs │ ├── vscause.rs │ ├── vsepc.rs │ ├── vsie.rs │ ├── vsip.rs │ ├── vsscratch.rs │ ├── vsstatus.rs │ ├── vstval.rs │ └── vstvec.rs ├── gpr.rs ├── instruction.S ├── instruction.rs └── interrupt.rs ├── sbi.rs ├── timer.rs ├── uart.rs ├── util.rs ├── util ├── jump.rs └── logger.rs └── virtio.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | target 3 | /.idea 4 | trace-events 5 | .DS_Store -------------------------------------------------------------------------------- /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 = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "bare-metal" 13 | version = "1.0.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 16 | 17 | [[package]] 18 | name = "bitflags" 19 | version = "1.3.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 22 | 23 | [[package]] 24 | name = "buddy_system_allocator" 25 | version = "0.8.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "55703ac5f02c246ce6158eff6ae2dd9e9069917969682b6831f8a5123abb8a48" 28 | dependencies = [ 29 | "spin 0.7.1", 30 | ] 31 | 32 | [[package]] 33 | name = "cc" 34 | version = "1.0.83" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 37 | dependencies = [ 38 | "libc", 39 | ] 40 | 41 | [[package]] 42 | name = "device_tree" 43 | version = "1.0.3" 44 | source = "git+https://github.com/rcore-os/device_tree-rs/#2f2e55fb5238466747fef49d9ce0f59b2e808154" 45 | 46 | [[package]] 47 | name = "elf_rs" 48 | version = "0.1.3" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "21a04ca262fb05c2a49524b0c4bb45a992b27fd0fc55bfff619275185a783e38" 51 | dependencies = [ 52 | "bitflags", 53 | "num-traits", 54 | ] 55 | 56 | [[package]] 57 | name = "lazy_static" 58 | version = "1.4.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 61 | dependencies = [ 62 | "spin 0.5.2", 63 | ] 64 | 65 | [[package]] 66 | name = "libc" 67 | version = "0.2.152" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" 70 | 71 | [[package]] 72 | name = "lock_api" 73 | version = "0.4.11" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 76 | dependencies = [ 77 | "autocfg", 78 | "scopeguard", 79 | ] 80 | 81 | [[package]] 82 | name = "log" 83 | version = "0.4.20" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 86 | 87 | [[package]] 88 | name = "num-traits" 89 | version = "0.2.17" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 92 | dependencies = [ 93 | "autocfg", 94 | ] 95 | 96 | [[package]] 97 | name = "num_enum" 98 | version = "0.5.11" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" 101 | dependencies = [ 102 | "num_enum_derive", 103 | ] 104 | 105 | [[package]] 106 | name = "num_enum_derive" 107 | version = "0.5.11" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" 110 | dependencies = [ 111 | "proc-macro2", 112 | "quote", 113 | "syn 1.0.109", 114 | ] 115 | 116 | [[package]] 117 | name = "proc-macro2" 118 | version = "1.0.76" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" 121 | dependencies = [ 122 | "unicode-ident", 123 | ] 124 | 125 | [[package]] 126 | name = "quote" 127 | version = "1.0.35" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 130 | dependencies = [ 131 | "proc-macro2", 132 | ] 133 | 134 | [[package]] 135 | name = "riscv-virt-guest" 136 | version = "0.2.0" 137 | dependencies = [ 138 | "cc", 139 | "log", 140 | "num_enum", 141 | "sbi-rt", 142 | ] 143 | 144 | [[package]] 145 | name = "rustsbi" 146 | version = "0.4.0-alpha.1" 147 | source = "git+https://github.com/rustsbi/rustsbi#3042206f5f37b62a6693789ee573642df0542e11" 148 | dependencies = [ 149 | "rustsbi-macros", 150 | "sbi-rt", 151 | "sbi-spec", 152 | ] 153 | 154 | [[package]] 155 | name = "rustsbi-macros" 156 | version = "0.0.0" 157 | source = "git+https://github.com/rustsbi/rustsbi#3042206f5f37b62a6693789ee573642df0542e11" 158 | dependencies = [ 159 | "quote", 160 | "syn 2.0.48", 161 | ] 162 | 163 | [[package]] 164 | name = "rustyvisor" 165 | version = "0.2.0" 166 | dependencies = [ 167 | "bare-metal", 168 | "buddy_system_allocator", 169 | "cc", 170 | "device_tree", 171 | "elf_rs", 172 | "lazy_static", 173 | "log", 174 | "num_enum", 175 | "rustsbi", 176 | "spin 0.9.8", 177 | ] 178 | 179 | [[package]] 180 | name = "sbi-rt" 181 | version = "0.0.3-rc.5" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "16fd1c8d9255a1cc094f2dde32da32dbae1ab945656a684b168bf75ed5dccbb6" 184 | dependencies = [ 185 | "sbi-spec", 186 | ] 187 | 188 | [[package]] 189 | name = "sbi-spec" 190 | version = "0.0.7-alpha.3" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "c890910c48a523b53b7cb0a27f20d169facd2281a2cae2220cddd314c7bee951" 193 | dependencies = [ 194 | "static_assertions", 195 | ] 196 | 197 | [[package]] 198 | name = "scopeguard" 199 | version = "1.2.0" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 202 | 203 | [[package]] 204 | name = "spin" 205 | version = "0.5.2" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 208 | 209 | [[package]] 210 | name = "spin" 211 | version = "0.7.1" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162" 214 | 215 | [[package]] 216 | name = "spin" 217 | version = "0.9.8" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 220 | dependencies = [ 221 | "lock_api", 222 | ] 223 | 224 | [[package]] 225 | name = "static_assertions" 226 | version = "1.1.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 229 | 230 | [[package]] 231 | name = "syn" 232 | version = "1.0.109" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 235 | dependencies = [ 236 | "proc-macro2", 237 | "quote", 238 | "unicode-ident", 239 | ] 240 | 241 | [[package]] 242 | name = "syn" 243 | version = "2.0.48" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 246 | dependencies = [ 247 | "proc-macro2", 248 | "quote", 249 | "unicode-ident", 250 | ] 251 | 252 | [[package]] 253 | name = "unicode-ident" 254 | version = "1.0.12" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 257 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "hypervisor", 4 | "guest" 5 | ] 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET := riscv64gc-unknown-none-elf 2 | MODE := debug 3 | 4 | GUEST := guest/target/$(TARGET)/$(MODE)/riscv-virt-guest 5 | 6 | $(GUEST): 7 | cd guest && cargo build 8 | 9 | run: $(GUEST) 10 | cd hypervisor && cargo run -- -drive file=../target/riscv64gc-unknown-none-elf/debug/riscv-virt-guest,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 11 | 12 | clean: 13 | cd hypervisor && cargo clean 14 | cd guest && cargo clean -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rustyvisor 2 | 3 | **NOTE: This project is still work in progress!** 4 | 5 | ## Requirements 6 | 7 | This project relies on the following tools. 8 | 9 | - [riscv/riscv-gnu-toolchain](https://github.com/riscv/riscv-gnu-toolchain) 10 | - [QEMU with RISC-V Hypervisor Extension Emulation](https://github.com/kvm-riscv/qemu) 11 | - Rust nightly 12 | 13 | To run rustyvisor, you need to install them and configure your `PATH` first. 14 | 15 | ## Usage 16 | 17 | ### Run rustyvisor with an example kernel 18 | 19 | You can run the simple guest kernel, whose implementation is in `./guest` directory, as follows. 20 | 21 | ```sh 22 | rustup target add riscv64gc-unknown-none-elf || true 23 | 24 | # build hypervisor 25 | cd hypervisor 26 | cargo build 27 | cd .. 28 | 29 | # build guest 30 | cd guest 31 | cargo build 32 | cd .. 33 | 34 | # change path for qemu in hypervisor/.cargo/config 35 | vim hypervisor/.cargo/config 36 | 37 | # run hypervisor with guest 38 | cd hypervisor 39 | cargo run -- -drive file=../guest/target/riscv64gc-unknown-none-elf/debug/riscv-virt-guest,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 40 | ``` 41 | 42 | ### Run rustyvisor with your own kernel 43 | 44 | Just change the path of the `--drive file=` to run a custom kernel with the hypervisor. 45 | 46 | ### NOTE: Debug rustyvisor with GDB 47 | 48 | You can debug rustyvisor with gdb like this: 49 | 50 | ```sh 51 | # in a shell ... 52 | $ cargo run -- -drive file=../guest/target/riscv64gc-unknown-none-elf/debug/riscv-virt-guest,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -S -s # + additional opts 53 | 54 | # in another shell ... 55 | $ riscv64-unknown-elf-gdb hypervisor/target/riscv64gc-unknown-none-elf/debug/rustyvisor 56 | 57 | # add for the added init commands-x gdb_init_commands 58 | ... 59 | (gdb) target remote :1234 60 | (gdb) continue 61 | ``` 62 | 63 | ## Features (to be supported) 64 | 65 | rustyvisor currently supports the following features: 66 | 67 | - :construction: Run a single VM upon rustyvisor 68 | - [x] load ELF image into the memory space of a VM 69 | - [x] jump to the kernel image loaded to a VM image while enabling guest physical address translation by `hgatp` 70 | - [x] run a tiny kernel that does not require any external hardware like disk devices 71 | - [ ] handle read/write requests for CSRs from a guest 72 | - [ ] handle SBI calls 73 | - [ ] Run multiple VMs upon rustyvisor 74 | - [ ] switch CPU contexts among guests 75 | - [ ] schedule the guest in a fancy way 76 | - [ ] Support multi-core host environment 77 | - [ ] Support device virtualization 78 | - [ ] block device 79 | - [ ] network device 80 | - [ ] input device 81 | - [ ] display device 82 | 83 | # Acknowledgments 84 | rustyvisor is a continuation of the [rvvisor](https://github.com/lmt-swallow/rvvisor) originally created by Takashi Yoneuchi which laid the foundation for this project. -------------------------------------------------------------------------------- /gdb_init_commands: -------------------------------------------------------------------------------- 1 | target remote :1234 2 | define stepdis 3 | stepi 4 | disassemble 5 | end 6 | #add-symbol-file /Users/stemnic/gits/xv6-riscv/kernel/kernel 7 | #break _entry 8 | #add-symbol-file /Users/stemnic/gits/osblog/risc_v/target/riscv64gc-unknown-none-elf/debug/sos 9 | #symbol-file /Users/stemnic/gits/osblog/risc_v/target/riscv64gc-unknown-none-elf/debug/sos 10 | add-symbol-file /Users/stemnic/gits/rvvisor/guest/target/riscv64gc-unknown-none-elf/debug/riscv-virt-guest 11 | #break _start 12 | #break sos::kinit:7 13 | #break hypervisor.rs:208 14 | 15 | #break kernel.rs:20 16 | #break src/kernel.rs:170 17 | #break src/kernel.rs:riscv_virt_guest::kernel::setup_vm 18 | break src/kernel.rs:27 19 | break src/hypervisor.rs:296 20 | break riscv_virt_guest::kernel::sbi_call 21 | break riscv_virt_guest::paging::PageTable::print_page_allocations 22 | #break src/mkernel.rs:144 23 | #break dump_h_csr 24 | #break trap_to_hypervisor 25 | #break 26 | #break sos::virtio::probe 27 | #break src/virtio.rs:245 28 | #break mkernel::switch_to_hypervisor 29 | #break mkernel.rs:92 30 | #break trap_to_mkernel 31 | #break trap_to_hypervisor 32 | #break *0x80000000 33 | 34 | #break hypervisor.rs:192 -------------------------------------------------------------------------------- /guest/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.riscv64gc-unknown-none-elf] 2 | runner = "qemu-system-riscv64 -M 512M -smp 1 -cpu rv64,x-h=true -nographic -machine virt -bios none -kernel" 3 | 4 | [build] 5 | target = "riscv64gc-unknown-none-elf" 6 | -------------------------------------------------------------------------------- /guest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "riscv-virt-guest" 3 | version = "0.2.0" 4 | authors = ["Ole Sivert Aarhug ", "Takashi Yoneuchi "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | num_enum = { version="0.5.1", default-features=false } 9 | log = { version="0.4.11", default-features=false } 10 | sbi-rt = "0.0.3-rc.5" 11 | 12 | [build-dependencies] 13 | cc = "1.0.66" 14 | -------------------------------------------------------------------------------- /guest/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, path::PathBuf}; 2 | 3 | fn main() { 4 | let out = PathBuf::from(env::var_os("OUT_DIR").unwrap()); 5 | let ld = &out.join("linker.ld"); 6 | 7 | std::fs::write(ld, LINKER_SCRIPT).unwrap(); 8 | 9 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 10 | println!("cargo:rustc-link-search={}", out.display()); 11 | } 12 | 13 | const LINKER_SCRIPT: &[u8] = b" 14 | OUTPUT_ARCH(riscv) 15 | 16 | ENTRY(entrypoint) 17 | 18 | SECTIONS 19 | { 20 | . = 0x80000000; 21 | .text.entrypoint : 22 | { 23 | PROVIDE(_elf_start = .); 24 | *(.text.entrypoint); 25 | } 26 | 27 | .text : 28 | { 29 | *(.text) *(.text.*); 30 | } 31 | 32 | .rodata : 33 | { 34 | *(.rdata .rodata. .rodata.*); 35 | } 36 | 37 | . = ALIGN(4096); 38 | .data : 39 | { 40 | *(.data .data.*); 41 | } 42 | 43 | _bss_start = .; 44 | .bss : 45 | { 46 | *(.bss .bss.*); 47 | PROVIDE(_elf_end = .); 48 | } 49 | }"; 50 | -------------------------------------------------------------------------------- /guest/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" -------------------------------------------------------------------------------- /guest/src/boot.S: -------------------------------------------------------------------------------- 1 | .option norvc 2 | 3 | .section .text.entrypoint 4 | .global entrypoint 5 | 6 | entrypoint: 7 | # load stack addr 8 | la sp, _stack_end 9 | # jump to rust code 10 | tail rust_entrypoint -------------------------------------------------------------------------------- /guest/src/boot.rs: -------------------------------------------------------------------------------- 1 | use core::arch::global_asm; 2 | 3 | // include raw assembly codes in ./asm 4 | global_asm!(include_str!("boot.S")); 5 | -------------------------------------------------------------------------------- /guest/src/debug.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | 3 | #[panic_handler] 4 | fn panic(info: &core::panic::PanicInfo) -> ! { 5 | print!("abort: "); 6 | if let Some(p) = info.location() { 7 | println!( 8 | "line {}, file {}: {}", 9 | p.line(), 10 | p.file(), 11 | info.message().unwrap() 12 | ); 13 | } else { 14 | println!("no information available."); 15 | } 16 | abort(); 17 | } 18 | 19 | #[no_mangle] 20 | extern "C" fn abort() -> ! { 21 | loop { 22 | unsafe { 23 | asm!("wfi", options(nostack)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /guest/src/kernel.S: -------------------------------------------------------------------------------- 1 | .option norvc 2 | #.altmacro 3 | 4 | .section .text.kernel 5 | .global trap_to_kernel 6 | .global _intr_stack_end 7 | 8 | #.macro load_gp i, base 9 | # ld x\i, ((\i)*8)(\base) 10 | #.endm 11 | # 12 | #.macro save_gp i, base 13 | # sd x\i, ((\i)*8)(\base) 14 | #.endm 15 | 16 | .align 4 17 | trap_to_kernel: 18 | csrrw t6, sscratch, t6 19 | 20 | # save GPRs 21 | #.set i, 1 22 | #.rept 30 23 | # save_gp %i, t6 24 | # .set i, i+1 25 | #.endr 26 | 27 | mv t5, t6 28 | csrr t6, sscratch 29 | #save_gp 31, t5 30 | 31 | # # save FPRs 32 | # .set i, 0 33 | # .rept 32 34 | # save_fp %i, t5 35 | # .set i, i+1 36 | # .endr 37 | 38 | # save sscratch 39 | csrw sscratch, t5 40 | 41 | csrr a0, sepc 42 | csrr a1, stval 43 | csrr a2, scause 44 | csrr a3, sstatus 45 | csrr a4, sscratch 46 | la sp, _intr_stack_end 47 | 48 | # ------- 49 | 50 | # jump to a handler written in Rust 51 | call rust_trap_handler 52 | 53 | # ------- 54 | 55 | # after getting back from rust_strap_handler ... 56 | csrw sepc, a0 57 | csrr t6, sscratch 58 | 59 | # # restore FPRs 60 | # .set i, 0 61 | # .rept 32 62 | # load_fp %i, t6 63 | # .set i, i+1 64 | # .endr 65 | 66 | # restore GPRs 67 | #.set i, 1 68 | #.rept 31 69 | # load_gp %i, t6 70 | # .set i, i+1 71 | #.endr 72 | 73 | sret 74 | 75 | .bss 76 | .global _stack_start, _stack_end 77 | _stack_start: 78 | # allocate 1 MB 79 | .skip 1024 * 1024 80 | _stack_end: 81 | .skip 1024 82 | 83 | .global _intr_stack_start, _intr_stack_end 84 | _intr_stack_start: 85 | .skip 1024 * 1024 # 1 MB 86 | _intr_stack_end: 87 | .skip 1024 -------------------------------------------------------------------------------- /guest/src/kernel.rs: -------------------------------------------------------------------------------- 1 | global_asm!(include_str!("kernel.S")); 2 | 3 | use crate::memlayout; 4 | use crate::paging; 5 | use crate::uart; 6 | use core::arch::asm; 7 | use core::arch::global_asm; 8 | use core::fmt::Error; 9 | 10 | extern "C" { 11 | #[link_name = "trap_to_kernel"] 12 | pub fn trap(); 13 | } 14 | 15 | #[no_mangle] 16 | pub extern "C" fn rust_entrypoint() -> ! { 17 | if let Err(e) = init() { 18 | panic!("failed to initialize. {:?}", e); 19 | }; 20 | println!("hello world from a guest"); 21 | 22 | 23 | setup_vm(); 24 | 25 | let call = sbi_rt::set_timer(0xdeadbeef); 26 | println!("Sbi call error: {}, value: {}", call.error, call.value); 27 | assert_eq!(call.error, 0); 28 | 29 | println!("Testing timer"); 30 | enable_timer_interrupts(); 31 | 32 | loop {} 33 | } 34 | 35 | fn setup_vm() { 36 | paging::init(); 37 | 38 | let root_page = paging::alloc(); 39 | println!( 40 | "a page 0x{:016x} was allocated for a guest page address translation page table", 41 | root_page.address().to_usize() 42 | ); 43 | let root_pt = paging::PageTable::from_page(root_page); 44 | 45 | let map_page_num = (unsafe{memlayout::elf_end()} - memlayout::DRAM_START) 46 | / (memlayout::PAGE_SIZE as usize) 47 | + 1; 48 | for i in 0..map_page_num { 49 | let vaddr = memlayout::DRAM_START + i * (memlayout::PAGE_SIZE as usize); 50 | let page = paging::Page::from_address(paging::PhysicalAddress::new(vaddr)); 51 | root_pt.map( 52 | paging::VirtualAddress::new(vaddr), 53 | &page, 54 | (paging::PageTableEntryFlag::Read as u16) 55 | | (paging::PageTableEntryFlag::Write as u16) 56 | | (paging::PageTableEntryFlag::Execute as u16) 57 | ) 58 | } 59 | 60 | let vaddr = memlayout::UART_BASE; 61 | let page = paging::Page::from_address(paging::PhysicalAddress::new(vaddr)); 62 | root_pt.map( 63 | paging::VirtualAddress::new(vaddr), 64 | &page, 65 | (paging::PageTableEntryFlag::Read as u16) 66 | | (paging::PageTableEntryFlag::Write as u16) 67 | | (paging::PageTableEntryFlag::Execute as u16) 68 | ); 69 | 70 | 71 | let map_page_num = (memlayout::USER_TEST_END - memlayout::USER_TEST_START) 72 | / (memlayout::PAGE_SIZE as usize) 73 | + 1; 74 | for i in 0..map_page_num { 75 | let vaddr = memlayout::USER_TEST_START + i * (memlayout::PAGE_SIZE as usize); 76 | let page = paging::Page::from_address(paging::PhysicalAddress::new(vaddr)); 77 | root_pt.map( 78 | paging::VirtualAddress::new(vaddr), 79 | &page, 80 | (paging::PageTableEntryFlag::Read as u16) 81 | | (paging::PageTableEntryFlag::Write as u16) 82 | | (paging::PageTableEntryFlag::Execute as u16) 83 | ) 84 | } 85 | 86 | let mut satp: usize = 0; 87 | satp |= (8 as usize) << 60; 88 | satp |= (0 as usize) << 44; 89 | satp |= root_pt.page.address().to_ppn(); 90 | 91 | println!("satp to be written: 0x{:016x}", satp); 92 | root_pt.print_page_allocations(); 93 | 94 | unsafe{ 95 | asm!( 96 | "csrw satp, {0}", in(reg) satp, options(nostack) 97 | ); 98 | 99 | //asm!("sfence.vma"); 100 | } 101 | } 102 | 103 | fn enable_timer_interrupts() { 104 | 105 | let mut sstatus:usize; 106 | unsafe{ 107 | asm!( 108 | "csrr {0}, sstatus", out(reg) sstatus, options(nostack) 109 | ) 110 | } 111 | let sie_mask = 1 << 1 as usize; 112 | let result: usize = sstatus | sie_mask; 113 | unsafe{ 114 | asm!( 115 | "csrw sstatus, {0}", in(reg) result, options(nostack) 116 | ) 117 | } 118 | 119 | 120 | let mut sie:usize; 121 | unsafe{ 122 | asm!( 123 | "csrr {0}, sie", out(reg) sie, options(nostack) 124 | ) 125 | } 126 | let sie_mask = 1 << 5 as usize; 127 | let result: usize = sie | sie_mask; 128 | unsafe{ 129 | asm!( 130 | "csrw sie, {0}", in(reg) result, options(nostack) 131 | ) 132 | } 133 | 134 | } 135 | 136 | fn clear_timers() { 137 | let mut sie:usize; 138 | unsafe{ 139 | asm!( 140 | "csrr {0}, sie", out(reg) sie, options(nostack) 141 | ) 142 | } 143 | let sie_mask = !(1 << 5) as usize; 144 | let result: usize = sie & sie_mask; 145 | unsafe{ 146 | asm!( 147 | "csrw sie, {0}", in(reg) result, options(nostack) 148 | ) 149 | } 150 | } 151 | 152 | pub fn init() -> Result<(), Error> { 153 | // init UART 154 | uart::Uart::new(memlayout::UART_BASE).init(); 155 | 156 | println!("trap set to: {:?}", &(trap as unsafe extern "C" fn()) ); 157 | 158 | unsafe{ 159 | asm!( 160 | "csrw stvec, {0}", in(reg) (trap as unsafe extern "C" fn() as usize) 161 | ); 162 | } 163 | let mut stvec: usize; 164 | unsafe{ 165 | asm!( 166 | "csrr {0}, stvec", out(reg) stvec 167 | ); 168 | } 169 | 170 | println!("stvec is set to: 0x{:016x}", stvec); 171 | 172 | 173 | // leave 174 | Ok(()) 175 | } 176 | 177 | #[repr(C)] 178 | #[derive(Clone, Copy, Debug)] 179 | pub struct TrapFrame { 180 | pub regs: [usize; 32], // 0 - 255 181 | pub fregs: [usize; 32], // 256 - 511 182 | pub pc: usize, // 512 183 | } 184 | 185 | #[no_mangle] 186 | pub extern "C" fn rust_trap_handler( 187 | sepc: usize, // a0 188 | stval: usize, // a1 189 | scause: usize, // a2 190 | sstatus: usize, // a3 191 | frame: *mut TrapFrame, // a4 192 | ) -> usize { 193 | println!("<--------- trap --------->"); 194 | println!("sepc: 0x{:016x}", sepc,); 195 | println!("stval: 0x{:016x}", stval,); 196 | println!("scause: 0x{:016x}", scause,); 197 | println!("sstatus: 0x{:016x}", sstatus,); 198 | let is_async = scause >> 63 & 1 == 1; 199 | let cause_code = scause & 0xfff; 200 | if is_async { 201 | match cause_code { 202 | 5 => { 203 | //log::info!("M mode timer triggered"); 204 | println!("vm timer interrupt triggered"); 205 | //clear_timers(); 206 | let call = sbi_rt::set_timer(0xdeadbeef); 207 | println!("Sbi call error: {}, value: {}", call.error, call.value); 208 | assert_eq!(call.error, 0); 209 | 210 | } 211 | _ => { 212 | unimplemented!("Unknown M-mode interrupt id: {}", cause_code); 213 | } 214 | } 215 | } else { 216 | match cause_code { 217 | _ => { 218 | unimplemented!("Unknown M-mode Exception id: {}", cause_code); 219 | } 220 | } 221 | } 222 | sepc 223 | } 224 | -------------------------------------------------------------------------------- /guest/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(panic_info_message, global_asm, asm)] 4 | 5 | // extenal crates 6 | extern crate log; 7 | 8 | // modules 9 | #[macro_use] 10 | pub mod uart; 11 | pub mod boot; 12 | mod debug; 13 | pub mod kernel; 14 | pub mod memlayout; 15 | pub mod paging; 16 | -------------------------------------------------------------------------------- /guest/src/memlayout.rs: -------------------------------------------------------------------------------- 1 | // generic constants 2 | ///// 3 | 4 | pub const PAGE_SIZE: u16 = 4096; 5 | pub static UART_BASE: usize = 0x1000_0000; 6 | pub static DRAM_START: usize = 0x8000_0000; 7 | pub static DRAM_END: usize = 0x8200_0000; 8 | pub static USER_TEST_START: usize = 0x8201_0000; 9 | pub static USER_TEST_END: usize = 0x8202_0000; 10 | 11 | extern "C" { 12 | static _elf_start: usize; 13 | static _elf_end: usize; 14 | } 15 | 16 | pub unsafe fn elf_start() -> usize { 17 | unsafe { &_elf_start as *const usize as usize } 18 | } 19 | 20 | pub unsafe fn elf_end() -> usize { 21 | unsafe { &_elf_end as *const usize as usize } 22 | } -------------------------------------------------------------------------------- /guest/src/paging.rs: -------------------------------------------------------------------------------- 1 | // TODO (enhancement): 2 | // if we run more rich guest OS or add more rich features to hypervisor, 3 | // we need to refine this implmentation :-D 4 | 5 | use crate::memlayout::{DRAM_END, PAGE_SIZE, elf_end}; 6 | 7 | // VirtualAddress 8 | ///// 9 | 10 | #[derive(Debug)] 11 | pub struct VirtualAddress { 12 | addr: usize, 13 | } 14 | 15 | impl VirtualAddress { 16 | pub fn new(addr: usize) -> VirtualAddress { 17 | VirtualAddress { addr: addr } 18 | } 19 | 20 | pub fn new_from_vpn(vpn : [usize; 3]) -> VirtualAddress { 21 | let addr = 22 | (vpn[2]) << 30 | 23 | (vpn[1]) << 21 | 24 | (vpn[0]) << 12 25 | ; 26 | VirtualAddress { addr: addr } 27 | } 28 | 29 | pub fn to_vpn(&self) -> [usize; 3] { 30 | [ 31 | (self.addr >> 12) & 0x1ff, //L0 9bit 32 | (self.addr >> 21) & 0x1ff, //L1 9bit 33 | (self.addr >> 30) & 0x3ff, //L2 11bit 34 | ] 35 | } 36 | 37 | pub fn to_offset(&self) -> usize { 38 | self.addr & 0x3ff //Offsett 12bit 39 | } 40 | 41 | pub fn to_usize(&self) -> usize { 42 | self.addr 43 | } 44 | 45 | pub fn as_pointer(&self) -> *mut usize { 46 | self.addr as *mut usize 47 | } 48 | } 49 | 50 | // PhysicalAddress 51 | ///// 52 | 53 | #[derive(Copy, Clone, Debug)] 54 | pub struct PhysicalAddress { 55 | addr: usize, 56 | } 57 | 58 | impl PhysicalAddress { 59 | pub fn new(addr: usize) -> PhysicalAddress { 60 | PhysicalAddress { addr: addr } 61 | } 62 | 63 | pub fn to_ppn(&self) -> usize { 64 | self.addr >> 12 //ppn 44bit 65 | } 66 | 67 | pub fn to_ppn_array(&self) -> [usize; 3] { 68 | [ 69 | (self.addr >> 12) & 0x1ff, //L0 9bit 70 | (self.addr >> 21) & 0x1ff, //L1 9bit 71 | (self.addr >> 30) & 0x3ff_ffff, //L2 26bit 72 | ] 73 | } 74 | 75 | pub fn to_usize(&self) -> usize { 76 | self.addr 77 | } 78 | 79 | pub fn as_pointer(&self) -> *mut usize { 80 | self.addr as *mut usize 81 | } 82 | } 83 | 84 | // Page 85 | ///// 86 | 87 | #[derive(Copy, Clone, Debug)] 88 | pub struct Page { 89 | addr: PhysicalAddress, 90 | } 91 | 92 | impl Page { 93 | pub fn from_address(addr: PhysicalAddress) -> Page { 94 | Page { addr: addr } 95 | } 96 | 97 | pub fn address(&self) -> PhysicalAddress { 98 | self.addr 99 | } 100 | /// Clears allocated memory for page 101 | pub fn clear(&self) { 102 | unsafe { 103 | let ptr = self.addr.as_pointer(); 104 | for i in 0..512 { 105 | ptr.add(i).write(0) 106 | } 107 | } 108 | } 109 | } 110 | 111 | // Page Allocator (soooo tiny version) 112 | ///// 113 | 114 | static mut base_addr: usize = 0; 115 | static mut last_index: usize = 0; 116 | static mut initialized: bool = false; 117 | 118 | pub fn init() { 119 | unsafe { 120 | base_addr = (elf_end() & !(0xfff as usize)) + 4096; // Mock heap that is page aligned 121 | last_index = 0; 122 | initialized = true; 123 | } 124 | } 125 | 126 | pub fn set_alloc_base(addr: usize) { 127 | unsafe { 128 | base_addr = addr; 129 | } 130 | } 131 | 132 | /// Allocated a page in the mock heap at the end of the elf 133 | pub fn alloc() -> Page { 134 | // TODO: this unsafe block is evil! 135 | unsafe { 136 | if !initialized { 137 | panic!("page manager was used but not initialized"); 138 | } 139 | 140 | last_index += 1; 141 | let addr = base_addr + (PAGE_SIZE as usize) * (last_index - 1); 142 | if addr > DRAM_END { 143 | panic!("memory exhausted; 0x{:016x}", addr) 144 | } 145 | let p = Page::from_address(PhysicalAddress::new(addr)); 146 | p.clear(); 147 | p 148 | } 149 | } 150 | 151 | 152 | 153 | pub fn alloc_continuous(num: usize) -> Page { 154 | if num <= 0 { 155 | panic!("invalid arg for alloc_contenious: {}", num); 156 | } 157 | 158 | let first = alloc(); 159 | for _ in 0..(num - 1) { 160 | let _ = alloc(); 161 | } 162 | 163 | first 164 | } 165 | 166 | // Page Table 167 | ///// 168 | 169 | #[derive(Debug)] 170 | struct PageTableEntry { 171 | pub ppn: [usize; 3], 172 | pub flags: u16, 173 | } 174 | 175 | pub enum PageTableEntryFlag { 176 | Valid = 1 << 0, 177 | Read = 1 << 1, 178 | Write = 1 << 2, 179 | Execute = 1 << 3, 180 | User = 1 << 4, 181 | Global = 1 << 5, 182 | Access = 1 << 6, 183 | Dirty = 1 << 7, 184 | // TODO (enhancement): RSW 185 | } 186 | 187 | impl PageTableEntry { 188 | pub fn from_value(v: usize) -> PageTableEntry { 189 | let ppn = [ (v >> 10) & 0x1ff, // PPN[0] 9 bit 190 | (v >> 19) & 0x1ff, // PPN[1] 9 bit 191 | (v >> 28) & 0x3ff_ffff]; // PPN[2] 26 bit 192 | PageTableEntry { 193 | ppn: ppn, 194 | flags: (v & (0x1ff as usize)) as u16, // flags 8 bit (seems like this is 9?) 195 | } 196 | } 197 | 198 | pub unsafe fn from_memory(paddr: PhysicalAddress) -> PageTableEntry { 199 | let ptr = paddr.as_pointer(); 200 | let entry = *ptr; 201 | PageTableEntry::from_value(entry) 202 | } 203 | 204 | pub fn to_usize(&self) -> usize { 205 | (if (self.ppn[2] >> 25) & 1 > 0 { 206 | 0x3ff << 54 207 | } else { 208 | 0 209 | }) | ((self.ppn[2] as usize) << 28) 210 | | ((self.ppn[1] as usize) << 19) 211 | | ((self.ppn[0] as usize) << 10) 212 | | (self.flags as usize) 213 | } 214 | 215 | pub fn next_page(&self) -> Page { 216 | Page::from_address(PhysicalAddress::new( 217 | (self.ppn[2] << 30) | (self.ppn[1] << 21) | (self.ppn[0] << 12), 218 | )) 219 | } 220 | 221 | pub fn set_flag(&mut self, flag: PageTableEntryFlag) { 222 | self.flags |= flag as u16; 223 | } 224 | 225 | pub fn is_valid(&self) -> bool { 226 | self.flags & (PageTableEntryFlag::Valid as u16) != 0 227 | } 228 | // A leaf has one or more RWX bits set 229 | pub fn is_leaf(&self) -> bool { 230 | self.flags & 0xe != 0 231 | } 232 | 233 | pub fn is_branch(&self) -> bool { 234 | !self.is_leaf() 235 | } 236 | } 237 | 238 | pub struct PageTable { 239 | pub page: Page, 240 | } 241 | 242 | // TODO (enhancement): this naming is not so good. 243 | // This implementation assumes the paging would be done with Sv39, 244 | // but the word 'page table" is a more general idea. 245 | // We can rename this like `Sv39PageTable` or change the implementation in a more polymorphic way. 246 | impl PageTable { 247 | fn set_entry(&self, i: usize, entry: PageTableEntry) { 248 | let ptr = self.page.address().as_pointer() as *mut usize; 249 | unsafe { ptr.add(i).write(entry.to_usize()) } 250 | } 251 | 252 | fn get_entry(&self, i: usize) -> PageTableEntry { 253 | let ptr = self.page.address().as_pointer() as *mut usize; 254 | unsafe { PageTableEntry::from_value(ptr.add(i).read()) } 255 | } 256 | 257 | pub fn from_page(page: Page) -> PageTable { 258 | PageTable { page: page } 259 | } 260 | 261 | pub fn resolve(&self, vaddr: &VirtualAddress) -> PhysicalAddress { 262 | self.resolve_intl(vaddr, self, 2) 263 | } 264 | 265 | fn resolve_intl( 266 | &self, 267 | vaddr: &VirtualAddress, 268 | pt: &PageTable, 269 | level: usize, 270 | ) -> PhysicalAddress { 271 | let vpn = vaddr.to_vpn(); 272 | 273 | let entry = pt.get_entry(vpn[level]); 274 | if !entry.is_valid() { 275 | panic!("failed to resolve vaddr: 0x{:016x}", vaddr.addr) 276 | } 277 | 278 | if level == 0 { 279 | let addr_base = entry.next_page().address().to_usize(); 280 | PhysicalAddress::new(addr_base | vaddr.to_offset()) 281 | } else { 282 | let next_page = entry.next_page(); 283 | let new_pt = PageTable::from_page(next_page); 284 | self.resolve_intl(vaddr, &new_pt, level - 1) 285 | } 286 | } 287 | 288 | pub fn map(&self, vaddr: VirtualAddress, dest: &Page, perm: u16) { 289 | self.map_intl(vaddr, dest, self, perm, 2) 290 | } 291 | 292 | fn map_intl( 293 | &self, 294 | vaddr: VirtualAddress, 295 | dest: &Page, 296 | pt: &PageTable, 297 | perm: u16, 298 | level: usize, 299 | ) { 300 | let vpn = vaddr.to_vpn(); 301 | 302 | if level == 0 { 303 | // register `dest` addr 304 | let new_entry = PageTableEntry::from_value( 305 | ((dest.address().to_usize() as i64 >> 2) as usize) 306 | | (PageTableEntryFlag::Valid as usize) 307 | | (PageTableEntryFlag::Dirty as usize) 308 | | (PageTableEntryFlag::Access as usize) 309 | | (perm as usize), 310 | ); 311 | pt.set_entry(vpn[0], new_entry); 312 | } else { 313 | // walk the page table 314 | let entry = pt.get_entry(vpn[level]); 315 | if !entry.is_valid() { 316 | // if no entry found, create new page and assign it. 317 | let new_page = alloc(); 318 | let new_entry = PageTableEntry::from_value( 319 | ((new_page.address().to_usize() as i64 >> 2) as usize) 320 | | (PageTableEntryFlag::Valid as usize), 321 | ); 322 | pt.set_entry(vpn[level], new_entry); 323 | let new_pt = PageTable::from_page(new_page); 324 | self.map_intl(vaddr, dest, &new_pt, perm, level - 1); 325 | } else { 326 | let next_page = entry.next_page(); 327 | let new_pt = PageTable::from_page(next_page); 328 | self.map_intl(vaddr, dest, &new_pt, perm, level - 1); 329 | }; 330 | } 331 | } 332 | 333 | fn print_walk_page_table(&self, next_pt:PageTable, level: usize, vpn: [usize ; 3]) -> usize { 334 | // Very messy but it works 335 | let mut physical_addr = 0; 336 | let mut virtual_addr = 0; 337 | let mut total_pages = 0; 338 | let mut total_valid_entries = 0; 339 | for i in 0..512{ 340 | let entry = next_pt.get_entry(i); 341 | if entry.is_valid(){ 342 | total_valid_entries = total_valid_entries + 1; 343 | if level == 0 { 344 | let new_physical_addr = entry.to_usize() << 2 & !0x3ff ; 345 | //print!("vpn:{:?} ppn:{:?} entry: {:?}, physical 0x{:x} ", vpn, entry.ppn, entry, new_physical_addr); 346 | if new_physical_addr - PAGE_SIZE as usize == physical_addr { 347 | //print!("SAME "); 348 | virtual_addr = VirtualAddress::new_from_vpn(vpn).to_usize() + i * PAGE_SIZE as usize; 349 | //println!("Virt: 0x{:x} => Phys: 0x{:x}", virtual_addr, new_physical_addr); 350 | physical_addr = new_physical_addr; 351 | total_pages = total_pages + 1; 352 | } else { 353 | if total_pages != 0 { 354 | println!("..."); 355 | println!("Virt: 0x{:x} => Phys: 0x{:x}", virtual_addr, physical_addr); 356 | println!("Num pages after each other: {}", total_pages); 357 | println!("") 358 | }else{ 359 | println!("") 360 | } 361 | physical_addr = new_physical_addr; 362 | total_pages = total_pages + 1; 363 | virtual_addr = VirtualAddress::new_from_vpn(vpn).to_usize() + i * PAGE_SIZE as usize; 364 | println!("Virt: 0x{:x} => Phys: 0x{:x}", virtual_addr, new_physical_addr); 365 | total_pages = 0; 366 | } 367 | } else { 368 | let mut vpn = vpn; 369 | vpn[level] = i; 370 | let next_page = entry.next_page(); 371 | let new_pt = PageTable::from_page(next_page); 372 | total_valid_entries = total_valid_entries + self.print_walk_page_table(new_pt, level - 1, vpn); 373 | } 374 | } 375 | } 376 | if total_pages != 0 { 377 | println!("..."); 378 | println!("Virt: 0x{:x} => Phys: 0x{:x}", virtual_addr, physical_addr); 379 | println!("Num pages after each other: {}", total_pages); 380 | println!("") 381 | } 382 | total_valid_entries 383 | } 384 | 385 | pub fn print_page_allocations(&self){ 386 | // Walking all the entries in the pagetable 387 | // assumes Sv39 388 | if unsafe {initialized} { 389 | let pt = PageTable::from_page(self.page); 390 | println!(); 391 | println!( 392 | "PAGE ALLOCATION TABLE\nALLOCATED: 0x{:x} -> 0x{:x}", 393 | unsafe{base_addr}, 394 | unsafe{base_addr + (PAGE_SIZE as usize) * (last_index - 1)} 395 | ); 396 | println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 397 | let vpn = [0 ; 3]; 398 | let total_entries = self.print_walk_page_table(pt, 2, vpn); 399 | //println!("{:?}", pagemapping); 400 | 401 | println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 402 | println!( 403 | "Allocated: {:>6} pages ({:>10} bytes).", 404 | total_entries, 405 | total_entries * PAGE_SIZE as usize 406 | ); 407 | /* 408 | println!( 409 | "Free : {:>6} pages ({:>10} bytes).", 410 | num_pages - num, 411 | (num_pages - num) * PAGE_SIZE 412 | ); 413 | */ 414 | println!(); 415 | 416 | } else { 417 | println!("Pageing not initilized"); 418 | } 419 | 420 | } 421 | } 422 | 423 | /* 424 | /// Print all page allocations 425 | /// This is mainly used for debugging. 426 | 427 | pub fn print_page_allocations() { 428 | unsafe { 429 | let num_pages = last_index+1; 430 | let mut beg = base_addr as *const PageTableEntry; 431 | let end = beg.add(num_pages); 432 | let alloc_beg = base_addr; 433 | let alloc_end = base_addr + num_pages * PAGE_SIZE as usize; 434 | println!(); 435 | println!( 436 | "PAGE ALLOCATION TABLE\nMETA: {:p} -> {:p}\nPHYS: \ 437 | 0x{:x} -> 0x{:x}", 438 | beg, end, alloc_beg, alloc_end 439 | ); 440 | println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 441 | let mut num = 0; 442 | while beg < end { 443 | if (*beg).is_taken() { 444 | let start = beg as usize; 445 | let memaddr = base_addr 446 | + (start - HEAP_START) 447 | * PAGE_SIZE; 448 | print!("0x{:x} => ", memaddr); 449 | loop { 450 | num += 1; 451 | if (*beg).is_last() { 452 | let end = beg as usize; 453 | let memaddr = base_addr 454 | + (end - HEAP_START) 455 | * PAGE_SIZE 456 | + PAGE_SIZE - 1; 457 | print!( 458 | "0x{:x}: {:>3} page(s)", 459 | memaddr, 460 | (end - start + 1) 461 | ); 462 | println!("."); 463 | break; 464 | } 465 | beg = beg.add(1); 466 | } 467 | } 468 | beg = beg.add(1); 469 | } 470 | println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 471 | println!( 472 | "Allocated: {:>6} pages ({:>10} bytes).", 473 | num, 474 | num * PAGE_SIZE 475 | ); 476 | println!( 477 | "Free : {:>6} pages ({:>10} bytes).", 478 | num_pages - num, 479 | (num_pages - num) * PAGE_SIZE 480 | ); 481 | println!(); 482 | } 483 | } 484 | */ -------------------------------------------------------------------------------- /guest/src/uart.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{Error, Write}; 2 | 3 | pub struct Uart { 4 | addr_base: *mut u8, 5 | } 6 | 7 | impl Write for Uart { 8 | fn write_str(&mut self, out: &str) -> Result<(), Error> { 9 | for c in out.bytes() { 10 | self.put(c); 11 | } 12 | Ok(()) 13 | } 14 | } 15 | 16 | impl Uart { 17 | pub fn new(uart_base: usize) -> Self { 18 | let ptr = uart_base as *mut u8; 19 | Uart { addr_base: ptr } 20 | } 21 | 22 | fn thr(&mut self) -> *mut u8 { 23 | unsafe { self.addr_base.offset(0) } 24 | } 25 | 26 | fn rbr(&mut self) -> *mut u8 { 27 | unsafe { self.addr_base.offset(0) } 28 | } 29 | 30 | fn ier(&mut self) -> *mut u8 { 31 | unsafe { self.addr_base.offset(1) } 32 | } 33 | 34 | fn fcr(&mut self) -> *mut u8 { 35 | unsafe { self.addr_base.offset(2) } 36 | } 37 | 38 | fn lcr(&mut self) -> *mut u8 { 39 | unsafe { self.addr_base.offset(3) } 40 | } 41 | 42 | fn lsr(&mut self) -> *mut u8 { 43 | unsafe { self.addr_base.offset(5) } 44 | } 45 | 46 | pub fn init(&mut self) { 47 | unsafe { 48 | // enable interrupts 49 | self.ier().write_volatile(1 << 0); 50 | 51 | // enable FIFO 52 | self.fcr().write_volatile(1 << 0); 53 | 54 | // set WLS to 8 bits 55 | self.lcr().write_volatile((1 << 0) | (1 << 1)); 56 | } 57 | } 58 | 59 | pub fn put(&mut self, c: u8) { 60 | unsafe { 61 | // spin until bit 5 of LSR holds 62 | while self.lsr().read_volatile() & (1 << 5) == 0 {} 63 | 64 | // add `c` to the FIFO 65 | self.thr().write_volatile(c); 66 | } 67 | } 68 | 69 | pub fn get(&mut self) -> Option { 70 | unsafe { 71 | // read LSR first in order to check whether read FIFO has any data or not 72 | if self.lsr().read_volatile() & (1 << 0) == 0 { 73 | None 74 | } else { 75 | Some(self.rbr().offset(0).read_volatile()) 76 | } 77 | } 78 | } 79 | } 80 | 81 | #[macro_export] 82 | macro_rules! print 83 | { 84 | ($($args:tt)+) => ({ 85 | use core::fmt::Write; 86 | let _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+); 87 | }); 88 | } 89 | 90 | #[macro_export] 91 | macro_rules! println 92 | { 93 | () => ({ 94 | print!("\r\n") 95 | }); 96 | ($fmt:expr) => ({ 97 | print!(concat!($fmt, "\r\n")) 98 | }); 99 | ($fmt:expr, $($args:tt)+) => ({ 100 | print!(concat!($fmt, "\r\n"), $($args)+) 101 | }); 102 | } 103 | -------------------------------------------------------------------------------- /guest/src/util.rs: -------------------------------------------------------------------------------- 1 | pub mod jump; 2 | pub mod logger; 3 | -------------------------------------------------------------------------------- /hypervisor/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.riscv64gc-unknown-none-elf] 2 | runner = "qemu-system-riscv64 -M 512M -smp 1 -cpu rv64 -nographic -machine virt -bios none -kernel" 3 | 4 | [build] 5 | target = "riscv64gc-unknown-none-elf" 6 | -------------------------------------------------------------------------------- /hypervisor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustyvisor" 3 | version = "0.2.0" 4 | authors = ["Ole Sivert Aarhug ","Takashi Yoneuchi "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | num_enum = { version="0.5.1", default-features=false } 9 | log = { version="0.4.11", default-features=false } 10 | elf_rs = { version="0.1.3", default-features=false } 11 | rustsbi = { version = "0.4.0", features = ["forward"]} 12 | device_tree = { git = "https://github.com/rcore-os/device_tree-rs/" } 13 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 14 | spin = "0.9.2" 15 | buddy_system_allocator = "0.8" 16 | bare-metal = "1.0.0" 17 | 18 | [build-dependencies] 19 | cc = "1.0.66" 20 | 21 | [unstable] 22 | build-std = ["alloc"] 23 | -------------------------------------------------------------------------------- /hypervisor/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, path::PathBuf}; 2 | 3 | fn main() { 4 | let out = PathBuf::from(env::var_os("OUT_DIR").unwrap()); 5 | let ld = &out.join("linker.ld"); 6 | 7 | std::fs::write(ld, LINKER_SCRIPT).unwrap(); 8 | 9 | println!("cargo:rustc-link-arg=-T{}", ld.display()); 10 | println!("cargo:rustc-link-search={}", out.display()); 11 | } 12 | 13 | const LINKER_SCRIPT: &[u8] = b" 14 | OUTPUT_ARCH(riscv) 15 | 16 | ENTRY(m_entrypoint) 17 | 18 | SECTIONS 19 | { 20 | . = 0x80000000; 21 | .text.entrypoint : 22 | { 23 | PROVIDE(_elf_start = .); 24 | *(.text.entrypoint); 25 | } 26 | 27 | .text : 28 | { 29 | *(.text) *(.text.*); 30 | } 31 | 32 | .rodata : 33 | { 34 | *(.rdata .rodata. .rodata.*); 35 | } 36 | 37 | . = ALIGN(4096); 38 | .data : 39 | { 40 | *(.data .data.*); 41 | } 42 | 43 | _bss_start = .; 44 | .bss : 45 | { 46 | *(.bss .bss.*); 47 | PROVIDE(_elf_end = .); 48 | } 49 | }"; 50 | -------------------------------------------------------------------------------- /hypervisor/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" -------------------------------------------------------------------------------- /hypervisor/src/boot.S: -------------------------------------------------------------------------------- 1 | .option norvc 2 | 3 | .section .text.entrypoint 4 | .global m_entrypoint 5 | 6 | m_entrypoint: 7 | la a0, _trapframe 8 | csrw mscratch, a0 9 | # load stack addr 10 | la sp, _m_stack_end 11 | # jump to rust code 12 | tail rust_m_entrypoint 13 | 14 | .global _trapframe 15 | _trapframe: 16 | .skip 1024 * 1024 17 | -------------------------------------------------------------------------------- /hypervisor/src/boot.rs: -------------------------------------------------------------------------------- 1 | use core::arch::global_asm; 2 | 3 | // include raw assembly codes in ./asm 4 | global_asm!(include_str!("boot.S")); 5 | -------------------------------------------------------------------------------- /hypervisor/src/clint.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | // Based on https://github.com/rustsbi/rustsbi-qemu/blob/main/rustsbi-qemu/src/clint.rs 4 | 5 | use crate::riscv; 6 | 7 | pub struct Clint { 8 | base: usize, 9 | } 10 | 11 | impl Clint { 12 | #[inline] 13 | pub fn new(base: *mut u8) -> Clint { 14 | Clint { 15 | base: base as usize, 16 | } 17 | } 18 | 19 | #[inline] 20 | pub fn get_mtime(&self) -> u64 { 21 | unsafe { 22 | let base = self.base as *mut u8; 23 | core::ptr::read_volatile(base.add(0xbff8) as *mut u64) 24 | } 25 | } 26 | 27 | #[inline] 28 | pub fn set_timer(&self, hart_id: usize, instant: u64) { 29 | unsafe { 30 | let base = self.base as *mut u8; 31 | core::ptr::write_volatile((base.offset(0x4000) as *mut u64).add(hart_id), instant); 32 | } 33 | } 34 | 35 | #[inline] 36 | pub fn send_soft(&self, hart_id: usize) { 37 | unsafe { 38 | let base = self.base as *mut u8; 39 | core::ptr::write_volatile((base as *mut u32).add(hart_id), 1); 40 | } 41 | } 42 | 43 | #[inline] 44 | pub fn clear_soft(&self, hart_id: usize) { 45 | unsafe { 46 | let base = self.base as *mut u8; 47 | core::ptr::write_volatile((base as *mut u32).add(hart_id), 0); 48 | } 49 | } 50 | } 51 | 52 | 53 | /* 54 | impl Ipi for Clint { 55 | #[inline] 56 | fn send_ipi_many(&self, hart_mask: HartMask) -> SbiRet { 57 | // println!("[rustsbi] send ipi many, {:?}", hart_mask); 58 | let num_harts = *crate::count_harts::NUM_HARTS.lock(); 59 | for i in 0..num_harts { 60 | if hart_mask.has_bit(i) { 61 | self.send_soft(i); 62 | } 63 | } 64 | SbiRet::ok(0) 65 | } 66 | } 67 | */ 68 | 69 | /* 70 | 71 | impl Timer for Clint { 72 | #[inline] 73 | fn set_timer(&self, time_value: u64) { 74 | //let this_mhartid = riscv::register::mhartid::read(); 75 | let this_mhartid = riscv::csr::mhartid::read(); 76 | self.set_timer(this_mhartid, time_value); 77 | } 78 | } 79 | 80 | */ -------------------------------------------------------------------------------- /hypervisor/src/count_harts.rs: -------------------------------------------------------------------------------- 1 | use device_tree::{DeviceTree, Node}; 2 | 3 | // Mock device tree from qemu-rustsbi example 4 | // Based on https://github.com/rustsbi/rustsbi-qemu/blob/main/rustsbi-qemu/src/count_harts.rs 5 | 6 | const DEVICE_TREE_MAGIC: u32 = 0xD00DFEED; 7 | 8 | lazy_static::lazy_static! { 9 | // Maximum hardware thread number; only write at startup, read when cross-core softirq occurs 10 | pub static ref NUM_HARTS: spin::Mutex = spin::Mutex::new(8); 11 | } 12 | 13 | pub unsafe fn init_hart_count(dtb_pa: usize) { 14 | *NUM_HARTS.lock() = count_harts(dtb_pa) 15 | } 16 | 17 | #[repr(C)] 18 | struct DtbHeader { 19 | magic: u32, 20 | size: u32, 21 | } 22 | 23 | unsafe fn count_harts(dtb_pa: usize) -> usize { 24 | let header = &*(dtb_pa as *const DtbHeader); 25 | // from_be 是大小端序的转换(from big endian) 26 | let magic = u32::from_be(header.magic); 27 | if magic == DEVICE_TREE_MAGIC { 28 | let size = u32::from_be(header.size); 29 | // 拷贝数据,加载并遍历 30 | let data = core::slice::from_raw_parts(dtb_pa as *const u8, size as usize); 31 | if let Ok(dt) = DeviceTree::load(data) { 32 | if let Some(cpu_map) = dt.find("/cpus/cpu-map") { 33 | return enumerate_cpu_map(cpu_map); 34 | } 35 | } 36 | } 37 | // 如果DTB的结构不对(读不到/cpus/cpu-map),返回默认的8个核 38 | let ans = 8; 39 | println!("[rustsbi-dtb] Could not read '/cpus/cpu-map' from 'dtb_pa' device tree root; assuming {} cores", ans); 40 | ans 41 | } 42 | 43 | // 遍历“cpu_map”结构 44 | // 这个结构的子结构是“处理核簇”(cluster) 45 | // 每个“处理核簇”的子结构分别表示一个处理器核 46 | fn enumerate_cpu_map(cpu_map_node: &Node) -> usize { 47 | let mut tot = 0; 48 | for cluster_node in cpu_map_node.children.iter() { 49 | let name = &cluster_node.name; 50 | let count = cluster_node.children.iter().count(); 51 | // 会输出:Hart count: cluster0 with 2 cores 52 | // 在justfile的“threads := "2"”处更改 53 | println!("[rustsbi-dtb] Hart count: {} with {} cores", name, count); 54 | tot += count; 55 | } 56 | tot 57 | } 58 | -------------------------------------------------------------------------------- /hypervisor/src/debug.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | 3 | #[panic_handler] 4 | fn panic(info: &core::panic::PanicInfo) -> ! { 5 | print!("abort: "); 6 | if let Some(p) = info.location() { 7 | println!( 8 | "line {}, file {}: {}", 9 | p.line(), 10 | p.file(), 11 | info.message().unwrap() 12 | ); 13 | } else { 14 | println!("no information available."); 15 | } 16 | abort(); 17 | } 18 | 19 | #[no_mangle] 20 | extern "C" fn abort() -> ! { 21 | loop { 22 | unsafe { 23 | asm!("wfi", options(nostack)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /hypervisor/src/global_const.rs: -------------------------------------------------------------------------------- 1 | pub const MAX_NUMBER_OF_GUESTS : usize = 4; 2 | pub const M_MODE_TIMER_VALUE : u64 = 10_000_000; 3 | pub const HYPERVISOR_TIMER_TICK : usize = 10_000; -------------------------------------------------------------------------------- /hypervisor/src/guest.rs: -------------------------------------------------------------------------------- 1 | use crate::clint; 2 | use crate::memlayout; 3 | use crate::paging; 4 | use crate::riscv; 5 | use crate::virtio; 6 | //use crate::clint; 7 | use core::fmt::Error; 8 | use core::usize; 9 | use elf_rs::Elf; 10 | 11 | pub struct Guest { 12 | pub name: &'static str, 13 | pub hgatp: riscv::csr::hgatp::Setting, 14 | pub sepc: usize 15 | // TODO: other CSRs & registers 16 | } 17 | 18 | impl Guest { 19 | pub fn new(name: &'static str) -> Guest { 20 | // hgatp 21 | let root_pt = prepare_gpat_pt().unwrap(); 22 | root_pt.print_page_allocations(); 23 | let hgatp = riscv::csr::hgatp::Setting::new( 24 | riscv::csr::hgatp::Mode::Sv39x4, 25 | 0, 26 | root_pt.page.address().to_ppn(), 27 | ); 28 | 29 | Guest { 30 | name: name, 31 | hgatp: hgatp, 32 | sepc: memlayout::GUEST_DRAM_START 33 | } 34 | } 35 | 36 | pub fn load_from_disk(&mut self) { 37 | let load_size = 1024 * 1024 * 16; 38 | let buf_page = paging::alloc_continuous(load_size / memlayout::PAGE_SIZE as usize); 39 | let buf_addr = buf_page.address().to_usize() as *mut u8; 40 | unsafe { 41 | let sector_max: u64 = load_size as u64 / virtio::SECTOR_SIZE as u64; 42 | if let Some(_queue) = virtio::QUEUE { 43 | for sector in 0..sector_max { 44 | (*_queue).read( 45 | sector as u64, 46 | buf_addr.offset(sector as isize * virtio::SECTOR_SIZE as isize) 47 | as *const (), 48 | ); 49 | if sector % 10 == 9 { 50 | log::debug!("progress: {} / {}", sector + 1, sector_max) 51 | } 52 | } 53 | } 54 | log::debug!("an ELF was copied into a buffer") 55 | } 56 | 57 | let gpat_pt = paging::PageTable::from_page(paging::Page::from_address( 58 | paging::PhysicalAddress::new(self.hgatp.ppn << 12), 59 | )); 60 | unsafe { 61 | let buf: &mut [u8] = core::slice::from_raw_parts_mut(buf_addr, load_size as usize); 62 | 63 | // TODO (enhancement): care about page permissions 64 | let elf = Elf::from_bytes(buf); 65 | match elf { 66 | Ok(Elf::Elf64(e)) => { 67 | // change entrypoint 68 | self.sepc = e.header().entry_point() as usize; 69 | log::info!("-> entrypoint: 0x{:016x}", self.sepc); 70 | // copy each section (page to page) 71 | for s in e.section_header_iter() { 72 | if s.sh.addr() > 0 && s.sh.sh_type() == elf_rs::SectionType::SHT_PROGBITS { 73 | log::info!( 74 | "-> section found: name={}, address:0x{:016x}, offset=0x{:016x}", 75 | s.section_name(), 76 | s.sh.addr(), 77 | s.sh.size() 78 | ); 79 | let start_page_head = s.sh.addr() >> 12 << 12; 80 | let end_page_head = (s.sh.addr() + s.sh.size()) >> 12 << 12; 81 | let last_idx = 82 | (end_page_head - start_page_head) / (memlayout::PAGE_SIZE as u64); 83 | let mut seek = (s.sh.addr() & 0xfff) as usize; 84 | 85 | for i in 0..=last_idx { 86 | let dest_base_vaddr = paging::VirtualAddress::new( 87 | (start_page_head + i * (memlayout::PAGE_SIZE as u64)) as usize, 88 | ); 89 | let dest_addr = (gpat_pt.resolve(&dest_base_vaddr).to_usize() 90 | as *mut u8) 91 | .add(seek % (memlayout::PAGE_SIZE) as usize); 92 | let src_addr = buf_addr 93 | .offset(s.sh.offset() as isize) 94 | .add(seek - (s.sh.addr() & 0xfff) as usize); 95 | let copy_size = core::cmp::min( 96 | (i + 1) as usize * memlayout::PAGE_SIZE as usize, 97 | ((s.sh.addr() + s.sh.size()) as usize) 98 | - (start_page_head as usize), 99 | ) as usize 100 | - seek; 101 | seek += copy_size; 102 | core::ptr::copy(src_addr, dest_addr, copy_size); 103 | log::debug!( 104 | "{:016x}: {:0x} {:0x} {:0x} {:0x}", 105 | dest_base_vaddr.to_usize(), 106 | dest_addr.read_volatile(), 107 | dest_addr.add(1).read_volatile(), 108 | dest_addr.add(2).read_volatile(), 109 | dest_addr.add(3).read_volatile() 110 | ); 111 | } 112 | } 113 | } 114 | log::info!("-> the ELF was extracted into the guest memory"); 115 | } 116 | Ok(Elf::Elf32(_)) => { 117 | panic!("-> 32bit ELF is not supported"); 118 | } 119 | Err(e) => { 120 | panic!("-> failed to parse ELF file. {:?}", e); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | // This function return newly allocated page table for Guest Physical Address Translation. 128 | fn prepare_gpat_pt() -> Result { 129 | // NOTE (from the RISC-V specification): 130 | // As explained in Section 5.5.1, for the paged virtual-memory schemes (Sv32x4, Sv39x4, and Sv48x4), 131 | // the root page table is 16 KiB and must be aligned to a 16-KiB boundary. In these modes, the lowest 132 | // two bits of the physical page number (PPN) in hgatp always read as zeros. An implementation 133 | // that supports only the defined paged virtual-memory schemes and/or Bare may hardwire PPN[1:0] 134 | // to zero 135 | 136 | // get a 16KiB-aligned & 16KiB page 137 | // TODO (enhancement): this is a native implementation. we need to implement a more clean allocator in `paging` crate. 138 | let root_page = paging::alloc_16(); 139 | log::info!( 140 | "a page 0x{:016x} was allocated for a guest page address translation page table", 141 | root_page.address().to_usize() 142 | ); 143 | let root_pt = paging::PageTable::from_page(root_page); 144 | 145 | // create an identity map for UART MMIO 146 | let vaddr = memlayout::GUEST_UART_BASE; 147 | let page = paging::Page::from_address(paging::PhysicalAddress::new(vaddr)); 148 | root_pt.map( 149 | paging::VirtualAddress::new(vaddr), 150 | &page, 151 | (paging::PageTableEntryFlag::Read as u16) 152 | | (paging::PageTableEntryFlag::Write as u16) 153 | | (paging::PageTableEntryFlag::Execute as u16) 154 | | (paging::PageTableEntryFlag::User as u16), // required! 155 | ); 156 | 157 | // Mapping VIRTIO memory to virtual machine 158 | for i in 0..8 { 159 | let vaddr = memlayout::VIRTIO0_BASE + (0x1000 * i); 160 | let page = paging::Page::from_address(paging::PhysicalAddress::new(vaddr)); 161 | root_pt.map( 162 | paging::VirtualAddress::new(vaddr), 163 | &page, 164 | (paging::PageTableEntryFlag::Read as u16) 165 | | (paging::PageTableEntryFlag::Write as u16) 166 | | (paging::PageTableEntryFlag::Execute as u16) 167 | | (paging::PageTableEntryFlag::User as u16), // required! 168 | ); 169 | } 170 | 171 | // allocating new pages and map GUEST_DRAM_START ~ GUEST_DRAM_END into those pages for guest kernel 172 | let map_page_num = (memlayout::GUEST_DRAM_END - memlayout::GUEST_DRAM_START) 173 | / (memlayout::PAGE_SIZE as usize) 174 | + 1; 175 | for i in 0..map_page_num { 176 | let vaddr = memlayout::GUEST_DRAM_START + i * (memlayout::PAGE_SIZE as usize); 177 | let page = paging::alloc(); 178 | root_pt.map( 179 | paging::VirtualAddress::new(vaddr), 180 | &page, 181 | (paging::PageTableEntryFlag::Read as u16) 182 | | (paging::PageTableEntryFlag::Write as u16) 183 | | (paging::PageTableEntryFlag::Execute as u16) 184 | | (paging::PageTableEntryFlag::User as u16), // required! 185 | ) 186 | } 187 | 188 | let map_page_num = (memlayout::GUEST_TEST_AREA_END - memlayout::GUEST_TEST_AREA_START) 189 | / (memlayout::PAGE_SIZE as usize) 190 | + 1; 191 | for i in 0..map_page_num { 192 | let vaddr = memlayout::GUEST_TEST_AREA_START + i * (memlayout::PAGE_SIZE as usize); 193 | let page = paging::alloc(); 194 | root_pt.map( 195 | paging::VirtualAddress::new(vaddr), 196 | &page, 197 | (paging::PageTableEntryFlag::Read as u16) 198 | | (paging::PageTableEntryFlag::Write as u16) 199 | | (paging::PageTableEntryFlag::Execute as u16) 200 | | (paging::PageTableEntryFlag::User as u16), // required! 201 | ) 202 | } 203 | 204 | Ok(root_pt) 205 | } 206 | -------------------------------------------------------------------------------- /hypervisor/src/hypervisor.S: -------------------------------------------------------------------------------- 1 | .option norvc 2 | .altmacro 3 | 4 | .section .text.hypervisor 5 | .global hypervisor_entrypoint 6 | 7 | .macro load_gp i, base 8 | ld x\i, ((\i)*8)(\base) 9 | .endm 10 | 11 | .macro save_gp i, base 12 | sd x\i, ((\i)*8)(\base) 13 | .endm 14 | 15 | .align 4 16 | hypervisor_entrypoint: 17 | # TODO: copy registers 18 | 19 | # jump to a handler written in Rust 20 | tail rust_hypervisor_entrypoint 21 | 22 | .align 4 23 | trap_to_hypervisor: 24 | csrrw t6, sscratch, t6 25 | 26 | # save GPRs 27 | .set i, 1 28 | .rept 30 29 | save_gp %i, t6 30 | .set i, i+1 31 | .endr 32 | 33 | mv t5, t6 34 | csrr t6, sscratch 35 | save_gp 31, t5 36 | 37 | # # save FPRs 38 | # .set i, 0 39 | # .rept 32 40 | # save_fp %i, t5 41 | # .set i, i+1 42 | # .endr 43 | 44 | # save sscratch 45 | csrw sscratch, t5 46 | 47 | csrr a0, sepc 48 | csrr a1, stval 49 | csrr a2, scause 50 | csrr a3, sstatus 51 | csrr a4, sscratch 52 | la sp, _intr_stack_end 53 | 54 | # ------- 55 | 56 | # jump to a handler written in Rust 57 | call rust_strap_handler 58 | 59 | # ------- 60 | 61 | # after getting back from rust_strap_handler ... 62 | csrw sepc, a0 63 | csrr t6, sscratch 64 | 65 | # # restore FPRs 66 | # .set i, 0 67 | # .rept 32 68 | # load_fp %i, t6 69 | # .set i, i+1 70 | # .endr 71 | 72 | # restore GPRs 73 | .set i, 1 74 | .rept 31 75 | load_gp %i, t6 76 | .set i, i+1 77 | .endr 78 | 79 | sret 80 | 81 | 82 | .bss 83 | .global _hs_stack_start, _hs_stack_end 84 | _hs_stack_start: 85 | .skip 1024 * 1024 * 16 # 16 MB 86 | _hs_stack_end: 87 | .skip 1024 88 | 89 | .global _intr_stack_start, _intr_stack_end 90 | _intr_stack_start: 91 | .skip 1024 * 1024 # 1 MB 92 | _intr_stack_end: 93 | .skip 1024 -------------------------------------------------------------------------------- /hypervisor/src/hypervisor.rs: -------------------------------------------------------------------------------- 1 | global_asm!(include_str!("hypervisor.S")); 2 | 3 | use crate::guest::Guest; 4 | use crate::memlayout; 5 | use crate::paging; 6 | use crate::plic; 7 | use crate::clint; 8 | use crate::riscv; 9 | use crate::riscv::gpr::Register; 10 | use crate::sbi::VmSBI; 11 | use crate::timer::VmTimers; 12 | use crate::uart; 13 | use crate::virtio; 14 | use crate::sbi; 15 | use crate::global_const::{HYPERVISOR_TIMER_TICK, MAX_NUMBER_OF_GUESTS}; 16 | use core::arch::asm; 17 | use core::arch::global_asm; 18 | use core::convert::TryFrom; 19 | use core::fmt::Error; 20 | 21 | use rustsbi::{RustSBI, spec::binary::Error as SbiError}; 22 | 23 | use alloc::boxed::Box; 24 | use alloc::string::String; 25 | use alloc::vec; 26 | 27 | extern "C" { 28 | #[link_name = "hypervisor_entrypoint"] 29 | pub fn entrypoint(); 30 | 31 | #[link_name = "trap_to_hypervisor"] 32 | pub fn trap(); 33 | } 34 | 35 | struct hypervisor { 36 | hypervisor_guests : [Guest ; MAX_NUMBER_OF_GUESTS] 37 | } 38 | 39 | #[no_mangle] 40 | pub fn rust_hypervisor_entrypoint() -> ! { 41 | log::info!("hypervisor started"); 42 | 43 | if let Err(e) = riscv::interrupt::free(|_| init()) { 44 | panic!("Failed to init hypervisor. {:?}", e) 45 | } 46 | log::info!("succeeded in initializing hypervisor"); 47 | 48 | // TODO (enhnancement): multiplex here 49 | let guest_name = "guest01"; 50 | log::info!("a new guest instance: {}", guest_name); 51 | log::info!("-> create metadata set"); 52 | let mut guest = riscv::interrupt::free(|_| Guest::new(guest_name)); 53 | log::info!("-> load a tiny kernel image"); 54 | riscv::interrupt::free(|_| guest.load_from_disk()); 55 | 56 | log::info!("switch to guest"); 57 | switch_to_guest(&guest); 58 | } 59 | 60 | pub fn init() -> Result<(), Error> { 61 | // inti memory allocator 62 | paging::init(); 63 | 64 | // init virtio 65 | virtio::init(); 66 | 67 | // hedeleg: delegate some synchoronous exceptions 68 | riscv::csr::hedeleg::write(riscv::csr::hedeleg::INST_ADDR_MISALIGN 69 | | riscv::csr::hedeleg::BREAKPOINT 70 | | riscv::csr::hedeleg::ENV_CALL_FROM_U_MODE_OR_VU_MODE 71 | | riscv::csr::hedeleg::INST_PAGE_FAULT 72 | | riscv::csr::hedeleg::LOAD_PAGE_FAULT 73 | | riscv::csr::hedeleg::STORE_AMO_PAGE_FAULT); 74 | 75 | // hideleg: delegate all interrupts 76 | riscv::csr::hideleg::write( 77 | riscv::csr::hideleg::VSEIP | riscv::csr::hideleg::VSTIP | riscv::csr::hideleg::VSSIP, 78 | ); 79 | 80 | // hvip: clear all interrupts first 81 | riscv::csr::hvip::write(0); 82 | 83 | // stvec: set handler 84 | riscv::csr::stvec::set(&(trap as unsafe extern "C" fn())); 85 | assert_eq!( 86 | riscv::csr::stvec::read(), 87 | (trap as unsafe extern "C" fn()) as usize 88 | ); 89 | 90 | // allocate memory region for TrapFrame and set it sscratch 91 | let trap_frame = paging::alloc(); 92 | riscv::csr::sscratch::write(trap_frame.address().to_usize()); 93 | log::info!("sscratch: {:016x}", riscv::csr::sscratch::read()); 94 | 95 | // enable interupts 96 | enable_interrupt(); 97 | 98 | // TODO: hip and sip 99 | // TODO: hie and sie 100 | 101 | // leave 102 | Ok(()) 103 | } 104 | 105 | fn enable_interrupt() { 106 | // TODO (enhancement): UART0 107 | 108 | // configure PLIC 109 | plic::enable_interrupt(); 110 | 111 | // sie; enable external interrupt 112 | // TODO (enhancement): timer interrupt 113 | riscv::csr::sie::enable_hardware_timer(); 114 | 115 | // TODO (enhancement): software interrupt 116 | let current_sie = riscv::csr::sie::read(); 117 | riscv::csr::sie::write(current_sie | (riscv::csr::sie::SEIE as usize)); 118 | 119 | // sstatus: enable global interrupt 120 | riscv::csr::sstatus::set_sie(true); 121 | } 122 | 123 | pub fn switch_to_guest(target: &Guest) -> ! { 124 | // hgatp: set page table for guest physical address translation 125 | riscv::csr::hgatp::set(&target.hgatp); 126 | riscv::instruction::hfence_gvma(); 127 | assert_eq!(target.hgatp.to_usize(), riscv::csr::hgatp::read()); 128 | 129 | // hstatus: handle SPV change the virtualization mode to 0 after sret 130 | riscv::csr::hstatus::set_spv(riscv::csr::VirtualzationMode::Guest); 131 | 132 | // sstatus: handle SPP to 1 to change the privilege level to S-Mode after sret 133 | riscv::csr::sstatus::set_spp(riscv::csr::CpuMode::S); 134 | 135 | // sepc: set the addr to jump 136 | riscv::csr::sepc::set(&target.sepc); 137 | 138 | // jump! 139 | riscv::instruction::sret(); 140 | } 141 | 142 | #[repr(C)] 143 | #[derive(Clone, Copy, Debug)] 144 | pub struct TrapFrame { 145 | pub regs: [usize; 32], // 0 - 255 146 | pub fregs: [usize; 32], // 256 - 511 147 | pub pc: usize, // 512 148 | } 149 | 150 | fn show_trapinfo( 151 | sepc: usize, // a0 152 | stval: usize, // a1 153 | scause: usize, // a2 154 | sstatus: usize, // a3 155 | frame: *mut TrapFrame, // a4 156 | ){ 157 | log::info!("<--------- trap --------->"); 158 | log::info!("sepc: 0x{:016x}", sepc,); 159 | log::info!("stval: 0x{:016x}", stval,); 160 | log::info!("scause: 0x{:016x}", scause,); 161 | log::info!("sstatus: 0x{:016x}", sstatus,); 162 | 163 | log::info!("------- trapframe --------"); 164 | let user_frame = unsafe{*frame.clone()}; 165 | let mut i = 0; 166 | for reg in user_frame.regs { 167 | let reg_name = Register::try_from(i).unwrap(); 168 | print!("{:<3} = 0x{:016x} ", reg_name, reg); 169 | if i % 4 == 3 { 170 | println!(); 171 | } else { 172 | print!("| ") 173 | } 174 | i += 1; 175 | } 176 | log::info!("------- registers --------"); 177 | riscv::gpr::dump(); 178 | log::info!("--------- S csr ---------"); 179 | riscv::csr::dump_s_csr(); 180 | log::info!("--------- H csr ---------"); 181 | riscv::csr::dump_h_csr(); 182 | log::info!("--------- VS csr ---------"); 183 | riscv::csr::dump_vs_csr(); 184 | log::info!("-------- Prev Mode -------"); 185 | let prev = riscv::csr::hstatus::previous_mode().unwrap(); 186 | let mode_str = match prev { 187 | riscv::csr::PreviousMode::U_mode => "User mode (U)", 188 | riscv::csr::PreviousMode::HS_mode => "Hypervisor mode (HS)", 189 | riscv::csr::PreviousMode::M_mode => "Machine Mode (M)", 190 | riscv::csr::PreviousMode::VU_mode => "Virtual User Mode (VU)", 191 | riscv::csr::PreviousMode::VS_mode => "Virtual Supervisor Mode (VS)", 192 | }; 193 | log::info!("Previous Mode before trap: {}", mode_str); 194 | } 195 | 196 | #[no_mangle] 197 | pub extern "C" fn rust_strap_handler( 198 | sepc: usize, // a0 199 | stval: usize, // a1 200 | scause: usize, // a2 201 | sstatus: usize, // a3 202 | frame: *mut TrapFrame, // a4 203 | ) -> usize { 204 | log::debug!("<--------- trap --------->"); 205 | log::debug!("sepc: 0x{:016x}", sepc,); 206 | log::debug!("stval: 0x{:016x}", stval,); 207 | log::debug!("scause: 0x{:016x}", scause,); 208 | log::debug!("sstatus: 0x{:016x}", sstatus,); 209 | 210 | let is_async = scause >> 63 & 1 == 1; 211 | let cause_code = scause & 0xfff; 212 | if is_async { 213 | match cause_code { 214 | // external interrupt 215 | 9 => { 216 | if let Some(interrupt) = plic::get_claim() { 217 | log::debug!("interrupt id: {}", interrupt); 218 | match interrupt { 219 | 1..=8 => { 220 | virtio::handle_interrupt(interrupt); 221 | } 222 | 10 => { 223 | uart::handle_interrupt(); 224 | } 225 | _ => { 226 | unimplemented!() 227 | } 228 | } 229 | plic::complete(interrupt); 230 | } else { 231 | panic!("invalid state") 232 | } 233 | } 234 | 5 => { 235 | //timer interrupt 236 | //show_trapinfo(sepc,stval,scause,sstatus,frame); 237 | log::debug!("Hypervisor timer interrupt fired"); 238 | riscv::csr::sip::clear_stimer(); 239 | riscv::csr::sie::clear_hardware_timer(); 240 | assert_eq!( 241 | riscv::csr::sie::read() >> 5 & 0b1, 242 | 0 243 | ); 244 | 245 | if let Some(mut timer) = crate::timer::TIMERS.try_lock() { 246 | //let mut timer = timer::TIMER.lock(); 247 | timer.tick_vm_timers(HYPERVISOR_TIMER_TICK); 248 | let timer_trigger_list = timer.check_timers(); 249 | //println!("{:?}", timer_trigger_list); 250 | //timer.debug_print(); 251 | 252 | // TODO: loop through all avalible guests 253 | // assuming 0 now since we have hardcoded one vm 254 | let guest0_timer_intr_trigger = timer_trigger_list [0]; 255 | if guest0_timer_intr_trigger { 256 | log::info!("triggering timer interrupt on guest0"); 257 | riscv::csr::hvip::trigger_timing_interrupt(); 258 | } 259 | 260 | } 261 | } 262 | // timer interrupt & software interrrupt 263 | _ => { 264 | unimplemented!("Unknown interrupt id: {}", cause_code); 265 | } 266 | } 267 | } else { 268 | match cause_code { 269 | 8 => { 270 | log::info!("environment call from U-mode / VU-mode at 0x{:016x}", sepc); 271 | // TODO: better handling 272 | loop {} 273 | } 274 | 10 => { 275 | log::info!("environment call from VS-mode at 0x{:016x}", sepc); 276 | let user_frame = unsafe{*frame.clone()}; 277 | //println!("{:?}", user_frame); 278 | 279 | // Hardcoded for now 280 | let guest_number = 0; 281 | 282 | let a7 = user_frame.regs[17]; 283 | let a6 = user_frame.regs[16]; 284 | let a1 = user_frame.regs[11]; 285 | let a0 = user_frame.regs[10]; 286 | let params = [user_frame.regs[10], user_frame.regs[11], user_frame.regs[12], user_frame.regs[13], user_frame.regs[14], user_frame.regs[15]]; 287 | log::info!("a0: 0x{:x}, a1: 0x{:x}, a6: 0x{:x}, a7: 0x{:x}", a0, a1, a6, a7); 288 | let sbi = VmSBI::with_guest_number(guest_number); 289 | let sbi_result = sbi.handle_ecall(a7, a6, params); 290 | match sbi_result.into_result() { 291 | Ok(_) => log::info!("SBI result SBI_SUCCESS "), 292 | Err(SbiError::NotSupported) => log::info!("SBI result SBI_ERR_NOT_SUPPORTED "), 293 | Err(SbiError::InvalidParam) => log::info!("SBI result SBI_ERR_INVALID_PARAM "), 294 | Err(SbiError::InvalidAddress) => log::info!("SBI result SBI_ERR_INVALID_ADDRESS"), 295 | Err(SbiError::Failed) => log::info!("SBI result SBI_ERR_FAILED "), 296 | _ => log::info!("SBI result Error {:?}", sbi_result) 297 | } 298 | log::info!("SBI result {:?}", sbi_result); 299 | // Maybe there is a better way todo this 300 | unsafe { 301 | (*frame).regs[10] = sbi_result.error; 302 | (*frame).regs[11] = sbi_result.value; 303 | } 304 | return sepc + 0x4; // Skips to the next instruction in guest 305 | //loop {} 306 | } 307 | 21 => { 308 | show_trapinfo(sepc,stval,scause,sstatus,frame); 309 | log::info!("exception: load guest page fault at 0x{:016x}", sepc); 310 | // TODO (enhancement): demand paging 311 | loop {} 312 | } 313 | 23 => { 314 | show_trapinfo(sepc,stval,scause,sstatus,frame); 315 | log::info!("exception: store/amo guest-page fault at 0x{:016x}", sepc); 316 | // TODO: better handling 317 | loop {} 318 | } 319 | _ => { 320 | show_trapinfo(sepc,stval,scause,sstatus,frame); 321 | unimplemented!("Unknown Exception id: {}", cause_code); 322 | } 323 | } 324 | } 325 | sepc 326 | } 327 | -------------------------------------------------------------------------------- /hypervisor/src/m_mode_calls.rs: -------------------------------------------------------------------------------- 1 | /// M mode calls is syscalls to the m mode layer todo tasks that require M mode priviliges 2 | 3 | use crate::riscv; 4 | 5 | pub const DISABLE_ALL_INTERRUPTS: usize = 0x01; 6 | pub const ENABLE_ALL_INTERRUPTS: usize = 0x02; 7 | pub const DISABLE_ALL_TIMERS: usize = 0x03; 8 | pub const ENABLE_ALL_TIMERS: usize = 0x04; 9 | 10 | pub fn disable_interrupts() { 11 | let _ = riscv::instruction::ecall_with_args(DISABLE_ALL_INTERRUPTS, 0x0, 0x0, 0x0); 12 | } 13 | 14 | pub fn enable_interrupts() { 15 | let _ = riscv::instruction::ecall_with_args(ENABLE_ALL_INTERRUPTS, 0x0, 0x0, 0x0); 16 | } 17 | 18 | pub fn disable_timers() { 19 | let _ = riscv::instruction::ecall_with_args(DISABLE_ALL_TIMERS, 0x0, 0x0, 0x0); 20 | } 21 | 22 | pub fn enable_timers() { 23 | let _ = riscv::instruction::ecall_with_args(ENABLE_ALL_TIMERS, 0x0, 0x0, 0x0); 24 | } -------------------------------------------------------------------------------- /hypervisor/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #![feature(panic_info_message, type_ascription, asm_const)] 5 | 6 | // Heap implementation 7 | use buddy_system_allocator::LockedHeap; 8 | 9 | #[global_allocator] 10 | static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::empty(); 11 | 12 | // extenal crates 13 | extern crate elf_rs; 14 | extern crate log; 15 | extern crate alloc; 16 | 17 | // modules 18 | #[macro_use] 19 | pub mod uart; 20 | #[macro_use] 21 | pub mod riscv; 22 | pub mod boot; 23 | pub mod memlayout; 24 | pub mod paging; 25 | pub mod plic; 26 | pub mod clint; 27 | pub mod timer; 28 | pub mod count_harts; 29 | 30 | pub mod mkernel; 31 | 32 | pub mod guest; 33 | pub mod hypervisor; 34 | 35 | pub mod debug; 36 | pub mod util; 37 | pub mod global_const; 38 | pub mod sbi; 39 | pub mod m_mode_calls; 40 | 41 | pub mod virtio; 42 | -------------------------------------------------------------------------------- /hypervisor/src/memlayout.rs: -------------------------------------------------------------------------------- 1 | // generic constants 2 | ///// 3 | 4 | pub const PAGE_SIZE: u16 = 4096; 5 | pub const VIRTIO0_IRQ: u16 = 1; 6 | pub const UART0_IRQ: u16 = 10; 7 | 8 | pub const HEAP_SIZE: usize = 64 * 1024; // 64KiB 9 | 10 | // information on hypervisor binary 11 | ///// 12 | extern "C" { 13 | static _elf_start: usize; 14 | static _elf_end: usize; 15 | } 16 | 17 | pub unsafe fn elf_start() -> usize { 18 | unsafe { &_elf_start as *const usize as usize } 19 | } 20 | 21 | pub unsafe fn elf_end() -> usize { 22 | unsafe { &_elf_end as *const usize as usize } 23 | } 24 | 25 | pub unsafe fn heap_start() -> usize { 26 | (elf_end() & !(0xfff as usize)) + 4096 27 | } 28 | 29 | pub unsafe fn heap_end() -> usize { 30 | heap_start() + HEAP_SIZE 31 | } 32 | 33 | // information on hardware for hypervisor 34 | ///// 35 | 36 | pub static UART_BASE: usize = 0x1000_0000; 37 | pub static VIRTIO0_BASE: usize = 0x1000_1000; 38 | 39 | pub static PLIC_BASE: usize = 0x0c00_0000; 40 | pub static PLIC_HART0_M_INT_EN: usize = 0x2000; 41 | pub static PLIC_HART0_M_PRIORITY: usize = 0x20_0000; 42 | pub static PLIC_HART0_M_CLAIM_COMPLETE: usize = 0x20_0004; 43 | pub static PLIC_HART1_M_INT_EN: usize = 0x2080; 44 | pub static PLIC_HART1_M_PRIORITY: usize = 0x20_1000; 45 | pub static PLIC_HART1_M_CLAIM_COMPLETE: usize = 0x20_1004; 46 | 47 | pub static CLINT_HART0_MSIP: usize = 0x200_0000; 48 | pub static CLINT_HART1_MSIP: usize = 0x200_0004; 49 | pub static CLINT_HART2_MSIP: usize = 0x200_0008; 50 | pub static CLINT_HART3_MSIP: usize = 0x200_000c; 51 | pub static CLINT_HART4_MSIP: usize = 0x200_0010; 52 | 53 | pub static CLINT_HART0_MTIMECMP: usize = 0x200_4000; 54 | pub static CLINT_HART1_MTIMECMP: usize = 0x200_4008; 55 | pub static CLINT_HART2_MTIMECMP: usize = 0x200_4010; 56 | pub static CLINT_HART3_MTIMECMP: usize = 0x200_4018; 57 | pub static CLINT_HART4_MTIMECMP: usize = 0x200_4020; 58 | 59 | pub static CLINT_MTIME: usize = 0x200_bff8; 60 | 61 | 62 | // TODO: make this more flexible 63 | // This value should be page-aligned. 64 | pub static DRAM_START: usize = 0x8000_0000; 65 | pub static DRAM_END: usize = 0x9000_0000; 66 | 67 | // information on hardware for guest 68 | ///// 69 | 70 | pub static GUEST_UART_BASE: usize = 0x1000_0000; 71 | 72 | // TODO: make this more flexible 73 | // This value should be page-aligned. 74 | pub static GUEST_DRAM_START: usize = 0x8000_0000; 75 | pub static GUEST_DRAM_END: usize = 0x8200_0000; 76 | 77 | pub static GUEST_TEST_AREA_START: usize = 0x8201_0000; 78 | pub static GUEST_TEST_AREA_END: usize = 0x8202_0000; -------------------------------------------------------------------------------- /hypervisor/src/mkernel.S: -------------------------------------------------------------------------------- 1 | .option norvc 2 | .altmacro 3 | 4 | .section .text.mkernel 5 | .global trap_to_m 6 | 7 | .macro load_gp i, base 8 | ld x\i, ((\i)*8)(\base) 9 | .endm 10 | 11 | .macro save_gp i, base 12 | sd x\i, ((\i)*8)(\base) 13 | .endm 14 | 15 | .align 4 16 | trap_to_mkernel: 17 | csrrw t6, mscratch, t6 18 | 19 | # save GPRs 20 | .set i, 0 21 | .rept 31 22 | save_gp %i, t6 23 | .set i, i+1 24 | .endr 25 | 26 | mv t5, t6 27 | csrr t6, mscratch 28 | save_gp 31, t5 29 | 30 | # save sscratch 31 | csrw mscratch, t5 32 | 33 | csrr a0, mepc 34 | csrr a1, mtval 35 | csrr a2, mcause 36 | csrr a3, mstatus 37 | csrr a4, mscratch 38 | la sp, _mintr_stack_end 39 | 40 | # ------- 41 | 42 | call rust_mtrap_handler 43 | 44 | # ------- 45 | 46 | # after getting back from rust_mtrap_handler ... 47 | csrw mepc, a0 48 | csrr t6, mscratch 49 | 50 | # restore GPRs 51 | .set i, 1 52 | .rept 31 53 | load_gp %i, t6 54 | .set i, i+1 55 | .endr 56 | 57 | mret 58 | 59 | .bss 60 | .global _m_stack_start, _m_stack_end 61 | _m_stack_start: 62 | # allocate 16 MB 63 | .skip 1024 * 1024 * 16 64 | _m_stack_end: 65 | .skip 1024 66 | 67 | .global _mintr_stack_start, _mintr_stack_end 68 | _mintr_stack_start: 69 | .skip 1024 * 1024 70 | _mintr_stack_end: 71 | .skip 1024 -------------------------------------------------------------------------------- /hypervisor/src/mkernel.rs: -------------------------------------------------------------------------------- 1 | global_asm!(include_str!("mkernel.S")); 2 | 3 | use crate::HEAP_ALLOCATOR; 4 | use crate::clint; 5 | use crate::count_harts; 6 | use crate::hypervisor; 7 | use crate::memlayout; 8 | use crate::memlayout::{heap_start, heap_end, HEAP_SIZE}; 9 | use crate::riscv; 10 | use crate::uart; 11 | use crate::util; 12 | use crate::m_mode_calls; 13 | use crate::global_const::M_MODE_TIMER_VALUE; 14 | use core::arch::asm; 15 | use core::arch::global_asm; 16 | use core::fmt::Error; 17 | extern "C" { 18 | #[link_name = "trap_to_mkernel"] 19 | pub fn trap(); 20 | } 21 | 22 | #[no_mangle] 23 | pub extern "C" fn rust_m_entrypoint(hartid: usize, opqaue: usize) -> ! { 24 | // init hardware and M-mode registers. 25 | if let Err(e) = init() { 26 | panic!("Failed to initialize. {:?}", e); 27 | }; 28 | 29 | println!("-----------------------"); 30 | println!(" rustyvisor"); 31 | println!("-----------------------"); 32 | 33 | // init logger. 34 | if let Err(e) = util::logger::init() { 35 | panic!("Failed to init logger. {:?}", e); 36 | } 37 | log::info!("logger was initialized"); 38 | log::info!("processor is in m-mode running with hartid: {}", hartid); 39 | unsafe { 40 | log::info!("Initing heap implementation: 0x{:016x} -> 0x{:016x} size: 0x{:016x}", heap_start(), heap_end(), HEAP_SIZE); 41 | HEAP_ALLOCATOR.lock().add_to_heap(heap_start(), heap_end()); 42 | } 43 | 44 | 45 | unsafe { count_harts::init_hart_count(opqaue) }; 46 | 47 | //init clint timer 48 | if let Err(e) = setup_timer() { 49 | panic!("Failed to initialize timer. {:?}", e); 50 | }; 51 | 52 | // jump to a next handler while changing CPU mode to HS 53 | log::info!("jump to hypervisor while chainging CPU mode from M to HS"); 54 | switch_to_hypervisor(hypervisor::entrypoint as unsafe extern "C" fn()); 55 | } 56 | 57 | pub fn setup_timer() -> Result<(), Error> { 58 | 59 | // clint: enable timer interrupts 60 | let timer = clint::Clint::new(0x200_0000 as *mut u8); 61 | timer.set_timer(0, timer.get_mtime() + 10_000_000); 62 | // enable timer interrupts 63 | riscv::csr::mie::enable_m_mode_hardware_timer(); 64 | 65 | Ok(()) 66 | } 67 | 68 | pub fn init() -> Result<(), Error> { 69 | // init UART 70 | uart::Uart::new(memlayout::UART_BASE).init(); 71 | 72 | // medeleg: delegate synchoronous exceptions except for ecall from HS-mode (bit 9) 73 | riscv::csr::medeleg::write(0xffffff ^ riscv::csr::medeleg::HYPERVISOR_ECALL ); 74 | 75 | // mideleg: delegate all interruptions 76 | riscv::csr::mideleg::write( 77 | riscv::csr::mideleg::SEIP | riscv::csr::mideleg::STIP | riscv::csr::mideleg::SSIP, 78 | ); 79 | 80 | // enable hypervisor extension 81 | let misa_state = riscv::csr::misa::read(); 82 | riscv::csr::misa::write(misa_state | riscv::csr::misa::HV); 83 | //assert_eq!( 84 | // (riscv::csr::misa::read()) & riscv::csr::misa::HV, 85 | // riscv::csr::misa::HV 86 | //); 87 | 88 | // mtvec: set M-mode trap handler 89 | riscv::csr::mtvec::set(&(trap as unsafe extern "C" fn())); 90 | assert_eq!( 91 | riscv::csr::mtvec::read(), 92 | (trap as unsafe extern "C" fn()) as usize 93 | ); 94 | 95 | // satp: disable paging 96 | riscv::csr::satp::write(0x0); 97 | 98 | // leave 99 | Ok(()) 100 | } 101 | 102 | pub fn switch_to_hypervisor(target: T) -> ! { 103 | riscv::csr::mstatus::set_mpp(riscv::csr::CpuMode::S); 104 | riscv::csr::mstatus::set_mpv(riscv::csr::VirtualzationMode::Host); 105 | riscv::csr::mepc::set(target); 106 | assert_eq!( 107 | riscv::csr::mepc::read(), 108 | target.convert_to_fn_address() 109 | ); 110 | 111 | unsafe{ 112 | asm!(" 113 | # Set up the PMP registers correctly 114 | li t4, 31 115 | csrw pmpcfg0, t4 116 | li t5, (1 << 55) - 1 117 | csrw pmpaddr0, t5 118 | "); 119 | } 120 | 121 | log::info!("Current mepc addr {:#x}", riscv::csr::mepc::read()); 122 | riscv::instruction::mret(); 123 | } 124 | 125 | #[repr(C)] 126 | #[derive(Clone, Copy, Debug)] 127 | pub struct TrapFrame { 128 | pub regs: [usize; 32], // 0 - 255 129 | pub fregs: [usize; 32], // 256 - 511 130 | pub pc: usize, // 512 131 | } 132 | 133 | #[no_mangle] 134 | pub extern "C" fn rust_mtrap_handler( 135 | mepc: usize, // a0 136 | mtval: usize, // a1 137 | mcause: usize, // a2 138 | mstatus: usize, // a3 139 | frame: *mut TrapFrame, // a4 140 | ) -> usize { 141 | log::debug!("trapped to M-mode!"); 142 | log::debug!("Machine trap cause {:#x}", mcause); 143 | log::debug!("Machine trap mepc {:#x}", mepc); 144 | log::debug!("Machine trap mstatus {:#x}", mstatus); 145 | let prev = riscv::csr::mstatus::previous_mode().unwrap(); 146 | let mode_str = match prev { 147 | riscv::csr::PreviousMode::U_mode => "User mode (U)", 148 | riscv::csr::PreviousMode::HS_mode => "Hypervisor mode (HS)", 149 | riscv::csr::PreviousMode::M_mode => "Machine Mode (M)", 150 | riscv::csr::PreviousMode::VU_mode => "Virtual User Mode (VU)", 151 | riscv::csr::PreviousMode::VS_mode => "Virtual Supervisor Mode (VS)", 152 | }; 153 | log::debug!("Previous Mode before trap: {}", mode_str); 154 | let is_async = mcause >> 63 & 1 == 1; 155 | let cause_code = mcause & 0xfff; 156 | if is_async { 157 | match cause_code { 158 | 7 => { 159 | //log::info!("M mode timer triggered"); 160 | riscv::csr::mip::set_stimer(); 161 | riscv::csr::mie::enable_s_mode_hardware_timer(); 162 | let timer = clint::Clint::new(0x200_0000 as *mut u8); 163 | timer.set_timer(0, timer.get_mtime() + M_MODE_TIMER_VALUE); 164 | //riscv::csr::mie::clear_m_mode_hardware_timer(); 165 | } 166 | _ => { 167 | unimplemented!("Unknown M-mode interrupt id: {}", cause_code); 168 | } 169 | } 170 | } else { 171 | match cause_code { 172 | 9 => { 173 | log::info!("environment call from HS-mode at 0x{:016x}", mepc); 174 | let hypervisor_frame = unsafe{*frame.clone()}; 175 | let a1 = hypervisor_frame.regs[11]; 176 | let a0 = hypervisor_frame.regs[10]; 177 | let mut result = 0; 178 | 179 | match a0 { 180 | m_mode_calls::ENABLE_ALL_INTERRUPTS => { 181 | unsafe{ 182 | riscv::interrupt::enable(); 183 | } 184 | } 185 | m_mode_calls::DISABLE_ALL_INTERRUPTS => { 186 | unsafe{ 187 | riscv::interrupt::disable(); 188 | } 189 | } 190 | m_mode_calls::ENABLE_ALL_TIMERS => { 191 | riscv::csr::mie::enable_m_mode_hardware_timer(); 192 | } 193 | m_mode_calls::DISABLE_ALL_TIMERS => { 194 | riscv::csr::mie::clear_m_mode_hardware_timer(); 195 | } 196 | _ => { 197 | result = 1; 198 | log::info!("Unimplemented ecall from hypervisor: {}", a0); 199 | } 200 | } 201 | 202 | unsafe { 203 | (*frame).regs[10] = result; 204 | } 205 | 206 | return mepc + 0x4; 207 | } 208 | _ => { 209 | unimplemented!("Unknown M-mode Exception id: {}", cause_code); 210 | } 211 | } 212 | } 213 | mepc 214 | } 215 | -------------------------------------------------------------------------------- /hypervisor/src/paging.rs: -------------------------------------------------------------------------------- 1 | // TODO (enhancement): 2 | // if we run more rich guest OS or add more rich features to hypervisor, 3 | // we need to refine this implmentation :-D 4 | 5 | use crate::memlayout::{DRAM_END, PAGE_SIZE, heap_end}; 6 | 7 | // VirtualAddress 8 | ///// 9 | 10 | #[derive(Debug)] 11 | pub struct VirtualAddress { 12 | addr: usize, 13 | } 14 | 15 | impl VirtualAddress { 16 | pub fn new(addr: usize) -> VirtualAddress { 17 | VirtualAddress { addr: addr } 18 | } 19 | 20 | pub fn new_from_vpn(vpn : [usize; 3]) -> VirtualAddress { 21 | let addr = 22 | (vpn[2]) << 30 | 23 | (vpn[1]) << 21 | 24 | (vpn[0]) << 12 25 | ; 26 | VirtualAddress { addr: addr } 27 | } 28 | 29 | pub fn to_vpn(&self) -> [usize; 3] { 30 | [ 31 | (self.addr >> 12) & 0x1ff, //L0 9bit 32 | (self.addr >> 21) & 0x1ff, //L1 9bit 33 | (self.addr >> 30) & 0x3ff, //L2 11bit 34 | ] 35 | } 36 | 37 | pub fn to_offset(&self) -> usize { 38 | self.addr & 0x3ff //Offsett 12bit 39 | } 40 | 41 | pub fn to_usize(&self) -> usize { 42 | self.addr 43 | } 44 | 45 | pub fn as_pointer(&self) -> *mut usize { 46 | self.addr as *mut usize 47 | } 48 | } 49 | 50 | // PhysicalAddress 51 | ///// 52 | 53 | #[derive(Copy, Clone, Debug)] 54 | pub struct PhysicalAddress { 55 | addr: usize, 56 | } 57 | 58 | impl PhysicalAddress { 59 | pub fn new(addr: usize) -> PhysicalAddress { 60 | PhysicalAddress { addr: addr } 61 | } 62 | 63 | pub fn to_ppn(&self) -> usize { 64 | self.addr >> 12 //ppn 44bit 65 | } 66 | 67 | pub fn to_ppn_array(&self) -> [usize; 3] { 68 | [ 69 | (self.addr >> 12) & 0x1ff, //L0 9bit 70 | (self.addr >> 21) & 0x1ff, //L1 9bit 71 | (self.addr >> 30) & 0x3ff_ffff, //L2 26bit 72 | ] 73 | } 74 | 75 | pub fn to_usize(&self) -> usize { 76 | self.addr 77 | } 78 | 79 | pub fn as_pointer(&self) -> *mut usize { 80 | self.addr as *mut usize 81 | } 82 | } 83 | 84 | // Page 85 | ///// 86 | 87 | #[derive(Copy, Clone, Debug)] 88 | pub struct Page { 89 | addr: PhysicalAddress, 90 | } 91 | 92 | impl Page { 93 | pub fn from_address(addr: PhysicalAddress) -> Page { 94 | Page { addr: addr } 95 | } 96 | 97 | pub fn address(&self) -> PhysicalAddress { 98 | self.addr 99 | } 100 | /// Clears allocated memory for page 101 | pub fn clear(&self) { 102 | unsafe { 103 | let ptr = self.addr.as_pointer(); 104 | for i in 0..512 { 105 | ptr.add(i).write(0) 106 | } 107 | } 108 | } 109 | } 110 | 111 | // Page Allocator (soooo tiny version) 112 | ///// 113 | 114 | static mut base_addr: usize = 0; 115 | static mut last_index: usize = 0; 116 | static mut initialized: bool = false; 117 | 118 | pub fn init() { 119 | unsafe { 120 | base_addr = (heap_end() & !(0xfff as usize)) + 4096; // Mock heap that is page aligned 121 | last_index = 0; 122 | initialized = true; 123 | } 124 | } 125 | 126 | pub fn set_alloc_base(addr: usize) { 127 | unsafe { 128 | base_addr = addr; 129 | } 130 | } 131 | 132 | /// Allocated a page in the mock heap at the end of the elf 133 | pub fn alloc() -> Page { 134 | // TODO: this unsafe block is evil! 135 | unsafe { 136 | if !initialized { 137 | panic!("page manager was used but not initialized"); 138 | } 139 | 140 | last_index += 1; 141 | let addr = base_addr + (PAGE_SIZE as usize) * (last_index - 1); 142 | if addr > DRAM_END { 143 | panic!("memory exhausted; 0x{:016x}", addr) 144 | } 145 | let p = Page::from_address(PhysicalAddress::new(addr)); 146 | p.clear(); 147 | p 148 | } 149 | } 150 | 151 | /// Makes sure the root page follows a 16KiB boundry 152 | pub fn alloc_16() -> Page { 153 | let mut root_page = alloc(); 154 | while root_page.address().to_usize() & (0b11_1111_1111_1111 as usize) > 0 { 155 | log::debug!( 156 | "a page 0x{:016x} was allocated, but it does not follow 16KiB boundary. drop.", 157 | root_page.address().to_usize() 158 | ); 159 | root_page = alloc(); 160 | } 161 | alloc(); 162 | alloc(); 163 | alloc(); 164 | root_page 165 | } 166 | 167 | pub fn alloc_continuous(num: usize) -> Page { 168 | if num <= 0 { 169 | panic!("invalid arg for alloc_contenious: {}", num); 170 | } 171 | 172 | let first = alloc(); 173 | for _ in 0..(num - 1) { 174 | let _ = alloc(); 175 | } 176 | 177 | first 178 | } 179 | 180 | // Page Table 181 | ///// 182 | 183 | #[derive(Debug)] 184 | struct PageTableEntry { 185 | pub ppn: [usize; 3], 186 | pub flags: u16, 187 | } 188 | 189 | pub enum PageTableEntryFlag { 190 | Valid = 1 << 0, 191 | Read = 1 << 1, 192 | Write = 1 << 2, 193 | Execute = 1 << 3, 194 | User = 1 << 4, 195 | Global = 1 << 5, 196 | Access = 1 << 6, 197 | Dirty = 1 << 7, 198 | // TODO (enhancement): RSW 199 | } 200 | 201 | impl PageTableEntry { 202 | pub fn from_value(v: usize) -> PageTableEntry { 203 | let ppn = [ (v >> 10) & 0x1ff, // PPN[0] 9 bit 204 | (v >> 19) & 0x1ff, // PPN[1] 9 bit 205 | (v >> 28) & 0x3ff_ffff]; // PPN[2] 26 bit 206 | PageTableEntry { 207 | ppn: ppn, 208 | flags: (v & (0x1ff as usize)) as u16, // flags 8 bit (seems like this is 9?) 209 | } 210 | } 211 | 212 | pub unsafe fn from_memory(paddr: PhysicalAddress) -> PageTableEntry { 213 | let ptr = paddr.as_pointer(); 214 | let entry = *ptr; 215 | PageTableEntry::from_value(entry) 216 | } 217 | 218 | pub fn to_usize(&self) -> usize { 219 | (if (self.ppn[2] >> 25) & 1 > 0 { 220 | 0x3ff << 54 221 | } else { 222 | 0 223 | }) | ((self.ppn[2] as usize) << 28) 224 | | ((self.ppn[1] as usize) << 19) 225 | | ((self.ppn[0] as usize) << 10) 226 | | (self.flags as usize) 227 | } 228 | 229 | pub fn next_page(&self) -> Page { 230 | Page::from_address(PhysicalAddress::new( 231 | (self.ppn[2] << 30) | (self.ppn[1] << 21) | (self.ppn[0] << 12), 232 | )) 233 | } 234 | 235 | pub fn set_flag(&mut self, flag: PageTableEntryFlag) { 236 | self.flags |= flag as u16; 237 | } 238 | 239 | pub fn is_valid(&self) -> bool { 240 | self.flags & (PageTableEntryFlag::Valid as u16) != 0 241 | } 242 | // A leaf has one or more RWX bits set 243 | pub fn is_leaf(&self) -> bool { 244 | self.flags & 0xe != 0 245 | } 246 | 247 | pub fn is_branch(&self) -> bool { 248 | !self.is_leaf() 249 | } 250 | } 251 | 252 | pub struct PageTable { 253 | pub page: Page, 254 | } 255 | 256 | // TODO (enhancement): this naming is not so good. 257 | // This implementation assumes the paging would be done with Sv39, 258 | // but the word 'page table" is a more general idea. 259 | // We can rename this like `Sv39PageTable` or change the implementation in a more polymorphic way. 260 | impl PageTable { 261 | fn set_entry(&self, i: usize, entry: PageTableEntry) { 262 | let ptr = self.page.address().as_pointer() as *mut usize; 263 | unsafe { ptr.add(i).write(entry.to_usize()) } 264 | } 265 | 266 | fn get_entry(&self, i: usize) -> PageTableEntry { 267 | let ptr = self.page.address().as_pointer() as *mut usize; 268 | unsafe { PageTableEntry::from_value(ptr.add(i).read()) } 269 | } 270 | 271 | pub fn from_page(page: Page) -> PageTable { 272 | PageTable { page: page } 273 | } 274 | 275 | pub fn resolve(&self, vaddr: &VirtualAddress) -> PhysicalAddress { 276 | self.resolve_intl(vaddr, self, 2) 277 | } 278 | 279 | fn resolve_intl( 280 | &self, 281 | vaddr: &VirtualAddress, 282 | pt: &PageTable, 283 | level: usize, 284 | ) -> PhysicalAddress { 285 | let vpn = vaddr.to_vpn(); 286 | 287 | let entry = pt.get_entry(vpn[level]); 288 | if !entry.is_valid() { 289 | panic!("failed to resolve vaddr: 0x{:016x}", vaddr.addr) 290 | } 291 | 292 | if level == 0 { 293 | let addr_base = entry.next_page().address().to_usize(); 294 | PhysicalAddress::new(addr_base | vaddr.to_offset()) 295 | } else { 296 | let next_page = entry.next_page(); 297 | let new_pt = PageTable::from_page(next_page); 298 | self.resolve_intl(vaddr, &new_pt, level - 1) 299 | } 300 | } 301 | 302 | pub fn map(&self, vaddr: VirtualAddress, dest: &Page, perm: u16) { 303 | self.map_intl(vaddr, dest, self, perm, 2) 304 | } 305 | 306 | fn map_intl( 307 | &self, 308 | vaddr: VirtualAddress, 309 | dest: &Page, 310 | pt: &PageTable, 311 | perm: u16, 312 | level: usize, 313 | ) { 314 | let vpn = vaddr.to_vpn(); 315 | 316 | if level == 0 { 317 | // register `dest` addr 318 | let new_entry = PageTableEntry::from_value( 319 | ((dest.address().to_usize() as i64 >> 2) as usize) 320 | | (PageTableEntryFlag::Valid as usize) 321 | | (PageTableEntryFlag::Dirty as usize) 322 | | (PageTableEntryFlag::Access as usize) 323 | | (perm as usize), 324 | ); 325 | pt.set_entry(vpn[0], new_entry); 326 | } else { 327 | // walk the page table 328 | let entry = pt.get_entry(vpn[level]); 329 | if !entry.is_valid() { 330 | // if no entry found, create new page and assign it. 331 | let new_page = alloc(); 332 | let new_entry = PageTableEntry::from_value( 333 | ((new_page.address().to_usize() as i64 >> 2) as usize) 334 | | (PageTableEntryFlag::Valid as usize), 335 | ); 336 | pt.set_entry(vpn[level], new_entry); 337 | let new_pt = PageTable::from_page(new_page); 338 | self.map_intl(vaddr, dest, &new_pt, perm, level - 1); 339 | } else { 340 | let next_page = entry.next_page(); 341 | let new_pt = PageTable::from_page(next_page); 342 | self.map_intl(vaddr, dest, &new_pt, perm, level - 1); 343 | }; 344 | } 345 | } 346 | 347 | fn print_walk_page_table(&self, next_pt:PageTable, level: usize, vpn: [usize ; 3]) -> usize { 348 | // Very messy but it works 349 | let mut physical_addr = 0; 350 | let mut virtual_addr = 0; 351 | let mut total_pages = 0; 352 | let mut total_valid_entries = 0; 353 | for i in 0..512{ 354 | let entry = next_pt.get_entry(i); 355 | if entry.is_valid(){ 356 | total_valid_entries = total_valid_entries + 1; 357 | if level == 0 { 358 | let new_physical_addr = entry.to_usize() << 2 & !0x3ff ; 359 | //print!("vpn:{:?} ppn:{:?} entry: {:?}, physical 0x{:x} ", vpn, entry.ppn, entry, new_physical_addr); 360 | if new_physical_addr - PAGE_SIZE as usize == physical_addr { 361 | //print!("SAME "); 362 | virtual_addr = VirtualAddress::new_from_vpn(vpn).to_usize() + i * PAGE_SIZE as usize; 363 | //log::info!("Virt: 0x{:x} => Phys: 0x{:x}", virtual_addr, new_physical_addr); 364 | physical_addr = new_physical_addr; 365 | total_pages = total_pages + 1; 366 | } else { 367 | if total_pages != 0 { 368 | log::info!("..."); 369 | log::info!("Virt: 0x{:x} => Phys: 0x{:x}", virtual_addr, physical_addr); 370 | log::info!("Num pages after each other: {}", total_pages); 371 | log::info!("") 372 | }else{ 373 | log::info!("") 374 | } 375 | physical_addr = new_physical_addr; 376 | total_pages = total_pages + 1; 377 | virtual_addr = VirtualAddress::new_from_vpn(vpn).to_usize() + i * PAGE_SIZE as usize; 378 | log::info!("Virt: 0x{:x} => Phys: 0x{:x}", virtual_addr, new_physical_addr); 379 | //log::info!(""); 380 | total_pages = 0; 381 | } 382 | } else { 383 | let mut vpn = vpn; 384 | vpn[level] = i; 385 | let next_page = entry.next_page(); 386 | let new_pt = PageTable::from_page(next_page); 387 | total_valid_entries = total_valid_entries + self.print_walk_page_table(new_pt, level - 1, vpn); 388 | } 389 | } 390 | } 391 | if total_pages != 0 { 392 | log::info!("..."); 393 | log::info!("Virt: 0x{:x} => Phys: 0x{:x}", virtual_addr, physical_addr); 394 | log::info!("Num pages after each other: {}", total_pages); 395 | log::info!("") 396 | } 397 | total_valid_entries 398 | } 399 | 400 | pub fn print_page_allocations(&self){ 401 | // Walking all the entries in the pagetable 402 | // assumes Sv39 403 | if unsafe {initialized} { 404 | let pt = PageTable::from_page(self.page); 405 | log::info!(""); 406 | log::info!("PAGE ALLOCATION TABLE"); 407 | log::info!("ALLOCATED: 0x{:x} -> 0x{:x}", 408 | unsafe{base_addr}, 409 | unsafe{base_addr + (PAGE_SIZE as usize) * (last_index - 1)} 410 | ); 411 | log::info!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 412 | let vpn = [0 ; 3]; 413 | let total_entries = self.print_walk_page_table(pt, 2, vpn); 414 | //log::info!("{:?}", pagemapping); 415 | 416 | log::info!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 417 | log::info!( 418 | "Allocated: {:>6} pages ({:>10} bytes).", 419 | total_entries, 420 | total_entries * PAGE_SIZE as usize 421 | ); 422 | /* 423 | log::info!( 424 | "Free : {:>6} pages ({:>10} bytes).", 425 | num_pages - num, 426 | (num_pages - num) * PAGE_SIZE 427 | ); 428 | */ 429 | log::info!(""); 430 | 431 | } else { 432 | log::info!("Pageing not initilized"); 433 | } 434 | 435 | } 436 | } 437 | 438 | /* 439 | /// Print all page allocations 440 | /// This is mainly used for debugging. 441 | 442 | pub fn print_page_allocations() { 443 | unsafe { 444 | let num_pages = last_index+1; 445 | let mut beg = base_addr as *const PageTableEntry; 446 | let end = beg.add(num_pages); 447 | let alloc_beg = base_addr; 448 | let alloc_end = base_addr + num_pages * PAGE_SIZE as usize; 449 | println!(); 450 | println!( 451 | "PAGE ALLOCATION TABLE\nMETA: {:p} -> {:p}\nPHYS: \ 452 | 0x{:x} -> 0x{:x}", 453 | beg, end, alloc_beg, alloc_end 454 | ); 455 | println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 456 | let mut num = 0; 457 | while beg < end { 458 | if (*beg).is_taken() { 459 | let start = beg as usize; 460 | let memaddr = base_addr 461 | + (start - HEAP_START) 462 | * PAGE_SIZE; 463 | print!("0x{:x} => ", memaddr); 464 | loop { 465 | num += 1; 466 | if (*beg).is_last() { 467 | let end = beg as usize; 468 | let memaddr = base_addr 469 | + (end - HEAP_START) 470 | * PAGE_SIZE 471 | + PAGE_SIZE - 1; 472 | print!( 473 | "0x{:x}: {:>3} page(s)", 474 | memaddr, 475 | (end - start + 1) 476 | ); 477 | println!("."); 478 | break; 479 | } 480 | beg = beg.add(1); 481 | } 482 | } 483 | beg = beg.add(1); 484 | } 485 | println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 486 | println!( 487 | "Allocated: {:>6} pages ({:>10} bytes).", 488 | num, 489 | num * PAGE_SIZE 490 | ); 491 | println!( 492 | "Free : {:>6} pages ({:>10} bytes).", 493 | num_pages - num, 494 | (num_pages - num) * PAGE_SIZE 495 | ); 496 | println!(); 497 | } 498 | } 499 | */ -------------------------------------------------------------------------------- /hypervisor/src/plic.rs: -------------------------------------------------------------------------------- 1 | use crate::memlayout; 2 | 3 | pub fn enable_interrupt() { 4 | // TODO (enhancement): UART0 5 | 6 | // configure PLIC 7 | unsafe { 8 | let plic_base = memlayout::PLIC_BASE as *mut u32; 9 | plic_base 10 | .offset(memlayout::VIRTIO0_IRQ as isize) 11 | .write_volatile(1); 12 | plic_base 13 | .offset(memlayout::UART0_IRQ as isize) 14 | .write_volatile(1); 15 | plic_base 16 | .offset((memlayout::PLIC_HART1_M_INT_EN / 4) as isize) 17 | .write_volatile((1 << memlayout::VIRTIO0_IRQ) | (1 << memlayout::UART0_IRQ)); 18 | plic_base.offset((memlayout::PLIC_HART1_M_PRIORITY / 4) as isize).write_volatile(0); 19 | } 20 | } 21 | 22 | pub fn complete(interrupt: u32) { 23 | let plic_base = memlayout::PLIC_BASE as *mut u32; 24 | unsafe { plic_base.offset((memlayout::PLIC_HART1_M_CLAIM_COMPLETE / 4) as isize ).write_volatile(interrupt) } 25 | } 26 | 27 | pub fn get_claim() -> Option { 28 | let plic_base = memlayout::PLIC_BASE as *mut u32; 29 | unsafe { 30 | let v = plic_base.offset((memlayout::PLIC_HART1_M_CLAIM_COMPLETE / 4) as isize ).read_volatile(); 31 | if v == 0 { 32 | None 33 | } else { 34 | Some(v) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /hypervisor/src/riscv.rs: -------------------------------------------------------------------------------- 1 | pub mod csr; 2 | pub mod gpr; 3 | pub mod instruction; 4 | pub mod interrupt; -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod macros; 3 | 4 | #[derive(PartialEq)] 5 | pub enum CpuMode { 6 | M = 0b11, 7 | S = 0b01, 8 | U = 0b00, 9 | } 10 | 11 | pub enum VirtualzationMode { 12 | Host = 0, 13 | Guest = 1, 14 | } 15 | 16 | pub enum PreviousMode { 17 | U_mode, 18 | HS_mode, 19 | M_mode, 20 | VU_mode, 21 | VS_mode 22 | } 23 | 24 | pub mod medeleg; 25 | pub mod mepc; 26 | pub mod mideleg; 27 | pub mod mie; 28 | pub mod mip; 29 | pub mod misa; 30 | pub mod mstatus; 31 | pub mod mtvec; 32 | pub mod mcause; 33 | pub mod mhartid; 34 | 35 | pub mod satp; 36 | pub mod sepc; 37 | pub mod sie; 38 | pub mod sscratch; 39 | pub mod sstatus; 40 | pub mod stvec; 41 | pub mod scounteren; 42 | pub mod scause; 43 | pub mod stval; 44 | pub mod sip; 45 | 46 | pub fn dump_s_csr() { 47 | let s_csr = [ 48 | satp::read(), 49 | sepc::read(), 50 | sie::read(), 51 | sscratch::read(), 52 | sstatus::read(), 53 | stvec::read(), 54 | scounteren::read(), 55 | scause::read(), 56 | stval::read(), 57 | sip::read()]; 58 | let s_csr_name = [ 59 | "satp", 60 | "sepc", 61 | "sie", 62 | "sscratch", 63 | "sstatus", 64 | "stvec", 65 | "scounteren", 66 | "scause", 67 | "stval", 68 | "sip"]; 69 | for i in 0..s_csr.len() { 70 | print!("{:<10} = 0x{:016x} ", s_csr_name[i], s_csr[i]); 71 | if i % 4 == 3 || i == s_csr.len()-1 { 72 | println!(); 73 | } else { 74 | print!("| ") 75 | } 76 | } 77 | } 78 | 79 | pub mod hcontext; 80 | pub mod hedeleg; 81 | pub mod hcounteren; 82 | pub mod hgatp; 83 | pub mod hgeie; 84 | pub mod hgeip; 85 | pub mod hideleg; 86 | pub mod hie; 87 | pub mod hip; 88 | pub mod hstatus; 89 | pub mod htval; 90 | pub mod hvip; 91 | pub mod htimedelta; 92 | 93 | pub fn dump_h_csr() { 94 | let h_csr = [ 95 | //hcontext::read(), // Causes fault when read 96 | hedeleg::read(), 97 | hcounteren::read(), 98 | hgatp::read(), 99 | hgeie::read(), 100 | hgeip::read(), 101 | hideleg::read(), 102 | hie::read(), 103 | hip::read(), 104 | hstatus::read(), 105 | htval::read(), 106 | hvip::read(), 107 | htimedelta::read()]; 108 | let h_csr_name = [ 109 | //"hcontext", 110 | "hedeleg", 111 | "hcounteren", 112 | "hgatp", 113 | "hgeie", 114 | "hgeip", 115 | "hideleg", 116 | "hie", 117 | "hip", 118 | "hstatus", 119 | "htval", 120 | "hvip", 121 | "htimedelta"]; 122 | for i in 0..h_csr.len() { 123 | print!("{:<10} = 0x{:016x} ", h_csr_name[i], h_csr[i]); 124 | if i % 4 == 3 || i == h_csr.len()-1 { 125 | println!(); 126 | } else { 127 | print!("| ") 128 | } 129 | } 130 | } 131 | 132 | pub mod vsatp; 133 | pub mod vscause; 134 | pub mod vsepc; 135 | pub mod vsie; 136 | pub mod vsip; 137 | pub mod vsscratch; 138 | pub mod vsstatus; 139 | pub mod vstval; 140 | pub mod vstvec; 141 | 142 | pub fn dump_vs_csr(){ 143 | let vs_csr = [vsatp::read(), vscause::read(), vsepc::read(), vsie::read(), vsip::read(), vsscratch::read(), vsstatus::read(), vstval::read(), vstvec::read()]; 144 | let vs_csr_name = ["vsatp", "vscause", "vsepc", "vsie", "vsip", "vsscratch", "vsstatus", "vstval", "vstvec"]; 145 | for i in 0..9 { 146 | print!("{:<10} = 0x{:016x} ", vs_csr_name[i], vs_csr[i]); 147 | if i % 4 == 3 || i == 9-1 { 148 | println!(); 149 | } else { 150 | print!("| ") 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/cpumode.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stemnic/rustyvisor/392a2ae51a715d3ff362fd3311a65992d4bb837f/hypervisor/src/riscv/csr/cpumode.rs -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hcontext.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x6A8); 2 | define_write!(0x6A8); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hcounteren.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x606); 2 | define_write!(0x606); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hedeleg.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x602); 2 | define_write!(0x602); 3 | 4 | pub const INST_ADDR_MISALIGN: usize = 1 << 0; 5 | pub const BREAKPOINT: usize = 1 << 3; 6 | pub const ENV_CALL_FROM_U_MODE_OR_VU_MODE: usize = 1 << 8; 7 | pub const INST_PAGE_FAULT: usize = 1 << 12; 8 | pub const LOAD_PAGE_FAULT: usize = 1 << 13; 9 | pub const STORE_AMO_PAGE_FAULT: usize = 1 << 15; -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/henvcfg.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x60A); 2 | define_write!(0x60A); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hgatp.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x680); 2 | define_write!(0x680); 3 | 4 | #[derive(Copy, Clone)] 5 | pub enum Mode { 6 | Bare = 0, 7 | Sv39x4 = 8, 8 | Sv48x4 = 9, 9 | Sv57x4 = 10, 10 | } 11 | 12 | pub fn set(s: &Setting) { 13 | write(s.to_usize()); 14 | } 15 | 16 | pub struct Setting { 17 | pub mode: Mode, 18 | pub vmid: u16, 19 | pub ppn: usize, 20 | } 21 | 22 | impl Setting { 23 | pub fn new(mode: Mode, vmid: u16, ppn: usize) -> Setting { 24 | Setting { 25 | mode: mode, 26 | vmid: vmid, 27 | ppn: ppn, 28 | } 29 | } 30 | 31 | #[cfg(target_pointer_width = "64")] 32 | pub fn to_usize(&self) -> usize { 33 | let mut v: usize = 0; 34 | v |= (self.mode as usize) << 60; 35 | v |= (self.vmid as usize) << 44; 36 | v |= self.ppn; 37 | v 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hgeie.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x607); 2 | define_write!(0x607); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hgeip.rs: -------------------------------------------------------------------------------- 1 | define_read!(0xE12); 2 | define_write!(0xE12); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hideleg.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x603); 2 | define_write!(0x603); 3 | 4 | pub const VSEIP: usize = 1 << 10; 5 | pub const VSTIP: usize = 1 << 6; 6 | pub const VSSIP: usize = 1 << 2; 7 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hie.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x604); 2 | define_write!(0x604); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hip.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x644); 2 | define_write!(0x644); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hstatus.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x600); 2 | define_write!(0x600); 3 | 4 | use super::sstatus; 5 | use super::PreviousMode; 6 | use core::fmt::Error; 7 | 8 | pub fn set_spv(mode: crate::riscv::csr::VirtualzationMode) { 9 | let hstatus = read(); 10 | let spv_mask = !(0b1 << 7 as usize); 11 | write((hstatus & spv_mask) | ((mode as usize) << 7)) 12 | } 13 | 14 | pub fn read_spv() -> crate::riscv::csr::VirtualzationMode { 15 | let hstatus = read(); 16 | if ((hstatus >> 7) & 0b1) == 0b0 { 17 | crate::riscv::csr::VirtualzationMode::Host 18 | } else { 19 | crate::riscv::csr::VirtualzationMode::Guest 20 | } 21 | } 22 | 23 | pub fn previous_mode() -> Result { 24 | let spv = read_spv(); 25 | let spp = sstatus::read_spp(); 26 | match spv { 27 | super::VirtualzationMode::Host => { 28 | match spp { 29 | super::CpuMode::M => Err(core::fmt::Error), 30 | super::CpuMode::S => Ok(PreviousMode::HS_mode), 31 | super::CpuMode::U => Ok(PreviousMode::U_mode), 32 | } 33 | }, 34 | super::VirtualzationMode::Guest => { 35 | match spp { 36 | super::CpuMode::M => Err(core::fmt::Error), 37 | super::CpuMode::S => Ok(PreviousMode::VS_mode), 38 | super::CpuMode::U => Ok(PreviousMode::VU_mode), 39 | } 40 | }, 41 | } 42 | } -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/htimedelta.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x605); 2 | define_write!(0x605); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/htinst.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x64A); 2 | define_write!(0x64A); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/htval.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x643); 2 | define_write!(0x643); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/hvip.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x645); 2 | define_write!(0x645); 3 | 4 | pub const VSSIP: usize = 1 << 2; 5 | pub const VSTIP: usize = 1 << 6; 6 | pub const VSEIP: usize = 1 << 10; 7 | 8 | pub fn trigger_software_interrupt(){ 9 | write( VSSIP | read() ); 10 | } 11 | 12 | pub fn trigger_timing_interrupt(){ 13 | write( VSTIP | read() ); 14 | } 15 | 16 | pub fn clear_timing_interrupt(){ 17 | write( !(VSTIP) & read() ); 18 | } 19 | 20 | pub fn trigger_external_interrupt(){ 21 | write( VSEIP | read() ); 22 | } -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/macros.rs: -------------------------------------------------------------------------------- 1 | /* 2 | asm!(assembly template 3 | : output operands 4 | : input operands 5 | : clobbers 6 | : options 7 | ); 8 | 9 | */ 10 | 11 | macro_rules! define_read { 12 | ($csr_number:expr) => { 13 | use core::arch::asm; 14 | #[inline] 15 | pub fn read() -> usize { 16 | unsafe { 17 | let r: usize; 18 | asm!("csrrs {0}, {csr}, x0", 19 | out(reg) r, 20 | csr = const $csr_number, 21 | options(nostack) 22 | ); 23 | /* 24 | asm!("csrrs $0, $1, x0" 25 | : "=r"(r) 26 | : "i"($csr_number) 27 | : 28 | : "volatile"); 29 | */ 30 | r 31 | } 32 | } 33 | }; 34 | } 35 | 36 | macro_rules! define_write { 37 | ($csr_number:expr) => { 38 | pub fn write(v: usize) { 39 | unsafe { 40 | asm!("csrrw x0, {csr}, {rs}", 41 | rs = in(reg) v, 42 | csr = const $csr_number, 43 | options(nostack) 44 | ); 45 | /* 46 | asm!("csrrw x0, $1, $0" 47 | : 48 | : "r"(v), "i"($csr_number) 49 | : 50 | : "volatile"); 51 | */ 52 | } 53 | } 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/mcause.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x342); 2 | define_write!(0x342); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/medeleg.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x302); 2 | define_write!(0x302); 3 | 4 | pub const HYPERVISOR_ECALL: usize = 1 << 9; 5 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/mepc.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x341); 2 | define_write!(0x341); 3 | 4 | pub fn set(t: T) { 5 | write(t.convert_to_fn_address()); 6 | } 7 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/mhartid.rs: -------------------------------------------------------------------------------- 1 | define_read!(0xF14); 2 | define_write!(0xF14); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/mideleg.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x303); 2 | define_write!(0x303); 3 | 4 | pub const SEIP: usize = 1 << 9; 5 | pub const MTIP: usize = 1 << 7; 6 | pub const STIP: usize = 1 << 5; 7 | pub const MSIP: usize = 1 << 3; 8 | pub const SSIP: usize = 1 << 1; 9 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/mie.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x304); 2 | define_write!(0x304); 3 | 4 | const MTIE : usize = 0b1 << 7; 5 | const STIE : usize = 0b1 << 5; 6 | 7 | pub fn enable_m_mode_hardware_timer() { 8 | let mie = read(); 9 | let mtie_mask = !(MTIE); 10 | write((mie & mtie_mask) | ((1 as usize) << 7)) 11 | } 12 | 13 | pub fn clear_m_mode_hardware_timer() { 14 | let mie = read(); 15 | let mtie_mask = !(MTIE); 16 | write((mie & mtie_mask) | ((0 as usize) << 7)) 17 | } 18 | 19 | pub fn enable_s_mode_hardware_timer() { 20 | let mie = read(); 21 | let stie_mask = !(STIE); 22 | write((mie & stie_mask) | (STIE)) 23 | } 24 | 25 | pub fn clear_s_mode_hardware_timer() { 26 | let mie = read(); 27 | let stie_mask = !(STIE); 28 | write((mie & stie_mask) | ((0 as usize) << 5)) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/mip.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x344); 2 | define_write!(0x344); 3 | 4 | const STIP: usize = 0b1 << 5; 5 | 6 | pub fn set_stimer() { 7 | let mip = read(); 8 | let stip_mask = !(STIP); 9 | write((mip & stip_mask) | STIP) 10 | } 11 | 12 | pub fn clear_stimer() { 13 | let mip = read(); 14 | let stip_mask = !(STIP); 15 | write((mip & stip_mask) | ((0 as usize) << 5)) 16 | } -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/misa.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x301); 2 | define_write!(0x301); 3 | 4 | 5 | pub const HV: usize = 1 << 7; 6 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/mstatus.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Error; 2 | use super::{PreviousMode}; 3 | 4 | define_read!(0x300); 5 | define_write!(0x300); 6 | 7 | pub const MIE: usize = 0b1 << 3; 8 | pub const SIE: usize = 0b1 << 1; 9 | 10 | pub fn is_mie_set() -> bool { 11 | let mstatus = read(); 12 | if ((mstatus >> 3) & 0b1) == 0b1 { 13 | true 14 | } else { 15 | false 16 | } 17 | } 18 | 19 | pub fn set_mie() { 20 | let mstatus = read(); 21 | let mie_mask = MIE; 22 | write(mstatus | mie_mask); 23 | } 24 | 25 | pub fn clear_mie() { 26 | let mstatus = read(); 27 | let mie_mask = !(MIE); 28 | write(mstatus & mie_mask); 29 | } 30 | 31 | pub fn set_sie() { 32 | let mstatus = read(); 33 | let mie_mask = SIE; 34 | write(mstatus | mie_mask); 35 | } 36 | 37 | pub fn clear_sie() { 38 | let mstatus = read(); 39 | let sie_mask = !(SIE); 40 | write(mstatus & sie_mask); 41 | } 42 | 43 | pub fn set_mpp(mode: crate::riscv::csr::CpuMode) { 44 | let mstatus = read(); 45 | let mpp_mask = !(0b11 << 11 as usize); 46 | write((mstatus & mpp_mask) | ((mode as usize) << 11)) 47 | } 48 | 49 | pub fn read_mpp() -> crate::riscv::csr::CpuMode { 50 | let mstatus = read(); 51 | let mpv = (mstatus >> 11) & 0b11; 52 | if mpv == 0b00 { 53 | crate::riscv::csr::CpuMode::U 54 | } else if mpv == 0b01 { 55 | crate::riscv::csr::CpuMode::S 56 | } else { 57 | crate::riscv::csr::CpuMode::M 58 | } 59 | } 60 | 61 | pub fn set_mpv(mode: crate::riscv::csr::VirtualzationMode) { 62 | let mstatus = read(); 63 | let mpv_mask = !(0b1 << 39 as usize); 64 | write((mstatus & mpv_mask) | ((mode as usize) << 39)) 65 | } 66 | 67 | pub fn read_mpv() -> crate::riscv::csr::VirtualzationMode { 68 | let mstatus = read(); 69 | if ((mstatus >> 39) & 0b1) == 0b0 { 70 | crate::riscv::csr::VirtualzationMode::Host 71 | } else { 72 | crate::riscv::csr::VirtualzationMode::Guest 73 | } 74 | } 75 | 76 | pub fn previous_mode() -> Result { 77 | let mpv = read_mpv(); 78 | let mpp = read_mpp(); 79 | match mpv { 80 | super::VirtualzationMode::Host => { 81 | match mpp { 82 | super::CpuMode::M => Ok(PreviousMode::M_mode), 83 | super::CpuMode::S => Ok(PreviousMode::HS_mode), 84 | super::CpuMode::U => Ok(PreviousMode::U_mode), 85 | } 86 | }, 87 | super::VirtualzationMode::Guest => { 88 | match mpp { 89 | super::CpuMode::M => Err(core::fmt::Error), 90 | super::CpuMode::S => Ok(PreviousMode::VS_mode), 91 | super::CpuMode::U => Ok(PreviousMode::VU_mode), 92 | } 93 | }, 94 | } 95 | } 96 | 97 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/mtvec.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x305); 2 | define_write!(0x305); 3 | 4 | pub fn set(t: &T) { 5 | write(t.convert_to_fn_address()); 6 | } 7 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/satp.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x180); 2 | define_write!(0x180); 3 | 4 | #[derive(Copy, Clone)] 5 | pub enum Mode { 6 | Bare = 0, 7 | Sv39 = 8, 8 | Sv48 = 9, 9 | } 10 | 11 | pub fn set(s: Setting) { 12 | write(s.to_usize()); 13 | } 14 | 15 | pub struct Setting { 16 | mode: Mode, 17 | asid: u16, 18 | ppn: usize, 19 | } 20 | 21 | impl Setting { 22 | pub fn new(mode: Mode, asid: u16, ppn: usize) -> Setting { 23 | Setting { 24 | mode: mode, 25 | asid: asid, 26 | ppn: ppn, 27 | } 28 | } 29 | 30 | #[cfg(target_pointer_width = "64")] 31 | pub fn to_usize(&self) -> usize { 32 | let mut v: usize = 0; 33 | v |= (self.mode as usize) << 60; 34 | v |= (self.asid as usize) << 44; 35 | v |= self.ppn; 36 | v 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/scause.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x142); 2 | define_write!(0x142); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/scounteren.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x106); 2 | define_write!(0x106); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/sepc.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x141); 2 | define_write!(0x141); 3 | 4 | pub fn set(t: &T) { 5 | write(t.convert_to_fn_address()); 6 | } 7 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/sie.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x104); 2 | define_write!(0x104); 3 | 4 | pub const SEIE: usize = 1 << 9; 5 | 6 | const STIE : usize = 0b1 << 5; 7 | 8 | pub fn enable_hardware_timer() { 9 | let sie = read(); 10 | let stie_mask = !(STIE); 11 | write((sie & stie_mask) | ((1 as usize) << 5)) 12 | } 13 | 14 | pub fn clear_hardware_timer() { 15 | let sie = read(); 16 | write(sie & !(STIE)) 17 | } 18 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/sip.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x144); 2 | define_write!(0x144); 3 | 4 | pub const STIP: usize = 0b1 << 5; 5 | 6 | pub fn clear_stimer() { 7 | let sip = read(); 8 | write(sip & !(STIP)) 9 | } -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/sscratch.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x140); 2 | define_write!(0x140); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/sstatus.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x100); 2 | define_write!(0x100); 3 | 4 | pub fn set_spp(mode: crate::riscv::csr::CpuMode) { 5 | if mode == crate::riscv::csr::CpuMode::M { 6 | log::debug!("set_spp was called riscv::csr::CpuMode::M") 7 | } 8 | 9 | let sstatus = read(); 10 | let spp_mask = !(0b1 << 8 as usize); 11 | write((sstatus & spp_mask) | (((mode as usize) & 1) << 8)) 12 | } 13 | 14 | pub fn read_spp() -> crate::riscv::csr::CpuMode { 15 | let sstatus = read(); 16 | let spv = (sstatus >> 8) & 0b1; 17 | if spv == 0b0 { 18 | crate::riscv::csr::CpuMode::U 19 | } else { 20 | crate::riscv::csr::CpuMode::S 21 | } 22 | } 23 | 24 | pub fn set_sie(enabled: bool) { 25 | let sstatus = read(); 26 | let sie_mask = 1 << 1 as usize; 27 | write((sstatus & sie_mask) | (if enabled { 1 << 1 } else { 0 })); 28 | } 29 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/stval.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x143); 2 | define_write!(0x143); 3 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/stvec.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x105); 2 | define_write!(0x105); 3 | 4 | pub fn set(t: &T) { 5 | write(t.convert_to_fn_address()); 6 | } 7 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/vsatp.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x280); 2 | define_write!(0x280); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/vscause.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x242); 2 | define_write!(0x242); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/vsepc.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x241); 2 | define_write!(0x241); 3 | 4 | pub fn set(t: &T) { 5 | write(t.convert_to_fn_address()); 6 | } 7 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/vsie.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x204); 2 | define_write!(0x204); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/vsip.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x244); 2 | define_write!(0x244); 3 | 4 | pub const VSTIP: usize = 0b1 << 5; 5 | 6 | pub fn set_vstimer() { 7 | let vsip = read(); 8 | write(vsip | (VSTIP)) 9 | } 10 | 11 | pub fn clear_vstimer() { 12 | let vsip = read(); 13 | write(vsip & !(VSTIP)) 14 | } -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/vsscratch.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x240); 2 | define_write!(0x240); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/vsstatus.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x200); 2 | define_write!(0x200); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/vstval.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x243); 2 | define_write!(0x243); -------------------------------------------------------------------------------- /hypervisor/src/riscv/csr/vstvec.rs: -------------------------------------------------------------------------------- 1 | define_read!(0x205); 2 | define_write!(0x205); -------------------------------------------------------------------------------- /hypervisor/src/riscv/gpr.rs: -------------------------------------------------------------------------------- 1 | // TODO (enhancement): this code is terrible. 2 | // however, due to the restriction of llvm_asm macro and asm macro, it's hard to rewrite this cleanly :-( 3 | 4 | extern crate num_enum; 5 | 6 | use core::{convert::TryFrom, arch::asm}; 7 | use core::marker::Copy; 8 | use num_enum::{IntoPrimitive, TryFromPrimitive}; 9 | 10 | #[repr(usize)] 11 | #[derive(Clone, Copy, IntoPrimitive, TryFromPrimitive)] 12 | pub enum Register { 13 | Zero = 0, 14 | Ra, 15 | Sp, 16 | Gp, 17 | Tp, 18 | T0, 19 | T1, 20 | T2, 21 | S0, 22 | S1, 23 | A0, 24 | A1, 25 | A2, 26 | A3, 27 | A4, 28 | A5, 29 | A6, 30 | A7, 31 | S2, 32 | S3, 33 | S4, 34 | S5, 35 | S6, 36 | S7, 37 | S8, 38 | S9, 39 | S10, 40 | S11, 41 | T3, 42 | T4, 43 | T5, 44 | T6, 45 | } 46 | 47 | impl core::fmt::Display for Register { 48 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 49 | let s = match *self { 50 | Register::Zero => "x0", 51 | Register::Ra => "ra", 52 | Register::Sp => "sp", 53 | Register::Gp => "gp", 54 | Register::Tp => "tp", 55 | Register::T0 => "t0", 56 | Register::T1 => "t1", 57 | Register::T2 => "t2", 58 | Register::S0 => "s0", 59 | Register::S1 => "s1", 60 | Register::A0 => "a0", 61 | Register::A1 => "a1", 62 | Register::A2 => "a2", 63 | Register::A3 => "a3", 64 | Register::A4 => "a4", 65 | Register::A5 => "a5", 66 | Register::A6 => "a6", 67 | Register::A7 => "a7", 68 | Register::S2 => "s2", 69 | Register::S3 => "s3", 70 | Register::S4 => "s4", 71 | Register::S5 => "s5", 72 | Register::S6 => "s6", 73 | Register::S7 => "s8", 74 | Register::S8 => "s8", 75 | Register::S9 => "s9", 76 | Register::S10 => "s10", 77 | Register::S11 => "s11", 78 | Register::T3 => "t3", 79 | Register::T4 => "t4", 80 | Register::T5 => "t5", 81 | Register::T6 => "t6", 82 | }; 83 | f.pad(s) 84 | } 85 | } 86 | 87 | impl Register { 88 | fn read(&self) -> usize { 89 | unsafe { 90 | let rval; 91 | match *self { 92 | Register::Zero => asm!("addi {}, zero, 0", out(reg) rval), 93 | Register::Ra => asm!("addi {}, ra, 0", out(reg) rval), 94 | Register::Sp => asm!("addi {}, sp, 0", out(reg) rval), 95 | Register::Gp => asm!("addi {}, gp, 0", out(reg) rval), 96 | Register::Tp => asm!("addi {}, tp, 0", out(reg) rval), 97 | Register::T0 => asm!("addi {}, t0, 0", out(reg) rval), 98 | Register::T1 => asm!("addi {}, t1, 0", out(reg) rval), 99 | Register::T2 => asm!("addi {}, t2, 0", out(reg) rval), 100 | Register::S0 => asm!("addi {}, s0, 0", out(reg) rval), 101 | Register::S1 => asm!("addi {}, s1, 0", out(reg) rval), 102 | Register::A0 => asm!("addi {}, a0, 0", out(reg) rval), 103 | Register::A1 => asm!("addi {}, a1, 0", out(reg) rval), 104 | Register::A2 => asm!("addi {}, a2, 0", out(reg) rval), 105 | Register::A3 => asm!("addi {}, a3, 0", out(reg) rval), 106 | Register::A4 => asm!("addi {}, a4, 0", out(reg) rval), 107 | Register::A5 => asm!("addi {}, a5, 0", out(reg) rval), 108 | Register::A6 => asm!("addi {}, a6, 0", out(reg) rval), 109 | Register::A7 => asm!("addi {}, a7, 0", out(reg) rval), 110 | Register::S2 => asm!("addi {}, s2, 0", out(reg) rval), 111 | Register::S3 => asm!("addi {}, s3, 0", out(reg) rval), 112 | Register::S4 => asm!("addi {}, s4, 0", out(reg) rval), 113 | Register::S5 => asm!("addi {}, s5, 0", out(reg) rval), 114 | Register::S6 => asm!("addi {}, s6, 0", out(reg) rval), 115 | Register::S7 => asm!("addi {}, s7, 0", out(reg) rval), 116 | Register::S8 => asm!("addi {}, s8, 0", out(reg) rval), 117 | Register::S9 => asm!("addi {}, s9, 0", out(reg) rval), 118 | Register::S10 => asm!("addi {}, s10, 0", out(reg) rval), 119 | Register::S11 => asm!("addi {}, s11, 0", out(reg) rval), 120 | Register::T3 => asm!("addi {}, t3, 0", out(reg) rval), 121 | Register::T4 => asm!("addi {}, t4, 0", out(reg) rval), 122 | Register::T5 => asm!("addi {}, t5, 0", out(reg) rval), 123 | Register::T6 => asm!("addi {}, t6, 0", out(reg) rval), 124 | }; 125 | rval 126 | } 127 | } 128 | } 129 | 130 | pub fn dump() { 131 | for i in 0..32 { 132 | let reg = Register::try_from(i).unwrap(); 133 | print!("{:<3} = 0x{:016x} ", reg, reg.read()); 134 | if i % 4 == 3 { 135 | println!(); 136 | } else { 137 | print!("| ") 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/instruction.S: -------------------------------------------------------------------------------- 1 | .option nor 2 | 3 | .section .text.instruction 4 | .global __hfence_gvma_all 5 | 6 | __hfence_gvma_all: 7 | .word 0x62000073 8 | ret -------------------------------------------------------------------------------- /hypervisor/src/riscv/instruction.rs: -------------------------------------------------------------------------------- 1 | use core::arch::{asm, global_asm}; 2 | 3 | global_asm!(include_str!("instruction.S")); 4 | 5 | pub fn ecall_with_args(arg0: usize, arg1: usize, arg2: usize, arg3: usize) -> (usize, usize) { 6 | let (out0, out1); 7 | match () { 8 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 9 | () => unsafe { asm!( 10 | "ecall", 11 | in("a0") arg0, in("a1") arg1, 12 | in("a2") arg2, in("a3") arg3, 13 | lateout("a0") out0, lateout("a1") out1, 14 | ) }, 15 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 16 | () => { 17 | drop((extension, function, arg0, arg1)); 18 | unimplemented!("not RISC-V instruction set architecture") 19 | } 20 | }; 21 | (out0, out1) 22 | } 23 | 24 | pub fn mret() -> ! { 25 | unsafe { 26 | asm!("mret"); 27 | } 28 | 29 | loop {} 30 | } 31 | 32 | pub fn sret() -> ! { 33 | unsafe { 34 | asm!("sret"); 35 | } 36 | 37 | loop {} 38 | } 39 | 40 | pub fn ecall() { 41 | unsafe { 42 | asm!("ecall"); 43 | } 44 | } 45 | 46 | pub fn sfenve_vma() { 47 | unsafe { 48 | asm!("sfence.vma"); 49 | } 50 | } 51 | 52 | extern "C" { 53 | fn __hfence_gvma_all(); 54 | } 55 | 56 | pub fn hfence_gvma() { 57 | unsafe { 58 | __hfence_gvma_all(); 59 | } 60 | } 61 | 62 | pub fn wfi() { 63 | unsafe { 64 | asm!("wfi"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /hypervisor/src/riscv/interrupt.rs: -------------------------------------------------------------------------------- 1 | // Taken from the riscv rust package 2 | 3 | //! Interrupts 4 | 5 | // NOTE: Adapted from cortex-m/src/interrupt.rs 6 | pub use bare_metal::{CriticalSection, Mutex}; 7 | use super::csr::mstatus; 8 | use crate::m_mode_calls; 9 | 10 | /// Disables all interrupts 11 | #[inline] 12 | pub unsafe fn disable() { 13 | mstatus::clear_mie(); 14 | mstatus::clear_sie(); 15 | } 16 | 17 | /// Enables all the interrupts 18 | /// 19 | /// # Safety 20 | /// 21 | /// - Do not call this function inside an `interrupt::free` critical section 22 | #[inline] 23 | pub unsafe fn enable() { 24 | mstatus::set_mie(); 25 | mstatus::set_sie(); 26 | } 27 | 28 | /// Execute closure `f` in an interrupt-free context. 29 | /// 30 | /// This as also known as a "critical section". 31 | pub fn free(f: F) -> R 32 | where 33 | F: FnOnce(&CriticalSection) -> R, 34 | { 35 | 36 | // disable interrupts 37 | m_mode_calls::disable_timers(); 38 | 39 | //unsafe { 40 | // disable(); 41 | //} 42 | 43 | let r = f(unsafe { &CriticalSection::new() }); 44 | 45 | // If the interrupts were active before our `disable` call, then re-enable 46 | // them. Otherwise, keep them disabled 47 | 48 | m_mode_calls::enable_timers(); 49 | 50 | //if mstatus::is_mie_set() { 51 | // unsafe { 52 | // enable(); 53 | // } 54 | //} 55 | 56 | r 57 | } -------------------------------------------------------------------------------- /hypervisor/src/sbi.rs: -------------------------------------------------------------------------------- 1 | use crate::timer::TimerHandle; 2 | use rustsbi::{Forward, RustSBI}; 3 | 4 | #[derive(RustSBI)] 5 | pub struct VmSBI { 6 | timer: TimerHandle, 7 | #[rustsbi(info)] 8 | forward: Forward, 9 | } 10 | 11 | impl VmSBI { 12 | #[inline] 13 | pub fn with_guest_number(guest_number: u64) -> Self { 14 | VmSBI { 15 | timer: TimerHandle::new(guest_number), 16 | forward: Forward, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /hypervisor/src/timer.rs: -------------------------------------------------------------------------------- 1 | use crate::global_const::MAX_NUMBER_OF_GUESTS; 2 | 3 | #[derive(Debug, Copy, Clone)] 4 | pub struct VmTimers { 5 | timers : [VmTimer; MAX_NUMBER_OF_GUESTS] 6 | } 7 | 8 | impl VmTimers { 9 | pub fn new() -> VmTimers { 10 | VmTimers{ 11 | timers: [VmTimer::new() ; MAX_NUMBER_OF_GUESTS] 12 | } 13 | } 14 | pub fn tick_vm_timers(&mut self, amount: usize ){ 15 | let mut i = 0; 16 | while i < MAX_NUMBER_OF_GUESTS-1 { 17 | self.timers[i].tick(amount as u64); 18 | i += 1; 19 | } 20 | } 21 | pub fn debug_print(&self) { 22 | for timer in self.timers { 23 | log::info!("timer {}, mtime value: {}, mtimecmp value: {}", timer.enabled, timer.mtime, timer.mtimecmp); 24 | } 25 | } 26 | pub fn check_timers(&self) -> [bool; MAX_NUMBER_OF_GUESTS] { 27 | let mut vm_timer_list = [false ; MAX_NUMBER_OF_GUESTS]; 28 | let mut i = 0; 29 | while i < MAX_NUMBER_OF_GUESTS-1 { 30 | let vmtimer = self.timers[i]; 31 | if vmtimer.enabled { 32 | if vmtimer.mtime >= vmtimer.mtimecmp { 33 | vm_timer_list[i] = true; 34 | } 35 | } 36 | i += 1; 37 | } 38 | return vm_timer_list 39 | } 40 | } 41 | 42 | #[derive(Debug, Copy, Clone)] 43 | pub struct VmTimer { 44 | enabled: bool, 45 | mtime: u64, 46 | mtimecmp: u64 47 | } 48 | 49 | impl VmTimer { 50 | pub fn new() -> VmTimer { 51 | VmTimer{ 52 | enabled: false, 53 | mtime: 0, 54 | mtimecmp: 0 55 | } 56 | } 57 | 58 | pub fn tick(&mut self, amount: u64){ 59 | if self.enabled { 60 | self.mtime += amount; 61 | } 62 | } 63 | 64 | pub fn set_timer(&mut self, amount: u64){ 65 | self.enabled = true; 66 | self.mtimecmp = amount; 67 | self.mtime = 0; 68 | } 69 | } 70 | 71 | lazy_static::lazy_static! { 72 | pub static ref TIMERS: spin::Mutex = spin::Mutex::new(VmTimers::new()); 73 | } 74 | 75 | pub struct TimerHandle { 76 | guest_number: u64, 77 | } 78 | 79 | impl TimerHandle { 80 | pub fn new(guest_number: u64) -> Self { 81 | Self { guest_number } 82 | } 83 | } 84 | 85 | impl rustsbi::Timer for TimerHandle { 86 | #[inline] 87 | fn set_timer(&self, stime_value: u64) { 88 | let mut timer = TIMERS.lock(); 89 | timer.timers[self.guest_number as usize].set_timer(stime_value); 90 | crate::riscv::csr::hvip::clear_timing_interrupt(); 91 | log::info!("Setting timer mtimecmp {} for guest {}", stime_value, self.guest_number); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /hypervisor/src/uart.rs: -------------------------------------------------------------------------------- 1 | use crate::memlayout; 2 | use core::fmt::{Error, Write}; 3 | 4 | pub struct Uart { 5 | addr_base: *mut u8, 6 | } 7 | 8 | impl Write for Uart { 9 | fn write_str(&mut self, out: &str) -> Result<(), Error> { 10 | for c in out.bytes() { 11 | self.put(c); 12 | } 13 | Ok(()) 14 | } 15 | } 16 | 17 | impl Uart { 18 | pub fn new(uart_base: usize) -> Self { 19 | let ptr = uart_base as *mut u8; 20 | Uart { addr_base: ptr } 21 | } 22 | 23 | fn thr(&mut self) -> *mut u8 { 24 | unsafe { self.addr_base.offset(0) } 25 | } 26 | 27 | fn rbr(&mut self) -> *mut u8 { 28 | unsafe { self.addr_base.offset(0) } 29 | } 30 | 31 | fn ier(&mut self) -> *mut u8 { 32 | unsafe { self.addr_base.offset(1) } 33 | } 34 | 35 | fn fcr(&mut self) -> *mut u8 { 36 | unsafe { self.addr_base.offset(2) } 37 | } 38 | 39 | fn lcr(&mut self) -> *mut u8 { 40 | unsafe { self.addr_base.offset(3) } 41 | } 42 | 43 | fn lsr(&mut self) -> *mut u8 { 44 | unsafe { self.addr_base.offset(5) } 45 | } 46 | 47 | pub fn init(&mut self) { 48 | unsafe { 49 | // enable interrupts 50 | self.ier().write_volatile(1 << 0); 51 | 52 | // enable FIFO 53 | self.fcr().write_volatile(1 << 0); 54 | 55 | // set WLS to 8 bits 56 | self.lcr().write_volatile((1 << 0) | (1 << 1)); 57 | } 58 | } 59 | 60 | pub fn put(&mut self, c: u8) { 61 | unsafe { 62 | // spin until bit 5 of LSR holds 63 | while self.lsr().read_volatile() & (1 << 5) == 0 {} 64 | 65 | // add `c` to the FIFO 66 | self.thr().write_volatile(c); 67 | } 68 | } 69 | 70 | pub fn get(&mut self) -> Option { 71 | unsafe { 72 | // read LSR first in order to check whether read FIFO has any data or not 73 | if self.lsr().read_volatile() & (1 << 0) == 0 { 74 | None 75 | } else { 76 | Some(self.rbr().offset(0).read_volatile()) 77 | } 78 | } 79 | } 80 | } 81 | 82 | #[macro_export] 83 | macro_rules! print 84 | { 85 | ($($args:tt)+) => ({ 86 | use core::fmt::Write; 87 | let _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+); 88 | }); 89 | } 90 | 91 | #[macro_export] 92 | macro_rules! println 93 | { 94 | () => ({ 95 | print!("\r\n") 96 | }); 97 | ($fmt:expr) => ({ 98 | print!(concat!($fmt, "\r\n")) 99 | }); 100 | ($fmt:expr, $($args:tt)+) => ({ 101 | print!(concat!($fmt, "\r\n"), $($args)+) 102 | }); 103 | } 104 | 105 | pub fn handle_interrupt() { 106 | let mut uart = Uart::new(memlayout::UART_BASE); 107 | if let Some(c) = uart.get() { 108 | // TODO (enhancement): pass it buffer or somewhere else 109 | 110 | // echo back 111 | match c { 112 | 8 => { 113 | println!("{} {}", 8 as char, 8 as char); 114 | } 115 | 10 | 13 => { 116 | println!(); 117 | } 118 | _ => { 119 | print!("{}", c as char); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /hypervisor/src/util.rs: -------------------------------------------------------------------------------- 1 | pub mod jump; 2 | pub mod logger; 3 | -------------------------------------------------------------------------------- /hypervisor/src/util/jump.rs: -------------------------------------------------------------------------------- 1 | pub trait Target { 2 | fn convert_to_fn_address(&self) -> usize; 3 | } 4 | 5 | impl Target for usize { 6 | fn convert_to_fn_address(&self) -> usize { 7 | return *self; 8 | } 9 | } 10 | 11 | impl Target for fn() { 12 | fn convert_to_fn_address(&self) -> usize { 13 | return (*self) as *const () as usize; 14 | } 15 | } 16 | 17 | impl Target for unsafe extern "C" fn() { 18 | fn convert_to_fn_address(&self) -> usize { 19 | return (*self) as *const () as usize; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /hypervisor/src/util/logger.rs: -------------------------------------------------------------------------------- 1 | struct Logger; 2 | impl log::Log for Logger { 3 | fn enabled(&self, metadata: &log::Metadata) -> bool { 4 | metadata.level() <= log::Level::Info 5 | } 6 | 7 | fn log(&self, record: &log::Record) { 8 | if self.enabled(record.metadata()) { 9 | println!("[{}] {}", record.level(), record.args()); 10 | } 11 | } 12 | 13 | fn flush(&self) {} 14 | } 15 | 16 | static LOGGER: Logger = Logger; 17 | pub fn init() -> Result<(), log::SetLoggerError> { 18 | log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Debug)) 19 | } 20 | -------------------------------------------------------------------------------- /hypervisor/src/virtio.rs: -------------------------------------------------------------------------------- 1 | use crate::memlayout; 2 | use crate::paging; 3 | use crate::riscv; 4 | use core::mem::size_of; 5 | 6 | pub const SECTOR_SIZE: usize = 512; 7 | 8 | const VIRTIO_DESC_F_NEXT: u16 = 1 << 0; 9 | const VIRTIO_DESC_F_WRITE: u16 = 1 << 1; 10 | 11 | const VIRTIO_MAGIC: u32 = 0x74_72_69_76; 12 | const VIRTIO_VENDOR: u32 = 0x55_4d_45_51; 13 | 14 | pub const VIRTIO_BLK_F_RO: u32 = 1 << 5; 15 | pub const VIRTIO_BLK_F_SCSI: u32 = 1 << 7; 16 | pub const VIRTIO_BLK_F_CONFIG_WCE: u32 = 1 << 11; 17 | pub const VIRTIO_BLK_F_MQ: u32 = 1 << 12; 18 | pub const VIRTIO_F_ANY_LAYOUT: u32 = 1 << 27; 19 | pub const VIRTIO_RING_F_INDIRECT_DESC: u32 = 1 << 28; 20 | pub const VIRTIO_RING_F_EVENT_IDX: u32 = 1 << 29; 21 | 22 | const VIRTIO_RING_SIZE: usize = 8; 23 | 24 | #[repr(usize)] 25 | #[derive(Copy, Clone)] 26 | enum Offset { 27 | MagicValue = 0x000, 28 | Version = 0x004, 29 | DeviceId = 0x008, 30 | VendorId = 0x00c, 31 | HostFeatures = 0x010, 32 | HostFeaturesSel = 0x014, 33 | GuestFeatures = 0x020, 34 | GuestFeaturesSel = 0x024, 35 | GuestPageSize = 0x028, 36 | QueueSel = 0x030, 37 | QueueNumMax = 0x034, 38 | QueueNum = 0x038, 39 | QueueAlign = 0x03c, 40 | QueuePfn = 0x040, 41 | QueueNotify = 0x050, 42 | InterruptStatus = 0x060, 43 | InterruptAck = 0x064, 44 | Status = 0x070, 45 | Config = 0x100, 46 | } 47 | 48 | impl Offset { 49 | fn apply(&self, base: &*mut u32) -> *mut u32 { 50 | unsafe { base.offset((*self as isize) / 4) } 51 | } 52 | } 53 | 54 | enum StatusFlag { 55 | Acknowledge = 1, 56 | Driver = 2, 57 | Failed = 128, 58 | FeaturesOk = 8, 59 | DriverOk = 4, 60 | DeviceNeedsReset = 64, 61 | } 62 | 63 | #[repr(C)] 64 | pub struct Descriptor { 65 | addr: u64, 66 | len: u32, 67 | flags: u16, 68 | next: u16, 69 | } 70 | 71 | #[repr(C)] 72 | pub struct Available { 73 | flags: u16, 74 | idx: u16, 75 | ring: [u16; VIRTIO_RING_SIZE], 76 | event: u16, 77 | } 78 | 79 | #[repr(C)] 80 | pub struct UsedElem { 81 | id: u32, 82 | len: u32, 83 | } 84 | 85 | #[repr(C)] 86 | pub struct Used { 87 | flags: u16, 88 | idx: u16, 89 | ring: [UsedElem; VIRTIO_RING_SIZE], 90 | event: u16, 91 | } 92 | 93 | #[repr(C)] 94 | pub struct VInfo { 95 | pub data: *mut u8, 96 | pub status: u8, 97 | } 98 | 99 | #[repr(C)] 100 | pub struct BlkOuthdr { 101 | pub typ: u32, 102 | pub reserved: u32, 103 | pub sector: u64, 104 | } 105 | 106 | #[repr(C)] 107 | pub struct Queue { 108 | pub desc: [Descriptor; VIRTIO_RING_SIZE], 109 | pub avail: Available, 110 | pub padding0: [u8; (memlayout::PAGE_SIZE as usize) 111 | - size_of::() * VIRTIO_RING_SIZE 112 | - size_of::()], 113 | pub used: Used, 114 | 115 | pub used_idx: u16, 116 | pub vinfo: [VInfo; VIRTIO_RING_SIZE], 117 | pub header: [BlkOuthdr; VIRTIO_RING_SIZE], 118 | 119 | pub notify_slot: [bool; VIRTIO_RING_SIZE], 120 | pub device_base_addr: *mut u32, 121 | } 122 | 123 | pub static mut QUEUE: Option<*mut Queue> = None; 124 | static mut INITIALIZED: bool = false; 125 | 126 | impl Queue { 127 | pub fn from_page(p: paging::Page) -> *mut Queue { 128 | unsafe { 129 | let queue = p.address().to_usize() as *mut Queue; 130 | (*queue).used_idx = 0; 131 | queue 132 | } 133 | } 134 | 135 | fn request(&mut self, sector: u64, buf_addr: *const (), is_write: bool) -> usize { 136 | unsafe { 137 | // TODO: select unused descriptors 138 | let idx = [0, 1, 2]; 139 | 140 | self.header[idx[0] as usize].typ = if is_write { 1 } else { 0 }; 141 | self.header[idx[0] as usize].reserved = 0; 142 | self.header[idx[0] as usize].sector = sector; 143 | 144 | self.desc[idx[0]].addr = &self.header[idx[0] as usize] as *const BlkOuthdr as u64; 145 | self.desc[idx[0]].len = size_of::() as u32; 146 | self.desc[idx[0]].flags = VIRTIO_DESC_F_NEXT; 147 | self.desc[idx[0]].next = idx[1] as u16; 148 | self.desc[idx[1]].addr = buf_addr as u64; 149 | self.desc[idx[1]].len = SECTOR_SIZE as u32; 150 | self.desc[idx[1]].flags = 151 | VIRTIO_DESC_F_NEXT | (if !is_write { VIRTIO_DESC_F_WRITE } else { 0 }); 152 | self.desc[idx[1]].next = idx[2] as u16; 153 | 154 | self.vinfo[idx[0] as usize].status = 0xff; 155 | self.desc[idx[2]].addr = &self.vinfo[idx[0] as usize].status as *const u8 as u64; 156 | self.desc[idx[2]].len = 1; 157 | self.desc[idx[2]].flags = VIRTIO_DESC_F_WRITE; 158 | self.desc[idx[2]].next = 0 as u16; 159 | 160 | self.avail.ring[self.avail.idx as usize % VIRTIO_RING_SIZE] = idx[0] as u16; 161 | self.avail.idx += 1; 162 | self.notify_slot[idx[0]] = false; 163 | Offset::QueueNotify 164 | .apply(&self.device_base_addr) 165 | .write_volatile(0); 166 | idx[0] 167 | } 168 | } 169 | 170 | pub fn read(&mut self, sector: u64, buf_addr: *const ()) { 171 | let idx = self.request(sector, buf_addr, false); 172 | log::debug!("request was sent. watching id: {}", idx); 173 | 174 | // TODO (enhancement): this spin lock is too heavy; we can do better 175 | unsafe { while !(&self.notify_slot[idx] as *const bool).read_volatile() {} } 176 | 177 | log::debug!("request was handled: {}", idx); 178 | } 179 | 180 | pub fn write(&mut self, sector: u64, buf_addr: *const ()) { 181 | let idx = self.request(sector, buf_addr, true); 182 | log::debug!("request was sent. watching id: {}", idx); 183 | 184 | // TODO (enhancement): this spin lock is too heavy; we can do better 185 | unsafe { while !(&self.notify_slot[idx] as *const bool).read_volatile() {} } 186 | 187 | log::debug!("request was handled: {}", idx); 188 | } 189 | 190 | pub fn mark_finished(&mut self, id: usize) { 191 | self.notify_slot[id] = true; 192 | } 193 | } 194 | 195 | pub fn init() { 196 | log::info!("virtio0 addr: 0x{:016x}", memlayout::VIRTIO0_BASE); 197 | let base = memlayout::VIRTIO0_BASE as *mut u32; 198 | assert_device_status(&base); 199 | assert_device_type(&base, 2); 200 | log::info!("a block device found"); 201 | 202 | let queue_page = paging::alloc_continuous(2); 203 | log::info!( 204 | "-> allocated query object: 0x{:016x}", 205 | queue_page.address().to_usize() 206 | ); 207 | 208 | let queue = Queue::from_page(queue_page); 209 | unsafe { 210 | // TODO (enhancement): support multi core 211 | (*queue).device_base_addr = base; 212 | QUEUE = Some(queue); 213 | INITIALIZED = true; 214 | } 215 | init_block_device(&base, queue); 216 | } 217 | 218 | fn init_block_device(base: &*mut u32, queue: *mut Queue) { 219 | let mut status: u32 = 0; 220 | 221 | unsafe { 222 | let status_addr = Offset::Status.apply(base); 223 | 224 | // start to config 225 | status |= StatusFlag::Acknowledge as u32; 226 | status_addr.write_volatile(status); 227 | 228 | status |= StatusFlag::Driver as u32; 229 | status_addr.write_volatile(status); 230 | 231 | // set features 232 | let mut features: u32 = Offset::HostFeatures.apply(base).read_volatile(); 233 | features &= !(VIRTIO_BLK_F_RO as u32); 234 | features &= !(VIRTIO_BLK_F_SCSI as u32); 235 | features &= !(VIRTIO_BLK_F_CONFIG_WCE as u32); 236 | features &= !(VIRTIO_BLK_F_MQ as u32); 237 | features &= !(VIRTIO_F_ANY_LAYOUT as u32); 238 | features &= !(VIRTIO_RING_F_EVENT_IDX as u32); 239 | features &= !(VIRTIO_RING_F_INDIRECT_DESC as u32); 240 | Offset::HostFeatures.apply(base).write_volatile(features); 241 | 242 | // finish feature configuration 243 | status |= StatusFlag::FeaturesOk as u32; 244 | status_addr.write_volatile(status); 245 | 246 | // finish configuration 247 | status |= StatusFlag::DriverOk as u32; 248 | status_addr.write_volatile(status); 249 | 250 | // tell our page size to virtio 251 | Offset::GuestPageSize 252 | .apply(base) 253 | .write_volatile(memlayout::PAGE_SIZE as u32); 254 | 255 | // set first queue selector 256 | Offset::QueueSel.apply(base).write_volatile(0); 257 | 258 | // set our queue num 259 | let queue_num = VIRTIO_RING_SIZE as u32; 260 | let queue_max = Offset::QueueNumMax.apply(base).read_volatile(); 261 | if queue_num > queue_max { 262 | panic!("virtio disk has invalid queue max setting"); 263 | } 264 | Offset::QueueNum.apply(base).write_volatile(queue_num); 265 | 266 | // set our queue addr 267 | Offset::QueuePfn 268 | .apply(base) 269 | .write_volatile(((queue as usize) >> 12) as u32); 270 | } 271 | } 272 | 273 | fn assert_device_status(base: &*mut u32) { 274 | unsafe { 275 | let magic = base.read_volatile(); 276 | if magic != VIRTIO_MAGIC { 277 | panic!("invalid magic number found: {}", magic); 278 | } 279 | let version = base.offset(1).read_volatile(); 280 | if version != 1 { 281 | panic!("invalid version: {}", version); 282 | } 283 | 284 | let vendor_id = base.offset(3).read_volatile(); 285 | if vendor_id != VIRTIO_VENDOR { 286 | panic!("invalid vendor id: {}", vendor_id); 287 | } 288 | } 289 | } 290 | 291 | fn assert_device_type(base: &*mut u32, t: u32) { 292 | unsafe { 293 | let device_id = base.offset(2).read_volatile(); 294 | if device_id != t { 295 | panic!("invalid device id: {} (expected: {})", device_id, t); 296 | } 297 | } 298 | } 299 | 300 | pub fn handle_interrupt(interrupt: u32) { 301 | let device_id = interrupt as usize - 1; 302 | if device_id == 0 { 303 | unsafe { 304 | // TODO (enhancement): notify related contes here 305 | if let Some(_queue) = QUEUE { 306 | while ((*_queue).used_idx as usize % VIRTIO_RING_SIZE) 307 | != ((*_queue).used.idx as usize % VIRTIO_RING_SIZE) 308 | { 309 | let used_elem = 310 | &(*_queue).used.ring[(*_queue).used_idx as usize % VIRTIO_RING_SIZE]; 311 | let finished_id = used_elem.id; 312 | log::debug!("used_elem: id={}, len={}", used_elem.id, used_elem.len); 313 | (*_queue).mark_finished(finished_id as usize); 314 | (*_queue).used_idx += 1; 315 | } 316 | } else { 317 | panic!("virtio queue uninitialized") 318 | } 319 | } 320 | } else { 321 | panic!("invalid interrupt from virtio0: {}", interrupt); 322 | } 323 | } 324 | --------------------------------------------------------------------------------