├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── apps ├── CMakeLists.txt ├── basic-demo │ ├── CMakeLists.txt │ ├── README.md │ └── src │ │ └── demo.cpp ├── common │ ├── CMakeLists.txt │ ├── README.md │ ├── include │ │ ├── align_alloc.hpp │ │ ├── print_helpers.hpp │ │ └── utils.hpp │ └── src │ │ ├── align_alloc.cpp │ │ ├── print_helpers.cpp │ │ └── utils.cpp └── x64-guest │ ├── CMakeLists.txt │ ├── README.md │ └── src │ ├── ram.asm │ ├── rom.asm │ └── x64guest.cpp └── cmake └── VSHelpers.cmake /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Visual Studio files 35 | .vs/ 36 | 37 | # CMake build folders 38 | build/ 39 | cmake-build-debug/ 40 | debug-build/ 41 | release-build/ 42 | 43 | # CLion project metadata 44 | .idea/ 45 | 46 | # macOS 47 | .DS_Store 48 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Top-level CMake project definitions. 2 | # ------------------------------------------------------------------------------- 3 | # MIT License 4 | # 5 | # Copyright (c) 2019 Ivan Roberto de Oliveira 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 all 15 | # 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 THE 23 | # SOFTWARE. 24 | cmake_minimum_required (VERSION 3.8) 25 | 26 | project(virt86-demos VERSION 1.0.0) 27 | 28 | # Differentiate between Linux and macOS 29 | if(UNIX AND NOT APPLE) 30 | set(LINUX TRUE) 31 | endif() 32 | 33 | # Define properties and include helpers for Visual Studio 34 | if(MSVC) 35 | include(cmake/VSHelpers.cmake) 36 | 37 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 38 | 39 | # Configure startup project 40 | set_property(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" PROPERTY VS_STARTUP_PROJECT virt86-basic-demo) 41 | endif() 42 | 43 | include(GNUInstallDirs) 44 | 45 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") 46 | 47 | # Require C++17 features 48 | set(CMAKE_CXX_STANDARD 17) 49 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 50 | set(CMAKE_CXX_EXTENSIONS ON) 51 | 52 | # Add apps 53 | add_subdirectory(apps) 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ivan Roberto de Oliveira 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # virt86-demos 2 | 3 | A set of demo applications using [virt86](https://github.com/StrikerX3/virt86). 4 | Each application has its own README.md file describing its purpose. 5 | 6 | ## Building 7 | 8 | virt86-demos can be built with [CMake](https://cmake.org/) 3.8.0 or later. 9 | 10 | You will need to install virt86 before compiling these demos. Follow the instructions on that project's [README.md](https://github.com/StrikerX3/virt86/blob/master/README.md). 11 | 12 | ### Building on Windows with Visual Studio 2017 13 | 14 | To make a Visual Studio 2017 project you'll need to specify the `"Visual Studio 15 2017"` CMake generator (`-G` command line parameter) and a target architecture (`-A`). Use either `Win32` for a 32-bit build or `x64` for a 64-bit build. 15 | 16 | The following commands create and open a Visual Studio 2017 64-bit project: 17 | 18 | ```cmd 19 | git clone https://github.com/StrikerX3/virt86-demos.git 20 | cd virt86-demos 21 | md build && cd build 22 | cmake -G "Visual Studio 15 2017" -A x64 .. 23 | start virt86-demos.sln 24 | ``` 25 | The project can be built directly from the command line with CMake, without opening Visual Studio: 26 | 27 | ```cmd 28 | cd virt86-demos/build 29 | cmake --build . --target ALL_BUILD --config Release -- /nologo /verbosity:minimal /maxcpucount 30 | ``` 31 | 32 | ### Building on Linux with GCC 7+ 33 | 34 | Make sure you have at least GCC 7, `make` and CMake 3.8.0 installed on your system. 35 | 36 | ```bash 37 | git clone https://github.com/StrikerX3/virt86-demos.git 38 | cd virt86-demos 39 | mkdir build; cd build 40 | cmake .. 41 | make 42 | ``` 43 | 44 | ### Building on Mac OS X 45 | 46 | Install [brew](https://brew.sh/) if you haven't already. The default installation is sufficient. Xcode is not needed to compile the applications -- virt86 demos can be compiled with the Xcode command line tools included with brew. 47 | 48 | ```bash 49 | git clone https://github.com/StrikerX3/virt86-demos.git 50 | cd virt86-demos 51 | mkdir build; cd build 52 | cmake .. 53 | make 54 | ``` 55 | 56 | ## Support 57 | 58 | You can support [the author](https://github.com/StrikerX3) on [Patreon](https://www.patreon.com/StrikerX3). 59 | -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Includes all applications. 2 | # ------------------------------------------------------------------------------- 3 | # MIT License 4 | # 5 | # Copyright (c) 2019 Ivan Roberto de Oliveira 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 all 15 | # 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 THE 23 | # SOFTWARE. 24 | add_subdirectory(common) 25 | add_subdirectory(basic-demo) 26 | add_subdirectory(x64-guest) 27 | -------------------------------------------------------------------------------- /apps/basic-demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Basic demo of the virt86 library which tests several basic features of 2 | # virtualization platforms, including some optional features such as guest 3 | # debugging and dirty page tracking. 4 | # ------------------------------------------------------------------------------- 5 | # MIT License 6 | # 7 | # Copyright (c) 2019 Ivan Roberto de Oliveira 8 | # 9 | # Permission is hereby granted, free of charge, to any person obtaining a copy 10 | # of this software and associated documentation files (the "Software"), to deal 11 | # in the Software without restriction, including without limitation the rights 12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | # copies of the Software, and to permit persons to whom the Software is 14 | # furnished to do so, subject to the following conditions: 15 | # 16 | # The above copyright notice and this permission notice shall be included in all 17 | # copies or substantial portions of the Software. 18 | # 19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | # SOFTWARE. 26 | project(virt86-basic-demo VERSION 1.0.0 LANGUAGES CXX) 27 | 28 | include(GNUInstallDirs) 29 | 30 | ############################## 31 | # Source files 32 | # 33 | file(GLOB_RECURSE sources 34 | src/*.cpp 35 | ) 36 | 37 | file(GLOB_RECURSE private_headers 38 | src/*.hpp 39 | src/*.h 40 | ) 41 | 42 | file(GLOB_RECURSE public_headers 43 | include/*.hpp 44 | include/*.h 45 | ) 46 | 47 | ############################## 48 | # Project structure 49 | # 50 | add_executable(virt86-basic-demo ${sources} ${private_headers} ${public_headers}) 51 | 52 | target_include_directories(virt86-basic-demo 53 | PUBLIC 54 | $ 55 | $ 56 | PRIVATE 57 | ${CMAKE_CURRENT_SOURCE_DIR}/src 58 | ) 59 | 60 | find_package(virt86 CONFIG REQUIRED) 61 | target_link_libraries(virt86-basic-demo PUBLIC virt86::virt86) 62 | target_link_libraries(virt86-basic-demo PUBLIC virt86-demo-common) 63 | 64 | if(MSVC) 65 | vs_set_filters(BASE_DIR src FILTER_ROOT "Sources" SOURCES ${sources}) 66 | vs_set_filters(BASE_DIR src FILTER_ROOT "Private Headers" SOURCES ${private_headers}) 67 | vs_set_filters(BASE_DIR include FILTER_ROOT "Public Headers" SOURCES ${public_headers}) 68 | 69 | vs_use_edit_and_continue() 70 | endif() 71 | -------------------------------------------------------------------------------- /apps/basic-demo/README.md: -------------------------------------------------------------------------------- 1 | # Basic demo 2 | 3 | This application demonstrates basic usage of a number of features provided by virt86. 4 | 5 | It selects the first platform available in the system and creates a virtual machine with one processor, 64 KiB of ROM at 0xFFFF0000 and 1 MiB of RAM at 0x00000000. No other hardware is emulated. 6 | 7 | The ROM program initializes the virtual CPU to 32-bit protected mode with paging enabled and executes a series of instructions designed to test several features of the virtualization platform: 8 | - Basic virtual memory support, to ensure the paging setup is correct 9 | - Basic stack tests, for the same purpose 10 | - Software interrupts generated by `INT` instructions in the guest 11 | - Hardware interrupts injected via the hypervisor 12 | - I/O and MMIO 13 | - Guest debugging, including: 14 | - Single stepping 15 | - Software breakpoints 16 | - Hardware breakpoints 17 | - Extended VM exits 18 | - CPUID access 19 | 20 | Other virt86 features tested include: 21 | - Platform features listing 22 | - Dirty page tracking 23 | - Linear address translations 24 | -------------------------------------------------------------------------------- /apps/basic-demo/src/demo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Entry point of the basic demo. 3 | 4 | This program demonstrates basic usage of the library and performs tests on 5 | the functionality of virtualization platforms. 6 | ------------------------------------------------------------------------------- 7 | MIT License 8 | 9 | Copyright (c) 2019 Ivan Roberto de Oliveira 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | #include "virt86/virt86.hpp" 30 | 31 | #include "print_helpers.hpp" 32 | #include "align_alloc.hpp" 33 | #include "utils.hpp" 34 | 35 | #if defined(_WIN32) 36 | # include 37 | #elif defined(__linux__) 38 | # include 39 | #elif defined(__APPLE__) 40 | # include 41 | #endif 42 | 43 | #include 44 | #include 45 | 46 | using namespace virt86; 47 | 48 | // The following flags cause some portions of guest code to be skipped and 49 | // executed on the host by manipulating the virtual processor's registers and 50 | // the guest's physical memory through the hypervisor. 51 | // 52 | // DO_MANUAL_INIT: The GDTR and IDTR are set and the virtual processor is 53 | // initialized to 32-bit protected mode 54 | // DO_MANUAL_JMP: Performs the jump into 32-bit protected mode. 55 | // DO_MANUAL_PAGING: Sets up the PTEs and the CR3 register for paging. 56 | 57 | //#define DO_MANUAL_INIT 58 | //#define DO_MANUAL_JMP 59 | //#define DO_MANUAL_PAGING 60 | 61 | int main() { 62 | // Initialize ROM and RAM 63 | const uint32_t romSize = PAGE_SIZE * 16; // 64 KiB 64 | const uint32_t ramSize = PAGE_SIZE * 256; // 1 MiB 65 | const uint64_t romBase = 0xFFFF0000; 66 | const uint64_t ramBase = 0x0; 67 | 68 | uint8_t *rom = alignedAlloc(romSize); 69 | if (rom == NULL) { 70 | printf("Failed to allocate memory for ROM\n"); 71 | return -1; 72 | } 73 | printf("ROM allocated: %u bytes\n", romSize); 74 | 75 | uint8_t *ram = alignedAlloc(ramSize); 76 | if (ram == NULL) { 77 | printf("Failed to allocate memory for RAM\n"); 78 | return -1; 79 | } 80 | printf("RAM allocated: %u bytes\n", ramSize); 81 | printf("\n"); 82 | 83 | // Fill ROM with HLT instructions 84 | memset(rom, 0xf4, romSize); 85 | 86 | // Zero out RAM 87 | memset(ram, 0, ramSize); 88 | 89 | // Write initialization code to ROM and a simple program to RAM 90 | { 91 | uint32_t addr; 92 | #define emit(buf, code) {memcpy(&buf[addr], code, sizeof(code) - 1); addr += sizeof(code) - 1;} 93 | 94 | // --- Start of ROM code ---------------------------------------------------------------------------------------------- 95 | 96 | // --- GDT and IDT tables --------------------------------------------------------------------------------------------- 97 | 98 | // GDT table 99 | addr = 0x0000; 100 | emit(rom, "\x00\x00\x00\x00\x00\x00\x00\x00"); // [0x0000] GDT entry 0: null 101 | emit(rom, "\xff\xff\x00\x00\x00\x9b\xcf\x00"); // [0x0008] GDT entry 1: code (full access to 4 GB linear space) 102 | emit(rom, "\xff\xff\x00\x00\x00\x93\xcf\x00"); // [0x0010] GDT entry 2: data (full access to 4 GB linear space) 103 | 104 | // IDT table (system) 105 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0018] Vector 0x00: Divide by zero 106 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0020] Vector 0x01: Reserved 107 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0028] Vector 0x02: Non-maskable interrupt 108 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0030] Vector 0x03: Breakpoint (INT3) 109 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0038] Vector 0x04: Overflow (INTO) 110 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0040] Vector 0x05: Bounds range exceeded (BOUND) 111 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0048] Vector 0x06: Invalid opcode (UD2) 112 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0050] Vector 0x07: Device not available (WAIT/FWAIT) 113 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0058] Vector 0x08: Double fault 114 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0060] Vector 0x09: Coprocessor segment overrun 115 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0068] Vector 0x0A: Invalid TSS 116 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0070] Vector 0x0B: Segment not present 117 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0078] Vector 0x0C: Stack-segment fault 118 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0080] Vector 0x0D: General protection fault 119 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0088] Vector 0x0E: Page fault 120 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0090] Vector 0x0F: Reserved 121 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x0098] Vector 0x10: x87 FPU error 122 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x00a0] Vector 0x11: Alignment check 123 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x00a8] Vector 0x12: Machine check 124 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x00b0] Vector 0x13: SIMD Floating-Point Exception 125 | for (uint8_t i = 0x14; i <= 0x1f; i++) { 126 | emit(rom, "\x05\x10\x08\x00\x00\x8f\x00\x10"); // [0x00b8..0x0110] Vector 0x14..0x1F: Reserved 127 | } 128 | 129 | // IDT table (user defined) 130 | emit(rom, "\x00\x10\x08\x00\x00\x8f\x00\x10"); // [0x0118] Vector 0x20: Just IRET 131 | emit(rom, "\x02\x10\x08\x00\x00\x8f\x00\x10"); // [0x0120] Vector 0x21: HLT, then IRET 132 | 133 | // --- 32-bit protected mode ------------------------------------------------------------------------------------------ 134 | 135 | // Prepare memory for paging 136 | // (based on https://github.com/unicorn-engine/unicorn/blob/master/tests/unit/test_x86_soft_paging.c) 137 | // 0x1000 = Page directory 138 | // 0x2000 = Page table (identity map RAM: 0x000xxxxx) 139 | // 0x3000 = Page table (identity map ROM: 0xffffxxxx) 140 | // 0x4000 = Page table (0x10000xxx .. 0x10001xxx -> 0x00005xxx .. 0x00006xxx) 141 | // 0x5000 = Data area (first dword reads 0xdeadbeef) 142 | // 0x6000 = Interrupt handler code area 143 | // 0xe000 = Page table (identity map first page of MMIO: 0xe00000xxx) 144 | 145 | // Load segment registers 146 | addr = 0xff00; 147 | #ifdef DO_MANUAL_PAGING 148 | emit(rom, "\xf4"); // [0xff00] hlt 149 | emit(rom, "\x90"); // [0xff01] nop 150 | #else 151 | emit(rom, "\x33\xc0"); // [0xff00] xor eax, eax 152 | #endif 153 | emit(rom, "\xb0\x10"); // [0xff02] mov al, 0x10 154 | emit(rom, "\x8e\xd8"); // [0xff04] mov ds, eax 155 | emit(rom, "\x8e\xc0"); // [0xff06] mov es, eax 156 | emit(rom, "\x8e\xd0"); // [0xff08] mov ss, eax 157 | 158 | // Clear page directory 159 | emit(rom, "\xbf\x00\x10\x00\x00"); // [0xff0a] mov edi, 0x1000 160 | emit(rom, "\xb9\x00\x10\x00\x00"); // [0xff0f] mov ecx, 0x1000 161 | emit(rom, "\x31\xc0"); // [0xff14] xor eax, eax 162 | emit(rom, "\xf3\xab"); // [0xff16] rep stosd 163 | 164 | // Write 0xdeadbeef at physical memory address 0x5000 165 | emit(rom, "\xbf\x00\x50\x00\x00"); // [0xff18] mov edi, 0x5000 166 | emit(rom, "\xb8\xef\xbe\xad\xde"); // [0xff1d] mov eax, 0xdeadbeef 167 | emit(rom, "\x89\x07"); // [0xff22] mov [edi], eax 168 | 169 | // Identity map the RAM to 0x00000000 170 | emit(rom, "\xb9\x00\x01\x00\x00"); // [0xff24] mov ecx, 0x100 171 | emit(rom, "\xbf\x00\x20\x00\x00"); // [0xff29] mov edi, 0x2000 172 | emit(rom, "\xb8\x03\x00\x00\x00"); // [0xff2e] mov eax, 0x0003 173 | // // aLoop: 174 | emit(rom, "\xab"); // [0xff33] stosd 175 | emit(rom, "\x05\x00\x10\x00\x00"); // [0xff34] add eax, 0x1000 176 | emit(rom, "\xe2\xf8"); // [0xff39] loop aLoop 177 | 178 | // Identity map the ROM 179 | emit(rom, "\xb9\x10\x00\x00\x00"); // [0xff3b] mov ecx, 0x10 180 | emit(rom, "\xbf\xc0\x3f\x00\x00"); // [0xff40] mov edi, 0x3fc0 181 | emit(rom, "\xb8\x03\x00\xff\xff"); // [0xff45] mov eax, 0xffff0003 182 | // // bLoop: 183 | emit(rom, "\xab"); // [0xff4a] stosd 184 | emit(rom, "\x05\x00\x10\x00\x00"); // [0xff4b] add eax, 0x1000 185 | emit(rom, "\xe2\xf8"); // [0xff50] loop bLoop 186 | 187 | // Map physical address 0x5000 to virtual address 0x10000000 188 | emit(rom, "\xbf\x00\x40\x00\x00"); // [0xff52] mov edi, 0x4000 189 | emit(rom, "\xb8\x03\x50\x00\x00"); // [0xff57] mov eax, 0x5003 190 | emit(rom, "\x89\x07"); // [0xff5c] mov [edi], eax 191 | 192 | // Map physical address 0x6000 to virtual address 0x10001000 193 | emit(rom, "\xbf\x04\x40\x00\x00"); // [0xff5e] mov edi, 0x4004 194 | emit(rom, "\xb8\x03\x60\x00\x00"); // [0xff63] mov eax, 0x6003 195 | emit(rom, "\x89\x07"); // [0xff68] mov [edi], eax 196 | 197 | // Map physical address 0xe0000000 to virtual address 0xe0000000 (for MMIO) 198 | emit(rom, "\xbf\x00\xe0\x00\x00"); // [0xff6a] mov edi, 0xe000 199 | emit(rom, "\xb8\x03\x00\x00\xe0"); // [0xff6f] mov eax, 0xe0000003 200 | emit(rom, "\x89\x07"); // [0xff74] mov [edi], eax 201 | 202 | // Add page tables into page directory 203 | emit(rom, "\xbf\x00\x10\x00\x00"); // [0xff76] mov edi, 0x1000 204 | emit(rom, "\xb8\x03\x20\x00\x00"); // [0xff7b] mov eax, 0x2003 205 | emit(rom, "\x89\x07"); // [0xff80] mov [edi], eax 206 | emit(rom, "\xbf\xfc\x1f\x00\x00"); // [0xff82] mov edi, 0x1ffc 207 | emit(rom, "\xb8\x03\x30\x00\x00"); // [0xff87] mov eax, 0x3003 208 | emit(rom, "\x89\x07"); // [0xff8c] mov [edi], eax 209 | emit(rom, "\xbf\x00\x11\x00\x00"); // [0xff8e] mov edi, 0x1100 210 | emit(rom, "\xb8\x03\x40\x00\x00"); // [0xff93] mov eax, 0x4003 211 | emit(rom, "\x89\x07"); // [0xff98] mov [edi], eax 212 | emit(rom, "\xbf\x00\x1e\x00\x00"); // [0xff9a] mov edi, 0x1e00 213 | emit(rom, "\xb8\x03\xe0\x00\x00"); // [0xff9f] mov eax, 0xe003 214 | emit(rom, "\x89\x07"); // [0xffa4] mov [edi], eax 215 | 216 | // Load the page directory register 217 | emit(rom, "\xb8\x00\x10\x00\x00"); // [0xffa6] mov eax, 0x1000 218 | emit(rom, "\x0f\x22\xd8"); // [0xffab] mov cr3, eax 219 | 220 | // Enable paging 221 | emit(rom, "\x0f\x20\xc0"); // [0xffae] mov eax, cr0 222 | emit(rom, "\x0d\x00\x00\x00\x80"); // [0xffb1] or eax, 0x80000000 223 | emit(rom, "\x0f\x22\xc0"); // [0xffb6] mov cr0, eax 224 | 225 | // Clear EAX 226 | emit(rom, "\x31\xc0"); // [0xffb9] xor eax, eax 227 | 228 | // Load using virtual memory address; EAX = 0xdeadbeef 229 | emit(rom, "\xbe\x00\x00\x00\x10"); // [0xffbb] mov esi, 0x10000000 230 | emit(rom, "\x8b\x06"); // [0xffc0] mov eax, [esi] 231 | 232 | // First stop 233 | emit(rom, "\xf4"); // [0xffc2] hlt 234 | 235 | // Jump to RAM 236 | emit(rom, "\xe9\x3c\x00\x00\x10"); // [0xffc3] jmp 0x10000004 237 | // .. ends at 0xffc7 238 | 239 | // --- 16-bit real mode transition to 32-bit protected mode ----------------------------------------------------------- 240 | 241 | // Load GDT and IDT tables 242 | addr = 0xffd0; 243 | emit(rom, "\x66\x2e\x0f\x01\x16\xf2\xff"); // [0xffd0] lgdt [cs:0xfff2] 244 | emit(rom, "\x66\x2e\x0f\x01\x1e\xf8\xff"); // [0xffd7] lidt [cs:0xfff8] 245 | 246 | // Enter protected mode 247 | emit(rom, "\x0f\x20\xc0"); // [0xffde] mov eax, cr0 248 | emit(rom, "\x0c\x01"); // [0xffe1] or al, 1 249 | emit(rom, "\x0f\x22\xc0"); // [0xffe3] mov cr0, eax 250 | #ifdef DO_MANUAL_JMP 251 | emit(rom, "\xf4") // [0xffe6] hlt 252 | // Fill the rest with HLTs 253 | while (addr < 0xfff0) { 254 | emit(rom, "\xf4"); // [0xffe7..0xffef] hlt 255 | } 256 | #else 257 | emit(rom, "\x66\xea\x00\xff\xff\xff\x08\x00"); // [0xffe6] jmp dword 0x8:0xffffff00 258 | emit(rom, "\xf4"); // [0xffef] hlt 259 | #endif 260 | 261 | // --- 16-bit real mode start ----------------------------------------------------------------------------------------- 262 | 263 | // Jump to initialization code and define GDT/IDT table pointer 264 | addr = 0xfff0; 265 | #ifdef DO_MANUAL_INIT 266 | emit(rom, "\xf4"); // [0xfff0] hlt 267 | emit(rom, "\x90"); // [0xfff1] nop 268 | #else 269 | emit(rom, "\xeb\xde"); // [0xfff0] jmp short 0x1d0 270 | #endif 271 | emit(rom, "\x18\x00\x00\x00\xff\xff"); // [0xfff2] GDT pointer: 0xffff0000:0x0018 272 | emit(rom, "\x10\x01\x18\x00\xff\xff"); // [0xfff8] IDT pointer: 0xffff0018:0x0110 273 | 274 | // There's room for two bytes at the end, so let's fill it up with HLTs 275 | emit(rom, "\xf4"); // [0xfffe] hlt 276 | emit(rom, "\xf4"); // [0xffff] hlt 277 | 278 | // --- End of ROM code ------------------------------------------------------------------------------------------------ 279 | 280 | // --- Start of RAM code ---------------------------------------------------------------------------------------------- 281 | addr = 0x5004; // Addresses 0x5000..0x5003 are reserved for 0xdeadbeef 282 | // Note that these addresses are mapped to virtual addresses 0x10000000 through 0x10000fff 283 | 284 | // Do some basic stuff 285 | emit(ram, "\xba\x78\x56\x34\x12"); // [0x5004] mov edx, 0x12345678 286 | emit(ram, "\xbf\x00\x00\x00\x10"); // [0x5009] mov edi, 0x10000000 287 | emit(ram, "\x31\xd0"); // [0x500e] xor eax, edx 288 | emit(ram, "\x89\x07"); // [0x5010] mov [edi], eax 289 | emit(ram, "\xf4"); // [0x5012] hlt 290 | 291 | // Setup a proper stack 292 | emit(ram, "\x31\xed"); // [0x5013] xor ebp, ebp 293 | emit(ram, "\xbc\x00\x00\x10\x00"); // [0x5015] mov esp, 0x100000 294 | 295 | // Test the stack 296 | emit(ram, "\x68\xfe\xca\x0d\xf0"); // [0x501a] push 0xf00dcafe 297 | emit(ram, "\x5a"); // [0x501f] pop edx 298 | emit(ram, "\xf4"); // [0x5020] hlt 299 | 300 | // ------------------------------- 301 | 302 | // Call interrupts 303 | emit(ram, "\xcd\x20"); // [0x5021] int 0x20 304 | emit(ram, "\xcd\x21"); // [0x5023] int 0x21 305 | emit(ram, "\xf4"); // [0x5025] hlt 306 | 307 | // ------------------------------- 308 | 309 | // Basic PMIO 310 | emit(ram, "\x66\xba\x00\x10"); // [0x5026] mov dx, 0x1000 311 | emit(ram, "\xec"); // [0x502a] in al, dx 312 | emit(ram, "\x66\x42"); // [0x502b] inc dx 313 | emit(ram, "\x34\xff"); // [0x502d] xor al, 0xff 314 | emit(ram, "\xee"); // [0x502f] out dx, al 315 | emit(ram, "\x66\x42"); // [0x5030] inc dx 316 | emit(ram, "\x66\xed"); // [0x5032] in ax, dx 317 | emit(ram, "\x66\x42"); // [0x5034] inc dx 318 | emit(ram, "\x66\x83\xf0\xff"); // [0x5036] xor ax, 0xffff 319 | emit(ram, "\x66\xef"); // [0x503a] out dx, ax 320 | emit(ram, "\x66\x42"); // [0x503c] inc dx 321 | emit(ram, "\xed"); // [0x503e] in eax, dx 322 | emit(ram, "\x66\x42"); // [0x503f] inc dx 323 | emit(ram, "\x83\xf0\xff"); // [0x5041] xor eax, 0xffffffff 324 | emit(ram, "\xef"); // [0x5044] out dx, eax 325 | 326 | // ------------------------------- 327 | 328 | // Basic MMIO 329 | emit(ram, "\xbf\x00\x00\x00\xe0"); // [0x5045] mov edi, 0xe0000000 330 | emit(ram, "\x8b\x1f"); // [0x504a] mov ebx, [edi] 331 | emit(ram, "\x83\xc7\x04"); // [0x504c] add edi, 4 332 | emit(ram, "\x89\x1f"); // [0x504f] mov [edi], ebx 333 | 334 | // Advanced MMIO 335 | emit(ram, "\xb9\x00\x00\x00\x10"); // [0x5051] mov ecx, 0x10000000 336 | emit(ram, "\x85\x0f"); // [0x5056] test [edi], ecx 337 | 338 | // ------------------------------- 339 | 340 | // Test single stepping 341 | emit(ram, "\xb9\x11\x00\x00\x00"); // [0x5058] mov ecx, 0x11 342 | emit(ram, "\xb9\x00\x22\x00\x00"); // [0x505d] mov ecx, 0x2200 343 | emit(ram, "\xb9\x00\x00\x33\x00"); // [0x5062] mov ecx, 0x330000 344 | emit(ram, "\xb9\x00\x00\x00\x44"); // [0x5067] mov ecx, 0x44000000 345 | 346 | // ------------------------------- 347 | 348 | // Test software and hardware breakpoints 349 | emit(ram, "\xb9\xff\x00\x00\x00"); // [0x506c] mov ecx, 0xff 350 | emit(ram, "\xb9\x00\xee\x00\x00"); // [0x5071] mov ecx, 0xee00 351 | emit(ram, "\xb9\x00\x00\xdd\x00"); // [0x5076] mov ecx, 0xdd0000 352 | emit(ram, "\xb9\x00\x00\x00\xcc"); // [0x507b] mov ecx, 0xcc000000 353 | emit(ram, "\xb9\xff\xee\xdd\xcc"); // [0x5080] mov ecx, 0xccddeeff 354 | 355 | // ------------------------------- 356 | 357 | // Test CPUID exit 358 | emit(ram, "\x33\xc0"); // [0x5085] xor eax, eax 359 | emit(ram, "\x0f\xa2"); // [0x5087] cpuid 360 | 361 | // Test custom CPUID 362 | emit(ram, "\xb8\x02\x00\x00\x80"); // [0x5089] xor eax, eax 363 | emit(ram, "\x0f\xa2"); // [0x508e] cpuid 364 | emit(ram, "\xf4"); // [0x5090] hlt 365 | 366 | // ------------------------------- 367 | 368 | // End 369 | emit(ram, "\xf4"); // [0x5091] hlt 370 | 371 | // ------------------------------- 372 | 373 | addr = 0x6000; // Interrupt handlers 374 | // Note that these addresses are mapped to virtual addresses 0x10001000 through 0x10001fff 375 | // 0x20: Just IRET 376 | emit(ram, "\xfb"); // [0x6000] sti 377 | emit(ram, "\xcf"); // [0x6001] iretd 378 | 379 | // 0x21: HLT, then IRET 380 | emit(ram, "\xf4"); // [0x6002] hlt 381 | emit(ram, "\xfb"); // [0x6003] sti 382 | emit(ram, "\xcf"); // [0x6004] iretd 383 | 384 | // 0x00 .. 0x1F: Clear stack then IRET 385 | emit(ram, "\x83\xc4\x04"); // [0x6005] add esp, 4 386 | emit(ram, "\xfb"); // [0x6008] sti 387 | emit(ram, "\xcf"); // [0x6009] iretd 388 | 389 | #undef emit 390 | } 391 | 392 | // ----- Hypervisor platform initialization ------------------------------------------------------------------------------- 393 | 394 | printf("virt86 version: " VIRT86_VERSION "\n\n"); 395 | 396 | // Pick the first hypervisor platform that is available and properly initialized on this system. 397 | printf("Loading virtualization platform... "); 398 | 399 | bool foundPlatform = false; 400 | size_t platformIndex = 0; 401 | for (size_t i = 0; i < array_size(PlatformFactories); i++) { 402 | const Platform& platform = PlatformFactories[i](); 403 | if (platform.GetInitStatus() == PlatformInitStatus::OK) { 404 | printf("%s loaded successfully\n", platform.GetName().c_str()); 405 | foundPlatform = true; 406 | platformIndex = i; 407 | break; 408 | } 409 | } 410 | 411 | if (!foundPlatform) { 412 | printf("none found\n"); 413 | return -1; 414 | } 415 | 416 | Platform& platform = PlatformFactories[platformIndex](); 417 | auto& features = platform.GetFeatures(); 418 | 419 | // Print out the host's features 420 | printHostFeatures(); 421 | 422 | // Print out the platform's features 423 | printPlatformFeatures(platform); 424 | 425 | // Create virtual machine 426 | VMSpecifications vmSpecs = { 0 }; 427 | vmSpecs.numProcessors = 1; 428 | vmSpecs.extendedVMExits = ExtendedVMExit::CPUID; 429 | vmSpecs.vmExitCPUIDFunctions.push_back(0); 430 | vmSpecs.CPUIDResults.emplace_back(0x80000002, 'vupc', ' tri', 'UPCV', ' '); 431 | printf("Creating virtual machine... "); 432 | auto opt_vm = platform.CreateVM(vmSpecs); 433 | if (!opt_vm) { 434 | printf("failed\n"); 435 | return -1; 436 | } 437 | printf("succeeded\n"); 438 | VirtualMachine& vm = opt_vm->get(); 439 | 440 | // Map ROM to the top of the 32-bit address range 441 | printf("Mapping ROM... "); 442 | { 443 | auto memMapStatus = vm.MapGuestMemory(romBase, romSize, MemoryFlags::Read | MemoryFlags::Execute, rom); 444 | printMemoryMappingStatus(memMapStatus); 445 | if (memMapStatus != MemoryMappingStatus::OK) return -1; 446 | } 447 | 448 | // Alias ROM to the top of the 31-bit address range if supported 449 | // TODO: test memory aliasing in the virtual machine 450 | if (features.memoryAliasing) { 451 | printf("Mapping ROM alias... "); 452 | auto memMapStatus = vm.MapGuestMemory(romBase >> 1, romSize, MemoryFlags::Read | MemoryFlags::Execute, rom); 453 | printMemoryMappingStatus(memMapStatus); 454 | if (memMapStatus != MemoryMappingStatus::OK) return -1; 455 | } 456 | 457 | // Map RAM to the bottom of the 32-bit address range 458 | printf("Mapping RAM... "); 459 | { 460 | auto memMapStatus = vm.MapGuestMemory(ramBase, ramSize, MemoryFlags::Read | MemoryFlags::Write | MemoryFlags::Execute | MemoryFlags::DirtyPageTracking, ram); 461 | printMemoryMappingStatus(memMapStatus); 462 | if (memMapStatus != MemoryMappingStatus::OK) return -1; 463 | } 464 | 465 | // Get the virtual processor 466 | printf("Retrieving virtual processor... "); 467 | auto opt_vp = vm.GetVirtualProcessor(0); 468 | if (!opt_vp) { 469 | printf("failed\n"); 470 | return -1; 471 | } 472 | auto& vp = opt_vp->get(); 473 | printf("succeeded\n"); 474 | 475 | printf("\nInitial CPU register state:\n"); 476 | printRegs(vp); 477 | printf("\n"); 478 | 479 | VPOperationStatus opStatus; 480 | 481 | #ifdef DO_MANUAL_INIT 482 | { 483 | RegValue cr0, eip; 484 | 485 | vp.RegRead(Reg::CR0, cr0); 486 | vp.RegRead(Reg::EIP, eip); 487 | 488 | // Load GDT table 489 | RegValue gdtr; 490 | gdtr.table.base = romBase; 491 | gdtr.table.limit = 0x0018; 492 | 493 | // Load IDT table 494 | RegValue idtr; 495 | idtr.table.base = romBase + 0x18; 496 | idtr.table.limit = 0x0110; 497 | 498 | // Enter protected mode 499 | cr0.u32 |= CR0_PE; 500 | 501 | // Skip initialization code 502 | eip.u32 = 0xffe6; 503 | 504 | vp.RegWrite(Reg::GDTR, gdtr); 505 | vp.RegWrite(Reg::IDTR, idtr); 506 | 507 | vp.RegWrite(Reg::CR0, cr0); 508 | vp.RegWrite(Reg::EIP, eip); 509 | } 510 | #endif 511 | 512 | // ----- Start of emulation ----------------------------------------------------------------------------------------------- 513 | 514 | printf("Starting tests!\n"); 515 | 516 | // The CPU starts in 16-bit real mode. 517 | // Memory addressing is based on segments and offsets, where a segment is basically a 16-byte offset. 518 | 519 | // On a real application, you should be checking the outcome of register reads and writes. 520 | // We're not going to bother since we know they cannot fail, except for segment registers. 521 | 522 | // Run the CPU! Will stop at the first HLT at ROM address 0xffc2 523 | auto execStatus = vp.Run(); 524 | if (execStatus != VPExecutionStatus::OK) { 525 | printf("VCPU failed to run\n"); 526 | return -1; 527 | } 528 | 529 | printf("\nCPU register state after 16-bit initialization code:\n"); 530 | printRegs(vp); 531 | printf("\n"); 532 | 533 | if (features.partialDirtyBitmap) { 534 | printDirtyBitmap(vm, 0x0, 0x10); 535 | } 536 | else { 537 | printf("Hypervisor does not support reading partial dirty bitmaps\n\n"); 538 | printDirtyBitmap(vm, 0x0, (ramSize + PAGE_SIZE - 1) / PAGE_SIZE); 539 | } 540 | 541 | #ifdef DO_MANUAL_JMP 542 | { 543 | // Do the jmp dword 0x8:0xffffff00 manually 544 | RegValue cs; 545 | if (vp.ReadSegment(0x0008, cs) != VPOperationStatus::OK) { 546 | printf("Failed to load segment data for selector 0x0008\n"); 547 | return -1; 548 | } 549 | opStatus = vp.RegWrite(Reg::CS, cs); 550 | if (opStatus != VPOperationStatus::OK) { 551 | printf("Failed to set CS register\n"); 552 | return -1; 553 | } 554 | vp.RegWrite(Reg::EIP, 0xffffff00); 555 | } 556 | 557 | // Run the CPU again! 558 | execStatus = vp.Run(); 559 | if (execStatus != VPExecutionStatus::OK) { 560 | printf("VCPU failed to run\n"); 561 | return -1; 562 | } 563 | 564 | printf("\nCPU register state after manual jump:\n"); 565 | printRegs(vp); 566 | printf("\n"); 567 | 568 | #endif 569 | 570 | #ifdef DO_MANUAL_PAGING 571 | { 572 | // Prepare the registers 573 | Reg regs[] = { 574 | Reg::EAX, Reg::ESI, Reg::EIP, Reg::CR0, Reg::CR3, 575 | Reg::SS, Reg::DS, Reg::ES, 576 | }; 577 | RegValue values[] = { 578 | 0, 0x10000000, 0xffffffc0, 0xe0000011, 0x1000, 579 | 0x0010, 0x0010, 0x0010, 580 | }; 581 | 582 | for (int i = 5; i < 8; i++) { 583 | if (vp.ReadSegment(0x0010, values[i]) != VPOperationStatus::OK) { 584 | printf("Failed to load segment data for selector 0x0010\n"); 585 | return -1; 586 | } 587 | } 588 | 589 | opStatus = vp.RegWrite(regs, values, array_size(regs)); 590 | if (opStatus != VPOperationStatus::OK) { 591 | printf("Failed to set VCPU registers\n"); 592 | return -1; 593 | } 594 | 595 | // Clear page directory 596 | memset(&ram[0x1000], 0, 0x1000 * sizeof(uint16_t)); 597 | 598 | // Write 0xdeadbeef at physical memory address 0x5000 599 | *(uint32_t *)&ram[0x5000] = 0xdeadbeef; 600 | 601 | // Identity map the RAM to 0x00000000 602 | for (uint32_t i = 0; i < 0x100; i++) { 603 | *(uint32_t *)&ram[0x2000 + i * 4] = 0x0003 + i * 0x1000; 604 | } 605 | 606 | // Identity map the ROM 607 | for (uint32_t i = 0; i < 0x10; i++) { 608 | *(uint32_t *)&ram[0x3fc0 + i * 4] = 0xffff0003 + i * 0x1000; 609 | } 610 | 611 | // Map physical address 0x5000 to virtual address 0x10000000 612 | *(uint32_t *)&ram[0x4000] = 0x5003; 613 | 614 | // Map physical address 0x6000 to virtual address 0x10001000 615 | *(uint32_t *)&ram[0x4004] = 0x6003; 616 | 617 | // Map physical address 0xe0000000 to virtual address 0xe0000000 618 | *(uint32_t *)&ram[0xe000] = 0xe0000003; 619 | 620 | // Add page tables into page directory 621 | *(uint32_t *)&ram[0x1000] = 0x2003; 622 | *(uint32_t *)&ram[0x1ffc] = 0x3003; 623 | *(uint32_t *)&ram[0x1100] = 0x4003; 624 | *(uint32_t *)&ram[0x1e00] = 0xe003; 625 | 626 | // Run the CPU again! 627 | execStatus = vp.Run(); 628 | if (execStatus != VPExecutionStatus::OK) { 629 | printf("VCPU failed to run\n"); 630 | return -1; 631 | } 632 | } 633 | 634 | printf("\nCPU register state after manual paging setup:\n"); 635 | printRegs(vp); 636 | printf("\n"); 637 | 638 | #endif 639 | 640 | // ----- Access data in virtual memory ------------------------------------------------------------------------------------ 641 | 642 | printf("Testing data in virtual memory\n\n"); 643 | 644 | // Validate output at the first stop 645 | auto& exitInfo = vp.GetVMExitInfo(); 646 | { 647 | // Get CPU registers 648 | RegValue cs, eip, eax; 649 | 650 | opStatus = vp.RegRead(Reg::CS, cs); 651 | if (opStatus != VPOperationStatus::OK) { 652 | printf("Failed to read CS register\n"); 653 | return -1; 654 | } 655 | vp.RegRead(Reg::EIP, eip); 656 | vp.RegRead(Reg::EAX, eax); 657 | 658 | // Validate 659 | if (eip.u32 == 0xffffffc3 && cs.u16 == 0x0008) { 660 | printf("Emulation stopped at the right place!\n"); 661 | if (eax.u32 == 0xdeadbeef) { 662 | printf("And we got the right result!\n"); 663 | } 664 | } 665 | } 666 | 667 | printf("\n"); 668 | 669 | // ----- Execute code in virtual memory ----------------------------------------------------------------------------------- 670 | 671 | printf("Testing code in virtual memory\n\n"); 672 | 673 | // Run CPU 674 | execStatus = vp.Run(); 675 | if (execStatus != VPExecutionStatus::OK) { 676 | printf("VCPU failed to run\n"); 677 | return -1; 678 | } 679 | 680 | switch (exitInfo.reason) { 681 | case VMExitReason::HLT: 682 | printf("Emulation exited due to HLT instruction as expected!\n"); 683 | break; 684 | default: 685 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 686 | break; 687 | } 688 | 689 | // Validate output 690 | { 691 | // Get CPU registers 692 | RegValue eip, eax, edx; 693 | 694 | vp.RegRead(Reg::EIP, eip); 695 | vp.RegRead(Reg::EAX, eax); 696 | vp.RegRead(Reg::EDX, edx); 697 | 698 | if (eip.u32 == 0x10000013) { 699 | printf("Emulation stopped at the right place!\n"); 700 | const uint32_t memValue = *(uint32_t *)&ram[0x5000]; 701 | if (eax.u32 == 0xcc99e897 && edx.u32 == 0x12345678 && memValue == 0xcc99e897) { 702 | printf("And we got the right result!\n"); 703 | } 704 | } 705 | } 706 | 707 | printf("\nCPU register state:\n"); 708 | printRegs(vp); 709 | printf("\n"); 710 | 711 | // ----- Stack ------------------------------------------------------------------------------------------------------------ 712 | 713 | printf("Testing the stack\n\n"); 714 | 715 | // Run CPU 716 | execStatus = vp.Run(); 717 | if (execStatus != VPExecutionStatus::OK) { 718 | printf("VCPU failed to run\n"); 719 | return -1; 720 | } 721 | 722 | switch (exitInfo.reason) { 723 | case VMExitReason::HLT: 724 | printf("Emulation exited due to HLT instruction as expected!\n"); 725 | break; 726 | default: 727 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 728 | break; 729 | } 730 | 731 | // Validate stack results 732 | { 733 | RegValue eip, edx, esp; 734 | vp.RegRead(Reg::EIP, eip); 735 | vp.RegRead(Reg::EDX, edx); 736 | vp.RegRead(Reg::ESP, esp); 737 | 738 | if (eip.u32 == 0x10000021) { 739 | printf("Emulation stopped at the right place!\n"); 740 | const uint32_t memValue = *(uint32_t *)&ram[0xffffc]; 741 | if (edx.u32 == 0xf00dcafe && esp.u32 == 0x00100000 && memValue == 0xf00dcafe) { 742 | printf("And we got the right result!\n"); 743 | } 744 | } 745 | } 746 | 747 | printf("\nCPU register state:\n"); 748 | printRegs(vp); 749 | printf("\n"); 750 | 751 | // ----- Interrupts ------------------------------------------------------------------------------------------------------- 752 | 753 | printf("Testing interrupts\n\n"); 754 | 755 | // Run until the HLT inside INT 0x21 756 | execStatus = vp.Run(); 757 | if (execStatus != VPExecutionStatus::OK) { 758 | printf("VCPU failed to run\n"); 759 | return -1; 760 | } 761 | 762 | switch (exitInfo.reason) { 763 | case VMExitReason::HLT: 764 | printf("Emulation exited due to HLT instruction as expected!\n"); 765 | break; 766 | default: 767 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 768 | break; 769 | } 770 | 771 | // Validate registers 772 | { 773 | RegValue eip; 774 | vp.RegRead(Reg::EIP, eip); 775 | 776 | if (eip.u32 == 0x10001003) { 777 | printf("Emulation stopped at the right place!\n"); 778 | } 779 | } 780 | 781 | printf("\nCPU register state:\n"); 782 | printRegs(vp); 783 | printf("\n"); 784 | 785 | 786 | // Now we should leave the interrupt handler and hit the HLT right after INT 0x21 787 | execStatus = vp.Run(); 788 | if (execStatus != VPExecutionStatus::OK) { 789 | printf("VCPU failed to run\n"); 790 | return -1; 791 | } 792 | 793 | switch (exitInfo.reason) { 794 | case VMExitReason::HLT: 795 | printf("Emulation exited due to HLT instruction as expected!\n"); 796 | break; 797 | default: 798 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 799 | break; 800 | } 801 | 802 | // Validate registers 803 | { 804 | RegValue eip; 805 | vp.RegRead(Reg::EIP, eip); 806 | 807 | if (eip.u32 == 0x10000026) { 808 | printf("Emulation stopped at the right place!\n"); 809 | } 810 | } 811 | 812 | printf("\nCPU register state:\n"); 813 | printRegs(vp); 814 | printf("\n"); 815 | 816 | 817 | // Enable interrupts 818 | { 819 | RegValue eflags; 820 | vp.RegRead(Reg::EFLAGS, eflags); 821 | eflags.u32 |= RFLAGS_IF; 822 | vp.RegWrite(Reg::EFLAGS, eflags); 823 | } 824 | 825 | // Inject an INT 0x21 826 | vp.EnqueueInterrupt(0x21); 827 | 828 | // Should hit the HLT in the INT 0x21 handler again 829 | execStatus = vp.Run(); 830 | if (execStatus != VPExecutionStatus::OK) { 831 | printf("VCPU failed to run\n"); 832 | return -1; 833 | } 834 | 835 | // Some hypervisors cause a VM exit due to either having to cancel 836 | // execution of the virtual processor to open a window for interrupt 837 | // injection, or because of the act of requesting an injection window. 838 | if (exitInfo.reason == VMExitReason::Cancelled || exitInfo.reason == VMExitReason::Interrupt) { 839 | printf("Emulation exited to inject an interrupt, continuing execution...\n"); 840 | execStatus = vp.Run(); 841 | if (execStatus != VPExecutionStatus::OK) { 842 | printf("VCPU failed to run\n"); 843 | return -1; 844 | } 845 | } 846 | 847 | switch (exitInfo.reason) { 848 | case VMExitReason::HLT: 849 | printf("Emulation exited due to HLT instruction as expected!\n"); 850 | break; 851 | default: 852 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 853 | break; 854 | } 855 | 856 | // Validate registers 857 | { 858 | RegValue eip; 859 | vp.RegRead(Reg::EIP, eip); 860 | 861 | if (eip.u32 == 0x10001003) { 862 | printf("Emulation stopped at the right place!\n"); 863 | } 864 | } 865 | 866 | printf("\nCPU register state:\n"); 867 | printRegs(vp); 868 | printf("\n"); 869 | 870 | // ----- I/O and MMIO test preparation ------------------------------------------------------------------------------------ 871 | 872 | // NOTE: typically in a program you'd register your own I/O callbacks once, 873 | // but for the purposes of readability we're going to change the callbacks on every test. 874 | 875 | // Define lambdas for unexpected callbacks 876 | const auto unexpectedIORead = [](void *, uint16_t port, size_t size) noexcept -> uint32_t { 877 | printf("** Unexpected I/O read from port 0x%x (%zd bytes)\n", port, size); 878 | return 0; 879 | }; 880 | const auto unexpectedIOWrite = [](void *, uint16_t port, size_t size, uint32_t value) noexcept { 881 | printf("** Unexpected I/O write to port 0x%x (%zd bytes) = 0x%x\n", port, size, value); 882 | }; 883 | const auto unexpectedMMIORead = [](void *, uint64_t address, size_t size) noexcept -> uint64_t { 884 | printf("** Unexpected MMIO read from address 0x%" PRIx64 " (%zd bytes)\n", address, size); 885 | return 0; 886 | }; 887 | const auto unexpectedMMIOWrite = [](void *, uint64_t address, size_t size, uint64_t value) noexcept { 888 | printf("** Unexpected MMIO write to address 0x%" PRIx64 " (%zd bytes)\n = 0x%" PRIx64 "", address, size, value); 889 | }; 890 | 891 | // ----- PIO -------------------------------------------------------------------------------------------------------------- 892 | 893 | printf("Testing PIO\n\n"); 894 | 895 | // Setup I/O callbacks 896 | vm.RegisterIOReadCallback([](void *, uint16_t port, size_t size) noexcept -> uint32_t { 897 | printf("I/O read callback reached!\n"); 898 | if (port == 0x1000 && size == 1) { 899 | printf("And we got the right port and size!\n"); 900 | return 0xac; 901 | } 902 | return 0; 903 | }); 904 | vm.RegisterIOWriteCallback(unexpectedIOWrite); 905 | vm.RegisterMMIOReadCallback(unexpectedMMIORead); 906 | vm.RegisterMMIOWriteCallback(unexpectedMMIOWrite); 907 | 908 | // Run CPU until 8-bit IN 909 | execStatus = vp.Run(); 910 | if (execStatus != VPExecutionStatus::OK) { 911 | printf("VCPU failed to run\n"); 912 | return -1; 913 | } 914 | 915 | switch (exitInfo.reason) { 916 | case VMExitReason::PIO: 917 | printf("Emulation exited due to I/O as expected!\n"); 918 | break; 919 | default: 920 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 921 | break; 922 | } 923 | 924 | printf("\nCPU register state:\n"); 925 | printRegs(vp); 926 | printf("\n"); 927 | 928 | 929 | // Setup I/O callbacks 930 | vm.RegisterIOReadCallback(unexpectedIORead); 931 | vm.RegisterIOWriteCallback([](void *, uint16_t port, size_t size, uint32_t value) noexcept { 932 | printf("I/O write callback reached!\n"); 933 | if (port == 0x1001 && size == 1) { 934 | printf("And we got the right port and size!\n"); 935 | if (value == 0x53) { 936 | printf("And the right result too!\n"); 937 | } 938 | } 939 | }); 940 | vm.RegisterMMIOReadCallback(unexpectedMMIORead); 941 | vm.RegisterMMIOWriteCallback(unexpectedMMIOWrite); 942 | 943 | // Run CPU until 8-bit OUT 944 | execStatus = vp.Run(); 945 | if (execStatus != VPExecutionStatus::OK) { 946 | printf("VCPU failed to run\n"); 947 | return -1; 948 | } 949 | 950 | switch (exitInfo.reason) { 951 | case VMExitReason::PIO: 952 | printf("Emulation exited due to I/O as expected!\n"); 953 | break; 954 | default: 955 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 956 | break; 957 | } 958 | 959 | printf("\nCPU register state:\n"); 960 | printRegs(vp); 961 | printf("\n"); 962 | 963 | 964 | // Setup I/O callbacks 965 | vm.RegisterIOReadCallback([](void *, uint16_t port, size_t size) noexcept -> uint32_t { 966 | printf("I/O read callback reached!\n"); 967 | if (port == 0x1002 && size == 2) { 968 | printf("And we got the right port and size!\n"); 969 | return 0xfade; 970 | } 971 | return 0; 972 | }); 973 | vm.RegisterIOWriteCallback(unexpectedIOWrite); 974 | vm.RegisterMMIOReadCallback(unexpectedMMIORead); 975 | vm.RegisterMMIOWriteCallback(unexpectedMMIOWrite); 976 | 977 | // Run CPU until 16-bit IN 978 | execStatus = vp.Run(); 979 | if (execStatus != VPExecutionStatus::OK) { 980 | printf("VCPU failed to run\n"); 981 | return -1; 982 | } 983 | 984 | switch (exitInfo.reason) { 985 | case VMExitReason::PIO: 986 | printf("Emulation exited due to I/O as expected!\n"); 987 | break; 988 | default: 989 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 990 | break; 991 | } 992 | 993 | printf("\nCPU register state:\n"); 994 | printRegs(vp); 995 | printf("\n"); 996 | 997 | 998 | // Setup I/O callbacks 999 | vm.RegisterIOReadCallback(unexpectedIORead); 1000 | vm.RegisterIOWriteCallback([](void *, uint16_t port, size_t size, uint32_t value) noexcept { 1001 | printf("I/O write callback reached!\n"); 1002 | if (port == 0x1003 && size == 2) { 1003 | printf("And we got the right port and size!\n"); 1004 | if (value == 0x0521) { 1005 | printf("And the right result too!\n"); 1006 | } 1007 | } 1008 | }); 1009 | vm.RegisterMMIOReadCallback(unexpectedMMIORead); 1010 | vm.RegisterMMIOWriteCallback(unexpectedMMIOWrite); 1011 | 1012 | // Run CPU until 16-bit OUT 1013 | execStatus = vp.Run(); 1014 | if (execStatus != VPExecutionStatus::OK) { 1015 | printf("VCPU failed to run\n"); 1016 | return -1; 1017 | } 1018 | 1019 | switch (exitInfo.reason) { 1020 | case VMExitReason::PIO: 1021 | printf("Emulation exited due to I/O as expected!\n"); 1022 | break; 1023 | default: 1024 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1025 | break; 1026 | } 1027 | 1028 | printf("\nCPU register state:\n"); 1029 | printRegs(vp); 1030 | printf("\n"); 1031 | 1032 | 1033 | // Setup I/O callbacks 1034 | vm.RegisterIOReadCallback([](void *, uint16_t port, size_t size) noexcept -> uint32_t { 1035 | printf("I/O read callback reached!\n"); 1036 | if (port == 0x1004 && size == 4) { 1037 | printf("And we got the right port and size!\n"); 1038 | return 0xfeedbabe; 1039 | } 1040 | return 0; 1041 | }); 1042 | vm.RegisterIOWriteCallback(unexpectedIOWrite); 1043 | vm.RegisterMMIOReadCallback(unexpectedMMIORead); 1044 | vm.RegisterMMIOWriteCallback(unexpectedMMIOWrite); 1045 | 1046 | // Run CPU until 32-bit IN 1047 | execStatus = vp.Run(); 1048 | if (execStatus != VPExecutionStatus::OK) { 1049 | printf("VCPU failed to run\n"); 1050 | return -1; 1051 | } 1052 | 1053 | switch (exitInfo.reason) { 1054 | case VMExitReason::PIO: 1055 | printf("Emulation exited due to I/O as expected!\n"); 1056 | break; 1057 | default: 1058 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1059 | break; 1060 | } 1061 | 1062 | printf("\nCPU register state:\n"); 1063 | printRegs(vp); 1064 | printf("\n"); 1065 | 1066 | 1067 | // Setup I/O callbacks 1068 | vm.RegisterIOReadCallback(unexpectedIORead); 1069 | vm.RegisterIOWriteCallback([](void *, uint16_t port, size_t size, uint32_t value) noexcept { 1070 | printf("I/O write callback reached!\n"); 1071 | if (port == 0x1005 && size == 4) { 1072 | printf("And we got the right port and size!\n"); 1073 | if (value == 0x01124541) { 1074 | printf("And the right result too!\n"); 1075 | } 1076 | } 1077 | }); 1078 | vm.RegisterMMIOReadCallback(unexpectedMMIORead); 1079 | vm.RegisterMMIOWriteCallback(unexpectedMMIOWrite); 1080 | 1081 | // Run CPU until 32-bit OUT 1082 | execStatus = vp.Run(); 1083 | if (execStatus != VPExecutionStatus::OK) { 1084 | printf("VCPU failed to run\n"); 1085 | return -1; 1086 | } 1087 | 1088 | switch (exitInfo.reason) { 1089 | case VMExitReason::PIO: 1090 | printf("Emulation exited due to I/O as expected!\n"); 1091 | break; 1092 | default: 1093 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1094 | break; 1095 | } 1096 | 1097 | printf("\nCPU register state:\n"); 1098 | printRegs(vp); 1099 | printf("\n"); 1100 | 1101 | // ----- MMIO ------------------------------------------------------------------------------------------------------------- 1102 | 1103 | printf("Testing MMIO\n\n"); 1104 | 1105 | // Setup I/O callbacks 1106 | vm.RegisterIOReadCallback(unexpectedIORead); 1107 | vm.RegisterIOWriteCallback(unexpectedIOWrite); 1108 | vm.RegisterMMIOReadCallback([](void *, uint64_t address, size_t size) noexcept -> uint64_t { 1109 | printf("MMIO read callback reached!\n"); 1110 | if (address == 0xe0000000 && size == 4) { 1111 | printf("And we got the right address and size!\n"); 1112 | return 0xbaadc0de; 1113 | } 1114 | return 0; 1115 | }); 1116 | vm.RegisterMMIOWriteCallback(unexpectedMMIOWrite); 1117 | 1118 | // Run CPU until the first MMIO 1119 | execStatus = vp.Run(); 1120 | if (execStatus != VPExecutionStatus::OK) { 1121 | printf("VCPU failed to run\n"); 1122 | return -1; 1123 | } 1124 | 1125 | switch (exitInfo.reason) { 1126 | case VMExitReason::MMIO: 1127 | printf("Emulation exited due to MMIO as expected!\n"); 1128 | break; 1129 | default: 1130 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1131 | break; 1132 | } 1133 | 1134 | printf("\nCPU register state:\n"); 1135 | printRegs(vp); 1136 | printf("\n"); 1137 | 1138 | 1139 | // Setup I/O callbacks 1140 | vm.RegisterIOReadCallback(unexpectedIORead); 1141 | vm.RegisterIOWriteCallback(unexpectedIOWrite); 1142 | vm.RegisterMMIOReadCallback(unexpectedMMIORead); 1143 | vm.RegisterMMIOWriteCallback([](void *, uint64_t address, size_t size, uint64_t value) noexcept { 1144 | printf("MMIO write callback reached!\n"); 1145 | if (address == 0xe0000004 && size == 4) { 1146 | printf("And we got the right address and size!\n"); 1147 | if (value == 0xbaadc0de) { 1148 | printf("And the right value too!\n"); 1149 | } 1150 | } 1151 | }); 1152 | 1153 | // Will now hit the MMIO read 1154 | execStatus = vp.Run(); 1155 | if (execStatus != VPExecutionStatus::OK) { 1156 | printf("VCPU failed to run\n"); 1157 | return -1; 1158 | } 1159 | 1160 | switch (exitInfo.reason) { 1161 | case VMExitReason::MMIO: 1162 | printf("Emulation exited due to MMIO as expected!\n"); 1163 | break; 1164 | default: 1165 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1166 | break; 1167 | } 1168 | 1169 | printf("\nCPU register state:\n"); 1170 | printRegs(vp); 1171 | printf("\n"); 1172 | 1173 | 1174 | // Setup I/O callbacks 1175 | vm.RegisterIOReadCallback(unexpectedIORead); 1176 | vm.RegisterIOWriteCallback(unexpectedIOWrite); 1177 | vm.RegisterMMIOReadCallback([](void *, uint64_t address, size_t size) noexcept -> uint64_t { 1178 | printf("MMIO read callback reached!\n"); 1179 | if (address == 0xe0000004 && size == 4) { 1180 | printf("And we got the right address and size!\n"); 1181 | return 0xdeadc0de; 1182 | } 1183 | return 0; 1184 | }); 1185 | vm.RegisterMMIOWriteCallback([](void *, uint64_t address, size_t size, uint64_t value) noexcept { 1186 | printf("MMIO write callback reached!\n"); 1187 | if (address == 0xe0000004 && size == 4) { 1188 | printf("And we got the right address and size!\n"); 1189 | if (value == 0xdeadc0de) { 1190 | printf("And the right value too!\n"); 1191 | } 1192 | } 1193 | }); 1194 | 1195 | // Will now hit the first part of TEST instruction with MMIO address 1196 | execStatus = vp.Run(); 1197 | if (execStatus != VPExecutionStatus::OK) { 1198 | printf("VCPU failed to run\n"); 1199 | return -1; 1200 | } 1201 | 1202 | switch (exitInfo.reason) { 1203 | case VMExitReason::MMIO: 1204 | printf("Emulation exited due to MMIO as expected!\n"); 1205 | break; 1206 | default: 1207 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1208 | break; 1209 | } 1210 | 1211 | printf("\nCPU register state:\n"); 1212 | printRegs(vp); 1213 | printf("\n"); 1214 | 1215 | // Some platforms require multiple executions to complete an emulated MMIO instruction 1216 | if (features.partialMMIOInstructions) { 1217 | printf("Hypervisor instruction emulator executes MMIO instructions partially, continuing execution...\n\n"); 1218 | 1219 | execStatus = vp.Run(); 1220 | if (execStatus != VPExecutionStatus::OK) { 1221 | printf("VCPU failed to run\n"); 1222 | return -1; 1223 | } 1224 | 1225 | switch (exitInfo.reason) { 1226 | case VMExitReason::MMIO: 1227 | printf("Emulation exited due to MMIO as expected!\n"); 1228 | break; 1229 | default: 1230 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1231 | break; 1232 | } 1233 | 1234 | printf("\nCPU register state:\n"); 1235 | printRegs(vp); 1236 | printf("\n"); 1237 | } 1238 | 1239 | // ----- Guest debugging -------------------------------------------------------------------------------------------------- 1240 | 1241 | if (!features.guestDebugging) { 1242 | printf("Guest debugging not supported by the platform, skipping tests\n"); 1243 | vp.RegWrite(Reg::EIP, 0x10000085); 1244 | } 1245 | else { 1246 | // ----- Single stepping ---------------------------------------------------------------------------------------------- 1247 | 1248 | printf("Testing single stepping\n\n"); 1249 | 1250 | // Step CPU 1251 | execStatus = vp.Step(); 1252 | if (execStatus != VPExecutionStatus::OK) { 1253 | printf("VCPU failed to step\n"); 1254 | return -1; 1255 | } 1256 | 1257 | // Some hypervisors may not step forward after completing the complex 1258 | // MMIO instruction from the previous test. Check if that's the case by 1259 | // looking at EIP 1260 | { 1261 | RegValue eip; 1262 | vp.RegRead(Reg::EIP, eip); 1263 | if (eip.u32 == 0x10000058) { 1264 | printf("Hypervisor does not complete complex MMIO instruction on execution, stepping again\n"); 1265 | execStatus = vp.Step(); 1266 | if (execStatus != VPExecutionStatus::OK) { 1267 | printf("VCPU failed to step\n"); 1268 | return -1; 1269 | } 1270 | } 1271 | } 1272 | 1273 | switch (exitInfo.reason) { 1274 | case VMExitReason::Step: 1275 | { 1276 | printf("Emulation exited due to single stepping as expected!\n"); 1277 | 1278 | RegValue eip, ecx; 1279 | RegValue dr6, dr7; 1280 | 1281 | vp.RegRead(Reg::EIP, eip); 1282 | vp.RegRead(Reg::ECX, ecx); 1283 | vp.RegRead(Reg::DR6, dr6); 1284 | vp.RegRead(Reg::DR7, dr7); 1285 | 1286 | if (eip.u32 == 0x1000005d) { 1287 | printf("And stopped at the right place!\n"); 1288 | } 1289 | if (ecx.u32 == 0x11) { 1290 | printf("And got the right result!\n"); 1291 | } 1292 | break; 1293 | } 1294 | default: 1295 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1296 | break; 1297 | } 1298 | 1299 | printf("\nCPU register state:\n"); 1300 | printRegs(vp); 1301 | printf("\n"); 1302 | 1303 | 1304 | // Step CPU 1305 | execStatus = vp.Step(); 1306 | if (execStatus != VPExecutionStatus::OK) { 1307 | printf("VCPU failed to step\n"); 1308 | return -1; 1309 | } 1310 | 1311 | switch (exitInfo.reason) { 1312 | case VMExitReason::Step: 1313 | { 1314 | printf("Emulation exited due to single stepping as expected!\n"); 1315 | 1316 | RegValue eip, ecx; 1317 | RegValue dr6, dr7; 1318 | 1319 | vp.RegRead(Reg::EIP, eip); 1320 | vp.RegRead(Reg::ECX, ecx); 1321 | vp.RegRead(Reg::DR6, dr6); 1322 | vp.RegRead(Reg::DR7, dr7); 1323 | 1324 | if (eip.u32 == 0x10000062) { 1325 | printf("And stopped at the right place!\n"); 1326 | } 1327 | if (ecx.u32 == 0x2200) { 1328 | printf("And got the right result!\n"); 1329 | } 1330 | break; 1331 | } 1332 | default: 1333 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1334 | break; 1335 | } 1336 | 1337 | printf("\nCPU register state:\n"); 1338 | printRegs(vp); 1339 | printf("\n"); 1340 | 1341 | 1342 | // Step CPU 1343 | execStatus = vp.Step(); 1344 | if (execStatus != VPExecutionStatus::OK) { 1345 | printf("VCPU failed to step\n"); 1346 | return -1; 1347 | } 1348 | 1349 | switch (exitInfo.reason) { 1350 | case VMExitReason::Step: 1351 | { 1352 | printf("Emulation exited due to single stepping as expected!\n"); 1353 | 1354 | RegValue eip, ecx; 1355 | RegValue dr6, dr7; 1356 | 1357 | vp.RegRead(Reg::EIP, eip); 1358 | vp.RegRead(Reg::ECX, ecx); 1359 | vp.RegRead(Reg::DR6, dr6); 1360 | vp.RegRead(Reg::DR7, dr7); 1361 | 1362 | if (eip.u32 == 0x10000067) { 1363 | printf("And stopped at the right place!\n"); 1364 | } 1365 | if (ecx.u32 == 0x330000) { 1366 | printf("And got the right result!\n"); 1367 | } 1368 | break; 1369 | } 1370 | default: 1371 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1372 | break; 1373 | } 1374 | 1375 | printf("\nCPU register state:\n"); 1376 | printRegs(vp); 1377 | printf("\n"); 1378 | 1379 | 1380 | // Step CPU 1381 | execStatus = vp.Step(); 1382 | if (execStatus != VPExecutionStatus::OK) { 1383 | printf("VCPU failed to step\n"); 1384 | return -1; 1385 | } 1386 | 1387 | switch (exitInfo.reason) { 1388 | case VMExitReason::Step: 1389 | { 1390 | printf("Emulation exited due to single stepping as expected!\n"); 1391 | 1392 | RegValue eip, ecx; 1393 | RegValue dr6, dr7; 1394 | 1395 | vp.RegRead(Reg::EIP, eip); 1396 | vp.RegRead(Reg::ECX, ecx); 1397 | vp.RegRead(Reg::DR6, dr6); 1398 | vp.RegRead(Reg::DR7, dr7); 1399 | 1400 | if (eip.u32 == 0x1000006c) { 1401 | printf("And stopped at the right place!\n"); 1402 | } 1403 | if (ecx.u32 == 0x44000000) { 1404 | printf("And got the right result!\n"); 1405 | } 1406 | break; 1407 | } 1408 | default: 1409 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1410 | break; 1411 | } 1412 | 1413 | printf("\nCPU register state:\n"); 1414 | printRegs(vp); 1415 | printf("\n"); 1416 | 1417 | // ----- Software breakpoints ----------------------------------------------------------------------------------------- 1418 | 1419 | // Enable software breakpoints and place a breakpoint 1420 | opStatus = vp.EnableSoftwareBreakpoints(true); 1421 | if (opStatus != VPOperationStatus::OK) { 1422 | printf("Failed to enable software breakpoints\n"); 1423 | return -1; 1424 | } 1425 | const uint8_t swBpBackup = ram[0x5071]; 1426 | ram[0x5071] = 0xCC; 1427 | 1428 | // Run CPU. Should hit the breakpoint 1429 | execStatus = vp.Run(); 1430 | if (execStatus != VPExecutionStatus::OK) { 1431 | printf("VCPU failed to run\n"); 1432 | return -1; 1433 | } 1434 | 1435 | switch (exitInfo.reason) { 1436 | case VMExitReason::SoftwareBreakpoint: 1437 | { 1438 | RegValue eip, ecx; 1439 | RegValue dr6, dr7; 1440 | uint64_t bpAddr; 1441 | 1442 | vp.RegRead(Reg::EIP, eip); 1443 | vp.RegRead(Reg::ECX, ecx); 1444 | vp.RegRead(Reg::DR6, dr6); 1445 | vp.RegRead(Reg::DR7, dr7); 1446 | 1447 | vp.GetBreakpointAddress(&bpAddr); 1448 | 1449 | printf("Emulation exited due to software breakpoint as expected!\n"); 1450 | if (bpAddr == 0x10000071) { 1451 | printf("And triggered the correct breakpoint!\n"); 1452 | } 1453 | if (eip.u32 == 0x10000071) { 1454 | printf("And stopped at the right place!\n"); 1455 | } 1456 | if (ecx.u32 == 0x000000ff) { 1457 | printf("And got the right result!\n"); 1458 | } 1459 | break; 1460 | } 1461 | default: 1462 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1463 | break; 1464 | } 1465 | 1466 | printf("\nCPU register state:\n"); 1467 | printRegs(vp); 1468 | printf("\n"); 1469 | 1470 | 1471 | // Disable software breakpoints and revert instruction 1472 | opStatus = vp.EnableSoftwareBreakpoints(false); 1473 | if (opStatus != VPOperationStatus::OK) { 1474 | printf("Failed to disable software breakpoints\n"); 1475 | return -1; 1476 | } 1477 | ram[0x5071] = swBpBackup; 1478 | 1479 | // ----- Hardware breakpoints ----------------------------------------------------------------------------------------- 1480 | 1481 | // Place hardware breakpoint 1482 | HardwareBreakpoints bps = { 0 }; 1483 | bps.bp[0].address = 0x1000007b; 1484 | bps.bp[0].localEnable = true; 1485 | bps.bp[0].globalEnable = false; 1486 | bps.bp[0].trigger = HardwareBreakpointTrigger::Execution; 1487 | bps.bp[0].length = HardwareBreakpointLength::Byte; 1488 | opStatus = vp.SetHardwareBreakpoints(bps); 1489 | if (opStatus != VPOperationStatus::OK) { 1490 | printf("Failed to set hardware breakpoint\n"); 1491 | return -1; 1492 | } 1493 | 1494 | // Run CPU. Should hit the breakpoint 1495 | execStatus = vp.Run(); 1496 | if (execStatus != VPExecutionStatus::OK) { 1497 | printf("VCPU failed to run\n"); 1498 | return -1; 1499 | } 1500 | 1501 | switch (exitInfo.reason) { 1502 | case VMExitReason::HardwareBreakpoint: 1503 | { 1504 | printf("Emulation exited due to hardware breakpoint as expected!\n"); 1505 | RegValue eip, ecx, dr6, dr7; 1506 | 1507 | vp.RegRead(Reg::EIP, eip); 1508 | vp.RegRead(Reg::ECX, ecx); 1509 | vp.RegRead(Reg::DR6, dr6); 1510 | vp.RegRead(Reg::DR7, dr7); 1511 | 1512 | if (dr6.u32 == 1) { 1513 | printf("And triggered the correct breakpoint!\n"); 1514 | } 1515 | if (eip.u32 == 0x1000007b) { 1516 | printf("And stopped at the right place!\n"); 1517 | } 1518 | if (ecx.u32 == 0x00dd0000) { 1519 | printf("And got the right result!\n"); 1520 | } 1521 | break; 1522 | } 1523 | default: 1524 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1525 | break; 1526 | } 1527 | 1528 | // Clear hardware breakpoints 1529 | opStatus = vp.ClearHardwareBreakpoints(); 1530 | if (opStatus != VPOperationStatus::OK) { 1531 | printf("Could not clear hardware breakpoints\n"); 1532 | } 1533 | printf("\nHardware breakpoints cleared\n"); 1534 | } 1535 | 1536 | printf("\nCPU register state:\n"); 1537 | printRegs(vp); 1538 | printf("\n"); 1539 | 1540 | // ----- Extended VM exit: CPUID ------------------------------------------------------------------------------------------ 1541 | 1542 | const auto extVMExits = BitmaskEnum(features.extendedVMExits); 1543 | if (extVMExits.NoneOf(ExtendedVMExit::CPUID)) { 1544 | printf("Extended VM exit on CPUID instruction not supported by the platform, skipping test\n"); 1545 | vp.RegWrite(Reg::EIP, 0x10000091); 1546 | } 1547 | else { 1548 | printf("Testing extended VM exit: CPUID instruction\n\n"); 1549 | 1550 | // Run CPU. Should hit the CPUID and exit with the correct result 1551 | execStatus = vp.Run(); 1552 | if (execStatus != VPExecutionStatus::OK) { 1553 | printf("VCPU failed to run\n"); 1554 | return -1; 1555 | } 1556 | 1557 | switch (exitInfo.reason) { 1558 | case VMExitReason::CPUID: 1559 | { 1560 | printf("Emulation exited due to CPUID instruction as expected!\n"); 1561 | 1562 | RegValue eax; 1563 | vp.RegRead(Reg::EAX, eax); 1564 | if (eax.u32 == 0) { 1565 | printf("And we got the correct function!\n"); 1566 | 1567 | vp.RegWrite(Reg::EAX, 0x80000008); 1568 | vp.RegWrite(Reg::EBX, 'vuoc'); 1569 | vp.RegWrite(Reg::ECX, 'Rtri'); 1570 | vp.RegWrite(Reg::EDX, 'SKCO'); 1571 | } 1572 | break; 1573 | } 1574 | default: 1575 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1576 | break; 1577 | } 1578 | 1579 | printf("\nCPU register state:\n"); 1580 | printRegs(vp); 1581 | printf("\n"); 1582 | 1583 | 1584 | // Should hit the next CPUID with function 0x800000002 then stop at the 1585 | // following HLT 1586 | execStatus = vp.Run(); 1587 | if (execStatus != VPExecutionStatus::OK) { 1588 | printf("VCPU failed to run\n"); 1589 | return -1; 1590 | } 1591 | 1592 | switch (exitInfo.reason) { 1593 | case VMExitReason::HLT: 1594 | { 1595 | printf("Emulation exited due to HLT instruction as expected!\n"); 1596 | 1597 | RegValue eax, ebx, ecx, edx; 1598 | vp.RegRead(Reg::EAX, eax); 1599 | vp.RegRead(Reg::EBX, ebx); 1600 | vp.RegRead(Reg::ECX, ecx); 1601 | vp.RegRead(Reg::EDX, edx); 1602 | if (eax.u32 == 'vupc' && ebx.u32 == ' tri' && ecx.u32 == 'UPCV' && edx.u32 == ' ') { 1603 | printf("And we got the correct results!\n"); 1604 | } 1605 | else if (features.customCPUIDs) { 1606 | printf("Custom CPUID results unsupported by the hypervisor\n"); 1607 | } 1608 | break; 1609 | } 1610 | default: 1611 | printf("Emulation exited for another reason: %s\n", reason_str(exitInfo.reason)); 1612 | break; 1613 | } 1614 | } 1615 | 1616 | printf("\nCPU register state:\n"); 1617 | printRegs(vp); 1618 | printf("\n"); 1619 | 1620 | // ----- End of the program ----------------------------------------------------------------------------------------------- 1621 | 1622 | // Run CPU. Will stop at the last HLT instruction 1623 | execStatus = vp.Run(); 1624 | if (execStatus != VPExecutionStatus::OK) { 1625 | printf("VCPU failed to run\n"); 1626 | return -1; 1627 | } 1628 | 1629 | // Validate 1630 | { 1631 | RegValue eip; 1632 | vp.RegRead(Reg::EIP, eip); 1633 | if (eip.u32 == 0x10000092) { 1634 | printf("Emulation stopped at the right place!\n"); 1635 | } 1636 | } 1637 | 1638 | printf("\nFinal CPU register state:\n"); 1639 | printRegs(vp); 1640 | printSTRegs(vp); 1641 | printMMRegs(vp, MMFormat::I16); 1642 | printMXCSRRegs(vp); 1643 | printXMMRegs(vp, XMMFormat::IF32); 1644 | printYMMRegs(vp, XMMFormat::IF64); 1645 | printZMMRegs(vp, XMMFormat::IF64); 1646 | printf("\n"); 1647 | 1648 | // ----- Linear memory address translation -------------------------------------------------------------------------------- 1649 | 1650 | printf("Linear memory address translations:\n"); 1651 | printAddressTranslation(vp, 0x00000000); 1652 | printAddressTranslation(vp, 0x00001000); 1653 | printAddressTranslation(vp, 0x00010000); 1654 | printAddressTranslation(vp, 0x10000000); 1655 | printAddressTranslation(vp, 0x10001000); 1656 | printAddressTranslation(vp, 0xe0000000); 1657 | printAddressTranslation(vp, 0xffffe000); 1658 | printAddressTranslation(vp, 0xfffff000); 1659 | 1660 | // ----- Cleanup ---------------------------------------------------------------------------------------------------------- 1661 | 1662 | printf("\n"); 1663 | 1664 | // Free VM 1665 | printf("Releasing VM... "); 1666 | if (platform.FreeVM(vm)) { 1667 | printf("succeeded\n"); 1668 | } 1669 | else { 1670 | printf("failed\n"); 1671 | } 1672 | 1673 | // Free RAM 1674 | if (alignedFree(ram)) { 1675 | printf("RAM freed\n"); 1676 | } 1677 | else { 1678 | printf("Failed to free RAM\n"); 1679 | } 1680 | 1681 | // Free ROM 1682 | if (alignedFree(rom)) { 1683 | printf("ROM freed\n"); 1684 | } 1685 | else { 1686 | printf("Failed to free ROM\n"); 1687 | } 1688 | 1689 | return 0; 1690 | } 1691 | -------------------------------------------------------------------------------- /apps/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Common code used by the demonstration applications. 2 | # ------------------------------------------------------------------------------- 3 | # MIT License 4 | # 5 | # Copyright (c) 2019 Ivan Roberto de Oliveira 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 all 15 | # 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 THE 23 | # SOFTWARE. 24 | project(virt86-demo-common VERSION 1.0.0 LANGUAGES CXX) 25 | 26 | include(GNUInstallDirs) 27 | 28 | ############################## 29 | # Source files 30 | # 31 | file(GLOB_RECURSE sources 32 | src/*.cpp 33 | ) 34 | 35 | file(GLOB_RECURSE private_headers 36 | src/*.hpp 37 | src/*.h 38 | ) 39 | 40 | file(GLOB_RECURSE public_headers 41 | include/*.hpp 42 | include/*.h 43 | ) 44 | 45 | ############################## 46 | # Project structure 47 | # 48 | add_library(virt86-demo-common STATIC ${sources} ${private_headers} ${public_headers}) 49 | 50 | target_include_directories(virt86-demo-common 51 | PUBLIC 52 | $ 53 | $ 54 | PRIVATE 55 | ${CMAKE_CURRENT_SOURCE_DIR}/src 56 | ) 57 | 58 | find_package(virt86 CONFIG REQUIRED) 59 | target_link_libraries(virt86-demo-common PUBLIC virt86::virt86) 60 | 61 | if(MSVC) 62 | vs_set_filters(BASE_DIR src FILTER_ROOT "Sources" SOURCES ${sources}) 63 | vs_set_filters(BASE_DIR src FILTER_ROOT "Private Headers" SOURCES ${private_headers}) 64 | vs_set_filters(BASE_DIR include FILTER_ROOT "Public Headers" SOURCES ${public_headers}) 65 | 66 | vs_use_edit_and_continue() 67 | endif() 68 | -------------------------------------------------------------------------------- /apps/common/README.md: -------------------------------------------------------------------------------- 1 | # Common demo code 2 | 3 | This static library contains code shared by the virt86 demo applications. 4 | -------------------------------------------------------------------------------- /apps/common/include/align_alloc.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Declares cross-platform functions for allocating and freeing aligned memory. 3 | ------------------------------------------------------------------------------- 4 | MIT License 5 | 6 | Copyright (c) 2019 Ivan Roberto de Oliveira 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | #pragma once 27 | 28 | #include 29 | #include 30 | 31 | 32 | uint8_t *alignedAlloc(const size_t size) noexcept; 33 | bool alignedFree(void *memory) noexcept; 34 | -------------------------------------------------------------------------------- /apps/common/include/print_helpers.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Declares helper functions that print out formatted register values. 3 | ------------------------------------------------------------------------------- 4 | MIT License 5 | 6 | Copyright (c) 2019 Ivan Roberto de Oliveira 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | #pragma once 27 | 28 | #include "virt86/virt86.hpp" 29 | 30 | #include 31 | 32 | enum class MMFormat { 33 | I8, I16, I32, I64, 34 | }; 35 | 36 | enum class XMMFormat { 37 | I8, I16, I32, I64, 38 | F32, F64, 39 | IF32, IF64, 40 | }; 41 | 42 | void printHostFeatures() noexcept; 43 | void printPlatformFeatures(virt86::Platform& platform) noexcept; 44 | void printMemoryMappingStatus(virt86::MemoryMappingStatus status) noexcept; 45 | void printFPExts(virt86::FloatingPointExtension fpExts) noexcept; 46 | void printRegs(virt86::VirtualProcessor& vp) noexcept; 47 | void printFPUControlRegs(virt86::VirtualProcessor& vp) noexcept; 48 | void printMXCSRRegs(virt86::VirtualProcessor& vp) noexcept; 49 | void printSTRegs(virt86::VirtualProcessor& vp) noexcept; 50 | void printMMRegs(virt86::VirtualProcessor& vp, MMFormat format) noexcept; 51 | void printXMMRegs(virt86::VirtualProcessor& vp, XMMFormat format) noexcept; 52 | void printYMMRegs(virt86::VirtualProcessor& vp, XMMFormat format) noexcept; 53 | void printZMMRegs(virt86::VirtualProcessor& vp, XMMFormat format) noexcept; 54 | void printFXSAVE(virt86::FXSAVEArea& fxsave, bool ia32e, bool printSSE, MMFormat mmFormat, XMMFormat xmmFormat) noexcept; 55 | void printXSAVE(virt86::VirtualProcessor& vp, uint64_t xsaveAddress, uint32_t bases[16], uint32_t sizes[16], uint32_t alignments, MMFormat mmFormat, XMMFormat xmmFormat) noexcept; 56 | void printDirtyBitmap(virt86::VirtualMachine& vm, uint64_t baseAddress, uint64_t numPages) noexcept; 57 | void printAddressTranslation(virt86::VirtualProcessor& vp, const uint64_t addr) noexcept; 58 | -------------------------------------------------------------------------------- /apps/common/include/utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Declares general utility functions. 3 | ------------------------------------------------------------------------------- 4 | MIT License 5 | 6 | Copyright (c) 2019 Ivan Roberto de Oliveira 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | #pragma once 27 | 28 | #include "virt86/virt86.hpp" 29 | 30 | template 31 | constexpr size_t array_size(T(&)[N]) { 32 | return N; 33 | } 34 | 35 | const char *reason_str(virt86::VMExitReason reason) noexcept; 36 | -------------------------------------------------------------------------------- /apps/common/src/align_alloc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Defines cross-platform functions for allocating and freeing aligned memory. 3 | ------------------------------------------------------------------------------- 4 | MIT License 5 | 6 | Copyright (c) 2019 Ivan Roberto de Oliveira 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | #include "align_alloc.hpp" 27 | 28 | #include "virt86/vp/vp.hpp" 29 | 30 | #if defined(_WIN32) 31 | # include 32 | #elif defined(__linux__) 33 | # include 34 | #elif defined(__APPLE__) 35 | # include 36 | #else 37 | # error Unsupported platform 38 | #endif 39 | 40 | uint8_t *alignedAlloc(const size_t size) noexcept { 41 | #if defined(_WIN32) 42 | LPVOID mem = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE); 43 | if (mem == NULL) { 44 | return NULL; 45 | } 46 | return (uint8_t *)VirtualAlloc(mem, size, MEM_COMMIT, PAGE_READWRITE); 47 | #elif defined(__linux__) 48 | return (uint8_t *)aligned_alloc(PAGE_SIZE, size); 49 | #elif defined(__APPLE__) 50 | // Allocate memory with room to keep track of the original pointer 51 | void *mem = malloc(size + (PAGE_SIZE - 1) + sizeof(void*)); 52 | 53 | // Get aligned address 54 | uint8_t *alignedMem = ((uint8_t *)mem + sizeof(void*)); 55 | alignedMem += PAGE_SIZE - ((uintptr_t)alignedMem & (PAGE_SIZE - 1)); 56 | 57 | // Write pointer to original memory block just before the aligned memory block 58 | ((void **)alignedMem)[-1] = mem; 59 | 60 | return alignedMem; 61 | #endif 62 | } 63 | 64 | bool alignedFree(void *memory) noexcept { 65 | #if defined(_WIN32) 66 | return VirtualFree(memory, 0, MEM_RELEASE) == TRUE; 67 | #elif defined(__linux__) 68 | free(memory); 69 | return true; 70 | #elif defined(__APPLE__) 71 | free(((void **)memory)[-1]); 72 | return true; 73 | #endif 74 | } 75 | -------------------------------------------------------------------------------- /apps/common/src/print_helpers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Defines helper functions that print out formatted register values. 3 | ------------------------------------------------------------------------------- 4 | MIT License 5 | 6 | Copyright (c) 2019 Ivan Roberto de Oliveira 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | #include "virt86/virt86.hpp" 27 | #include "virt86/util/host_info.hpp" 28 | 29 | #include "print_helpers.hpp" 30 | #include "align_alloc.hpp" 31 | #include "utils.hpp" 32 | 33 | #include 34 | #include 35 | 36 | using namespace virt86; 37 | 38 | void printHostFeatures() noexcept { 39 | printf("Host features:\n"); 40 | printf(" Maximum guest physical address: 0x%" PRIx64 "\n", HostInfo.gpa.maxAddress); 41 | printf(" Floating point extensions:"); 42 | printFPExts(HostInfo.floatingPointExtensions); 43 | printf("\n\n"); 44 | } 45 | 46 | void printPlatformFeatures(Platform& platform) noexcept { 47 | printf("%s v%s\n", platform.GetName().c_str(), platform.GetVersion().c_str()); 48 | printf("Hypervisor features:\n"); 49 | auto& features = platform.GetFeatures(); 50 | printf(" Maximum number of VCPUs: %u per VM, %u global\n", features.maxProcessorsPerVM, features.maxProcessorsGlobal); 51 | printf(" Maximum guest physical address: 0x%" PRIx64 "\n", features.guestPhysicalAddress.maxAddress); 52 | printf(" Unrestricted guest: %s\n", (features.unrestrictedGuest) ? "supported" : "unsuported"); 53 | printf(" Extended Page Tables: %s\n", (features.extendedPageTables) ? "supported" : "unsuported"); 54 | printf(" Guest debugging: %s\n", (features.guestDebugging) ? "available" : "unavailable"); 55 | printf(" Memory protection: %s\n", (features.guestMemoryProtection) ? "available" : "unavailable"); 56 | printf(" Dirty page tracking: %s\n", (features.dirtyPageTracking) ? "available" : "unavailable"); 57 | printf(" Partial dirty bitmap querying: %s\n", (features.partialDirtyBitmap) ? "supported" : "unsupported"); 58 | printf(" Large memory allocation: %s\n", (features.largeMemoryAllocation) ? "supported" : "unsuported"); 59 | printf(" Memory aliasing: %s\n", (features.memoryAliasing) ? "supported" : "unsuported"); 60 | printf(" Memory unmapping: %s\n", (features.memoryUnmapping) ? "supported" : "unsuported"); 61 | printf(" Partial unmapping: %s\n", (features.partialUnmapping) ? "supported" : "unsuported"); 62 | printf(" Partial MMIO instructions: %s\n", (features.partialMMIOInstructions) ? "yes" : "no"); 63 | printf(" Guest TSC scaling: %s\n", (features.guestTSCScaling) ? "supported" : "unsupported"); 64 | printf(" Custom CPUID results: %s\n", (features.customCPUIDs) ? "supported" : "unsupported"); 65 | if (features.customCPUIDs && !features.supportedCustomCPUIDs.empty()) { 66 | printf(" Function EAX EBX ECX EDX\n"); 67 | for (auto it = features.supportedCustomCPUIDs.cbegin(); it != features.supportedCustomCPUIDs.cend(); it++) { 68 | printf(" 0x%08x = 0x%08x 0x%08x 0x%08x 0x%08x\n", it->function, it->eax, it->ebx, it->ecx, it->edx); 69 | } 70 | } 71 | printf(" Floating point extensions:"); 72 | printFPExts(features.floatingPointExtensions); 73 | printf("\n"); 74 | printf(" Extended control registers:"); 75 | const auto extCRs = BitmaskEnum(features.extendedControlRegisters); 76 | if (!extCRs) printf(" None"); 77 | else { 78 | if (extCRs.AnyOf(ExtendedControlRegister::CR8)) printf(" CR8"); 79 | if (extCRs.AnyOf(ExtendedControlRegister::XCR0)) printf(" XCR0"); 80 | if (extCRs.AnyOf(ExtendedControlRegister::MXCSRMask)) printf(" MXCSR_MASK"); 81 | } 82 | printf("\n"); 83 | printf(" Extended VM exits:"); 84 | const auto extVMExits = BitmaskEnum(features.extendedVMExits); 85 | if (!extVMExits) printf(" None"); 86 | else { 87 | if (extVMExits.AnyOf(ExtendedVMExit::CPUID)) printf(" CPUID"); 88 | if (extVMExits.AnyOf(ExtendedVMExit::MSRAccess)) printf(" MSRAccess"); 89 | if (extVMExits.AnyOf(ExtendedVMExit::Exception)) printf(" Exception"); 90 | if (extVMExits.AnyOf(ExtendedVMExit::TSCAccess)) printf(" TSCAccess"); 91 | } 92 | printf("\n"); 93 | printf(" Exception exits:"); 94 | const auto excptExits = BitmaskEnum(features.exceptionExits); 95 | if (!excptExits) printf(" None"); 96 | else { 97 | if (excptExits.AnyOf(ExceptionCode::DivideErrorFault)) printf(" DivideErrorFault"); 98 | if (excptExits.AnyOf(ExceptionCode::DebugTrapOrFault)) printf(" DebugTrapOrFault"); 99 | if (excptExits.AnyOf(ExceptionCode::BreakpointTrap)) printf(" BreakpointTrap"); 100 | if (excptExits.AnyOf(ExceptionCode::OverflowTrap)) printf(" OverflowTrap"); 101 | if (excptExits.AnyOf(ExceptionCode::BoundRangeFault)) printf(" BoundRangeFault"); 102 | if (excptExits.AnyOf(ExceptionCode::InvalidOpcodeFault)) printf(" InvalidOpcodeFault"); 103 | if (excptExits.AnyOf(ExceptionCode::DeviceNotAvailableFault)) printf(" DeviceNotAvailableFault"); 104 | if (excptExits.AnyOf(ExceptionCode::DoubleFaultAbort)) printf(" DoubleFaultAbort"); 105 | if (excptExits.AnyOf(ExceptionCode::InvalidTaskStateSegmentFault)) printf(" InvalidTaskStateSegmentFault"); 106 | if (excptExits.AnyOf(ExceptionCode::SegmentNotPresentFault)) printf(" SegmentNotPresentFault"); 107 | if (excptExits.AnyOf(ExceptionCode::StackFault)) printf(" StackFault"); 108 | if (excptExits.AnyOf(ExceptionCode::GeneralProtectionFault)) printf(" GeneralProtectionFault"); 109 | if (excptExits.AnyOf(ExceptionCode::PageFault)) printf(" PageFault"); 110 | if (excptExits.AnyOf(ExceptionCode::FloatingPointErrorFault)) printf(" FloatingPointErrorFault"); 111 | if (excptExits.AnyOf(ExceptionCode::AlignmentCheckFault)) printf(" AlignmentCheckFault"); 112 | if (excptExits.AnyOf(ExceptionCode::MachineCheckAbort)) printf(" MachineCheckAbort"); 113 | if (excptExits.AnyOf(ExceptionCode::SimdFloatingPointFault)) printf(" SimdFloatingPointFault"); 114 | } 115 | printf("\n\n"); 116 | } 117 | 118 | void printMemoryMappingStatus(virt86::MemoryMappingStatus status) noexcept { 119 | switch (status) { 120 | case MemoryMappingStatus::OK: printf("succeeded\n"); break; 121 | case MemoryMappingStatus::Unsupported: printf("failed: unsupported operation\n"); break; 122 | case MemoryMappingStatus::MisalignedHostMemory: printf("failed: memory host block is misaligned\n"); break; 123 | case MemoryMappingStatus::MisalignedAddress: printf("failed: base address is misaligned\n"); break; 124 | case MemoryMappingStatus::MisalignedSize: printf("failed: size is misaligned\n"); break; 125 | case MemoryMappingStatus::EmptyRange: printf("failed: size is zero\n"); break; 126 | case MemoryMappingStatus::AlreadyAllocated: printf("failed: host memory block is already allocated\n"); break; 127 | case MemoryMappingStatus::InvalidFlags: printf("failed: invalid flags supplied\n"); break; 128 | case MemoryMappingStatus::Failed: printf("failed\n"); break; 129 | case MemoryMappingStatus::OutOfBounds: printf("out of bounds\n"); break; 130 | default: printf("failed: unhandled reason (%d)\n", static_cast(status)); break; 131 | } 132 | } 133 | 134 | void printFPExts(FloatingPointExtension fpExts) noexcept { 135 | auto bmFpExts = BitmaskEnum(fpExts); 136 | if (!bmFpExts) printf(" None"); 137 | else { 138 | if (bmFpExts.AnyOf(FloatingPointExtension::MMX)) printf(" MMX"); 139 | if (bmFpExts.AnyOf(FloatingPointExtension::SSE)) printf(" SSE"); 140 | if (bmFpExts.AnyOf(FloatingPointExtension::SSE2)) printf(" SSE2"); 141 | if (bmFpExts.AnyOf(FloatingPointExtension::SSE3)) printf(" SSE3"); 142 | if (bmFpExts.AnyOf(FloatingPointExtension::SSSE3)) printf(" SSSE3"); 143 | if (bmFpExts.AnyOf(FloatingPointExtension::SSE4_1)) printf(" SSE4.1"); 144 | if (bmFpExts.AnyOf(FloatingPointExtension::SSE4_2)) printf(" SSE4.2"); 145 | if (bmFpExts.AnyOf(FloatingPointExtension::SSE4a)) printf(" SSE4a"); 146 | if (bmFpExts.AnyOf(FloatingPointExtension::XOP)) printf(" XOP"); 147 | if (bmFpExts.AnyOf(FloatingPointExtension::F16C)) printf(" F16C"); 148 | if (bmFpExts.AnyOf(FloatingPointExtension::FMA4)) printf(" FMA4"); 149 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX)) printf(" AVX"); 150 | if (bmFpExts.AnyOf(FloatingPointExtension::FMA3)) printf(" FMA3"); 151 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX2)) printf(" AVX2"); 152 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512F)) { 153 | printf(" AVX-512[F"); 154 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512DQ)) printf(" DQ"); 155 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512IFMA)) printf(" IFMA"); 156 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512PF)) printf(" PF"); 157 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512ER)) printf(" ER"); 158 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512CD)) printf(" CD"); 159 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512BW)) printf(" BW"); 160 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512VL)) printf(" VL"); 161 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512VBMI)) printf(" VBMI"); 162 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512VBMI2)) printf(" VBMI2"); 163 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512GFNI)) printf(" GFNI"); 164 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512VAES)) printf(" VAES"); 165 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512VNNI)) printf(" VNNI"); 166 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512BITALG)) printf(" BITALG"); 167 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512VPOPCNTDQ)) printf(" VPOPCNTDQ"); 168 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512QVNNIW)) printf(" QVNNIW"); 169 | if (bmFpExts.AnyOf(FloatingPointExtension::AVX512QFMA)) printf(" QFMA"); 170 | printf("]"); 171 | } 172 | if (bmFpExts.AnyOf(FloatingPointExtension::FXSAVE)) printf(" FXSAVE"); 173 | if (bmFpExts.AnyOf(FloatingPointExtension::XSAVE)) printf(" XSAVE"); 174 | } 175 | } 176 | 177 | #define PRINT_FLAG(flags, prefix, flag) \ 178 | do { \ 179 | if (flags & prefix##_##flag) printf(" " #flag); \ 180 | } while (0) 181 | 182 | void printRFLAGSBits(uint64_t rflags) noexcept { 183 | PRINT_FLAG(rflags, RFLAGS, CF); 184 | PRINT_FLAG(rflags, RFLAGS, PF); 185 | PRINT_FLAG(rflags, RFLAGS, AF); 186 | PRINT_FLAG(rflags, RFLAGS, ZF); 187 | PRINT_FLAG(rflags, RFLAGS, SF); 188 | PRINT_FLAG(rflags, RFLAGS, TF); 189 | PRINT_FLAG(rflags, RFLAGS, IF); 190 | PRINT_FLAG(rflags, RFLAGS, DF); 191 | PRINT_FLAG(rflags, RFLAGS, OF); 192 | PRINT_FLAG(rflags, RFLAGS, NT); 193 | PRINT_FLAG(rflags, RFLAGS, RF); 194 | PRINT_FLAG(rflags, RFLAGS, VM); 195 | PRINT_FLAG(rflags, RFLAGS, AC); 196 | PRINT_FLAG(rflags, RFLAGS, VIF); 197 | PRINT_FLAG(rflags, RFLAGS, VIP); 198 | PRINT_FLAG(rflags, RFLAGS, ID); 199 | const uint8_t iopl = (rflags & RFLAGS_IOPL) >> RFLAGS_IOPL_SHIFT; 200 | printf(" IOPL=%u", iopl); 201 | } 202 | 203 | void printEFERBits(uint64_t efer) noexcept { 204 | PRINT_FLAG(efer, EFER, SCE); 205 | PRINT_FLAG(efer, EFER, LME); 206 | PRINT_FLAG(efer, EFER, LMA); 207 | PRINT_FLAG(efer, EFER, NXE); 208 | PRINT_FLAG(efer, EFER, SVME); 209 | PRINT_FLAG(efer, EFER, LMSLE); 210 | PRINT_FLAG(efer, EFER, FFXSR); 211 | PRINT_FLAG(efer, EFER, TCE); 212 | } 213 | 214 | void printCR0Bits(uint64_t cr0) noexcept { 215 | PRINT_FLAG(cr0, CR0, PE); 216 | PRINT_FLAG(cr0, CR0, MP); 217 | PRINT_FLAG(cr0, CR0, EM); 218 | PRINT_FLAG(cr0, CR0, TS); 219 | PRINT_FLAG(cr0, CR0, ET); 220 | PRINT_FLAG(cr0, CR0, NE); 221 | PRINT_FLAG(cr0, CR0, WP); 222 | PRINT_FLAG(cr0, CR0, AM); 223 | PRINT_FLAG(cr0, CR0, NW); 224 | PRINT_FLAG(cr0, CR0, CD); 225 | PRINT_FLAG(cr0, CR0, PG); 226 | } 227 | 228 | void printCR4Bits(uint64_t cr4) noexcept { 229 | PRINT_FLAG(cr4, CR4, VME); 230 | PRINT_FLAG(cr4, CR4, PVI); 231 | PRINT_FLAG(cr4, CR4, TSD); 232 | PRINT_FLAG(cr4, CR4, DE); 233 | PRINT_FLAG(cr4, CR4, PSE); 234 | PRINT_FLAG(cr4, CR4, PAE); 235 | PRINT_FLAG(cr4, CR4, MCE); 236 | PRINT_FLAG(cr4, CR4, PGE); 237 | PRINT_FLAG(cr4, CR4, PCE); 238 | PRINT_FLAG(cr4, CR4, OSFXSR); 239 | PRINT_FLAG(cr4, CR4, OSXMMEXCPT); 240 | PRINT_FLAG(cr4, CR4, UMIP); 241 | PRINT_FLAG(cr4, CR4, VMXE); 242 | PRINT_FLAG(cr4, CR4, SMXE); 243 | PRINT_FLAG(cr4, CR4, PCID); 244 | PRINT_FLAG(cr4, CR4, OSXSAVE); 245 | PRINT_FLAG(cr4, CR4, SMEP); 246 | PRINT_FLAG(cr4, CR4, SMAP); 247 | } 248 | 249 | void printCR8Bits(uint64_t cr8) noexcept { 250 | const uint8_t tpr = cr8 & CR8_TPR; 251 | printf(" TPR=%u", tpr); 252 | } 253 | 254 | void printXCR0Bits(uint64_t xcr0) noexcept { 255 | PRINT_FLAG(xcr0, XCR0, FP); 256 | PRINT_FLAG(xcr0, XCR0, SSE); 257 | PRINT_FLAG(xcr0, XCR0, AVX); 258 | PRINT_FLAG(xcr0, XCR0, BNDREG); 259 | PRINT_FLAG(xcr0, XCR0, BNDCSR); 260 | PRINT_FLAG(xcr0, XCR0, opmask); 261 | PRINT_FLAG(xcr0, XCR0, ZMM_Hi256); 262 | PRINT_FLAG(xcr0, XCR0, Hi16_ZMM); 263 | PRINT_FLAG(xcr0, XCR0, PKRU); 264 | } 265 | 266 | void printDR6Bits(uint64_t dr6) noexcept { 267 | PRINT_FLAG(dr6, DR6, BP0); 268 | PRINT_FLAG(dr6, DR6, BP1); 269 | PRINT_FLAG(dr6, DR6, BP2); 270 | PRINT_FLAG(dr6, DR6, BP3); 271 | } 272 | 273 | void printDR7Bits(uint64_t dr7) noexcept { 274 | for (uint8_t i = 0; i < 4; i++) { 275 | if (dr7 & (DR7_LOCAL(i) | DR7_GLOBAL(i))) { 276 | printf(" BP%u[", i); 277 | 278 | if (dr7 & DR7_LOCAL(i)) printf("L"); 279 | if (dr7 & DR7_GLOBAL(i)) printf("G"); 280 | 281 | const uint8_t size = (dr7 & DR7_SIZE(i)) >> DR7_SIZE_SHIFT(i); 282 | switch (size) { 283 | case DR7_SIZE_BYTE: printf(" byte"); break; 284 | case DR7_SIZE_WORD: printf(" word"); break; 285 | case DR7_SIZE_QWORD: printf(" qword"); break; 286 | case DR7_SIZE_DWORD: printf(" dword"); break; 287 | } 288 | 289 | const uint8_t cond = (dr7 & DR7_COND(i)) >> DR7_COND_SHIFT(i); 290 | switch (cond) { 291 | case DR7_COND_EXEC: printf(" exec"); break; 292 | case DR7_COND_WIDTH8: printf(" width8"); break; 293 | case DR7_COND_WRITE: printf(" write"); break; 294 | case DR7_COND_READWRITE: printf(" r/w"); break; 295 | } 296 | 297 | printf("]"); 298 | } 299 | } 300 | } 301 | #undef PRINT_FLAG 302 | 303 | void printSeg(VirtualProcessor& vp, Reg seg) noexcept { 304 | CPUExecutionMode mode = vp.GetExecutionMode(); 305 | SegmentSize size; 306 | vp.GetSegmentSize(seg, size); 307 | RegValue value; 308 | vp.RegRead(seg, value); 309 | 310 | // In IA-32e mode: 311 | // - Limit is ignored for CS, SS, DS, ES, FS and GS (effectively giving access to the entire memory) 312 | // - CS, SS, DS, ES all have base addresses of 0 313 | // - FS and GS have their base addresses stored in MSRs 314 | // - LDT and TSS entries are extended to 16 bytes to accomodate a 64-bit base address 315 | 316 | if (mode == CPUExecutionMode::IA32e) { 317 | if (seg == Reg::LDTR || seg == Reg::TR) { 318 | printf("%04" PRIx16 " -> %016" PRIx64 ":%08" PRIx32 " [%04" PRIx16 "] ", value.segment.selector, value.segment.base, value.segment.limit, value.segment.attributes.u16); 319 | } 320 | else { 321 | printf("%04" PRIx16 " -> %016" PRIx64 " [%04" PRIx16 "] ", value.segment.selector, value.segment.base, value.segment.attributes.u16); 322 | } 323 | } 324 | else { 325 | switch (size) { 326 | case SegmentSize::_16: printf("%04" PRIx16 " -> %08" PRIx32 ":%04" PRIx16 " [%04" PRIx16 "] ", value.segment.selector, (uint32_t)value.segment.base, (uint16_t)value.segment.limit, value.segment.attributes.u16); break; 327 | case SegmentSize::_32: printf("%04" PRIx16 " -> %08" PRIx32 ":%08" PRIx32 " [%04" PRIx16 "] ", value.segment.selector, (uint32_t)value.segment.base, value.segment.limit, value.segment.attributes.u16); break; 328 | } 329 | } 330 | 331 | // Print attributes 332 | if (value.segment.attributes.present) { 333 | if (value.segment.attributes.system) { 334 | if (value.segment.attributes.type & SEG_TYPE_CODE) { 335 | if (mode == CPUExecutionMode::IA32e && value.segment.attributes.longMode) printf("64-bit code"); 336 | else if (value.segment.attributes.defaultSize) printf("32-bit code"); 337 | else printf("16-bit code"); 338 | } 339 | else { 340 | if (mode == CPUExecutionMode::IA32e) printf("64-bit data"); 341 | else if (value.segment.attributes.defaultSize) printf("32-bit data"); 342 | else printf("16-bit data"); 343 | } 344 | } 345 | else { 346 | if (mode == CPUExecutionMode::IA32e) { 347 | switch (value.segment.attributes.type) { 348 | case 0b0010: printf("LDT"); break; 349 | case 0b1001: printf("64-bit TSS (available)"); break; 350 | case 0b1011: printf("64-bit TSS (busy)"); break; 351 | case 0b1100: printf("64-bit call gate"); break; 352 | case 0b1110: printf("64-bit interrupt gate"); break; 353 | case 0b1111: printf("64-bit trap gate"); break; 354 | default: printf("Reserved"); break; 355 | } 356 | } 357 | else { 358 | switch (value.segment.attributes.type) { 359 | case 0b0010: printf("LDT"); break; 360 | case 0b0001: printf("16-bit TSS (available)"); break; 361 | case 0b0011: printf("16-bit TSS (busy)"); break; 362 | case 0b0100: printf("16-bit call gate"); break; 363 | case 0b0110: printf("16-bit interrupt gate"); break; 364 | case 0b0111: printf("16-bit trap gate"); break; 365 | case 0b0101: printf("Task gate"); break; 366 | case 0b1001: printf("32-bit TSS (available)"); break; 367 | case 0b1011: printf("32-bit TSS (busy)"); break; 368 | case 0b1100: printf("32-bit call gate"); break; 369 | case 0b1110: printf("32-bit interrupt gate"); break; 370 | case 0b1111: printf("32-bit trap gate"); break; 371 | default: printf("Reserved"); break; 372 | } 373 | } 374 | } 375 | 376 | printf(" ("); 377 | printf((value.segment.attributes.granularity) ? "G=page" : "G=byte"); 378 | printf(" DPL=%u", value.segment.attributes.privilegeLevel); 379 | if (value.segment.attributes.system) { 380 | if (value.segment.attributes.type & SEG_TYPE_CODE) { 381 | if (value.segment.attributes.type & SEG_TYPE_READABLE) printf(" R-X"); else printf(" --X"); 382 | if (value.segment.attributes.type & SEG_TYPE_ACCESSED) printf("A"); else printf("-"); 383 | if (value.segment.attributes.type & SEG_TYPE_CONFORMING) printf(" conforming"); 384 | } 385 | else { 386 | if (value.segment.attributes.type & SEG_TYPE_WRITABLE) printf(" RW-"); else printf(" R--"); 387 | if (value.segment.attributes.type & SEG_TYPE_ACCESSED) printf("A"); else printf("-"); 388 | if (value.segment.attributes.type & SEG_TYPE_EXPANDDOWN) printf(" expand-down"); 389 | } 390 | } 391 | if (value.segment.attributes.available) printf(" AVL"); 392 | printf(")"); 393 | } 394 | } 395 | 396 | void printTable(VirtualProcessor& vp, Reg table) noexcept { 397 | CPUExecutionMode mode = vp.GetExecutionMode(); 398 | RegValue value; 399 | vp.RegRead(table, value); 400 | 401 | if (mode == CPUExecutionMode::IA32e) { 402 | printf("%016" PRIx64 ":%04" PRIx16, value.table.base, value.table.limit); 403 | } 404 | else { 405 | printf("%08" PRIx32 ":%04" PRIx16, (uint32_t)value.table.base, value.table.limit); 406 | } 407 | } 408 | 409 | #define READREG(code, name) bool has_##name; RegValue name; has_##name = vp.RegRead(code, name) == VPOperationStatus::OK; 410 | void printSegAndTableRegs(VirtualProcessor& vp) noexcept { 411 | READREG(Reg::CS, cs); 412 | READREG(Reg::SS, ss); 413 | READREG(Reg::DS, ds); 414 | READREG(Reg::ES, es); 415 | READREG(Reg::FS, fs); 416 | READREG(Reg::GS, gs); 417 | READREG(Reg::TR, tr); 418 | READREG(Reg::LDTR, ldtr); 419 | READREG(Reg::GDTR, gdtr); 420 | READREG(Reg::IDTR, idtr); 421 | 422 | printf(" CS = "); printSeg(vp, Reg::CS); printf("\n"); 423 | printf(" SS = "); printSeg(vp, Reg::SS); printf("\n"); 424 | printf(" DS = "); printSeg(vp, Reg::DS); printf("\n"); 425 | printf(" ES = "); printSeg(vp, Reg::ES); printf("\n"); 426 | printf(" FS = "); printSeg(vp, Reg::FS); printf("\n"); 427 | printf(" GS = "); printSeg(vp, Reg::GS); printf("\n"); 428 | printf(" TR = "); printSeg(vp, Reg::TR); printf("\n"); 429 | printf("LDTR = "); printSeg(vp, Reg::LDTR); printf("\n"); 430 | printf("GDTR = "); printTable(vp, Reg::GDTR); printf("\n"); 431 | printf("IDTR = "); printTable(vp, Reg::IDTR); printf("\n"); 432 | } 433 | 434 | void printControlAndDebugRegs(VirtualProcessor& vp) noexcept { 435 | READREG(Reg::EFER, efer); 436 | READREG(Reg::CR2, cr2); READREG(Reg::CR0, cr0); 437 | READREG(Reg::CR3, cr3); READREG(Reg::CR4, cr4); 438 | READREG(Reg::DR0, dr0); READREG(Reg::CR8, cr8); 439 | READREG(Reg::DR1, dr1); READREG(Reg::XCR0, xcr0); 440 | READREG(Reg::DR2, dr2); READREG(Reg::DR6, dr6); 441 | READREG(Reg::DR3, dr3); READREG(Reg::DR7, dr7); 442 | 443 | CPUExecutionMode mode = vp.GetExecutionMode(); 444 | 445 | const auto extendedRegs = BitmaskEnum(vp.GetVirtualMachine().GetPlatform().GetFeatures().extendedControlRegisters); 446 | 447 | printf("EFER = %016" PRIx64, efer.u64); printEFERBits(efer.u64); printf("\n"); 448 | if (mode == CPUExecutionMode::IA32e) { 449 | printf(" CR2 = %016" PRIx64 " CR0 = %016" PRIx64, cr2.u64, cr0.u64); printCR0Bits(cr0.u64); printf("\n"); 450 | printf(" CR3 = %016" PRIx64 " CR4 = %016" PRIx64, cr3.u64, cr4.u64); printCR4Bits(cr4.u64); printf("\n"); 451 | printf(" DR0 = %016" PRIx64 " CR8 = ", dr0.u64); 452 | if (extendedRegs.AnyOf(ExtendedControlRegister::CR8) && has_cr8) { 453 | printf("%016" PRIx64, cr8.u64); printCR8Bits(cr8.u64); printf("\n"); 454 | } 455 | else { 456 | printf("................\n"); 457 | } 458 | printf(" DR1 = %016" PRIx64 " XCR0 = ", dr1.u64); 459 | if (extendedRegs.AnyOf(ExtendedControlRegister::XCR0) && has_xcr0) { 460 | printf("%016" PRIx64, xcr0.u64); printXCR0Bits(xcr0.u64); printf("\n"); 461 | } 462 | else { 463 | printf("................\n"); 464 | } 465 | printf(" DR2 = %016" PRIx64 " DR6 = %016" PRIx64, dr2.u64, dr6.u64); printDR6Bits(dr6.u64); printf("\n"); 466 | printf(" DR3 = %016" PRIx64 " DR7 = %016" PRIx64, dr3.u64, dr7.u64); printDR7Bits(dr7.u64); printf("\n"); 467 | } 468 | else { 469 | printf(" CR2 = %08" PRIx32 " CR0 = %08" PRIx32, cr2.u32, cr0.u32); printCR0Bits(cr0.u32); printf("\n"); 470 | printf(" CR3 = %08" PRIx32 " CR4 = %08" PRIx32, cr3.u32, cr4.u32); printCR4Bits(cr4.u32); printf("\n"); 471 | printf(" DR0 = %08" PRIx32 "\n", dr0.u32); 472 | printf(" DR1 = %08" PRIx32 " XCR0 = ", dr1.u32); 473 | if (extendedRegs.AnyOf(ExtendedControlRegister::XCR0) && has_xcr0) { 474 | printf("%016" PRIx64, xcr0.u64); printXCR0Bits(xcr0.u64); printf("\n"); 475 | } 476 | else { 477 | printf("................\n"); 478 | } 479 | printf(" DR2 = %08" PRIx32 " DR6 = %08" PRIx32, dr2.u32, dr6.u32); printDR6Bits(dr6.u32); printf("\n"); 480 | printf(" DR3 = %08" PRIx32 " DR7 = %08" PRIx32, dr3.u32, dr7.u32); printDR7Bits(dr7.u32); printf("\n"); 481 | } 482 | } 483 | 484 | void printRegs16(VirtualProcessor& vp) noexcept { 485 | READREG(Reg::EAX, eax); READREG(Reg::ECX, ecx); READREG(Reg::EDX, edx); READREG(Reg::EBX, ebx); 486 | READREG(Reg::ESP, esp); READREG(Reg::EBP, ebp); READREG(Reg::ESI, esi); READREG(Reg::EDI, edi); 487 | READREG(Reg::IP, ip); 488 | READREG(Reg::EFLAGS, eflags); 489 | 490 | printf(" EAX = %08" PRIx32 " ECX = %08" PRIx32 " EDX = %08" PRIx32 " EBX = %08" PRIx32 "\n", eax.u32, ecx.u32, edx.u32, ebx.u32); 491 | printf(" ESP = %08" PRIx32 " EBP = %08" PRIx32 " ESI = %08" PRIx32 " EDI = %08" PRIx32 "\n", esp.u32, ebp.u32, esi.u32, edi.u32); 492 | printf(" IP = %04" PRIx16 "\n", ip.u16); 493 | printSegAndTableRegs(vp); 494 | printf("EFLAGS = %08" PRIx32, eflags.u32); printRFLAGSBits(eflags.u32); printf("\n"); 495 | printControlAndDebugRegs(vp); 496 | } 497 | 498 | void printRegs32(VirtualProcessor& vp) noexcept { 499 | READREG(Reg::EAX, eax); READREG(Reg::ECX, ecx); READREG(Reg::EDX, edx); READREG(Reg::EBX, ebx); 500 | READREG(Reg::ESP, esp); READREG(Reg::EBP, ebp); READREG(Reg::ESI, esi); READREG(Reg::EDI, edi); 501 | READREG(Reg::EIP, eip); 502 | READREG(Reg::EFLAGS, eflags); 503 | 504 | printf(" EAX = %08" PRIx32 " ECX = %08" PRIx32 " EDX = %08" PRIx32 " EBX = %08" PRIx32 "\n", eax.u32, ecx.u32, edx.u32, ebx.u32); 505 | printf(" ESP = %08" PRIx32 " EBP = %08" PRIx32 " ESI = %08" PRIx32 " EDI = %08" PRIx32 "\n", esp.u32, ebp.u32, esi.u32, edi.u32); 506 | printf(" EIP = %08" PRIx32 "\n", eip.u32); 507 | printSegAndTableRegs(vp); 508 | printf("EFLAGS = %08" PRIx32, eflags.u32); printRFLAGSBits(eflags.u32); printf("\n"); 509 | printControlAndDebugRegs(vp); 510 | } 511 | 512 | void printRegs64(VirtualProcessor& vp) noexcept { 513 | READREG(Reg::RAX, rax); READREG(Reg::RCX, rcx); READREG(Reg::RDX, rdx); READREG(Reg::RBX, rbx); 514 | READREG(Reg::RSP, rsp); READREG(Reg::RBP, rbp); READREG(Reg::RSI, rsi); READREG(Reg::RDI, rdi); 515 | READREG(Reg::R8, r8); READREG(Reg::R9, r9); READREG(Reg::R10, r10); READREG(Reg::R11, r11); 516 | READREG(Reg::R12, r12); READREG(Reg::R13, r13); READREG(Reg::R14, r14); READREG(Reg::R15, r15); 517 | READREG(Reg::RIP, rip); 518 | READREG(Reg::RFLAGS, rflags); 519 | 520 | printf(" RAX = %016" PRIx64 " RCX = %016" PRIx64 " RDX = %016" PRIx64 " RBX = %016" PRIx64 "\n", rax.u64, rcx.u64, rdx.u64, rbx.u64); 521 | printf(" RSP = %016" PRIx64 " RBP = %016" PRIx64 " RSI = %016" PRIx64 " RDI = %016" PRIx64 "\n", rsp.u64, rbp.u64, rsi.u64, rdi.u64); 522 | printf(" R8 = %016" PRIx64 " R9 = %016" PRIx64 " R10 = %016" PRIx64 " R11 = %016" PRIx64 "\n", r8.u64, r9.u64, r10.u64, r11.u64); 523 | printf(" R12 = %016" PRIx64 " R13 = %016" PRIx64 " R14 = %016" PRIx64 " R15 = %016" PRIx64 "\n", r12.u64, r13.u64, r14.u64, r15.u64); 524 | printf(" RIP = %016" PRIx64 "\n", rip.u64); 525 | printSegAndTableRegs(vp); 526 | printf("RFLAGS = %016" PRIx64, rflags.u64); printRFLAGSBits(rflags.u64); printf("\n"); 527 | printControlAndDebugRegs(vp); 528 | } 529 | #undef READREG 530 | 531 | void printRegs(VirtualProcessor& vp) noexcept { 532 | // Print CPU mode, paging mode and code segment size 533 | CPUExecutionMode cpuMode = vp.GetExecutionMode(); 534 | CPUPagingMode pagingMode = vp.GetPagingMode(); 535 | SegmentSize segmentSize; 536 | vp.GetSegmentSize(Reg::CS, segmentSize); 537 | 538 | switch (cpuMode) { 539 | case CPUExecutionMode::RealAddress: printf("Real-address mode"); break; 540 | case CPUExecutionMode::Virtual8086: printf("Virtual-8086 mode"); break; 541 | case CPUExecutionMode::Protected: printf("Protected mode"); break; 542 | case CPUExecutionMode::IA32e: printf("IA-32e mode"); break; 543 | } 544 | printf(", "); 545 | 546 | switch (pagingMode) { 547 | case CPUPagingMode::None: printf("no paging"); break; 548 | case CPUPagingMode::NoneLME: printf("no paging (LME enabled)"); break; 549 | case CPUPagingMode::NonePAE: printf("no paging (PAE enabled)"); break; 550 | case CPUPagingMode::NonePAEandLME: printf("no paging (PAE and LME enabled)"); break; 551 | case CPUPagingMode::ThirtyTwoBit: printf("32-bit paging"); break; 552 | case CPUPagingMode::Invalid: printf("*invalid*"); break; 553 | case CPUPagingMode::PAE: printf("PAE paging"); break; 554 | case CPUPagingMode::FourLevel: printf("4-level paging"); break; 555 | } 556 | printf(", "); 557 | 558 | switch (segmentSize) { 559 | case SegmentSize::_16: printf("16-bit code"); break; 560 | case SegmentSize::_32: printf("32-bit code"); break; 561 | case SegmentSize::_64: printf("64-bit code"); break; 562 | } 563 | printf("\n"); 564 | 565 | // Print registers according to segment size 566 | switch (segmentSize) { 567 | case SegmentSize::_16: printRegs16(vp); break; 568 | case SegmentSize::_32: printRegs32(vp); break; 569 | case SegmentSize::_64: printRegs64(vp); break; 570 | } 571 | } 572 | 573 | void printFPUControlRegs(VirtualProcessor& vp) noexcept { 574 | FPUControl fpuCtl; 575 | auto status = vp.GetFPUControl(fpuCtl); 576 | if (status != VPOperationStatus::OK) { 577 | printf("Failed to retrieve FPU control registers\n"); 578 | return; 579 | } 580 | 581 | printf("FPU.CW = %04x FPU.SW = %04x FPU.TW = %04x FPU.OP = %04x\n", fpuCtl.cw, fpuCtl.sw, fpuCtl.tw, fpuCtl.op); 582 | printf("FPU.CS:IP = %04x:%08x\n", fpuCtl.cs, fpuCtl.ip); 583 | printf("FPU.DS:DP = %04x:%08x\n", fpuCtl.ds, fpuCtl.dp); 584 | } 585 | 586 | void printMXCSRRegs(VirtualProcessor& vp) noexcept { 587 | MXCSR mxcsr, mxcsrMask; 588 | auto status = vp.GetMXCSR(mxcsr); 589 | if (status != VPOperationStatus::OK) { 590 | printf("Failed to retrieve MMX control/status registers\n"); 591 | } 592 | 593 | const auto extCRs = BitmaskEnum(vp.GetVirtualMachine().GetPlatform().GetFeatures().extendedControlRegisters); 594 | if (extCRs.AnyOf(ExtendedControlRegister::MXCSRMask)) { 595 | status = vp.GetMXCSRMask(mxcsrMask); 596 | if (status != VPOperationStatus::OK) { 597 | printf("Failed to retrieve MXCSR mask\n"); 598 | } 599 | } 600 | 601 | printf("MXCSR = %08x\n", mxcsr.u32); 602 | if (extCRs.AnyOf(ExtendedControlRegister::MXCSRMask)) { 603 | printf("MXCSR_MASK = %08x\n", mxcsrMask.u32); 604 | } 605 | } 606 | 607 | void printSTRegs(VirtualProcessor& vp) noexcept { 608 | Reg regs[] = { 609 | Reg::ST0, Reg::ST1, Reg::ST2, Reg::ST3, Reg::ST4, Reg::ST5, Reg::ST6, Reg::ST7, 610 | }; 611 | RegValue values[array_size(regs)]; 612 | 613 | auto status = vp.RegRead(regs, values, array_size(regs)); 614 | if (status != VPOperationStatus::OK) { 615 | printf("Failed to retrieve FPU registers\n"); 616 | return; 617 | } 618 | 619 | for (int i = 0; i < 8; i++) { 620 | printf("ST(%d) = %016" PRIx64 " %04x\n", i, values[i].st.significand, values[i].st.exponentSign); 621 | } 622 | } 623 | 624 | void printMMRegs(VirtualProcessor& vp, MMFormat format) noexcept { 625 | Reg regs[] = { 626 | Reg::MM0, Reg::MM1, Reg::MM2, Reg::MM3, Reg::MM4, Reg::MM5, Reg::MM6, Reg::MM7, 627 | }; 628 | RegValue values[array_size(regs)]; 629 | 630 | auto status = vp.RegRead(regs, values, array_size(regs)); 631 | if (status != VPOperationStatus::OK) { 632 | printf("Failed to retrieve MMX registers\n"); 633 | return; 634 | } 635 | 636 | for (int i = 0; i < 8; i++) { 637 | printf(" MM%d =", i); 638 | switch (format) { 639 | case MMFormat::I8: 640 | for (int j = 7; j >= 0; j--) { 641 | printf(" %02" PRIx8, values[i].mm.i8[j]); 642 | } 643 | break; 644 | case MMFormat::I16: 645 | for (int j = 3; j >= 0; j--) { 646 | printf(" %04" PRIx16, values[i].mm.i16[j]); 647 | } 648 | break; 649 | case MMFormat::I32: 650 | for (int j = 1; j >= 0; j--) { 651 | printf(" %08" PRIx32, values[i].mm.i32[j]); 652 | } 653 | break; 654 | case MMFormat::I64: 655 | printf(" %016" PRIx64, values[i].mm.i64[0]); 656 | break; 657 | } 658 | printf("\n"); 659 | } 660 | } 661 | 662 | template 663 | static void printXMMValsI8(T& values) { 664 | for (int j = bytes - 1; j >= 0; j--) { 665 | printf(" %02" PRIx8, values.i8[j]); 666 | } 667 | } 668 | 669 | template 670 | static void printXMMValsI16(T& values) { 671 | for (int j = bytes / 2 - 1; j >= 0; j--) { 672 | printf(" %04" PRIx16, values.i16[j]); 673 | } 674 | } 675 | 676 | template 677 | static void printXMMValsI32(T& values) { 678 | for (int j = bytes / 4 - 1; j >= 0; j--) { 679 | printf(" %08" PRIx32, values.i32[j]); 680 | } 681 | } 682 | 683 | template 684 | static void printXMMValsI64(T& values) { 685 | for (int j = bytes / 8 - 1; j >= 0; j--) { 686 | printf(" %016" PRIx64, values.i64[j]); 687 | } 688 | } 689 | 690 | template 691 | static void printXMMValsF32(T& values) { 692 | for (int j = bytes / 4 - 1; j >= 0; j--) { 693 | printf(" %f", values.f32[j]); 694 | } 695 | } 696 | 697 | template 698 | static void printXMMValsF64(T& values) { 699 | for (int j = bytes / 8 - 1; j >= 0; j--) { 700 | printf(" %lf", values.f64[j]); 701 | } 702 | } 703 | 704 | template 705 | static void _printXMMVals(XMMFormat format, T1& values, TN&... moreValues) { 706 | switch (format) { 707 | case XMMFormat::I8: 708 | printXMMValsI8(values); 709 | break; 710 | case XMMFormat::I16: 711 | printXMMValsI16(values); 712 | break; 713 | case XMMFormat::I32: 714 | printXMMValsI32(values); 715 | break; 716 | case XMMFormat::I64: 717 | printXMMValsI64(values); 718 | break; 719 | case XMMFormat::F32: 720 | printXMMValsF32(values); 721 | break; 722 | case XMMFormat::F64: 723 | printXMMValsF64(values); 724 | break; 725 | } 726 | _printXMMVals(format, moreValues...); 727 | } 728 | 729 | template 730 | static void _printXMMVals(XMMFormat format) { 731 | } 732 | 733 | template 734 | static void printXMMVals(XMMFormat format, T1& values, TN&... moreValues) { 735 | switch (format) { 736 | case XMMFormat::I8: 737 | case XMMFormat::I16: 738 | case XMMFormat::I32: 739 | case XMMFormat::I64: 740 | case XMMFormat::F32: 741 | case XMMFormat::F64: 742 | _printXMMVals(format, values, moreValues...); 743 | break; 744 | case XMMFormat::IF32: 745 | _printXMMVals(XMMFormat::I32, values, moreValues...); 746 | printf("\n "); 747 | _printXMMVals(XMMFormat::F32, values, moreValues...); 748 | break; 749 | case XMMFormat::IF64: 750 | _printXMMVals(XMMFormat::I64, values, moreValues...); 751 | printf("\n "); 752 | _printXMMVals(XMMFormat::F64, values, moreValues...); 753 | break; 754 | } 755 | } 756 | 757 | void printXMMRegs(VirtualProcessor& vp, XMMFormat format) noexcept { 758 | auto cpuMode = vp.GetExecutionMode(); 759 | const uint8_t maxMMRegs = (cpuMode == CPUExecutionMode::IA32e) ? 32 : 8; 760 | 761 | for (uint8_t i = 0; i < maxMMRegs; i++) { 762 | RegValue value; 763 | auto status = vp.RegRead(RegAdd(Reg::XMM0, i), value); 764 | if (status != VPOperationStatus::OK) { 765 | break; 766 | } 767 | 768 | const auto& v = value.xmm; 769 | printf("XMM%-2u =", i); 770 | printXMMVals<16>(format, v); 771 | printf("\n"); 772 | } 773 | } 774 | 775 | void printYMMRegs(VirtualProcessor& vp, XMMFormat format) noexcept { 776 | auto cpuMode = vp.GetExecutionMode(); 777 | const uint8_t maxMMRegs = (cpuMode == CPUExecutionMode::IA32e) ? 32 : 8; 778 | 779 | for (uint8_t i = 0; i < maxMMRegs; i++) { 780 | RegValue value; 781 | auto status = vp.RegRead(RegAdd(Reg::YMM0, i), value); 782 | if (status != VPOperationStatus::OK) { 783 | break; 784 | } 785 | 786 | const auto& v = value.ymm; 787 | printf("YMM%-2u =", i); 788 | printXMMVals<32>(format, v); 789 | printf("\n"); 790 | } 791 | } 792 | 793 | void printZMMRegs(VirtualProcessor& vp, XMMFormat format) noexcept { 794 | auto cpuMode = vp.GetExecutionMode(); 795 | const uint8_t maxMMRegs = (cpuMode == CPUExecutionMode::IA32e) ? 32 : 8; 796 | 797 | for (uint8_t i = 0; i < maxMMRegs; i++) { 798 | RegValue value; 799 | auto status = vp.RegRead(RegAdd(Reg::ZMM0, i), value); 800 | if (status != VPOperationStatus::OK) { 801 | break; 802 | } 803 | 804 | const auto& v = value.zmm; 805 | printf("ZMM%-2u =", i); 806 | printXMMVals<64>(format, v); 807 | printf("\n"); 808 | } 809 | } 810 | 811 | void printFXSAVE(FXSAVEArea& fxsave, bool ia32e, bool printSSE, MMFormat mmFormat, XMMFormat xmmFormat) noexcept { 812 | printf("FPU.CW = %04x FPU.SW = %04x FPU.TW = %04x FPU.OP = %04x\n", fxsave.fcw, fxsave.fsw, fxsave.ftw, fxsave.fop); 813 | if (ia32e) { 814 | printf("FPU.IP = %016" PRIx64 "\n", fxsave.ip64.fip); 815 | printf("FPU.DP = %016" PRIx64 "\n", fxsave.dp64.fdp); 816 | } 817 | else { 818 | printf("FPU.CS:IP = %04" PRIx16 ":%08" PRIx32 "\n", fxsave.ip32.fcs, fxsave.ip32.fip); 819 | printf("FPU.DS:DP = %04" PRIx16 ":%08" PRIx32 "\n", fxsave.dp32.fds, fxsave.dp32.fdp); 820 | } 821 | printf("MXCSR = %08x\n", fxsave.mxcsr.u32); 822 | printf("MXCSR_MASK = %08x\n", fxsave.mxcsr_mask.u32); 823 | for (int i = 0; i < 8; i++) { 824 | printf("ST(%d) = %016" PRIx64 " %04x\n", i, fxsave.st_mm[i].st.significand, fxsave.st_mm[i].st.exponentSign); 825 | } 826 | for (int i = 0; i < 8; i++) { 827 | printf(" MM%d =", i); 828 | switch (mmFormat) { 829 | case MMFormat::I8: 830 | for (int j = 7; j >= 0; j--) { 831 | printf(" %02" PRIx8, fxsave.st_mm[i].mm.i8[j]); 832 | } 833 | break; 834 | case MMFormat::I16: 835 | for (int j = 3; j >= 0; j--) { 836 | printf(" %04" PRIx16, fxsave.st_mm[i].mm.i16[j]); 837 | } 838 | break; 839 | case MMFormat::I32: 840 | for (int j = 1; j >= 0; j--) { 841 | printf(" %08" PRIx32, fxsave.st_mm[i].mm.i32[j]); 842 | } 843 | break; 844 | case MMFormat::I64: 845 | printf(" %016" PRIx64, fxsave.st_mm[i].mm.i64[0]); 846 | break; 847 | } 848 | printf("\n"); 849 | } 850 | 851 | if (printSSE) { 852 | const uint8_t maxMMRegs = ia32e ? 32 : 8; 853 | 854 | for (uint8_t i = 0; i < maxMMRegs; i++) { 855 | printf("XMM%-2u =", i); 856 | printXMMVals<16>(xmmFormat, fxsave.xmm[i]); 857 | } 858 | } 859 | } 860 | 861 | void printXSAVE(VirtualProcessor& vp, uint64_t xsaveAddress, uint32_t bases[16], uint32_t sizes[16], uint32_t alignments, MMFormat mmFormat, XMMFormat xmmFormat) noexcept { 862 | XSAVEArea xsave; 863 | if (!vp.LMemRead(xsaveAddress, sizeof(xsave), &xsave)) { 864 | printf("Could not read XSAVE from memory at 0x%" PRIx64, xsaveAddress); 865 | return; 866 | } 867 | 868 | auto cpuMode = vp.GetExecutionMode(); 869 | bool ia32e = cpuMode == CPUExecutionMode::IA32e; 870 | 871 | printFXSAVE(xsave.fxsave, ia32e, false, mmFormat, xmmFormat); 872 | 873 | // Components used in XSAVE 874 | XSAVE_AVX avx; 875 | XSAVE_MPX_BNDREGS bndregs; 876 | XSAVE_MPX_BNDCSR bndcsr; 877 | XSAVE_AVX512_Opmask opmask; 878 | XSAVE_AVX512_ZMM_Hi256 zmm_hi256; 879 | XSAVE_AVX512_Hi16_ZMM hi16_zmm; 880 | XSAVE_PT pt; 881 | XSAVE_PKRU pkru; 882 | XSAVE_HDC hdc; 883 | 884 | // Addresses of components and whether they are available 885 | uint64_t addr_avx; bool has_avx = false; 886 | uint64_t addr_bndregs; bool has_bndregs = false; 887 | uint64_t addr_bndcsr; bool has_bndcsr = false; 888 | uint64_t addr_opmask; bool has_opmask = false; 889 | uint64_t addr_zmm_hi256; bool has_zmm_hi256 = false; 890 | uint64_t addr_hi16_zmm; bool has_hi16_zmm = false; 891 | uint64_t addr_pt; bool has_pt = false; 892 | uint64_t addr_pkru; bool has_pkru = false; 893 | uint64_t addr_hdc; bool has_hdc = false; 894 | 895 | // Get addresses of each component according to data format 896 | if (xsave.header.xcomp_bv.data.format) { 897 | // XSAVE is in compacted format 898 | auto& components = xsave.header.xcomp_bv.data; 899 | 900 | // The following algorithm is described in Section 13.4.3 of 901 | // Intel� 64 and IA-32 Architectures Software Developer's Manual, Volume 1 902 | 903 | // Keep track of the current location and size of previous component. 904 | // Location 0 indicates this is the first component. 905 | uint64_t location = 0; 906 | uint64_t prevSize; 907 | 908 | // Get the address of the specified component and updates the offset 909 | auto getAddr = [&](uint8_t index) -> uint64_t { 910 | if (location == 0) { 911 | // First item is always located at location 576 912 | location = 576; 913 | } 914 | else if (alignments & (1 << (index + 2))) { 915 | // Aligned components are located at the next 64 byte boundary 916 | location = (location + prevSize + 63) & ~63; 917 | } 918 | else { 919 | // Unaligned components are located immediately after the previous component 920 | location += prevSize; 921 | } 922 | prevSize = sizes[index]; 923 | return xsaveAddress + location; 924 | }; 925 | 926 | if (components.AVX) { addr_avx = getAddr(0); has_avx = true; } 927 | if (components.MPX_bndregs) { addr_bndregs = getAddr(1); has_bndregs = true; } 928 | if (components.MPX_bndcsr) { addr_bndcsr = getAddr(2); has_bndcsr = true; } 929 | if (components.AVX512_opmask) { addr_opmask = getAddr(3); has_opmask = true; } 930 | if (components.ZMM_Hi256) { addr_zmm_hi256 = getAddr(4); has_zmm_hi256 = true; } 931 | if (components.Hi16_ZMM) { addr_hi16_zmm = getAddr(5); has_hi16_zmm = true; } 932 | if (components.PT) { addr_pt = getAddr(6); has_pt = true; } 933 | if (components.PKRU) { addr_pkru = getAddr(7); has_pkru = true; } 934 | if (components.HDC) { addr_hdc = getAddr(11); has_hdc = true; } 935 | } 936 | else { 937 | // XSAVE is in standard format 938 | auto& components = xsave.header.xstate_bv.data; 939 | if (components.AVX) { addr_avx = xsaveAddress + bases[0]; has_avx = true; } 940 | if (components.MPX_bndregs) { addr_bndregs = xsaveAddress + bases[1]; has_bndregs = true; } 941 | if (components.MPX_bndcsr) { addr_bndcsr = xsaveAddress + bases[2]; has_bndcsr = true; } 942 | if (components.AVX512_opmask) { addr_opmask = xsaveAddress + bases[3]; has_opmask = true; } 943 | if (components.ZMM_Hi256) { addr_zmm_hi256 = xsaveAddress + bases[4]; has_zmm_hi256 = true; } 944 | if (components.Hi16_ZMM) { addr_hi16_zmm = xsaveAddress + bases[5]; has_hi16_zmm = true; } 945 | if (components.PT) { addr_pt = xsaveAddress + bases[6]; has_pt = true; } 946 | if (components.PKRU) { addr_pkru = xsaveAddress + bases[7]; has_pkru = true; } 947 | if (components.HDC) { addr_hdc = xsaveAddress + bases[11]; has_hdc = true; } 948 | } 949 | 950 | // Read components from memory 951 | if (has_avx && !vp.LMemRead(addr_avx, sizes[0], &avx)) { 952 | printf("Could not read AVX state\n"); 953 | has_avx = false; 954 | } 955 | if (has_bndregs && !vp.LMemRead(addr_bndregs, sizes[1], &bndregs)) { 956 | printf("Could not read MPX.BNDREGS state\n"); 957 | has_bndregs = false; 958 | } 959 | if (has_bndcsr && !vp.LMemRead(addr_bndcsr, sizes[2], &bndcsr)) { 960 | printf("Could not read MPX.BNDCSR state\n"); 961 | has_bndcsr = false; 962 | } 963 | if (has_opmask && !vp.LMemRead(addr_opmask, sizes[3], &opmask)) { 964 | printf("Could not read AVX512.opmask state\n"); 965 | has_opmask = false; 966 | } 967 | if (has_zmm_hi256 && !vp.LMemRead(addr_zmm_hi256, sizes[4], &zmm_hi256)) { 968 | printf("Could not read AVX512.ZMM_Hi256 state\n"); 969 | has_zmm_hi256 = false; 970 | } 971 | if (has_hi16_zmm && !vp.LMemRead(addr_hi16_zmm, sizes[5], &hi16_zmm)) { 972 | printf("Could not read AVX512.Hi16_ZMM state\n"); 973 | has_hi16_zmm = false; 974 | } 975 | if (has_pt && !vp.LMemRead(addr_pt, sizes[6], &pt)) { 976 | printf("Could not read PT state\n"); 977 | has_pt = false; 978 | } 979 | if (has_pkru && !vp.LMemRead(addr_pkru, sizes[7], &pkru)) { 980 | printf("Could not read PKRU state\n"); 981 | has_pkru = false; 982 | } 983 | if (has_hdc && !vp.LMemRead(addr_hdc, sizes[11], &hdc)) { 984 | printf("Could not read PKRU state\n"); 985 | has_hdc = false; 986 | } 987 | 988 | // Print available components 989 | if (has_avx) { 990 | if (has_zmm_hi256) { 991 | for (uint8_t i = 0; i < sizes[4] / sizeof(ZMMHighValue); i++) { 992 | printf("ZMM%-2u =", i); 993 | printXMMVals<16>(xmmFormat, zmm_hi256.zmmHigh[i], avx.ymmHigh[i], xsave.fxsave.xmm[i]); 994 | printf("\n"); 995 | } 996 | 997 | if (has_hi16_zmm) { 998 | for (uint8_t i = 0; i < sizes[5] / sizeof(ZMMValue); i++) { 999 | printf("ZMM%-2u =", i + 16); 1000 | printXMMVals<64>(xmmFormat, hi16_zmm.zmm[i]); 1001 | printf("\n"); 1002 | } 1003 | } 1004 | } 1005 | else { 1006 | for (uint8_t i = 0; i < sizes[0] / sizeof(YMMHighValue); i++) { 1007 | printf("YMM%-2u =", i); 1008 | printXMMVals<16>(xmmFormat, avx.ymmHigh[i], xsave.fxsave.xmm[i]); 1009 | printf("\n"); 1010 | } 1011 | } 1012 | 1013 | if (has_opmask) { 1014 | for (uint8_t i = 0; i < array_size(opmask.k); i++) { 1015 | printf(" K%u = %016" PRIx64 "\n", i, opmask.k[i]); 1016 | } 1017 | } 1018 | 1019 | if (has_bndregs) { 1020 | for (uint8_t i = 0; i < array_size(bndregs.bnd); i++) { 1021 | printf("BND%u = %016" PRIx64 "%016" PRIx64 "\n", i, bndregs.bnd[i].high, bndregs.bnd[i].low); 1022 | } 1023 | } 1024 | 1025 | if (has_bndcsr) { 1026 | printf("BNDCFGU = %016" PRIx64 "\n", bndcsr.BNDCFGU); 1027 | printf("BNDSTATUS = %016" PRIx64 "\n", bndcsr.BNDSTATUS); 1028 | } 1029 | 1030 | if (has_pt) { 1031 | printf("PT.IA32_RTIT_CTL = %016" PRIx64 "\n", pt.IA32_RTIT_CTL); 1032 | printf("PT.IA32_RTIT_OUTPUT_BASE = %016" PRIx64 "\n", pt.IA32_RTIT_OUTPUT_BASE); 1033 | printf("PT.IA32_RTIT_OUTPUT_MASK_PTRS = %016" PRIx64 "\n", pt.IA32_RTIT_OUTPUT_MASK_PTRS); 1034 | printf("PT.IA32_RTIT_STATUS = %016" PRIx64 "\n", pt.IA32_RTIT_STATUS); 1035 | printf("PT.IA32_RTIT_CR3_MATCH = %016" PRIx64 "\n", pt.IA32_RTIT_CR3_MATCH); 1036 | printf("PT.IA32_RTIT_ADDR0_A = %016" PRIx64 "\n", pt.IA32_RTIT_ADDR0_A); 1037 | printf("PT.IA32_RTIT_ADDR0_B = %016" PRIx64 "\n", pt.IA32_RTIT_ADDR0_B); 1038 | printf("PT.IA32_RTIT_ADDR1_A = %016" PRIx64 "\n", pt.IA32_RTIT_ADDR1_A); 1039 | printf("PT.IA32_RTIT_ADDR1_B = %016" PRIx64 "\n", pt.IA32_RTIT_ADDR1_B); 1040 | } 1041 | 1042 | if (has_pkru) { 1043 | printf("PKRU = %08" PRIx32 "\n", pkru.pkru); 1044 | } 1045 | 1046 | if (has_hdc) { 1047 | printf("HDC.IA32_PM_CTL1 = %016" PRIx64 "\n", hdc.IA32_PM_CTL1); 1048 | } 1049 | } 1050 | } 1051 | 1052 | void printDirtyBitmap(VirtualMachine& vm, uint64_t baseAddress, uint64_t numPages) noexcept { 1053 | if (!vm.GetPlatform().GetFeatures().dirtyPageTracking) { 1054 | printf("Dirty page tracking not supported by the hypervisor\n\n"); 1055 | } 1056 | if (numPages == 0) { 1057 | return; 1058 | } 1059 | 1060 | const size_t bitmapSize = (numPages - 1) / sizeof(uint64_t) + 1; 1061 | uint64_t *bitmap = (uint64_t *)alignedAlloc(bitmapSize * sizeof(uint64_t)); 1062 | memset(bitmap, 0, bitmapSize * sizeof(uint64_t)); 1063 | const auto dptStatus = vm.QueryDirtyPages(baseAddress, numPages * PAGE_SIZE, bitmap, bitmapSize * sizeof(uint64_t)); 1064 | if (dptStatus == DirtyPageTrackingStatus::OK) { 1065 | printf("Dirty pages:\n"); 1066 | uint64_t pageNum = 0; 1067 | for (size_t i = 0; i < bitmapSize; i++) { 1068 | if (bitmap[i] == 0) { 1069 | continue; 1070 | } 1071 | for (uint8_t bit = 0; bit < 64; bit++) { 1072 | if (bitmap[i] & (1ull << bit)) { 1073 | printf(" 0x%" PRIx64 "\n", pageNum * PAGE_SIZE); 1074 | } 1075 | if (++pageNum > numPages) { 1076 | goto done; 1077 | } 1078 | } 1079 | } 1080 | done: 1081 | printf("\n"); 1082 | } 1083 | alignedFree(bitmap); 1084 | } 1085 | 1086 | void printAddressTranslation(VirtualProcessor& vp, const uint64_t addr) noexcept { 1087 | printf(" 0x%" PRIx64 " -> ", addr); 1088 | uint64_t paddr; 1089 | if (vp.LinearToPhysical(addr, &paddr)) { 1090 | printf("0x%" PRIx64 "\n", paddr); 1091 | } 1092 | else { 1093 | printf("\n"); 1094 | } 1095 | } 1096 | -------------------------------------------------------------------------------- /apps/common/src/utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Defines general utility functions. 3 | ------------------------------------------------------------------------------- 4 | MIT License 5 | 6 | Copyright (c) 2019 Ivan Roberto de Oliveira 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | #include "utils.hpp" 27 | 28 | const char *reason_str(virt86::VMExitReason reason) noexcept { 29 | switch (reason) { 30 | case virt86::VMExitReason::Normal: return "Normal"; 31 | case virt86::VMExitReason::Cancelled: return "Cancelled"; 32 | case virt86::VMExitReason::Interrupt: return "Interrupt"; 33 | case virt86::VMExitReason::PIO: return "Port I/O"; 34 | case virt86::VMExitReason::MMIO: return "MMIO"; 35 | case virt86::VMExitReason::Step: return "Single stepping"; 36 | case virt86::VMExitReason::SoftwareBreakpoint: return "Software breakpoint"; 37 | case virt86::VMExitReason::HardwareBreakpoint: return "Hardware breakpoint"; 38 | case virt86::VMExitReason::HLT: return "HLT instruction"; 39 | case virt86::VMExitReason::CPUID: return "CPUID instruction"; 40 | case virt86::VMExitReason::MSRAccess: return "MSR access"; 41 | case virt86::VMExitReason::Exception: return "CPU exception"; 42 | case virt86::VMExitReason::Shutdown: return "VM is shutting down"; 43 | case virt86::VMExitReason::Error: return "Hypervisor error"; 44 | case virt86::VMExitReason::Unhandled: return "Unhandled reason"; 45 | default: return "Unknown/unexpected reason"; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /apps/x64-guest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Creates a virtual machine that switches to 64-bit mode from real mode. 2 | # Based on instruction from https://wiki.osdev.org/Entering_Long_Mode_Directly 3 | # ------------------------------------------------------------------------------- 4 | # MIT License 5 | # 6 | # Copyright (c) 2019 Ivan Roberto de Oliveira 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | project(virt86-x64-guest VERSION 1.0.0 LANGUAGES CXX) 26 | 27 | include(GNUInstallDirs) 28 | 29 | ############################## 30 | # Source files 31 | # 32 | file(GLOB_RECURSE sources 33 | src/*.cpp 34 | ) 35 | 36 | file(GLOB_RECURSE private_headers 37 | src/*.hpp 38 | src/*.h 39 | ) 40 | 41 | file(GLOB_RECURSE public_headers 42 | include/*.hpp 43 | include/*.h 44 | ) 45 | 46 | ############################## 47 | # Project structure 48 | # 49 | add_executable(virt86-x64-guest ${sources} ${private_headers} ${public_headers}) 50 | 51 | target_include_directories(virt86-x64-guest 52 | PUBLIC 53 | $ 54 | $ 55 | PRIVATE 56 | ${CMAKE_CURRENT_SOURCE_DIR}/src 57 | ) 58 | 59 | find_package(virt86 CONFIG REQUIRED) 60 | target_link_libraries(virt86-x64-guest PUBLIC virt86::virt86) 61 | target_link_libraries(virt86-x64-guest PUBLIC virt86-demo-common) 62 | 63 | if(MSVC) 64 | vs_set_filters(BASE_DIR src FILTER_ROOT "Sources" SOURCES ${sources}) 65 | vs_set_filters(BASE_DIR src FILTER_ROOT "Private Headers" SOURCES ${private_headers}) 66 | vs_set_filters(BASE_DIR include FILTER_ROOT "Public Headers" SOURCES ${public_headers}) 67 | 68 | vs_use_edit_and_continue() 69 | endif() 70 | -------------------------------------------------------------------------------- /apps/x64-guest/README.md: -------------------------------------------------------------------------------- 1 | # 64-bit guest demo 2 | 3 | This application creates a virtual machine where the guest switches into 64-bit long mode and performs a series of 64-bit operations. 4 | 5 | The initialization procedure follows the instructions on [Entering Long Mode Directly in the OSDev wiki](https://wiki.osdev.org/Entering_Long_Mode_Directly). 6 | 7 | The application also executes several floating point instructions from the MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, FMA3 and AVX2 extensions, depending on support from the virtualization platform and the host CPU. 8 | -------------------------------------------------------------------------------- /apps/x64-guest/src/ram.asm: -------------------------------------------------------------------------------- 1 | ; Compile with NASM: 2 | ; $ nasm ram.asm -o ram.bin 3 | 4 | ; This is where the RAM program is loaded 5 | [BITS 64] 6 | org 0x10000 7 | 8 | ; Define bits for floating point extension tests 9 | %define FPTEST_MMX (1 << 0) 10 | %define FPTEST_SSE (1 << 1) 11 | %define FPTEST_SSE2 (1 << 2) 12 | %define FPTEST_SSE3 (1 << 3) 13 | %define FPTEST_SSSE3 (1 << 4) 14 | %define FPTEST_SSE4 (1 << 5) 15 | %define FPTEST_XSAVE (1 << 6) 16 | %define FPTEST_AVX (1 << 7) 17 | %define FPTEST_FMA3 (1 << 8) 18 | %define FPTEST_AVX2 (1 << 9) 19 | 20 | Entry: 21 | ; Do a simple read 22 | mov rax, [0x10000] 23 | 24 | ; Test the stack 25 | push rax 26 | pop r10 27 | mov rdx, r10 28 | 29 | Host.PageManipulation1: 30 | hlt ; Stop for a moment to let the host manipulate the page tables 31 | mov rsi, 0x100000000 ; Test new memory allocated by the host 32 | mov rax, [rsi] 33 | 34 | Host.PageManipulation2: 35 | hlt ; Let the host manipulate the page tables again 36 | mov rax, [rsi] ; Test modification by the host 37 | hlt ; Stop here 38 | 39 | FPTests.Init: 40 | xor r15, r15 ; R15 will contain bits indicating which features the guest supports and will test 41 | 42 | mov eax, 1 ; Read CPUID page 1 43 | xor rcx, rcx 44 | cpuid 45 | 46 | test edx, (1 << 23) ; MMX bit 47 | jz FPTests.Init.End 48 | or r15, FPTEST_MMX 49 | 50 | test edx, (1 << 25) ; SSE bit 51 | jz FPTests.Init.End 52 | or r15, FPTEST_SSE 53 | 54 | test edx, (1 << 26) ; SSE2 bit 55 | jz FPTests.Init.End 56 | or r15, FPTEST_SSE2 57 | 58 | test ecx, (1 << 0) ; SSE3 bit 59 | jz FPTests.Init.End 60 | or r15, FPTEST_SSE3 61 | 62 | test ecx, (1 << 9) ; SSSE3 bit 63 | jz FPTests.Init.End 64 | or r15, FPTEST_SSSE3 65 | 66 | test ecx, (0b11 << 19) ; SSE4_1 and SSE4_2 bits 67 | jz FPTests.Init.End 68 | or r15, FPTEST_SSE4 69 | 70 | test ecx, (1 << 26) ; XSAVE bit 71 | jz FPTests.Init.End 72 | or r15, FPTEST_XSAVE 73 | 74 | test ecx, (1 << 28) ; AVX bit 75 | jz FPTests.Init.End 76 | or r15, FPTEST_AVX 77 | 78 | test ecx, (1 << 12) ; FMA3 bit 79 | jz FPTests.Init.End 80 | or r15, FPTEST_FMA3 81 | 82 | mov eax, 7 ; Read CPUID page 7 83 | xor ecx, ecx 84 | cpuid 85 | 86 | test ebx, (1 << 5) ; AVX2 bit 87 | jz FPTests.Init.End 88 | or r15, FPTEST_AVX2 89 | 90 | FPTests.Init.End: 91 | hlt 92 | 93 | MMX.Test: 94 | test r15, FPTEST_MMX ; Check if MMX test is enabled 95 | jz FPTests.End ; Leave tests if disabled 96 | 97 | emms ; Clear MMX state 98 | 99 | movq mm0, [mmx.v1] ; Load first vector 100 | movq mm1, [mmx.v2] ; Load second vector 101 | movq mm2, [mmx.v3] ; Load third vector 102 | 103 | pmullw mm0, mm1 ; Multiply v1 and v2, store low words into mm0 104 | paddw mm0, mm2 ; Add v3 to result 105 | 106 | movq [mmx.r], mm0 ; Write result to memory 107 | movq rax, mm0 ; Copy result to RAX 108 | lea rsi, [mmx.r] ; Put address of result into RSI 109 | 110 | hlt ; Let the host check the results 111 | emms ; Be a good citizen and clear MMX state after we're done 112 | 113 | SSE.Enable: 114 | test r15, FPTEST_SSE ; Check if SSE test is enabled 115 | jz FPTests.End ; Leave tests if disabled 116 | 117 | mov rax, cr0 118 | and ax, 0xFFFB ; Clear coprocessor emulation CR0.EM 119 | or ax, 0x2 ; Set coprocessor monitoring CR0.MP 120 | mov cr0, rax 121 | mov rax, cr4 122 | or eax, (0b11 << 9) ; Set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time 123 | mov cr4, rax 124 | 125 | SSE.Test: 126 | movups xmm0, [sse.v1] ; Load first vector 127 | movups xmm1, [sse.v2] ; Load second vector 128 | 129 | addps xmm0, xmm1 ; Add v1 and v2, store result in xmm0 130 | mulps xmm0, xmm1 ; Multiply result by v2, store result in xmm0 131 | subps xmm0, xmm1 ; Subtract v2 from result, store result in xmm0 132 | 133 | movups [sse.r], xmm0 ; Write result to memory 134 | movq rax, xmm0 ; Copy low 64 bits of result to RAX 135 | lea rsi, [sse.r] ; Put address of result into RSI 136 | 137 | hlt ; Let the host check the result 138 | 139 | SSE2.Test: 140 | test r15, FPTEST_SSE2 ; Check if SSE2 test is enabled 141 | jz FPTests.End ; Leave tests if disabled 142 | 143 | movupd xmm0, [sse2.v1] ; Load first vector 144 | movupd xmm1, [sse2.v2] ; Load second vector 145 | 146 | addpd xmm0, xmm1 ; Add v1 and v2, store result in xmm0 147 | mulpd xmm0, xmm1 ; Multiply result by v2, store result in xmm0 148 | subpd xmm0, xmm1 ; Subtract v2 from result, store result in xmm0 149 | 150 | movupd [sse2.r], xmm0 ; Write result to memory 151 | movq rax, xmm0 ; Copy low 64 bits of result to RAX 152 | lea rsi, [sse2.r] ; Put address of result into RSI 153 | 154 | hlt ; Let the host check the result 155 | 156 | SSE3.Test: 157 | test r15, FPTEST_SSE3 ; Check if SSE3 test is enabled 158 | jz FPTests.End ; Leave tests if disabled 159 | 160 | movupd xmm0, [sse3.v1] ; Load first vector 161 | movupd xmm1, [sse3.v2] ; Load second vector 162 | 163 | haddpd xmm0, xmm1 ; Horizontally add the values in xmm1 and xmm0, store results in xmm0 164 | 165 | movupd [sse3.r], xmm0 ; Write result to memory 166 | movq rax, xmm0 ; Copy low 64 bits of result to RAX 167 | lea rsi, [sse3.r] ; Put address of result into RSI 168 | 169 | hlt ; Let the host check the result 170 | 171 | SSSE3.Test: 172 | test r15, FPTEST_SSSE3 ; Check if SSSE3 test is enabled 173 | jz FPTests.End ; Leave tests if disabled 174 | 175 | movupd xmm0, [ssse3.v] ; Load vector into xmm0 176 | movupd xmm1, [ssse3.v] ; Load vector into xmm1 177 | 178 | pabsd xmm0, xmm0 ; Compute absolute values for xmm0 179 | phaddd xmm1, xmm0 ; Horizontally add each pair of consecutive 32-bit integers from xmm0 and xmm1, pack results into xmm1 180 | 181 | movupd [ssse3.r], xmm1 ; Write result to memory 182 | movq rax, xmm1 ; Copy low 64 bits of result to RAX 183 | lea rsi, [ssse3.r] ; Put address of result into RSI 184 | 185 | hlt ; Let the host check the result 186 | 187 | SSE4.Test: ; Includes SSE4.1 and SSE4.2 188 | test r15, FPTEST_SSE4 ; Check if SSE4 test is enabled 189 | jz FPTests.End ; Leave tests if disabled 190 | 191 | movupd xmm0, [sse4.v1] ; Load first vector into xmm0 192 | movupd xmm1, [sse4.v2] ; Load second vector into xmm1 193 | movupd xmm2, [sse4.v2] ; Load third vector into xmm2 194 | 195 | pmuldq xmm0, xmm1 ; [SSE4.1] Multiply first and third signed dwords xmm0 196 | ; with first and third signed dwords in xmm1, store qword results in xmm0 197 | pcmpgtq xmm2, xmm0 ; [SSE4.2] Compare qwords in xmm0 and xmm2 for greater than. 198 | ; If true, sets corresponding element in xmm0 to all 1s, otherwise all 0s 199 | 200 | movupd [sse4.r], xmm2 ; Write result to memory 201 | movq rax, xmm2 ; Copy low 64 bits of result to RAX 202 | lea rsi, [sse4.r] ; Put address of result into RSI 203 | 204 | hlt ; Let the host check the result 205 | 206 | AVX.Enable: 207 | test r15, FPTEST_AVX ; Check if AVX test is enabled 208 | jz FPTests.End ; Leave tests if disabled 209 | 210 | mov rax, cr4 211 | or eax, (1 << 18) ; Set CR4.OSXSAVE 212 | mov cr4, rax 213 | xor rcx, rcx 214 | xgetbv ; Load XCR0 register 215 | or eax, 7 ; Set AVX, SSE, X87 bits 216 | xsetbv ; Save back to XCR0 217 | 218 | AVX.Test: 219 | test r15, FPTEST_AVX ; Check if AVX test is enabled 220 | jz FPTests.End ; Leave tests if disabled 221 | 222 | vzeroall ; Clear YMM registers 223 | vmovups ymm0, [avx.v1] ; Load v1 into ymm0 224 | vmovups ymm1, [avx.v2] ; Load v2 into ymm1 225 | vmovups ymm2, [avx.v3] ; Load v3 into ymm2 226 | 227 | vaddps ymm3, ymm0, ymm1 ; Add ymm0 and ymm1, store result in ymm3 228 | vmulps ymm3, ymm3, ymm2 ; Multiply ymm3 with ymm2, store result in ymm3 229 | 230 | vmovups [avx.r], ymm3 ; Write result to memory 231 | vmovq rax, xmm3 ; Copy low 64 bits of result to RAX 232 | lea rsi, [avx.r] ; Put address of result into RSI 233 | 234 | hlt ; Let the host check the result 235 | 236 | FMA3.Test: 237 | test r15, FPTEST_FMA3 ; Check if FMA3 test is enabled 238 | jz FPTests.End ; Leave tests if disabled 239 | 240 | vmovups ymm0, [fma3.v1] ; Load v1 into ymm0 241 | vmovups ymm1, [fma3.v2] ; Load v2 into ymm1 242 | vmovups ymm2, [fma3.v3] ; Load v3 into ymm2 243 | 244 | vfmadd132pd ymm0, ymm1, ymm2 ; ymm0 = ymm0 * ymm2 + ymm1 245 | 246 | vmovups [fma3.r], ymm0 ; Write result to memory 247 | vmovq rax, xmm0 ; Copy low 64 bits of result to RAX 248 | lea rsi, [fma3.r] ; Put address of result into RSI 249 | 250 | hlt ; Let the host check the result 251 | 252 | AVX2.Test: 253 | test r15, FPTEST_AVX2 ; Check if AVX2 test is enabled 254 | jz FPTests.End ; Leave tests if disabled 255 | 256 | vmovups ymm14, [avx2.v] ; Load v into ymm14 257 | 258 | vpermq ymm15, ymm14, 0x1B ; Permutates qwords from ymm6 to reverse order, store result in ymm15 259 | 260 | vmovups [avx2.r], ymm15 ; Write result to memory 261 | vmovq rax, xmm15 ; Copy low 64 bits of result to RAX 262 | lea rsi, [avx2.r] ; Put address of result into RSI 263 | 264 | hlt ; Let the host check the result 265 | 266 | XSAVE.Init: 267 | test r15, FPTEST_XSAVE ; Check if XSAVE test is enabled 268 | jz FPTests.End ; Leave tests if disabled 269 | 270 | xor rax, rax ; Clear RAX, in order to clear data 271 | 272 | mov rcx, 16 ; Clear base addresses 273 | lea rdi, [xsavebases] 274 | rep stosq 275 | 276 | mov rcx, 16 ; Clear sizes 277 | lea rdi, [xsavesizes] 278 | rep stosq 279 | 280 | mov [xsavealign], rax ; Clear alignment bits 281 | 282 | mov rdx, 1 << 17 ; Initialize RDX with our bit mask 283 | mov rcx, 16 ; Initialize loop counter 284 | 285 | XSAVE.Init.Loop: 286 | mov r10, rcx ; Save RCX 287 | mov rax, 0xD ; Read CPUID page 0xD for the component... 288 | add rcx, 1 ; ... RCX + 1 (0 = main, 1 = reserved, 2..62 = XCR0.n, 63 = reserved) 289 | cpuid ; ... in order to retrieve its base address, size and alignment mode 290 | mov [xsavebases + rcx * 4], ebx ; EBX contains the base address 291 | mov [xsavesizes + rcx * 4], eax ; EAX contains the size 292 | test ecx, 2 ; ECX contains the alignment bit 293 | jz XSAVE.Init.NoAlign ; If the alignment bit is set... 294 | or [xsavealign], rdx ; ... set the corresponding bit in the destination 295 | 296 | XSAVE.Init.NoAlign: 297 | mov rcx, r10 ; Restore RCX 298 | shr rdx, 1 ; Shift test bit 299 | loop XSAVE.Init.Loop ; Repeat until all components have been examined 300 | 301 | XSAVE.Test: 302 | mov rdx, ~0 ; Enable all XSAVE features... 303 | mov rax, ~0 ; ... on RDX and RAX 304 | xsave [xsavearea] ; XSAVE to reserved memory area 305 | 306 | lea rsi, [xsavearea] ; Put address of XSAVE area into RSI 307 | lea r12, [xsavebases] ; Put address of base addresses into R12 308 | lea r13, [xsavesizes] ; Put address of sizes into R13 309 | lea r14, [xsavealign] ; Put address of alignment bits into R14 310 | 311 | hlt ; Let the host check the result 312 | 313 | FPTests.End: 314 | ; We're done 315 | 316 | Die: 317 | cli 318 | hlt 319 | jmp Die 320 | 321 | ; Data for MMX test 322 | ALIGN 16 323 | mmx.v1: dw 5, 10, 15, 20 324 | mmx.v2: dw 2, 2, 2, 2 325 | mmx.v3: dw 1, 2, 3, 4 326 | mmx.r: resw 4 327 | 328 | ; Data for SSE test 329 | ALIGN 16 330 | sse.v1: dd 1.1, 2.2, 3.3, 4.4 331 | sse.v2: dd 5.5, 6.6, 7.7, 8.8 332 | sse.r: resd 4 333 | 334 | ; Data for SSE2 test 335 | ALIGN 16 336 | sse2.v1: dq 1.1, 2.2 337 | sse2.v2: dq 3.3, 4.4 338 | sse2.r: resq 2 339 | 340 | ; Data for SSE3 test 341 | ALIGN 16 342 | sse3.v1: dq 1.5, 2.5 343 | sse3.v2: dq 2.5, -0.5 344 | sse3.r: resq 2 345 | 346 | ; Data for SSSE3 test 347 | ALIGN 16 348 | ssse3.v: dd 1234, -4321, -1234, 4321 349 | ssse3.r: resd 4 350 | 351 | ; Data for SSE4 test 352 | ALIGN 16 353 | sse4.v1: dd 0, -30, 0, 60 354 | sse4.v2: dd 0, -6, 0, 2 355 | sse4.v3: dq 120, 120 356 | sse4.r: resd 4 357 | 358 | ; Data for AVX test 359 | ALIGN 16 360 | avx.v1: dd 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5 361 | avx.v2: dd 8.5, 7.5, 6.5, 5.5, 4.5, 3.5, 2.5, 1.5 362 | avx.v3: dd 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5 363 | avx.r: resd 8 364 | 365 | ; Data for FMA3 test 366 | ALIGN 16 367 | fma3.v1: dq 0.5, 1.0, 1.5, 2.0 368 | fma3.v2: dq 2.0, 2.5, 3.0, 3.5 369 | fma3.v3: dq 4.0, 3.0, 2.0, 1.0 370 | fma3.r: resq 4 371 | 372 | ; Data for AVX2 test 373 | ALIGN 16 374 | avx2.v: dq 1, 2, 3, 4 375 | avx2.r: resq 4 376 | 377 | ; XSAVE state area 378 | ALIGN 4096 379 | xsavearea: resb 4096 ; XSAVE data area 380 | xsavebases: times 16 dd 0 ; Base offsets of each XSAVE component 381 | xsavesizes: times 16 dd 0 ; Sizes of each XSAVE component 382 | xsavealign: dd 0 ; Alignment bits of each XSAVE component -------------------------------------------------------------------------------- /apps/x64-guest/src/rom.asm: -------------------------------------------------------------------------------- 1 | ; Compile with NASM: 2 | ; $ nasm rom.asm -o rom.bin 3 | %define PAGE_PRESENT (1 << 0) 4 | %define PAGE_WRITE (1 << 1) 5 | 6 | %define CODE_SEG 0x0008 7 | %define DATA_SEG 0x0010 8 | 9 | ; This is where the ROM is loaded 10 | org 0xFFFF0000 11 | 12 | ; Main code adapted from https://wiki.osdev.org/Entering_Long_Mode_Directly 13 | 14 | ALIGN 4 15 | IDT: 16 | .Length dw 0 17 | .Base dd 0 18 | 19 | ; Function to switch directly to long mode from real mode. 20 | ; Identity maps the first 2MiB and last 64KiB. 21 | ; Uses Intel syntax. 22 | 23 | ; es:edi Should point to a valid page-aligned 16KiB buffer, for the PML4, PDPT, PD and a PT. 24 | ; ss:esp Should point to memory that can be used as a small (1 uint32_t) stack 25 | 26 | SwitchToLongMode: 27 | ; Zero out the 16KiB buffer. 28 | ; Since we are doing a rep stosd, count should be bytes/4. 29 | push di ; REP STOSD alters DI. 30 | mov ecx, 0x1000 31 | xor eax, eax 32 | cld 33 | rep stosd 34 | pop di ; Get DI back. 35 | 36 | 37 | ; Build the Page Map Level 4. 38 | ; es:di points to the Page Map Level 4 table. 39 | lea eax, [es:di + 0x1000] ; Put the address of the Page Directory Pointer Table in to EAX. 40 | or eax, PAGE_PRESENT | PAGE_WRITE ; Or EAX with the flags - present flag, writable flag. 41 | mov [es:di], eax ; Store the value of EAX as the first PML4E. 42 | 43 | 44 | ; Build the Page Directory Pointer Table. 45 | lea eax, [es:di + 0x2000] ; Put the address of the Page Directory in to EAX. 46 | or eax, PAGE_PRESENT | PAGE_WRITE ; Or EAX with the flags - present flag, writable flag. 47 | mov [es:di + 0x1000], eax ; Store the value of EAX as the first PDPTE. 48 | mov [es:di + 0x1018], eax ; Store the value of EAX as the fourth PDPTE. 49 | 50 | ; Build the Page Directory. 51 | lea eax, [es:di + 0x3000] ; Put the address of the Page Table in to EAX. 52 | or eax, PAGE_PRESENT | PAGE_WRITE ; Or EAX with the flags - present flag, writeable flag. 53 | mov [es:di + 0x2000], eax ; Store to value of EAX as the first PDE. 54 | 55 | add eax, 0x1000 ; Move to the next entry 56 | mov [es:di + 0x2ff8], eax ; Store the value of EAX as the last PDE. 57 | 58 | push di ; Save DI for the time being. 59 | 60 | ; Build the RAM Page Table. 61 | lea di, [di + 0x3000] ; Point DI to the RAM page table. 62 | mov eax, PAGE_PRESENT | PAGE_WRITE ; Move the flags into EAX - and point it to 0x0000. 63 | 64 | .LoopRAMPageTable: 65 | mov [es:di], eax 66 | add eax, 0x1000 67 | add di, 8 68 | cmp eax, 0x200000 ; If we did all 2MiB, end. 69 | jb .LoopRAMPageTable 70 | 71 | pop di ; Restore DI. 72 | push di 73 | 74 | ; Build the ROM Page Table. 75 | lea di, [di + 0x4F80] ; Point DI to the ROM page table. 76 | mov eax, 0xFFFF0000 | PAGE_PRESENT | PAGE_WRITE ; Move the flags into EAX - and point it to 0xFFFF0000. 77 | 78 | .LoopROMPageTable: 79 | mov [es:di], eax 80 | add eax, 0x1000 81 | add di, 8 82 | cmp eax, 0xFFFF0000 ; If we did all 64KiB, end. 83 | jnb .LoopROMPageTable 84 | 85 | pop di ; Restore DI. 86 | 87 | ; Disable IRQs 88 | ;mov al, 0xFF ; Out 0xFF to 0xA1 and 0x21 to disable all IRQs. 89 | ;out 0xA1, al 90 | ;out 0x21, al 91 | 92 | nop 93 | nop 94 | 95 | o32 lidt [cs:IDT] ; Load a zero length IDT so that any NMI causes a triple fault. 96 | 97 | ; Enter long mode. 98 | mov eax, 10100000b ; Set the PAE and PGE bit. 99 | mov cr4, eax 100 | 101 | mov edx, edi ; Point CR3 at the PML4. 102 | mov cr3, edx 103 | 104 | mov ecx, 0xC0000080 ; Read from the EFER MSR. 105 | rdmsr 106 | 107 | or eax, 0x00000100 ; Set the LME bit. 108 | wrmsr 109 | 110 | mov ebx, cr0 ; Activate long mode - 111 | or ebx, 0x80000001 ; - by enabling paging and protection simultaneously. 112 | mov cr0, ebx 113 | 114 | o32 lgdt [cs:GDT.Pointer] ; Load GDT.Pointer defined below. 115 | 116 | jmp dword CODE_SEG:LongMode ; Load CS with 64 bit segment and flush the instruction cache 117 | 118 | ; Global Descriptor Table 119 | ALIGN 16 120 | GDT: 121 | .Null: 122 | dq 0x0000000000000000 ; Null Descriptor - should be present. 123 | 124 | .Code: 125 | dq 0x00209B0000000000 ; 64-bit code descriptor (exec/read). 126 | dq 0x0000930000000000 ; 64-bit data descriptor (read/write). 127 | 128 | .End: 129 | ALIGN 4 130 | dw 0 ; Padding to make the "address of the GDT" field aligned on a 4-byte boundary 131 | 132 | .Pointer: 133 | dw GDT.End - GDT ; 16-bit Size (Limit) of GDT. 134 | dd GDT ; 32-bit Base Address of GDT. (CPU will zero extend to 64-bit) 135 | 136 | 137 | [BITS 64] 138 | LongMode: 139 | ; Load segments 140 | mov ax, DATA_SEG 141 | mov ds, ax 142 | mov es, ax 143 | mov fs, ax 144 | mov gs, ax 145 | mov ss, ax 146 | 147 | ; Put the stack at the top of RAM 148 | mov rsp, 0x200000 149 | mov rbp, rsp 150 | 151 | ; Jump to program entry point in RAM 152 | mov rax, 0x10000 153 | jmp rax 154 | 155 | 156 | ; Reset vector entry point 157 | 158 | [BITS 16] 159 | times 0xFFF0 - ($-$$) db 0 160 | jmp SwitchToLongMode 161 | cli 162 | hlt 163 | times 0xFFFF - ($-$$) + 1 hlt -------------------------------------------------------------------------------- /apps/x64-guest/src/x64guest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | Entry point of the 64-bit guest demo. 5 | 6 | Creates a virtual machine that switches to 64-bit mode from real mode. 7 | Based on instruction from https://wiki.osdev.org/Entering_Long_Mode_Directly 8 | ------------------------------------------------------------------------------- 9 | MIT License 10 | 11 | Copyright (c) 2019 Ivan Roberto de Oliveira 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | */ 31 | #include "virt86/virt86.hpp" 32 | 33 | #include "print_helpers.hpp" 34 | #include "align_alloc.hpp" 35 | #include "utils.hpp" 36 | 37 | #include 38 | 39 | #if defined(_WIN32) 40 | # include 41 | #elif defined(__linux__) 42 | # include 43 | #elif defined(__APPLE__) 44 | # include 45 | #endif 46 | 47 | #include 48 | #include 49 | 50 | // Define constants matching those in the guest code to determine which tests 51 | // the host code will check 52 | const uint32_t FPTEST_MMX = (1 << 0); 53 | const uint32_t FPTEST_SSE = (1 << 1); 54 | const uint32_t FPTEST_SSE2 = (1 << 2); 55 | const uint32_t FPTEST_SSE3 = (1 << 3); 56 | const uint32_t FPTEST_SSSE3 = (1 << 4); 57 | const uint32_t FPTEST_SSE4 = (1 << 5); 58 | const uint32_t FPTEST_XSAVE = (1 << 6); 59 | const uint32_t FPTEST_AVX = (1 << 7); 60 | const uint32_t FPTEST_FMA3 = (1 << 8); 61 | const uint32_t FPTEST_AVX2 = (1 << 9); 62 | 63 | using namespace virt86; 64 | 65 | void runToHLT(VirtualProcessor& vp) { 66 | // Run until HLT is reached 67 | bool running = true; 68 | while (running) { 69 | auto execStatus = vp.Run(); 70 | if (execStatus != VPExecutionStatus::OK) { 71 | printf("Virtual CPU execution failed\n"); 72 | break; 73 | } 74 | 75 | printRegs(vp); 76 | printf("\n"); 77 | 78 | auto& exitInfo = vp.GetVMExitInfo(); 79 | switch (exitInfo.reason) { 80 | case VMExitReason::HLT: 81 | printf("HLT reached\n"); 82 | running = false; 83 | break; 84 | case VMExitReason::Shutdown: 85 | printf("VCPU shutting down\n"); 86 | running = false; 87 | break; 88 | case VMExitReason::Error: 89 | printf("VCPU execution failed\n"); 90 | running = false; 91 | break; 92 | } 93 | } 94 | } 95 | 96 | int main(int argc, char* argv[]) { 97 | // Require two arguments: the ROM code and the RAM code 98 | if (argc < 3) { 99 | printf("fatal: no input files specified\n"); 100 | printf("usage: %s \n", argv[0]); 101 | return -1; 102 | } 103 | 104 | // ROM and RAM sizes 105 | const uint32_t romSize = PAGE_SIZE * 16; // 64 KiB 106 | const uint32_t ramSize = PAGE_SIZE * 512; // 2 MiB 107 | const uint64_t romBase = 0xFFFF0000; 108 | const uint64_t ramBase = 0x0; 109 | const uint64_t ramProgramBase = 0x10000; 110 | 111 | // --- ROM ------------------ 112 | 113 | // Initialize ROM 114 | uint8_t *rom = alignedAlloc(romSize); 115 | if (rom == NULL) { 116 | printf("fatal: failed to allocate memory for ROM\n"); 117 | return -1; 118 | } 119 | printf("ROM allocated: %u bytes\n", romSize); 120 | 121 | // Open ROM file specified in the command line 122 | FILE *fp = fopen(argv[1], "rb"); 123 | if (fp == NULL) { 124 | printf("fatal: could not open ROM file: %s\n", argv[1]); 125 | return -1; 126 | } 127 | 128 | // Check that the ROM file is exactly 64 KiB 129 | fseek(fp, 0, SEEK_END); 130 | long len = ftell(fp); 131 | fseek(fp, 0, SEEK_SET); 132 | if (len != romSize) { 133 | printf("fatal: ROM file size must be %ud bytes\n", romSize); 134 | return -1; 135 | } 136 | 137 | // Load ROM from the file 138 | size_t readLen = fread(rom, 1, len, fp); 139 | if (readLen != len) { 140 | printf("fatal: could not fully read ROM file\n"); 141 | return -1; 142 | } 143 | fclose(fp); 144 | printf("ROM loaded from %s\n", argv[1]); 145 | 146 | // --- RAM ------------------ 147 | 148 | // Initialize and clear RAM 149 | uint8_t *ram = alignedAlloc(ramSize); 150 | if (ram == NULL) { 151 | printf("fatal: failed to allocate memory for RAM\n"); 152 | return -1; 153 | } 154 | memset(ram, 0, ramSize); 155 | printf("RAM allocated: %u bytes\n", ramSize); 156 | 157 | // Open RAM file specified in the command line 158 | fp = fopen(argv[2], "rb"); 159 | if (fp == NULL) { 160 | printf("fatal: could not open RAM file: %s\n", argv[2]); 161 | return -1; 162 | } 163 | 164 | // Check that the RAM file fits in our RAM 165 | fseek(fp, 0, SEEK_END); 166 | len = ftell(fp); 167 | fseek(fp, 0, SEEK_SET); 168 | if (len > ramSize - ramProgramBase) { 169 | printf("fatal: RAM file size must be no larger than %llu bytes\n", ramSize - ramProgramBase); 170 | return -1; 171 | } 172 | 173 | // Load RAM from the file 174 | readLen = fread(&ram[0x10000], 1, len, fp); 175 | if (readLen != len) { 176 | printf("fatal: could not fully read RAM file\n"); 177 | return -1; 178 | } 179 | fclose(fp); 180 | printf("RAM loaded from %s (%zu bytes)\n", argv[2], readLen); 181 | 182 | printf("\n"); 183 | 184 | // ----- Hypervisor platform initialization ------------------------------------------------------------------------------- 185 | 186 | printf("virt86 version: " VIRT86_VERSION "\n\n"); 187 | 188 | // Pick the first hypervisor platform that is available and properly initialized on this system. 189 | printf("Loading virtualization platform... "); 190 | 191 | bool foundPlatform = false; 192 | size_t platformIndex = 0; 193 | for (size_t i = 0; i < array_size(PlatformFactories); i++) { 194 | const Platform& platform = PlatformFactories[i](); 195 | if (platform.GetInitStatus() == PlatformInitStatus::OK) { 196 | printf("%s loaded successfully\n", platform.GetName().c_str()); 197 | foundPlatform = true; 198 | platformIndex = i; 199 | break; 200 | } 201 | } 202 | 203 | if (!foundPlatform) { 204 | printf("none found\n"); 205 | return -1; 206 | } 207 | 208 | Platform& platform = PlatformFactories[platformIndex](); 209 | auto& features = platform.GetFeatures(); 210 | 211 | // Print out the host's features 212 | printHostFeatures(); 213 | 214 | // Print out the platform's features 215 | printPlatformFeatures(platform); 216 | 217 | // Create virtual machine 218 | VMSpecifications vmSpecs = { 0 }; 219 | vmSpecs.numProcessors = 1; 220 | printf("Creating virtual machine... "); 221 | auto opt_vm = platform.CreateVM(vmSpecs); 222 | if (!opt_vm) { 223 | printf("failed\n"); 224 | return -1; 225 | } 226 | printf("succeeded\n"); 227 | VirtualMachine& vm = opt_vm->get(); 228 | 229 | // Map ROM to the top of the 32-bit address range 230 | printf("Mapping ROM... "); 231 | { 232 | auto memMapStatus = vm.MapGuestMemory(romBase, romSize, MemoryFlags::Read | MemoryFlags::Execute, rom); 233 | printMemoryMappingStatus(memMapStatus); 234 | if (memMapStatus != MemoryMappingStatus::OK) return -1; 235 | } 236 | 237 | // Map RAM to the bottom of the 32-bit address range 238 | printf("Mapping RAM... "); 239 | { 240 | auto memMapStatus = vm.MapGuestMemory(ramBase, ramSize, MemoryFlags::Read | MemoryFlags::Write | MemoryFlags::Execute | MemoryFlags::DirtyPageTracking, ram); 241 | printMemoryMappingStatus(memMapStatus); 242 | if (memMapStatus != MemoryMappingStatus::OK) return -1; 243 | } 244 | 245 | 246 | // Get the virtual processor 247 | printf("Retrieving virtual processor... "); 248 | auto opt_vp = vm.GetVirtualProcessor(0); 249 | if (!opt_vp) { 250 | printf("failed\n"); 251 | return -1; 252 | } 253 | auto& vp = opt_vp->get(); 254 | printf("succeeded\n"); 255 | 256 | printf("\nInitial CPU register state:\n"); 257 | printRegs(vp); 258 | printf("\n"); 259 | 260 | // The ROM code expects the following: 261 | // es:edi Should point to a valid page-aligned 16KiB buffer, for the PML4, PDPT, PD and a PT. 262 | // ss:esp Should point to memory that can be used as a small (1 uint32_t) stack 263 | // We'll set up our page table at 0x0 and use 0x10000 as the base of our stack, just below the user program. 264 | { 265 | RegValue edi, esp; 266 | edi.u32 = 0x0; 267 | esp.u32 = 0x10000; 268 | vp.RegWrite(Reg::EDI, edi); 269 | vp.RegWrite(Reg::ESP, esp); 270 | } 271 | 272 | // ----- Start ---------------------------------------------------------------------------------------------------- 273 | 274 | // Run next block 275 | runToHLT(vp); 276 | printf("\n"); 277 | 278 | // ----- Page table manipulation ---------------------------------------------------------------------------------- 279 | 280 | // Map a page of memory to the guest and write some data to be read by the guest in order to check if the mapping worked 281 | 282 | // Values written to the newly allocated pages 283 | const static uint64_t checkValue1 = 0xfedcba9876543210; 284 | const static uint64_t checkValue2 = 0x0123456789abcdef; 285 | 286 | // Allocate host memory for the new page and write the check value to its base address 287 | // We allocate near the top of the maximum supported GPA, with one page of breathing room because HAXM doesn't let us use the last page 288 | const size_t moreRamSize = PAGE_SIZE * 2; 289 | const uint64_t moreRamBase = features.guestPhysicalAddress.maxAddress - moreRamSize - 0x1000; 290 | uint8_t* moreRam = alignedAlloc(moreRamSize); 291 | if (moreRam == NULL) { 292 | printf("fatal: failed to allocate memory for additional RAM\n"); 293 | return -1; 294 | } 295 | memset(moreRam, 0, moreRamSize); 296 | printf("Additional RAM allocated: %zu bytes\n", moreRamSize); 297 | memcpy(moreRam, &checkValue1, sizeof(checkValue1)); 298 | memcpy(moreRam + PAGE_SIZE, &checkValue2, sizeof(checkValue2)); 299 | 300 | // Map the memory to the guest at the desired base address 301 | printf("Mapping additional RAM to 0x%llx... ", moreRamBase); 302 | { 303 | auto memMapStatus = vm.MapGuestMemory(moreRamBase, moreRamSize, MemoryFlags::Read | MemoryFlags::Write | MemoryFlags::Execute, moreRam); 304 | printMemoryMappingStatus(memMapStatus); 305 | if (memMapStatus != MemoryMappingStatus::OK) return -1; 306 | } 307 | 308 | // Map the newly added physical page to linear address 0x100000000 309 | // PML4E for that virtual address already exists 310 | *(uint64_t*)&ram[0x1020] = 0x5023; // PDPTE -> PDE at 0x5000 311 | *(uint64_t*)&ram[0x5000] = 0x6023; // PDE -> PTE at 0x6000 312 | *(uint64_t*)&ram[0x6000] = (moreRamBase & ~0xFFF) | 0x23; // PTE -> physical address 313 | 314 | // Display linear-to-physical address translation of the new page 315 | printAddressTranslation(vp, 0x100000000); 316 | printf("\n"); 317 | 318 | // Run next block 319 | runToHLT(vp); 320 | printf("\n"); 321 | 322 | // Update page mapping to point to the second page of the newly allocated RAM 323 | *(uint64_t*)&ram[0x6000] = ((moreRamBase & ~0xFFF) + 0x1000) | 0x23; // PTE -> physical address 324 | 325 | // Display new address translation 326 | printf("Page mapping updated:\n"); 327 | printAddressTranslation(vp, 0x100000000); 328 | printf("\n"); 329 | 330 | // Run next block 331 | runToHLT(vp); 332 | printf("\n"); 333 | 334 | // ----- Floating point extensions tests initialization ----------------------------------------------------------- 335 | 336 | // Define some helper functions for tests 337 | static constexpr float float_epsilon = 1e-5f; 338 | auto feq = [](float x, float y) -> bool { return std::fabs(x - y) <= float_epsilon; }; 339 | 340 | static constexpr double double_epsilon = 1e-9f; 341 | auto deq = [](double x, double y) -> bool { return fabs(x - y) <= double_epsilon; }; 342 | 343 | // Run next block 344 | runToHLT(vp); 345 | 346 | // Get the test bit set 347 | uint64_t testBits; 348 | { 349 | RegValue r15; 350 | vp.RegRead(Reg::R15, r15); 351 | testBits = r15.u64; 352 | } 353 | 354 | // Display tests 355 | if (testBits == 0) { 356 | printf("Guest does not support any floating point features... wait, what?\n"); 357 | } 358 | else { 359 | printf("Guest will execute the following tests:"); 360 | if (testBits & FPTEST_MMX) printf(" MMX"); 361 | if (testBits & FPTEST_SSE) printf(" SSE"); 362 | if (testBits & FPTEST_SSE2) printf(" SSE2"); 363 | if (testBits & FPTEST_SSE3) printf(" SSE3"); 364 | if (testBits & FPTEST_SSSE3) printf(" SSSE3"); 365 | if (testBits & FPTEST_SSE4) printf(" SSE4"); 366 | if (testBits & FPTEST_XSAVE) printf(" XSAVE"); 367 | if (testBits & FPTEST_AVX) printf(" AVX"); 368 | if (testBits & FPTEST_FMA3) printf(" FMA3"); 369 | if (testBits & FPTEST_AVX2) printf(" AVX2"); 370 | printf("\n\n"); 371 | } 372 | 373 | // ----- MMX ------------------------------------------------------------------------------------------------------ 374 | 375 | if (testBits & FPTEST_MMX) { 376 | // Run next block 377 | runToHLT(vp); 378 | printf("\n"); 379 | printMMRegs(vp, MMFormat::I16); 380 | printf("\n"); 381 | 382 | // Check result 383 | { 384 | RegValue rax, rsi, mm0; 385 | vp.RegRead(Reg::RAX, rax); 386 | vp.RegRead(Reg::RSI, rsi); // contains address of result in memory 387 | vp.RegRead(Reg::MM0, mm0); 388 | 389 | uint64_t memValue; 390 | vp.LMemRead(rsi.u64, sizeof(memValue), &memValue); 391 | 392 | if (rax.u64 == 0x002c00210016000b) printf("RAX contains the correct result\n"); 393 | if (memValue == 0x002c00210016000b) printf("Memory contains the correct result\n"); 394 | if (mm0.mm.i64[0] == 0x002c00210016000b) printf("MM0 contains the correct result\n"); 395 | printf("MMX test complete\n"); 396 | } 397 | printf("\n"); 398 | } 399 | else { 400 | printf("MMX not supported by guest; skipping test\n\n"); 401 | } 402 | 403 | // ----- SSE ------------------------------------------------------------------------------------------------------ 404 | 405 | if (testBits & FPTEST_SSE) { 406 | // Run next block 407 | runToHLT(vp); 408 | printf("\n"); 409 | printXMMRegs(vp, XMMFormat::F32); 410 | printf("\n"); 411 | 412 | // Check result 413 | { 414 | RegValue rax, rsi, xmm0; 415 | vp.RegRead(Reg::RAX, rax); 416 | vp.RegRead(Reg::RSI, rsi); // contains address of result in memory 417 | vp.RegRead(Reg::XMM0, xmm0); 418 | 419 | float memValue[4]; 420 | vp.LMemRead(rsi.u64, sizeof(memValue), &memValue); 421 | 422 | // Reinterpret RAX as if it were the lowest 64 bits of XMM0 423 | if (feq(rax.xmm.f32[0], 30.8f) && feq(rax.xmm.f32[1], 51.48f)) printf("RAX contains the correct result\n"); 424 | if (feq(memValue[0], 30.8f) && feq(memValue[1], 51.48f) && feq(memValue[2], 77.0f) && feq(memValue[3], 107.36f)) printf("Memory contains the correct result\n"); 425 | if (feq(xmm0.xmm.f32[0], 30.8f) && feq(xmm0.xmm.f32[1], 51.48f) && feq(xmm0.xmm.f32[2], 77.0f) && feq(xmm0.xmm.f32[3], 107.36f)) printf("XMM0 contains the correct result\n"); 426 | printf("SSE test complete\n"); 427 | } 428 | 429 | printf("\n"); 430 | } 431 | else { 432 | printf("SSE not supported by guest; skipping test\n\n"); 433 | } 434 | 435 | // ----- SSE2 ----------------------------------------------------------------------------------------------------- 436 | 437 | if (testBits & FPTEST_SSE2) { 438 | // Run next block 439 | runToHLT(vp); 440 | printf("\n"); 441 | printXMMRegs(vp, XMMFormat::F64); 442 | printf("\n"); 443 | 444 | // Check result 445 | { 446 | RegValue rax, rsi, xmm0; 447 | vp.RegRead(Reg::RAX, rax); 448 | vp.RegRead(Reg::RSI, rsi); // contains address of result in memory 449 | vp.RegRead(Reg::XMM0, xmm0); 450 | 451 | double memValue[4]; 452 | vp.LMemRead(rsi.u64, sizeof(memValue), &memValue); 453 | 454 | // Reinterpret RAX as if it were the lowest 64 bits of XMM0 455 | if (deq(rax.xmm.f64[0], 11.22)) printf("RAX contains the correct result\n"); 456 | if (deq(memValue[0], 11.22) && deq(memValue[1], 24.64)) printf("Memory contains the correct result\n"); 457 | if (deq(xmm0.xmm.f64[0], 11.22) && deq(xmm0.xmm.f64[1], 24.64)) printf("XMM0 contains the correct result\n"); 458 | printf("SSE2 test complete\n"); 459 | } 460 | 461 | printf("\n"); 462 | } 463 | else { 464 | printf("SSE2 not supported by guest; skipping test\n\n"); 465 | } 466 | 467 | // ----- SSE3 ----------------------------------------------------------------------------------------------------- 468 | 469 | if (testBits & FPTEST_SSE3) { 470 | // Run next block 471 | runToHLT(vp); 472 | printf("\n"); 473 | printXMMRegs(vp, XMMFormat::F64); 474 | printf("\n"); 475 | 476 | // Check result 477 | { 478 | RegValue rax, rsi, xmm0; 479 | vp.RegRead(Reg::RAX, rax); 480 | vp.RegRead(Reg::RSI, rsi); // contains address of result in memory 481 | vp.RegRead(Reg::XMM0, xmm0); 482 | 483 | double memValue[4]; 484 | vp.LMemRead(rsi.u64, sizeof(memValue), &memValue); 485 | 486 | // Reinterpret RAX as if it were the lowest 64 bits of XMM0 487 | if (deq(rax.xmm.f64[0], 4.0)) printf("RAX contains the correct result\n"); 488 | if (deq(memValue[0], 4.0) && deq(memValue[1], 2.0)) printf("Memory contains the correct result\n"); 489 | if (deq(xmm0.xmm.f64[0], 4.0) && deq(xmm0.xmm.f64[1], 2.0)) printf("XMM0 contains the correct result\n"); 490 | printf("SSE3 test complete\n"); 491 | } 492 | 493 | printf("\n"); 494 | } 495 | else { 496 | printf("SSE3 not supported by guest; skipping test\n\n"); 497 | } 498 | 499 | // ----- SSSE3 ---------------------------------------------------------------------------------------------------- 500 | 501 | if (testBits & FPTEST_SSSE3) { 502 | // Run next block 503 | runToHLT(vp); 504 | printf("\n"); 505 | printXMMRegs(vp, XMMFormat::I32); 506 | printf("\n"); 507 | 508 | // Check result 509 | { 510 | RegValue rax, rsi, xmm1; 511 | vp.RegRead(Reg::RAX, rax); 512 | vp.RegRead(Reg::RSI, rsi); // contains address of result in memory 513 | vp.RegRead(Reg::XMM1, xmm1); 514 | 515 | int32_t memValue[4]; 516 | vp.LMemRead(rsi.u64, sizeof(memValue), &memValue); 517 | 518 | // Reinterpret RAX as if it were the lowest 64 bits of XMM1 519 | if (rax.xmm.i32[0] == -3087 && rax.xmm.i32[1] == 3087) printf("RAX contains the correct result\n"); 520 | if (memValue[0] == -3087 && memValue[1] == 3087 && memValue[2] == 5555 && memValue[3] == 5555) printf("Memory contains the correct result\n"); 521 | if (xmm1.xmm.i32[0] == -3087 && xmm1.xmm.i32[1] == 3087 && xmm1.xmm.i32[2] == 5555 && xmm1.xmm.i32[3] == 5555) printf("XMM1 contains the correct result\n"); 522 | printf("SSSE3 test complete\n"); 523 | } 524 | 525 | printf("\n"); 526 | } 527 | else { 528 | printf("SSSE3 not supported by guest; skipping test\n\n"); 529 | } 530 | 531 | // ----- SSE4 ----------------------------------------------------------------------------------------------------- 532 | 533 | if (testBits & FPTEST_SSE4) { 534 | // Run next block 535 | runToHLT(vp); 536 | printf("\n"); 537 | printXMMRegs(vp, XMMFormat::I64); 538 | printf("\n"); 539 | 540 | // Check result 541 | { 542 | RegValue rax, rsi, xmm2; 543 | vp.RegRead(Reg::RAX, rax); 544 | vp.RegRead(Reg::RSI, rsi); // contains address of result in memory 545 | vp.RegRead(Reg::XMM2, xmm2); 546 | 547 | int64_t memValue[2]; 548 | vp.LMemRead(rsi.u64, sizeof(memValue), &memValue); 549 | 550 | if (rax.u64 == 0) printf("RAX contains the correct result\n"); 551 | if (memValue[0] == 0 && memValue[1] == -1) printf("Memory contains the correct result\n"); 552 | if (xmm2.xmm.i64[0] == 0 && xmm2.xmm.i64[1] == -1) printf("XMM2 contains the correct result\n"); 553 | printf("SSE4 test complete\n"); 554 | } 555 | 556 | printf("\n"); 557 | } 558 | else { 559 | printf("SSE4 not supported by guest; skipping test\n\n"); 560 | } 561 | 562 | // ----- AVX ------------------------------------------------------------------------------------------------------ 563 | 564 | if (testBits & FPTEST_AVX) { 565 | // Run next block 566 | runToHLT(vp); 567 | printf("\n"); 568 | printXMMRegs(vp, XMMFormat::F32); 569 | printf("\n"); 570 | 571 | // Check result 572 | { 573 | RegValue rax, rsi, xmm3; 574 | vp.RegRead(Reg::RAX, rax); 575 | vp.RegRead(Reg::RSI, rsi); // contains address of result in memory 576 | vp.RegRead(Reg::XMM3, xmm3); // TODO: should read YMM3, but no hypervisors support that so far 577 | 578 | float memValue[8]; 579 | vp.LMemRead(rsi.u64, sizeof(memValue), &memValue); 580 | 581 | // Reinterpret RAX as if it were the lowest 64 bits of XMM3 582 | if (feq(rax.xmm.f32[0], 10.0) && feq(rax.xmm.f32[1], 15.0)) printf("RAX contains the correct result\n"); 583 | if (feq(memValue[0], 10.0) && feq(memValue[1], 15.0) && feq(memValue[2], 20.0) && feq(memValue[3], 25.0) 584 | && feq(memValue[4], 30.0) && feq(memValue[5], 35.0) && feq(memValue[6], 40.0) && feq(memValue[7], 45.0)) printf("Memory contains the correct result\n"); 585 | if (feq(xmm3.xmm.f32[0], 10.0) && feq(xmm3.xmm.f32[1], 15.0) && feq(xmm3.xmm.f32[2], 20.0) && feq(xmm3.xmm.f32[3], 25.0)) printf("XMM3 contains the correct result\n"); 586 | printf("AVX test complete\n"); 587 | } 588 | 589 | printf("\n"); 590 | } 591 | else { 592 | printf("AVX not supported by guest; skipping test\n\n"); 593 | } 594 | 595 | // ----- FMA3 ----------------------------------------------------------------------------------------------------- 596 | 597 | if (testBits & FPTEST_FMA3) { 598 | // Run next block 599 | runToHLT(vp); 600 | printf("\n"); 601 | printXMMRegs(vp, XMMFormat::F64); 602 | printf("\n"); 603 | 604 | // Check result 605 | { 606 | RegValue rax, rsi, xmm0; 607 | vp.RegRead(Reg::RAX, rax); 608 | vp.RegRead(Reg::RSI, rsi); // contains address of result in memory 609 | vp.RegRead(Reg::XMM0, xmm0); // TODO: should read YMM0, but no hypervisors support that so far 610 | 611 | double memValue[4]; 612 | vp.LMemRead(rsi.u64, sizeof(memValue), &memValue); 613 | 614 | // Reinterpret RAX as if it were the lowest 64 bits of XMM0 615 | if (deq(rax.xmm.f64[0], 4.0)) printf("RAX contains the correct result\n"); 616 | if (deq(memValue[0], 4.0) && deq(memValue[1], 5.5) && deq(memValue[2], 6.0) && deq(memValue[3], 5.5)) printf("Memory contains the correct result\n"); 617 | if (deq(xmm0.xmm.f64[0], 4.0) && deq(xmm0.xmm.f64[1], 5.5)) printf("XMM0 contains the correct result\n"); 618 | printf("FMA3 test complete\n"); 619 | } 620 | 621 | printf("\n"); 622 | } 623 | else { 624 | printf("FMA3 not supported by guest; skipping test\n\n"); 625 | } 626 | 627 | // ----- AVX2 ----------------------------------------------------------------------------------------------------- 628 | 629 | if (testBits & FPTEST_AVX2) { 630 | // Run next block 631 | runToHLT(vp); 632 | printf("\n"); 633 | printXMMRegs(vp, XMMFormat::I64); 634 | printf("\n"); 635 | 636 | // Check result 637 | { 638 | RegValue rax, rsi, xmm15; 639 | vp.RegRead(Reg::RAX, rax); 640 | vp.RegRead(Reg::RSI, rsi); // contains address of result in memory 641 | vp.RegRead(Reg::XMM15, xmm15); // TODO: should read YMM1, but no hypervisors support that so far 642 | 643 | uint64_t memValue[4]; 644 | vp.LMemRead(rsi.u64, sizeof(memValue), &memValue); 645 | 646 | if (rax.u64 == 4) printf("RAX contains the correct result\n"); 647 | if (memValue[0] == 4 && memValue[1] == 3 && memValue[2] == 2 && memValue[3] == 1) printf("Memory contains the correct result\n"); 648 | if (xmm15.xmm.i64[0] == 4 && xmm15.xmm.i64[1] == 3) printf("XMM15 contains the correct result\n"); 649 | printf("AVX2 test complete\n"); 650 | } 651 | 652 | printf("\n"); 653 | } 654 | else { 655 | printf("AVX2 not supported by guest; skipping test\n\n"); 656 | } 657 | 658 | // ----- XSAVE ---------------------------------------------------------------------------------------------------- 659 | 660 | if (testBits & FPTEST_XSAVE) { 661 | // Run next block 662 | runToHLT(vp); 663 | printf("\n"); 664 | 665 | // Check result 666 | { 667 | RegValue rsi, r12, r13, r14; 668 | vp.RegRead(Reg::RSI, rsi); // contains address of XSAVE data in memory 669 | vp.RegRead(Reg::R12, r12); // contains address of XSAVE component base addresses in memory 670 | vp.RegRead(Reg::R13, r13); // contains address of XSAVE component sizes in memory 671 | vp.RegRead(Reg::R14, r14); // contains address of XSAVE component alignment bits in memory 672 | 673 | uint32_t bases[16], sizes[16], alignments; 674 | vp.LMemRead(r12.u64, sizeof(bases), bases); 675 | vp.LMemRead(r13.u64, sizeof(sizes), sizes); 676 | vp.LMemRead(r14.u64, sizeof(alignments), &alignments); 677 | 678 | printXSAVE(vp, rsi.u64, bases, sizes, alignments, MMFormat::I16, XMMFormat::IF32); 679 | printf("\n"); 680 | printf("XSAVE test complete\n"); 681 | } 682 | 683 | printf("\n"); 684 | } 685 | else { 686 | printf("XSAVE not supported by guest; skipping test\n\n"); 687 | } 688 | 689 | // ----- End ------------------------------------------------------------------------------------------------------ 690 | 691 | printf("Final VCPU state:\n"); 692 | printRegs(vp); 693 | printSTRegs(vp); 694 | printMMRegs(vp, MMFormat::I16); 695 | printMXCSRRegs(vp); 696 | printXMMRegs(vp, XMMFormat::IF32); 697 | printYMMRegs(vp, XMMFormat::IF64); 698 | printZMMRegs(vp, XMMFormat::IF64); 699 | printf("\n"); 700 | 701 | printf("Linear memory address translations:\n"); 702 | printAddressTranslation(vp, 0x00000000); 703 | printAddressTranslation(vp, 0x00010000); 704 | printAddressTranslation(vp, 0xffff0000); 705 | printAddressTranslation(vp, 0xffff00e8); 706 | printAddressTranslation(vp, 0x100000000); 707 | printf("\n"); 708 | 709 | { 710 | uint64_t stackVal; 711 | if (vp.LMemRead(0x200000 - 8, sizeof(uint64_t), &stackVal)) { 712 | printf("Value written to stack: 0x%016" PRIx64 "\n", stackVal); 713 | } 714 | 715 | RegValue gdtr, idtr; 716 | vp.RegRead(Reg::GDTR, gdtr); 717 | vp.RegRead(Reg::IDTR, idtr); 718 | 719 | GDTEntry gdtCode, gdtData; 720 | vp.GetGDTEntry(0x0008, gdtCode); 721 | vp.GetGDTEntry(0x0010, gdtData); 722 | printf("Code GDT: base=0x%08x, limit=0x%08x, access=0x%02x, flags=0x%x\n", gdtCode.gdt.GetBase(), gdtCode.gdt.GetLimit(), gdtCode.gdt.data.access.u8, gdtCode.gdt.data.flags); 723 | printf("Data GDT: base=0x%08x, limit=0x%08x, access=0x%02x, flags=0x%x\n", gdtData.gdt.GetBase(), gdtData.gdt.GetLimit(), gdtData.gdt.data.access.u8, gdtData.gdt.data.flags); 724 | 725 | printf("\n"); 726 | } 727 | 728 | // ----- Cleanup ---------------------------------------------------------------------------------------------------------- 729 | 730 | // Free VM 731 | printf("Releasing VM... "); 732 | if (platform.FreeVM(vm)) { 733 | printf("succeeded\n"); 734 | } 735 | else { 736 | printf("failed\n"); 737 | } 738 | 739 | // Free RAM 740 | if (alignedFree(ram)) { 741 | printf("RAM freed\n"); 742 | } 743 | else { 744 | printf("Failed to free RAM\n"); 745 | } 746 | 747 | // Free ROM 748 | if (alignedFree(rom)) { 749 | printf("ROM freed\n"); 750 | } 751 | else { 752 | printf("Failed to free ROM\n"); 753 | } 754 | 755 | return 0; 756 | } 757 | -------------------------------------------------------------------------------- /cmake/VSHelpers.cmake: -------------------------------------------------------------------------------- 1 | # Add Visual Studio filters to better organize the code 2 | function(vs_set_filters) 3 | cmake_parse_arguments(VS_SET_FILTERS "" "FILTER_ROOT;BASE_DIR" "SOURCES" ${ARGN}) 4 | if(MSVC) 5 | foreach(FILE IN ITEMS ${VS_SET_FILTERS_SOURCES}) 6 | # Get the directory of the source file 7 | get_filename_component(PARENT_DIR "${FILE}" DIRECTORY) 8 | 9 | # Remove common directory prefix to make the group 10 | if(BASE_DIR STREQUAL "") 11 | string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "" GROUP "${PARENT_DIR}") 12 | else() 13 | string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/${VS_SET_FILTERS_BASE_DIR}" "" GROUP "${PARENT_DIR}") 14 | endif() 15 | 16 | # Use Windows path separators 17 | string(REPLACE "/" "\\" GROUP "${GROUP}") 18 | 19 | # Add to filter 20 | source_group("${VS_SET_FILTERS_FILTER_ROOT}${GROUP}" FILES "${FILE}") 21 | endforeach() 22 | endif() 23 | endfunction() 24 | 25 | # Make the Debug and RelWithDebInfo targets use Program Database for Edit and Continue for easier debugging 26 | function(vs_use_edit_and_continue) 27 | if(MSVC) 28 | string(REPLACE "/Zi" "/ZI" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") 29 | string(REPLACE "/Zi" "/ZI" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") 30 | set(CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG} PARENT_SCOPE) 31 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO} PARENT_SCOPE) 32 | endif() 33 | endfunction() 34 | --------------------------------------------------------------------------------