├── .gitignore ├── .vscode └── settings.json ├── Makefile ├── README.md ├── common ├── Cargo.lock ├── Cargo.toml └── src │ ├── constants.rs │ └── lib.rs ├── loader ├── .cargo │ └── config.toml ├── Cargo.lock ├── Cargo.toml ├── rust-toolchain.toml └── src │ ├── main.rs │ └── paging.rs ├── memo.md └── vmm ├── .cargo └── config ├── Cargo.lock ├── Cargo.toml ├── htvmm.json ├── rust-toolchain.toml └── src ├── allocator.rs ├── arch ├── amd │ ├── mod.rs │ └── svm.rs ├── intel │ ├── ept.rs │ ├── mod.rs │ ├── vmcs.rs │ ├── vmexit_handlers.rs │ └── vmx.rs └── mod.rs ├── cpu.rs ├── emu.rs ├── entry.s ├── htvmm.lds ├── ioapic.rs ├── lib.rs └── serial.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.map 3 | image/ 4 | tools/ 5 | 6 | *.elf* 7 | .gdb_history 8 | disk.iso 9 | *.log -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false 3 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := bash 2 | .RECIPEPREFIX = > 3 | .ONESHELL: 4 | MAKEFLAGS += --no-builtin-rules --no-builtin-variables 5 | 6 | LD = ld.lld 7 | LDFLAGS = --no-nmagic --gc-sections --Map=vmm/htvmm.map -nostdlib --script=vmm/src/htvmm.lds 8 | 9 | OVMFBASE=../edk2/Build/OvmfX64/DEBUG_GCC5/FV/ 10 | #OVMFBASE=tools/ovmf/ 11 | OVMFCODE=$(OVMFBASE)/OVMF_CODE.fd 12 | OVMFVARS=$(OVMFBASE)/OVMF_VARS.fd 13 | #OVMFCODE=$(OVMFBASE)/OVMF_CODE_4M.fd 14 | #OVMFVARS=$(OVMFBASE)/OVMF_VARS_4M.fd 15 | 16 | export RELEASE ?= 17 | build_mode := $(if $(RELEASE),release,debug) 18 | 19 | features := 20 | 21 | export GPD ?= 22 | ifeq ($(GPD),1) 23 | features +=gpd 24 | endif 25 | 26 | export RUSTFLAGS = -Z emit-stack-sizes 27 | CARGOFLAGS += $(if $(RELEASE),--release,) 28 | 29 | export QEMU ?= qemu-system-x86_64 30 | QEMUFLAGS := -s -m 8G \ 31 | -drive if=pflash,format=raw,readonly,file=$(OVMFCODE) \ 32 | -drive if=pflash,format=raw,file=$(OVMFVARS) \ 33 | -drive if=ide,file=fat:rw:image,index=0,media=disk \ 34 | -drive format=raw,index=1,file=disk.iso \ 35 | -enable-kvm -cpu host,+vmx \ 36 | -debugcon file:ovmf.debug.log -global isa-debugcon.iobase=0x402 \ 37 | -serial stdio \ 38 | -monitor telnet::4444,server,nowait 39 | 40 | .PHONY: default 41 | default: build 42 | 43 | VMM_OBJ = vmm/target/htvmm/$(build_mode)/libhtvmm.a 44 | $(VMM_OBJ): .FORCE 45 | > cd vmm; cargo build $(CARGOFLAGS) --features "$(features)" 46 | 47 | .PHONY: build-vmm 48 | build-vmm: $(VMM_OBJ) 49 | > $(LD) $(LDFLAGS) -o vmm/htvmm.elf.$(build_mode) $(VMM_OBJ) 50 | 51 | .PHONY: build-loader 52 | build-loader: 53 | > cd loader; cargo build $(CARGOFLAGS) 54 | 55 | .PHONY: build 56 | build: build-loader build-vmm 57 | 58 | .PHONY: clean-vmm 59 | clean-vmm: 60 | > cd vmm; cargo clean 61 | 62 | .PHONY: clean-loader 63 | clean-loader: 64 | > cd loader; cargo clean 65 | 66 | .PHONY: clean 67 | clean: clean-loader clean-vmm 68 | > rm -f vmm/htvmm.elf.* image/htvmm.elf image/EFI/BOOT/BOOTX64.EFI 69 | 70 | .PHONY: run 71 | run: 72 | > rm -f image/htvmm.elf image/EFI/BOOT/BOOTX64.EFI 73 | > cp vmm/htvmm.elf.$(build_mode) image/htvmm.elf 74 | > cp loader/target/x86_64-unknown-uefi/$(build_mode)/htloader.efi image/EFI/BOOT/BOOTX64.EFI 75 | > $(QEMU) $(QEMUFLAGS) 76 | 77 | .PHONY: disk 78 | disk: 79 | > rm -f disk.iso && \ 80 | grub-mkrescue -o disk.iso /boot && \ 81 | chmod 666 disk.iso 82 | 83 | .PHONY: init 84 | init: 85 | > mkdir -p tools/ovmf && \ 86 | cp `find /usr -type f -name "OVMF_CODE.fd" 2> /dev/null | head -n 1` tools/ovmf && \ 87 | cp `find /usr -type f -name "OVMF_VARS.fd" 2> /dev/null | head -n 1` tools/ovmf 88 | > mkdir -p image/EFI/BOOT 89 | 90 | .PHONY: install 91 | install: 92 | > mkdir -p /boot/efi/EFI/htvmm 93 | > cp vmm/htvmm.elf.$(build_mode) /boot/efi/htvmm.elf 94 | > cp loader/target/x86_64-unknown-uefi/$(build_mode)/htloader.efi /boot/efi/EFI/htvmm/htloader.efi 95 | 96 | .PHONY: bootnext 97 | bootnext: 98 | > efibootmgr -n `efibootmgr | grep htvmm | cut -c 5-8` 99 | 100 | .PHONY: trace 101 | trace: 102 | > echo 1 | sudo tee /sys/kernel/tracing/events/kvm/kvm_nested_vmenter_failed/enable 103 | > echo 1 | sudo tee /sys/kernel/tracing/events/kvm/kvm_nested_vmexit/enable 104 | > echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on 105 | > sudo watch tail /sys/kernel/debug/tracing/trace 106 | 107 | .PHONY: clippy 108 | clippy: 109 | > (cd vmm; cargo clippy) 110 | > (cd loader; cargo clippy) 111 | 112 | .FORCE: 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTVMM 2 | 3 | A toy hypervisor written in Rust. 4 | 5 | ## init 6 | 7 | ``` 8 | sudo apt install ovmf qemu-system-x86 9 | make init 10 | ``` 11 | 12 | ## build 13 | 14 | ### loader 15 | 16 | ``` 17 | make build-loader 18 | ``` 19 | 20 | ### vmm 21 | 22 | ``` 23 | make build-vmm 24 | ``` 25 | 26 | ## release build 27 | 28 | Please add shell variable `RELEASE=1` to make command. 29 | 30 | ``` 31 | RELEASE=1 make build 32 | ``` 33 | 34 | ## run in QEMU 35 | 36 | ``` 37 | make run 38 | ``` 39 | 40 | If you want to debug, use GDB. 41 | 42 | ``` 43 | $ gdb 44 | (gdb) target remote :1234 45 | ``` 46 | -------------------------------------------------------------------------------- /common/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 = "bit_field" 7 | version = "0.10.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.3.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 16 | 17 | [[package]] 18 | name = "common" 19 | version = "0.1.0" 20 | dependencies = [ 21 | "x86_64", 22 | ] 23 | 24 | [[package]] 25 | name = "rustversion" 26 | version = "1.0.9" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" 29 | 30 | [[package]] 31 | name = "volatile" 32 | version = "0.4.5" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "e3ca98349dda8a60ae74e04fd90c7fb4d6a4fbe01e6d3be095478aa0b76f6c0c" 35 | 36 | [[package]] 37 | name = "x86_64" 38 | version = "0.14.10" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69" 41 | dependencies = [ 42 | "bit_field", 43 | "bitflags", 44 | "rustversion", 45 | "volatile", 46 | ] 47 | -------------------------------------------------------------------------------- /common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | x86_64 = "0.14.10" 10 | -------------------------------------------------------------------------------- /common/src/constants.rs: -------------------------------------------------------------------------------- 1 | // MSR 2 | pub const MSR_IA32_SYSENTER_CS: u32 = 0x0000_0174; 3 | pub const MSR_IA32_SYSENTER_ESP: u32 = 0x0000_0175; 4 | pub const MSR_IA32_SYSENTER_EIP: u32 = 0x0000_0176; 5 | 6 | pub const MSR_IA32_CR_PAT: u32 = 0x0000_0277; 7 | 8 | pub const MSR_IA32_VMX_BASIC: u32 = 0x0000_0480; 9 | pub const MSR_IA32_VMX_PINBASED_CTLS: u32 = 0x0000_0481; 10 | pub const MSR_IA32_VMX_PROCBASED_CTLS: u32 = 0x0000_0482; 11 | pub const MSR_IA32_VMX_EXIT_CTLS: u32 = 0x0000_0483; 12 | pub const MSR_IA32_VMX_ENTRY_CTLS: u32 = 0x0000_0484; 13 | pub const MSR_IA32_VMX_MISC: u32 = 0x0000_0485; 14 | pub const MSR_IA32_VMX_CR0_FIXED0: u32 = 0x0000_0486; 15 | pub const MSR_IA32_VMX_CR0_FIXED1: u32 = 0x0000_0487; 16 | pub const MSR_IA32_VMX_CR4_FIXED0: u32 = 0x0000_0488; 17 | pub const MSR_IA32_VMX_CR4_FIXED1: u32 = 0x0000_0489; 18 | pub const MSR_IA32_VMX_VMCS_ENUM: u32 = 0x0000_048a; 19 | pub const MSR_IA32_VMX_PROCBASED_CTLS2: u32 = 0x0000_048b; 20 | pub const MSR_IA32_VMX_EPT_VPID_CAP: u32 = 0x0000_048c; 21 | 22 | pub const MSR_EFER: u32 = 0xc000_0080; 23 | -------------------------------------------------------------------------------- /common/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub mod constants; 4 | 5 | use x86_64::{registers::control::Cr3Flags, PhysAddr}; 6 | 7 | pub const VMM_AREA_SIZE: u64 = 512 * 1024 * 1024; 8 | pub const VMM_AREA_HEAD_VADDR: usize = 0x1_0000_0000; 9 | pub const VMM_HEAP_HEAD_VADDR: usize = VMM_AREA_HEAD_VADDR + (128 * 1024 * 1024); 10 | pub const VMM_HEAP_SIZE: u64 = 128 * 1024 * 1024; 11 | 12 | #[derive(Debug, Clone, Copy)] 13 | #[repr(C)] 14 | pub struct BootArgs { 15 | pub uefi_cr3: PhysAddr, 16 | pub uefi_cr3_flags: Cr3Flags, 17 | pub vmm_phys_offset: i64, 18 | pub memory_size: u64, 19 | pub uefi_write_char: u64, 20 | pub uefi_output: u64, 21 | } 22 | 23 | impl BootArgs { 24 | pub const fn new() -> Self { 25 | Self { 26 | uefi_cr3: PhysAddr::new(0), 27 | uefi_cr3_flags: Cr3Flags::empty(), 28 | vmm_phys_offset: 0, 29 | memory_size: 0, 30 | uefi_write_char: 0, 31 | uefi_output: 0, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /loader/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-uefi" 3 | rustflags = ["-C", "link-arg=/debug:dwarf"] 4 | 5 | [unstable] 6 | build-std = ["core", "compiler_builtins", "alloc"] 7 | build-std-features = ["compiler-builtins-mem"] 8 | -------------------------------------------------------------------------------- /loader/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 = "bit_field" 7 | version = "0.10.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.3.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 16 | 17 | [[package]] 18 | name = "cfg-if" 19 | version = "1.0.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 22 | 23 | [[package]] 24 | name = "common" 25 | version = "0.1.0" 26 | dependencies = [ 27 | "x86_64", 28 | ] 29 | 30 | [[package]] 31 | name = "crossbeam" 32 | version = "0.8.2" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" 35 | dependencies = [ 36 | "cfg-if", 37 | "crossbeam-utils", 38 | ] 39 | 40 | [[package]] 41 | name = "crossbeam-utils" 42 | version = "0.8.14" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" 45 | dependencies = [ 46 | "cfg-if", 47 | ] 48 | 49 | [[package]] 50 | name = "goblin" 51 | version = "0.5.4" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" 54 | dependencies = [ 55 | "log", 56 | "plain", 57 | "scroll", 58 | ] 59 | 60 | [[package]] 61 | name = "htloader" 62 | version = "0.1.0" 63 | dependencies = [ 64 | "common", 65 | "crossbeam", 66 | "goblin", 67 | "uefi", 68 | "uefi-services", 69 | "x86_64", 70 | ] 71 | 72 | [[package]] 73 | name = "log" 74 | version = "0.4.17" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 77 | dependencies = [ 78 | "cfg-if", 79 | ] 80 | 81 | [[package]] 82 | name = "plain" 83 | version = "0.2.3" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 86 | 87 | [[package]] 88 | name = "proc-macro2" 89 | version = "1.0.43" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" 92 | dependencies = [ 93 | "unicode-ident", 94 | ] 95 | 96 | [[package]] 97 | name = "quote" 98 | version = "1.0.21" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 101 | dependencies = [ 102 | "proc-macro2", 103 | ] 104 | 105 | [[package]] 106 | name = "rustversion" 107 | version = "1.0.9" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" 110 | 111 | [[package]] 112 | name = "scroll" 113 | version = "0.11.0" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" 116 | dependencies = [ 117 | "scroll_derive", 118 | ] 119 | 120 | [[package]] 121 | name = "scroll_derive" 122 | version = "0.11.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" 125 | dependencies = [ 126 | "proc-macro2", 127 | "quote", 128 | "syn", 129 | ] 130 | 131 | [[package]] 132 | name = "syn" 133 | version = "1.0.99" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" 136 | dependencies = [ 137 | "proc-macro2", 138 | "quote", 139 | "unicode-ident", 140 | ] 141 | 142 | [[package]] 143 | name = "ucs2" 144 | version = "0.3.2" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "bad643914094137d475641b6bab89462505316ec2ce70907ad20102d28a79ab8" 147 | dependencies = [ 148 | "bit_field", 149 | ] 150 | 151 | [[package]] 152 | name = "uefi" 153 | version = "0.17.0" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "1681124c58206560962ed40f54603f0f8364dc4ee747b4f5adb8edf2f48a00d5" 156 | dependencies = [ 157 | "bitflags", 158 | "log", 159 | "ucs2", 160 | "uefi-macros", 161 | ] 162 | 163 | [[package]] 164 | name = "uefi-macros" 165 | version = "0.8.1" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "3c3cc7f4faa361e529a6c92683e8e5c2a8a1522879d0f65fa924197e2b268b93" 168 | dependencies = [ 169 | "proc-macro2", 170 | "quote", 171 | "syn", 172 | ] 173 | 174 | [[package]] 175 | name = "uefi-services" 176 | version = "0.14.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "d420aeaf5194ba1432470b4a316f366c16ef436f2884779eb81430d4d5fad27e" 179 | dependencies = [ 180 | "cfg-if", 181 | "log", 182 | "uefi", 183 | ] 184 | 185 | [[package]] 186 | name = "unicode-ident" 187 | version = "1.0.3" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" 190 | 191 | [[package]] 192 | name = "volatile" 193 | version = "0.4.5" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "e3ca98349dda8a60ae74e04fd90c7fb4d6a4fbe01e6d3be095478aa0b76f6c0c" 196 | 197 | [[package]] 198 | name = "x86_64" 199 | version = "0.14.10" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69" 202 | dependencies = [ 203 | "bit_field", 204 | "bitflags", 205 | "rustversion", 206 | "volatile", 207 | ] 208 | -------------------------------------------------------------------------------- /loader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "htloader" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | uefi = { version = "0.17.0", features = ["exts", "alloc"] } 8 | uefi-services = "0.14.0" 9 | goblin = { version = "0.5.4", default-features = false, features = [ 10 | "elf64", 11 | "endian_fd", 12 | "elf32", 13 | ] } 14 | x86_64 = "0.14.10" 15 | crossbeam = { version = "0.8.2", default-features = false } 16 | 17 | common = { path = "../common" } 18 | -------------------------------------------------------------------------------- /loader/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # Pinned due to https://github.com/rust-osdev/uefi-rs/issues/500. 3 | channel = "nightly-2022-09-01" 4 | components = ["rust-src"] 5 | -------------------------------------------------------------------------------- /loader/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(abi_efiapi)] 4 | 5 | mod paging; 6 | 7 | #[macro_use] 8 | extern crate alloc; 9 | 10 | use crate::paging::create_page_table; 11 | use common::{BootArgs, VMM_AREA_HEAD_VADDR, VMM_AREA_SIZE}; 12 | use core::{arch::asm, fmt::Write}; 13 | use goblin::elf; 14 | use uefi::{ 15 | data_types::Align, 16 | prelude::*, 17 | proto::{ 18 | console::text::Output, 19 | media::file::{File, FileAttribute, FileInfo, FileMode}, 20 | }, 21 | table::boot::{AllocateType, MemoryType}, 22 | CStr16, 23 | }; 24 | use uefi_services::{self, println}; 25 | use x86_64::{structures::paging::PhysFrame, PhysAddr}; 26 | 27 | const VMM_FILE_NAME: &str = "htvmm.elf"; 28 | const PAGE_SIZE: usize = 0x1000; 29 | pub const MAX_ADDRESS: usize = 0x4000_0000; 30 | pub const VMM_ENTRY_VADDR: usize = 0x1_0000_1000; 31 | 32 | #[entry] 33 | fn efi_main(image_handle: Handle, mut systab: SystemTable) -> Status { 34 | uefi_services::init(&mut systab).unwrap(); 35 | 36 | let boot_services = systab.boot_services(); 37 | 38 | let memory_size = get_memory_size(boot_services); 39 | println!("Memory size: {}GB", memory_size / (1024 * 1024 * 1024)); 40 | let uefi_write_char = Output::write_char as *const () as u64; 41 | let mut systab_clone = unsafe { systab.unsafe_clone() }; 42 | let uefi_output = systab_clone.stdout() as *mut Output as u64; 43 | 44 | let simple_fs = boot_services.get_image_file_system(image_handle); 45 | if simple_fs.is_err() { 46 | halt("[ERROR] SimpleFileSystem"); 47 | } 48 | let mut simple_fs = simple_fs.unwrap(); 49 | 50 | let volume = simple_fs.open_volume(); 51 | if volume.is_err() { 52 | halt("[ERROR] OpenVolume"); 53 | } 54 | let mut volume = volume.unwrap(); 55 | 56 | let mut file_name_buf = [0; 0x20]; 57 | let file_name = CStr16::from_str_with_buf(VMM_FILE_NAME, &mut file_name_buf).unwrap(); 58 | let vmm_file_handle = volume.open(file_name, FileMode::Read, FileAttribute::empty()); 59 | if vmm_file_handle.is_err() { 60 | halt("[ERROR] OpenFile"); 61 | } 62 | let mut vmm_file_handle = vmm_file_handle.unwrap(); 63 | 64 | let mut info_buf = vec![0; 0x1000]; 65 | let info_buf = FileInfo::align_buf(&mut info_buf); 66 | if info_buf.is_none() { 67 | halt("[ERROR] alignment file info"); 68 | } 69 | let info_buf = info_buf.unwrap(); 70 | let file_info = vmm_file_handle.get_info::(info_buf); 71 | if file_info.is_err() { 72 | halt("[ERROR] get FileInfo"); 73 | } 74 | let file_info = file_info.unwrap(); 75 | 76 | let file_size = file_info.file_size(); 77 | let vmm_page_count = VMM_AREA_SIZE as usize / PAGE_SIZE; 78 | let alloc_paddr = boot_services.allocate_pages( 79 | AllocateType::MaxAddress(MAX_ADDRESS as usize), 80 | MemoryType::UNUSABLE, 81 | vmm_page_count, 82 | ); 83 | if alloc_paddr.is_err() { 84 | halt("[ERROR] allocate_pages"); 85 | } 86 | let alloc_paddr = alloc_paddr.unwrap(); 87 | println!( 88 | "Allocate region for VMM: phys addrress = 0x{alloc_paddr:x}, count = 0x{vmm_page_count:x}" 89 | ); 90 | 91 | let vmm_regular_file = vmm_file_handle.into_regular_file(); 92 | if vmm_regular_file.is_none() { 93 | halt("[ERROR] into_regular_file"); 94 | } 95 | let mut vmm_regular_file = vmm_regular_file.unwrap(); 96 | let region = 97 | unsafe { core::slice::from_raw_parts_mut(alloc_paddr as *mut u8, file_size as usize) }; 98 | let read_res = vmm_regular_file.read(region); 99 | if read_res.is_err() { 100 | halt("[ERROR] read"); 101 | } 102 | 103 | let vmm_elf = elf::Elf::parse(region); 104 | if vmm_elf.is_err() { 105 | halt("[ERROR] parse ELF"); 106 | } 107 | let vmm_elf = vmm_elf.unwrap(); 108 | let vmm_entry_offset = vmm_elf.program_headers[0].p_offset; // FIXME!!! 109 | 110 | let (uefi_cr3, uefi_cr3_flags) = x86_64::registers::control::Cr3::read(); 111 | let uefi_cr3_u64 = uefi_cr3.start_address().as_u64(); 112 | let boot_args = BootArgs { 113 | uefi_cr3: PhysAddr::new(uefi_cr3_u64), 114 | uefi_cr3_flags, 115 | vmm_phys_offset: alloc_paddr as i64 - VMM_AREA_HEAD_VADDR as i64, 116 | memory_size, 117 | uefi_write_char, 118 | uefi_output, 119 | }; 120 | 121 | println!( 122 | "UEFI CR3: 0x{:x}", 123 | uefi_cr3.start_address().as_u64() | uefi_cr3_flags.bits() 124 | ); 125 | 126 | let entry_point = alloc_paddr + vmm_entry_offset; 127 | 128 | println!("ENTER VMM: 0x{:x}", VMM_ENTRY_VADDR); 129 | 130 | let (vmm_pml4_table, cr3_flags) = create_page_table(PhysAddr::new(entry_point), boot_services); 131 | 132 | x86_64::instructions::interrupts::disable(); 133 | unsafe { 134 | x86_64::registers::control::Cr3::write( 135 | PhysFrame::from_start_address(PhysAddr::new(vmm_pml4_table.as_u64())).unwrap(), 136 | cr3_flags, 137 | ); 138 | 139 | asm!( 140 | "push %rbp", 141 | "push %rax", 142 | "push %rbx", 143 | "push %rcx", 144 | "push %rdx", 145 | "push %rdi", 146 | "push %rsi", 147 | "push %r8", 148 | "push %r9", 149 | "push %r10", 150 | "push %r11", 151 | "push %r12", 152 | "push %r13", 153 | "push %r14", 154 | "push %r15", 155 | "call *%rax", 156 | "pop %r15", 157 | "pop %r14", 158 | "pop %r13", 159 | "pop %r12", 160 | "pop %r11", 161 | "pop %r10", 162 | "pop %r9", 163 | "pop %r8", 164 | "pop %rsi", 165 | "pop %rdi", 166 | "pop %rdx", 167 | "pop %rcx", 168 | "pop %rbx", 169 | "pop %rax", 170 | "pop %rbp", 171 | in("rdi") &boot_args as *const BootArgs, 172 | in("rax") VMM_ENTRY_VADDR, 173 | options(att_syntax) 174 | ); 175 | 176 | x86_64::registers::control::Cr3::write(uefi_cr3, uefi_cr3_flags); 177 | loop { 178 | asm!("hlt"); 179 | } 180 | } 181 | x86_64::instructions::interrupts::enable(); 182 | // unsafe { 183 | // loop { 184 | // asm!("hlt"); 185 | // } 186 | // } 187 | 188 | println!("VMM boot OK!"); 189 | 190 | Status::SUCCESS 191 | } 192 | 193 | fn halt(error_msg: &str) -> ! { 194 | println!("{error_msg}"); 195 | x86_64::instructions::interrupts::disable(); 196 | loop { 197 | x86_64::instructions::hlt(); 198 | } 199 | } 200 | 201 | fn get_memory_size(bs: &BootServices) -> u64 { 202 | let mut size = 0; 203 | loop { 204 | size += 0x100; 205 | let pool = bs.allocate_pool(MemoryType::UNUSABLE, size).unwrap(); 206 | let buf = unsafe { core::slice::from_raw_parts_mut(pool, size) }; 207 | let memmap = bs.memory_map(buf); 208 | if let Ok((_mapkey, memdesc_iter)) = memmap { 209 | let mut memory_size = 0; 210 | for memdesc in memdesc_iter { 211 | let phys_end = memdesc.phys_start + (0x1000 * memdesc.page_count); 212 | if memory_size < phys_end { 213 | memory_size = phys_end; 214 | } 215 | } 216 | bs.free_pool(pool).unwrap(); 217 | return memory_size; 218 | } else { 219 | bs.free_pool(pool).unwrap(); 220 | continue; 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /loader/src/paging.rs: -------------------------------------------------------------------------------- 1 | use crate::MAX_ADDRESS; 2 | use common::VMM_AREA_SIZE; 3 | use uefi::{ 4 | prelude::BootServices, 5 | table::boot::{AllocateType, MemoryType}, 6 | }; 7 | use x86_64::{ 8 | registers::control::{Cr3, Cr3Flags}, 9 | structures::paging::{page_table::PageTableFlags, PageTable}, 10 | PhysAddr, 11 | }; 12 | 13 | pub fn create_page_table(vmm_entry_phys: PhysAddr, bs: &BootServices) -> (PhysAddr, Cr3Flags) { 14 | let (uefi_pml4, cr3_flags) = Cr3::read(); 15 | let uefi_pml4_table = unsafe { 16 | (uefi_pml4.start_address().as_u64() as *const PageTable) 17 | .as_ref() 18 | .unwrap() 19 | }; 20 | unsafe { 21 | let vmm_pml4_table = construct_table(uefi_pml4_table, bs); 22 | modify_table(vmm_pml4_table, vmm_entry_phys, uefi_pml4_table, bs); 23 | 24 | (PhysAddr::new(vmm_pml4_table as *const _ as u64), cr3_flags) 25 | } 26 | } 27 | 28 | unsafe fn construct_table( 29 | uefi_pml4_table: &PageTable, 30 | bs: &BootServices, 31 | ) -> &'static mut PageTable { 32 | let vmm_pml4_table = bs 33 | .allocate_pages( 34 | AllocateType::MaxAddress(MAX_ADDRESS), 35 | MemoryType::UNUSABLE, 36 | 1, 37 | ) 38 | .unwrap(); 39 | let vmm_pml4_table = core::mem::transmute::(vmm_pml4_table); 40 | vmm_pml4_table.zero(); 41 | 42 | construct_table_inner(vmm_pml4_table, uefi_pml4_table, bs, 4); 43 | 44 | vmm_pml4_table 45 | } 46 | 47 | unsafe fn construct_table_inner( 48 | vmm_page_table: &mut PageTable, 49 | uefi_page_table: &PageTable, 50 | bs: &BootServices, 51 | level: usize, 52 | ) { 53 | for i in 0..512 { 54 | let uefi_table_entry = &uefi_page_table[i]; 55 | if uefi_table_entry.is_unused() { 56 | continue; 57 | } 58 | 59 | if ((level == 2 || level == 3) 60 | && uefi_table_entry.flags().contains(PageTableFlags::HUGE_PAGE)) 61 | || level == 1 62 | { 63 | vmm_page_table[i] = uefi_table_entry.clone(); 64 | continue; 65 | } 66 | 67 | let sub_vmm_page_table = bs 68 | .allocate_pages( 69 | AllocateType::MaxAddress(MAX_ADDRESS), 70 | MemoryType::UNUSABLE, 71 | 1, 72 | ) 73 | .unwrap(); 74 | let sub_vmm_page_table = core::mem::transmute::(sub_vmm_page_table); 75 | 76 | let sub_uefi_page_table = &*(uefi_table_entry.addr().as_u64() as *const PageTable); 77 | 78 | construct_table_inner(sub_vmm_page_table, sub_uefi_page_table, bs, level - 1); 79 | 80 | vmm_page_table[i].set_addr( 81 | PhysAddr::new(sub_vmm_page_table as *const PageTable as u64), 82 | uefi_table_entry.flags(), 83 | ) 84 | } 85 | } 86 | 87 | unsafe fn modify_table( 88 | vmm_pml4_table: &mut PageTable, 89 | vmm_entry_phys: PhysAddr, 90 | uefi_pml4_table: &PageTable, 91 | bs: &BootServices, 92 | ) { 93 | // create 0x1_0000_0000 ~ linear address page table 94 | let pd_table = bs 95 | .allocate_pages( 96 | AllocateType::MaxAddress(MAX_ADDRESS), 97 | MemoryType::UNUSABLE, 98 | 1, 99 | ) 100 | .unwrap(); 101 | let pd_table = core::mem::transmute::(pd_table); 102 | pd_table.zero(); 103 | 104 | let uefi_pdp_table = 105 | core::mem::transmute::(uefi_pml4_table[0].addr().as_u64()); 106 | let uefi_pd_table4 = core::mem::transmute::(uefi_pdp_table[4].addr().as_u64()); 107 | 108 | let vmm_entry_phys = vmm_entry_phys.as_u64() & !0x1f_ffff; 109 | 110 | for i in 0..512 { 111 | let pde = &mut pd_table[i]; 112 | if i < (VMM_AREA_SIZE as usize / 0x20_0000) { 113 | let addr = vmm_entry_phys + 0x20_0000 * i as u64; 114 | pde.set_addr( 115 | PhysAddr::new(addr), 116 | PageTableFlags::PRESENT 117 | | PageTableFlags::WRITABLE 118 | | PageTableFlags::HUGE_PAGE 119 | | PageTableFlags::GLOBAL, 120 | ); 121 | } else { 122 | let pd4e = uefi_pd_table4[i].clone(); 123 | *pde = pd4e; 124 | } 125 | } 126 | 127 | let vmm_pdp_table = 128 | core::mem::transmute::(vmm_pml4_table[0].addr().as_u64()); 129 | let vmm_pdpte4 = &mut vmm_pdp_table[4]; 130 | let mut vmm_pdpte4_flags = vmm_pdpte4.flags(); 131 | 132 | if vmm_pdpte4_flags.contains(PageTableFlags::HUGE_PAGE | PageTableFlags::PRESENT) { 133 | vmm_pdpte4_flags.remove(PageTableFlags::HUGE_PAGE); 134 | for i in (VMM_AREA_SIZE as usize / 0x20_0000)..512 { 135 | pd_table[i].set_addr( 136 | PhysAddr::new(0x1_0000_0000 + 0x20_0000 * i as u64), 137 | PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::HUGE_PAGE, 138 | ); 139 | } 140 | } 141 | vmm_pdpte4.set_addr( 142 | PhysAddr::new(pd_table as *mut PageTable as u64), 143 | vmm_pdpte4_flags, 144 | ); 145 | } 146 | -------------------------------------------------------------------------------- /memo.md: -------------------------------------------------------------------------------- 1 | # 開発メモ 2 | 3 | ## メモリ配置について 4 | 5 | ## ブートエントリ追加 6 | 7 | https://qiita.com/deep_tkkn/items/b218e6a1d52a4d3b5e95 8 | 9 | ``` 10 | efibootmgr --create --disk /dev/sdX --part 1 --loader \\EFI\\htvmm\\htloader.efi --label htvmm #エントリー追加 11 | efibootmgr -o 0003,0004,0000,0005,0001,0002 #OS のエントリーが優先されるように変更 12 | efibootmgr -n $(efibootmgr | grep htvmm | cut -c 5-8) #htvmm を起動したい時のみ実行 13 | ``` 14 | 15 | ## VM EntryとかVM Exitとかのモニタリング 16 | 17 | https://resea.org/docs/servers/hv.html あたりをやる 18 | 19 | ## その他資料 20 | 21 | - https://seiya.me/blog/implementing-hypervisor-on-resea -------------------------------------------------------------------------------- /vmm/.cargo/config: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std = ["core", "compiler_builtins", "alloc"] 3 | build-std-features = ["compiler-builtins-mem"] 4 | 5 | [build] 6 | target = "./htvmm.json" -------------------------------------------------------------------------------- /vmm/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 = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "autocfg" 18 | version = "1.1.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 21 | 22 | [[package]] 23 | name = "bit_field" 24 | version = "0.10.1" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" 27 | 28 | [[package]] 29 | name = "bitflags" 30 | version = "1.3.2" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 33 | 34 | [[package]] 35 | name = "cc" 36 | version = "1.0.73" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 39 | 40 | [[package]] 41 | name = "cfg-if" 42 | version = "1.0.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 45 | 46 | [[package]] 47 | name = "common" 48 | version = "0.1.0" 49 | dependencies = [ 50 | "x86_64", 51 | ] 52 | 53 | [[package]] 54 | name = "crossbeam" 55 | version = "0.8.2" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" 58 | dependencies = [ 59 | "cfg-if", 60 | "crossbeam-utils", 61 | ] 62 | 63 | [[package]] 64 | name = "crossbeam-utils" 65 | version = "0.8.12" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" 68 | dependencies = [ 69 | "cfg-if", 70 | ] 71 | 72 | [[package]] 73 | name = "getrandom" 74 | version = "0.2.8" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 77 | dependencies = [ 78 | "cfg-if", 79 | "libc", 80 | "wasi", 81 | ] 82 | 83 | [[package]] 84 | name = "hashbrown" 85 | version = "0.12.3" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 88 | dependencies = [ 89 | "ahash", 90 | ] 91 | 92 | [[package]] 93 | name = "htvmm" 94 | version = "0.1.0" 95 | dependencies = [ 96 | "bitflags", 97 | "cc", 98 | "common", 99 | "crossbeam", 100 | "iced-x86", 101 | "lazy_static", 102 | "linked_list_allocator", 103 | "x86_64", 104 | ] 105 | 106 | [[package]] 107 | name = "iced-x86" 108 | version = "1.18.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec" 111 | dependencies = [ 112 | "hashbrown", 113 | "lazy_static", 114 | ] 115 | 116 | [[package]] 117 | name = "lazy_static" 118 | version = "1.4.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 121 | dependencies = [ 122 | "spin", 123 | ] 124 | 125 | [[package]] 126 | name = "libc" 127 | version = "0.2.139" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 130 | 131 | [[package]] 132 | name = "linked_list_allocator" 133 | version = "0.10.4" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "e322f259d225fbae43a1b053b2dc6a5968a6bdf8b205f5de684dab485b95030e" 136 | dependencies = [ 137 | "spinning_top", 138 | ] 139 | 140 | [[package]] 141 | name = "lock_api" 142 | version = "0.4.9" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 145 | dependencies = [ 146 | "autocfg", 147 | "scopeguard", 148 | ] 149 | 150 | [[package]] 151 | name = "once_cell" 152 | version = "1.16.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 155 | 156 | [[package]] 157 | name = "rustversion" 158 | version = "1.0.9" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" 161 | 162 | [[package]] 163 | name = "scopeguard" 164 | version = "1.1.0" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 167 | 168 | [[package]] 169 | name = "spin" 170 | version = "0.5.2" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 173 | 174 | [[package]] 175 | name = "spinning_top" 176 | version = "0.2.4" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" 179 | dependencies = [ 180 | "lock_api", 181 | ] 182 | 183 | [[package]] 184 | name = "version_check" 185 | version = "0.9.4" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 188 | 189 | [[package]] 190 | name = "volatile" 191 | version = "0.4.5" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "e3ca98349dda8a60ae74e04fd90c7fb4d6a4fbe01e6d3be095478aa0b76f6c0c" 194 | 195 | [[package]] 196 | name = "wasi" 197 | version = "0.11.0+wasi-snapshot-preview1" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 200 | 201 | [[package]] 202 | name = "x86_64" 203 | version = "0.14.10" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69" 206 | dependencies = [ 207 | "bit_field", 208 | "bitflags", 209 | "rustversion", 210 | "volatile", 211 | ] 212 | -------------------------------------------------------------------------------- /vmm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "htvmm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | crossbeam = { version = "0.8.2", default-features = false } 8 | x86_64 = "0.14.10" 9 | linked_list_allocator = "0.10.4" 10 | bitflags = "1.3.2" 11 | lazy_static = { version = "1.4.0", default-features = false, features = [ 12 | "spin_no_std", 13 | ] } 14 | iced-x86 = { version = "1.18.0", default-features = false, features = [ 15 | "no_std", 16 | "decoder", 17 | "gas", 18 | ] } 19 | 20 | common = { path = "../common" } 21 | 22 | [features] 23 | gpd = [] 24 | 25 | [lib] 26 | crate-type = ["staticlib"] 27 | 28 | [build-dependencies] 29 | cc = "1.0.73" 30 | -------------------------------------------------------------------------------- /vmm/htvmm.json: -------------------------------------------------------------------------------- 1 | { 2 | "llvm-target": "x86_64-unknown-none", 3 | "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", 4 | "target-endian": "little", 5 | "target-pointer-width": "64", 6 | "target-c-int-width": "32", 7 | "arch": "x86_64", 8 | "relocation-model": "pic", 9 | "os": "none", 10 | "code-model": "small", 11 | "features": "-mmx,-sse,+soft-float", 12 | "panic-strategy": "abort", 13 | "disable-redzone": true, 14 | "frame-pointer": "always" 15 | } -------------------------------------------------------------------------------- /vmm/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # Pinned due to https://github.com/rust-osdev/uefi-rs/issues/500. 3 | channel = "nightly-2022-09-01" 4 | components = ["rust-src"] 5 | -------------------------------------------------------------------------------- /vmm/src/allocator.rs: -------------------------------------------------------------------------------- 1 | use linked_list_allocator::LockedHeap; 2 | 3 | #[global_allocator] 4 | static ALLOCATOR: LockedHeap = LockedHeap::empty(); 5 | 6 | pub unsafe fn init(heap: usize, len: usize) { 7 | ALLOCATOR.lock().init(heap as *mut u8, len); 8 | } 9 | -------------------------------------------------------------------------------- /vmm/src/arch/amd/mod.rs: -------------------------------------------------------------------------------- 1 | mod svm; 2 | -------------------------------------------------------------------------------- /vmm/src/arch/amd/svm.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Totsugekitai/htvmm/24ac3c4dc1eb99fa0a92d20c21bf74635430fd1a/vmm/src/arch/amd/svm.rs -------------------------------------------------------------------------------- /vmm/src/arch/intel/ept.rs: -------------------------------------------------------------------------------- 1 | use crate::BOOT_ARGS; 2 | use alloc::alloc::alloc; 3 | use bitflags::bitflags; 4 | use core::{ 5 | alloc::Layout, 6 | ops::{Index, IndexMut}, 7 | slice, 8 | }; 9 | use x86_64::PhysAddr; 10 | 11 | bitflags! { 12 | pub struct EptPointerFlags: u64 { 13 | const MEMORY_TYPE_UNCACHEABLE = 0; 14 | const MEMORY_TYPE_WRITEBACK = 6; 15 | const PAGE_WALK_LENGTH_4 = 0b011 << 3; 16 | const ACCESSED_AND_DIRTY = 1 << 6; 17 | const SUPERVISOR_SHADOW_STACK = 1 << 7; 18 | } 19 | } 20 | 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 22 | pub struct EptPointer(u64); 23 | 24 | impl EptPointer { 25 | pub const fn new() -> Self { 26 | Self(0) 27 | } 28 | 29 | pub const fn flags(&self) -> EptPointerFlags { 30 | EptPointerFlags::from_bits_truncate(self.0) 31 | } 32 | 33 | pub fn set_flags(&mut self, flags: EptPointerFlags) { 34 | self.0 = self.addr().as_u64() | flags.bits(); 35 | } 36 | 37 | pub const fn addr(&self) -> PhysAddr { 38 | PhysAddr::new(self.0 & 0x000f_ffff_ffff_f000) 39 | } 40 | 41 | pub fn set_addr(&mut self, paddr: PhysAddr) { 42 | self.0 = paddr.as_u64() | self.flags().bits(); 43 | } 44 | 45 | pub fn as_u64(&self) -> u64 { 46 | self.0 47 | } 48 | 49 | // pub fn walk_table(&self, guest_phys: PhysAddr) -> *mut EptTableEntry { 50 | // let pml4 = self.addr().as_u64(); 51 | // } 52 | } 53 | 54 | #[derive(Debug)] 55 | pub struct EptTable(*mut u8); 56 | 57 | impl EptTable { 58 | pub fn new() -> Self { 59 | let layout = Layout::from_size_align(4096, 4096).unwrap(); 60 | let table = unsafe { alloc(layout) }; 61 | 62 | Self(table) 63 | } 64 | 65 | pub fn zero(&mut self) { 66 | let table = unsafe { slice::from_raw_parts_mut(self.0, 4096) }; 67 | table.fill(0); 68 | } 69 | 70 | pub fn as_mut_ptr(&self) -> *mut u8 { 71 | self.0 72 | } 73 | 74 | pub fn paddr(&self) -> PhysAddr { 75 | let virt = i64::try_from(self.as_mut_ptr() as u64).unwrap(); 76 | let phys = u64::try_from(virt + BOOT_ARGS.load().vmm_phys_offset).unwrap(); 77 | PhysAddr::new(phys) 78 | } 79 | } 80 | 81 | impl Index for EptTable { 82 | type Output = EptTableEntry; 83 | 84 | fn index(&self, index: usize) -> &Self::Output { 85 | let table = unsafe { slice::from_raw_parts(self.0 as *const EptTableEntry, 512) }; 86 | &table[index] 87 | } 88 | } 89 | 90 | impl IndexMut for EptTable { 91 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 92 | let table = unsafe { slice::from_raw_parts_mut(self.0 as *mut EptTableEntry, 512) }; 93 | &mut table[index] 94 | } 95 | } 96 | 97 | // #[derive(Clone, Copy, Debug)] 98 | // #[repr(C, align(4096))] 99 | // pub struct EptTable { 100 | // entries: [EptTableEntry; 512], 101 | // } 102 | 103 | // impl EptTable { 104 | // fn new() -> Self { 105 | // Self { 106 | // entries: [EptTableEntry::new(); 512], 107 | // } 108 | // } 109 | 110 | // pub fn paddr(&self) -> PhysAddr { 111 | // let virt = i64::try_from(self as *const Self as u64).unwrap(); 112 | // let phys = u64::try_from(virt + BOOT_ARGS.load().vmm_phys_offset).unwrap(); 113 | // PhysAddr::new(phys) 114 | // } 115 | // } 116 | 117 | // impl Index for EptTable { 118 | // type Output = EptTableEntry; 119 | 120 | // fn index(&self, index: usize) -> &Self::Output { 121 | // &self.entries[index] 122 | // } 123 | // } 124 | 125 | // impl IndexMut for EptTable { 126 | // fn index_mut(&mut self, index: usize) -> &mut Self::Output { 127 | // &mut self.entries[index] 128 | // } 129 | // } 130 | 131 | #[derive(Clone, Copy, Debug)] 132 | #[repr(C)] 133 | pub struct EptTableEntry(u64); 134 | 135 | impl EptTableEntry { 136 | #[allow(unused)] 137 | pub const fn new() -> Self { 138 | Self(0) 139 | } 140 | 141 | #[allow(unused)] 142 | pub const fn is_unused(&self) -> bool { 143 | self.0 == 0 144 | } 145 | 146 | #[allow(unused)] 147 | pub fn set_unused(&mut self) { 148 | self.0 = 0; 149 | } 150 | 151 | pub const fn flags(&self) -> EptTableFlags { 152 | EptTableFlags::from_bits_truncate(self.0) 153 | } 154 | 155 | pub fn set_flags(&mut self, flags: EptTableFlags) { 156 | self.0 = self.addr().as_u64() | flags.bits(); 157 | } 158 | 159 | pub const fn addr(&self) -> PhysAddr { 160 | PhysAddr::new(self.0 & 0x000f_ffff_ffff_f000) 161 | } 162 | 163 | pub fn set_addr(&mut self, paddr: PhysAddr) { 164 | self.0 = paddr.as_u64() | self.flags().bits(); 165 | } 166 | } 167 | 168 | bitflags! { 169 | pub struct EptTableFlags: u64 { 170 | const READ_ACCESS = 1 << 0; 171 | const WRITE_ACCESS = 1 << 1; 172 | const EXECUTE_ACCESS = 1 << 2; 173 | const MEMORY_TYPE_UC = 0 << 3; 174 | const MEMORY_TYPE_WC = 1 << 3; 175 | const MEMORY_TYPE_WT = 4 << 3; 176 | const MEMORY_TYPE_WP = 5 << 3; 177 | const MEMORY_TYPE_WB = 6 << 3; 178 | const IGNORE_PAT_MEMORY_TYPE = 1 << 6; 179 | const HUGE_PAGE = 1 << 7; 180 | const ACCESSED = 1 << 8; 181 | const DIRTY = 1 << 9; 182 | const EXECUTE_ACCESS_USER_MODE = 1 << 10; 183 | const VERIFY_GUEST_PAGING = 1 << 57; 184 | const PAGING_WRITE_ACCESS = 1 << 58; 185 | const SUPERVISOR_SHADOW_STACK = 1 << 60; 186 | const SUPPRESS_VE = 1 << 63; 187 | } 188 | } 189 | 190 | pub fn init_ept() -> EptPointer { 191 | let memory_size = BOOT_ARGS.load().memory_size; 192 | let memory_size_gb = memory_size / (1024 * 1024 * 1024); 193 | 194 | let mut ept_pml4 = EptTable::new(); 195 | ept_pml4.zero(); 196 | 197 | let mut ept_pdpt = EptTable::new(); 198 | ept_pdpt.zero(); 199 | 200 | ept_pml4[0].set_addr(ept_pdpt.paddr()); 201 | ept_pml4[0].set_flags( 202 | EptTableFlags::READ_ACCESS | EptTableFlags::WRITE_ACCESS | EptTableFlags::EXECUTE_ACCESS, 203 | ); 204 | 205 | for i_pdpt in 0..(memory_size_gb as usize) { 206 | let mut ept_pdt = EptTable::new(); 207 | 208 | ept_pdpt[i_pdpt].set_addr(ept_pdt.paddr()); 209 | ept_pdpt[i_pdpt].set_flags( 210 | EptTableFlags::READ_ACCESS 211 | | EptTableFlags::WRITE_ACCESS 212 | | EptTableFlags::EXECUTE_ACCESS, 213 | ); 214 | 215 | for i_pdt in 0..512 { 216 | ept_pdt[i_pdt].set_addr(PhysAddr::new( 217 | (2 * 1024 * 1024 * i_pdt as u64) + (1024 * 1024 * 1024 * i_pdpt as u64), 218 | )); 219 | ept_pdt[i_pdt].set_flags( 220 | EptTableFlags::HUGE_PAGE 221 | | EptTableFlags::READ_ACCESS 222 | | EptTableFlags::WRITE_ACCESS 223 | | EptTableFlags::EXECUTE_ACCESS 224 | | EptTableFlags::MEMORY_TYPE_WB, 225 | ); 226 | } 227 | } 228 | 229 | let mut eptp = EptPointer::new(); 230 | eptp.set_addr(ept_pml4.paddr()); 231 | eptp.set_flags(EptPointerFlags::MEMORY_TYPE_WRITEBACK | EptPointerFlags::PAGE_WALK_LENGTH_4); 232 | 233 | eptp 234 | } 235 | -------------------------------------------------------------------------------- /vmm/src/arch/intel/mod.rs: -------------------------------------------------------------------------------- 1 | mod ept; 2 | mod vmcs; 3 | mod vmexit_handlers; 4 | pub mod vmx; 5 | 6 | use crate::{ 7 | arch::intel::vmx::VmExitGeneralPurposeRegister, 8 | cpu::{Cpu, CpuError}, 9 | serial_println, 10 | }; 11 | use core::arch::asm; 12 | use crossbeam::atomic::AtomicCell; 13 | use ept::{init_ept, EptPointer}; 14 | use lazy_static::lazy_static; 15 | use vmcs::{VmcsField, VmcsRegion}; 16 | use vmx::{handle_vmexit, vmlaunch, vmxon, VmxError, VmxonRegion}; 17 | 18 | lazy_static! { 19 | pub static ref BSP: AtomicCell = AtomicCell::new(unsafe { IntelCpu::new() }); 20 | } 21 | 22 | extern "C" { 23 | static vmexit_handler: u8; 24 | } 25 | 26 | pub struct IntelCpu { 27 | vmxon_region: VmxonRegion, 28 | vmcs_region: VmcsRegion, 29 | eptp: EptPointer, 30 | } 31 | 32 | impl IntelCpu { 33 | pub unsafe fn new() -> Self { 34 | Self { 35 | vmxon_region: VmxonRegion::new(), 36 | vmcs_region: VmcsRegion::new(), 37 | eptp: EptPointer::new(), 38 | } 39 | } 40 | 41 | fn is_vmx_supported() -> bool { 42 | let cpuid = unsafe { core::arch::x86_64::__cpuid_count(1, 0) }; 43 | let vmx = cpuid.ecx & (1 << 5); 44 | 0 < vmx 45 | } 46 | 47 | fn vmxon(&mut self) -> Result<(), CpuError> { 48 | unsafe { 49 | let vmxon = vmxon(&mut self.vmxon_region); 50 | if let Err(_e) = vmxon { 51 | Err(CpuError::VmxRelated) 52 | } else { 53 | Ok(()) 54 | } 55 | } 56 | } 57 | } 58 | 59 | impl Cpu for IntelCpu { 60 | fn is_virtualization_supported(&self) -> bool { 61 | Self::is_vmx_supported() 62 | } 63 | 64 | fn enable_virtualization(&mut self) -> Result<(), CpuError> { 65 | if !self.is_virtualization_supported() { 66 | Err(CpuError::NotSupported) 67 | } else { 68 | Self::vmxon(self) 69 | } 70 | } 71 | 72 | fn disable_virtualization(&mut self) -> Result<(), CpuError> { 73 | Ok(()) 74 | } 75 | 76 | fn init_as_bsp(&mut self) { 77 | self.vmcs_region.clear(); 78 | self.vmcs_region.load(); 79 | 80 | self.eptp = init_ept(); 81 | self.vmcs_region 82 | .setup(self.eptp, unsafe { &vmexit_handler as *const u8 as u64 }); 83 | } 84 | 85 | fn run_vm(&mut self) { 86 | unsafe { 87 | if let Err(e) = vmlaunch() { 88 | match e { 89 | VmxError::InvalidPointer => panic!(), 90 | VmxError::VmInstructionError => { 91 | let error_code = self.vmcs_region.read(VmcsField::VmInstructionError); 92 | asm!("mov r15, {}; hlt", in(reg) error_code, options(readonly, nostack, preserves_flags)); 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } 99 | 100 | #[no_mangle] 101 | unsafe fn resume_vm(gpr: *mut VmExitGeneralPurposeRegister) { 102 | let bsp = BSP.as_ptr().as_ref().unwrap(); 103 | let exit_reason = bsp.vmcs_region.read(VmcsField::VmExitReason); 104 | let exit_qual = bsp.vmcs_region.read(VmcsField::ExitQualification); 105 | 106 | serial_println!("=== VMExit!!!!! ==="); 107 | 108 | handle_vmexit(exit_reason, exit_qual, gpr); 109 | 110 | serial_println!("=== VMEntry!!!! ==="); 111 | } 112 | -------------------------------------------------------------------------------- /vmm/src/arch/intel/vmcs.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | arch::intel::{ 3 | ept::EptPointer, 4 | vmx::{vmclear, vmptrld, vmread, vmwrite}, 5 | }, 6 | cpu::{Ldtr, SegmentDescriptor, Tr}, 7 | BOOT_ARGS, 8 | }; 9 | use alloc::alloc::alloc; 10 | use common::constants; 11 | use core::{alloc::Layout, ptr, slice}; 12 | use x86_64::{ 13 | instructions::tables::{sgdt, sidt}, 14 | registers::{ 15 | control::{Cr0, Cr3, Cr4, Cr4Flags}, 16 | debug::Dr7, 17 | model_specific::Msr, 18 | rflags, 19 | segmentation::{Segment, Segment64, CS, DS, ES, FS, GS, SS}, 20 | }, 21 | structures::DescriptorTablePointer, 22 | PhysAddr, VirtAddr, 23 | }; 24 | 25 | extern "C" { 26 | static entry_ret: u8; 27 | static uefi_cs: u16; 28 | static uefi_ds: u16; 29 | static uefi_es: u16; 30 | static uefi_fs: u16; 31 | static uefi_gs: u16; 32 | static uefi_ss: u16; 33 | static uefi_gdtr: u8; 34 | static uefi_idtr: u8; 35 | static uefi_ldtr: u64; 36 | static uefi_tr: u64; 37 | static uefi_rsp: u64; 38 | } 39 | 40 | #[derive(Debug)] 41 | pub struct VmcsRegion(*mut u8); 42 | 43 | unsafe impl Send for VmcsRegion {} 44 | 45 | impl VmcsRegion { 46 | pub unsafe fn new() -> Self { 47 | let layout = Layout::from_size_align(4096, 4096).unwrap(); 48 | let region = alloc(layout); 49 | let region_slice = slice::from_raw_parts_mut(region, 4096); 50 | region_slice.fill(0); 51 | 52 | let ia32_vmx_basic = Msr::new(constants::MSR_IA32_VMX_BASIC).read(); 53 | let vmcs_rev_id = (ia32_vmx_basic & 0x7fff_ffff) as u32; 54 | 55 | ptr::write_volatile(region as *mut u32, vmcs_rev_id); 56 | 57 | Self(region) 58 | } 59 | 60 | fn as_mut_ptr(&self) -> *mut u8 { 61 | self.0 62 | } 63 | 64 | pub fn paddr(&self) -> PhysAddr { 65 | let virt = i64::try_from(self.as_mut_ptr() as u64).unwrap(); 66 | let phys = u64::try_from(virt + BOOT_ARGS.load().vmm_phys_offset).unwrap(); 67 | PhysAddr::new(phys) 68 | } 69 | 70 | pub fn clear(&mut self) { 71 | unsafe { 72 | vmclear(self); 73 | } 74 | } 75 | 76 | pub fn load(&self) { 77 | unsafe { 78 | vmptrld(self); 79 | } 80 | } 81 | 82 | pub fn read(&self, field: VmcsField) -> u64 { 83 | unsafe { vmread(field) } 84 | } 85 | 86 | pub fn write(&mut self, field: VmcsField, val: u64) { 87 | unsafe { 88 | vmwrite(field, val); 89 | } 90 | } 91 | 92 | pub fn setup(&mut self, eptp: EptPointer, vmexit_host_rip: u64) { 93 | self.setup_guest_state_area(); 94 | self.setup_host_state_area(vmexit_host_rip); 95 | self.setup_vm_control_fields(eptp); 96 | } 97 | 98 | fn setup_guest_state_area(&mut self) { 99 | // 16 bit guest state fields 100 | let _cs = CS::get_reg(); 101 | let _ds = DS::get_reg(); 102 | let _es = ES::get_reg(); 103 | let _fs = FS::get_reg(); 104 | let _gs = GS::get_reg(); 105 | let _ss = SS::get_reg(); 106 | let _ldtr = Ldtr::get_reg(); 107 | let _tr = Tr::get_reg(); 108 | let cs = unsafe { uefi_cs }; 109 | let ds = unsafe { uefi_ds }; 110 | let es = unsafe { uefi_es }; 111 | let fs = unsafe { uefi_fs }; 112 | let gs = unsafe { uefi_gs }; 113 | let ss = unsafe { uefi_ss }; 114 | let ldtr = unsafe { uefi_ldtr }; 115 | let tr = unsafe { uefi_tr }; 116 | self.write(VmcsField::GuestCsSelector, cs as u64); 117 | self.write(VmcsField::GuestDsSelector, ds as u64); 118 | self.write(VmcsField::GuestEsSelector, es as u64); 119 | self.write(VmcsField::GuestFsSelector, fs as u64); 120 | self.write(VmcsField::GuestGsSelector, gs as u64); 121 | self.write(VmcsField::GuestSsSelector, ss as u64); 122 | self.write(VmcsField::GuestLdtrSelector, ldtr); 123 | self.write(VmcsField::GuestTrSelector, tr); 124 | 125 | // 32 bit guest state fields 126 | let gdtr = unsafe { 127 | let limit = *(&uefi_gdtr as *const u8 as *const u16); 128 | let base = *((&uefi_gdtr as *const u8).offset(2) as *const u64); 129 | DescriptorTablePointer { 130 | limit, 131 | base: VirtAddr::new(base), 132 | } 133 | }; 134 | let idtr = unsafe { 135 | let limit = *(&uefi_idtr as *const u8 as *const u16); 136 | let base = *((&uefi_idtr as *const u8).offset(2) as *const u64); 137 | DescriptorTablePointer { 138 | limit, 139 | base: VirtAddr::new(base), 140 | } 141 | }; 142 | let gdtr_limit = gdtr.limit; 143 | let idtr_limit = idtr.limit; 144 | let sysenter_cs = unsafe { Msr::new(constants::MSR_IA32_SYSENTER_CS).read() }; 145 | let efer = unsafe { Msr::new(constants::MSR_EFER).read() }; 146 | self.write(VmcsField::GuestCsLimit, 0xffffffff); 147 | self.write(VmcsField::GuestDsLimit, 0xffffffff); 148 | self.write(VmcsField::GuestEsLimit, 0xffffffff); 149 | self.write(VmcsField::GuestFsLimit, 0xffffffff); 150 | self.write(VmcsField::GuestGsLimit, 0xffffffff); 151 | self.write(VmcsField::GuestSsLimit, 0xffffffff); 152 | self.write(VmcsField::GuestLdtrLimit, 0xffff); 153 | self.write(VmcsField::GuestTrLimit, 0xffff); 154 | self.write(VmcsField::GuestGdtrLimit, gdtr_limit as u64); 155 | self.write(VmcsField::GuestIdtrLimit, idtr_limit as u64); 156 | self.write(VmcsField::GuestCsAccessRights, 0xa09b); 157 | self.write(VmcsField::GuestDsAccessRights, 0xc093); 158 | self.write(VmcsField::GuestEsAccessRights, 0xc093); 159 | self.write(VmcsField::GuestFsAccessRights, 0xc093); 160 | self.write(VmcsField::GuestGsAccessRights, 0xc093); 161 | self.write(VmcsField::GuestSsAccessRights, 0xc093); 162 | self.write(VmcsField::GuestLdtrAccessRights, 0x0082); 163 | self.write(VmcsField::GuestTrAccessRights, 0x008b); 164 | self.write(VmcsField::GuestInterruptibilityState, 0); 165 | self.write(VmcsField::GuestActivityState, 0); 166 | self.write(VmcsField::GuestPendingDbgExceptions, 0); 167 | self.write(VmcsField::GuestIa32SysenterCs, sysenter_cs); 168 | 169 | // 64 bit guest state fields 170 | self.write(VmcsField::VmcsLinkPointer, 0xffff_ffff_ffff_ffff_u64); 171 | self.write(VmcsField::GuestIa32Debugctl, 0u64); 172 | self.write(VmcsField::GuestIa32Efer, efer); 173 | 174 | // natural width guest state fields 175 | let cr0 = Cr0::read_raw(); 176 | let cr3_tuple = Cr3::read_raw(); 177 | let cr3 = cr3_tuple.0.start_address().as_u64() | (cr3_tuple.1 as u64); 178 | let cr4 = Cr4::read_raw(); 179 | // let ldtr_base = SegmentDescriptor::base(&ldtr); 180 | // let tr_base = SegmentDescriptor::base(&tr); 181 | let gdtr_base = gdtr.base.as_u64(); 182 | let idtr_base = idtr.base.as_u64(); 183 | let dr7 = Dr7::read_raw(); 184 | // VM(bit 17) must be 0, IF(bit 9) is 0 185 | let rflags = rflags::read_raw() & !(1 << 17 | 1 << 9); 186 | let sysenter_esp = unsafe { Msr::new(constants::MSR_IA32_SYSENTER_ESP).read() }; 187 | let sysenter_eip = unsafe { Msr::new(constants::MSR_IA32_SYSENTER_EIP).read() }; 188 | let rsp = unsafe { uefi_rsp }; 189 | let rip = unsafe { &entry_ret as *const u8 as u64 }; 190 | self.write(VmcsField::GuestCr0, cr0); 191 | self.write(VmcsField::GuestCr3, cr3); 192 | self.write(VmcsField::GuestCr4, cr4); 193 | self.write(VmcsField::GuestCsBase, 0); 194 | self.write(VmcsField::GuestDsBase, 0); 195 | self.write(VmcsField::GuestEsBase, 0); 196 | self.write(VmcsField::GuestFsBase, 0); 197 | self.write(VmcsField::GuestGsBase, 0); 198 | self.write(VmcsField::GuestSsBase, 0); 199 | self.write(VmcsField::GuestLdtrBase, 0); 200 | self.write(VmcsField::GuestTrBase, 0); 201 | self.write(VmcsField::GuestGdtrBase, gdtr_base); 202 | self.write(VmcsField::GuestIdtrBase, idtr_base); 203 | self.write(VmcsField::GuestDr7, dr7); 204 | self.write(VmcsField::GuestRsp, rsp); 205 | self.write(VmcsField::GuestRip, rip); 206 | self.write(VmcsField::GuestRflags, rflags); 207 | self.write(VmcsField::GuestSysenterEsp, sysenter_esp); 208 | self.write(VmcsField::GuestSysenterEip, sysenter_eip); 209 | } 210 | 211 | fn setup_host_state_area(&mut self, vmexit_host_rip: u64) { 212 | // 16 bit host state fields 213 | let cs = CS::get_reg(); 214 | let ds = DS::get_reg(); 215 | let es = ES::get_reg(); 216 | let fs = FS::get_reg(); 217 | let gs = GS::get_reg(); 218 | let ss = SS::get_reg(); 219 | let tr = Tr::get_reg(); 220 | self.write(VmcsField::HostCsSelector, cs.0 as u64); 221 | self.write(VmcsField::HostDsSelector, ds.0 as u64); 222 | self.write(VmcsField::HostEsSelector, es.0 as u64); 223 | self.write(VmcsField::HostFsSelector, fs.0 as u64); 224 | self.write(VmcsField::HostGsSelector, gs.0 as u64); 225 | self.write(VmcsField::HostSsSelector, ss.0 as u64); 226 | self.write(VmcsField::HostTrSelector, tr.0 as u64); 227 | 228 | // 32 bit host state fields 229 | let sysenter_cs = unsafe { Msr::new(constants::MSR_IA32_SYSENTER_CS).read() }; 230 | self.write(VmcsField::HostIa32SysenterCs, sysenter_cs); 231 | 232 | // native width host state fields 233 | let cr3_tuple = Cr3::read_raw(); 234 | let cr3 = cr3_tuple.0.start_address().as_u64() | (cr3_tuple.1 as u64); 235 | // If the “host address-space size” VM-exit control is 1, Bit 5 of the CR4 field (corresponding to CR4.PAE) is 1. 236 | let cr4 = Cr4::read() | Cr4Flags::FSGSBASE | Cr4Flags::PHYSICAL_ADDRESS_EXTENSION; 237 | unsafe { 238 | Cr4::write(cr4); 239 | } 240 | let cr4 = Cr4::read_raw(); 241 | // If bit 23 in the CR4 field (corresponding to CET) is 1, bit 16 in the CR0 field (WP) must also be 1. 242 | if (cr4 >> 23) & 1 == 1 { 243 | let cr0 = Cr0::read_raw(); 244 | unsafe { 245 | Cr0::write_raw(cr0 | (1 << 16)); 246 | } 247 | } 248 | let cr0 = Cr0::read_raw(); 249 | let gdtr = sgdt(); 250 | let idtr = sidt(); 251 | let fs_base = FS::read_base().as_u64(); 252 | let gs_base = GS::read_base().as_u64(); 253 | let tr_base = SegmentDescriptor::base(&tr); 254 | let gdtr_base = gdtr.base.as_u64(); 255 | let idtr_base = idtr.base.as_u64(); 256 | let sysenter_esp = unsafe { Msr::new(constants::MSR_IA32_SYSENTER_ESP).read() }; 257 | let sysenter_eip = unsafe { Msr::new(constants::MSR_IA32_SYSENTER_EIP).read() }; 258 | let efer = unsafe { Msr::new(constants::MSR_EFER).read() }; 259 | let pat = unsafe { Msr::new(constants::MSR_IA32_CR_PAT).read() }; 260 | let layout = Layout::from_size_align(4096, 16).unwrap(); 261 | let stack_bottom = unsafe { alloc(layout) }; 262 | let stack_top = stack_bottom as u64 + 4096 - 16; 263 | self.write(VmcsField::HostCr0, cr0); 264 | self.write(VmcsField::HostCr3, cr3); 265 | self.write(VmcsField::HostCr4, cr4); 266 | self.write(VmcsField::HostFsBase, fs_base); 267 | self.write(VmcsField::HostGsBase, gs_base); 268 | self.write(VmcsField::HostTrBase, tr_base); 269 | self.write(VmcsField::HostGdtrBase, gdtr_base); 270 | self.write(VmcsField::HostIdtrBase, idtr_base); 271 | self.write(VmcsField::HostIa32SysenterEsp, sysenter_esp); 272 | self.write(VmcsField::HostIa32SysenterEip, sysenter_eip); 273 | self.write(VmcsField::HostRsp, stack_top); 274 | self.write(VmcsField::HostRip, vmexit_host_rip); 275 | self.write(VmcsField::HostIa32Efer, efer); 276 | self.write(VmcsField::HostIa32Pat, pat); 277 | } 278 | 279 | fn setup_vm_control_fields(&mut self, eptp: EptPointer) { 280 | // 32 bit control fields 281 | let pin_based_ctls = unsafe { Msr::new(constants::MSR_IA32_VMX_PINBASED_CTLS).read() }; 282 | let pin_based_ctls_or = (pin_based_ctls & 0xffff_ffff) as u32; 283 | let pin_based_ctls_and = ((pin_based_ctls >> 32) & 0xffff_ffff) as u32; 284 | let pin_based_ctls = (pin_based_ctls_or & pin_based_ctls_and) as u64; 285 | let proc_based_ctls = unsafe { Msr::new(constants::MSR_IA32_VMX_PROCBASED_CTLS).read() }; 286 | let proc_based_ctls_or = (proc_based_ctls & 0xffff_ffff) as u32; 287 | let proc_based_ctls_and = ((proc_based_ctls >> 32) & 0xffff_ffff) as u32; 288 | let proc_based_ctls = (proc_based_ctls_or & proc_based_ctls_and) as u64; 289 | let proc_based_ctls2 = unsafe { Msr::new(constants::MSR_IA32_VMX_PROCBASED_CTLS2).read() }; 290 | let proc_based_ctls2_or = (proc_based_ctls2 & 0xffff_ffff) as u32; 291 | let proc_based_ctls2_and = ((proc_based_ctls2 >> 32) & 0xffff_ffff) as u32; 292 | let proc_based_ctls2 = (proc_based_ctls2_or & proc_based_ctls2_and) as u64; 293 | let exit_ctls = unsafe { Msr::new(constants::MSR_IA32_VMX_EXIT_CTLS).read() }; 294 | let exit_ctls_or = (exit_ctls & 0xffff_ffff) as u32; 295 | let exit_ctls_and = ((exit_ctls >> 32) & 0xffff_ffff) as u32; 296 | let exit_ctls = (exit_ctls_or & exit_ctls_and) as u64; 297 | let entry_ctls = unsafe { Msr::new(constants::MSR_IA32_VMX_ENTRY_CTLS).read() }; 298 | let entry_ctls_or = (entry_ctls & 0xffff_ffff) as u32; 299 | let entry_ctls_and = ((entry_ctls >> 32) & 0xffff_ffff) as u32; 300 | let entry_ctls = (entry_ctls_or & entry_ctls_and) as u64; 301 | let msr_bitmap_phys = unsafe { 302 | let layout = Layout::from_size_align(4096, 4096).unwrap(); 303 | let bitmap_region = alloc(layout); 304 | let bitmap_slice = core::slice::from_raw_parts_mut(bitmap_region, 4096); 305 | bitmap_slice.fill(0); 306 | let virt = i64::try_from(bitmap_region as u64).unwrap(); 307 | let phys = u64::try_from(virt + BOOT_ARGS.load().vmm_phys_offset).unwrap(); 308 | PhysAddr::new(phys) 309 | }; 310 | 311 | self.write(VmcsField::PinBasedVmExecControls, pin_based_ctls); 312 | self.write( 313 | VmcsField::ProcBasedVmExecControls, 314 | proc_based_ctls 315 | // | VMCS_PROC_BASED_VMEXEC_CTLS_HLTEXIT 316 | | VMCS_PROC_BASED_VMEXEC_CTLS_USE_MSR_BITMAPS 317 | | VMCS_PROC_BASED_VMEXEC_CTLS_ACTIVE_SECOND_CTLS, 318 | ); 319 | self.write( 320 | VmcsField::ProcBasedVmExecControls2, 321 | proc_based_ctls2, //| VMCS_PROC_BASED_VMEXEC_CTLS2_ENABLE_EPT, 322 | ); 323 | self.write(VmcsField::ExceptionBitmap, 0xffff_ffff); 324 | self.write(VmcsField::PageFaultErrorCodeMask, 0); 325 | self.write(VmcsField::PageFaultErrorCodeMatch, 0); 326 | self.write(VmcsField::Cr3TargetCount, 0); 327 | self.write( 328 | VmcsField::VmExitControls, 329 | exit_ctls | VMCS_VMEXIT_CTLS_HOST_ADDR_SPACE_SIZE, 330 | ); 331 | self.write(VmcsField::VmExitMsrStoreCount, 0); 332 | self.write(VmcsField::VmExitMsrLoadCount, 0); 333 | self.write( 334 | VmcsField::VmEntryControls, 335 | entry_ctls | VMCS_VMENTRY_CTLS_IA32E_MODE_GUEST, 336 | ); 337 | self.write(VmcsField::VmEntryMsrLoadCount, 0); 338 | self.write(VmcsField::VmEntryIntrInfoField, 0); 339 | self.write(VmcsField::VmEntryExceptionErrorCode, 0); 340 | self.write(VmcsField::VmEntryInstructionLen, 0); 341 | self.write(VmcsField::TprThreshold, 0); 342 | self.write(VmcsField::MsrBitmap, msr_bitmap_phys.as_u64()); 343 | 344 | // 64 bit control fields 345 | self.write(VmcsField::VmExitMsrLoadAddr, 0); 346 | self.write(VmcsField::VmExitMsrStoreAddr, 0); 347 | self.write(VmcsField::VmEntryMsrLoadAddr, 0); 348 | self.write(VmcsField::TscOffset, 0); 349 | self.write(VmcsField::EptPointer, eptp.as_u64()); 350 | 351 | // natural width control fields 352 | // let cr0 = Cr0::read_raw(); 353 | // let cr4 = Cr4::read_raw(); 354 | self.write(VmcsField::Cr0GuestHostMask, 0); 355 | self.write(VmcsField::Cr4GuestHostMask, 0); 356 | self.write(VmcsField::Cr0ReadShadow, 0); 357 | self.write(VmcsField::Cr4ReadShadow, 0); 358 | // self.write(VmcsField::Cr3TargetValue0, 0); // hung 359 | // self.write(VmcsField::Cr3TargetValue1, 0); // hung 360 | // self.write(VmcsField::Cr3TargetValue2, 0); // hung 361 | // self.write(VmcsField::Cr3TargetValue3, 0); // hung 362 | } 363 | } 364 | 365 | #[allow(unused)] 366 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 367 | #[repr(u32)] 368 | pub enum VmcsField { 369 | GuestEsSelector = 0x00000800, 370 | GuestCsSelector = 0x00000802, 371 | GuestSsSelector = 0x00000804, 372 | GuestDsSelector = 0x00000806, 373 | GuestFsSelector = 0x00000808, 374 | GuestGsSelector = 0x0000080a, 375 | GuestLdtrSelector = 0x0000080c, 376 | GuestTrSelector = 0x0000080e, 377 | HostEsSelector = 0x00000c00, 378 | HostCsSelector = 0x00000c02, 379 | HostSsSelector = 0x00000c04, 380 | HostDsSelector = 0x00000c06, 381 | HostFsSelector = 0x00000c08, 382 | HostGsSelector = 0x00000c0a, 383 | HostTrSelector = 0x00000c0c, 384 | IoBitmapA = 0x00002000, 385 | IoBitmapAHigh = 0x00002001, 386 | IoBitmapB = 0x00002002, 387 | IoBitmapBHigh = 0x00002003, 388 | MsrBitmap = 0x00002004, 389 | MsrBitmapHigh = 0x00002005, 390 | VmExitMsrStoreAddr = 0x00002006, 391 | VmExitMsrStoreAddrHigh = 0x00002007, 392 | VmExitMsrLoadAddr = 0x00002008, 393 | VmExitMsrLoadAddrHigh = 0x00002009, 394 | VmEntryMsrLoadAddr = 0x0000200a, 395 | VmEntryMsrLoadAddrHigh = 0x0000200b, 396 | ExecVmcsPointer = 0x0000200c, 397 | TscOffset = 0x00002010, 398 | TscOffsetHigh = 0x00002011, 399 | VirtualApicPageAddr = 0x00002012, 400 | VirtualApicPageAddrHigh = 0x00002013, 401 | VmfuncControls = 0x00002018, 402 | VmfuncControlsHigh = 0x00002019, 403 | EptPointer = 0x0000201A, 404 | EptPointerHigh = 0x0000201B, 405 | EptpList = 0x00002024, 406 | EptpListHigh = 0x00002025, 407 | GuestPhysicalAddress = 0x00002400, 408 | GuestPhysicalAddressHigh = 0x00002401, 409 | VmcsLinkPointer = 0x00002800, 410 | VmcsLinkPointerHigh = 0x00002801, 411 | GuestIa32Debugctl = 0x00002802, 412 | GuestIa32DebugctlHigh = 0x00002803, 413 | GuestIa32Efer = 0x00002806, 414 | HostIa32Pat = 0x00002c00, 415 | HostIa32Efer = 0x00002c02, 416 | HostIa32EferHigh = 0x00002c03, 417 | PinBasedVmExecControls = 0x00004000, 418 | ProcBasedVmExecControls = 0x00004002, 419 | ExceptionBitmap = 0x00004004, 420 | PageFaultErrorCodeMask = 0x00004006, 421 | PageFaultErrorCodeMatch = 0x00004008, 422 | Cr3TargetCount = 0x0000400a, 423 | VmExitControls = 0x0000400c, 424 | VmExitMsrStoreCount = 0x0000400e, 425 | VmExitMsrLoadCount = 0x00004010, 426 | VmEntryControls = 0x00004012, 427 | VmEntryMsrLoadCount = 0x00004014, 428 | VmEntryIntrInfoField = 0x00004016, 429 | VmEntryExceptionErrorCode = 0x00004018, 430 | VmEntryInstructionLen = 0x0000401a, 431 | TprThreshold = 0x0000401c, 432 | ProcBasedVmExecControls2 = 0x0000401e, 433 | VmInstructionError = 0x00004400, 434 | VmExitReason = 0x00004402, 435 | VmExitIntrInfo = 0x00004404, 436 | VmExitIntrErrorCode = 0x00004406, 437 | IdtVectoringInfoField = 0x00004408, 438 | IdtVectoringErrorCode = 0x0000440a, 439 | VmExitInstructionLen = 0x0000440c, 440 | VmxInstructionInfo = 0x0000440e, 441 | GuestEsLimit = 0x00004800, 442 | GuestCsLimit = 0x00004802, 443 | GuestSsLimit = 0x00004804, 444 | GuestDsLimit = 0x00004806, 445 | GuestFsLimit = 0x00004808, 446 | GuestGsLimit = 0x0000480a, 447 | GuestLdtrLimit = 0x0000480c, 448 | GuestTrLimit = 0x0000480e, 449 | GuestGdtrLimit = 0x00004810, 450 | GuestIdtrLimit = 0x00004812, 451 | GuestEsAccessRights = 0x00004814, 452 | GuestCsAccessRights = 0x00004816, 453 | GuestSsAccessRights = 0x00004818, 454 | GuestDsAccessRights = 0x0000481a, 455 | GuestFsAccessRights = 0x0000481c, 456 | GuestGsAccessRights = 0x0000481e, 457 | GuestLdtrAccessRights = 0x00004820, 458 | GuestTrAccessRights = 0x00004822, 459 | GuestInterruptibilityState = 0x00004824, 460 | GuestActivityState = 0x00004826, 461 | GuestSmBase = 0x00004828, 462 | GuestIa32SysenterCs = 0x0000482A, 463 | HostIa32SysenterCs = 0x00004c00, 464 | Cr0GuestHostMask = 0x00006000, 465 | Cr4GuestHostMask = 0x00006002, 466 | Cr0ReadShadow = 0x00006004, 467 | Cr4ReadShadow = 0x00006006, 468 | Cr3TargetValue0 = 0x00006008, 469 | Cr3TargetValue1 = 0x0000600a, 470 | Cr3TargetValue2 = 0x0000600c, 471 | Cr3TargetValue3 = 0x0000600e, 472 | ExitQualification = 0x00006400, 473 | GuestLinearAddress = 0x0000640a, 474 | GuestCr0 = 0x00006800, 475 | GuestCr3 = 0x00006802, 476 | GuestCr4 = 0x00006804, 477 | GuestEsBase = 0x00006806, 478 | GuestCsBase = 0x00006808, 479 | GuestSsBase = 0x0000680a, 480 | GuestDsBase = 0x0000680c, 481 | GuestFsBase = 0x0000680e, 482 | GuestGsBase = 0x00006810, 483 | GuestLdtrBase = 0x00006812, 484 | GuestTrBase = 0x00006814, 485 | GuestGdtrBase = 0x00006816, 486 | GuestIdtrBase = 0x00006818, 487 | GuestDr7 = 0x0000681a, 488 | GuestRsp = 0x0000681c, 489 | GuestRip = 0x0000681e, 490 | GuestRflags = 0x00006820, 491 | GuestPendingDbgExceptions = 0x00006822, 492 | GuestSysenterEsp = 0x00006824, 493 | GuestSysenterEip = 0x00006826, 494 | HostCr0 = 0x00006c00, 495 | HostCr3 = 0x00006c02, 496 | HostCr4 = 0x00006c04, 497 | HostFsBase = 0x00006c06, 498 | HostGsBase = 0x00006c08, 499 | HostTrBase = 0x00006c0a, 500 | HostGdtrBase = 0x00006c0c, 501 | HostIdtrBase = 0x00006c0e, 502 | HostIa32SysenterEsp = 0x00006c10, 503 | HostIa32SysenterEip = 0x00006c12, 504 | HostRsp = 0x00006c14, 505 | HostRip = 0x00006c16, 506 | } 507 | 508 | #[allow(unused)] 509 | const VMCS_PROC_BASED_VMEXEC_CTLS_HLTEXIT: u64 = 1 << 7; 510 | const VMCS_PROC_BASED_VMEXEC_CTLS_USE_MSR_BITMAPS: u64 = 1 << 28; 511 | const VMCS_PROC_BASED_VMEXEC_CTLS_ACTIVE_SECOND_CTLS: u64 = 1 << 31; 512 | 513 | const VMCS_PROC_BASED_VMEXEC_CTLS2_ENABLE_EPT: u64 = 1 << 1; 514 | 515 | const VMCS_VMEXIT_CTLS_HOST_ADDR_SPACE_SIZE: u64 = 1 << 9; 516 | 517 | const VMCS_VMENTRY_CTLS_IA32E_MODE_GUEST: u64 = 1 << 9; 518 | -------------------------------------------------------------------------------- /vmm/src/arch/intel/vmexit_handlers.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | arch::intel::{vmcs::VmcsField, vmx::VmExitGeneralPurposeRegister, BSP}, 3 | cpu::guest_virt_to_guest_phys, 4 | serial_print, serial_println, 5 | }; 6 | use alloc::string::String; 7 | use iced_x86::{Decoder, DecoderOptions, Formatter, GasFormatter, Instruction}; 8 | use x86_64::PhysAddr; 9 | 10 | pub fn cpuid(gpr: &mut VmExitGeneralPurposeRegister) { 11 | let eax = gpr.rax as u32; 12 | let ecx = gpr.rcx as u32; 13 | let cpuid = unsafe { core::arch::x86_64::__cpuid_count(eax, ecx) }; 14 | gpr.rax = cpuid.eax as u64; 15 | gpr.rbx = cpuid.ebx as u64; 16 | gpr.rcx = cpuid.ecx as u64; 17 | gpr.rdx = cpuid.edx as u64; 18 | } 19 | 20 | pub fn cr_access(qual: u64, gpr: &mut VmExitGeneralPurposeRegister) { 21 | let bsp = unsafe { BSP.as_ptr().as_mut().unwrap() }; 22 | let cr_number = qual & 0b1111; 23 | let access_type = (qual & 0b11_0000) >> 4; 24 | let _lmsw_operand_size = (qual & 0b100_0000) >> 6; 25 | let gpr_for_mov = (qual & 0b1111_0000_0000) >> 8; 26 | serial_println!("[CR{cr_number}] ACCESS TYPE: {access_type}, GPR: {gpr_for_mov}"); 27 | 28 | match access_type { 29 | 0 => { 30 | // mov to cr 31 | let value = match gpr_for_mov { 32 | 0 => gpr.rax, 33 | 1 => gpr.rcx, 34 | 2 => gpr.rdx, 35 | 3 => gpr.rbx, 36 | 4 => bsp.vmcs_region.read(VmcsField::GuestRsp), 37 | 5 => gpr.rbp, 38 | 6 => gpr.rsi, 39 | 7 => gpr.rdi, 40 | 8 => gpr.r8, 41 | 9 => gpr.r9, 42 | 10 => gpr.r10, 43 | 11 => gpr.r11, 44 | 12 => gpr.r12, 45 | 13 => gpr.r13, 46 | 14 => gpr.r14, 47 | 15 => gpr.r15, 48 | _ => panic!(), 49 | }; 50 | match cr_number { 51 | 0 => bsp.vmcs_region.write(VmcsField::GuestCr0, value), 52 | 3 => bsp.vmcs_region.write(VmcsField::GuestCr3, value), 53 | 4 => bsp.vmcs_region.write(VmcsField::GuestCr4, value), 54 | _ => panic!(), 55 | }; 56 | serial_println!("CR{cr_number} write: 0x{value:016x}"); 57 | } 58 | 1 => { 59 | // mov from cr 60 | let value = match cr_number { 61 | 0 => bsp.vmcs_region.read(VmcsField::GuestCr0), 62 | 3 => bsp.vmcs_region.read(VmcsField::GuestCr3), 63 | 4 => bsp.vmcs_region.read(VmcsField::GuestCr4), 64 | _ => panic!(), 65 | }; 66 | match gpr_for_mov { 67 | 0 => gpr.rax = value, 68 | 1 => gpr.rcx = value, 69 | 2 => gpr.rdx = value, 70 | 3 => gpr.rbx = value, 71 | 4 => bsp.vmcs_region.write(VmcsField::GuestRsp, value), 72 | 5 => gpr.rbp = value, 73 | 6 => gpr.rsi = value, 74 | 7 => gpr.rdi = value, 75 | 8 => gpr.r8 = value, 76 | 9 => gpr.r9 = value, 77 | 10 => gpr.r10 = value, 78 | 11 => gpr.r11 = value, 79 | 12 => gpr.r12 = value, 80 | 13 => gpr.r13 = value, 81 | 14 => gpr.r14 = value, 82 | 15 => gpr.r15 = value, 83 | _ => panic!(), 84 | } 85 | serial_println!("CR{cr_number} read: 0x{value:016x}"); 86 | } 87 | _ => panic!(), 88 | } 89 | } 90 | 91 | pub fn triple_fault() { 92 | let bsp = unsafe { BSP.as_ptr().as_ref().unwrap() }; 93 | let guest_rip = bsp.vmcs_region.read(VmcsField::GuestRip); 94 | let guest_cr3 = bsp.vmcs_region.read(VmcsField::GuestCr3); 95 | let guest_rip_phys = guest_virt_to_guest_phys(guest_rip, guest_cr3); 96 | 97 | dump_instructions(guest_rip_phys, 0x20); 98 | 99 | x86_64::instructions::hlt(); 100 | } 101 | 102 | pub fn ept_violation(_gpr: &mut VmExitGeneralPurposeRegister) { 103 | let bsp = unsafe { BSP.as_ptr().as_ref().unwrap() }; 104 | let guest_rip = bsp.vmcs_region.read(VmcsField::GuestRip); 105 | let guest_cr3 = bsp.vmcs_region.read(VmcsField::GuestCr3); 106 | let guest_rip_phys = guest_virt_to_guest_phys(guest_rip, guest_cr3); 107 | 108 | dump_instructions(guest_rip_phys, 0x20); 109 | 110 | x86_64::instructions::hlt(); 111 | } 112 | 113 | pub fn init_signal() { 114 | let bsp = unsafe { BSP.as_ptr().as_ref().unwrap() }; 115 | let guest_rip = bsp.vmcs_region.read(VmcsField::GuestRip); 116 | let guest_cr3 = bsp.vmcs_region.read(VmcsField::GuestCr3); 117 | let guest_rip_phys = guest_virt_to_guest_phys(guest_rip, guest_cr3); 118 | 119 | dump_instructions(guest_rip_phys, 0x20); 120 | 121 | x86_64::instructions::hlt(); 122 | } 123 | 124 | fn dump_instructions(guest_rip_phys: PhysAddr, len: usize) { 125 | let code = unsafe { core::slice::from_raw_parts(guest_rip_phys.as_u64() as *const u8, len) }; 126 | let mut decoder = Decoder::with_ip(64, code, guest_rip_phys.as_u64(), DecoderOptions::NONE); 127 | let mut formatter = GasFormatter::new(); 128 | let mut output = String::new(); 129 | let mut instruction = Instruction::default(); 130 | while decoder.can_decode() { 131 | decoder.decode_out(&mut instruction); 132 | output.clear(); 133 | formatter.format(&instruction, &mut output); 134 | serial_print!("{:016x} ", instruction.ip()); 135 | let start_index = (instruction.ip() - guest_rip_phys.as_u64()) as usize; 136 | let instr_bytes = &code[start_index..(start_index + instruction.len())]; 137 | for b in instr_bytes.iter() { 138 | serial_print!("{:02x} ", b); 139 | } 140 | if instr_bytes.len() < 10 { 141 | for _ in 0..(10 - instr_bytes.len()) { 142 | serial_print!(" "); 143 | } 144 | } 145 | serial_println!(" {output}"); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /vmm/src/arch/intel/vmx.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | arch::intel::{ 3 | vmcs::{VmcsField, VmcsRegion}, 4 | vmexit_handlers, BSP, 5 | }, 6 | cpu::guest_virt_to_guest_phys, 7 | emu::decode_one, 8 | serial_println, BOOT_ARGS, 9 | }; 10 | use alloc::alloc::alloc; 11 | use common::constants; 12 | use core::{alloc::Layout, arch::asm, ptr, slice}; 13 | use x86_64::{ 14 | registers::{ 15 | control::{Cr0, Cr0Flags, Cr4, Cr4Flags}, 16 | model_specific::Msr, 17 | }, 18 | PhysAddr, 19 | }; 20 | 21 | pub unsafe fn vmxon(vmxon_region: &mut VmxonRegion) -> Result<(), VmxError> { 22 | let cr4 = Cr4::read(); 23 | let cr4_fixed_0 = Msr::new(0x488).read(); 24 | let cr4_fixed_1 = Msr::new(0x489).read(); 25 | let cr4 = cr4 & Cr4Flags::from_bits_unchecked(cr4_fixed_1); 26 | let cr4 = cr4 | Cr4Flags::from_bits_unchecked(cr4_fixed_0); 27 | Cr4::write(cr4); 28 | let cr4 = Cr4::read(); 29 | let cr4 = cr4 | Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS; 30 | Cr4::write(cr4); 31 | 32 | let cr0 = Cr0::read(); 33 | let cr0_fixed_0 = Msr::new(0x486).read(); 34 | let cr0_fixed_1 = Msr::new(0x487).read(); 35 | let cr0 = cr0 & Cr0Flags::from_bits_unchecked(cr0_fixed_1); 36 | let cr0 = cr0 | Cr0Flags::from_bits_unchecked(cr0_fixed_0); 37 | Cr0::write(cr0); 38 | 39 | let mut msr_ia32_feature_control = Msr::new(0x0000003a); // IA32_FEATURE_CONTROL 40 | let ia32_feature_control = unsafe { msr_ia32_feature_control.read() }; 41 | let lock = ia32_feature_control & 0b1 == 1; 42 | if !lock { 43 | msr_ia32_feature_control.write(ia32_feature_control | 5); 44 | } 45 | 46 | let paddr = vmxon_region.paddr(); 47 | asm_vmxon(paddr) 48 | } 49 | 50 | unsafe fn asm_vmxon(phys_addr: PhysAddr) -> Result<(), VmxError> { 51 | let mut flags; 52 | asm!("vmxon [rdi]; pushfq; pop rax", in("rdi") &phys_addr.as_u64(), out("rax") flags); 53 | check_vmx_error(flags) 54 | } 55 | 56 | pub unsafe fn vmclear(vmcs_region: &mut VmcsRegion) { 57 | let paddr = vmcs_region.paddr(); 58 | if let Err(_e) = asm_vmclear(paddr) { 59 | panic!(); 60 | } 61 | } 62 | 63 | unsafe fn asm_vmclear(phys_addr: PhysAddr) -> Result<(), VmxError> { 64 | let mut flags; 65 | asm!("vmclear [rdi]; pushfq; pop rax", in("rdi") &phys_addr.as_u64(), out("rax") flags); 66 | check_vmx_error(flags) 67 | } 68 | 69 | pub unsafe fn vmptrld(vmcs_region: &VmcsRegion) { 70 | let paddr = vmcs_region.paddr(); 71 | if let Err(_e) = asm_vmptrld(paddr) { 72 | panic!(); 73 | } 74 | } 75 | 76 | unsafe fn asm_vmptrld(phys_addr: PhysAddr) -> Result<(), VmxError> { 77 | let mut flags; 78 | asm!("vmptrld [rdi]; pushfq; pop rax", in("rdi") &phys_addr.as_u64(), out("rax") flags); 79 | check_vmx_error(flags) 80 | } 81 | 82 | pub unsafe fn vmread(field: VmcsField) -> u64 { 83 | if let Ok(val) = asm_vmread(field) { 84 | val 85 | } else { 86 | panic!(); 87 | } 88 | } 89 | 90 | unsafe fn asm_vmread(field: VmcsField) -> Result { 91 | let mut flags; 92 | let mut value; 93 | asm!("vmread rdi, rsi; pushfq; pop rax", in("rsi") field as u32, out("rdi") value, out("rax") flags); 94 | #[allow(clippy::question_mark)] 95 | if let Err(e) = check_vmx_error(flags) { 96 | Err(e) 97 | } else { 98 | Ok(value) 99 | } 100 | } 101 | 102 | pub unsafe fn vmwrite(field: VmcsField, value: u64) { 103 | if let Err(_e) = asm_vmwrite(field, value) { 104 | panic!(); 105 | } 106 | } 107 | 108 | unsafe fn asm_vmwrite(field: VmcsField, value: u64) -> Result<(), VmxError> { 109 | let mut flags; 110 | asm!("vmwrite rdi, rsi; pushfq; pop rax", in("rdi") field as u32, in("rsi") value, out("rax") flags); 111 | check_vmx_error(flags) 112 | } 113 | 114 | pub unsafe fn vmlaunch() -> Result<(), VmxError> { 115 | asm_vmlaunch() 116 | } 117 | 118 | unsafe fn asm_vmlaunch() -> Result<(), VmxError> { 119 | let mut flags; 120 | asm!("vmlaunch; pushfq; pop rax", out("rax") flags); 121 | check_vmx_error(flags) 122 | } 123 | 124 | fn check_vmx_error(flags: u64) -> Result<(), VmxError> { 125 | let cf = (flags & 0b1) == 1; 126 | let zf = ((flags >> 6) & 0b1) == 1; 127 | 128 | if cf { 129 | Err(VmxError::InvalidPointer) 130 | } else if zf { 131 | Err(VmxError::VmInstructionError) 132 | } else { 133 | Ok(()) 134 | } 135 | } 136 | 137 | #[derive(Debug)] 138 | pub struct VmxonRegion(*mut u8); 139 | 140 | unsafe impl Send for VmxonRegion {} 141 | 142 | impl VmxonRegion { 143 | pub unsafe fn new() -> Self { 144 | let layout = Layout::from_size_align_unchecked(4096, 4096); 145 | let region = alloc(layout); 146 | let region_slice = slice::from_raw_parts_mut(region, 4096); 147 | region_slice.fill(0); 148 | 149 | let ia32_vmx_basic = Msr::new(constants::MSR_IA32_VMX_BASIC).read(); 150 | let vmcs_rev_id = (ia32_vmx_basic & 0x7fff_ffff) as u32; 151 | 152 | ptr::write_volatile(region as *mut u32, vmcs_rev_id); 153 | 154 | Self(region) 155 | } 156 | 157 | fn as_mut_ptr(&self) -> *mut u8 { 158 | self.0 159 | } 160 | 161 | fn paddr(&self) -> PhysAddr { 162 | let virt = self.as_mut_ptr() as u64 as i64; 163 | let phys = (virt + BOOT_ARGS.load().vmm_phys_offset) as u64; 164 | PhysAddr::new(phys) 165 | } 166 | } 167 | 168 | pub enum VmxError { 169 | InvalidPointer, 170 | VmInstructionError, 171 | } 172 | 173 | #[allow(unused)] 174 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 175 | #[repr(u64)] 176 | enum VmExitReason { 177 | ExceptionOrNmi = 0, 178 | ExternalInterrupt = 1, 179 | TripleFault = 2, 180 | InitSignal = 3, 181 | StartupIpi = 4, 182 | IoSmi = 5, 183 | OtherSmi = 6, 184 | InterruptWindow = 7, 185 | NmiWindow = 8, 186 | TaskSwitch = 9, 187 | Cpuid = 10, 188 | Getsec = 11, 189 | Hlt = 12, 190 | Invd = 13, 191 | Invlpg = 14, 192 | Rdpmc = 15, 193 | Rdtsc = 16, 194 | Rsm = 17, 195 | Vmcall = 18, 196 | Vmclear = 19, 197 | Vmlaunch = 20, 198 | Vmptrld = 21, 199 | Vmptrst = 22, 200 | Vmread = 23, 201 | Vmresume = 24, 202 | Vmwrite = 25, 203 | Vmxoff = 26, 204 | Vmxon = 27, 205 | CrAccess = 28, 206 | MovDr = 29, 207 | IoInstruction = 30, 208 | Rdmsr = 31, 209 | Wrmsr = 32, 210 | VmentryFailInvalidGuestState = 33, 211 | VmentryFailMsrLoading = 34, 212 | Mwait = 36, 213 | MonitorTrapFlag = 37, 214 | Monitor = 39, 215 | Pause = 40, 216 | VmentryFailMachineCheckEvent = 41, 217 | TprBelowThreshold = 43, 218 | ApicAccess = 44, 219 | VirtualizedEoi = 45, 220 | AccessGdtrOrIdtr = 46, 221 | AccessLdtrOrTr = 47, 222 | EptViolation = 48, 223 | EptMisconfiguration = 49, 224 | Invept = 50, 225 | Rdtscp = 51, 226 | VmxPreemptionTimerExpired = 52, 227 | Invvpid = 53, 228 | WbinvdOrWbnoinvd = 54, 229 | Xsetbv = 55, 230 | ApicWrite = 56, 231 | Rdrand = 57, 232 | Invpcid = 58, 233 | Vmfunc = 59, 234 | Encls = 60, 235 | Rdseed = 61, 236 | PageModificationLogFull = 62, 237 | Xsaves = 63, 238 | Xrstors = 64, 239 | Pconfig = 65, 240 | SppRelatedEvent = 66, 241 | Umwait = 67, 242 | Tpause = 68, 243 | Loadiwkey = 69, 244 | } 245 | 246 | #[derive(Debug)] 247 | #[repr(C)] 248 | pub struct VmExitGeneralPurposeRegister { 249 | pub r15: u64, 250 | pub r14: u64, 251 | pub r13: u64, 252 | pub r12: u64, 253 | pub r11: u64, 254 | pub r10: u64, 255 | pub r9: u64, 256 | pub r8: u64, 257 | pub rsi: u64, 258 | pub rdi: u64, 259 | pub rdx: u64, 260 | pub rcx: u64, 261 | pub rbx: u64, 262 | pub rax: u64, 263 | pub rbp: u64, 264 | } 265 | 266 | pub fn handle_vmexit(reason: u64, qual: u64, gpr: *mut VmExitGeneralPurposeRegister) { 267 | serial_println!("reason: {reason}"); 268 | let reason = unsafe { core::mem::transmute(reason) }; 269 | serial_println!("{reason:?}, qualification: 0x{qual:x}"); 270 | let bsp = unsafe { BSP.as_ptr().as_mut().unwrap() }; 271 | let gpr = unsafe { gpr.as_mut().unwrap() }; 272 | let rsp = bsp.vmcs_region.read(VmcsField::GuestRsp); 273 | let rip = bsp.vmcs_region.read(VmcsField::GuestRip); 274 | let rflags = bsp.vmcs_region.read(VmcsField::GuestRflags); 275 | serial_println!("rip: 0x{:016x} flg: 0x{:016x}", rip, rflags); 276 | serial_println!( 277 | "rax: 0x{:016x} rbx: 0x{:016x} rcx: 0x{:016x} rdx: 0x{:016x}", 278 | gpr.rax, 279 | gpr.rbx, 280 | gpr.rcx, 281 | gpr.rdx 282 | ); 283 | serial_println!( 284 | "rsi: 0x{:016x} rdi: 0x{:016x} rsp: 0x{:016x} rbp: 0x{:016x}", 285 | gpr.rsi, 286 | gpr.rdi, 287 | rsp, 288 | gpr.rbp 289 | ); 290 | serial_println!( 291 | " r8: 0x{:016x} r9: 0x{:016x} r10: 0x{:016x} r11: 0x{:016x}", 292 | gpr.r8, 293 | gpr.r9, 294 | gpr.r10, 295 | gpr.r11 296 | ); 297 | serial_println!( 298 | "r12: 0x{:016x} r13: 0x{:016x} r14: 0x{:016x} r15: 0x{:016x}", 299 | gpr.r12, 300 | gpr.r13, 301 | gpr.r14, 302 | gpr.r15 303 | ); 304 | 305 | match reason { 306 | VmExitReason::TripleFault => vmexit_handlers::triple_fault(), 307 | VmExitReason::EptViolation => vmexit_handlers::ept_violation(gpr), 308 | VmExitReason::CrAccess => vmexit_handlers::cr_access(qual, gpr), 309 | VmExitReason::ExceptionOrNmi => { 310 | let vmexit_intr_info = bsp.vmcs_region.read(VmcsField::VmExitIntrInfo); 311 | let vector = vmexit_intr_info & 0b1111_1111; 312 | let intr_type = (vmexit_intr_info & 0b111_0000_0000) >> 8; 313 | let error_code_valid = ((vmexit_intr_info & 0b1000_0000_0000) >> 11) == 1; 314 | serial_println!("Interruption vector: {vector}"); 315 | serial_println!("Interruption type number: {intr_type}"); 316 | if error_code_valid { 317 | let error_code = bsp.vmcs_region.read(VmcsField::VmExitIntrErrorCode); 318 | serial_println!("Error code: {error_code}"); 319 | } 320 | 321 | x86_64::instructions::hlt(); 322 | 323 | let rip = bsp.vmcs_region.read(VmcsField::GuestRip); 324 | let instruction = decode_one(rip); 325 | if instruction.is_err() { 326 | serial_println!("decode error"); 327 | panic!(); 328 | } 329 | let instruction = instruction.unwrap(); 330 | bsp.vmcs_region 331 | .write(VmcsField::GuestRip, rip + instruction.len() as u64); 332 | } 333 | VmExitReason::Cpuid => vmexit_handlers::cpuid(gpr), 334 | VmExitReason::InitSignal => vmexit_handlers::init_signal(), 335 | _ => x86_64::instructions::hlt(), 336 | } 337 | 338 | let cr3 = bsp.vmcs_region.read(VmcsField::GuestCr3); 339 | let rip_phys = guest_virt_to_guest_phys(rip, cr3); 340 | let instruction = decode_one(rip_phys.as_u64()); 341 | if instruction.is_err() { 342 | panic!("instruction decode error"); 343 | } 344 | let instruction = instruction.unwrap(); 345 | serial_println!("{instruction:x?}"); 346 | bsp.vmcs_region 347 | .write(VmcsField::GuestRip, rip + instruction.len() as u64); 348 | } 349 | -------------------------------------------------------------------------------- /vmm/src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod amd; 2 | pub mod intel; 3 | -------------------------------------------------------------------------------- /vmm/src/cpu.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use x86_64::{ 3 | instructions::tables::sgdt, 4 | registers::segmentation::Segment, 5 | structures::gdt::SegmentSelector, 6 | structures::paging::{PageTable, PageTableFlags}, 7 | PhysAddr, 8 | }; 9 | 10 | pub trait Cpu { 11 | fn is_virtualization_supported(&self) -> bool; 12 | fn enable_virtualization(&mut self) -> Result<(), CpuError>; 13 | fn disable_virtualization(&mut self) -> Result<(), CpuError>; 14 | fn init_as_bsp(&mut self); 15 | fn run_vm(&mut self); 16 | } 17 | 18 | #[derive(Debug)] 19 | pub enum CpuError { 20 | NotSupported, 21 | VmxRelated, 22 | } 23 | 24 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 25 | pub struct Ldtr; 26 | 27 | impl Segment for Ldtr { 28 | fn get_reg() -> SegmentSelector { 29 | let mut ldtr: u16 = 0; 30 | unsafe { 31 | asm!("sldt [{}]", in(reg) &mut ldtr, options(nostack, preserves_flags)); 32 | } 33 | SegmentSelector(ldtr) 34 | } 35 | 36 | unsafe fn set_reg(sel: SegmentSelector) { 37 | asm!("lldt [{}]", in(reg) &sel.0, options(readonly, nostack, preserves_flags)); 38 | } 39 | } 40 | 41 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 42 | pub struct Tr; 43 | 44 | impl Segment for Tr { 45 | fn get_reg() -> SegmentSelector { 46 | let tr: u16; 47 | unsafe { 48 | asm!("str {0:x}", out(reg) tr, options(nostack, nomem, preserves_flags)); 49 | } 50 | SegmentSelector(tr) 51 | } 52 | 53 | unsafe fn set_reg(sel: SegmentSelector) { 54 | asm!("ltr [{}]", in(reg) &sel.0, options(readonly, nostack, preserves_flags)); 55 | } 56 | } 57 | 58 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 59 | pub struct SegmentDescriptor; 60 | 61 | impl SegmentDescriptor { 62 | pub fn base(sel: &SegmentSelector) -> u64 { 63 | let i = sel.index() as usize; 64 | let gdt = sgdt(); 65 | let gdt_limit = gdt.limit; 66 | let gdt = unsafe { 67 | core::slice::from_raw_parts(gdt.base.as_ptr() as *const u64, gdt_limit as usize) 68 | }; 69 | let segment_descriptor = gdt[i]; 70 | let segment_descriptor_high = gdt[i + 1]; 71 | 72 | ((segment_descriptor_high & 0xffff_ffff) << 32) 73 | | ((segment_descriptor & (0xff << 56)) >> 32) 74 | | ((segment_descriptor & (0xff << 32)) >> 16) 75 | | ((segment_descriptor & (0xffff << 16)) >> 16) 76 | } 77 | 78 | // pub fn limit(sel: &SegmentSelector) -> u32 { 79 | // let mut limit: u32 = 0; 80 | // unsafe { 81 | // asm!("lsl {}, [{}]", in(reg) &mut limit, in(reg) &sel.0, options(nostack, preserves_flags)); 82 | // } 83 | // limit 84 | // } 85 | } 86 | 87 | pub fn guest_virt_to_guest_phys(guest_virt: u64, guest_cr3: u64) -> PhysAddr { 88 | let pml4_index = ((guest_virt >> 39) & 0b1_1111_1111) as usize; 89 | let pdp_index = ((guest_virt >> 30) & 0b1_1111_1111) as usize; 90 | let pd_index = ((guest_virt >> 21) & 0b1_1111_1111) as usize; 91 | let pt_index = ((guest_virt >> 12) & 0b1_1111_1111) as usize; 92 | // serial_println!("pml4: {pml4_index}, pdp: {pdp_index}, pd: {pd_index}, pt: {pt_index}"); 93 | 94 | let guest_pml4_phys = PhysAddr::new(guest_cr3 & !0b1111_1111_1111); 95 | let guest_pml4 = unsafe { 96 | (guest_pml4_phys.as_u64() as *mut PageTable) 97 | .as_mut() 98 | .unwrap() 99 | }; 100 | 101 | // use crate::serial_println; 102 | 103 | let guest_pml4_entry = &guest_pml4[pml4_index]; 104 | // serial_println!("{:?}", guest_pml4_entry); 105 | if !guest_pml4_entry.flags().contains(PageTableFlags::PRESENT) { 106 | return PhysAddr::new(0); 107 | } 108 | 109 | let guest_pdp = unsafe { 110 | (guest_pml4_entry.addr().as_u64() as *mut PageTable) 111 | .as_mut() 112 | .unwrap() 113 | }; 114 | 115 | let guest_pdp_entry = &guest_pdp[pdp_index]; 116 | // serial_println!("{:?}", guest_pdp_entry); 117 | if !guest_pdp_entry.flags().contains(PageTableFlags::PRESENT) { 118 | return PhysAddr::new(0); 119 | } 120 | if guest_pdp_entry.flags().contains(PageTableFlags::HUGE_PAGE) { 121 | let pdp_offset = guest_virt & 0b1_1111_1111_1111_1111_1111_1111_1111; 122 | return guest_pdp_entry.addr() + pdp_offset; 123 | } 124 | 125 | let guest_pd = unsafe { 126 | (guest_pdp_entry.addr().as_u64() as *mut PageTable) 127 | .as_mut() 128 | .unwrap() 129 | }; 130 | 131 | let guest_pd_entry = &guest_pd[pd_index]; 132 | // serial_println!("{:?}", guest_pd_entry); 133 | if !guest_pd_entry.flags().contains(PageTableFlags::PRESENT) { 134 | return PhysAddr::new(0); 135 | } 136 | if guest_pd_entry.flags().contains(PageTableFlags::HUGE_PAGE) { 137 | let pd_offset = guest_virt & 0b1_1111_1111_1111_1111_1111; 138 | return guest_pd_entry.addr() + pd_offset; 139 | } 140 | 141 | let guest_pt = unsafe { 142 | (guest_pd_entry.addr().as_u64() as *mut PageTable) 143 | .as_mut() 144 | .unwrap() 145 | }; 146 | 147 | let guest_pt_entry = &guest_pt[pt_index]; 148 | let pt_offset = guest_virt & 0b1111_1111_1111; 149 | guest_pt_entry.addr() + pt_offset 150 | } 151 | -------------------------------------------------------------------------------- /vmm/src/emu.rs: -------------------------------------------------------------------------------- 1 | // use crate::arch::intel::vmx::VmExitGeneralPurposeRegister; 2 | use iced_x86::{Decoder, DecoderOptions, Instruction}; 3 | 4 | pub fn decode_one(rip: u64) -> Result { 5 | let code = unsafe { core::slice::from_raw_parts(rip as *const u8, 10) }; 6 | let mut decoder = Decoder::with_ip(64, code, rip, DecoderOptions::NONE); 7 | if decoder.can_decode() { 8 | Ok(decoder.decode()) 9 | } else { 10 | Err(()) 11 | } 12 | } 13 | 14 | // pub fn emulation(instruction: Instruction, gpr: &mut VmExitGeneralPurposeRegister) {} 15 | -------------------------------------------------------------------------------- /vmm/src/entry.s: -------------------------------------------------------------------------------- 1 | .code64 2 | .section .entry, "awx" 3 | 4 | .global entry, entry_ret 5 | entry: # pub extern "sysv64" fn entry(boot_args: *const BootArgs); 6 | push %rbp 7 | mov %rsp, %rbp 8 | mov 16(%rdi), %rax 9 | mov %rax, vmm_physoff(%rip) 10 | call set_vmm_tss64 11 | call save_uefi_regs 12 | call init_vmm_regs 13 | lea vmm_gdtr(%rip), %rax 14 | lgdt (%rax) 15 | mov $8, %rax 16 | shl $3, %rax 17 | ltr %ax 18 | mov $10, %rax 19 | shl $3, %rax 20 | lldt %ax 21 | mov %rsp, uefi_rsp(%rip) 22 | lea vmm_stack_end(%rip), %rax 23 | mov %rax, %rsp 24 | lea vmm_main(%rip), %rax 25 | mov %rax, vmm_main_ljmp(%rip) 26 | mov $4, %ax # DATA64 27 | shl $3, %ax 28 | mov %ax, %ds 29 | mov %ax, %es 30 | mov %ax, %ss 31 | mov %ax, %fs 32 | mov %ax, %gs 33 | lea vmm_main_ljmp(%rip), %rax 34 | .byte 0x48 # REX.W prefix 35 | ljmpl *(%rax) 36 | entry_ret: 37 | mov uefi_rsp(%rip), %rsp 38 | pop %rbp 39 | ret 40 | 41 | .align 16 42 | vmm_main_ljmp: 43 | .quad 0 44 | .quad 0x18 # CODE64 45 | 46 | .global save_uefi_regs 47 | save_uefi_regs: # 1st arg: uefi_cr3 48 | push %rax 49 | push %rcx 50 | push %rdx 51 | mov %cs, uefi_cs(%rip) 52 | mov %ds, uefi_ds(%rip) 53 | mov %es, uefi_es(%rip) 54 | mov %fs, uefi_fs(%rip) 55 | mov %gs, uefi_gs(%rip) 56 | mov %ss, uefi_ss(%rip) 57 | lea uefi_gdtr(%rip), %rax 58 | sgdt (%rax) 59 | lea uefi_idtr(%rip), %rax 60 | sidt (%rax) 61 | lea uefi_ldtr(%rip), %rax 62 | sldt (%rax) 63 | lea uefi_tr(%rip), %rax 64 | str (%rax) 65 | mov %cr0, %rax 66 | mov %rax, uefi_cr0(%rip) 67 | mov (%rdi), %rax 68 | mov %rax, uefi_cr3(%rip) 69 | mov %cr4, %rax 70 | mov %rax, uefi_cr4(%rip) 71 | mov $0x174, %rcx # MSR_IA32_SYSENTER_CS 72 | rdmsr 73 | mov %ax, uefi_msr_ia32_sysenter_cs(%rip) 74 | mov $0x175, %rcx # MSR_IA32_SYSENTER_ESP 75 | rdmsr 76 | mov %eax, uefi_msr_ia32_sysenter_esp(%rip) 77 | mov %edx, uefi_msr_ia32_sysenter_esp_high(%rip) 78 | mov $0x176, %rcx # MSR_IA32_SYSENTER_EIP 79 | rdmsr 80 | mov %eax, uefi_msr_ia32_sysenter_eip(%rip) 81 | mov %edx, uefi_msr_ia32_sysenter_eip_high(%rip) 82 | pop %rdx 83 | pop %rcx 84 | pop %rax 85 | ret 86 | 87 | .global restore_uefi_regs 88 | restore_uefi_regs: 89 | push %rax 90 | push %rcx 91 | push %rdx 92 | lea uefi_gdtr(%rip), %rax 93 | lgdt (%rax) 94 | lea uefi_idtr(%rip), %rax 95 | lidt (%rax) 96 | lea uefi_ldtr(%rip), %rax 97 | lldt (%rax) 98 | mov uefi_es(%rip), %es 99 | xor %rax, %rax 100 | mov uefi_cs(%rip), %ax 101 | push %rax 102 | lea 1f(%rip), %rax 103 | push %rax 104 | lretq 105 | 1: 106 | mov uefi_ds(%rip), %ds 107 | mov uefi_es(%rip), %es 108 | mov uefi_fs(%rip), %fs 109 | mov uefi_gs(%rip), %gs 110 | mov uefi_ss(%rip), %ss 111 | # hold cr0, cr4 state!!! 112 | # mov uefi_cr4(%rip), %rax 113 | # or $0b10000000000000, %rax # VMXE bit(intel only, FIXME) 114 | # mov %rax, %cr4 115 | # mov uefi_cr0(%rip), %rax 116 | # mov %rax, %cr0 117 | xor %rax, %rax 118 | mov $0x174, %rcx # MSR_IA32_SYSENTER_CS 119 | mov uefi_msr_ia32_sysenter_cs(%rip), %eax 120 | wrmsr 121 | xor %rax, %rax 122 | mov $0x175, %rcx # MSR_IA32_SYSENTER_ESP 123 | mov uefi_msr_ia32_sysenter_esp(%rip), %eax 124 | mov uefi_msr_ia32_sysenter_esp_high(%rip), %rdx 125 | wrmsr 126 | xor %rax, %rax 127 | mov $0x176, %rcx # MSR_IA32_SYSENTER_EIP 128 | mov uefi_msr_ia32_sysenter_eip(%rip), %eax 129 | mov uefi_msr_ia32_sysenter_eip_high(%rip), %rdx 130 | wrmsr 131 | pop %rdx 132 | pop %rcx 133 | pop %rax 134 | ret 135 | 136 | .align 16 137 | restore_uefi_regs_ljmp_rip: 138 | .quad 0 # rip 139 | restore_uefi_regs_ljmp_cs: 140 | .quad 0 # cs 141 | 142 | .global init_vmm_regs 143 | init_vmm_regs: 144 | push %rax 145 | push %rbx 146 | mov $(vmm_gdt_end - vmm_gdt), %ax # sizeof GDT 147 | mov %ax, vmm_gdtr(%rip) 148 | lea vmm_gdtr(%rip), %rax 149 | lea vmm_gdt(%rip), %rbx 150 | mov %rbx, 2(%rax) 151 | mov %cr3, %rax 152 | mov %rax, vmm_cr3(%rip) 153 | mov %cr4, %rax 154 | or $(0x20|0x80|0x2000), %rax # PAE + PGE + VMXE 155 | and $(~0x40), %rax # !MCE 156 | mov %rax, vmm_cr4(%rip) 157 | mov %rax, %cr4 158 | pop %rbx 159 | pop %rax 160 | ret 161 | 162 | .global set_vmm_tss64 163 | set_vmm_tss64: 164 | push %rax 165 | push %rcx 166 | push %rdx 167 | xor %rcx, %rcx 168 | lea vmm_tss64(%rip), %rax 169 | lea vmm_gdt(%rip), %rdx 170 | mov %ax, %cx 171 | mov %cx, 0x42(%rdx) # base address 15:00 172 | shr $16, %rax 173 | mov %ax, %cx 174 | mov %cl, 0x44(%rdx) # base address 23:16 175 | mov %ch, 0x47(%rdx) # base address 31:24 176 | shr $16, %rax 177 | mov %eax, %ecx 178 | mov %ecx, 0x48(%rdx) # base address 63:32 179 | # movb $0x89, 0x45(%rdx) # TSS-available 180 | pop %rdx 181 | pop %rcx 182 | pop %rax 183 | ret 184 | 185 | .global call_uefi_write_char # unsafe fn call_uefi_write_char(fp: u64, output: u64, c: char); 186 | call_uefi_write_char: 187 | push %rbp 188 | mov %rsp, %rbp 189 | push %rbx 190 | push %rcx 191 | push %rdx 192 | push %r8 193 | push %r9 194 | push %r10 195 | mov %rsp, %r10 196 | mov uefi_rsp(%rip), %rsp 197 | push %r10 # vmm_rsp 198 | mov %cr3, %r10 199 | push %r10 # vmm_cr3 200 | mov %rdi, %r8 201 | mov %rsi, %r9 202 | mov uefi_cr3(%rip), %rbx 203 | lea 3f(%rip), %rax 204 | # because vmm_physoff is i64, must check whether positive of negative 205 | mov vmm_physoff(%rip), %rdi 206 | shr $63, %rdi 207 | cmp $1, %rdi 208 | jne 1f 209 | # negative 210 | mov vmm_physoff(%rip), %rdi 211 | neg %rdi 212 | sub %rdi, %rax 213 | jmp 2f 214 | 1: 215 | # positive 216 | mov vmm_physoff(%rip), %rdi 217 | add %rdi, %rax 218 | 2: 219 | jmp *%rax 220 | 3: 221 | nop # for QEMU 222 | mov %r8, %rax 223 | mov %r9, %rcx 224 | mov %rbx, %cr3 225 | mov %rsp, %rbx 226 | and $0xf, %rbx 227 | cmpb $0x8, %bl 228 | cld 229 | sti 230 | je 2f 231 | call *%rax 232 | jmp 3f 233 | 2: 234 | sub $8, %rsp 235 | call *%rax 236 | add $8, %rsp 237 | 3: 238 | cli 239 | pop %r10 # vmm_cr3 240 | pop %rsp # vmm_rsp 241 | mov %r10, %cr3 242 | lea 1f(%rip), %rax 243 | jmp *%rax 244 | 1: 245 | pop %r10 246 | pop %r9 247 | pop %r8 248 | pop %rdx 249 | pop %rcx 250 | pop %rbx 251 | pop %rbp 252 | ret 253 | 254 | .global vmexit_handler 255 | vmexit_handler: 256 | # save guest general register 257 | push %rbp 258 | push %rax 259 | push %rbx 260 | push %rcx 261 | push %rdx 262 | push %rdi 263 | push %rsi 264 | push %r8 265 | push %r9 266 | push %r10 267 | push %r11 268 | push %r12 269 | push %r13 270 | push %r14 271 | push %r15 272 | mov %rsp, %rdi 273 | call resume_vm 274 | # restore guest general register 275 | pop %r15 276 | pop %r14 277 | pop %r13 278 | pop %r12 279 | pop %r11 280 | pop %r10 281 | pop %r9 282 | pop %r8 283 | pop %rsi 284 | pop %rdi 285 | pop %rdx 286 | pop %rcx 287 | pop %rbx 288 | pop %rax 289 | pop %rbp 290 | vmresume 291 | 292 | .global asm_init_serial # fn asm_init_serial(base: u16); 293 | asm_init_serial: 294 | push %rdx 295 | push %rax 296 | xor %rdx, %rdx 297 | mov $0x2f9, %dx 298 | mov $0x00, %al 299 | out %al, %dx # disable all interrupts 300 | mov $0x2fb, %dx 301 | mov $0x80, %al 302 | out %al, %dx # enable dlab(set baud rate divisor) 303 | mov $0x2f8, %dx 304 | mov $0x01, %al 305 | out %al, %dx # set divisor to 1 (lo byte) 115200 baud 306 | mov $0x2f9, %dx 307 | mov $0x00, %al 308 | out %al, %dx # (hi byte) 309 | mov $0x2fb, %dx 310 | mov $0x03, %al 311 | out %al, %dx # 8 bits, no parity, one stop bit 312 | mov $0x2fa, %dx 313 | mov $0xc7, %al 314 | out %al, %dx # enable fifo, clear them, with 14-byte threshold 315 | mov $0x2fc, %dx 316 | mov $0x0b, %al 317 | out %al, %dx # IRQs enabled, RTS/DSR set 318 | mov $0x2fc, %dx 319 | mov $0x1e, %al 320 | out %al, %dx # set in loopback mode, test the serial chip 321 | pop %rax 322 | pop %rdx 323 | ret 324 | 325 | .global asm_serial_output_char 326 | asm_serial_output_char: 327 | mov %rdi, %rax 328 | mov $0x2f8, %dx 329 | out %al, %dx 330 | mov $0x3f8, %dx 331 | out %al, %dx 332 | ret 333 | 334 | # ===== UEFI special registers ===== 335 | .align 8 336 | .global uefi_cs 337 | uefi_cs: 338 | .short 0 339 | 340 | .align 8 341 | .global uefi_ds 342 | uefi_ds: 343 | .short 0 344 | 345 | .align 8 346 | .global uefi_es 347 | uefi_es: 348 | .short 0 349 | 350 | .align 8 351 | .global uefi_fs 352 | uefi_fs: 353 | .short 0 354 | 355 | .align 8 356 | .global uefi_gs 357 | uefi_gs: 358 | .short 0 359 | 360 | .align 8 361 | .global uefi_ss 362 | uefi_ss: 363 | .short 0 364 | 365 | .align 8 366 | .global uefi_cr0 367 | uefi_cr0: 368 | .quad 0 369 | 370 | .align 8 371 | .global uefi_cr3 372 | uefi_cr3: 373 | .quad 0 374 | 375 | .align 8 376 | .global uefi_cr4 377 | uefi_cr4: 378 | .quad 0 379 | 380 | .align 8 381 | .global uefi_rsp 382 | uefi_rsp: 383 | .quad 0 384 | 385 | .align 16 386 | .global uefi_gdtr 387 | uefi_gdtr: 388 | .space 16 389 | 390 | .align 16 391 | .global uefi_idtr 392 | uefi_idtr: 393 | .space 16 394 | 395 | .align 8 396 | .global uefi_ldtr 397 | uefi_ldtr: 398 | .short 0 399 | 400 | .align 8 401 | .global uefi_tr 402 | uefi_tr: 403 | .short 0 404 | 405 | .align 8 406 | .global uefi_msr_ia32_sysenter_cs 407 | uefi_msr_ia32_sysenter_cs: 408 | .short 0 409 | 410 | .align 16 411 | .global uefi_msr_ia32_sysenter_esp 412 | uefi_msr_ia32_sysenter_esp: 413 | .word 0 414 | .global uefi_msr_ia32_sysenter_esp_high 415 | uefi_msr_ia32_sysenter_esp_high: 416 | .word 0 417 | 418 | .align 16 419 | .global uefi_msr_ia32_sysenter_eip 420 | uefi_msr_ia32_sysenter_eip: 421 | .word 0 422 | .global uefi_msr_ia32_sysenter_eip_high 423 | uefi_msr_ia32_sysenter_eip_high: 424 | .word 0 425 | # === UEFI special registers end === 426 | 427 | # ===== VMM special registers ===== 428 | .align 8 429 | .global vmm_physoff 430 | vmm_physoff: 431 | .quad 0 432 | 433 | .align 8 434 | .global vmm_rsp 435 | vmm_rsp: 436 | .quad 0 437 | 438 | .align 8 439 | .global vmm_cr3 440 | vmm_cr3: 441 | .quad 0 442 | 443 | .align 8 444 | .global vmm_cr4 445 | vmm_cr4: 446 | .quad 0 447 | 448 | .align 16 449 | .global vmm_gdtr 450 | vmm_gdtr: 451 | .short 0 452 | .quad 0 453 | 454 | .align 16 455 | .global vmm_gdt, vmm_gdt_end 456 | vmm_gdt: 457 | .quad 0x0000000000000000 # NULL 458 | .quad 0x00CF9B000000FFFF # 0x08 CODE32, DPL=0 459 | .quad 0x00CF93000000FFFF # 0x10 DATA32, DPL=0 460 | .quad 0x00AF9B000000FFFF # 0x18 CODE64, DPL=0 461 | .quad 0x00AF93000000FFFF # 0x20 DATA64, DPL=0 462 | .quad 0x00009B000000FFFF # 0x28 CODE16, DPL=0 463 | .quad 0x000093000000FFFF # 0x30 DATA16, DPL=0 464 | .quad 0x0000930B8000FFFF # 0x38 DATA16, DPL=0 465 | .quad 0x000089000000FFFF # 0x40 TSS64(LOW) 466 | .quad 0x0000000000000000 # 0x48 TSS64(HIGH) 467 | .quad 0x000082000000FFFF # 0x50 LDT(LOW) 468 | .quad 0x0000000000000000 # 0x58 LDT(HIGH) 469 | vmm_gdt_end: 470 | 471 | .align 16 472 | .global vmm_tss64 473 | vmm_tss64: 474 | .word 0x00000000 # reserved 475 | .quad 0x0000000000000000 # RSP0 476 | .quad 0x0000000000000000 # RSP1 477 | .quad 0x0000000000000000 # RSP2 478 | .quad 0x0000000000000000 # reserved 479 | .quad 0x0000000000000000 # IST1 480 | .quad 0x0000000000000000 # IST2 481 | .quad 0x0000000000000000 # IST3 482 | .quad 0x0000000000000000 # IST4 483 | .quad 0x0000000000000000 # IST5 484 | .quad 0x0000000000000000 # IST6 485 | .quad 0x0000000000000000 # IST7 486 | .quad 0x0000000000000000 # reserved 487 | .short 0x0000 # reserved 488 | .short 0xffff # iomap base 489 | # === VMM special registers end === 490 | 491 | # ===== VMM stack ===== 492 | .align 1024 493 | .global vmm_stack 494 | vmm_stack: 495 | .space 0x1000*32 496 | .global vmm_stack_end 497 | vmm_stack_end: 498 | # === VMM stack end === 499 | -------------------------------------------------------------------------------- /vmm/src/htvmm.lds: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf64-x86-64) 2 | ENTRY(entry) 3 | 4 | PHDRS { 5 | all PT_LOAD; 6 | } 7 | 8 | base = 0x100000000; 9 | SIZE_HEADER = 0x1000; 10 | 11 | SECTIONS { 12 | . = base; 13 | . += SIZE_HEADER; 14 | .entry : { *(.entry*) } 15 | .text : { *(.text*) } 16 | .rodata : { *(.rodata*) } 17 | .data : { *(.data*) } 18 | .bss : { 19 | . = ALIGN(8); 20 | __bss = .; 21 | *(.bss*) 22 | . = ALIGN(8); 23 | __bss_end = .; 24 | } 25 | } -------------------------------------------------------------------------------- /vmm/src/ioapic.rs: -------------------------------------------------------------------------------- 1 | #[repr(C, packed)] 2 | struct IoApic { 3 | reg: u32, 4 | pad: [u32; 3], 5 | data: u32, 6 | } 7 | 8 | const IOAPIC: *mut IoApic = 0xFEC0_0000 as *mut IoApic; 9 | const REG_TABLE: u32 = 0x10; 10 | const T_IRQ0: u32 = 32; 11 | 12 | pub unsafe fn write(reg: u32, data: u32) { 13 | let ioapic = IoApic { 14 | reg, 15 | pad: [0; 3], 16 | data, 17 | }; 18 | core::ptr::write_volatile::(IOAPIC, ioapic); 19 | } 20 | 21 | pub fn enable(irq: u32, cpunum: u32) { 22 | unsafe { 23 | write(REG_TABLE + 2 * irq, T_IRQ0 + irq); 24 | write(REG_TABLE + 2 * irq + 1, cpunum << 24); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vmm/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(default_alloc_error_handler)] 3 | 4 | mod allocator; 5 | mod arch; 6 | mod cpu; 7 | mod emu; 8 | mod ioapic; 9 | mod serial; 10 | 11 | extern crate alloc; 12 | 13 | use crate::arch::intel::IntelCpu; 14 | use alloc::fmt::format; 15 | use common::{BootArgs, VMM_HEAP_HEAD_VADDR, VMM_HEAP_SIZE}; 16 | use core::{arch::global_asm, panic::PanicInfo, ptr}; 17 | use cpu::Cpu; 18 | use crossbeam::atomic::AtomicCell; 19 | 20 | global_asm!(include_str!("entry.s"), options(att_syntax)); 21 | 22 | pub static BOOT_ARGS: AtomicCell = AtomicCell::new(BootArgs::new()); 23 | pub static UEFI_WRITE_CHAR: AtomicCell = AtomicCell::new(0); 24 | 25 | /// # Safety 26 | /// This function is unsafe. 27 | #[no_mangle] 28 | pub unsafe extern "sysv64" fn vmm_main(boot_args: *const BootArgs) { 29 | clear_bss(); 30 | BOOT_ARGS.store(*boot_args); 31 | UEFI_WRITE_CHAR.store(BOOT_ARGS.load().uefi_write_char); 32 | allocator::init(VMM_HEAP_HEAD_VADDR, VMM_HEAP_SIZE as usize); 33 | serial::init(serial::COM); 34 | 35 | serial_println!("VMM init complete"); 36 | 37 | let mut intel = IntelCpu::new(); 38 | 39 | if let Err(e) = intel.enable_virtualization() { 40 | panic!("failed to enable virtualization: {e:?}"); 41 | } 42 | intel.init_as_bsp(); 43 | intel.run_vm(); 44 | } 45 | 46 | #[panic_handler] 47 | fn panic(info: &PanicInfo) -> ! { 48 | serial_println!("{info:?}"); 49 | loop { 50 | x86_64::instructions::hlt(); 51 | } 52 | } 53 | 54 | extern "C" { 55 | static __bss: u8; 56 | static __bss_end: u8; 57 | // fn call_uefi_write_char(fp: u64, output: u64, c: u32); 58 | } 59 | 60 | unsafe fn clear_bss() { 61 | let bss = &__bss as *const u8 as u64; 62 | let bss_end = &__bss_end as *const u8 as u64; 63 | let start = bss; 64 | let count = (bss_end - bss) / 8; 65 | for i in 0..count { 66 | let dst = (start + i * 8) as *mut u64; 67 | ptr::write_volatile(dst, 0); 68 | } 69 | } 70 | 71 | // fn _print_uefi(args: core::fmt::Arguments) { 72 | // let uefi_write_char = UEFI_WRITE_CHAR.load(); 73 | // let output = BOOT_ARGS.load().uefi_output; 74 | // let s = format(args); 75 | // let s = s.as_str(); 76 | // for c in s.chars() { 77 | // unsafe { 78 | // call_uefi_write_char(uefi_write_char, output, c as u32); 79 | // } 80 | // } 81 | // } 82 | 83 | // #[macro_export] 84 | // macro_rules! uefi_print { 85 | // ($($arg:tt)*) => ($crate::_print_uefi(core::format_args!($($arg)*))); 86 | // } 87 | 88 | // #[macro_export] 89 | // macro_rules! uefi_println { 90 | // () => ($crate::uefi_print!("\r\n")); 91 | // ($($arg:tt)*) => ($crate::_print_uefi(core::format_args!("{}{}", core::format_args!($($arg)*), "\r\n"))); 92 | // } 93 | 94 | fn _print_serial_asm(args: core::fmt::Arguments) { 95 | for c in format(args).as_str().chars() { 96 | unsafe { 97 | use core::arch::asm; 98 | asm!("out dx, al", in("dx") 0x3e8, in("al") c as u8, options(nomem, nostack)); 99 | } 100 | } 101 | } 102 | 103 | fn _print_serial(args: core::fmt::Arguments) { 104 | let s = format(args); 105 | let s = s.as_str(); 106 | for c in s.chars() { 107 | unsafe { 108 | serial::write(serial::COM, c as u8); 109 | } 110 | } 111 | } 112 | 113 | #[macro_export] 114 | macro_rules! serial_print { 115 | ($($arg:tt)*) => ($crate::_print_serial(core::format_args!($($arg)*))); 116 | } 117 | 118 | #[macro_export] 119 | macro_rules! serial_println { 120 | () => ($crate::serial_print!("\r\n")); 121 | ($($arg:tt)*) => ($crate::_print_serial(core::format_args!("{}{}", core::format_args!($($arg)*), "\r\n"))); 122 | } 123 | -------------------------------------------------------------------------------- /vmm/src/serial.rs: -------------------------------------------------------------------------------- 1 | use crate::ioapic; 2 | use x86_64::instructions::{ 3 | interrupts::without_interrupts, 4 | port::{PortReadOnly, PortWriteOnly}, 5 | }; 6 | 7 | pub const COM: u16 = if cfg!(feature = "gpd") { COM2 } else { COM1 }; 8 | pub const COM1: u16 = 0x3f8; 9 | pub const COM2: u16 = 0x2f8; 10 | #[allow(unused)] 11 | pub const COM3: u16 = 0x3e8; 12 | #[allow(unused)] 13 | pub const COM4: u16 = 0x2e8; 14 | 15 | const IRQ: u32 = if COM == COM1 || COM == COM3 { 16 | IRQ4 17 | } else { 18 | IRQ3 19 | }; 20 | const IRQ4: u32 = 4; 21 | const IRQ3: u32 = 3; 22 | 23 | pub unsafe fn init(com: u16) { 24 | without_interrupts(|| { 25 | // 8259 PIC Disable 26 | PortWriteOnly::::new(0xa1).write(0xff); 27 | PortWriteOnly::::new(0x21).write(0xff); 28 | // 16550A UART Enable 29 | PortWriteOnly::::new(com + 1).write(0); // disable all interrupts 30 | PortWriteOnly::::new(com + 3).write(0x80); // DLAB set 1 31 | PortWriteOnly::::new(com).write(1); // 115200 / 115200 32 | PortWriteOnly::::new(com + 1).write(0); // baud rate hi bytes 33 | PortWriteOnly::::new(com + 3).write(0x03); // DLAB set 0 34 | PortWriteOnly::::new(com + 4).write(0x0b); // IRQ enable 35 | PortWriteOnly::::new(com + 1).write(0x01); // interrupt enable 36 | 37 | if PortReadOnly::::new(com + 5).read() == 0xff { 38 | panic!(); 39 | } 40 | 41 | PortReadOnly::::new(com + 2).read(); 42 | PortReadOnly::::new(com).read(); 43 | 44 | ioapic::enable(IRQ, 0); 45 | }); 46 | } 47 | 48 | pub unsafe fn write(com: u16, c: u8) { 49 | while (PortReadOnly::::new(com + 5).read() & 0b10_0000) >> 5 != 1 { 50 | x86_64::instructions::nop(); 51 | } 52 | 53 | PortWriteOnly::::new(com).write(c); 54 | } 55 | --------------------------------------------------------------------------------