├── .editorconfig ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md └── src └── heye ├── arch ├── arch.hpp ├── asm.hpp ├── cpuid.hpp ├── cr.hpp ├── dr.hpp ├── exceptions.hpp ├── memory.cpp ├── memory.hpp ├── msr.hpp ├── mtrr.cpp ├── mtrr.hpp ├── paging.hpp ├── segments.hpp ├── vmx.cpp └── vmx.hpp ├── asm ├── registers.asm └── vmm.asm ├── config.hpp ├── hv ├── callbacks.hpp ├── ept.cpp ├── ept.hpp ├── hypervisor.cpp ├── hypervisor.hpp ├── vcpu.cpp ├── vcpu.hpp ├── vmcall.cpp ├── vmcall.hpp ├── vmexit.cpp ├── vmexit.hpp ├── vmx.cpp └── vmx.hpp ├── shared ├── cpu.cpp ├── cpu.hpp ├── std │ ├── atomic.hpp │ ├── callable.hpp │ ├── mutex.hpp │ ├── new.cpp │ ├── traits.hpp │ ├── unique.hpp │ └── utility.hpp ├── trace.cpp └── trace.hpp └── vmi ├── process.hpp └── vmi.hpp /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | # C++ 3 | [*.{h,hpp,c,cpp,asm,md,txt}] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | cert 3 | .vscode 4 | .vs 5 | cli/target 6 | cli/Cargo.lock -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "FindWDK"] 2 | path = FindWDK 3 | url = https://github.com/SergiusTheBest/FindWDK.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(hypereye LANGUAGES CXX ASM_MASM) 4 | 5 | # Include FindWDK 6 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/FindWDK/cmake") 7 | find_package(WDK REQUIRED) 8 | 9 | file(GLOB_RECURSE HEYE_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/*.asm") 10 | file(GLOB_RECURSE HEYE_INCLUDES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp") 11 | 12 | wdk_add_library(hypereye KMDF 1.15 ${HEYE_SOURCES} ${HEYE_INCLUDES}) 13 | 14 | source_group(TREE ${PROJECT_SOURCE_DIR} FILES ${HEYE_SOURCES} ${HEYE_INCLUDES}) 15 | 16 | # Enable std23 17 | target_compile_features(hypereye PRIVATE 18 | "cxx_std_23" 19 | ) 20 | 21 | target_include_directories(hypereye PUBLIC 22 | "${CMAKE_CURRENT_SOURCE_DIR}/src/" 23 | ) 24 | 25 | target_compile_options(hypereye PRIVATE 26 | "/GR-" # Disable RTTI 27 | "/EHs-c-" # Disable exceptions 28 | "/W4" 29 | "/WX" 30 | "/wd4005" 31 | "/wd4201" 32 | "/Wv:18" 33 | "/Zc:strictStrings-" 34 | ) 35 | 36 | target_compile_definitions(hypereye PRIVATE 37 | "_VCRUNTIME_DISABLED_WARNINGS" 38 | "-D_HAS_EXCEPTIONS=0" 39 | ) 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hypereye is a type 2 research hypervisor for windows. 2 | 3 | Currently WIP. 4 | -------------------------------------------------------------------------------- /src/heye/arch/arch.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "exceptions.hpp" 4 | #include "segments.hpp" 5 | #include "memory.hpp" 6 | #include "cpuid.hpp" 7 | #include "mtrr.hpp" 8 | #include "msr.hpp" 9 | #include "asm.hpp" 10 | #include "vmx.hpp" 11 | #include "cr.hpp" 12 | #include "dr.hpp" 13 | 14 | #include "heye/shared/std/traits.hpp" 15 | 16 | #include 17 | 18 | namespace heye 19 | { 20 | template requires (!std::has_subleaf_v && std::has_leaf_v) 21 | inline T read() 22 | { 23 | T result{}; 24 | __cpuid(result.data, T::leaf); 25 | return result; 26 | } 27 | 28 | template requires (std::has_subleaf_v && std::has_leaf_v) 29 | inline T read() 30 | { 31 | T result{}; 32 | __cpuidex(result, T::leaf, T::subleaf); 33 | return result; 34 | } 35 | 36 | template requires (std::has_id_v) 37 | inline T read() { return T{ __readmsr(T::id) }; } 38 | 39 | template requires (std::has_id_v) 40 | inline T read(uint32_t value) { return T{ __readmsr(value) };} 41 | 42 | template requires (std::has_id_v) 43 | inline void write(T value) { __writemsr(T::id, value.flags); } 44 | 45 | template T read() { __debugbreak(); } 46 | template void write(T v) { __debugbreak(); } 47 | 48 | template<> inline cr0_t read() { return cr0_t{ __readcr0() }; } 49 | template<> inline cr2_t read() { return cr2_t{ __readcr2() }; } 50 | template<> inline cr3_t read() { return cr3_t{ __readcr3() }; } 51 | template<> inline cr4_t read() { return cr4_t{ __readcr4() }; } 52 | 53 | template<> inline void write(cr0_t cr0) { __writecr0(cr0.flags); } 54 | template<> inline void write(cr3_t cr3) { __writecr3(cr3.flags); } 55 | template<> inline void write(cr4_t cr4) { __writecr4(cr4.flags); } 56 | 57 | template<> inline dr7_t read() { return dr7_t{ __readdr(7) }; } 58 | 59 | template<> inline gdtr_t read() 60 | { 61 | gdtr_t gdtr{}; 62 | asm_read_gdtr(&gdtr); 63 | return gdtr; 64 | } 65 | 66 | template<> inline idtr_t read() 67 | { 68 | idtr_t idtr{}; 69 | asm_read_idtr(&idtr); 70 | return idtr; 71 | } 72 | 73 | template <> inline void write(gdtr_t gdtr) 74 | { 75 | asm_write_gdtr(&gdtr); 76 | } 77 | 78 | template <> inline void write(idtr_t idtr) 79 | { 80 | asm_write_idtr(&idtr); 81 | } 82 | 83 | template inline uint64_t read() 84 | { 85 | uint64_t value{}; 86 | __vmx_vmread(static_cast(field), &value); 87 | return value; 88 | } 89 | 90 | template inline uint64_t write(uint64_t value) 91 | { 92 | return __vmx_vmwrite(static_cast(field), value); 93 | } 94 | 95 | #define impl_read(name) \ 96 | template<> inline segment_t read() \ 97 | { \ 98 | segment_t segment{}; \ 99 | const auto selector = name ##_t{ asm_read_ ##name() }; \ 100 | segment.selector = selector; \ 101 | segment.limit = __segmentlimit(selector.flags); \ 102 | segment.rights = access_t \ 103 | { \ 104 | .flags = static_cast((asm_lar(selector.flags) >> 8) & 0xf0ff) \ 105 | }; \ 106 | segment.rights.unusable = selector.flags ? 0 : 1; \ 107 | \ 108 | if (!std::scmp(#name, "gs")) \ 109 | { \ 110 | segment.base = read().flags; \ 111 | } \ 112 | else if (!std::scmp(#name, "fs")) \ 113 | { \ 114 | segment.base = read().flags; \ 115 | } \ 116 | else \ 117 | { \ 118 | const auto desc = reinterpret_cast( \ 119 | read().base + static_cast(selector.index) * 8); \ 120 | segment.base = desc->base(); \ 121 | } \ 122 | return segment; \ 123 | } 124 | 125 | impl_read(es); 126 | impl_read(cs); 127 | impl_read(ss); 128 | impl_read(ds); 129 | impl_read(fs); 130 | impl_read(gs); 131 | impl_read(tr); 132 | impl_read(ldtr); 133 | 134 | #undef impl_read 135 | }; 136 | -------------------------------------------------------------------------------- /src/heye/arch/asm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /// Registers controls. 6 | /// 7 | extern "C" uint64_t asm_read_dr0(); 8 | extern "C" uint64_t asm_read_dr1(); 9 | extern "C" uint64_t asm_read_dr2(); 10 | extern "C" uint64_t asm_read_dr3(); 11 | extern "C" uint64_t asm_read_dr6(); 12 | extern "C" uint64_t asm_read_dr7(); 13 | 14 | extern "C" uint64_t asm_read_rflags(); 15 | 16 | extern "C" void asm_read_gdtr(void* gdtr); 17 | extern "C" void asm_read_idtr(void* idtr); 18 | 19 | extern "C" void asm_write_gdtr(const void* gdtr); 20 | extern "C" void asm_write_idtr(const void* idtr); 21 | 22 | /// Selectors. 23 | /// 24 | extern "C" uint16_t asm_read_cs(); 25 | extern "C" uint16_t asm_read_ds(); 26 | extern "C" uint16_t asm_read_es(); 27 | extern "C" uint16_t asm_read_ss(); 28 | extern "C" uint16_t asm_read_fs(); 29 | extern "C" uint16_t asm_read_gs(); 30 | extern "C" uint16_t asm_read_tr(); 31 | extern "C" uint16_t asm_read_ldtr(); 32 | 33 | /// Read segment access rights. 34 | /// 35 | extern "C" uint64_t asm_lar(uint64_t); 36 | 37 | /// VMX invalidate ept. 38 | /// 39 | extern "C" uint64_t asm_invept(uint64_t, void*); 40 | 41 | /// VMX invalidate vpid. 42 | /// 43 | extern "C" uint64_t asm_invvpid(uint64_t, void*); 44 | 45 | /// VMX call hypervisor. 46 | /// 47 | extern "C" bool asm_vmcall(void*, void*, void*, void*); 48 | 49 | /// VMX vm-exit function. 50 | /// 51 | extern "C" void vmexit_stub(); 52 | 53 | /// Launch vm. Return 0 on success meaning cpu executing now 54 | /// in non-root operation. return error code based on __vmx_vmlaunch intrinsic. 55 | /// 56 | extern "C" uint8_t asm_vmlaunch(); 57 | -------------------------------------------------------------------------------- /src/heye/arch/cpuid.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace heye::cpuid 6 | { 7 | /// CPUID EAX=0. 8 | /// 9 | struct manufacturer_id 10 | { 11 | static constexpr int leaf = 0; 12 | 13 | union 14 | { 15 | int data[4]; 16 | 17 | struct 18 | { 19 | union 20 | { 21 | uint32_t eax; 22 | uint32_t highest_function_parameter; 23 | }; 24 | 25 | char ebx[4]; 26 | char ecx[4]; 27 | char edx[4]; 28 | }; 29 | }; 30 | }; 31 | static_assert(sizeof(manufacturer_id) == sizeof(uint32_t) * 4, "CPUID EAX=0 size mismatch"); 32 | 33 | /// CPUID EAX=1. 34 | /// 35 | struct processor_features 36 | { 37 | static constexpr int leaf = 1; 38 | 39 | union 40 | { 41 | int data[4]; 42 | 43 | struct 44 | { 45 | union 46 | { 47 | uint32_t eax; 48 | 49 | struct 50 | { 51 | /// Stepping ID is a product revision number assigned due to fixed errata or other changes. 52 | /// 53 | uint32_t stepping_id : 4; 54 | /// 55 | /// 56 | uint32_t model_id : 4; 57 | /// 58 | /// 59 | uint32_t family_id : 4; 60 | /// 61 | /// 62 | uint32_t processor_type : 2; 63 | /// 64 | /// 65 | uint32_t _reserved1 : 2; 66 | /// 67 | /// 68 | uint32_t ext_model_id : 4; 69 | /// 70 | /// 71 | uint32_t ext_family_id : 8; 72 | /// 73 | /// 74 | uint32_t _reserved2 : 4; 75 | }; 76 | }; 77 | 78 | union 79 | { 80 | uint32_t ebx; 81 | 82 | struct 83 | { 84 | /// Brand Index. 85 | /// 86 | uint32_t brand_index : 8; 87 | /// CLFLUSH line size (Value * 8 = cache line size in bytes). 88 | /// 89 | uint32_t clfush_size : 8; 90 | /// Maximum number of addressable IDs for logical processors in this physical package. 91 | /// 92 | uint32_t addressable_ids : 8; 93 | /// Local APIC ID: The initial APIC-ID is used to identify the executing logical processor. 94 | /// 95 | uint32_t local_apic_id : 8; 96 | }; 97 | }; 98 | 99 | union 100 | { 101 | uint32_t ecx; 102 | 103 | struct 104 | { 105 | /// Prescott New Instructions-SSE3 (PNI). 106 | /// 107 | uint32_t sse3 : 1; 108 | /// PCLMULQDQ. 109 | /// 110 | uint32_t pclmulqdq : 1; 111 | /// 64-bit debug store (edx bit 21). 112 | /// 113 | uint32_t dtes64 : 1; 114 | /// MONITOR and MWAIT instructions (SSE3). 115 | /// 116 | uint32_t monitor : 1; 117 | /// CPL qualified debug store. 118 | /// 119 | uint32_t ds_cpl : 1; 120 | /// Virtual Machine extensions. 121 | /// 122 | uint32_t vmx : 1; 123 | /// Safer Mode Extensions (LaGrande). 124 | /// 125 | uint32_t smx : 1; 126 | /// Enhanced SpeedStep. 127 | /// 128 | uint32_t est : 1; 129 | /// Thermal Monitor 2. 130 | /// 131 | uint32_t tm2 : 1; 132 | /// Supplemental SSE3 instructions. 133 | /// 134 | uint32_t ssse3 : 1; 135 | /// L1 Context ID. 136 | /// 137 | uint32_t cnxt_id : 1; 138 | /// Silicon Debug interface. 139 | /// 140 | uint32_t sdbg : 1; 141 | /// Fused multiply-add (FMA3). 142 | /// 143 | uint32_t fma : 1; 144 | /// CMPXCHG16B instruction. 145 | /// 146 | uint32_t cx16 : 1; 147 | /// Can disable sending task priority messages 148 | /// 149 | uint32_t xtpr : 1; 150 | /// Perfmon & debug capability. 151 | /// 152 | uint32_t pdcm : 1; 153 | /// 154 | /// 155 | uint32_t _reserved1 : 1; 156 | /// Process context identifiers (CR4 bit 17). 157 | /// 158 | uint32_t pcid : 1; 159 | /// Direct cache access for DMA writes. 160 | /// 161 | uint32_t dca : 1; 162 | /// SSE4.1 instructions. 163 | /// 164 | uint32_t sse41 : 1; 165 | /// SSE4.2 instructions. 166 | /// 167 | uint32_t sse42 : 1; 168 | /// x2APIC. 169 | /// 170 | uint32_t x2apic : 1; 171 | /// MOVBE instruction (big-endian). 172 | /// 173 | uint32_t movbe : 1; 174 | /// POPCNT instruction. 175 | /// 176 | uint32_t popcnt : 1; 177 | /// APIC implements one-shot operation using a TSC deadline value. 178 | /// 179 | uint32_t tsc_deadline : 1; 180 | /// AES instruction set. 181 | /// 182 | uint32_t aes : 1; 183 | /// XSAVE, XRESTOR, XSETBV, XGETBV. 184 | /// 185 | uint32_t xsave : 1; 186 | /// XSAVE enabled by OS. 187 | /// 188 | uint32_t osxsave : 1; 189 | /// Advanced Vector Extensions. 190 | /// 191 | uint32_t avx : 1; 192 | /// F16C (half-precision) FP feature. 193 | /// 194 | uint32_t f16c : 1; 195 | /// RDRAND (on-chip random number generator) feature. 196 | /// 197 | uint32_t rdrnd : 1; 198 | /// Hypervisor present (always zero on physical CPUs). 199 | /// 200 | uint32_t hypervisor : 1; 201 | }; 202 | }; 203 | 204 | union 205 | { 206 | uint32_t edx; 207 | 208 | struct 209 | { 210 | /// Onboard x87 FPU. 211 | /// 212 | uint32_t fpu : 1; 213 | /// Virtual 8086 mode extensions (such as VIF, VIP, PIV). 214 | /// 215 | uint32_t vme : 1; 216 | /// Debugging extensions (CR4 bit 3). 217 | /// 218 | uint32_t de : 1; 219 | /// Page Size Extension. 220 | /// 221 | uint32_t pse : 1; 222 | /// Time Stamp Counter. 223 | /// 224 | uint32_t tsc : 1; 225 | /// Model-specific registers. 226 | /// 227 | uint32_t msr : 1; 228 | /// Physical Address Extension. 229 | /// 230 | uint32_t pae : 1; 231 | /// Machine Check Exception. 232 | /// 233 | uint32_t mce : 1; 234 | /// CMPXCHG8 (compare-and-swap) instruction. 235 | /// 236 | uint32_t cx8 : 1; 237 | /// Onboard Advanced Programmable Interrupt Controller. 238 | /// 239 | uint32_t apic : 1; 240 | /// 241 | /// 242 | uint32_t _reserved1 : 1; 243 | /// SYSENTER and SYSEXIT instructions. 244 | /// 245 | uint32_t sep : 1; 246 | /// Memory Type Range Registers. 247 | /// 248 | uint32_t mtrr : 1; 249 | /// Page Global Enable bit in CR4. 250 | /// 251 | uint32_t pge : 1; 252 | /// Machine check architecture. 253 | /// 254 | uint32_t mca : 1; 255 | /// Conditional move and FCMOV instructions. 256 | /// 257 | uint32_t cmov : 1; 258 | /// Page Attribute Table. 259 | /// 260 | uint32_t pat : 1; 261 | /// 36-bit page size extension. 262 | /// 263 | uint32_t pse_36 : 1; 264 | /// Processor Serial Number. 265 | /// 266 | uint32_t psn : 1; 267 | /// CLFLUSH instruction (SSE2). 268 | /// 269 | uint32_t clfsh : 1; 270 | /// 271 | /// 272 | uint32_t _reserved2 : 1; 273 | /// Debug store: save trace of executed jumps. 274 | /// 275 | uint32_t ds : 1; 276 | /// Onboard thermal control MSRs for ACPI. 277 | /// 278 | uint32_t acpi : 1; 279 | /// MMX instructions. 280 | /// 281 | uint32_t mmx : 1; 282 | /// FXSAVE, FXRESTOR instructions, CR4 bit 9. 283 | /// 284 | uint32_t fxsr : 1; 285 | /// SSE instructions (a.k.a. Katmai New Instructions). 286 | /// 287 | uint32_t sse : 1; 288 | /// SSE2 instructions. 289 | /// 290 | uint32_t sse2 : 1; 291 | /// CPU cache implements self-snoop. 292 | /// 293 | uint32_t ss : 1; 294 | /// Hyper-threading. 295 | /// 296 | uint32_t htt : 1; 297 | /// Thermal monitor automatically limits temperature. 298 | /// 299 | uint32_t tm : 1; 300 | /// IA64 processor emulating x86. 301 | /// 302 | uint32_t ia64 : 1; 303 | /// Pending Break Enable (PBE# pin) wakeup capability. 304 | /// 305 | uint32_t pbe : 1; 306 | }; 307 | }; 308 | }; 309 | }; 310 | }; 311 | static_assert(sizeof(processor_features) == sizeof(uint32_t) * 4, "CPUID EAX=1 size mismatch"); 312 | }; 313 | -------------------------------------------------------------------------------- /src/heye/arch/cr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace heye 6 | { 7 | struct cr0_t 8 | { 9 | union 10 | { 11 | uint64_t flags; 12 | 13 | struct 14 | { 15 | /// If 1, system is in protected mode, else system is in real mode. 16 | /// 17 | uint64_t pe : 1; 18 | /// Controls interaction of WAIT/FWAIT instructions with TS flag in CR0. 19 | /// 20 | uint64_t mp : 1; 21 | /// If set, no x87 floating-point unit present, if clear, x87 FPU present. 22 | /// 23 | uint64_t em : 1; 24 | /// Allows saving x87 task context upon a task switch only after x87 instruction used. 25 | /// 26 | uint64_t ts : 1; 27 | /// On the 386, it allowed to specify whether the external math coprocessor was an 80287 or 80387. 28 | /// 29 | uint64_t et : 1; 30 | /// Enable internal x87 floating point error reporting when set, else enables PC style x87 error detection. 31 | /// 32 | uint64_t ne : 1; 33 | /// Reserved. 34 | /// 35 | uint64_t reserved0 : 10; 36 | /// When set, the CPU can't write to read-only pages when privilege level is 0. 37 | /// 38 | uint64_t wp : 1; 39 | /// Reserved. 40 | /// 41 | uint64_t reserved1 : 1; 42 | /// Alignment check enabled if AM set, AC flag (in EFLAGS register) set, and privilege level is 3. 43 | /// 44 | uint64_t am : 1; 45 | /// Reserved. 46 | /// 47 | uint64_t reserved2 : 10; 48 | /// Globally enables/disable write-through caching. 49 | /// 50 | uint64_t nw : 1; 51 | /// Globally enables/disable the memory cache. 52 | /// 53 | uint64_t cd : 1; 54 | /// If 1, enable paging and use the § CR3 register, else disable paging. 55 | /// 56 | uint64_t pg : 1; 57 | }; 58 | }; 59 | }; 60 | static_assert(sizeof(cr0_t) == sizeof(uint64_t), "cr0_t size mismatch"); 61 | 62 | struct cr2_t 63 | { 64 | uint64_t address; 65 | }; 66 | static_assert(sizeof(cr2_t) == sizeof(uint64_t), "cr2_t size mismatch"); 67 | 68 | struct cr3_t 69 | { 70 | uint64_t flags; 71 | }; 72 | static_assert(sizeof(cr3_t) == sizeof(uint64_t), "cr3_t size mismatch"); 73 | 74 | struct cr4_t 75 | { 76 | union 77 | { 78 | uint64_t flags; 79 | 80 | struct 81 | { 82 | /// If set, enables support for the virtual interrupt flag (VIF) in virtual-8086 mode. 83 | /// 84 | uint64_t vme : 1; 85 | /// If set, enables support for the virtual interrupt flag (VIF) in protected mode. 86 | /// 87 | uint64_t pvi : 1; 88 | /// If set, RDTSC instruction can only be executed when in ring 0, otherwise RDTSC can be used at any privilege level. 89 | /// 90 | uint64_t tsd : 1; 91 | /// If set, enables debug register based breaks on I/O space access. 92 | /// 93 | uint64_t de : 1; 94 | /// If unset, page size is 4 KiB, else page size is increased to 4 MiB. If PAE is enabled or the processor is in x86-64 long mode this bit is ignored. 95 | /// 96 | uint64_t pse : 1; 97 | /// If set, changes page table layout to translate 32-bit virtual addresses into extended 36-bit physical addresses. 98 | /// 99 | uint64_t pae : 1; 100 | /// If set, enables machine check interrupts to occur. 101 | /// 102 | uint64_t mce : 1; 103 | /// If set, address translations (PDE or PTE records) may be shared between address spaces. 104 | /// 105 | uint64_t pge : 1; 106 | /// If set, RDPMC can be executed at any privilege level, else RDPMC can only be used in ring 0. 107 | /// 108 | uint64_t pce : 1; 109 | /// If set, enables Streaming SIMD Extensions (SSE) instructions and fast FPU save & restore. 110 | /// 111 | uint64_t osfxsr : 1; 112 | /// If set, enables unmasked SSE exceptions. 113 | /// 114 | uint64_t osxmmexcpt : 1; 115 | /// If set, the SGDT, SIDT, SLDT, SMSW and STR instructions cannot be executed if CPL > 0. 116 | /// 117 | uint64_t umip : 1; 118 | /// If set, enables 5-Level Paging. 119 | /// 120 | uint64_t la57 : 1; 121 | /// If set, Virtual Machine Extensions supported. 122 | /// 123 | uint64_t vmxe : 1; 124 | /// If set, Safer Mode Extensions supported. 125 | /// 126 | uint64_t smxe : 1; 127 | /// 128 | /// 129 | uint64_t _reserved1 : 1; 130 | /// Enables the instructions RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE. 131 | /// 132 | uint64_t fsgsbase : 1; 133 | /// If set, enables process-context identifiers (PCIDs). 134 | /// 135 | uint64_t pcide : 1; 136 | /// XSAVE and Processor Extended States Enable. 137 | /// 138 | uint64_t osxsave : 1; 139 | /// 140 | /// 141 | uint64_t _reserved2 : 1; 142 | /// If set, execution of code in a higher ring generates a fault. 143 | /// 144 | uint64_t smep : 1; 145 | /// If set, access of data in a higher ring generates a fault. 146 | /// 147 | uint64_t smap : 1; 148 | /// Protection Key Enable. 149 | /// 150 | uint64_t pke : 1; 151 | /// If set, enables control-flow enforcement technology. 152 | /// 153 | uint64_t cet : 1; 154 | /// If set, each supervisor-mode linear address is associated with a protection key when 4-level or 5-level paging is in use. 155 | /// 156 | uint64_t pks : 1; 157 | }; 158 | }; 159 | 160 | }; 161 | static_assert(sizeof(cr4_t) == sizeof(uint64_t), "cr4_t size mismatch"); 162 | }; 163 | -------------------------------------------------------------------------------- /src/heye/arch/dr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace heye 6 | { 7 | struct dr7_t 8 | { 9 | union 10 | { 11 | uint64_t flags; 12 | 13 | struct 14 | { 15 | /// Local enable for breakpoint in DR0. 16 | /// 17 | uint64_t dr0_enable_local : 1; 18 | /// Global enable for breakpoint in DR0. 19 | /// 20 | uint64_t dr0_enable_global : 1; 21 | /// Local enable for breakpoint in DR1. 22 | /// 23 | uint64_t dr1_enable_local : 1; 24 | /// Global enable for breakpoint in DR1. 25 | /// 26 | uint64_t dr1_enable_global : 1; 27 | /// Local enable for breakpoint in DR2. 28 | /// 29 | uint64_t dr2_enable_local : 1; 30 | /// Global enable for breakpoint in DR2. 31 | /// 32 | uint64_t dr2_enable_global : 1; 33 | /// Local enable for breakpoint in DR3. 34 | /// 35 | uint64_t dr3_enable_local : 1; 36 | /// Global enable for breakpoint in DR3. 37 | /// 38 | uint64_t dr3_enable_global : 1; 39 | /// Exact data breakpoint. 40 | /// 41 | uint64_t le : 1; 42 | /// Exact data breakpoint. 43 | /// 44 | uint64_t ge : 1; 45 | /// @brief 46 | /// 47 | uint64_t _reserved1 : 5; 48 | /// DR0 breakpoint type. 49 | /// 50 | uint64_t dr0_bp_type : 2; 51 | /// DR0 breakpoint size in bytes. 52 | /// 53 | uint64_t dr0_bp_size : 2; 54 | /// DR1 breakpoint type. 55 | /// 56 | uint64_t dr1_bp_type : 2; 57 | /// DR1 breakpoint size in bytes. 58 | /// 59 | uint64_t dr1_bp_size : 2; 60 | /// DR2 breakpoint type. 61 | /// 62 | uint64_t dr2_bp_type : 2; 63 | /// DR2 breakpoint size in bytes. 64 | /// 65 | uint64_t dr2_bp_size : 2; 66 | /// DR3 breakpoint type. 67 | /// 68 | uint64_t dr3_bp_type : 2; 69 | /// DR3 breakpoint size in bytes. 70 | /// 71 | uint64_t dr3_bp_size : 2; 72 | }; 73 | }; 74 | }; 75 | }; 76 | -------------------------------------------------------------------------------- /src/heye/arch/exceptions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace heye 6 | { 7 | enum exception_t 8 | { 9 | devide_error = 0, 10 | debug_breakpoint = 1, 11 | nmi = 2, 12 | breakpoint = 3, 13 | overflow = 4, 14 | bound_range_exceeded = 5, 15 | undefined_opcode = 6, 16 | no_math_coprocessor = 7, 17 | double_fault = 8, 18 | reserved0 = 9, 19 | invalid_task_segment = 10, 20 | segment_not_present = 11, 21 | stack_segment_fault = 12, 22 | general_protection_fault = 13, 23 | page_fault = 14, 24 | reserved1 = 15, 25 | math_fault = 16, 26 | alignment_check = 17, 27 | machine_check = 18, 28 | simd_floating_numeric_error = 19, 29 | virtual_exception = 20, 30 | reserved2 = 21, 31 | reserved3 = 22, 32 | reserved4 = 23, 33 | reserved5 = 24, 34 | reserved6 = 25, 35 | reserved7 = 26, 36 | reserved8 = 27, 37 | reserved9 = 28, 38 | reserved10 = 29, 39 | reserved11 = 30, 40 | reserved12 = 31, 41 | }; 42 | }; 43 | -------------------------------------------------------------------------------- /src/heye/arch/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "heye/arch/memory.hpp" 2 | 3 | #include 4 | 5 | namespace heye 6 | { 7 | uint64_t pa_from_va(const void* va) 8 | { 9 | return static_cast(MmGetPhysicalAddress((PVOID)va).QuadPart); 10 | } 11 | 12 | void* va_from_pa(uint64_t pa) 13 | { 14 | return MmGetVirtualForPhysical({ .QuadPart = static_cast(pa) }); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/heye/arch/memory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "heye/config.hpp" 3 | 4 | #include 5 | 6 | namespace heye 7 | { 8 | enum memory_type_t 9 | { 10 | uncachable = 0, 11 | write_combining = 1, 12 | write_through = 4, 13 | write_protected = 5, 14 | write_back = 6 15 | }; 16 | 17 | inline uint64_t operator""_kb(uint64_t size) { return size * 1024; } 18 | inline uint64_t operator""_mb(uint64_t size) { return size * 1024_kb; } 19 | inline uint64_t operator""_gb(uint64_t size) { return size * 1024_mb; } 20 | 21 | 22 | inline uint64_t pfn(uint64_t pa) { return pa >> page_shift; } 23 | 24 | uint64_t pa_from_va(const void* va); 25 | void* va_from_pa(uint64_t pa); 26 | }; 27 | -------------------------------------------------------------------------------- /src/heye/arch/msr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace heye::msr 6 | { 7 | struct feature_control 8 | { 9 | static constexpr unsigned id = 0x3a; 10 | 11 | union 12 | { 13 | uint64_t flags; 14 | 15 | struct 16 | { 17 | /// When set, locks this MSR from being written; writes to this bit will result in GP(0). 18 | /// 19 | uint64_t lock : 1; 20 | /// Enable VMX inside SMX operation. 21 | /// 22 | uint64_t smx_vmxon : 1; 23 | /// Enable VMX outside SMX operation. 24 | /// 25 | uint64_t vmxon : 1; 26 | /// @brief 27 | /// 28 | uint64_t _reserved1 : 6; 29 | /// When set, each bit in the field represents an enable control for a corresponding SENTER function. 30 | /// 31 | uint64_t senter_lf_enables : 6; 32 | /// This bit must be set to enable SENTER leaf functions. 33 | /// 34 | uint64_t senter_enable : 1; 35 | /// @brief 36 | /// 37 | uint64_t _reserved2 : 1; 38 | /// This bit must be set to enable runtime reconfiguration of SGX Launch Control via the IA32_SGXLEPUBKEYHASHn MSR. 39 | /// 40 | uint64_t sgx_lc_enable : 1; 41 | /// This bit must be set to enable SGX leaf functions. 42 | /// 43 | uint64_t sgx_enable : 1; 44 | /// @brief 45 | /// 46 | uint64_t _reserved3 : 1; 47 | /// When set, system software can program the MSRs associated with LMCE to configure delivery of some machine check exceptions to a single logical processor. 48 | /// 49 | uint64_t lmce_on : 1; 50 | }; 51 | }; 52 | }; 53 | 54 | struct mtrrcap 55 | { 56 | static constexpr unsigned id = 0xfe; 57 | 58 | union 59 | { 60 | uint64_t flags; 61 | 62 | struct 63 | { 64 | /// Indicates the number of variable ranges implemented on the processor. 65 | /// 66 | uint64_t vnct : 8; 67 | /// Fixed range MTRRs (IA32_MTRR_FIX64K_00000 through IA32_MTRR_FIX4K_0F8000) are supported when set. 68 | /// 69 | uint64_t fix : 1; 70 | /// @brief 71 | /// 72 | uint64_t _reserved1 : 1; 73 | /// The write-combining (WC) memory type is supported when set. 74 | /// 75 | uint64_t write_combining : 1; 76 | /// The system-management range register interface is supported when bit 11 is set. 77 | /// 78 | uint64_t smrr : 1; 79 | 80 | }; 81 | }; 82 | }; 83 | 84 | struct mtrr_physbase 85 | { 86 | static constexpr unsigned id = 0x200; 87 | 88 | union 89 | { 90 | uint64_t flags; 91 | 92 | struct 93 | { 94 | /// Specifies the memory type for the range. 95 | /// 96 | uint64_t type : 8; 97 | /// @brief 98 | /// 99 | uint64_t _reserved1 : 4; 100 | /// @brief 101 | /// 102 | uint64_t pfn : 36; 103 | }; 104 | }; 105 | }; 106 | 107 | struct mtrr_physmask 108 | { 109 | static constexpr unsigned id = 0x201; 110 | 111 | union 112 | { 113 | uint64_t flags; 114 | 115 | struct 116 | { 117 | /// @brief 118 | /// 119 | uint64_t _reserved1 : 11; 120 | /// Enables the register pair when set; disables register pair when clear. 121 | /// 122 | uint64_t valid : 1; 123 | /// @brief 124 | /// 125 | uint64_t pfn : 36; 126 | }; 127 | }; 128 | }; 129 | 130 | struct mtrr_fix 131 | { 132 | union 133 | { 134 | uint64_t flags; 135 | uint8_t types[8]; 136 | }; 137 | }; 138 | 139 | struct mtrr_fix_64k : mtrr_fix 140 | { 141 | static constexpr unsigned id = 0x250; 142 | static constexpr unsigned base = 0x00000; 143 | static constexpr unsigned size = 0x10000; 144 | }; 145 | 146 | struct mtrr_fix_16k_0 : mtrr_fix 147 | { 148 | static constexpr unsigned id = 0x258; 149 | static constexpr unsigned base = 0x80000; 150 | static constexpr unsigned size = 0x04000; 151 | }; 152 | 153 | struct mtrr_fix_16k_1 : mtrr_fix 154 | { 155 | static constexpr unsigned id = 0x259; 156 | static constexpr unsigned base = 0xa0000; 157 | static constexpr unsigned size = 0x04000; 158 | }; 159 | 160 | struct mtrr_fix_4k_0 : mtrr_fix 161 | { 162 | static constexpr unsigned id = 0x268; 163 | static constexpr unsigned base = 0xc0000; 164 | static constexpr unsigned size = 0x01000; 165 | }; 166 | 167 | struct mtrr_fix_4k_1 : mtrr_fix 168 | { 169 | static constexpr unsigned id = 0x269; 170 | static constexpr unsigned base = 0xc8000; 171 | static constexpr unsigned size = 0x01000; 172 | }; 173 | 174 | struct mtrr_fix_4k_2 : mtrr_fix 175 | { 176 | static constexpr unsigned id = 0x26a; 177 | static constexpr unsigned base = 0xd0000; 178 | static constexpr unsigned size = 0x01000; 179 | }; 180 | 181 | struct mtrr_fix_4k_3 : mtrr_fix 182 | { 183 | static constexpr unsigned id = 0x26b; 184 | static constexpr unsigned base = 0xd8000; 185 | static constexpr unsigned size = 0x01000; 186 | }; 187 | 188 | struct mtrr_fix_4k_4 : mtrr_fix 189 | { 190 | static constexpr unsigned id = 0x26c; 191 | static constexpr unsigned base = 0xe0000; 192 | static constexpr unsigned size = 0x01000; 193 | }; 194 | 195 | struct mtrr_fix_4k_5 : mtrr_fix 196 | { 197 | static constexpr unsigned id = 0x26d; 198 | static constexpr unsigned base = 0xe8000; 199 | static constexpr unsigned size = 0x01000; 200 | }; 201 | 202 | struct mtrr_fix_4k_6 : mtrr_fix 203 | { 204 | static constexpr unsigned id = 0x26e; 205 | static constexpr unsigned base = 0xf0000; 206 | static constexpr unsigned size = 0x01000; 207 | }; 208 | 209 | struct mtrr_fix_4k_7 : mtrr_fix 210 | { 211 | static constexpr unsigned id = 0x26f; 212 | static constexpr unsigned base = 0xf8000; 213 | static constexpr unsigned size = 0x01000; 214 | }; 215 | 216 | struct mtrr_def_type 217 | { 218 | static constexpr unsigned id = 0x2ff; 219 | 220 | union 221 | { 222 | uint64_t flags; 223 | 224 | struct 225 | { 226 | /// Default Memory Type. 227 | /// 228 | uint64_t memory_type : 3; 229 | /// @brief 230 | /// 231 | uint64_t _reserved1 : 7; 232 | /// Fixed Range MTRR Enable. 233 | /// 234 | uint64_t fixed_range_mtrr_enable : 1; 235 | /// MTRR Enable. 236 | /// 237 | uint64_t mtrr_enable : 1; 238 | }; 239 | }; 240 | }; 241 | 242 | struct vmx_basic 243 | { 244 | static constexpr unsigned id = 0x480; 245 | 246 | union 247 | { 248 | uint64_t flags; 249 | 250 | struct 251 | { 252 | /// VMCS revision identifier used by the processor. 253 | /// 254 | uint64_t vmcs_id : 31; 255 | /// Bit 31 is always 0. 256 | /// 257 | uint64_t zero : 1; 258 | /// Size of the VMCS. 259 | /// 260 | uint64_t vmcs_size : 12; 261 | /// @brief 262 | /// 263 | uint64_t _reserved1 : 4; 264 | /// Width of physical address used for the VMCS. 265 | /// 266 | uint64_t phys_width : 1; 267 | /// Whether the processor supports the dual-monitor treatment of system-management interrupts and system-management code (always 1). 268 | /// 269 | uint64_t dual_mon : 1; 270 | /// Memory type that must be used for the VMCS. 271 | /// 272 | uint64_t mem_type : 4; 273 | /// Whether the processor provides additional information for exits due to INS/OUTS. 274 | /// 275 | uint64_t ins_outs : 1; 276 | /// Whether default 1 bits in control MSRs (pin/proc/exit/entry) may be cleared to 0 and that 'true' control MSRs are supported. 277 | /// 278 | uint64_t true_controls : 1; 279 | }; 280 | }; 281 | }; 282 | 283 | struct vmx_pinbased_controls 284 | { 285 | static constexpr unsigned id = 0x481; 286 | 287 | union 288 | { 289 | uint64_t flags; 290 | 291 | struct 292 | { 293 | /// External interrupts cause VM-exits if set; otherwise dispatched through the guest's IDT. 294 | /// 295 | uint64_t external_interrupt_exiting : 1; 296 | /// @brief 297 | /// 298 | uint64_t _reserved1 : 2; 299 | /// Non-maskable interrupts cause VM-exits if set; otherwise dispatched through the guest's IDT. 300 | /// 301 | uint64_t nmi_exiting : 1; 302 | /// @brief 303 | /// 304 | uint64_t _reserved2 : 1; 305 | /// If this control is 1, NMIs are never blocked. 306 | /// 307 | uint64_t virtual_nmi : 1; 308 | /// If this control is 1, the VMX-preemption timer counts down in VMX non-root operation. 309 | /// A VM exit occurs when the timer counts down to zero. 310 | /// 311 | uint64_t preemption_timer : 1; 312 | /// If this control is 1, the processor treats interrupts with the posted-interrupt notification vector 313 | /// specially, updating the virtual-APIC page with posted-interrupt requests. 314 | /// 315 | uint64_t posted_interrupts : 1; 316 | }; 317 | }; 318 | }; 319 | 320 | struct vmx_exit_controls 321 | { 322 | static constexpr unsigned id = 0x483; 323 | 324 | union 325 | { 326 | uint64_t flags; 327 | 328 | struct 329 | { 330 | /// @brief 331 | /// 332 | uint64_t _reserved1 : 2; 333 | /// This control determines whether DR7 and the IA32_DEBUGCTL MSR are saved on VM exit. 334 | /// 335 | uint64_t save_debug_controls : 1; 336 | /// @brief 337 | /// 338 | uint64_t _reserved2 : 6; 339 | /// On processors that support Intel 64 architecture, this control determines whether a logical 340 | /// processor is in 64-bit mode after the next VM exit. 341 | /// 342 | uint64_t host_address_space_size : 1; 343 | /// @brief 344 | /// 345 | uint64_t _reserved3 : 2; 346 | /// Whether the IA32_PERF_GLOBAL_CTRL MSR is loaded on VM-exit. 347 | /// 348 | uint64_t load_perf_global_ctrl : 1; 349 | /// @brief 350 | /// 351 | uint64_t _reserved4 : 2; 352 | /// Acknowledge external interrupts with the irq controller if one caused a VM-exit. 353 | /// 354 | uint64_t acknowledge_interrupt_on_exit : 1; 355 | /// @brief 356 | /// 357 | uint64_t _reserved5 : 2; 358 | /// This control determines whether the IA32_PAT MSR is saved on VM exit. 359 | /// 360 | uint64_t save_guest_pat_msr : 1; 361 | /// This control determines whether the IA32_PAT MSR is loaded on VM exit. 362 | /// 363 | uint64_t load_host_pat_msr : 1; 364 | /// This control determines whether the IA32_EFER MSR is saved on VM exit. 365 | /// 366 | uint64_t save_guest_efer_msr : 1; 367 | /// This control determines whether the IA32_EFER MSR is loaded on VM exit. 368 | /// 369 | uint64_t load_host_efer_msr : 1; 370 | /// This control determines whether the value of the VMX-preemption timer is saved on VM exit. 371 | /// 372 | uint64_t save_vmx_preempt_timer : 1; 373 | /// This control determines whether the IA32_BNDCFGS MSR is cleared on VM exit. 374 | /// 375 | uint64_t clear_bndcfgs : 1; 376 | /// If this control is 1, Intel Processor Trace does not produce a paging information packet (PIP) on 377 | /// a VM exit or a VMCS packet on an SMM VM exit. 378 | /// 379 | uint64_t conceal_vmx_from_pt : 1; 380 | /// This control determines whether the IA32_RTIT_CTL MSR is cleared on VM exit. 381 | /// 382 | uint64_t clear_rtit_ctl : 1; 383 | /// This control determines whether the IA32_LBR_CTL MSR is cleared on VM exit. 384 | /// 385 | uint64_t clear_lbr_ctl : 1; 386 | /// @brief 387 | /// 388 | uint64_t _reserved6 : 1; 389 | /// This control determines whether CET-related MSRs and SPP are loaded on VM exit. 390 | /// 391 | uint64_t load_host_cet_state : 1; 392 | /// This control determines whether the IA32_LBR_CTL MSR is cleared on VM exit. 393 | /// 394 | uint64_t load_host_pkrs : 1; 395 | /// @brief 396 | /// 397 | uint64_t _reserved7 : 1; 398 | /// This control determines whether the secondary VM-exit controls are used. 399 | /// 400 | uint64_t use_secondary_controls : 1; 401 | }; 402 | }; 403 | }; 404 | 405 | struct vmx_entry_controls 406 | { 407 | static constexpr unsigned id = 0x484; 408 | 409 | union 410 | { 411 | uint64_t flags; 412 | 413 | struct 414 | { 415 | /// @brief 416 | /// 417 | uint64_t _reserved1 : 2; 418 | /// This control determines whether DR7 and the IA32_DEBUGCTL MSR are loaded on VM entry. 419 | /// 420 | uint64_t load_debug_controls : 1; 421 | /// @brief 422 | /// 423 | uint64_t _reserved2 : 6; 424 | /// On processors that support Intel 64 architecture, this control determines whether the logical 425 | /// processor is in IA-32e mode after VM entry. 426 | /// 427 | uint64_t ia32_mode_guest : 1; 428 | /// This control determines whether the logical processor is in system-management mode (SMM) after VM entry. 429 | /// 430 | uint64_t entry_smm : 1; 431 | /// If set to 1, the default treatment of SMIs and SMM is in effect after the VM entry. 432 | /// 433 | uint64_t deactivate_dual_monitor : 1; 434 | /// @brief 435 | /// 436 | uint64_t _reserved3 : 1; 437 | /// This control determines whether the IA32_PERF_GLOBAL_CTRL MSR is loaded on VM entry. 438 | /// 439 | uint64_t load_guest_perf_msr : 1; 440 | /// This control determines whether the IA32_PAT MSR is loaded on VM entry. 441 | /// 442 | uint64_t load_guest_pat_msr : 1; 443 | /// This control determines whether the IA32_EFER MSR is loaded on VM entry. 444 | /// 445 | uint64_t load_guest_efer_msr : 1; 446 | /// This control determines whether the IA32_BNDCFGS MSR is loaded on VM entry. 447 | /// 448 | uint64_t load_guest_bndcfgs : 1; 449 | /// If this control is 1, Intel Processor Trace does not produce a paging information packet (PIP) on a VM entry or a VMCS packet on a VM entry that returns from SMM. 450 | /// 451 | uint64_t conceal_vmx_from_pt : 1; 452 | /// This control determines whether the IA32_RTIT_CTL MSR is loaded on VM entry. 453 | /// 454 | uint64_t load_guest_rtit_ctl : 1; 455 | /// @brief 456 | /// 457 | uint64_t _reserved4 : 1; 458 | /// This control determines whether CET-related MSRs and SPP are loaded on VM entry. 459 | /// 460 | uint64_t load_cet_state : 1; 461 | /// This control determines whether the IA32_LBR_CTL MSR is loaded on VM entry. 462 | /// 463 | uint64_t load_guest_lbr_ctl : 1; 464 | /// This control determines whether the IA32_PKRS MSR is loaded on VM entry. 465 | /// 466 | uint64_t load_guest_prks : 1; 467 | }; 468 | }; 469 | }; 470 | 471 | struct vmx_procbased_controls 472 | { 473 | static constexpr unsigned id = 0x482; 474 | 475 | union 476 | { 477 | uint64_t flags; 478 | 479 | struct 480 | { 481 | /// @brief 482 | /// 483 | uint64_t _reserved1 : 2; 484 | /// If this control is 1, a VM exit occurs at the beginning of any instruction if RFLAGS.IF = 1 and 485 | /// there are no other blocking of interrupts. 486 | /// 487 | uint64_t interrupt_window_exiting : 1; 488 | /// This control determines whether executions of RDTSC, executions of RDTSCP, and executions 489 | /// of RDMSR that read from the IA32_TIME_STAMP_COUNTER MSR return a value modified by the TSC offset field. 490 | /// 491 | uint64_t use_tsc_offsetting : 1; 492 | /// @brief 493 | /// 494 | uint64_t _reserved2 : 3; 495 | /// This control determines whether executions of HLT cause VM exits. 496 | /// 497 | uint64_t hlt_exiting : 1; 498 | /// @brief 499 | /// 500 | uint64_t _reserved3 : 1; 501 | /// This control determines whether executions of INVLPG cause VM exits. 502 | /// 503 | uint64_t invlpg_exiting : 1; 504 | /// This control determines whether executions of MWAIT cause VM exits. 505 | /// 506 | uint64_t mwait_exiting : 1; 507 | /// This control determines whether executions of RDPMC cause VM exits. 508 | /// 509 | uint64_t rdpmc_exiting : 1; 510 | /// This control determines whether executions of RDTSC and RDTSCP cause VM exits. 511 | /// 512 | uint64_t rtdsc_exiting : 1; 513 | /// @brief 514 | /// 515 | uint64_t _reserved4 : 2; 516 | /// VM-exit when executing the MOV to CR3 instruction. 517 | /// 518 | uint64_t cr3_load_exiting : 1; 519 | /// VM-exit when executing the MOV from CR3 instruction. 520 | /// 521 | uint64_t cr3_store_exiting : 1; 522 | /// This control determines whether the tertiary processor-based VM-execution controls are used. 523 | /// 524 | uint64_t use_tertiary_controls : 1; 525 | /// @brief 526 | /// 527 | uint64_t _reserved5 : 1; 528 | /// This control determines whether executions of MOV to CR8 cause VM exits. 529 | /// 530 | uint64_t cr8_load_exiting : 1; 531 | /// This control determines whether executions of MOV from CR8 cause VM exits. 532 | /// 533 | uint64_t cr8_store_exiting : 1; 534 | /// Setting this control to 1 enables TPR virtualization and other APIC-virtualization features. 535 | /// 536 | uint64_t use_tpr_shadow : 1; 537 | /// If this control is 1, a VM exit occurs at the beginning of any instruction if there is no virtual-NMI blocking. 538 | /// 539 | uint64_t nmi_window_exiting : 1; 540 | /// This control determines whether executions of MOV DR cause VM exits. 541 | /// 542 | uint64_t mov_dr_exiting : 1; 543 | /// VM-exit when executing IO instructions. 544 | /// 545 | uint64_t unconditional_io_exiting : 1; 546 | /// This control determines whether I/O bitmaps are used to restrict executions of I/O instructions. 547 | /// 548 | uint64_t use_io_bitmaps : 1; 549 | /// @brief 550 | /// 551 | uint64_t _reserved6 : 1; 552 | /// If this control is 1, the monitor trap flag debugging feature is enabled. 553 | /// 554 | uint64_t monitor_trap_flag : 1; 555 | /// This control determines whether MSR bitmaps are used to control execution of the RDMSR and WRMSR instructions. 556 | /// 557 | uint64_t use_msr_bitmaps : 1; 558 | /// This control determines whether executions of MONITOR cause VM exits. 559 | /// 560 | uint64_t monitor_exiting : 1; 561 | /// This control determines whether executions of PAUSE cause VM exits. 562 | /// 563 | uint64_t pause_exiting : 1; 564 | /// Determines whether the secondary processor based VM-execution controls are used. 565 | /// 566 | uint64_t use_secondary_controls : 1; 567 | }; 568 | }; 569 | }; 570 | 571 | struct vmx_procbased_controls2 572 | { 573 | static constexpr unsigned id = 0x48b; 574 | 575 | union 576 | { 577 | uint64_t flags; 578 | 579 | struct 580 | { 581 | /// If this control is 1, the logical processor treats specially accesses to the page with the APICaccess address. 582 | /// 583 | uint64_t virtualize_apic : 1; 584 | /// If this control is 1, extended page tables (EPT) are enabled. 585 | /// 586 | uint64_t enable_ept : 1; 587 | /// Descriptor table instructions cause VM-exits. 588 | /// 589 | uint64_t descriptor_table_exiting : 1; 590 | /// If this control is 0, any execution of RDTSCP causes an invalid-opcode exception (#UD). 591 | /// 592 | uint64_t enable_rdtcp : 1; 593 | /// If this control is 1, the logical processor treats specially RDMSR and WRMSR to APIC MSRs. 594 | /// 595 | uint64_t virtualize_x2apic : 1; 596 | /// If this control is 1, cached translations of linear addresses are associated with a VPID. 597 | /// 598 | uint64_t enable_vpid : 1; 599 | /// This control determines whether executions of WBINVD cause VM exits. 600 | /// 601 | uint64_t wbinvd_exiting : 1; 602 | /// This control determines whether guest software may run in unpaged protected mode or in realaddress mode. 603 | /// 604 | uint64_t unrestricted_guest : 1; 605 | /// If this control is 1, the logical processor virtualizes certain APIC accesses. 606 | /// 607 | uint64_t apic_register_virtualization : 1; 608 | /// This controls enables the evaluation and delivery of pending virtual interrupts as well as the 609 | /// emulation of writes to the APIC registers that control interrupt prioritization. 610 | /// 611 | uint64_t virtualize_interrupt_delivery : 1; 612 | /// This control determines whether a series of executions of PAUSE can cause a VM exit. 613 | /// 614 | uint64_t pause_loop_exiting : 1; 615 | /// This control determines whether executions of RDRAND cause VM exits. 616 | /// 617 | uint64_t rdrand_exiting : 1; 618 | /// If this control is 0, any execution of INVPCID causes a #UD. 619 | /// 620 | uint64_t enable_invpcid : 1; 621 | /// Setting this control to 1 enables use of the VMFUNC instruction in VMX non-root operation. 622 | /// 623 | uint64_t enable_vm_functions : 1; 624 | /// If this control is 1, executions of VMREAD and VMWRITE in VMX non-root operation may access 625 | /// a shadow VMCS (instead of causing VM exits). 626 | /// 627 | uint64_t vmcs_shadowing : 1; 628 | /// If this control is 1, executions of ENCLS consult the ENCLS-exiting bitmap to determine whether 629 | /// the instruction causes a VM exit. 630 | /// 631 | uint64_t enable_encls_exiting : 1; 632 | /// This control determines whether executions of RDSEED cause VM exits. 633 | /// 634 | uint64_t rdseed_exiting : 1; 635 | /// If this control is 1, an access to a guest-physical address that sets an EPT dirty bit first adds an 636 | /// entry to the page-modification log. 637 | /// 638 | uint64_t enable_pml : 1; 639 | /// If this control is 1, EPT violations may cause virtualization exceptions (#VE) instead of VM exits. 640 | /// 641 | uint64_t ept_violation : 1; 642 | /// Conceal VMX non-root operation from Intel processor trace (PT). 643 | /// 644 | uint64_t conceal_vmx_from_pt : 1; 645 | /// If this control is 0, any execution of XSAVES or XRSTORS causes a #UD. 646 | /// 647 | uint64_t enable_xsaves : 1; 648 | /// @brief 649 | /// 650 | uint64_t _reserved1 : 1; 651 | /// If this control is 1, EPT execute permissions are based on whether the linear address being 652 | /// accessed is supervisor mode or user mode. 653 | /// 654 | uint64_t mb_exec_ctl_ept : 1; 655 | /// If this control is 1, EPT write permissions may be specified at the granularity of 128 bytes. 656 | /// 657 | uint64_t sp_write_perm_ept : 1; 658 | /// If this control is 1, all output addresses used by Intel Processor Trace are treated as guestphysical 659 | /// addresses and translated using EPT. 660 | /// 661 | uint64_t pt_guest_pa : 1; 662 | /// This control determines whether executions of RDTSC, executions of RDTSCP, and executions 663 | /// of RDMSR that read from the IA32_TIME_STAMP_COUNTER MSR return a value modified by the TSC multiplier field. 664 | /// 665 | uint64_t use_tsc_scaling : 1; 666 | /// If this control is 0, any execution of TPAUSE, UMONITOR, or UMWAIT causes a #UD. 667 | /// 668 | uint64_t enable_user_wait_pause : 1; 669 | /// @brief 670 | /// 671 | uint64_t _reserved2 : 1; 672 | /// If this control is 1, executions of ENCLV consult the ENCLV-exiting bitmap to determine whether 673 | /// the instruction causes a VM exit. 674 | /// 675 | uint64_t enable_enclv_exiting : 1; 676 | }; 677 | }; 678 | 679 | }; 680 | 681 | struct vmx_true_pinbased_controls 682 | { 683 | static constexpr unsigned id = 0x48d; 684 | 685 | uint64_t flags; 686 | }; 687 | 688 | struct vmx_true_procbased_controls 689 | { 690 | static constexpr unsigned id = 0x48e; 691 | 692 | uint64_t flags; 693 | }; 694 | 695 | struct vmx_true_exit_controls 696 | { 697 | static constexpr unsigned id = 0x48f; 698 | 699 | uint64_t flags; 700 | }; 701 | 702 | struct vmx_true_entry_controls 703 | { 704 | static constexpr unsigned id = 0x490; 705 | 706 | uint64_t flags; 707 | }; 708 | 709 | struct vmx_cr0_fixed0 710 | { 711 | static constexpr unsigned id = 0x486; 712 | 713 | uint64_t flags; 714 | }; 715 | 716 | struct vmx_cr0_fixed1 717 | { 718 | static constexpr unsigned id = 0x487; 719 | 720 | uint64_t flags; 721 | }; 722 | 723 | struct vmx_cr4_fixed0 724 | { 725 | static constexpr unsigned id = 0x488; 726 | 727 | uint64_t flags; 728 | }; 729 | 730 | struct vmx_cr4_fixed1 731 | { 732 | static constexpr unsigned id = 0x489; 733 | 734 | uint64_t flags; 735 | }; 736 | 737 | struct vmx_ept_vpid_cap 738 | { 739 | static constexpr unsigned id = 0x48c; 740 | 741 | union 742 | { 743 | uint64_t flags; 744 | 745 | struct 746 | { 747 | /// When set to 1, the processor supports execute-only translations by EPT. 748 | /// 749 | uint64_t rwx_x_only : 1; 750 | /// @brief 751 | /// 752 | uint64_t _reserved1 : 5; 753 | /// Indicates support for a page-walk length of 4. 754 | /// 755 | uint64_t page_walk_length_4 : 1; 756 | /// @brief 757 | /// 758 | uint64_t _reserved2 : 1; 759 | /// When set to 1, the logical processor allows software to configure the EPT paging-structure memory type to be uncacheable (UC). 760 | /// 761 | uint64_t memory_type_uc : 1; 762 | /// @brief 763 | /// 764 | uint64_t _reserved3 : 5; 765 | /// When set to 1, the logical processor allows software to configure the EPT paging-structure memory type to be write-back (WB). 766 | /// 767 | uint64_t memory_type_wb : 1; 768 | /// @brief 769 | /// 770 | uint64_t _reserved4 : 1; 771 | /// When set to 1, the logical processor allows software to configure a EPT PDE to map a 2-Mbyte page. 772 | /// 773 | uint64_t pde_2m : 1; 774 | /// When set to 1, the logical processor allows software to configure a EPT PDPTE to map a 1-Gbyte page. 775 | /// 776 | uint64_t pde_1g : 1; 777 | /// @brief 778 | /// 779 | uint64_t _reserved5 : 2; 780 | /// Support for the INVEPT instruction. 781 | /// 782 | uint64_t invept : 1; 783 | /// When set to 1, accessed and dirty flags for EPT are supported. 784 | /// 785 | uint64_t ept_access_dirty : 1; 786 | /// When set to 1, the processor reports advanced VM-exit information for EPT violations. 787 | /// 788 | uint64_t advanced_vmexit_ept_info : 1; 789 | /// When set to 1, supervisor shadow-stack control is supported. 790 | /// 791 | uint64_t supervisor_shadow_stack : 1; 792 | /// @brief 793 | /// 794 | uint64_t _reserved6 : 1; 795 | /// Support for the INVEPT instruction. 796 | /// 797 | uint64_t invept_single_context : 1; 798 | /// When set to 1, the all-context INVEPT type is supported. 799 | /// 800 | uint64_t invept_all_contexts : 1; 801 | /// @brief 802 | /// 803 | uint64_t _reserved7 : 5; 804 | /// When set to 1, the INVVPID instruction is supported. 805 | /// 806 | uint64_t invvpid : 1; 807 | /// @brief 808 | /// 809 | uint64_t _reserved8 : 7; 810 | /// When set to 1, the individual-address INVVPID type is supported. 811 | /// 812 | uint64_t invvpid_indiv_addr : 1; 813 | /// When set to 1, the single-context INVVPID type is supported. 814 | /// 815 | uint64_t invvpid_single_context : 1; 816 | /// When set to 1, the all-context INVVPID type is supported. 817 | /// 818 | uint64_t invvpid_all_contexts : 1; 819 | /// When set to 1, the single-context-retaining-globals INVVPID type is supported. 820 | /// 821 | uint64_t invvpid_single_context_retain_globals : 1; 822 | /// @brief 823 | /// 824 | uint64_t _reserved9 : 4; 825 | /// Enumerate the maximum HLAT prefix size. 826 | /// 827 | uint64_t max_hlat_prefix_size : 6; 828 | }; 829 | }; 830 | }; 831 | static_assert(sizeof(vmx_ept_vpid_cap) == sizeof(uint64_t), "vmx_ept_vpid_cap size mismatch"); 832 | 833 | struct debugctl 834 | { 835 | static constexpr unsigned id = 0x1d9; 836 | 837 | uint64_t flags; 838 | }; 839 | 840 | struct sysenter_cs 841 | { 842 | static constexpr unsigned id = 0x174; 843 | 844 | uint64_t flags; 845 | }; 846 | 847 | struct sysenter_esp 848 | { 849 | static constexpr unsigned id = 0x175; 850 | 851 | uint64_t flags; 852 | }; 853 | 854 | struct sysenter_eip 855 | { 856 | static constexpr unsigned id = 0x176; 857 | 858 | uint64_t flags; 859 | }; 860 | 861 | struct fsbase 862 | { 863 | static constexpr unsigned id = 0xC0000100; 864 | 865 | uint64_t flags; 866 | }; 867 | 868 | struct gsbase 869 | { 870 | static constexpr unsigned id = 0xC0000101; 871 | 872 | uint64_t flags; 873 | }; 874 | 875 | struct gsbase_shadow 876 | { 877 | static constexpr unsigned id = 0xC0000102; 878 | 879 | uint64_t flags; 880 | }; 881 | 882 | }; 883 | -------------------------------------------------------------------------------- /src/heye/arch/mtrr.cpp: -------------------------------------------------------------------------------- 1 | #include "heye/arch/arch.hpp" 2 | #include "heye/shared/trace.hpp" 3 | 4 | namespace heye 5 | { 6 | mtrr_descriptor::mtrr_descriptor() : variable_available(0) 7 | { 8 | uint64_t index{}; 9 | // Fill fixed range mtrr first. 10 | // 11 | build_mtrr (index); 12 | build_mtrr(index); 13 | build_mtrr(index); 14 | build_mtrr (index); 15 | build_mtrr (index); 16 | build_mtrr (index); 17 | build_mtrr (index); 18 | build_mtrr (index); 19 | build_mtrr (index); 20 | build_mtrr (index); 21 | build_mtrr (index); 22 | // Fill variable range mtrr. 23 | // 24 | for (int i = 0; i < read().vnct; i++) 25 | { 26 | const auto physbase = read(msr::mtrr_physbase::id + i * 2); 27 | const auto physmask = read(msr::mtrr_physmask::id + i * 2); 28 | 29 | if (physmask.valid) 30 | { 31 | unsigned long length{}; 32 | _BitScanForward64(&length, physmask.pfn); 33 | 34 | mtrr[index + variable_available].type = static_cast(physbase.type); 35 | mtrr[index + variable_available].base = (physbase.pfn << page_shift); 36 | mtrr[index + variable_available].size = (physbase.pfn + (1ull << length)) << page_shift; 37 | variable_available++; 38 | } 39 | } 40 | } 41 | 42 | memory_type_t mtrr_descriptor::get_type_or(uint64_t pa, memory_type_t def) const 43 | { 44 | for (const auto& range : *this) 45 | { 46 | if (pa < range.base + range.size) 47 | { 48 | if (pa + 2_mb - 1 >= range.base) 49 | { 50 | def = range.type; 51 | if (def == memory_type_t::uncachable) 52 | break; 53 | } 54 | } 55 | } 56 | return def; 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/heye/arch/mtrr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "memory.hpp" 3 | #include "heye/shared/std/utility.hpp" 4 | 5 | #include 6 | 7 | namespace heye 8 | { 9 | struct mtrr_range 10 | { 11 | uint64_t base; 12 | uint64_t size; 13 | memory_type_t type; 14 | }; 15 | 16 | struct mtrr_descriptor 17 | { 18 | mtrr_descriptor(); 19 | 20 | /// Helper functions for `for` iterator. 21 | /// 22 | auto begin() const { return &mtrr[0]; } 23 | auto end() const { return &mtrr[size()]; } 24 | size_t size() const { return fixed_count + variable_available; } 25 | 26 | /// Get memory type of the physical address, return default if not found. 27 | /// 28 | memory_type_t get_type_or(uint64_t pa, memory_type_t def) const; 29 | 30 | private: 31 | template requires (std::has_id_v) 32 | void build_mtrr(auto& index) 33 | { 34 | uint64_t offset{}; 35 | 36 | for (auto type : read().types) 37 | { 38 | mtrr[index].type = static_cast(type); 39 | mtrr[index].base = T::base + offset; 40 | mtrr[index].size = T::size + T::base + offset; 41 | index += 1; 42 | offset += T::size; 43 | } 44 | }; 45 | 46 | /// Architecture defined number of fixed mtrr registers. 47 | /// 1 register for 64k, 2 registers for 16k and 8 registers for 4k. 48 | /// Each register has 8 ranges as per "Fixed Range MTRRs" states. 49 | /// 50 | static constexpr auto fixed_count = (1 + 2 + 8) * 8; 51 | static constexpr auto variable_count = 255; 52 | 53 | union 54 | { 55 | struct 56 | { 57 | mtrr_range fixed[fixed_count]; 58 | mtrr_range variable[variable_count]; 59 | }; 60 | 61 | mtrr_range mtrr[fixed_count + variable_count]; 62 | }; 63 | 64 | size_t variable_available; 65 | }; 66 | }; 67 | -------------------------------------------------------------------------------- /src/heye/arch/paging.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace heye 6 | { 7 | struct eptp_t 8 | { 9 | union 10 | { 11 | uint64_t flags; 12 | 13 | struct 14 | { 15 | /// EPT paging-structure memory type. 16 | /// 0 = Uncacheable (UC) 17 | /// 6 = Write-back (WB) 18 | /// 19 | uint64_t memory_type : 3; 20 | /// This value less than the EPT page walk length. 21 | /// 22 | uint64_t page_walk_length : 3; 23 | /// Setting this control to 1 enables accessed and dirty flags for EPT. 24 | /// 25 | uint64_t access_flags : 1; 26 | /// @brief 27 | /// 28 | uint64_t _reserved1 : 5; 29 | /// Physical address of EPT PML4 table. 30 | /// 31 | uint64_t pml4_address : 36; 32 | /// @brief 33 | /// 34 | uint64_t _reserved2 : 16; 35 | }; 36 | }; 37 | }; 38 | 39 | struct pml4_t 40 | { 41 | union 42 | { 43 | uint64_t flags; 44 | 45 | struct 46 | { 47 | /// Read access. 48 | /// 49 | uint64_t read : 1; 50 | /// Write access. 51 | /// 52 | uint64_t write : 1; 53 | /// Execute access. 54 | /// 55 | uint64_t execute : 1; 56 | /// @brief 57 | /// 58 | uint64_t _reserved1 : 5; 59 | /// Indicates whether software has accessed this region. 60 | /// 61 | uint64_t accessed : 1; 62 | /// @brief 63 | /// 64 | uint64_t _reserved2 : 1; 65 | /// Execute access for usermode linear address. 66 | /// 67 | uint64_t execute_usermode : 1; 68 | /// @brief 69 | /// 70 | uint64_t _reserved3 : 1; 71 | /// Physical address of PDPT. 72 | /// 73 | uint64_t pfn : 36; 74 | /// @brief 75 | /// 76 | uint64_t _reserved4 : 16; 77 | }; 78 | }; 79 | }; 80 | static_assert(sizeof(pml4_t) == sizeof(uint64_t), "EPT PML4 Entry size mismatch"); 81 | 82 | struct pdpt_1gb_t 83 | { 84 | union 85 | { 86 | uint64_t flags; 87 | 88 | struct 89 | { 90 | /// Read access. 91 | /// 92 | uint64_t read : 1; 93 | /// Write access. 94 | /// 95 | uint64_t write : 1; 96 | /// Execute access. 97 | /// 98 | uint64_t execute : 1; 99 | /// EPT memory type for this 1-GByte page. 100 | /// 101 | uint64_t memory_type : 3; 102 | /// Ignore PAT memory type for this 1-GByte page. 103 | /// 104 | uint64_t ignore_pat : 1; 105 | /// Must be 1 (otherwise, this entry references an EPT page directory). 106 | /// 107 | uint64_t large_page : 1; 108 | /// Indicates whether software has accessed this region. 109 | /// 110 | uint64_t accessed : 1; 111 | /// Indicates whether software has written to the 1-GByte page referenced by this entry. 112 | /// 113 | uint64_t dirty : 1; 114 | /// Execute access for usermode linear address. 115 | /// 116 | uint64_t execute_usermode : 1; 117 | /// @brief 118 | /// 119 | uint64_t _reserved1 : 19; 120 | /// Physical address of PD. 121 | /// 122 | uint64_t pfn : 18; 123 | /// @brief 124 | /// 125 | uint64_t _reserved2 : 15; 126 | /// Suppress #VE. 127 | /// 128 | uint64_t suppress_ve : 1; 129 | }; 130 | }; 131 | }; 132 | static_assert(sizeof(pdpt_1gb_t) == sizeof(uint64_t), "EPT PDPT 1GB Entry size mismatch"); 133 | 134 | struct pdpt_t 135 | { 136 | union 137 | { 138 | uint64_t flags; 139 | 140 | struct 141 | { 142 | /// Read access. 143 | /// 144 | uint64_t read : 1; 145 | /// Write access. 146 | /// 147 | uint64_t write : 1; 148 | /// Execute access. 149 | /// 150 | uint64_t execute : 1; 151 | /// @brief 152 | /// 153 | uint64_t _reserved1 : 5; 154 | /// Indicates whether software has accessed this region. 155 | /// 156 | uint64_t accessed : 1; 157 | /// @brief 158 | /// 159 | uint64_t _reserved2 : 1; 160 | /// Execute access for usermode linear address. 161 | /// 162 | uint64_t execute_usermode : 1; 163 | /// @brief 164 | /// 165 | uint64_t _reserved3 : 1; 166 | /// Physical address of PD. 167 | /// 168 | uint64_t pfn : 36; 169 | /// @brief 170 | /// 171 | uint64_t _reserved4 : 16; 172 | }; 173 | }; 174 | }; 175 | static_assert(sizeof(pdpt_t) == sizeof(uint64_t), "EPT PDPT Entry size mismatch"); 176 | 177 | struct pd_2mb_t 178 | { 179 | union 180 | { 181 | uint64_t flags; 182 | 183 | struct 184 | { 185 | /// Read access. 186 | /// 187 | uint64_t read : 1; 188 | /// Write access. 189 | /// 190 | uint64_t write : 1; 191 | /// Execute access. 192 | /// 193 | uint64_t execute : 1; 194 | /// EPT memory type for this 1-GByte page. 195 | /// 196 | uint64_t memory_type : 3; 197 | /// Ignore PAT memory type for this 1-GByte page. 198 | /// 199 | uint64_t ignore_pat : 1; 200 | /// Must be 1 (otherwise, this entry references an EPT page table). 201 | /// 202 | uint64_t large_page : 1; 203 | /// Indicates whether software has accessed this region. 204 | /// 205 | uint64_t accessed : 1; 206 | /// Indicates whether software has written to the 1-GByte page referenced by this entry. 207 | /// 208 | uint64_t dirty : 1; 209 | /// Execute access for usermode linear address. 210 | /// 211 | uint64_t execute_usermode : 1; 212 | /// @brief 213 | /// 214 | uint64_t _reserved1 : 10; 215 | /// Physical address of PD. 216 | /// 217 | uint64_t pfn : 27; 218 | /// @brief 219 | /// 220 | uint64_t _reserved2 : 15; 221 | /// Suppress #VE. 222 | /// 223 | uint64_t suppress_ve : 1; 224 | }; 225 | }; 226 | }; 227 | static_assert(sizeof(pd_2mb_t) == sizeof(uint64_t), "EPT PD 2MB Entry size mismatch"); 228 | 229 | struct pd_t 230 | { 231 | union 232 | { 233 | uint64_t flags; 234 | 235 | struct 236 | { 237 | /// Read access. 238 | /// 239 | uint64_t read : 1; 240 | /// Write access. 241 | /// 242 | uint64_t write : 1; 243 | /// Execute access. 244 | /// 245 | uint64_t execute : 1; 246 | /// @brief 247 | /// 248 | uint64_t _reserved1 : 5; 249 | /// Indicates whether software has accessed this region. 250 | /// 251 | uint64_t accessed : 1; 252 | /// @brief 253 | /// 254 | uint64_t _reserved2 : 1; 255 | /// Execute access for usermode linear address. 256 | /// 257 | uint64_t execute_usermode : 1; 258 | /// @brief 259 | /// 260 | uint64_t _reserved3 : 1; 261 | /// Physical address of PD. 262 | /// 263 | uint64_t pfn : 36; 264 | /// @brief 265 | /// 266 | uint64_t _reserved4 : 16; 267 | }; 268 | }; 269 | }; 270 | static_assert(sizeof(pd_t) == sizeof(uint64_t), "EPT PD Entry size mismatch"); 271 | 272 | struct pte_t 273 | { 274 | union 275 | { 276 | uint64_t flags; 277 | 278 | struct 279 | { 280 | /// Read access. 281 | /// 282 | uint64_t read : 1; 283 | /// Write access. 284 | /// 285 | uint64_t write : 1; 286 | /// Execute access. 287 | /// 288 | uint64_t execute : 1; 289 | /// @brief 290 | /// 291 | uint64_t memory_type : 3; 292 | /// @brief 293 | /// 294 | uint64_t ignore_pat : 1; 295 | /// @brief 296 | /// 297 | uint64_t _reserved1 : 1; 298 | /// Indicates whether software has accessed this region. 299 | /// 300 | uint64_t accessed : 1; 301 | /// Indicates whether software has modified this region. 302 | /// 303 | uint64_t dirty : 1; 304 | /// Execute access for usermode linear address. 305 | /// 306 | uint64_t execute_usermode : 1; 307 | /// @brief 308 | /// 309 | uint64_t _reserved2 : 1; 310 | /// Physical address of PD. 311 | /// 312 | uint64_t pfn : 36; 313 | /// @brief 314 | /// 315 | uint64_t _reserved3 : 15; 316 | /// Suppress #VE. 317 | /// 318 | uint64_t suppress_ve : 1; 319 | }; 320 | }; 321 | }; 322 | static_assert(sizeof(pte_t) == sizeof(uint64_t), "EPT PTE Entry size mismatch"); 323 | }; 324 | -------------------------------------------------------------------------------- /src/heye/arch/segments.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace heye 6 | { 7 | struct selector_t 8 | { 9 | union 10 | { 11 | uint16_t flags; 12 | 13 | struct 14 | { 15 | /// Requested Privilege Level. 16 | /// 17 | uint16_t rpl : 2; 18 | /// Table Indicator. 19 | /// 20 | uint16_t ti : 1; 21 | /// Index. 22 | /// 23 | uint16_t index : 13; 24 | }; 25 | }; 26 | }; 27 | static_assert(sizeof(selector_t) == sizeof(uint16_t), "segment selector size mismatch"); 28 | 29 | struct es_t : public selector_t{}; 30 | struct cs_t : public selector_t{}; 31 | struct ss_t : public selector_t{}; 32 | struct ds_t : public selector_t{}; 33 | struct fs_t : public selector_t{}; 34 | struct gs_t : public selector_t{}; 35 | struct tr_t : public selector_t{}; 36 | struct ldtr_t : public selector_t{}; 37 | 38 | struct access_t 39 | { 40 | union 41 | { 42 | uint32_t flags; 43 | 44 | struct 45 | { 46 | /// Segment type. 47 | /// 48 | uint32_t type : 4; 49 | /// @brief 50 | /// 51 | uint32_t dt : 1; 52 | /// Requested Privilege Level. 53 | /// 54 | uint32_t rpl : 2; 55 | /// Segment present. 56 | /// 57 | uint32_t present : 1; 58 | /// @brief 59 | /// 60 | uint32_t _reserved1 : 4; 61 | /// Available for use by system software. 62 | /// 63 | uint32_t avl : 1; 64 | /// 64-bit code segment (IA-32e mode only). 65 | /// 66 | uint32_t l : 1; 67 | /// Default operation size (0 = 16-bit segment; 1 = 32-bit segment). 68 | /// 69 | uint32_t db : 1; 70 | /// Granularity. 71 | /// 72 | uint32_t granularity : 1; 73 | /// Segment is unsuable. 74 | /// 75 | uint32_t unusable : 1; 76 | }; 77 | }; 78 | }; 79 | static_assert(sizeof(access_t) == sizeof(uint32_t), "access_t size mismatch"); 80 | 81 | template 82 | struct segment_t 83 | { 84 | /// @brief 85 | /// 86 | T selector; 87 | 88 | /// @brief 89 | /// 90 | uint32_t limit; 91 | 92 | /// @brief 93 | /// 94 | access_t rights; 95 | 96 | /// @brief 97 | /// 98 | uint64_t base; 99 | }; 100 | 101 | struct descriptor_t 102 | { 103 | union 104 | { 105 | uint64_t flags; 106 | 107 | struct 108 | { 109 | /// Segment Limit. 110 | /// 111 | uint64_t limit_low : 16; 112 | /// Segment base address. 113 | /// 114 | uint64_t base_low : 16; 115 | /// Segment base address. 116 | /// 117 | uint64_t base_mid : 8; 118 | /// Segment type. 119 | /// 120 | uint64_t type : 4; 121 | /// Descriptor type. 122 | /// 123 | uint64_t system : 1; 124 | /// Descriptor privilege level. 125 | /// 126 | uint64_t dpl : 2; 127 | /// Segment present. 128 | /// 129 | uint64_t present : 1; 130 | /// Segment Limit. 131 | /// 132 | uint64_t limit_high : 4; 133 | /// Available for use by system software. 134 | /// 135 | uint64_t avl : 1; 136 | /// 64-bit code segment (IA-32e mode only). 137 | /// 138 | uint64_t l : 1; 139 | /// Default operation size (0 = 16-bit segment; 1 = 32-bit segment). 140 | /// 141 | uint64_t db : 1; 142 | /// Granularity. 143 | /// 144 | uint64_t granularity: 1; 145 | /// Segment base address. 146 | /// 147 | uint64_t base_high : 8; 148 | }; 149 | }; 150 | 151 | /// Segment base address. 152 | /// 153 | uint64_t base_upper : 32; 154 | uint64_t must_be_zero : 32; 155 | 156 | inline uint64_t base() const 157 | { 158 | uint64_t base_address{}; 159 | base_address |= static_cast(base_low) << 0; 160 | base_address |= static_cast(base_mid) << 16; 161 | base_address |= static_cast(base_high) << 24; 162 | if (!system) 163 | base_address |= static_cast(base_upper) << 32; 164 | return base_address; 165 | } 166 | 167 | inline uint64_t limit() const 168 | { 169 | return limit_low | limit_high << 8; 170 | } 171 | }; 172 | static_assert(sizeof(descriptor_t) == 16, "segment descriptor size mismatch"); 173 | 174 | /// Descriptor table register. 175 | /// 176 | #pragma pack(push, 1) 177 | struct gdtr_t 178 | { 179 | /// @brief 180 | /// 181 | uint16_t limit; 182 | /// @brief 183 | /// 184 | uint64_t base; 185 | }; 186 | #pragma pack(pop) 187 | static_assert(sizeof(gdtr_t) == 10, "Gdtr size mismatch"); 188 | 189 | /// Same for interrupt descriptor table register. 190 | /// 191 | struct idtr_t : public gdtr_t {}; 192 | static_assert(sizeof(idtr_t) == 10, "Idtr size mismatch"); 193 | }; 194 | -------------------------------------------------------------------------------- /src/heye/arch/vmx.cpp: -------------------------------------------------------------------------------- 1 | #include "vmx.hpp" 2 | #include "asm.hpp" 3 | 4 | namespace heye::vmx 5 | { 6 | void invept(vmx::invept_t type, uint64_t eptp) 7 | { 8 | vmx::invept_desc_t descriptor{ .eptp = eptp }; 9 | asm_invept(static_cast(type), &descriptor); 10 | } 11 | 12 | void invvpid(vmx::invvpid_t type, uint64_t vpid, uint64_t address) 13 | { 14 | vmx::invvpid_desc_t descriptor{}; 15 | switch (type) 16 | { 17 | case vmx::invvpid_t::linear_address: 18 | descriptor.vpid = vpid; 19 | descriptor.linear_address = address; 20 | break; 21 | case vmx::invvpid_t::single_context: 22 | descriptor.vpid = vpid; 23 | break; 24 | default: 25 | break; 26 | } 27 | asm_invvpid(static_cast(type), &descriptor); 28 | } 29 | } // namespace heye::vmx 30 | -------------------------------------------------------------------------------- /src/heye/arch/vmx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../config.hpp" 3 | 4 | #include 5 | 6 | namespace heye::vmx 7 | { 8 | enum class vmcs : uint64_t 9 | { 10 | virtual_processor_id = 0x00000000, 11 | posted_interrupt_notification = 0x00000002, 12 | eptp_index = 0x00000004, 13 | /// 16-bit guest fields. 14 | /// 15 | guest_es_selector = 0x00000800, 16 | guest_cs_selector = 0x00000802, 17 | guest_ss_selector = 0x00000804, 18 | guest_ds_selector = 0x00000806, 19 | guest_fs_selector = 0x00000808, 20 | guest_gs_selector = 0x0000080a, 21 | guest_ldtr_selector = 0x0000080c, 22 | guest_tr_selector = 0x0000080e, 23 | guest_interrupt_status = 0x00000810, 24 | pml_index = 0x00000812, 25 | /// 16-bit host fields. 26 | /// 27 | host_es_selector = 0x00000c00, 28 | host_cs_selector = 0x00000c02, 29 | host_ss_selector = 0x00000c04, 30 | host_ds_selector = 0x00000c06, 31 | host_fs_selector = 0x00000c08, 32 | host_gs_selector = 0x00000c0a, 33 | host_tr_selector = 0x00000c0c, 34 | /// 64-bit control fields. 35 | /// 36 | io_bitmap_a = 0x00002000, 37 | io_bitmap_a_high = 0x00002001, 38 | io_bitmap_b = 0x00002002, 39 | io_bitmap_b_high = 0x00002003, 40 | msr_bitmap = 0x00002004, 41 | msr_bitmap_high = 0x00002005, 42 | vm_exit_msr_store_addr = 0x00002006, 43 | vm_exit_msr_store_addr_high = 0x00002007, 44 | vm_exit_msr_load_addr = 0x00002008, 45 | vm_exit_msr_load_addr_high = 0x00002009, 46 | vm_entry_msr_load_addr = 0x0000200a, 47 | vm_entry_msr_load_addr_high = 0x0000200b, 48 | executive_vmcs_pointer = 0x0000200c, 49 | executive_vmcs_pointer_high = 0x0000200d, 50 | tsc_offset = 0x00002010, 51 | tsc_offset_high = 0x00002011, 52 | virtual_apic_page_addr = 0x00002012, 53 | virtual_apic_page_addr_high = 0x00002013, 54 | apic_access_addr = 0x00002014, 55 | apic_access_addr_high = 0x00002015, 56 | vmfunc_controls = 0x00002018, 57 | vmfunc_controls_high = 0x00002019, 58 | ept_pointer = 0x0000201a, 59 | ept_pointer_high = 0x0000201b, 60 | eptp_list = 0x00002024, 61 | eptp_list_high = 0x00002025, 62 | /// 64-bit read-only fields. 63 | /// 64 | guest_physical_address = 0x00002400, 65 | guest_physical_address_high = 0x00002401, 66 | /// 64-bit guest fields. 67 | /// 68 | vmcs_link_pointer = 0x00002800, 69 | vmcs_link_pointer_high = 0x00002801, 70 | guest_ia32_debugctl = 0x00002802, 71 | guest_ia32_debugctl_high = 0x00002803, 72 | guest_ia32_pat = 0x00002804, 73 | guest_ia32_pat_high = 0x00002805, 74 | guest_ia32_efer = 0x00002806, 75 | guest_ia32_efer_high = 0x00002807, 76 | guest_ia32_perf_ctrl = 0x00002808, 77 | guest_ia32_perf_ctrl_high = 0x00002809, 78 | guest_pdptr0 = 0x0000280a, 79 | guest_pdptr0_high = 0x0000280b, 80 | guest_pdptr1 = 0x0000280c, 81 | guest_pdptr1_high = 0x0000280d, 82 | guest_pdptr2 = 0x0000280e, 83 | guest_pdptr2_high = 0x0000280f, 84 | guest_pdptr3 = 0x00002810, 85 | guest_pdptr3_high = 0x00002811, 86 | guest_ia32_bndcfgs = 0x00002812, 87 | guest_ia32_bndcfgs_high = 0x00002813, 88 | /// 64-bit host fields. 89 | /// 90 | host_ia32_pat = 0x00002c00, 91 | host_ia32_pat_high = 0x00002c01, 92 | host_ia32_efer = 0x00002c02, 93 | host_ia32_efer_high = 0x00002c03, 94 | host_ia32_perf_ctrl = 0x00002c04, 95 | host_ia32_perf_ctrl_high = 0x00002c05, 96 | /// 32-bit control fields. 97 | /// 98 | pin_based_vm_exec_control = 0x00004000, 99 | cpu_based_vm_exec_control = 0x00004002, 100 | exception_bitmap = 0x00004004, 101 | page_fault_error_code_mask = 0x00004006, 102 | page_fault_error_code_match = 0x00004008, 103 | cr3_target_count = 0x0000400a, 104 | vm_exit_controls = 0x0000400c, 105 | vm_exit_msr_store_count = 0x0000400e, 106 | vm_exit_msr_load_count = 0x00004010, 107 | vm_entry_controls = 0x00004012, 108 | vm_entry_msr_load_count = 0x00004014, 109 | vm_entry_intr_info = 0x00004016, 110 | vm_entry_intr_error_code = 0x00004018, 111 | vm_entry_instruction_len = 0x0000401a, 112 | tpr_threshold = 0x0000401c, 113 | secondary_vm_exec_control = 0x0000401e, 114 | ple_gap = 0x00004020, 115 | ple_window = 0x00004022, 116 | /// 32-bit read-only fields. 117 | /// 118 | vm_instruction_error = 0x00004400, 119 | vm_exit_reason = 0x00004402, 120 | vm_exit_intr_info = 0x00004404, 121 | vm_exit_intr_error_code = 0x00004406, 122 | idt_vectoring_info_field = 0x00004408, 123 | idt_vectoring_error_code = 0x0000440a, 124 | vm_exit_instruction_len = 0x0000440c, 125 | vmx_instruction_info = 0x0000440e, 126 | /// 32-bit guest fields. 127 | /// 128 | guest_es_limit = 0x00004800, 129 | guest_cs_limit = 0x00004802, 130 | guest_ss_limit = 0x00004804, 131 | guest_ds_limit = 0x00004806, 132 | guest_fs_limit = 0x00004808, 133 | guest_gs_limit = 0x0000480a, 134 | guest_ldtr_limit = 0x0000480c, 135 | guest_tr_limit = 0x0000480e, 136 | guest_gdtr_limit = 0x00004810, 137 | guest_idtr_limit = 0x00004812, 138 | guest_es_ar_bytes = 0x00004814, 139 | guest_cs_ar_bytes = 0x00004816, 140 | guest_ss_ar_bytes = 0x00004818, 141 | guest_ds_ar_bytes = 0x0000481a, 142 | guest_fs_ar_bytes = 0x0000481c, 143 | guest_gs_ar_bytes = 0x0000481e, 144 | guest_ldtr_ar_bytes = 0x00004820, 145 | guest_tr_ar_bytes = 0x00004822, 146 | guest_interruptibility_info = 0x00004824, 147 | guest_activity_state = 0x00004826, 148 | guest_sm_base = 0x00004828, 149 | guest_sysenter_cs = 0x0000482a, 150 | vmx_preemption_timer = 0x000048ae, 151 | /// 32-bit host fields. 152 | /// 153 | host_sysenter_cs = 0x00004c00, 154 | /// 64-bit control fields. 155 | /// 156 | cr0_guest_host_mask = 0x00006000, 157 | cr4_guest_host_mask = 0x00006002, 158 | cr0_read_shadow = 0x00006004, 159 | cr4_read_shadow = 0x00006006, 160 | cr3_target_value0 = 0x00006008, 161 | cr3_target_value1 = 0x0000600a, 162 | cr3_target_value2 = 0x0000600c, 163 | cr3_target_value3 = 0x0000600e, 164 | /// 64-bit read-only fields. 165 | /// 166 | exit_qualification = 0x00006400, 167 | io_rcx = 0x00006402, 168 | io_rsi = 0x00006404, 169 | io_rdi = 0x00006406, 170 | io_rip = 0x00006408, 171 | guest_linear_address = 0x0000640a, 172 | /// 64-bit guest fields. 173 | /// 174 | guest_cr0 = 0x00006800, 175 | guest_cr3 = 0x00006802, 176 | guest_cr4 = 0x00006804, 177 | guest_es_base = 0x00006806, 178 | guest_cs_base = 0x00006808, 179 | guest_ss_base = 0x0000680a, 180 | guest_ds_base = 0x0000680c, 181 | guest_fs_base = 0x0000680e, 182 | guest_gs_base = 0x00006810, 183 | guest_ldtr_base = 0x00006812, 184 | guest_tr_base = 0x00006814, 185 | guest_gdtr_base = 0x00006816, 186 | guest_idtr_base = 0x00006818, 187 | guest_dr7 = 0x0000681a, 188 | guest_rsp = 0x0000681c, 189 | guest_rip = 0x0000681e, 190 | guest_rflags = 0x00006820, 191 | guest_pending_dbg_exceptions = 0x00006822, 192 | guest_sysenter_esp = 0x00006824, 193 | guest_sysenter_eip = 0x00006826, 194 | /// 64-bit host fields. 195 | /// 196 | host_cr0 = 0x00006c00, 197 | host_cr3 = 0x00006c02, 198 | host_cr4 = 0x00006c04, 199 | host_fs_base = 0x00006c06, 200 | host_gs_base = 0x00006c08, 201 | host_tr_base = 0x00006c0a, 202 | host_gdtr_base = 0x00006c0c, 203 | host_idtr_base = 0x00006c0e, 204 | host_sysenter_esp = 0x00006c10, 205 | host_sysenter_eip = 0x00006c12, 206 | host_rsp = 0x00006c14, 207 | host_rip = 0x00006c16, 208 | }; 209 | 210 | enum class exit_reason : uint64_t 211 | { 212 | exception_nmi = 0, 213 | external_interrupt = 1, 214 | triple_fault = 2, 215 | init = 3, 216 | sipi = 4, 217 | io_smi = 5, 218 | other_smi = 6, 219 | pending_virt_intr = 7, 220 | pending_virt_nmi = 8, 221 | task_switch = 9, 222 | cpuid = 10, 223 | getsec = 11, 224 | hlt = 12, 225 | invd = 13, 226 | invlpg = 14, 227 | rdpmc = 15, 228 | rdtsc = 16, 229 | rsm = 17, 230 | vmcall = 18, 231 | vmclear = 19, 232 | vmlaunch = 20, 233 | vmptrld = 21, 234 | vmptrst = 22, 235 | vmread = 23, 236 | vmresume = 24, 237 | vmwrite = 25, 238 | vmxoff = 26, 239 | vmxon = 27, 240 | cr_access = 28, 241 | dr_access = 29, 242 | io_instruction = 30, 243 | msr_read = 31, 244 | msr_write = 32, 245 | invalid_guest_state = 33, 246 | msr_loading = 34, 247 | mwait_instruction = 36, 248 | monitor_trap_flag = 37, 249 | monitor_instruction = 39, 250 | pause_instruction = 40, 251 | mce_during_vmentry = 41, 252 | tpr_below_threshold = 43, 253 | apic_access = 44, 254 | access_gdtr_or_idtr = 46, 255 | access_ldtr_or_tr = 47, 256 | ept_violation = 48, 257 | ept_misconfig = 49, 258 | invept = 50, 259 | rdtscp = 51, 260 | vmx_preemption_timer_expired = 52, 261 | invvpid = 53, 262 | wbinvd = 54, 263 | xsetbv = 55, 264 | apic_write = 56, 265 | rdrand = 57, 266 | invpcid = 58, 267 | rdseed = 61, 268 | pml_full = 62, 269 | xsaves = 63, 270 | xrstors = 64, 271 | pcommit = 65, 272 | max = 66 273 | }; 274 | 275 | enum interrupt_t 276 | { 277 | external_interrupt = 0, 278 | reserved = 1, 279 | nmi = 2, 280 | hardware_exception = 3, 281 | software_interrupt = 4, 282 | privileged_software_interrupt = 5, 283 | software_exception = 6, 284 | other_event = 7 285 | }; 286 | 287 | enum class invept_t : uint64_t 288 | { 289 | single_context = 1, 290 | all_contexts = 2 291 | }; 292 | 293 | enum class invvpid_t : uint64_t 294 | { 295 | linear_address = 0, 296 | single_context = 1, 297 | all_contexts = 2, 298 | single_context_retaining_globals = 3, 299 | }; 300 | 301 | struct vmcs_t 302 | { 303 | uint32_t revision_id; 304 | uint32_t abort_indicator; 305 | uint8_t data[page_size - 8]; 306 | }; 307 | static_assert(sizeof(vmcs_t) == page_size, "VMCS size mismatch"); 308 | 309 | /// Table 28-1. Exit Qualification for Debug Exceptions 310 | /// 311 | struct exit_qualification_debug_exception_t 312 | { 313 | union 314 | { 315 | uint64_t flags; 316 | 317 | struct 318 | { 319 | /// When set, each of these bits indicates that the corresponding breakpoint condition was met. 320 | /// Any of these bits may be set even if its corresponding enabling bit in DR7 is not set. 321 | /// 322 | uint64_t breakpoint_condition : 4; 323 | /// Reserved. 324 | /// 325 | uint64_t reserved0 : 9; 326 | /// When set, this bit indicates that the cause of the debug exception is "debug register access detected." 327 | /// 328 | uint64_t debug_register_access_deteced : 1; 329 | /// When set, this bit indicates that the cause of the debug exception is either the execution of a single 330 | /// instruction (if RFLAGS.TF = 1 and IA32_DEBUGCTL.BTF = 0) or a taken branch (if RFLAGS.TF = DEBUGCTL.BTF = 1). 331 | /// 332 | uint64_t single_instruction : 1; 333 | /// Reserved. 334 | /// 335 | uint64_t reserved1 : 1; 336 | /// When set, this bit indicates that a debug exception (#DB) or a breakpoint exception (#BP) occurred 337 | /// inside an RTM region while advanced debugging of RTM transactional regions was enabled. 338 | /// 339 | uint64_t rtm_region_exception : 1; 340 | }; 341 | }; 342 | }; 343 | 344 | /// Table 28-2. Exit Qualification for Task Switches. 345 | /// 346 | struct exit_qualification_task_switch_t 347 | { 348 | union 349 | { 350 | uint64_t flags; 351 | 352 | struct 353 | { 354 | /// Selector of task-state segment (TSS) to which the guest attempted to switch. 355 | /// 356 | uint64_t selector : 16; 357 | /// Reserved. 358 | /// 359 | uint64_t reserved0 : 14; 360 | /// Source of task switch initiation: 361 | /// 0: CALL instruction 362 | /// 1: IRET instruction 363 | /// 2: JMP instruction 364 | /// 3: Task gate in IDT 365 | /// 366 | uint64_t type : 2; 367 | }; 368 | }; 369 | }; 370 | 371 | /// Table 28-3. Exit Qualification for Control-Register Accesses. 372 | /// 373 | struct exit_qualification_mov_cr_t 374 | { 375 | enum 376 | { 377 | mov_to_cr = 0, 378 | mov_from_cr = 1, 379 | clts = 2, 380 | lmsw = 3, 381 | }; 382 | 383 | enum 384 | { 385 | lmsw_op_register = 0, 386 | lmsw_op_memory = 1 387 | }; 388 | 389 | union 390 | { 391 | uint64_t flags; 392 | 393 | struct 394 | { 395 | /// Number of control register (0 for CLTS and LMSW). 396 | /// 397 | uint64_t cr_number : 4; 398 | /// Access type: 399 | /// 0 = MOV to CR 400 | /// 1 = MOV from CR 401 | /// 2 = CLTS 402 | /// 3 = LMSW 403 | /// 404 | uint64_t access_type : 2; 405 | /// LMSW operand type: 406 | /// 0 = register 407 | /// 1 = memory 408 | /// 409 | uint64_t lmsw_type : 1; 410 | /// Reserved. 411 | /// 412 | uint64_t reserved0 : 1; 413 | /// For MOV CR, the general-purpose register. 414 | /// 415 | uint64_t gpr : 4; 416 | /// Reserved. 417 | /// 418 | uint64_t reserved1 : 4; 419 | /// The LMSW source data. 420 | /// 421 | uint64_t lmsw_data : 16; 422 | }; 423 | }; 424 | }; 425 | 426 | /// Table 28-4. Exit Qualification for MOV DR 427 | /// 428 | struct exit_qualification_mov_dr_t 429 | { 430 | union 431 | { 432 | uint64_t flags; 433 | 434 | struct 435 | { 436 | /// Number of debug register. 437 | /// 438 | uint64_t dr_number : 3; 439 | /// Reserved. 440 | /// 441 | uint64_t reserved0 : 1; 442 | /// Direction of access (0 = MOV to DR; 1 = MOV from DR). 443 | /// 444 | uint64_t access_type : 1; 445 | /// Reserved. 446 | /// 447 | uint64_t reserved1 : 3; 448 | /// General-purpose register. 449 | /// 450 | uint64_t grp : 4; 451 | }; 452 | }; 453 | }; 454 | 455 | /// Table 28-5. Exit Qualification for I/O Instructions 456 | /// 457 | struct exit_qualification_io_t 458 | { 459 | enum 460 | { 461 | access_out = 0, 462 | access_in = 1 463 | }; 464 | 465 | enum 466 | { 467 | op_encoding_dx = 0, 468 | op_encoding_imm = 1 469 | }; 470 | 471 | union 472 | { 473 | uint64_t flags; 474 | 475 | struct 476 | { 477 | /// Size of access. 478 | /// 479 | uint64_t access_size : 3; 480 | /// Direction of the attempted access (0 = OUT, 1 = IN). 481 | /// 482 | uint64_t access_type : 1; 483 | /// String instruction (0 = not string; 1 = string). 484 | /// 485 | uint64_t string_instruction : 1; 486 | /// REP prefixed (0 = not REP; 1 = REP). 487 | /// 488 | uint64_t rep_prefixed : 1; 489 | /// Operand encoding (0 = DX, 1 = immediate). 490 | uint64_t op_encoding : 1; 491 | /// Reserved. 492 | /// 493 | uint64_t reserved0 : 9; 494 | /// Port number. 495 | /// 496 | uint64_t port_number : 16; 497 | }; 498 | }; 499 | }; 500 | 501 | /// Table 28-6. Exit Qualification for APIC-Access VM Exits from Linear Accesses and Guest-Physical Accesses 502 | /// 503 | struct exit_qualification_apic_access_t 504 | { 505 | union 506 | { 507 | uint64_t flags; 508 | 509 | struct 510 | { 511 | /// If the APIC-access VM exit is due to a linear access, the offset of access within the APIC page. 512 | /// 513 | uint64_t page_offset : 12; 514 | /// Access type. 515 | /// 516 | uint64_t access_type : 4; 517 | /// This bit is set for certain accesses that are asynchronous to instruction execution and not part of event delivery. 518 | /// 519 | uint64_t access_async : 1; 520 | }; 521 | }; 522 | }; 523 | 524 | /// Table 28-7. Exit Qualification for EPT Violations 525 | /// 526 | struct exit_qualification_ept_violation_t 527 | { 528 | union 529 | { 530 | uint64_t flags; 531 | 532 | struct 533 | { 534 | uint64_t read : 1; 535 | uint64_t write : 1; 536 | uint64_t execute : 1; 537 | uint64_t gpa_readable : 1; 538 | uint64_t gpa_writable : 1; 539 | uint64_t gpa_executable : 1; 540 | uint64_t gpa_execute_for_user_mode : 1; 541 | uint64_t guest_linear_address_valid : 1; 542 | uint64_t violation_type : 1; 543 | uint64_t user_mode_linear_address : 1; 544 | uint64_t readable_writable_page : 1; 545 | uint64_t execute_disable_page : 1; 546 | uint64_t nmi_unblocking : 1; 547 | }; 548 | }; 549 | }; 550 | 551 | /// https://github.com/wbenny/hvpp/blob/84b3f3c241e1eec3ab42f75cad9deef3ad67e6ab/src/hvpp/hvpp/ia32/vmx/exit_qualification.h#L157 552 | /// 553 | struct exit_qualification_t 554 | { 555 | // For INVEPT, INVPCID, INVVPID, LGDT, LIDT, LLDT, LTR, SGDT, SIDT, 556 | // SLDT, STR, VMCLEAR, VMPTRLD, VMPTRST, VMREAD, VMWRITE, VMXON, 557 | // XRSTORS, and XSAVES, the exit qualification receives the value 558 | // of the instruction’s displacement field, which is sign-extended 559 | // to 64 bits if necessary (32 bits on processors that do not support 560 | // Intel 64 architecture). If the instruction has no displacement 561 | // (for example, has a register operand), zero is stored into the 562 | // exit qualification. On processors that support Intel 64 architecture, 563 | // an exception is made for RIP-relative addressing (used only in 64-bit 564 | // mode). Such addressing causes an instruction to use an address 565 | // that is the sum of the displacement field and the value of RIP 566 | // that references the following instruction. In this case, the 567 | // exit qualification is loaded with the sum of the displacement field 568 | // and the appropriate RIP value. 569 | // (ref: Vol3C[27.2.1(Basic VM-Exit Information)]) 570 | // 571 | uint64_t displacement; 572 | 573 | // For a page-fault exception, the exit qualification contains the 574 | // linear-address that caused the page fault. 575 | // 576 | // For INVLPG, the exit qualification contains the linear-address operand 577 | // of the instruction. 578 | // 579 | uint64_t linear_address; 580 | 581 | exit_qualification_debug_exception_t debug_exception; 582 | exit_qualification_task_switch_t task_switch; 583 | exit_qualification_mov_cr_t mov_cr; 584 | exit_qualification_mov_dr_t mov_dr; 585 | exit_qualification_io_t io_instruction; 586 | exit_qualification_apic_access_t apic_access; 587 | exit_qualification_ept_violation_t ept_violation; 588 | }; 589 | 590 | struct msr_bitmap_t 591 | { 592 | static constexpr auto low_min = 0x00000000; 593 | static constexpr auto low_max = 0x00001FFF; 594 | static constexpr auto high_min = 0xC0000000; 595 | static constexpr auto high_max = 0xC0001FFF; 596 | 597 | union 598 | { 599 | uint8_t buffer[0x1000]; 600 | 601 | struct 602 | { 603 | uint8_t read_low [0x400]; 604 | uint8_t read_high [0x400]; 605 | uint8_t write_low [0x400]; 606 | uint8_t write_high[0x400]; 607 | }; 608 | }; 609 | }; 610 | 611 | struct io_bitmap_t 612 | { 613 | static constexpr auto a_min = 0x00000000; 614 | static constexpr auto a_max = 0x00007FFF; 615 | static constexpr auto b_min = 0x00008000; 616 | static constexpr auto b_max = 0x0000FFFF; 617 | uint8_t io_a[0x1000]; 618 | uint8_t io_b[0x1000]; 619 | }; 620 | 621 | struct vm_interrupt_info_t 622 | { 623 | union 624 | { 625 | uint32_t flags; 626 | struct 627 | { 628 | uint32_t vector : 8; 629 | uint32_t type : 3; 630 | uint32_t code : 1; 631 | uint32_t nmi_unblocking : 1; 632 | uint32_t reserved : 18; 633 | uint32_t valid : 1; 634 | }; 635 | }; 636 | }; 637 | 638 | /// INVEPT descriptor. 639 | /// 640 | struct invept_desc_t 641 | { 642 | uint64_t eptp; 643 | uint64_t reserved; 644 | }; 645 | static_assert(sizeof(invept_desc_t) == 16); 646 | 647 | /// INVVPID descriptor. 648 | /// 649 | struct invvpid_desc_t 650 | { 651 | uint64_t vpid : 16; 652 | uint64_t reserved : 48; 653 | uint64_t linear_address; 654 | }; 655 | static_assert(sizeof(invvpid_desc_t) == 16); 656 | 657 | void invept(vmx::invept_t type, uint64_t eptp = 0); 658 | void invvpid(vmx::invvpid_t type, uint64_t vpid = 0, uint64_t address = 0); 659 | }; 660 | -------------------------------------------------------------------------------- /src/heye/asm/registers.asm: -------------------------------------------------------------------------------- 1 | .code 2 | 3 | PUBLIC asm_read_dr0 4 | PUBLIC asm_read_dr1 5 | PUBLIC asm_read_dr2 6 | PUBLIC asm_read_dr3 7 | PUBLIC asm_read_dr6 8 | PUBLIC asm_read_dr7 9 | 10 | PUBLIC asm_read_rflags 11 | 12 | PUBLIC asm_read_gdtr 13 | PUBLIC asm_read_idtr 14 | PUBLIC asm_read_ldtr 15 | PUBLIC asm_read_tr 16 | 17 | PUBLIC asm_read_cs 18 | PUBLIC asm_read_ds 19 | PUBLIC asm_read_es 20 | PUBLIC asm_read_ss 21 | PUBLIC asm_read_fs 22 | PUBLIC asm_read_gs 23 | 24 | PUBLIC asm_write_gdtr 25 | PUBLIC asm_write_idtr 26 | 27 | asm_read_dr0 proc 28 | mov rax, dr0 29 | ret 30 | asm_read_dr0 endp 31 | 32 | asm_read_dr1 proc 33 | mov rax, dr1 34 | ret 35 | asm_read_dr1 endp 36 | 37 | asm_read_dr2 proc 38 | mov rax, dr2 39 | ret 40 | asm_read_dr2 endp 41 | 42 | asm_read_dr3 proc 43 | mov rax, dr3 44 | ret 45 | asm_read_dr3 endp 46 | 47 | asm_read_dr6 proc 48 | mov rax, dr6 49 | ret 50 | asm_read_dr6 endp 51 | 52 | asm_read_dr7 proc 53 | mov rax, dr7 54 | ret 55 | asm_read_dr7 endp 56 | 57 | asm_read_rflags proc 58 | pushfq 59 | pop rax 60 | ret 61 | asm_read_rflags endp 62 | 63 | asm_read_gdtr proc 64 | sgdt fword ptr [rcx] 65 | ret 66 | asm_read_gdtr endp 67 | 68 | asm_read_idtr proc 69 | sidt fword ptr [rcx] 70 | ret 71 | asm_read_idtr endp 72 | 73 | asm_read_ldtr proc 74 | sldt ax 75 | ret 76 | asm_read_ldtr endp 77 | 78 | asm_read_tr proc 79 | str ax 80 | ret 81 | asm_read_tr endp 82 | 83 | asm_read_cs proc 84 | mov ax, cs 85 | ret 86 | asm_read_cs endp 87 | 88 | asm_read_ds proc 89 | mov ax, ds 90 | ret 91 | asm_read_ds endp 92 | 93 | asm_read_es proc 94 | mov ax, es 95 | ret 96 | asm_read_es endp 97 | 98 | asm_read_ss proc 99 | mov ax, ss 100 | ret 101 | asm_read_ss endp 102 | 103 | asm_read_fs proc 104 | mov ax, fs 105 | ret 106 | asm_read_fs endp 107 | 108 | asm_read_gs proc 109 | mov ax, gs 110 | ret 111 | asm_read_gs endp 112 | 113 | asm_lar proc 114 | lar rax, rcx 115 | ret 116 | asm_lar endp 117 | 118 | asm_write_gdtr proc 119 | lgdt fword ptr [rcx] 120 | ret 121 | asm_write_gdtr endp 122 | 123 | asm_write_idtr proc 124 | lidt fword ptr [rcx] 125 | ret 126 | asm_write_idtr endp 127 | 128 | end 129 | -------------------------------------------------------------------------------- /src/heye/asm/vmm.asm: -------------------------------------------------------------------------------- 1 | .code 2 | 3 | VMX_ERROR_CODE_SUCCESS = 0 4 | VMX_ERROR_CODE_FAILED_WITH_STATUS = 1 5 | VMX_ERROR_CODE_FAILED = 2 6 | 7 | VMCS_GUEST_RSP = 0681Ch 8 | VMCS_GUEST_RIP = 0681Eh 9 | VMCS_GUEST_RFLAGS = 06820h 10 | KGDT64_R3_DATA = 00028h 11 | KGDT64_R3_CODE = 00030h 12 | RPL_MASK = 00003h 13 | SHADOW_SPACE_SIZE = 00020h 14 | 15 | ; Defined in cpu.hpp. 16 | regs_t struct 17 | ; General-purpose registers. 18 | $rax qword ? 19 | $rcx qword ? 20 | $rbx qword ? 21 | $rdx qword ? 22 | $rsp qword ? 23 | $rbp qword ? 24 | $rsi qword ? 25 | $rdi qword ? 26 | $r8 qword ? 27 | $r9 qword ? 28 | $r10 qword ? 29 | $r11 qword ? 30 | $r12 qword ? 31 | $r13 qword ? 32 | $r14 qword ? 33 | $r15 qword ? 34 | $rip qword ? 35 | $rflags qword ? 36 | ; SSE registers. 37 | $xmm0 oword ? 38 | $xmm1 oword ? 39 | $xmm2 oword ? 40 | $xmm3 oword ? 41 | $xmm4 oword ? 42 | $xmm5 oword ? 43 | $xmm6 oword ? 44 | $xmm7 oword ? 45 | $xmm8 oword ? 46 | $xmm9 oword ? 47 | $xmm10 oword ? 48 | $xmm11 oword ? 49 | $xmm12 oword ? 50 | $xmm13 oword ? 51 | $xmm14 oword ? 52 | $xmm15 oword ? 53 | regs_t ends 54 | 55 | ; Defined in vcpu.hpp 56 | frame_t struct 57 | $rip qword ? 58 | $cs qword ? 59 | $rflags qword ? 60 | $rsp qword ? 61 | $ss qword ? 62 | frame_t ends 63 | 64 | vmexit_stub proc frame 65 | ; Create trap frame for stack traces. 66 | sub rsp, 8 + sizeof frame_t 67 | push rax 68 | mov frame_t.$ss[rsp + 8], KGDT64_R3_DATA or RPL_MASK 69 | mov rax, VMCS_GUEST_RSP 70 | vmread frame_t.$rsp[rsp + 8], rax 71 | mov rax, VMCS_GUEST_RFLAGS 72 | vmread frame_t.$rflags[rsp + 8], rax 73 | mov frame_t.$cs[rsp + 8], KGDT64_R3_CODE or RPL_MASK 74 | mov rax, VMCS_GUEST_RIP 75 | vmread frame_t.$rip[rsp + 8], rax 76 | pop rax 77 | .pushframe 78 | 79 | sub rsp, sizeof regs_t 80 | .allocstack sizeof regs_t + SHADOW_SPACE_SIZE 81 | .endprolog 82 | 83 | ; General-purpose registers. 84 | mov regs_t.$rax[rsp], rax 85 | mov regs_t.$rcx[rsp], rcx 86 | mov regs_t.$rbx[rsp], rbx 87 | mov regs_t.$rdx[rsp], rdx 88 | mov regs_t.$rsp[rsp], rsp 89 | mov regs_t.$rbp[rsp], rbp 90 | mov regs_t.$rsi[rsp], rsi 91 | mov regs_t.$rdi[rsp], rdi 92 | mov regs_t.$r8[rsp], r8 93 | mov regs_t.$r9[rsp], r9 94 | mov regs_t.$r10[rsp], r10 95 | mov regs_t.$r11[rsp], r11 96 | mov regs_t.$r12[rsp], r12 97 | mov regs_t.$r13[rsp], r13 98 | mov regs_t.$r14[rsp], r14 99 | mov regs_t.$r15[rsp], r15 100 | 101 | mov rax, VMCS_GUEST_RSP 102 | vmread regs_t.$rsp[rsp], rax 103 | mov rax, VMCS_GUEST_RIP 104 | vmread regs_t.$rip[rsp], rax 105 | mov rax, VMCS_GUEST_RFLAGS 106 | vmread regs_t.$rflags[rsp], rax 107 | ; SSE registers. 108 | movaps regs_t.$xmm0[rsp], xmm0 109 | movaps regs_t.$xmm1[rsp], xmm1 110 | movaps regs_t.$xmm2[rsp], xmm2 111 | movaps regs_t.$xmm3[rsp], xmm3 112 | movaps regs_t.$xmm4[rsp], xmm4 113 | movaps regs_t.$xmm5[rsp], xmm5 114 | movaps regs_t.$xmm6[rsp], xmm6 115 | movaps regs_t.$xmm7[rsp], xmm7 116 | movaps regs_t.$xmm8[rsp], xmm8 117 | movaps regs_t.$xmm9[rsp], xmm9 118 | movaps regs_t.$xmm10[rsp], xmm10 119 | movaps regs_t.$xmm11[rsp], xmm11 120 | movaps regs_t.$xmm12[rsp], xmm12 121 | movaps regs_t.$xmm13[rsp], xmm13 122 | movaps regs_t.$xmm14[rsp], xmm14 123 | movaps regs_t.$xmm15[rsp], xmm15 124 | ; Make shadow space. 125 | sub rsp, SHADOW_SPACE_SIZE 126 | ; Load vcpu pointer. 127 | mov rcx, [rsp + 30h + sizeof frame_t + sizeof regs_t] 128 | ; Call vmexit handler. 129 | call qword ptr [rsp + 28h + sizeof frame_t + sizeof regs_t] 130 | 131 | add rsp, SHADOW_SPACE_SIZE 132 | 133 | cmp al, 1 134 | je vmxoff_stub 135 | 136 | vmresume 137 | jmp @error 138 | 139 | vmxoff_stub: 140 | movaps xmm0, regs_t.$xmm0[rsp] 141 | movaps xmm1, regs_t.$xmm1[rsp] 142 | movaps xmm2, regs_t.$xmm2[rsp] 143 | movaps xmm3, regs_t.$xmm3[rsp] 144 | movaps xmm4, regs_t.$xmm4[rsp] 145 | movaps xmm5, regs_t.$xmm5[rsp] 146 | movaps xmm6, regs_t.$xmm6[rsp] 147 | movaps xmm7, regs_t.$xmm7[rsp] 148 | movaps xmm8, regs_t.$xmm8[rsp] 149 | movaps xmm9, regs_t.$xmm9[rsp] 150 | movaps xmm10, regs_t.$xmm10[rsp] 151 | movaps xmm11, regs_t.$xmm11[rsp] 152 | movaps xmm12, regs_t.$xmm12[rsp] 153 | movaps xmm13, regs_t.$xmm13[rsp] 154 | movaps xmm14, regs_t.$xmm14[rsp] 155 | movaps xmm15, regs_t.$xmm15[rsp] 156 | 157 | mov rax, regs_t.$rax[rsp] 158 | mov rcx, regs_t.$rcx[rsp] 159 | mov rdx, regs_t.$rdx[rsp] 160 | mov rbx, regs_t.$rbx[rsp] 161 | mov rbp, regs_t.$rbp[rsp] 162 | mov rsi, regs_t.$rsi[rsp] 163 | mov rdi, regs_t.$rdi[rsp] 164 | mov r8, regs_t.$r8[rsp] 165 | mov r9, regs_t.$r9[rsp] 166 | mov r10, regs_t.$r10[rsp] 167 | mov r11, regs_t.$r11[rsp] 168 | mov r12, regs_t.$r12[rsp] 169 | mov r13, regs_t.$r13[rsp] 170 | mov r14, regs_t.$r14[rsp] 171 | mov r15, regs_t.$r15[rsp] 172 | mov rcx, regs_t.$rip[rsp] 173 | mov rdx, regs_t.$rsp[rsp] 174 | 175 | add rsp, sizeof regs_t 176 | 177 | vmxoff 178 | jz @error 179 | jc @error 180 | 181 | mov rsp, rdx 182 | jmp rcx 183 | 184 | @error: 185 | int 3 186 | vmexit_stub endp 187 | 188 | asm_vmlaunch proc 189 | mov rcx, VMCS_GUEST_RSP 190 | vmwrite rcx, rsp ; Set guest stack to the current stack. 191 | jz @jz 192 | jc @jc 193 | 194 | mov rcx, VMCS_GUEST_RIP 195 | lea rdx, done 196 | vmwrite rcx, rdx ; Set guest rip to the `done` label. 197 | jz @jz 198 | jc @jc 199 | 200 | pushfq 201 | pop rdx 202 | mov rcx, VMCS_GUEST_RFLAGS 203 | vmwrite rcx, rdx ; Set guest rflags. 204 | jz @jz 205 | jc @jc 206 | 207 | vmlaunch ; Following __vmx_vmlaunch abi. 208 | jz @jz 209 | jc @jc 210 | 211 | done: 212 | xor rax, rax 213 | ret 214 | @jz: 215 | mov rax, VMX_ERROR_CODE_FAILED_WITH_STATUS 216 | ret 217 | @jc: 218 | mov rax, VMX_ERROR_CODE_FAILED 219 | ret 220 | asm_vmlaunch endp 221 | 222 | asm_invept proc 223 | invept rcx, oword ptr [rdx] 224 | jz @jz 225 | jc @jc 226 | xor rax, rax 227 | ret 228 | 229 | @jz: 230 | mov rax, VMX_ERROR_CODE_FAILED_WITH_STATUS 231 | ret 232 | 233 | @jc: 234 | mov rax, VMX_ERROR_CODE_FAILED 235 | ret 236 | asm_invept endp 237 | 238 | asm_invvpid proc 239 | invvpid rcx, oword ptr [rdx] 240 | jz @jz 241 | jc @jc 242 | xor rax, rax 243 | ret 244 | @jz: 245 | mov rax, VMX_ERROR_CODE_FAILED_WITH_STATUS 246 | ret 247 | @jc: 248 | mov rax, VMX_ERROR_CODE_FAILED 249 | ret 250 | asm_invvpid endp 251 | 252 | asm_vmcall proc 253 | vmcall 254 | ret 255 | asm_vmcall endp 256 | end 257 | -------------------------------------------------------------------------------- /src/heye/config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | static constexpr auto max_cpu_count = 256; 4 | static constexpr auto pool_tag = 'heye'; 5 | static constexpr auto page_size = 0x1000; 6 | static constexpr auto page_shift = 12; 7 | static constexpr auto kernel_stack_size = 6 * page_size; 8 | -------------------------------------------------------------------------------- /src/heye/hv/callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace heye 4 | { 5 | struct vcpu_t; 6 | 7 | using setup_cb_t = void(*)(vcpu_t*); 8 | using teardown_cb_t = void(*)(vcpu_t*); 9 | using vmexit_cb_t = bool(*)(vcpu_t*); 10 | }; 11 | -------------------------------------------------------------------------------- /src/heye/hv/ept.cpp: -------------------------------------------------------------------------------- 1 | #include "heye/hv/ept.hpp" 2 | #include "heye/arch/arch.hpp" 3 | #include "heye/shared/trace.hpp" 4 | 5 | namespace heye 6 | { 7 | ept_t::ept_t() 8 | { 9 | // Allocate page table. 10 | // 11 | page_table = new page_table_t; 12 | 13 | const auto mtrr = mtrr_descriptor(); 14 | const auto cap = read(); 15 | // Setup EPT pointer. 16 | // 17 | ept.access_flags = cap.ept_access_dirty; 18 | ept.page_walk_length = page_walk_4; 19 | ept.memory_type = cap.memory_type_wb ? memory_type_t::write_back : memory_type_t::uncachable; 20 | ept.pml4_address = pfn(pa_from_va(page_table->pml4)); 21 | // Setup PML4 entries. 22 | // 23 | page_table->pml4[0].read = true; 24 | page_table->pml4[0].write = true; 25 | page_table->pml4[0].execute = true; 26 | page_table->pml4[0].pfn = pfn(pa_from_va(page_table->pdpt)); 27 | // Setup PDPT entries. 28 | // 29 | for (int i = 0; i < std::countof(page_table->pdpt); i++) 30 | { 31 | page_table->pdpt[i].read = true; 32 | page_table->pdpt[i].write = true; 33 | page_table->pdpt[i].execute = true; 34 | page_table->pdpt[i].pfn = pfn(pa_from_va(&page_table->pd[i][0])); 35 | } 36 | // Setup PD and PTE entries. 37 | // 38 | for (int i = 0; i < std::countof(page_table->pd); i++) 39 | { 40 | for (int j = 0; j < std::countof(page_table->pd); j++) 41 | { 42 | const auto pfn = (i * 512) + j; 43 | page_table->pd[i][j].read = true; 44 | page_table->pd[i][j].write = true; 45 | page_table->pd[i][j].execute = true; 46 | page_table->pd[i][j].large_page = true; 47 | page_table->pd[i][j].pfn = pfn; 48 | page_table->pd[i][j].memory_type = mtrr.get_type_or(pfn * 2_mb, memory_type_t::write_back); 49 | } 50 | } 51 | } 52 | 53 | ept_t::~ept_t() 54 | { 55 | delete page_table; 56 | } 57 | 58 | eptp_t ept_t::ept_pointer() const 59 | { 60 | return ept; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/heye/hv/ept.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "heye/arch/memory.hpp" 3 | #include "heye/arch/mtrr.hpp" 4 | #include "heye/arch/paging.hpp" 5 | 6 | namespace heye 7 | { 8 | static constexpr auto pt_enties = 512; 9 | static constexpr auto page_walk_4 = 3; 10 | 11 | struct page_table_t 12 | { 13 | pml4_t pml4[pt_enties]; 14 | pdpt_t pdpt[pt_enties]; 15 | pd_2mb_t pd [pt_enties][pt_enties]; 16 | }; 17 | 18 | struct ept_t final 19 | { 20 | ept_t (); 21 | ~ept_t(); 22 | 23 | eptp_t ept_pointer() const; 24 | 25 | private: 26 | eptp_t ept{}; 27 | page_table_t* page_table; 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /src/heye/hv/hypervisor.cpp: -------------------------------------------------------------------------------- 1 | #include "heye/hv/hypervisor.hpp" 2 | #include "heye/hv/vmx.hpp" 3 | 4 | #include "heye/shared/trace.hpp" 5 | #include "heye/shared/cpu.hpp" 6 | #include "heye/arch/arch.hpp" 7 | 8 | namespace heye 9 | { 10 | hv_t::hv_t(setup_cb_t setup, teardown_cb_t teardown, vmexit_cb_t vmexit) : state(state_t::off), kernel_page_table(read()) 11 | { 12 | // Although all `new` allocations come from `ExAllocatePoolZero`, we zero initialize vcpu array. 13 | // 14 | __stosb(reinterpret_cast(vcpu), 0, sizeof(vcpu)); 15 | // Allocate and initialize vcpu. 16 | // 17 | for (size_t core = 0; core < cpu::count(); core++) 18 | { 19 | vcpu[core] = new vcpu_t(this, setup, teardown, vmexit); 20 | } 21 | // Allocate and initialize ept. 22 | // 23 | ept = new ept_t; 24 | } 25 | 26 | hv_t::~hv_t() 27 | { 28 | stop(); 29 | // Delete all vcpu instances. 30 | // 31 | for (auto core : vcpu) 32 | { 33 | if (core != nullptr) 34 | { 35 | delete core; 36 | } 37 | } 38 | delete ept; 39 | } 40 | 41 | bool hv_t::supported() 42 | { 43 | const auto features = read(); 44 | const auto controls = read(); 45 | const auto ept_vpid = read(); 46 | const auto mtrr_type = read(); 47 | 48 | return features.vmx 49 | && controls.vmxon 50 | && ept_vpid.page_walk_length_4 51 | && ept_vpid.memory_type_wb 52 | && ept_vpid.invept 53 | && ept_vpid.invept_single_context 54 | && ept_vpid.invept_all_contexts 55 | && ept_vpid.invvpid 56 | && ept_vpid.invvpid_indiv_addr 57 | && ept_vpid.invvpid_single_context 58 | && ept_vpid.invvpid_single_context_retain_globals 59 | && ept_vpid.invvpid_all_contexts 60 | && mtrr_type.mtrr_enable; 61 | } 62 | 63 | bool hv_t::start() 64 | { 65 | if (is_running() || !supported()) 66 | return false; 67 | 68 | volatile bool failed_start{ false }; 69 | // Enter VMM on all cores. This function runs at IPI_LEVEL. 70 | // 71 | cpu::for_each([&, this](uint64_t cpu_number) 72 | { 73 | auto current_vcpu = vcpu[cpu_number]; 74 | if (current_vcpu != nullptr && current_vcpu->is_off()) 75 | { 76 | if (!current_vcpu->start()) 77 | { 78 | logger::info("Failed to virtualize %ld core", cpu_number); 79 | failed_start = true; 80 | } 81 | } 82 | else 83 | { 84 | failed_start = true; 85 | } 86 | }); 87 | 88 | _mm_mfence(); 89 | 90 | if (failed_start) 91 | { 92 | logger::info("Failed to start hypervisor"); 93 | stop(); 94 | return false; 95 | } 96 | 97 | state = state_t::on; 98 | return true; 99 | } 100 | 101 | void hv_t::stop() 102 | { 103 | // Leave vmm on all cores. 104 | // 105 | cpu::for_each([this](uint64_t cpu_number) 106 | { 107 | if (vcpu[cpu_number] != nullptr) 108 | { 109 | vcpu[cpu_number]->stop(); 110 | } 111 | }); 112 | // Mark state as off. 113 | // 114 | state = state_t::off; 115 | } 116 | 117 | bool hv_t::is_running() const 118 | { 119 | return state == state_t::on; 120 | } 121 | 122 | cr3_t hv_t::system_process_pagetable() const 123 | { 124 | return kernel_page_table; 125 | } 126 | }; 127 | -------------------------------------------------------------------------------- /src/heye/hv/hypervisor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ept.hpp" 4 | #include "vcpu.hpp" 5 | #include "vmexit.hpp" 6 | 7 | #include "heye/arch/cr.hpp" 8 | 9 | namespace heye 10 | { 11 | struct hv_t 12 | { 13 | hv_t (setup_cb_t setup, teardown_cb_t teardown, vmexit_cb_t vmexit); 14 | ~hv_t(); 15 | 16 | bool start(); 17 | void stop(); 18 | 19 | bool is_running() const; 20 | 21 | /// Get system process cr3 value. 22 | /// 23 | cr3_t system_process_pagetable() const; 24 | 25 | /// Check for vmx support. 26 | /// 27 | static bool supported(); 28 | 29 | /// Virtual machines per core. 30 | /// 31 | vcpu_t* vcpu[max_cpu_count]; 32 | 33 | /// Global EPT pointer used by all vcpus. 34 | /// 35 | ept_t* ept; 36 | 37 | private: 38 | /// Hypervisor running state. 39 | /// 40 | state_t state; 41 | 42 | /// CR3 value of the system process. 43 | /// Used as `host cr3` value in vmcs. Initialized during class construction. 44 | /// 45 | cr3_t kernel_page_table; 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /src/heye/hv/vcpu.cpp: -------------------------------------------------------------------------------- 1 | #include "hypervisor.hpp" 2 | #include "vcpu.hpp" 3 | #include "vmx.hpp" 4 | #include "vmexit.hpp" 5 | #include "heye/shared/trace.hpp" 6 | 7 | namespace heye 8 | { 9 | vcpu_t::vcpu_t(hv_t* owner, setup_cb_t setup_cb, teardown_cb_t teardown_cb, vmexit_cb_t vmexit_cb) 10 | : hv(owner), state(state_t::off), setup_cb(setup_cb), teardown_cb(teardown_cb), vmexit_cb(vmexit_cb) 11 | { 12 | vmcs = new vmx::vmcs_t; 13 | vmxon = new vmx::vmcs_t; 14 | io_bitmap = new vmx::io_bitmap_t; 15 | msr_bitmap = new vmx::msr_bitmap_t; 16 | stack = new stack_t; 17 | 18 | __stosb(reinterpret_cast(vmcs), 0, sizeof(vmx::vmcs_t)); 19 | __stosb(reinterpret_cast(vmxon), 0, sizeof(vmx::vmcs_t)); 20 | __stosb(reinterpret_cast(io_bitmap), 0, sizeof(vmx::io_bitmap_t)); 21 | __stosb(reinterpret_cast(msr_bitmap), 0, sizeof(vmx::msr_bitmap_t)); 22 | __stosb(reinterpret_cast(stack), 0, sizeof(stack_t)); 23 | } 24 | 25 | vcpu_t::~vcpu_t() 26 | { 27 | if (!is_off()) 28 | stop(); 29 | 30 | delete vmcs; 31 | delete vmxon; 32 | delete io_bitmap; 33 | delete msr_bitmap; 34 | delete[] stack; 35 | } 36 | 37 | bool vcpu_t::start() 38 | { 39 | if (!is_off()) 40 | return false; 41 | 42 | auto cr0 = read(); 43 | auto cr4 = read(); 44 | // Make sure CR0 meets CR0 fixed bits in VMX operation. 45 | // 46 | auto cr0_fixed0 = read(); 47 | auto cr0_fixed1 = read(); 48 | if ((~cr0.flags & cr0_fixed0.flags) || (~cr0.flags & cr0_fixed1.flags)) 49 | { 50 | logger::info("Host CR0 is not allowed in VMX operation"); 51 | return false; 52 | } 53 | // Check VMX support. 54 | // 55 | auto controls = read(); 56 | if (controls.lock) 57 | { 58 | if (!controls.vmxon) 59 | { 60 | logger::info("VMX disabled in BIOS"); 61 | return false; 62 | } 63 | } 64 | else 65 | { 66 | controls.lock = 1; 67 | controls.vmxon = 1; 68 | write(controls); 69 | } 70 | // Enable vmx. 71 | // 72 | cr4.vmxe = true; 73 | // Adjust CR0 and CR4 registers. 74 | // 75 | write(vmx::adjust(cr0)); 76 | write(vmx::adjust(cr4)); 77 | // Setup vmxon/vmcs. 78 | // 79 | vmxon->revision_id = read().vmcs_id; 80 | vmcs->revision_id = read().vmcs_id; 81 | // Enter vmx root opration. 82 | // 83 | if (vmx::on(pa_from_va(vmxon))) 84 | { 85 | logger::info("__vmxon failed"); 86 | return false; 87 | } 88 | // Mark as `init`. 89 | // 90 | state = state_t::init; 91 | 92 | invvpid(vmx::invvpid_t::all_contexts); 93 | invept(vmx::invept_t::all_contexts); 94 | 95 | if (vmx::clear(pa_from_va(vmcs)) || vmx::vmptrld(pa_from_va(vmcs))) 96 | { 97 | logger::info("__vmx_clear || __vmx_vmptrld failed"); 98 | return false; 99 | } 100 | 101 | if (!setup_guest() || !setup_host() || !setup_controls()) 102 | { 103 | logger::info("Failed to setup vmcs"); 104 | return false; 105 | } 106 | 107 | // Pass control to the user defined callback. 108 | // 109 | setup_cb(this); 110 | 111 | if (vmx::launch()) 112 | { 113 | logger::info("Failed to launch with code: 0x%lx", read()); 114 | } 115 | else 116 | { 117 | logger::info("running in vmx non-root"); 118 | state = state_t::on; 119 | } 120 | return is_on(); 121 | } 122 | 123 | void vcpu_t::stop() 124 | { 125 | logger::info("Leaving vmx root operation"); 126 | 127 | if (is_on()) 128 | { 129 | vmx::vmcall(vmcall_reason::vmxoff); 130 | } 131 | else if (is_init()) 132 | { 133 | vmx::off(); 134 | } 135 | // Disable vmx. 136 | // 137 | auto cr4 = read(); 138 | cr4.vmxe = false; 139 | write(cr4); 140 | // Zero out per-processor structs since we might re-enter vm later. 141 | // 142 | __stosb(reinterpret_cast(vmcs), 0, sizeof(vmx::vmcs_t)); 143 | __stosb(reinterpret_cast(vmxon), 0, sizeof(vmx::vmcs_t)); 144 | __stosb(reinterpret_cast(io_bitmap), 0, sizeof(vmx::io_bitmap_t)); 145 | __stosb(reinterpret_cast(msr_bitmap), 0, sizeof(vmx::msr_bitmap_t)); 146 | __stosb(reinterpret_cast(stack), 0, sizeof(stack_t)); 147 | // Mark state as `off`. 148 | // 149 | state = state_t::off; 150 | /// Pass control to the user defined callback. 151 | /// 152 | teardown_cb(this); 153 | } 154 | 155 | bool vcpu_t::setup_host() 156 | { 157 | uint64_t err{}; 158 | 159 | err |= write(read>().selector.flags & 0xf8); 160 | err |= write(read>().selector.flags & 0xf8); 161 | err |= write(read>().selector.flags & 0xf8); 162 | err |= write(read>().selector.flags & 0xf8); 163 | err |= write(read>().selector.flags & 0xf8); 164 | err |= write(read>().selector.flags & 0xf8); 165 | err |= write(read>().selector.flags & 0xf8); 166 | err |= write(read().flags); 167 | // Host cr3 is taken from the hypervisor intance (hypervisor should be initialized on driver load). 168 | // 169 | err |= write(hv->system_process_pagetable().flags); 170 | err |= write(read().flags); 171 | err |= write(read().flags); 172 | 173 | err |= write( read>().base); 174 | err |= write( read>().base); 175 | err |= write( read>().base); 176 | err |= write(read().base); 177 | err |= write(read().base); 178 | 179 | err |= write(read().flags); 180 | err |= write(read().flags); 181 | // Host rip points to vmexit stub. 182 | // 183 | err |= write(reinterpret_cast(vmexit_stub)); 184 | // Host rsp points to the top of allocated stack. 185 | // 186 | // (Low) | 187 | // +-------------------+ <- 0x1000 (stack base) 188 | // | | 189 | // +-------------------+ 190 | // | cpu regs | 191 | // +-------------------+ 192 | // | trap frame | 193 | // +-------------------+ <- host rsp 194 | // | vmexit callback | 195 | // +-------------------+ 196 | // | vcpu* | 197 | // +-------------------+ <- 0x2000 (stack base + stack size) 198 | // (High) | 199 | // 200 | stack->vmexit_handler = vmexit_cb; 201 | stack->vcpu = this; 202 | err |= write(reinterpret_cast(vmexit_stub)); 203 | err |= write(reinterpret_cast(&stack->vmexit_handler)); 204 | 205 | return err == 0; 206 | } 207 | 208 | bool vcpu_t::setup_controls() 209 | { 210 | uint64_t err{}; 211 | 212 | err |= write(1); 213 | 214 | err |= write(vmx::adjust(msr::vmx_pinbased_controls{}).flags); 215 | 216 | msr::vmx_procbased_controls procbased_controls 217 | { 218 | .use_msr_bitmaps = true, 219 | .use_secondary_controls = true, 220 | }; 221 | err |= write(vmx::adjust(procbased_controls).flags); 222 | 223 | msr::vmx_procbased_controls2 procbased_controls2 224 | { 225 | .enable_ept = true, 226 | .enable_rdtcp = true, 227 | .enable_vpid = true, 228 | .enable_invpcid = true, 229 | .enable_xsaves = true, 230 | }; 231 | err |= write(vmx::adjust(procbased_controls2).flags); 232 | 233 | msr::vmx_exit_controls exit_controls 234 | { 235 | .host_address_space_size = true 236 | }; 237 | err |= write(vmx::adjust(exit_controls).flags); 238 | 239 | msr::vmx_entry_controls entry_controls 240 | { 241 | .ia32_mode_guest = true 242 | }; 243 | err |= write(vmx::adjust(entry_controls).flags); 244 | 245 | err |= write( pa_from_va(msr_bitmap)); 246 | err |= write(hv->ept->ept_pointer().flags); 247 | err |= write(~0ull); 248 | 249 | return err == 0; 250 | } 251 | 252 | bool vcpu_t::setup_guest() 253 | { 254 | const auto gdtr = read(); 255 | const auto idtr = read(); 256 | 257 | const auto es = read>(); 258 | const auto cs = read>(); 259 | const auto ss = read>(); 260 | const auto ds = read>(); 261 | const auto fs = read>(); 262 | const auto gs = read>(); 263 | const auto tr = read>(); 264 | const auto ldtr = read>(); 265 | 266 | cr0_t cr0_mask{}; 267 | cr3_t cr3_mask{}; 268 | cr4_t cr4_mask{}; 269 | 270 | uint64_t err{}; 271 | 272 | err |= write( es.selector.flags); 273 | err |= write( cs.selector.flags); 274 | err |= write( ss.selector.flags); 275 | err |= write( ds.selector.flags); 276 | err |= write( fs.selector.flags); 277 | err |= write( gs.selector.flags); 278 | err |= write( tr.selector.flags); 279 | err |= write(ldtr.selector.flags); 280 | err |= write(read().flags); 281 | 282 | err |= write( es.limit); 283 | err |= write( cs.limit); 284 | err |= write( ss.limit); 285 | err |= write( ds.limit); 286 | err |= write( fs.limit); 287 | err |= write( gs.limit); 288 | err |= write( tr.limit); 289 | err |= write(ldtr.limit); 290 | err |= write(gdtr.limit); 291 | err |= write(idtr.limit); 292 | 293 | err |= write( es.rights.flags); 294 | err |= write( cs.rights.flags); 295 | err |= write( ss.rights.flags); 296 | err |= write( ds.rights.flags); 297 | err |= write( fs.rights.flags); 298 | err |= write( gs.rights.flags); 299 | err |= write( tr.rights.flags); 300 | err |= write(ldtr.rights.flags); 301 | 302 | err |= write(read().flags); 303 | 304 | err |= write(cr0_mask.flags); 305 | err |= write(cr4_mask.flags); 306 | 307 | err |= write(read().flags); 308 | err |= write(read().flags); 309 | 310 | err |= write(read().flags); 311 | err |= write(read().flags); 312 | err |= write(read().flags); 313 | 314 | err |= write( es.base); 315 | err |= write( cs.base); 316 | err |= write( ss.base); 317 | err |= write( ds.base); 318 | err |= write( fs.base); 319 | err |= write( gs.base); 320 | err |= write( tr.base); 321 | err |= write(ldtr.base); 322 | err |= write(gdtr.base); 323 | err |= write(idtr.base); 324 | 325 | err |= write(read().flags); 326 | 327 | err |= write(read().flags); 328 | err |= write(read().flags); 329 | 330 | return err == 0; 331 | } 332 | 333 | void vcpu_t::skip_instruction() 334 | { 335 | write(regs().rip + read()); 336 | } 337 | 338 | uint64_t vcpu_t::id() const 339 | { 340 | return read(); 341 | } 342 | 343 | cpu::regs_t& vcpu_t::regs() 344 | { 345 | return stack->regs; 346 | } 347 | 348 | vmx::exit_reason vcpu_t::exit_reason() const 349 | { 350 | return static_cast(read() & 0xffff); 351 | } 352 | 353 | vmx::exit_qualification_t vcpu_t::exit_qualification() const 354 | { 355 | vmx::exit_qualification_t qualification{ read() }; 356 | return qualification; 357 | } 358 | 359 | vmx::vm_interrupt_info_t vcpu_t::exit_interrupt_info() const 360 | { 361 | vmx::vm_interrupt_info_t intr{ read() & 0xffffffff }; 362 | return intr; 363 | } 364 | 365 | vmx::vm_interrupt_info_t vcpu_t::entry_interrupt_info() const 366 | { 367 | vmx::vm_interrupt_info_t intr{ read() & 0xffffffff }; 368 | return intr; 369 | } 370 | }; 371 | -------------------------------------------------------------------------------- /src/heye/hv/vcpu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "vmx.hpp" 3 | #include "callbacks.hpp" 4 | #include "heye/config.hpp" 5 | #include "heye/arch/arch.hpp" 6 | 7 | #include "heye/shared/cpu.hpp" 8 | 9 | namespace heye 10 | { 11 | struct hv_t; 12 | struct ept_t; 13 | struct vcpu_t; 14 | 15 | struct stack_t 16 | { 17 | struct frame_t 18 | { 19 | uint64_t rip; 20 | uint64_t cs; 21 | uint64_t rflags; 22 | uint64_t rsp; 23 | uint64_t ss; 24 | }; 25 | 26 | uint8_t data[kernel_stack_size 27 | - sizeof(cpu::regs_t) 28 | - sizeof(frame_t) 29 | - sizeof(uint64_t) * 3]; 30 | cpu::regs_t regs; 31 | frame_t frame; 32 | uint64_t unused; 33 | vmexit_cb_t vmexit_handler; 34 | vcpu_t* vcpu; 35 | }; 36 | static_assert(sizeof(stack_t) == kernel_stack_size); 37 | 38 | enum class state_t : uint32_t 39 | { 40 | /// Hypervisor is active. 41 | /// 42 | on, 43 | /// Hypervisor is disabled. 44 | /// 45 | off, 46 | /// Hypervisor is initializing. 47 | /// 48 | init 49 | }; 50 | 51 | /// Virtual machine context. 52 | /// 53 | struct vcpu_t 54 | { 55 | vcpu_t(hv_t* owner, setup_cb_t setup, teardown_cb_t teardown, vmexit_cb_t vmexit); 56 | ~vcpu_t(); 57 | 58 | /// Enter vmx non root. 59 | /// 60 | bool start(); 61 | 62 | /// Leave vmx non root. 63 | /// 64 | void stop(); 65 | 66 | bool is_on() const { return state == state_t::on; } 67 | bool is_off() const { return state == state_t::off; } 68 | bool is_init() const { return state == state_t::init; } 69 | 70 | void skip_instruction(); 71 | 72 | uint64_t id() const; 73 | 74 | cpu::regs_t& regs(); 75 | 76 | vmx::exit_reason exit_reason() const; 77 | vmx::exit_qualification_t exit_qualification() const; 78 | vmx::vm_interrupt_info_t exit_interrupt_info() const; 79 | vmx::vm_interrupt_info_t entry_interrupt_info() const; 80 | 81 | private: 82 | bool setup_guest(); 83 | bool setup_host(); 84 | bool setup_controls(); 85 | 86 | /// Hypervisor instance that owns this vcpu. 87 | /// 88 | hv_t* hv; 89 | 90 | /// VCPU running state. 91 | /// 92 | state_t state; 93 | 94 | /// User provided callbacks. 95 | /// 96 | setup_cb_t setup_cb; 97 | teardown_cb_t teardown_cb; 98 | vmexit_cb_t vmexit_cb; 99 | 100 | /// Per core global variables. 101 | /// 102 | vmx::vmcs_t* vmcs; 103 | vmx::vmcs_t* vmxon; 104 | vmx::io_bitmap_t* io_bitmap; 105 | vmx::msr_bitmap_t* msr_bitmap; 106 | stack_t* stack; 107 | }; 108 | }; 109 | -------------------------------------------------------------------------------- /src/heye/hv/vmcall.cpp: -------------------------------------------------------------------------------- 1 | #include "vmcall.hpp" 2 | #include "vcpu.hpp" 3 | #include "vmx.hpp" 4 | 5 | #include "heye/arch/arch.hpp" 6 | 7 | #include "heye/shared/trace.hpp" 8 | #include "heye/shared/cpu.hpp" 9 | 10 | namespace heye 11 | { 12 | bool handle_vmcall(vcpu_t* vcpu) 13 | { 14 | auto reason = static_cast(vcpu->regs().rcx); 15 | 16 | switch (reason) 17 | { 18 | case vmcall_reason::ping: 19 | { 20 | logger::info("pong :)"); 21 | break; 22 | } 23 | case vmcall_reason::vmxoff: 24 | { 25 | logger::info("vmxoff called"); 26 | // Set rcx to the next instruction address and rdx to the guest stack pointer. 27 | // 28 | vcpu->regs().rip = read() + read(); 29 | // Since we will not vmresume, we must overwrite host cr3 with the guest cr3. 30 | // 31 | write (cr3_t{ read() }); 32 | write(gdtr_t{ static_cast(read() & 0xffff), read() }); 33 | write(idtr_t{ static_cast(read() & 0xffff), read() }); 34 | return true; 35 | } 36 | default: 37 | break; 38 | } 39 | return false; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /src/heye/hv/vmcall.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace heye 6 | { 7 | namespace cpu 8 | { 9 | struct regs_t; 10 | }; 11 | 12 | enum class vmcall_reason : uint64_t 13 | { 14 | /// Smoke test. 15 | /// 16 | ping = 0, 17 | /// Turn of hypervisor. 18 | /// 19 | vmxoff = 1, 20 | }; 21 | 22 | struct vcpu_t; 23 | 24 | bool handle_vmcall(vcpu_t*); 25 | }; 26 | -------------------------------------------------------------------------------- /src/heye/hv/vmexit.cpp: -------------------------------------------------------------------------------- 1 | #include "vmexit.hpp" 2 | #include "vmcall.hpp" 3 | #include "vcpu.hpp" 4 | 5 | #include "heye/shared/trace.hpp" 6 | #include "heye/shared/cpu.hpp" 7 | #include "heye/arch/arch.hpp" 8 | 9 | #include 10 | 11 | namespace heye 12 | { 13 | static void handle_exception(vcpu_t* vcpu) 14 | { 15 | auto interrupt = vcpu->exit_interrupt_info(); 16 | 17 | switch (interrupt.type) 18 | { 19 | case vmx::interrupt_t::external_interrupt: 20 | break; 21 | 22 | default: 23 | break; 24 | } 25 | vcpu->skip_instruction(); 26 | } 27 | 28 | static void handle_cpuid(vcpu_t* vcpu) 29 | { 30 | // #UD If the LOCK prefix is used. 31 | // 32 | int info[4]; 33 | __cpuidex(info, vcpu->regs().eax, vcpu->regs().ecx); 34 | vcpu->regs().rax = info[0]; 35 | vcpu->regs().rbx = info[1]; 36 | vcpu->regs().rcx = info[2]; 37 | vcpu->regs().rdx = info[3]; 38 | vcpu->skip_instruction(); 39 | } 40 | 41 | static void handle_invd(vcpu_t* vcpu) 42 | { 43 | __wbinvd(); 44 | vcpu->skip_instruction(); 45 | } 46 | 47 | static void handle_invlpg(vcpu_t* vcpu) 48 | { 49 | auto linear_address = vcpu->exit_qualification().linear_address; 50 | invvpid(vmx::invvpid_t::linear_address, vcpu->id(), linear_address); 51 | vcpu->skip_instruction(); 52 | } 53 | 54 | static void handle_msr_read(vcpu_t* vcpu) 55 | { 56 | auto id = vcpu->regs().ecx; 57 | uint64_t value{}; 58 | 59 | switch (id) 60 | { 61 | case msr::sysenter_cs::id: 62 | value = read(); 63 | break; 64 | case msr::sysenter_eip::id: 65 | value = read(); 66 | break; 67 | case msr::sysenter_esp::id: 68 | value = read(); 69 | break; 70 | case msr::fsbase::id: 71 | value = read(); 72 | break; 73 | case msr::gsbase::id: 74 | value = read(); 75 | break; 76 | case msr::debugctl::id: 77 | value = read(); 78 | break; 79 | default: 80 | // TODO: Check for valid msr. 81 | // 82 | value = __readmsr(id); 83 | break; 84 | } 85 | vcpu->regs().rax = (value >> 0) & 0xffffffff; 86 | vcpu->regs().rdx = (value >> 32) & 0xffffffff; 87 | vcpu->skip_instruction(); 88 | } 89 | 90 | static void handle_msr_write(vcpu_t* vcpu) 91 | { 92 | // TODO: Check for valid msr. 93 | // 94 | auto id = vcpu->regs().ecx; 95 | auto value = vcpu->regs().rax | vcpu->regs().rdx << 32; 96 | 97 | switch (id) 98 | { 99 | case msr::sysenter_cs::id: 100 | write(msr::sysenter_cs{ value }); 101 | break; 102 | case msr::sysenter_eip::id: 103 | write(msr::sysenter_eip{ value }); 104 | break; 105 | case msr::sysenter_esp::id: 106 | write(msr::sysenter_esp{ value }); 107 | break; 108 | case msr::fsbase::id: 109 | write(msr::fsbase{ value }); 110 | break; 111 | case msr::gsbase::id: 112 | write(msr::gsbase{ value }); 113 | break; 114 | case msr::debugctl::id: 115 | write(msr::debugctl{ value }); 116 | break; 117 | default: 118 | // TODO: Check for valid msr. 119 | // 120 | __writemsr(id, value); 121 | break; 122 | } 123 | vcpu->skip_instruction(); 124 | } 125 | 126 | static void handle_vmx_fallback(vcpu_t*) 127 | { 128 | // Inject undefined opcode. 129 | // 130 | vmx::inject_ud(); 131 | } 132 | 133 | namespace vmexit 134 | { 135 | bool passthrough(vcpu_t* vcpu) 136 | { 137 | bool terminate = false; 138 | 139 | switch (vcpu->exit_reason()) 140 | { 141 | case vmx::exit_reason::exception_nmi: 142 | { 143 | handle_exception(vcpu); 144 | break; 145 | } 146 | case vmx::exit_reason::cpuid: 147 | { 148 | handle_cpuid(vcpu); 149 | break; 150 | } 151 | case vmx::exit_reason::invd: 152 | { 153 | handle_invd(vcpu); 154 | break; 155 | } 156 | case vmx::exit_reason::invlpg: 157 | { 158 | handle_invlpg(vcpu); 159 | break; 160 | } 161 | case vmx::exit_reason::vmclear: [[fallthrough]]; 162 | case vmx::exit_reason::vmptrld: [[fallthrough]]; 163 | case vmx::exit_reason::vmptrst: [[fallthrough]]; 164 | case vmx::exit_reason::vmread: [[fallthrough]]; 165 | case vmx::exit_reason::vmresume: [[fallthrough]]; 166 | case vmx::exit_reason::vmwrite: [[fallthrough]]; 167 | case vmx::exit_reason::vmxoff: [[fallthrough]]; 168 | case vmx::exit_reason::vmlaunch: 169 | { 170 | handle_vmx_fallback(vcpu); 171 | break; 172 | } 173 | case vmx::exit_reason::ept_violation: [[fallthrough]]; 174 | case vmx::exit_reason::ept_misconfig: 175 | { 176 | __debugbreak(); 177 | break; 178 | } 179 | 180 | case vmx::exit_reason::msr_read: 181 | { 182 | handle_msr_read(vcpu); 183 | break; 184 | } 185 | case vmx::exit_reason::msr_write: 186 | { 187 | handle_msr_write(vcpu); 188 | break; 189 | } 190 | case vmx::exit_reason::vmcall: 191 | { 192 | terminate = handle_vmcall(vcpu); 193 | break; 194 | } 195 | default: 196 | __debugbreak(); 197 | break; 198 | } 199 | return terminate; 200 | } 201 | }; // namespace vmexit 202 | }; // namespace heye 203 | -------------------------------------------------------------------------------- /src/heye/hv/vmexit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vmx.hpp" 4 | 5 | #include 6 | 7 | namespace heye 8 | { 9 | struct vcpu_t; 10 | 11 | namespace vmexit 12 | { 13 | bool passthrough(vcpu_t* vcpu); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /src/heye/hv/vmx.cpp: -------------------------------------------------------------------------------- 1 | #include "vmx.hpp" 2 | 3 | namespace heye::vmx 4 | { 5 | status_t on(uint64_t pa) 6 | { 7 | return static_cast(__vmx_on(&pa)); 8 | } 9 | 10 | bool vmcall(vmcall_reason reason, void* a1, void* a2, void* a3) 11 | { 12 | return asm_vmcall(reinterpret_cast(reason), a1, a2, a3); 13 | } 14 | 15 | status_t off() 16 | { 17 | __vmx_off(); 18 | return status_t::success; 19 | } 20 | 21 | status_t clear(uint64_t pa) 22 | { 23 | return static_cast(__vmx_vmclear(&pa)); 24 | } 25 | 26 | status_t vmptrld(uint64_t pa) 27 | { 28 | return static_cast(__vmx_vmptrld(&pa)); 29 | } 30 | 31 | status_t launch() 32 | { 33 | return static_cast(asm_vmlaunch()); 34 | } 35 | 36 | void inject_exception(vm_interrupt_info_t interrupt) 37 | { 38 | write(interrupt.flags); 39 | } 40 | 41 | void inject_exception(exception_t vector, interrupt_t type) 42 | { 43 | vm_interrupt_info_t interrupt 44 | { 45 | .vector = static_cast(vector), 46 | .type = static_cast(type), 47 | .valid = 1 48 | }; 49 | write(interrupt.flags); 50 | } 51 | 52 | void inject_exception(exception_t vector, interrupt_t type, uint32_t code) 53 | { 54 | vm_interrupt_info_t interrupt 55 | { 56 | .vector = static_cast(vector), 57 | .type = static_cast(type), 58 | .code = 1, 59 | .valid = 1 60 | }; 61 | write(interrupt.flags); 62 | write(code); 63 | } 64 | 65 | void inject_bp() 66 | { 67 | inject_exception(exception_t::breakpoint, interrupt_t::software_exception); 68 | write(read()); 69 | } 70 | 71 | void inject_gp() 72 | { 73 | inject_exception(exception_t::undefined_opcode, interrupt_t::hardware_exception); 74 | write(read()); 75 | } 76 | 77 | void inject_ud() 78 | { 79 | inject_exception(exception_t::general_protection_fault, interrupt_t::hardware_exception); 80 | } 81 | }; 82 | -------------------------------------------------------------------------------- /src/heye/hv/vmx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "heye/arch/arch.hpp" 4 | 5 | #include "vmcall.hpp" 6 | 7 | namespace heye::vmx 8 | { 9 | bool vmcall(vmcall_reason reason, void* a1 = nullptr, void* a2 = nullptr, void* a3 = nullptr); 10 | 11 | enum status_t : uint8_t 12 | { 13 | success = 0, 14 | failed_ex = 1, 15 | failed = 2 16 | }; 17 | 18 | [[nodiscard]] status_t on(uint64_t pa); 19 | [[nodiscard]] status_t off(); 20 | [[nodiscard]] status_t clear(uint64_t pa); 21 | [[nodiscard]] status_t vmptrld(uint64_t pa); 22 | [[nodiscard]] status_t launch(); 23 | 24 | template 25 | T adjust(T value) 26 | { 27 | static_assert(std::is_same_v 28 | || std::is_same_v 29 | || std::is_same_v 30 | || std::is_same_v 31 | || std::is_same_v 32 | || std::is_same_v 33 | || std::is_same_v); 34 | 35 | uint64_t mask{}; 36 | const auto true_controls = heye::read().true_controls; 37 | if constexpr (std::is_same_v) 38 | { 39 | mask = true_controls ? heye::read().flags : heye::read().flags; 40 | value.flags &= mask >> 32; 41 | value.flags |= mask & 0xffffffff; 42 | } 43 | else if constexpr (std::is_same_v) 44 | { 45 | mask = true_controls ? heye::read().flags : heye::read().flags; 46 | value.flags &= mask >> 32; 47 | value.flags |= mask & 0xffffffff; 48 | } 49 | else if constexpr (std::is_same_v) 50 | { 51 | mask = true_controls ? heye::read().flags : heye::read().flags; 52 | value.flags &= mask >> 32; 53 | value.flags |= mask & 0xffffffff; 54 | } 55 | else if constexpr (std::is_same_v) 56 | { 57 | mask = true_controls ? heye::read().flags : heye::read().flags; 58 | value.flags &= mask >> 32; 59 | value.flags |= mask & 0xffffffff; 60 | } 61 | else if constexpr (std::is_same_v) 62 | { 63 | mask = heye::read().flags; 64 | value.flags &= mask >> 32; 65 | value.flags |= mask & 0xffffffff; 66 | } 67 | else if constexpr (std::is_same_v) 68 | { 69 | value.flags |= heye::read().flags; 70 | value.flags &= heye::read().flags; 71 | } 72 | else if constexpr (std::is_same_v) 73 | { 74 | value.flags |= heye::read().flags; 75 | value.flags &= heye::read().flags; 76 | } 77 | return value; 78 | } 79 | 80 | void inject_exception(vm_interrupt_info_t interrupt); 81 | void inject_exception(exception_t vector, interrupt_t type); 82 | void inject_exception(exception_t vector, interrupt_t type, uint32_t code); 83 | void inject_bp(); 84 | void inject_gp(); 85 | void inject_ud(); 86 | }; 87 | -------------------------------------------------------------------------------- /src/heye/shared/cpu.cpp: -------------------------------------------------------------------------------- 1 | #include "heye/shared/cpu.hpp" 2 | 3 | #include 4 | 5 | namespace heye::cpu 6 | { 7 | uint64_t count() 8 | { 9 | return KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS); 10 | } 11 | 12 | uint64_t current() 13 | { 14 | return KeGetCurrentProcessorNumberEx(nullptr); 15 | } 16 | 17 | void for_each(core_cb fn) 18 | { 19 | KeIpiGenericCall([](uint64_t arg) -> uint64_t 20 | { 21 | auto fn = reinterpret_cast(arg); 22 | (*fn)(current()); 23 | return true; 24 | }, reinterpret_cast(&fn)); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/heye/shared/cpu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "std/callable.hpp" 4 | #include 5 | 6 | namespace heye::cpu 7 | { 8 | struct uint128_t 9 | { 10 | uint64_t low; 11 | uint64_t high; 12 | }; 13 | static_assert(sizeof(uint128_t) == 16); 14 | 15 | struct alignas(8) regs_t 16 | { 17 | union 18 | { 19 | uint64_t gpr[16]; 20 | struct 21 | { 22 | union 23 | { 24 | uint64_t rax; 25 | uint32_t eax; 26 | }; 27 | union 28 | { 29 | uint64_t rcx; 30 | uint32_t ecx; 31 | }; 32 | union 33 | { 34 | uint64_t rbx; 35 | uint32_t ebx; 36 | }; 37 | uint64_t rdx; 38 | uint64_t rsp; 39 | uint64_t rbp; 40 | uint64_t rsi; 41 | uint64_t rdi; 42 | uint64_t r8; 43 | uint64_t r9; 44 | uint64_t r10; 45 | uint64_t r11; 46 | uint64_t r12; 47 | uint64_t r13; 48 | uint64_t r14; 49 | uint64_t r15; 50 | }; 51 | }; 52 | uint64_t rip; 53 | uint64_t rflags; 54 | uint128_t xmm0; 55 | uint128_t xmm1; 56 | uint128_t xmm2; 57 | uint128_t xmm3; 58 | uint128_t xmm4; 59 | uint128_t xmm5; 60 | uint128_t xmm6; 61 | uint128_t xmm7; 62 | uint128_t xmm8; 63 | uint128_t xmm9; 64 | uint128_t xmm10; 65 | uint128_t xmm11; 66 | uint128_t xmm12; 67 | uint128_t xmm13; 68 | uint128_t xmm14; 69 | uint128_t xmm15; 70 | }; 71 | static_assert(sizeof(regs_t) == 18 * 8 + 16 * 16, "regs_t size mismatch"); 72 | 73 | using core_cb = std::function; 74 | 75 | /// Get total number of cores. 76 | /// 77 | uint64_t count(); 78 | 79 | /// Get current cpu number. 80 | /// 81 | uint64_t current(); 82 | 83 | /// Run IPI routine on each core. 84 | /// 85 | void for_each(core_cb fn); 86 | }; 87 | -------------------------------------------------------------------------------- /src/heye/shared/std/atomic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/heye/shared/std/callable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "unique.hpp" 3 | 4 | namespace std 5 | { 6 | /// cv paste from stackoverflow 7 | /// 8 | template 9 | struct function{}; 10 | 11 | template 12 | struct function 13 | { 14 | private: 15 | struct callable 16 | { 17 | virtual ~callable() = default; 18 | virtual R operator()(A...) const = 0; 19 | }; 20 | 21 | template 22 | struct callable_t : callable 23 | { 24 | callable_t(T&& fn) : fn(std::forward(fn)) {} 25 | 26 | R operator()(A... args) const { return fn(std::forward(args)...); } 27 | 28 | private: 29 | T fn; 30 | }; 31 | 32 | unique_ptr callable_fn; 33 | 34 | public: 35 | function() = default; 36 | 37 | template 38 | function(T&& fn) : callable_fn(new callable_t(std::forward(fn))) {} 39 | 40 | R operator()(A... args) const 41 | { 42 | return (*callable_fn)(std::forward(args)...); 43 | } 44 | 45 | operator bool() const { return callable_fn; } 46 | 47 | function(const function&) = delete; 48 | function& operator=(const function&) = delete; 49 | }; 50 | }; 51 | -------------------------------------------------------------------------------- /src/heye/shared/std/mutex.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std 6 | { 7 | /// @brief https://rayanfam.com/topics/hypervisor-from-scratch-part-8/#designing-a-spinlock 8 | /// 9 | struct lock_guard 10 | { 11 | void lock() 12 | { 13 | // unsigned wait = 1; 14 | 15 | // while (!_lock && !InterlockedBitTestAndSet(&_lock, 0)) 16 | // { 17 | // for (unsigned i = 0; i < wait; i++) 18 | // _mm_pause(); 19 | 20 | // if (wait * 2 > max_wait) 21 | // wait = max_wait; 22 | // else 23 | // wait *= 2; 24 | // } 25 | } 26 | 27 | void unlock() 28 | { 29 | _lock = 0; 30 | } 31 | 32 | private: 33 | static constexpr auto max_wait = 65536; 34 | volatile long _lock = 0; 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /src/heye/shared/std/new.cpp: -------------------------------------------------------------------------------- 1 | #include "heye/config.hpp" 2 | 3 | #include 4 | #include 5 | 6 | void* operator new(size_t size) noexcept 7 | { 8 | PAGED_CODE(); 9 | return ExAllocatePoolZero(NonPagedPool, size, pool_tag); 10 | } 11 | 12 | void* operator new[](size_t size) noexcept 13 | { 14 | PAGED_CODE(); 15 | return ExAllocatePoolZero(NonPagedPool, size, pool_tag); 16 | } 17 | 18 | void operator delete (void* ptr) noexcept { PAGED_CODE(); if (ptr != nullptr) ExFreePoolWithTag(ptr, pool_tag); } 19 | void operator delete[](void* ptr) noexcept { PAGED_CODE(); if (ptr != nullptr) ExFreePoolWithTag(ptr, pool_tag); } 20 | void operator delete[](void* ptr, size_t) noexcept { PAGED_CODE(); if (ptr != nullptr) ExFreePoolWithTag(ptr, pool_tag); } 21 | void operator delete (void* ptr, size_t) noexcept { PAGED_CODE(); if (ptr != nullptr) ExFreePoolWithTag(ptr, pool_tag); } 22 | -------------------------------------------------------------------------------- /src/heye/shared/std/traits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace std 4 | { 5 | struct false_type 6 | { 7 | static constexpr bool value = false; 8 | constexpr operator bool() const noexcept { return value; } 9 | }; 10 | 11 | struct true_type 12 | { 13 | static constexpr bool value = true; 14 | constexpr operator bool() const noexcept { return value; } 15 | }; 16 | 17 | /// @brief cpuid concepts. 18 | /// 19 | template 20 | struct has_subleaf : false_type {}; 21 | 22 | template 23 | struct has_subleaf : true_type {}; 24 | 25 | template 26 | constexpr bool has_subleaf_v = has_subleaf::value; 27 | 28 | template 29 | struct has_leaf : false_type {}; 30 | 31 | template 32 | struct has_leaf : true_type {}; 33 | 34 | template 35 | constexpr bool has_leaf_v = has_leaf::value; 36 | 37 | /// @brief MSR concepts. 38 | /// 39 | template 40 | struct has_id : false_type {}; 41 | 42 | template 43 | struct has_id : true_type {}; 44 | 45 | template 46 | constexpr bool has_id_v = has_id::value; 47 | 48 | /// @brief std::remove_extent 49 | /// 50 | template struct remove_extent { typedef T type; }; 51 | template struct remove_extent { typedef T type; }; 52 | template struct remove_extent { typedef T type; }; 53 | 54 | /// @brief std::is_array 55 | /// 56 | template struct is_array : false_type {}; 57 | template struct is_array : true_type {}; 58 | template struct is_array : true_type {}; 59 | 60 | /// @brief std::remove_reference 61 | /// 62 | template struct remove_reference { typedef T type; }; 63 | template struct remove_reference { typedef T type; }; 64 | template struct remove_reference { typedef T type; }; 65 | template using remove_reference_t = typename remove_reference::type; 66 | 67 | /// @brief std::is_lvalue_reference 68 | /// 69 | template struct is_lvalue_reference : std::false_type {}; 70 | template struct is_lvalue_reference : std::true_type {}; 71 | template 72 | constexpr bool is_lvalue_reference_v = is_lvalue_reference::value; 73 | 74 | /// @brief std::is_same_v 75 | /// 76 | template struct is_same : false_type {}; 77 | template struct is_same : true_type {}; 78 | template 79 | constexpr bool is_same_v = is_same::value; 80 | 81 | template 82 | constexpr auto countof(T(&)[N]) { return N; } 83 | 84 | /// @brief Compile-time string compare. 85 | /// 86 | consteval int scmp(const char* lhs, const char* rhs) 87 | { 88 | return (('\0' == lhs[0]) && ('\0' == rhs[0])) ? 0 89 | : (lhs[0] != rhs[0]) ? (lhs[0] - rhs[0]) 90 | : scmp(lhs+1, rhs+1); 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /src/heye/shared/std/unique.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "traits.hpp" 3 | #include "utility.hpp" 4 | 5 | namespace std 6 | { 7 | template 8 | struct unique_ptr 9 | { 10 | using value_type = remove_extent::type; 11 | 12 | unique_ptr() : ptr(nullptr) {} 13 | unique_ptr(value_type* ptr) : ptr(ptr) {} 14 | 15 | ~unique_ptr() 16 | { 17 | if constexpr (is_array::value) 18 | { 19 | delete[] ptr; 20 | } 21 | else 22 | delete ptr; 23 | } 24 | 25 | unique_ptr(unique_ptr&& other) 26 | { 27 | operator=(std::move(other)); 28 | } 29 | 30 | unique_ptr& operator=(unique_ptr&& other) 31 | { 32 | if (this != &other) 33 | { 34 | this->~unique_ptr(); 35 | this->ptr = std::exchange(other.ptr, nullptr); 36 | } 37 | return *this; 38 | } 39 | 40 | value_type* operator->() { return ptr; } 41 | value_type* get() { return ptr; } 42 | value_type& operator*() { return *ptr; } 43 | 44 | const value_type* operator->() const { return ptr; } 45 | const value_type& operator*() const { return *ptr; } 46 | 47 | operator bool() const { return ptr; } 48 | 49 | unique_ptr(const unique_ptr& ptr) = delete; 50 | unique_ptr& operator=(const unique_ptr& ptr) = delete; 51 | 52 | private: 53 | value_type* ptr; 54 | }; 55 | 56 | template 57 | unique_ptr make_unique(A&&... args) 58 | { 59 | return unique_ptr(new T(forward(args)...)); 60 | } 61 | 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /src/heye/shared/std/utility.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "traits.hpp" 3 | 4 | namespace std 5 | { 6 | template 7 | [[nodisard]] remove_reference_t&& move(T&& arg) noexcept 8 | { 9 | return static_cast&&>(arg); 10 | } 11 | 12 | template 13 | [[nodisard]] constexpr T&& forward(remove_reference_t& arg) noexcept 14 | { 15 | return static_cast(arg); 16 | } 17 | 18 | template 19 | [[nodisard]] constexpr T&& forward(remove_reference_t&& arg) noexcept 20 | { 21 | static_assert(!is_lvalue_reference_v, "bad forward call"); 22 | return static_cast(arg); 23 | } 24 | 25 | template 26 | constexpr T exchange(T& object, U&& value) noexcept 27 | { 28 | T old_value = std::move(object); 29 | object = std::forward(value); 30 | return old_value; 31 | } 32 | 33 | template 34 | struct singleton 35 | { 36 | singleton(const singleton&) = delete; 37 | singleton(singleton&&) = delete; 38 | singleton& operator=(const singleton&) = delete; 39 | singleton& operator=(singleton&&) = delete; 40 | 41 | static T& get() 42 | { 43 | static T object; 44 | return object; 45 | } 46 | 47 | protected: 48 | singleton(){}; 49 | }; 50 | }; 51 | -------------------------------------------------------------------------------- /src/heye/shared/trace.cpp: -------------------------------------------------------------------------------- 1 | #include "heye/shared/cpu.hpp" 2 | #include "heye/shared/trace.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define _NO_CRT_STDIO_INLINE 9 | #include 10 | #include 11 | 12 | /// GUID: {60c3d354-edc4-4d20-8132-e16d9eeba96c} 13 | /// 14 | TRACELOGGING_DECLARE_PROVIDER(provider); 15 | TRACELOGGING_DEFINE_PROVIDER( 16 | provider, "HyperEyeProvider", 17 | (0x60c3d354, 0xedc4, 0x4d20, 0x81, 0x32, 0xe1, 0x6d, 0x9e, 0xeb, 0xa9, 0x6c) 18 | ); 19 | 20 | namespace heye 21 | { 22 | namespace detail 23 | { 24 | static bool initialized = false; 25 | 26 | void do_trace(const char* message) 27 | { 28 | TraceLoggingWrite( 29 | provider, 30 | "MessageEvent", 31 | TraceLoggingLevel(WINEVENT_LEVEL_INFO), 32 | TraceLoggingValue(cpu::current(), "Core"), 33 | TraceLoggingValue(message, "Message") 34 | ); 35 | } 36 | }; 37 | 38 | bool logger::setup() 39 | { 40 | if (NT_SUCCESS(TraceLoggingRegister(provider))) 41 | detail::initialized = true; 42 | return detail::initialized; 43 | } 44 | 45 | void logger::teardown() 46 | { 47 | if (detail::initialized) 48 | TraceLoggingUnregister(provider); 49 | detail::initialized = false; 50 | } 51 | 52 | void logger::info(const char* format, ...) 53 | { 54 | if (detail::initialized) 55 | { 56 | va_list args; 57 | va_start(args, format); 58 | 59 | char message[512]; 60 | vsprintf_s(message, sizeof(message), format, args); 61 | detail::do_trace(message); 62 | } 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/heye/shared/trace.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "std/utility.hpp" 3 | 4 | namespace heye::logger 5 | { 6 | bool setup(); 7 | void teardown(); 8 | void info(const char* format, ...); 9 | }; 10 | -------------------------------------------------------------------------------- /src/heye/vmi/process.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace heye 4 | { 5 | 6 | } // namespace heye 7 | -------------------------------------------------------------------------------- /src/heye/vmi/vmi.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "process.hpp" 3 | 4 | #include 5 | 6 | namespace heye 7 | { 8 | uint64_t get_current_thread(); 9 | uint64_t get_current_process(); 10 | } // namespace heye 11 | --------------------------------------------------------------------------------