├── .gitignore ├── LICENSE ├── Makefile ├── README.md └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.efi 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Takahiro Shinagawa (The University of Tokyo) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = x86_64-w64-mingw32-gcc 2 | CFLAGS = -std=gnu11 -ffreestanding -shared -nostdlib -Wall -Werror \ 3 | -fno-stack-check -fno-stack-protector \ 4 | -mno-stack-arg-probe -mno-red-zone -mno-sse -mno-ms-bitfields \ 5 | -Wl,--subsystem,10 \ 6 | -e EfiMain \ 7 | -O6 8 | 9 | QEMU = qemu-system-x86_64 10 | QEMU_DISK = 'json:{ "fat-type": 0, "dir": "image", "driver": "vvfat", "floppy": false, "rw": true }' 11 | QEMU_OPTS =-nodefaults -machine accel=kvm -cpu host -m 128 -bios OVMF.fd -hda $(QEMU_DISK) -nographic -serial mon:stdio -no-reboot 12 | 13 | NESTED=$(shell cat /sys/module/kvm_intel/parameters/nested) 14 | ifeq ($(NESTED),N) 15 | ENABLE_NESTED=enable_nested 16 | else 17 | ENABLE_NESTED= 18 | endif 19 | 20 | %.efi: %.c 21 | $(CC) $(CFLAGS) $< -o $@ 22 | 23 | .PHONY: all enable_nested disable_nested qemu clean 24 | 25 | 26 | all: main.efi 27 | 28 | qemu: OVMF.fd image/EFI/BOOT/BOOTX64.EFI $(ENABLE_NESTED) 29 | $(QEMU) $(QEMU_OPTS) 30 | 31 | OVMF.fd: 32 | wget http://downloads.sourceforge.net/project/edk2/OVMF/OVMF-X64-r15214.zip 33 | unzip OVMF-X64-r15214.zip OVMF.fd 34 | rm OVMF-X64-r15214.zip 35 | 36 | image/EFI/BOOT/BOOTX64.EFI: main.efi 37 | mkdir -p image/EFI/BOOT 38 | ln -sf ../../../main.efi image/EFI/BOOT/BOOTX64.EFI 39 | 40 | enable_nested: 41 | @echo Enabling nested virtualization in KVM ... 42 | sudo modprobe -r kvm_intel; 43 | sudo modprobe kvm_intel nested=1; 44 | 45 | disable_nested: 46 | @echo Disabling nested virtualization in KVM ... 47 | sudo modprobe -r kvm_intel; 48 | sudo modprobe kvm_intel nested=0; 49 | 50 | clean: 51 | rm -f main.efi OVMF.fd 52 | rm -rf image 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The VMX benchmark (VMXbench) 2 | 3 | VMXbench is a benchmark program written as a UEFI application that measures the number of cycles involved in a VM entry/exit. It currently supports Intel VT-x processors that have the Virtual Machine Extensions (VMX) capability. 4 | 5 | ## Description 6 | 7 | VMXbench measures the number of cycles in a VM exit and entry ten times after warming up, and print the min/max/avg cycles to the console. It is useful for measuring the bare-metal hardware performance of the virtualization technology in different generation processors. It also helps learn the basic usage of Intel VT-x and how to make a UEFI application. 8 | 9 | ## Sample Output 10 | 11 | ``` 12 | Starting VMXbench ... 13 | VMX is supported 14 | Enable VMX 15 | Enable VMX operation 16 | Enter VMX operation 17 | Initialize VMCS 18 | Launch a VM 19 | VM exit[0]: 330, VM entry[0]: 300 20 | VM exit[1]: 330, VM entry[1]: 294 21 | VM exit[2]: 332, VM entry[2]: 292 22 | VM exit[3]: 330, VM entry[3]: 293 23 | VM exit[4]: 330, VM entry[4]: 296 24 | VM exit[5]: 330, VM entry[5]: 298 25 | VM exit[6]: 326, VM entry[6]: 296 26 | VM exit[7]: 330, VM entry[7]: 293 27 | VM exit[8]: 332, VM entry[8]: 290 28 | VM exit[9]: 332, VM entry[9]: 292 29 | VM exit : min = 326, max = 332, avg = 330 30 | VM entry: min = 290, max = 330, avg = 294 31 | Press any key to go back to the UEFI menu 32 | ``` 33 | 34 | ## Build 35 | 36 | You first need to install the mingw cross compiler. 37 | 38 | Ubuntu: `sudo apt-get install gcc-mingw-w64` 39 | 40 | Fedora: `sudo dnf install mingw64-gcc` 41 | 42 | CentOS: `sudo yum install mingw64-gcc` 43 | 44 | If you use a distribution other than the above, find a 64bit mingw cross compiler, and set its name to "CC" in the Makefile. 45 | 46 | Then, type the following command. 47 | 48 | `make` 49 | 50 | ## Test-Run 51 | 52 | Typing the following command will run VMXbench on QEMU. 53 | 54 | `make qemu` 55 | 56 | This will download a UEFI firmware (OVMF-X64-r15214.zip). If you can't download it, find the latest version from http://tianocore.sourceforge.net/wiki/OVMF or in your distribution. 57 | 58 | ## Run 59 | 60 | Copy main.efi into a USB frash drive as \EFI\BOOT\BOOTX64.EFI and boot from the drive. You may need to change the boot order at the boot menu. 61 | 62 | ## Licence 63 | 64 | [The MIT License](http://opensource.org/licenses/MIT) 65 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2017 Takahiro Shinagawa (The University of Tokyo) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | ******************************************************************************/ 26 | 27 | /** *************************************************************************** 28 | * @file main.c 29 | * @brief The VMX benchmark (VMXbench) 30 | * @copyright Copyright (c) 2017 Takahiro Shinagawa (The University of Tokyo) 31 | * @license The MIT License (http://opensource.org/licenses/MIT) 32 | *************************************************************************** */ 33 | 34 | #include 35 | 36 | /** *************************************************************************** 37 | * @section section_uefi Section 1. UEFI definitions 38 | * This section contains several basic UEFI type and function definitions. 39 | *************************************************************************** */ 40 | 41 | #define IN 42 | #define OUT 43 | #define EFIAPI 44 | 45 | typedef unsigned short CHAR16, UINT16; 46 | typedef unsigned long long EFI_STATUS; 47 | typedef void *EFI_HANDLE; 48 | 49 | static const EFI_STATUS EFI_SUCCESS = 0; 50 | static const EFI_STATUS EFI_NOT_READY = 0x8000000000000006; 51 | 52 | struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL; 53 | typedef struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL EFI_SIMPLE_TEXT_INPUT_PROTOCOL; 54 | typedef struct { 55 | UINT16 ScanCode; 56 | CHAR16 UnicodeChar; 57 | } EFI_INPUT_KEY; 58 | typedef 59 | EFI_STATUS 60 | (EFIAPI *EFI_INPUT_READ_KEY) ( 61 | IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, 62 | OUT EFI_INPUT_KEY *Key 63 | ); 64 | struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL { 65 | void *a; 66 | EFI_INPUT_READ_KEY ReadKeyStroke; 67 | }; 68 | 69 | struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL; 70 | typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL; 71 | typedef 72 | EFI_STATUS 73 | (EFIAPI *EFI_TEXT_STRING) ( 74 | IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 75 | IN CHAR16 *String 76 | ); 77 | struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { 78 | void *a; 79 | EFI_TEXT_STRING OutputString; 80 | }; 81 | 82 | typedef struct { 83 | char a[36]; 84 | EFI_HANDLE ConsoleInHandle; 85 | EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; 86 | EFI_HANDLE ConsoleOutHandle; 87 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; 88 | } EFI_SYSTEM_TABLE; 89 | 90 | EFI_SYSTEM_TABLE *SystemTable; 91 | 92 | CHAR16 getwchar() 93 | { 94 | EFI_STATUS status; 95 | EFI_INPUT_KEY key; 96 | 97 | do { 98 | status = SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn, &key); 99 | } while (status == EFI_NOT_READY); 100 | return key.UnicodeChar; 101 | } 102 | 103 | void putws(CHAR16 *str) 104 | { 105 | SystemTable->ConOut->OutputString(SystemTable->ConOut, str); 106 | } 107 | 108 | void putchar_buffered(CHAR16 c) 109 | { 110 | const int BUFSIZE = 1024; 111 | CHAR16 buf[BUFSIZE]; 112 | static int index = 0; 113 | 114 | buf[index++] = c; 115 | if (index == BUFSIZE - 1 || c == L'\n' || c == L'\0') { 116 | buf[index] = L'\0'; 117 | putws(buf); 118 | index = 0; 119 | } 120 | } 121 | 122 | void wprintf (const CHAR16 *format, ...) 123 | { 124 | __builtin_va_list va_list; 125 | __builtin_va_start(va_list, format); 126 | for (CHAR16 c = *format; (c = *format++) != L'\0';) { 127 | if (c != L'%') { 128 | putchar_buffered(c); 129 | continue; 130 | } 131 | 132 | CHAR16 prefix; 133 | c = *format++; 134 | if (c == L'0') { 135 | prefix = L'0'; 136 | c = *format++; 137 | } else 138 | prefix = L' '; 139 | 140 | int len; 141 | if (L'1' <= c && c <= L'9') { 142 | len = c - L'0'; 143 | c = *format++; 144 | } else 145 | len = 1; 146 | 147 | if (L'0' <= c && c <= L'9') { 148 | len = len * 10 + (c - L'0'); 149 | c = *format++; 150 | } 151 | 152 | uint64_t arg = __builtin_va_arg(va_list, uint64_t); 153 | if (c == L's') { 154 | CHAR16 *str = (CHAR16 *)arg; 155 | while (*str != L'\0') 156 | putchar_buffered(*str++); 157 | continue; 158 | } 159 | 160 | int base, digit; 161 | uint64_t divisor; 162 | if (c == L'd') { 163 | base = 10; 164 | digit = 20; 165 | divisor = 10000000000000000000ULL; 166 | } else if (c == L'x') { 167 | base = 16; 168 | digit = 16; 169 | divisor = 0x1000000000000000ULL; 170 | } else 171 | continue; // not supported yet 172 | 173 | int start_output = 0, end_prefix = 0; 174 | for (; digit > 0; digit--) { 175 | int q = arg / divisor; 176 | arg %= divisor; 177 | 178 | CHAR16 c = (q > 9 ? L'a' - 10 : L'0') + q; 179 | if (start_output == 0) 180 | if (c != L'0' || digit <= len) 181 | start_output = 1; 182 | if (start_output == 1) { 183 | if (end_prefix == 0) 184 | if (c != L'0' || digit == 1) 185 | end_prefix = 1; 186 | if (end_prefix == 0) 187 | c = prefix; 188 | putchar_buffered(c); 189 | } 190 | divisor /= base; 191 | } 192 | } 193 | putchar_buffered(L'\0'); 194 | __builtin_va_end(va_list); 195 | } 196 | 197 | /** *************************************************************************** 198 | * @section section_vmx Section 2. VMX definitions 199 | * This section contains several basic VMX function definitions. 200 | *************************************************************************** */ 201 | 202 | static inline uint64_t rdmsr(uint32_t index) 203 | { 204 | uint32_t eax, edx; 205 | asm volatile ("rdmsr" 206 | : "=a" (eax), "=d" (edx) 207 | : "c" (index)); 208 | return ((uint64_t)edx << 32) | eax; 209 | } 210 | 211 | static inline void wrmsr(uint32_t index, uint64_t value) 212 | { 213 | uint32_t eax, edx; 214 | eax = value & 0xffffffff; 215 | edx = value >> 32; 216 | asm volatile ("wrmsr" 217 | : 218 | : "c" (index), "a" (eax), "d" (edx)); 219 | } 220 | 221 | static inline uint32_t vmread(uint32_t index) 222 | { 223 | uint32_t value; 224 | asm volatile ("vmread %%rax, %%rdx" 225 | : "=d" (value) 226 | : "a" (index) 227 | : "cc"); 228 | return value; 229 | } 230 | 231 | static inline void vmwrite(uint32_t index, uint64_t value) 232 | { 233 | asm volatile ("vmwrite %%rdx, %%rax" 234 | : 235 | : "a" (index), "d" (value) 236 | : "cc", "memory"); 237 | } 238 | 239 | static inline uint64_t rdtsc(void) 240 | { 241 | uint32_t eax, edx; 242 | asm volatile ("rdtsc" : "=a"(eax), "=d"(edx)); 243 | return (uint64_t)edx << 32 | (uint64_t)eax; 244 | } 245 | 246 | static inline uint64_t vmcall(uint64_t arg) 247 | { 248 | uint64_t ret; 249 | asm volatile ("vmcall" 250 | : "=a" (ret) 251 | : "c" (arg) 252 | : "memory", "rdx", "r8", "r9", "r10", "r11"); 253 | return ret; 254 | } 255 | 256 | /** *************************************************************************** 257 | * @section section_vmxbench Section 3. VMXbench 258 | * This section contains VMXbench main functions 259 | *************************************************************************** */ 260 | 261 | static int env[28]; 262 | static int index; 263 | static uint64_t tsc_exit[10], tsc_entry[10]; 264 | 265 | void print_results() 266 | { 267 | uint64_t exit_min = UINT64_MAX, entry_min = UINT64_MAX, exit_max = 0, entry_max = 0; 268 | uint64_t exit_avg = 0, entry_avg = 0; 269 | 270 | for (int i = 0; i < 10; i++) { 271 | wprintf(L"VM exit[%d]: %5d, VM entry[%d]: %5d\r\n", i, tsc_exit[i], i, tsc_entry[i]); 272 | if (tsc_exit[i] < exit_min) exit_min = tsc_exit[i]; 273 | if (tsc_exit[i] > exit_max) exit_max = tsc_exit[i]; 274 | exit_avg += tsc_exit[i]; 275 | if (tsc_entry[i] < entry_min) entry_min = tsc_entry[i]; 276 | if (tsc_entry[i] > entry_max) entry_max = tsc_entry[i]; 277 | entry_avg += tsc_entry[i]; 278 | } 279 | wprintf(L"VM exit : min = %5d, max = %5d, avg = %5d\r\n", exit_min, exit_max, exit_avg / 10); 280 | wprintf(L"VM entry: min = %5d, max = %5d, avg = %5d\r\n", entry_min, entry_max, entry_avg / 10); 281 | } 282 | 283 | void print_exitreason(uint64_t reason) 284 | { 285 | uint64_t q = vmread(0x6400); 286 | uint64_t rip = vmread(0x681E); 287 | uint64_t rsp = vmread(0x681C); 288 | wprintf(L"Unexpected VM exit: reason=%x, qualification=%x\r\n", reason, q); 289 | wprintf(L"rip: %08x, rsp: %08x\r\n", rip, rsp); 290 | for (int i = 0; i < 16; i++, rip++) 291 | wprintf(L"%02x ", *(uint8_t *)rip); 292 | wprintf(L"\r\n"); 293 | for (int i = 0; i < 16; i++, rsp += 8) 294 | wprintf(L"%016x: %016x\r\n", rsp, *(uint64_t *)rsp); 295 | wprintf(L"\r\n"); 296 | } 297 | 298 | uint64_t host_entry(uint64_t arg) 299 | { 300 | tsc_exit[index] = rdtsc() - arg; 301 | uint64_t reason = vmread(0x4402); 302 | if (reason == 18) { 303 | if (arg > 0) { 304 | uint64_t rip = vmread(0x681E); // Guest RIP 305 | uint64_t len = vmread(0x440C); // VM-exit instruction length 306 | vmwrite(0x681E, rip + len); 307 | return rdtsc(); 308 | } 309 | print_results(); 310 | } else 311 | print_exitreason(reason); 312 | 313 | __builtin_longjmp(env, 1); 314 | } 315 | 316 | void __host_entry(void); 317 | void _host_entry(void) 318 | { 319 | asm volatile ( 320 | "__host_entry:\n\t" 321 | "call host_entry\n\t" 322 | "vmresume\n\t" 323 | "loop: jmp loop\n\t" 324 | ); 325 | } 326 | 327 | _Noreturn 328 | void guest_entry(void) 329 | { 330 | // warm up 331 | for (int i = 0; i < 10; i++) 332 | vmcall(1); 333 | // benchmark 334 | for (index = 0; index < 10; index++) { 335 | uint64_t tsc; 336 | tsc = vmcall(rdtsc()); 337 | tsc = rdtsc() - tsc; 338 | tsc_entry[index] = tsc; 339 | } 340 | vmcall(0); 341 | while(1); 342 | } 343 | 344 | struct registers { 345 | uint16_t cs, ds, es, fs, gs, ss, tr, ldt; 346 | uint32_t rflags; 347 | uint64_t cr0, cr3, cr4; 348 | uint64_t ia32_efer, ia32_feature_control; 349 | struct { 350 | uint16_t limit; 351 | uint64_t base; 352 | } __attribute__((packed)) gdt, idt; 353 | // attribute "packed" requires -mno-ms-bitfields 354 | }; 355 | 356 | void save_registers(struct registers *regs) 357 | { 358 | asm volatile ("mov %%cr0, %0" : "=r" (regs->cr0)); 359 | asm volatile ("mov %%cr3, %0" : "=r" (regs->cr3)); 360 | asm volatile ("mov %%cr4, %0" : "=r" (regs->cr4)); 361 | regs->ia32_efer = rdmsr(0xC0000080); 362 | asm volatile ("pushf; pop %%rax" : "=a" (regs->rflags)); 363 | asm volatile ("mov %%cs, %0" : "=m" (regs->cs)); 364 | } 365 | 366 | void print_registers(struct registers *regs) 367 | { 368 | wprintf(L"CR0: %016x, CR3: %016x, CR4: %016x\n", regs->cr0, regs->cr3, regs->cr4); 369 | wprintf(L"RFLAGS: %016x\n", regs->rflags); 370 | wprintf(L"CS: %04x\n", regs->cs); 371 | wprintf(L"IA32_EFER: %016x\n", regs->ia32_efer); 372 | wprintf(L"IA32_FEATURE_CONTROL: %016x\n", rdmsr(0x3a)); 373 | } 374 | 375 | char vmxon_region[4096] __attribute__ ((aligned (4096))); 376 | char vmcs[4096] __attribute__ ((aligned (4096))); 377 | char host_stack[4096] __attribute__ ((aligned (4096))); 378 | char guest_stack[4096] __attribute__ ((aligned (4096))); 379 | char tss[4096] __attribute__ ((aligned (4096))); 380 | 381 | 382 | EFI_STATUS 383 | EFIAPI 384 | EfiMain ( 385 | IN EFI_HANDLE ImageHandle, 386 | IN EFI_SYSTEM_TABLE *_SystemTable 387 | ) 388 | { 389 | uint32_t error; 390 | struct registers regs; 391 | 392 | SystemTable = _SystemTable; 393 | wprintf(L"Starting VMXbench ...\r\n"); 394 | 395 | // check the presence of VMX support 396 | uint32_t ecx; 397 | asm volatile ("cpuid" : "=c" (ecx) : "a" (1) : "ebx", "edx"); 398 | if ((ecx & 0x20) == 0) // CPUID.1:ECX.VMX[bit 5] != 1 399 | goto error_vmx_not_supported; 400 | wprintf(L"VMX is supported\r\n"); 401 | 402 | // enable VMX 403 | wprintf(L"Enable VMX\r\n"); 404 | asm volatile ("mov %%cr4, %0" : "=r" (regs.cr4)); 405 | regs.cr4 |= 0x2000; // CR4.VME[bit 13] = 1 406 | asm volatile ("mov %0, %%cr4" :: "r" (regs.cr4)); 407 | 408 | // enable VMX operation 409 | wprintf(L"Enable VMX operation\r\n"); 410 | regs.ia32_feature_control = rdmsr(0x3a); 411 | if ((regs.ia32_feature_control & 0x1) == 0) { 412 | regs.ia32_feature_control |= 0x5; // firmware should set this 413 | wrmsr(0x3a, regs.ia32_feature_control); 414 | } else if ((regs.ia32_feature_control & 0x4) == 0) 415 | goto error_vmx_disabled; 416 | 417 | // apply fixed bits to CR0 & CR4 418 | uint64_t apply_fixed_bits(uint64_t reg, uint32_t fixed0, uint32_t fixed1) 419 | { 420 | reg |= rdmsr(fixed0); 421 | reg &= rdmsr(fixed1); 422 | return reg; 423 | } 424 | asm volatile ("mov %%cr0, %0" : "=r" (regs.cr0)); 425 | regs.cr0 = apply_fixed_bits(regs.cr0, 0x486, 0x487); 426 | asm volatile ("mov %0, %%cr0" :: "r" (regs.cr0)); 427 | asm volatile ("mov %%cr4, %0" : "=r" (regs.cr4)); 428 | regs.cr4 = apply_fixed_bits(regs.cr4, 0x488, 0x489); 429 | asm volatile ("mov %0, %%cr4" :: "r" (regs.cr4)); 430 | 431 | // enter VMX operation 432 | wprintf(L"Enter VMX operation\r\n"); 433 | uint32_t revision_id = rdmsr(0x480); 434 | uint32_t *ptr = (uint32_t *)vmxon_region; 435 | ptr[0] = revision_id; 436 | asm volatile ("vmxon %1" : "=@ccbe" (error) : "m" (ptr)); 437 | if (error) 438 | goto error_vmxon; 439 | 440 | // initialize VMCS 441 | wprintf(L"Initialize VMCS\r\n"); 442 | __builtin_memset(vmcs, 0, 4096); 443 | ptr = (uint32_t *)vmcs; 444 | ptr[0] = revision_id; 445 | asm volatile ("vmclear %1" : "=@ccbe" (error) : "m" (ptr)); 446 | if (error) 447 | goto error_vmclear; 448 | asm volatile ("vmptrld %1" : "=@ccbe" (error) : "m" (ptr)); 449 | if (error) 450 | goto error_vmptrld; 451 | 452 | // initialize control fields 453 | uint32_t apply_allowed_settings(uint32_t value, uint64_t msr_index) 454 | { 455 | uint64_t msr_value = rdmsr(msr_index); 456 | value |= (msr_value & 0xffffffff); 457 | value &= (msr_value >> 32); 458 | return value; 459 | } 460 | uint32_t pinbased_ctls = apply_allowed_settings(0x1e, 0x481); 461 | vmwrite(0x4000, pinbased_ctls); // Pin-based VM-execution controls 462 | uint32_t procbased_ctls = apply_allowed_settings(0x0401e9f2, 0x482); 463 | vmwrite(0x4002, procbased_ctls); // Primary processor-based VM-execution controls 464 | vmwrite(0x4004, 0x0); // Exception bitmap 465 | uint32_t exit_ctls = apply_allowed_settings(0x336fff, 0x483); 466 | vmwrite(0x400c, exit_ctls); // VM-exit controls 467 | uint32_t entry_ctls = apply_allowed_settings(0x93ff, 0x484); 468 | vmwrite(0x4012, entry_ctls); // VM-entry controls 469 | 470 | void vmwrite_gh(uint32_t guest_id, uint32_t host_id, uint64_t value) 471 | { 472 | vmwrite(guest_id, value); 473 | vmwrite(host_id, value); 474 | } 475 | 476 | // 16-Bit Guest and Host State Fields 477 | asm volatile ("mov %%es, %0" : "=m" (regs.es)); 478 | asm volatile ("mov %%cs, %0" : "=m" (regs.cs)); 479 | asm volatile ("mov %%ss, %0" : "=m" (regs.ss)); 480 | asm volatile ("mov %%ds, %0" : "=m" (regs.ds)); 481 | asm volatile ("mov %%fs, %0" : "=m" (regs.fs)); 482 | asm volatile ("mov %%gs, %0" : "=m" (regs.gs)); 483 | asm volatile ("sldt %0" : "=m" (regs.ldt)); 484 | asm volatile ("str %0" : "=m" (regs.tr)); 485 | vmwrite_gh(0x0800, 0x0c00, regs.es); // ES selector 486 | vmwrite_gh(0x0802, 0x0c02, regs.cs); // CS selector 487 | vmwrite_gh(0x0804, 0x0c04, regs.ss); // SS selector 488 | vmwrite_gh(0x0806, 0x0c06, regs.ds); // DS selector 489 | vmwrite_gh(0x0808, 0x0c08, regs.fs); // FS selector 490 | vmwrite_gh(0x080a, 0x0c0a, regs.gs); // GS selector 491 | vmwrite(0x080c, regs.ldt); // Guest LDTR selector 492 | vmwrite_gh(0x080e, 0x0c0c, regs.tr); // TR selector 493 | vmwrite(0x0c0c, 0x08); // dummy TR selector for real hardware 494 | 495 | // 64-Bit Guest and Host State Fields 496 | vmwrite(0x2800, ~0ULL); // VMCS link pointer 497 | // vmwrite(0x2802, 0); // Guest IA32_DEBUGCTL 498 | regs.ia32_efer = rdmsr(0xC0000080); 499 | vmwrite_gh(0x2806, 0x2c02, regs.ia32_efer); // IA32_EFER 500 | 501 | // 32-Bit Guest and Host State Fields 502 | asm volatile ("sgdt %0" : "=m" (regs.gdt)); 503 | asm volatile ("sidt %0" : "=m" (regs.idt)); 504 | uint32_t get_seg_limit(uint32_t selector) 505 | { 506 | uint32_t limit; 507 | asm volatile ("lsl %1, %0" : "=r" (limit) : "r" (selector)); 508 | return limit; 509 | } 510 | vmwrite(0x4800, get_seg_limit(regs.es)); // Guest ES limit 511 | vmwrite(0x4802, get_seg_limit(regs.cs)); // Guest CS limit 512 | vmwrite(0x4804, get_seg_limit(regs.ss)); // Guest SS limit 513 | vmwrite(0x4806, get_seg_limit(regs.ds)); // Guest DS limit 514 | vmwrite(0x4808, get_seg_limit(regs.fs)); // Guest FS limit 515 | vmwrite(0x480a, get_seg_limit(regs.gs)); // Guest GS limit 516 | vmwrite(0x480c, get_seg_limit(regs.ldt)); // Guest LDTR limit 517 | uint32_t tr_limit = get_seg_limit(regs.tr); 518 | if (tr_limit == 0) tr_limit = 0x0000ffff; 519 | vmwrite(0x480e, tr_limit); // Guest TR limit 520 | vmwrite(0x4810, regs.gdt.limit); // Guest GDTR limit 521 | vmwrite(0x4812, regs.idt.limit); // Guest IDTR limit 522 | uint32_t get_seg_access_rights(uint32_t selector) 523 | { 524 | uint32_t access_rights; 525 | asm volatile ("lar %1, %0" : "=r" (access_rights) : "r" (selector)); 526 | return access_rights >> 8; 527 | } 528 | vmwrite(0x4814, get_seg_access_rights(regs.es)); // Guest ES access rights 529 | vmwrite(0x4816, get_seg_access_rights(regs.cs)); // Guest CS access rights 530 | vmwrite(0x4818, get_seg_access_rights(regs.ss)); // Guest SS access rights 531 | vmwrite(0x481a, get_seg_access_rights(regs.ds)); // Guest DS access rights 532 | vmwrite(0x481c, get_seg_access_rights(regs.fs)); // Guest FS access rights 533 | vmwrite(0x481e, get_seg_access_rights(regs.gs)); // Guest GS access rights 534 | uint32_t ldtr_access_rights = get_seg_access_rights(regs.ldt); 535 | if (ldtr_access_rights == 0) ldtr_access_rights = 0x18082; 536 | vmwrite(0x4820, ldtr_access_rights); // Guest LDTR access rights 537 | uint32_t tr_access_rights = get_seg_access_rights(regs.tr); 538 | if (tr_access_rights == 0) tr_access_rights = 0x0808b; 539 | vmwrite(0x4822, tr_access_rights); // Guest TR access rights 540 | 541 | // Natual-Width Control Fields 542 | asm volatile ("mov %%cr3, %0" : "=r" (regs.cr3)); 543 | vmwrite_gh(0x6800, 0x6c00, regs.cr0); 544 | vmwrite_gh(0x6802, 0x6c02, regs.cr3); 545 | vmwrite_gh(0x6804, 0x6c04, regs.cr4); 546 | 547 | uint64_t get_seg_base(uint32_t selector) { return 0; } 548 | vmwrite(0x6806, get_seg_base(regs.es)); // es base 549 | vmwrite(0x6808, get_seg_base(regs.cs)); // cs base 550 | vmwrite(0x680a, get_seg_base(regs.ss)); // ss base 551 | vmwrite(0x680c, get_seg_base(regs.ds)); // ds base 552 | vmwrite(0x680e, get_seg_base(regs.fs)); // fs base 553 | vmwrite(0x6810, get_seg_base(regs.gs)); // gs base 554 | vmwrite(0x6812, get_seg_base(regs.ldt)); // LDTR base 555 | vmwrite(0x6814, (uint64_t)tss); // TR base 556 | 557 | vmwrite_gh(0x6816, 0x6C0C, regs.gdt.base); // GDTR base 558 | vmwrite_gh(0x6818, 0x6C0E, regs.idt.base); // IDT base 559 | 560 | vmwrite(0x6C14, (uint64_t)&host_stack[sizeof(host_stack)]); // HOST_RSP 561 | vmwrite(0x6C16, (uint64_t)__host_entry); // Host RIP 562 | vmwrite(0x681C, (uint64_t)&guest_stack[sizeof(guest_stack)]); // GUEST_RSP 563 | vmwrite(0x681E, (uint64_t)guest_entry); // Guest RIP 564 | asm volatile ("pushf; pop %%rax" : "=a" (regs.rflags)); 565 | regs.rflags &= ~0x200ULL; // clear interrupt enable flag 566 | vmwrite(0x6820, regs.rflags); 567 | 568 | if (!__builtin_setjmp(env)) { 569 | wprintf(L"Launch a VM\r\n"); 570 | asm volatile ("cli"); 571 | asm volatile ("vmlaunch" ::: "memory"); 572 | goto error_vmx; 573 | } else 574 | goto disable_vmx; 575 | 576 | error_vmx: 577 | wprintf(L"VMLAUNCH failed: "); 578 | wprintf(L"Error Number is %d\r\n", vmread(0x4400)); 579 | goto disable_vmx; 580 | 581 | error_vmptrld: 582 | wprintf(L"VMPTRLD failed.\r\n"); 583 | goto disable_vmx; 584 | 585 | error_vmclear: 586 | wprintf(L"VMCLEAR failed.\r\n"); 587 | goto disable_vmx; 588 | 589 | error_vmxon: 590 | wprintf(L"VMXON failed.\r\n"); 591 | goto disable_vmx; 592 | 593 | disable_vmx: 594 | asm volatile ("vmxoff"); 595 | asm volatile ("mov %%cr4, %0" : "=r" (regs.cr4)); 596 | regs.cr4 &= ~0x2000; // CR4.VME[bit 13] = 0 597 | asm volatile ("mov %0, %%cr4" :: "r" (regs.cr4)); 598 | goto exit; 599 | 600 | error_vmx_disabled: 601 | putws(L"VMX is disabled by the firmware\r\n"); 602 | goto exit; 603 | 604 | error_vmx_not_supported: 605 | putws(L"VMX is not supported in this processor\r\n"); 606 | goto exit; 607 | 608 | exit: 609 | putws(L"Press any key to go back to the UEFI menu\r\n"); 610 | getwchar(); 611 | return EFI_SUCCESS; 612 | } 613 | --------------------------------------------------------------------------------