diff --git a/README.md b/README.md index 742eaa8..f64437c 100644 --- a/README.md +++ b/README.md @@ -84,21 +84,20 @@ read and navigate through 5000 pages with browser's built-in PDF reader. - preallocate enough memory and initialize the **hvpp** memory manager - initialize the logger - Bootstrap of the hypervisor (hvppdrv, [main.cpp](src/hvppdrv/main.cpp)) - - create **hvpp** instance - create **vmexit_handler** instance -- Start the hypervisor with provided VM-exit handler (`hypervisor::start(vmexit_handler* handler)`) - - initialize each virtual cpu (VCPU) on each logical processor via IPI (inter-processor interrupt) - this also includes - initialization of EPT +- Start the hypervisor with provided VM-exit handler: `hypervisor::start(vmexit_handler& handler)` + - initialize virtual cpu (VCPU) for each logical processor - assign provided `vmexit_handler` instance to each VCPU - - launch all VCPUs - for each VCPU `vmexit_handler::setup()` is called within `vcpu_t::launch()` method, which - allows anyone to initialize the VM-exit handler and/or modify the VMCS before the launch (see `custom_vmexit_handler::setup()` - in hvppdrv, [custom_vmexit.cpp](src/hvppdrv/custom_vmexit.cpp)) + - launch all VCPUs via IPI (inter-processor interrupt): `vcpu_t::start()` + - setup VMXON region and VMCS: `vcpu_t::vmx_enter()` + - `vmexit_handler::setup()` is called, which allows anyone to initialize the VM-exit handler and/or modify the VMCS + before the launch (see `vmexit_custom_handler::setup()` in hvppdrv, [vmexit_custom.cpp](src/hvppdrv/vmexit_custom.cpp)) - Hypervisor is now running and handling VM-exits via provided VM-exit handler -- Terminate the hypervisor (`hypervisor::destroy()`) - - destroy each VCPU via IPI - for each VCPU `vmexit_handler::invoke_termination()` is called within `vcpu_t::destroy()` - method, which should be responsible for switching into VMX mode and then call `vcpu_t::terminate()` - - this is by default handled via `VMCALL` instruction - - `vcpu_t::terminate()` leaves VMX mode with `VMXOFF` instruction (which is available only in VMX mode), +- Stop the hypervisor: `hypervisor::stop()` + - destroy each VCPU via IPI: `vcpu_t::stop()` + - `vmexit_handler::teardown()` is called and switches into VMX mode (`vmexit_passthrough_handler::teardown()` does + it by `VMCALL` instruction) + - in VMX mode, `vcpu_t::vmx_leave()` is called - it leaves VMX mode with `VMXOFF` instruction ### Compilation @@ -154,7 +153,7 @@ Run **hvppctrl**: - **hvppctrl** performs `CPUID` instruction with `EAX = 0x70707668 ('hvpp')` which **hvpp** should intercept and return - string `hello from hvpp` in EAX, EBX, ECX and EDX registers (see [custom_vmexit.cpp](src/hvppdrv/custom_vmexit.cpp)). + string `hello from hvpp` in EAX, EBX, ECX and EDX registers (see [vmexit_custom.cpp](src/hvppdrv/vmexit_custom.cpp)). **hvppctrl** should print this string. - **hvppctrl** tries to "stealthily" hook `ntdll!ZwClose` function using EPT. The exact process is described diff --git a/src/hvpp/hvpp.vcxproj b/src/hvpp/hvpp.vcxproj index 6602e31..3455136 100644 --- a/src/hvpp/hvpp.vcxproj +++ b/src/hvpp/hvpp.vcxproj @@ -132,6 +132,7 @@ + diff --git a/src/hvpp/hvpp.vcxproj.filters b/src/hvpp/hvpp.vcxproj.filters index 2818a46..2a54ea6 100644 --- a/src/hvpp/hvpp.vcxproj.filters +++ b/src/hvpp/hvpp.vcxproj.filters @@ -302,6 +302,9 @@ Header Files\hvpp\vmexit + + Header Files\hvpp + diff --git a/src/hvpp/hvpp/config.h b/src/hvpp/hvpp/config.h index e85decf..3af4122 100644 --- a/src/hvpp/hvpp/config.h +++ b/src/hvpp/hvpp/config.h @@ -6,12 +6,6 @@ #define HVPP_MAX_CPU 256 -// -// Uncomment this if you want to subvert just one CPU (with ID 0). -// This can be helpful for debugging purposes. -// -// #define HVPP_SINGLE_VCPU - // // Uncomment this if you plan to intercept I/O ports 0x5658/0x5659 // in VMWare and you don't want the VMWare Tools to crash. diff --git a/src/hvpp/hvpp/ept.cpp b/src/hvpp/hvpp/ept.cpp index 03be323..70944ec 100644 --- a/src/hvpp/hvpp/ept.cpp +++ b/src/hvpp/hvpp/ept.cpp @@ -7,54 +7,35 @@ namespace hvpp { -auto ept_t::initialize() noexcept -> error_code_t +ept_t::ept_t() noexcept + : epml4_{} + , eptptr_{} { // - // Initialize EPT's PML4. Each PML4 maps 512GB of memory. We would be fine + // Initialize EPT's PML4. Each PML4 maps 512GB of memory. We would be fine // with just one PML4 in most scenarios, but we have to waste single page // on it anyway. Single page can handle 512 PML4s (their size is 8 bytes) // so just fill the whole page with 512 PML4s. // static_assert(sizeof(epte_t) * 512 == page_size); - epml4_ = new epte_t[512]; - hvpp_assert(epml4_ != nullptr); - - if (!epml4_) - { - return make_error_code_t(std::errc::not_enough_memory); - } - - memset(epml4_, 0, sizeof(epte_t) * 512); - // // Get physical address of EPT's PML4. // - pa_t empl4_pa = pa_t::from_va(epml4_); + const pa_t empl4_pa = pa_t::from_va(epml4_); // // Initialize EPT pointer. // It's not really JUST pointer, but Intel Manual calls it this way. // - eptptr_.flags = 0; - eptptr_.memory_type = static_cast(memory_manager::mtrr().type(empl4_pa)); + eptptr_.memory_type = static_cast(mm::mtrr().type(empl4_pa)); eptptr_.page_walk_length = ept_ptr_t::page_walk_length_4; eptptr_.page_frame_number = empl4_pa.pfn(); - - return error_code_t{}; } -void ept_t::destroy() noexcept +ept_t::~ept_t() noexcept { - eptptr_.flags = 0; - - if (epml4_) - { - unmap_table(epml4_); - delete[] epml4_; - - epml4_ = nullptr; - } + unmap_table(epml4_); } void ept_t::map_identity(epte_t::access_type access /* = epte_t::access_type::read_write_execute */) noexcept @@ -185,8 +166,8 @@ epte_t* ept_t::ept_entry(pa_t guest_pa, pml level /* = pml::pt */) noexcept // Start at PML4 and traverse down the paging hierarchy. // Returns nullptr for unmapped (non-present) physical addresses. // - auto pml4e = &epml4_[guest_pa.index(pml::pml4)]; - auto pdpte = pml4e->present() + const auto pml4e = &epml4_[guest_pa.index(pml::pml4)]; + const auto pdpte = pml4e->present() ? &pml4e->subtable()[guest_pa.index(pml::pdpt)] : nullptr; @@ -195,7 +176,7 @@ epte_t* ept_t::ept_entry(pa_t guest_pa, pml level /* = pml::pt */) noexcept return pdpte; } - auto pde = pdpte->present() + const auto pde = pdpte->present() ? &pdpte->subtable()[guest_pa.index(pml::pd)] : nullptr; @@ -204,7 +185,7 @@ epte_t* ept_t::ept_entry(pa_t guest_pa, pml level /* = pml::pt */) noexcept return pde; } - auto pte = pde->present() + const auto pte = pde->present() ? &pde->subtable()[guest_pa.index(pml::pt)] : nullptr; @@ -260,7 +241,7 @@ void ept_t::split(pa_t guest_pa, pa_t host_pa, epte_t::access_type access) noexc // The returned EPT entry is fetched at the "ept_table_from_t::level", // this means that if we're splitting from PD to PTs, we've fetched PD entry. // - auto entry = ept_entry(guest_pa, ept_table_from_t::level); + const auto entry = ept_entry(guest_pa, ept_table_from_t::level); // // Make sure that the fetched entry is indeed large. @@ -388,7 +369,7 @@ epte_t* ept_t::map_subtable(epte_t* table) noexcept return table->subtable(); } - auto subtable = new epte_t[512]; + const auto subtable = new epte_t[512]; hvpp_assert(subtable != nullptr); memset(subtable, 0, sizeof(epte_t) * 512); static_assert(sizeof(epte_t) * 512 == page_size); @@ -400,8 +381,8 @@ epte_t* ept_t::map_subtable(epte_t* table) noexcept epte_t* ept_t::map_pml4(pa_t guest_pa, pa_t host_pa, epte_t* pml4, epte_t::access_type access, pml large) noexcept { - auto pml4e = &pml4[guest_pa.index(pml::pml4)]; - auto pdpt = map_subtable(pml4e); + const auto pml4e = &pml4[guest_pa.index(pml::pml4)]; + const auto pdpt = map_subtable(pml4e); return map_pdpt(guest_pa, host_pa, pdpt, access, large); } @@ -409,42 +390,42 @@ epte_t* ept_t::map_pml4(pa_t guest_pa, pa_t host_pa, epte_t* pml4, epte_t* ept_t::map_pdpt(pa_t guest_pa, pa_t host_pa, epte_t* pdpt, epte_t::access_type access, pml large) noexcept { - auto pdpte = &pdpt[guest_pa.index(pml::pdpt)]; + const auto pdpte = &pdpt[guest_pa.index(pml::pdpt)]; if (large == pml::pdpt) { - pdpte->update(host_pa, memory_manager::mtrr().type(guest_pa), true, access); + pdpte->update(host_pa, mm::mtrr().type(guest_pa), true, access); return pdpte; } - auto pd = map_subtable(pdpte); + const auto pd = map_subtable(pdpte); return map_pd(guest_pa, host_pa, pd, access, large); } epte_t* ept_t::map_pd(pa_t guest_pa, pa_t host_pa, epte_t* pd, epte_t::access_type access, pml large) noexcept { - auto pde = &pd[guest_pa.index(pml::pd)]; + const auto pde = &pd[guest_pa.index(pml::pd)]; if (large == pml::pd) { - pde->update(host_pa, memory_manager::mtrr().type(guest_pa), true, access); + pde->update(host_pa, mm::mtrr().type(guest_pa), true, access); return pde; } - auto pt = map_subtable(pde); + const auto pt = map_subtable(pde); return map_pt(guest_pa, host_pa, pt, access, large); } epte_t* ept_t::map_pt(pa_t guest_pa, pa_t host_pa, epte_t* pt, epte_t::access_type access, pml large) noexcept { - auto pte = &pt[guest_pa.index(pml::pt)]; + const auto pte = &pt[guest_pa.index(pml::pt)]; (void)(large); hvpp_assert(large == pml::pt); { - pte->update(host_pa, memory_manager::mtrr().type(guest_pa), access); + pte->update(host_pa, mm::mtrr().type(guest_pa), access); return pte; } } @@ -461,7 +442,7 @@ void ept_t::unmap_table(epte_t* table, pml level /* = pml::pml4 */) noexcept // for (int i = 0; i < 512; ++i) { - auto entry = &table[i]; + const auto entry = &table[i]; unmap_entry(entry, level); } } @@ -492,7 +473,7 @@ void ept_t::unmap_entry(epte_t* entry, pml level) noexcept // // Fetch subtable. Only non-large pages have subtables. // - auto subtable = entry->subtable(); + const auto subtable = entry->subtable(); // // Unmap and/or deallocate the subtable based on current page map level. diff --git a/src/hvpp/hvpp/ept.h b/src/hvpp/hvpp/ept.h index e380e80..6dd8293 100644 --- a/src/hvpp/hvpp/ept.h +++ b/src/hvpp/hvpp/ept.h @@ -11,8 +11,8 @@ using namespace ia32; class ept_t final { public: - auto initialize() noexcept -> error_code_t; - void destroy() noexcept; + ept_t() noexcept; + ~ept_t() noexcept; void map_identity(epte_t::access_type access = epte_t::access_type::read_write_execute) noexcept; @@ -35,9 +35,9 @@ class ept_t final epte_t::access_type access = epte_t::access_type::read_write_execute) noexcept; void join_2mb_to_1gb(pa_t guest_pa, pa_t host_pa, - epte_t::access_type access = epte_t::access_type::read_write_execute) noexcept; + epte_t::access_type access = epte_t::access_type::read_write_execute) noexcept; void join_4kb_to_2mb(pa_t guest_pa, pa_t host_pa, - epte_t::access_type access = epte_t::access_type::read_write_execute) noexcept; + epte_t::access_type access = epte_t::access_type::read_write_execute) noexcept; epte_t* ept_entry(pa_t guest_pa, pml level = pml::pt) noexcept; ept_ptr_t ept_pointer() const noexcept; @@ -69,8 +69,9 @@ class ept_t final void unmap_table(epte_t* table, pml level = pml::pml4) noexcept; void unmap_entry(epte_t* entry, pml level) noexcept; + alignas(page_size) + epte_t epml4_[512]; ept_ptr_t eptptr_; - epte_t* epml4_; }; } diff --git a/src/hvpp/hvpp/hvpp.cpp b/src/hvpp/hvpp/hvpp.cpp index 9b649cd..056fac8 100644 --- a/src/hvpp/hvpp/hvpp.cpp +++ b/src/hvpp/hvpp/hvpp.cpp @@ -3,6 +3,7 @@ #include "hypervisor.h" #include "vcpu.h" +#include "lib/assert.h" #include "lib/cr3_guard.h" #include "lib/driver.h" #include "lib/mm.h" @@ -13,7 +14,6 @@ using namespace ia32; using namespace hvpp; -#define hvpp_ ((hypervisor*)Hvpp) #define vcpu_ ((vcpu_t*)Vcpu) #define ept_ ((ept_t*)Ept) @@ -200,55 +200,33 @@ HvppEptGetEptPointer( #pragma region hypervisor.h +vmexit_c_wrapper_handler* c_exit_handler = nullptr; + NTSTATUS NTAPI HvppInitialize( - _Out_ PHVPP* Hvpp + VOID ) { // // Initialize the memory manager and logger. // - driver::common::initialize(); - - // - // Allocate memory for the hypervisor instance. - // - - *Hvpp = (PHVPP)new hypervisor(); - - if (!*Hvpp) + if (auto err = driver::common::initialize()) { - // - // Allocation failed - exit. - // - driver::common::destroy(); return STATUS_INSUFFICIENT_RESOURCES; } - // - // Initialize the hypervisor. - // - - return ErrorCodeToNtStatus(((hypervisor*)(*Hvpp))->initialize()); + return STATUS_SUCCESS; } VOID NTAPI HvppDestroy( - _In_ PHVPP Hvpp + VOID ) { - // - // Destroy the hypervisor. - // - - hvpp_->destroy(); - delete &hvpp_->exit_handler(); - delete hvpp_; - // // Destroy the memory manager and logger. // @@ -259,16 +237,9 @@ HvppDestroy( NTSTATUS NTAPI HvppStart( - _In_ PHVPP Hvpp, _In_ PVMEXIT_HANDLER VmExitHandler ) { - // - // Create the VM-exit handler instance. - // - - auto exit_handler = new vmexit_c_wrapper_handler(); - // // Initialize the C-handlers array. // @@ -277,34 +248,39 @@ HvppStart( memcpy(c_handlers.data(), VmExitHandler->HandlerRoutine, sizeof(VmExitHandler->HandlerRoutine)); // - // Initialize the VM-exit handler. + // Create the VM-exit handler instance. // - exit_handler->initialize(c_handlers); + hvpp_assert(c_exit_handler == nullptr); + c_exit_handler = new vmexit_c_wrapper_handler(c_handlers); // // Start the hypervisor. // - return ErrorCodeToNtStatus(hvpp_->start(*exit_handler)); + return ErrorCodeToNtStatus(hypervisor::start(*c_exit_handler)); } VOID NTAPI HvppStop( - _In_ PHVPP Hvpp + VOID ) { - hvpp_->stop(); + hvpp_assert(c_exit_handler != nullptr); + + hypervisor::stop(); + + delete c_exit_handler; } BOOLEAN NTAPI -HvppIsStarted( - _In_ PHVPP Hvpp +HvppIsRunning( + VOID ) { - return hvpp_->is_started(); + return hypervisor::is_running(); } #pragma endregion diff --git a/src/hvpp/hvpp/hvpp.h b/src/hvpp/hvpp/hvpp.h index f46a9cb..21690e6 100644 --- a/src/hvpp/hvpp/hvpp.h +++ b/src/hvpp/hvpp/hvpp.h @@ -820,7 +820,6 @@ typedef enum _VMCS_FIELD // Opaque type definitions. ////////////////////////////////////////////////////////////////////////// -typedef PVOID PHVPP; typedef PVOID PVCPU; typedef PVOID PEPT; @@ -1216,32 +1215,31 @@ HvppEptGetEptPointer( NTSTATUS NTAPI HvppInitialize( - _Out_ PHVPP* Hvpp + VOID ); VOID NTAPI HvppDestroy( - _In_ PHVPP Hvpp + VOID ); NTSTATUS NTAPI HvppStart( - _In_ PHVPP Hvpp, _In_ PVMEXIT_HANDLER VmExitHandler ); VOID NTAPI HvppStop( - _In_ PHVPP Hvpp + VOID ); BOOLEAN NTAPI -HvppIsStarted( - _In_ PHVPP Hvpp +HvppIsRunning( + VOID ); #pragma endregion diff --git a/src/hvpp/hvpp/hypervisor.cpp b/src/hvpp/hvpp/hypervisor.cpp index c73deae..20ae7c8 100644 --- a/src/hvpp/hvpp/hypervisor.cpp +++ b/src/hvpp/hvpp/hypervisor.cpp @@ -7,191 +7,171 @@ #include "lib/mm.h" #include "lib/mp.h" -#ifdef HVPP_SINGLE_VCPU -# include - -# define single_cpu_call(callback) \ - do \ - { \ - auto idx = 0; \ - KeSetSystemAffinityThread((ULONG_PTR)1 << (idx)); \ - callback(); \ - KeRevertToUserAffinityThread(); \ - } while (0) -#endif - -namespace hvpp { - -auto hypervisor::initialize() noexcept -> error_code_t +namespace hvpp::hypervisor { - vcpu_list_ = new vcpu_t[mp::cpu_count()]; - exit_handler_ = nullptr; - check_passed_ = false; - started_ = false; - - if (!vcpu_list_) + namespace detail { - return make_error_code_t(std::errc::not_enough_memory); + static + bool + check_cpu_features( + void + ) noexcept + { + cpuid_eax_01 cpuid_info; + ia32_asm_cpuid(cpuid_info.cpu_info, 1); + if (!cpuid_info.feature_information_ecx.virtual_machine_extensions) + { + return false; + } + + const auto cr4 = read(); + if (cr4.vmx_enable) + { + return false; + } + + const auto vmx_basic = msr::read(); + if ( + vmx_basic.vmcs_size_in_bytes > page_size || + vmx_basic.memory_type != uint64_t(memory_type::write_back) || + !vmx_basic.true_controls + ) + { + return false; + } + + const auto vmx_ept_vpid_cap = msr::read(); + if ( + !vmx_ept_vpid_cap.page_walk_length_4 || + !vmx_ept_vpid_cap.memory_type_write_back || + !vmx_ept_vpid_cap.invept || + !vmx_ept_vpid_cap.invept_all_contexts || + !vmx_ept_vpid_cap.execute_only_pages || + !vmx_ept_vpid_cap.pde_2mb_pages + ) + { + return false; + } + + return true; + } } - if (!check_cpu_features()) + struct global_t { - return make_error_code_t(std::errc::not_supported); + vcpu_t* vcpu_list; + bool running; + }; + + global_t global; + + auto start(vmexit_handler& handler) noexcept -> error_code_t + { + // + // If hypervisor is already running, + // don't do anything. + // + hvpp_assert(!global.running); + if (global.running) + { + return make_error_code_t(std::errc::operation_not_permitted); + } + + // + // Create array of VCPUs. + // Note that since + // - vcpu_t is not default-constructible + // - operator new[] doesn't support constructing objects + // with parameters + // ... we have to construct this array "placement new". + // + hvpp_assert(global.vcpu_list == nullptr); + + global.vcpu_list = reinterpret_cast(operator new(sizeof(vcpu_t) * mp::cpu_count())); + if (!global.vcpu_list) + { + return make_error_code_t(std::errc::not_enough_memory); + } + + // + // Construct each vcpu_t object as `vcpu_t(handler)'. + // + std::for_each_n(global.vcpu_list, mp::cpu_count(), + [&](vcpu_t& vp) { + ::new (static_cast(std::addressof(vp))) + vcpu_t(handler); + }); + + // + // Check that CPU supports all required features to + // run this hypervisor. + // Note that this check is performed only on current CPU + // and assumes all CPUs are symmetrical. + // + if (!detail::check_cpu_features()) + { + return make_error_code_t(std::errc::not_supported); + } + + // + // Start virtualization on all CPUs. + // TODO: + // - error handling + // - create new error_category for VMX errors? + // + mp::ipi_call([]() { + mm::allocator_guard _; + + const auto idx = mp::cpu_index(); + global.vcpu_list[idx].start(); + }); + + // + // Signalize that hypervisor has started. + // + global.running = true; + + return error_code_t{}; } - return error_code_t{}; -} - -void hypervisor::destroy() noexcept -{ - if (started_) + void stop() noexcept { - stop(); + // + // If hypervisor is already stopped, + // don't do anything. + // + hvpp_assert(global.running); + if (!global.running) + { + return; + } + + // + // Stop virtualization on all CPUs. + // + mp::ipi_call([]() { + mm::allocator_guard _; + + const auto idx = mp::cpu_index(); + global.vcpu_list[idx].stop(); + }); + + // + // Destroy array of VCPUs. + // + std::destroy_n(global.vcpu_list, mp::cpu_count()); + delete global.vcpu_list; + + global.vcpu_list = nullptr; + + // + // Signalize that hypervisor has stopped. + // + global.running = false; } - if (vcpu_list_) + bool is_running() noexcept { - delete[] vcpu_list_; - vcpu_list_ = nullptr; - check_passed_ = false; - started_ = false; + return global.running; } } - -auto hypervisor::start(vmexit_handler& handler) noexcept -> error_code_t -{ - hvpp_assert(vcpu_list_ && check_passed_); - hvpp_assert(!started_); - - if (!vcpu_list_ || !check_passed_) - { - return make_error_code_t(std::errc::invalid_argument); - } - - if (started_) - { - return make_error_code_t(std::errc::operation_not_permitted); - } - - exit_handler_ = &handler; - -#ifdef HVPP_SINGLE_VCPU - single_cpu_call(start_ipi_callback); -#else - mp::ipi_call(this, &hypervisor::start_ipi_callback); -#endif - - started_ = true; - - return error_code_t{}; -} - -void hypervisor::stop() noexcept -{ - hvpp_assert(started_); - - if (!started_) - { - return; - } - -#ifdef HVPP_SINGLE_VCPU - single_cpu_call(stop_ipi_callback); -#else - mp::ipi_call(this, &hypervisor::stop_ipi_callback); -#endif - - started_ = false; -} - -bool hypervisor::is_started() const noexcept -{ - return started_; -} - -auto hypervisor::exit_handler() noexcept -> vmexit_handler& -{ - return *exit_handler_; -} - -// -// Private -// - -bool hypervisor::check_cpu_features() noexcept -{ - hvpp_assert(vcpu_list_); - -#ifdef HVPP_SINGLE_VCPU - single_cpu_call(check_ipi_callback); -#else - mp::ipi_call(this, &hypervisor::check_ipi_callback); -#endif - - return check_passed_; -} - -void hypervisor::check_ipi_callback() noexcept -{ - cpuid_eax_01 cpuid_info; - ia32_asm_cpuid(cpuid_info.cpu_info, 1); - if (!cpuid_info.feature_information_ecx.virtual_machine_extensions) - { - return; - } - - auto cr4 = read(); - if (cr4.vmx_enable) - { - return; - } - - auto vmx_basic = msr::read(); - if ( - vmx_basic.vmcs_size_in_bytes > page_size || - vmx_basic.memory_type != uint64_t(memory_type::write_back) || - !vmx_basic.true_controls - ) - { - return; - } - - auto vmx_ept_vpid_cap = msr::read(); - if ( - !vmx_ept_vpid_cap.page_walk_length_4 || - !vmx_ept_vpid_cap.memory_type_write_back || - !vmx_ept_vpid_cap.invept || - !vmx_ept_vpid_cap.invept_all_contexts || - !vmx_ept_vpid_cap.execute_only_pages || - !vmx_ept_vpid_cap.pde_2mb_pages - ) - { - return; - } - - check_passed_ = true; -} - -void hypervisor::start_ipi_callback() noexcept -{ - // - // TODO: - // - error handling - // - create new error_category for VMX errors - // - memory_manager::allocator_guard _; - - auto idx = mp::cpu_index(); - vcpu_list_[idx].initialize(*exit_handler_); - vcpu_list_[idx].launch(); -} - -void hypervisor::stop_ipi_callback() noexcept -{ - memory_manager::allocator_guard _; - - auto idx = mp::cpu_index(); - vcpu_list_[idx].destroy(); -} - -} diff --git a/src/hvpp/hvpp/hypervisor.h b/src/hvpp/hvpp/hypervisor.h index 8f3fc5a..53e4704 100644 --- a/src/hvpp/hvpp/hypervisor.h +++ b/src/hvpp/hvpp/hypervisor.h @@ -4,33 +4,10 @@ #include "lib/error.h" -namespace hvpp { - -using namespace ia32; - -class hypervisor final +namespace hvpp::hypervisor { - public: - auto initialize() noexcept -> error_code_t; - void destroy() noexcept; - - auto start(vmexit_handler& handler) noexcept -> error_code_t; - void stop() noexcept; - - bool is_started() const noexcept; - auto exit_handler() noexcept -> vmexit_handler&; - - private: - bool check_cpu_features() noexcept; - - void start_ipi_callback() noexcept; - void stop_ipi_callback() noexcept; - void check_ipi_callback() noexcept; - - vcpu_t* vcpu_list_; - vmexit_handler* exit_handler_; - bool check_passed_; - bool started_; -}; + auto start(vmexit_handler& handler) noexcept -> error_code_t; + void stop() noexcept; + bool is_running() noexcept; } diff --git a/src/hvpp/hvpp/ia32/arch/segment.h b/src/hvpp/hvpp/ia32/arch/segment.h index 8b33f64..a79f00b 100644 --- a/src/hvpp/hvpp/ia32/arch/segment.h +++ b/src/hvpp/hvpp/ia32/arch/segment.h @@ -148,7 +148,8 @@ struct segment_access_t // Segment access (as represented in VMX) // -struct segment_access_vmx_t : segment_access_t +struct segment_access_vmx_t + : segment_access_t { struct { @@ -209,7 +210,7 @@ struct gdt_entry_t if (!access.descriptor_type) { - result |= static_cast(base_address_upper) << 32; + result |= uint64_t(base_address_upper) << 32; } return reinterpret_cast(result); @@ -226,6 +227,13 @@ struct gdt_entry_t // Used for LDT. // + const gdt_entry_t& at(segment_selector_t selector) const noexcept + { + return *reinterpret_cast( + uint64_t(base_address()) + selector.index * 8 + ); + } + gdt_entry_t& at(segment_selector_t selector) noexcept { return *reinterpret_cast( @@ -233,10 +241,11 @@ struct gdt_entry_t ); } + const gdt_entry_t& operator[](segment_selector_t selector) const noexcept + { return at(selector); } + gdt_entry_t& operator[](segment_selector_t selector) noexcept - { - return at(selector); - } + { return at(selector); } }; struct idt_entry_t @@ -270,6 +279,16 @@ struct descriptor_table32_t // GDT entries are accessed by segment selector. // + const gdt_entry_t& at(segment_selector_t selector) const noexcept + { + // + // See explanation of (selector.index * 8) in vcpu.inl. + // + return *reinterpret_cast( + uint64_t(base_address) + selector.index * 8 + ); + } + gdt_entry_t& at(segment_selector_t selector) noexcept { // @@ -284,6 +303,13 @@ struct descriptor_table32_t // IDT entries are accessed by numeric index. // + const idt_entry_t& at(int index) const noexcept + { + return reinterpret_cast( + base_address + )[index]; + } + idt_entry_t& at(int index) noexcept { return reinterpret_cast( @@ -291,15 +317,17 @@ struct descriptor_table32_t )[index]; } + const gdt_entry_t& operator[](segment_selector_t selector) const noexcept + { return at(selector); } + gdt_entry_t& operator[](segment_selector_t selector) noexcept - { - return at(selector); - } + { return at(selector); } + + const idt_entry_t& operator[](int index) const noexcept + { return at(index); } idt_entry_t& operator[](int index) noexcept - { - return at(index); - } + { return at(index); } }; struct descriptor_table64_t @@ -307,6 +335,16 @@ struct descriptor_table64_t uint16_t limit; uint64_t base_address; + const gdt_entry_t& at(segment_selector_t selector) const noexcept + { + // + // See explanation of (selector.index * 8) in vcpu.inl. + // + return *reinterpret_cast( + uint64_t(base_address) + selector.index * 8 + ); + } + gdt_entry_t& at(segment_selector_t selector) noexcept { // @@ -317,6 +355,13 @@ struct descriptor_table64_t ); } + const idt_entry_t& at(int index) const noexcept + { + return reinterpret_cast( + base_address + )[index]; + } + idt_entry_t& at(int index) noexcept { return reinterpret_cast( @@ -324,15 +369,17 @@ struct descriptor_table64_t )[index]; } + const gdt_entry_t& operator[](segment_selector_t selector) const noexcept + { return at(selector); } + gdt_entry_t& operator[](segment_selector_t selector) noexcept - { - return at(selector); - } + { return at(selector); } + + const idt_entry_t& operator[](int index) const noexcept + { return at(index); } idt_entry_t& operator[](int index) noexcept - { - return at(index); - } + { return at(index); } }; #pragma pack(pop) @@ -377,42 +424,34 @@ struct segment_t T selector; segment_t() noexcept - : base_address() - , limit() - , access() - , selector() - { - - } + : base_address{} + , limit{} + , access{} + , selector{} + { } segment_t(T selector) noexcept - : base_address() - , limit() - , access() - , selector(selector) - { - - } + : base_address{} + , limit{} + , access{} + , selector{ selector } + { } segment_t(T selector, void* base_address) noexcept - : base_address(base_address) - , limit() - , access() - , selector(selector) - { - - } + : base_address{ base_address } + , limit{} + , access{} + , selector{ selector } + { } segment_t(void* base_address, uint32_t limit, segment_access_vmx_t access, T selector) noexcept - : base_address(base_address) - , limit(limit) - , access(access) - , selector(selector) - { + : base_address{ base_address } + , limit{ limit } + , access{ access } + , selector{ selector } + { } - } - - segment_t(descriptor_table64_t& descriptor_table, const T& segment_selector) noexcept + segment_t(descriptor_table64_t descriptor_table, T segment_selector) noexcept { static_assert(sizeof(segment_t) == 24); @@ -449,7 +488,7 @@ struct segment_t // if (selector.table == segment_selector_t::table_ldt) ia32_asm_int3(); - auto& table_entry = selector.table + const auto& table_entry = selector.table ? descriptor_table[read()][selector] : descriptor_table[ selector]; diff --git a/src/hvpp/hvpp/ia32/exception.h b/src/hvpp/hvpp/ia32/exception.h index a3422de..df6f804 100644 --- a/src/hvpp/hvpp/ia32/exception.h +++ b/src/hvpp/hvpp/ia32/exception.h @@ -27,13 +27,13 @@ enum class exception_vector : uint32_t virtualization_exception = 20, // - // Windows specific. + // NT (Windows) specific exception vectors. // - apc_interrupt = 31, - dpc_interrupt = 47, - clock_interrupt = 209, - pmi_interrupt = 254, + nt_apc_interrupt = 31, + nt_dpc_interrupt = 47, + nt_clock_interrupt = 209, + nt_pmi_interrupt = 254, }; struct pagefault_error_code_t @@ -76,7 +76,7 @@ struct exception_error_code_t }; }; -inline constexpr const char* exception_vector_to_string(exception_vector value) noexcept +constexpr inline const char* exception_vector_to_string(exception_vector value) noexcept { switch (value) { @@ -100,6 +100,10 @@ inline constexpr const char* exception_vector_to_string(exception_vector value) case exception_vector::machine_check: return "machine_check"; case exception_vector::simd_floating_point_error: return "simd_floating_point_error"; case exception_vector::virtualization_exception: return "virtualization_exception"; + case exception_vector::nt_apc_interrupt: return "nt_apc_interrupt"; + case exception_vector::nt_dpc_interrupt: return "nt_dpc_interrupt"; + case exception_vector::nt_clock_interrupt: return "nt_clock_interrupt"; + case exception_vector::nt_pmi_interrupt: return "nt_pmi_interrupt"; default: return ""; } } diff --git a/src/hvpp/hvpp/ia32/memory.cpp b/src/hvpp/hvpp/ia32/memory.cpp index 3534c48..544fcb9 100644 --- a/src/hvpp/hvpp/ia32/memory.cpp +++ b/src/hvpp/hvpp/ia32/memory.cpp @@ -22,7 +22,7 @@ namespace detail pe_t* va_t::pt_entry(cr3_t cr3 /*= read()*/, pml level /*= pml::pt*/) const noexcept { - auto pml4e = &reinterpret_cast( + const auto pml4e = &reinterpret_cast( pa_t::from_pfn(cr3.page_frame_number).va() )[index(pml::pml4)]; @@ -31,7 +31,7 @@ pe_t* va_t::pt_entry(cr3_t cr3 /*= read()*/, pml level /*= pml::pt*/) con return pml4e; } - auto pdpte = &reinterpret_cast( + const auto pdpte = &reinterpret_cast( pa_t::from_pfn(pml4e->page_frame_number).va() )[index(pml::pdpt)]; @@ -40,7 +40,7 @@ pe_t* va_t::pt_entry(cr3_t cr3 /*= read()*/, pml level /*= pml::pt*/) con return pdpte; } - auto pde = &reinterpret_cast( + const auto pde = &reinterpret_cast( pa_t::from_pfn(pdpte->page_frame_number).va() )[index(pml::pd)]; @@ -49,7 +49,7 @@ pe_t* va_t::pt_entry(cr3_t cr3 /*= read()*/, pml level /*= pml::pt*/) con return pde; } - auto pt = &reinterpret_cast( + const auto pt = &reinterpret_cast( pa_t::from_pfn(pde->page_frame_number).va() )[index(pml::pt)]; diff --git a/src/hvpp/hvpp/ia32/memory.h b/src/hvpp/hvpp/ia32/memory.h index a6027e2..3c2b77e 100644 --- a/src/hvpp/hvpp/ia32/memory.h +++ b/src/hvpp/hvpp/ia32/memory.h @@ -65,7 +65,7 @@ class pa_t pa_t(pa_t&& other) noexcept = default; pa_t& operator=(const pa_t& other) noexcept = default; pa_t& operator=(pa_t&& other) noexcept = default; - pa_t(uint64_t pa) noexcept : value_(pa) { } + pa_t(uint64_t pa) noexcept : value_{ pa } { } pa_t& operator= (uint64_t other) noexcept { value_ = other; return *this; } @@ -124,8 +124,8 @@ class va_t va_t(va_t&& other) noexcept = default; va_t& operator=(const va_t& other) noexcept = default; va_t& operator=(va_t&& other) noexcept = default; - va_t(const void* va) noexcept : value_(uint64_t(va)) { } - va_t(uint64_t va) noexcept : value_(va) { } + va_t(const void* va) noexcept : value_{ uint64_t(va) } { } + va_t(uint64_t va) noexcept : value_{ va } { } va_t& operator= (uint64_t other) noexcept { value_ = other; return *this; } @@ -206,13 +206,13 @@ class memory_range memory_range(const memory_range& other) noexcept = default; memory_range(memory_range&& other) noexcept = default; memory_range(const void* begin_va, const void* end_va) noexcept - : begin_(reinterpret_cast(begin_va)) - , end_(reinterpret_cast(end_va)) + : begin_{ reinterpret_cast(begin_va) } + , end_{ reinterpret_cast(end_va) } { } memory_range(const void* data, size_t size) noexcept - : begin_(reinterpret_cast(data)) - , end_(reinterpret_cast(data) + size) + : begin_{ reinterpret_cast(data) } + , end_{ reinterpret_cast(data) + size } { } memory_range& operator=(const memory_range& other) noexcept = default; @@ -276,8 +276,8 @@ class physical_memory_range physical_memory_range(const physical_memory_range& other) noexcept = default; physical_memory_range(physical_memory_range&& other) noexcept = default; physical_memory_range(pa_t begin_pa, pa_t end_pa) noexcept - : begin_(begin_pa) - , end_(end_pa) + : begin_{ begin_pa } + , end_{ end_pa } { } physical_memory_range& operator=(const physical_memory_range& other) noexcept = default; @@ -352,7 +352,7 @@ class physical_memory_descriptor int count_ = 0; }; -inline constexpr const char* memory_type_to_string(memory_type type) noexcept +constexpr inline const char* memory_type_to_string(memory_type type) noexcept { switch (type) { diff --git a/src/hvpp/hvpp/ia32/paging.h b/src/hvpp/hvpp/ia32/paging.h index 1b6ed1b..c3f55ae 100644 --- a/src/hvpp/hvpp/ia32/paging.h +++ b/src/hvpp/hvpp/ia32/paging.h @@ -30,29 +30,29 @@ enum class pml : uint8_t pml4 = 3, }; -inline constexpr pml& operator++(pml& ptl) noexcept -{ reinterpret_cast(ptl)++; return ptl; } +constexpr inline pml& operator++(pml& ptl) noexcept +{ ((uint8_t&)(ptl))++; return ptl; } -inline constexpr pml& operator--(pml& ptl) noexcept -{ reinterpret_cast(ptl)--; return ptl; } +constexpr inline pml& operator--(pml& ptl) noexcept +{ ((uint8_t&)(ptl))--; return ptl; } -inline constexpr pml operator++(pml& ptl, int) noexcept -{ auto result = ptl; reinterpret_cast(ptl)++; return result; } +constexpr inline pml operator++(pml& ptl, int) noexcept +{ auto result = ptl; ((uint8_t&)(ptl))++; return result; } -inline constexpr pml operator--(pml& ptl, int) noexcept -{ auto result = ptl; reinterpret_cast(ptl)--; return result; } +constexpr inline pml operator--(pml& ptl, int) noexcept +{ auto result = ptl; ((uint8_t&)(ptl))--; return result; } -inline constexpr pml operator+(pml ptl, uint8_t value) noexcept +constexpr inline pml operator+(pml ptl, uint8_t value) noexcept { return static_cast(static_cast(ptl) + value); } -inline constexpr pml operator-(pml ptl, uint8_t value) noexcept +constexpr inline pml operator-(pml ptl, uint8_t value) noexcept { return static_cast(static_cast(ptl) - value); } -inline constexpr pml& operator+=(pml& ptl, uint8_t value) noexcept -{ reinterpret_cast(ptl) += value; return ptl; } +constexpr inline pml& operator+=(pml& ptl, uint8_t value) noexcept +{ ((uint8_t&)(ptl)) += value; return ptl; } -inline constexpr pml& operator-=(pml& ptl, uint8_t value) noexcept -{ reinterpret_cast(ptl) -= value; return ptl; } +constexpr inline pml& operator-=(pml& ptl, uint8_t value) noexcept +{ ((uint8_t&)(ptl)) -= value; return ptl; } // // Page Table Entry @@ -346,7 +346,7 @@ template < ) > > -inline constexpr T page_align(T va, PAGE_DESCRIPTOR) noexcept +constexpr inline T page_align(T va, PAGE_DESCRIPTOR) noexcept { return (T)(uintptr_t(va) & PAGE_DESCRIPTOR::mask); } // @@ -362,7 +362,7 @@ template < ) > > -inline constexpr T page_align_up(T va, PAGE_DESCRIPTOR) noexcept +constexpr inline T page_align_up(T va, PAGE_DESCRIPTOR) noexcept { return (T)((uintptr_t(va) + PAGE_DESCRIPTOR::size - 1) & PAGE_DESCRIPTOR::mask); } // @@ -378,7 +378,7 @@ template < ) > > -inline constexpr uint32_t byte_offset(T va, PAGE_DESCRIPTOR) noexcept +constexpr inline uint32_t byte_offset(T va, PAGE_DESCRIPTOR) noexcept { return (uint32_t)(uintptr_t(va) & ~PAGE_DESCRIPTOR::mask); } // @@ -392,7 +392,7 @@ template < std::is_integral_v > > -inline constexpr uint64_t bytes_to_pages(T size, PAGE_DESCRIPTOR) noexcept +constexpr inline uint64_t bytes_to_pages(T size, PAGE_DESCRIPTOR) noexcept { return (size >> PAGE_DESCRIPTOR::shift) + ((size & ~PAGE_DESCRIPTOR::mask) != 0); } // @@ -406,7 +406,7 @@ template < std::is_integral_v > > -inline constexpr uint64_t round_to_pages(T size, PAGE_DESCRIPTOR) noexcept +constexpr inline uint64_t round_to_pages(T size, PAGE_DESCRIPTOR) noexcept { return uint64_t(page_align_up(size, PAGE_DESCRIPTOR{})); } // @@ -414,23 +414,23 @@ inline constexpr uint64_t round_to_pages(T size, PAGE_DESCRIPTOR) noexcept // template -inline constexpr T page_align(T va) noexcept +constexpr inline T page_align(T va) noexcept { return page_align(va, pt_t{}); } template -inline constexpr T page_align_up(T va) noexcept +constexpr inline T page_align_up(T va) noexcept { return page_align_up(va, pt_t{}); } template -inline constexpr uint32_t byte_offset(T va) noexcept +constexpr inline uint32_t byte_offset(T va) noexcept { return byte_offset(va, pt_t{}); } template -inline constexpr uint64_t bytes_to_pages(T size) noexcept +constexpr inline uint64_t bytes_to_pages(T size) noexcept { return bytes_to_pages(size, pt_t{}); } template -inline constexpr uint64_t round_to_pages(T size) noexcept +constexpr inline uint64_t round_to_pages(T size) noexcept { return round_to_pages(size, pt_t{}); } } diff --git a/src/hvpp/hvpp/ia32/vmx/exit_reason.h b/src/hvpp/hvpp/ia32/vmx/exit_reason.h index 44f9ce4..c9b2c62 100644 --- a/src/hvpp/hvpp/ia32/vmx/exit_reason.h +++ b/src/hvpp/hvpp/ia32/vmx/exit_reason.h @@ -72,7 +72,7 @@ enum class exit_reason : uint16_t execute_xrstors = 0x00000040, }; -inline constexpr const char* exit_reason_to_string(exit_reason value) noexcept +constexpr inline const char* exit_reason_to_string(exit_reason value) noexcept { switch (value) { diff --git a/src/hvpp/hvpp/ia32/vmx/instruction_error.h b/src/hvpp/hvpp/ia32/vmx/instruction_error.h index 00f91d4..e46b0b3 100644 --- a/src/hvpp/hvpp/ia32/vmx/instruction_error.h +++ b/src/hvpp/hvpp/ia32/vmx/instruction_error.h @@ -36,7 +36,7 @@ enum instruction_error : uint32_t invept_invvpid_invalid_operand = 28, }; -inline constexpr const char* instruction_error_to_string(instruction_error value) noexcept +constexpr inline const char* instruction_error_to_string(instruction_error value) noexcept { switch (value) { diff --git a/src/hvpp/hvpp/ia32/vmx/instruction_info.h b/src/hvpp/hvpp/ia32/vmx/instruction_info.h index 2b2a6ef..ceeb25a 100644 --- a/src/hvpp/hvpp/ia32/vmx/instruction_info.h +++ b/src/hvpp/hvpp/ia32/vmx/instruction_info.h @@ -236,7 +236,7 @@ struct instruction_info_t }; }; -inline constexpr const char* instruction_info_gdtr_idtr_to_string(uint64_t value) noexcept +constexpr inline const char* instruction_info_gdtr_idtr_to_string(uint64_t value) noexcept { switch (value) { @@ -249,7 +249,7 @@ inline constexpr const char* instruction_info_gdtr_idtr_to_string(uint64_t value } -inline constexpr const char* instruction_info_ldtr_tr_to_string(uint64_t value) noexcept +constexpr inline const char* instruction_info_ldtr_tr_to_string(uint64_t value) noexcept { switch (value) { diff --git a/src/hvpp/hvpp/ia32/vmx/interrupt.h b/src/hvpp/hvpp/ia32/vmx/interrupt.h index 2944bc7..a18750e 100644 --- a/src/hvpp/hvpp/ia32/vmx/interrupt.h +++ b/src/hvpp/hvpp/ia32/vmx/interrupt.h @@ -52,7 +52,7 @@ struct interruptibility_state_t }; }; -inline constexpr const char* interrupt_type_to_string(interrupt_type value) noexcept +constexpr inline const char* interrupt_type_to_string(interrupt_type value) noexcept { switch (value) { diff --git a/src/hvpp/hvpp/interrupt.h b/src/hvpp/hvpp/interrupt.h new file mode 100644 index 0000000..33efcfb --- /dev/null +++ b/src/hvpp/hvpp/interrupt.h @@ -0,0 +1,133 @@ +#pragma once +#include "ia32/exception.h" +#include "ia32/vmx.h" + +namespace hvpp { + +class interrupt_t final +{ + public: + // + // Constructors. + // + constexpr + interrupt_t( + vmx::interrupt_type interrupt_type, + exception_vector exception_vector, + int rip_adjust = -1 + ) noexcept + : interrupt_t{ interrupt_type, + exception_vector, + exception_error_code_t{}, + false, + rip_adjust } + { } + + constexpr + interrupt_t( + vmx::interrupt_type interrupt_type, + exception_vector exception_vector, + exception_error_code_t exception_code, + int rip_adjust = -1 + ) noexcept + : interrupt_t{ interrupt_type, + exception_vector, + exception_code, + true, + rip_adjust } + { } + + // + // Default copy/move constructor. + // Default copy/move assignment operator. + // + constexpr interrupt_t(const interrupt_t& other) noexcept = default; + constexpr interrupt_t(interrupt_t&& other) noexcept = default; + constexpr interrupt_t& operator=(const interrupt_t& other) noexcept = default; + constexpr interrupt_t& operator=(interrupt_t&& other) noexcept = default; + + // + // Getters. + // + constexpr auto vector() const noexcept { return static_cast(info_.vector); } + constexpr auto type() const noexcept { return static_cast(info_.type); } + constexpr auto error_code_valid() const noexcept { return info_.error_code_valid; } + constexpr auto nmi_unblocking() const noexcept { return info_.nmi_unblocking; } + constexpr auto valid() const noexcept { return info_.valid; } + constexpr auto error_code() const noexcept { return error_code_; } + constexpr auto rip_adjust() const noexcept { return rip_adjust_; } + + private: + friend class vcpu_t; + + constexpr + interrupt_t() noexcept + : info_{} + , error_code_{} + , rip_adjust_{} + { } + + constexpr + interrupt_t( + vmx::interrupt_type interrupt_type, + exception_vector exception_vector, + exception_error_code_t exception_code, + bool exception_code_valid, + int rip_adjust + ) noexcept + : error_code_{ exception_code } + , rip_adjust_{ rip_adjust } + { + info_.flags = 0; + + info_.vector = static_cast(exception_vector); + info_.type = static_cast(interrupt_type); + info_.valid = true; + + // + // Final sanitization of the following fields takes place + // in vcpu::interrupt_inject_force(). + // + + info_.error_code_valid = exception_code_valid; + } + + vmx::interrupt_info_t info_; + exception_error_code_t error_code_; + int rip_adjust_; +}; + +namespace interrupt +{ + // + // Predefined interrupt structures. + // Helpful when injecting events. + // + + static constexpr auto nmi = + interrupt_t { + vmx::interrupt_type::nmi, + exception_vector::nmi_interrupt + }; + + static constexpr auto debug = + interrupt_t { + vmx::interrupt_type::hardware_exception, + exception_vector::debug + }; + + static constexpr auto invalid_opcode = + interrupt_t { + vmx::interrupt_type::hardware_exception, + exception_vector::invalid_opcode + }; + + static constexpr auto general_protection = + interrupt_t { + vmx::interrupt_type::hardware_exception, + exception_vector::general_protection, + exception_error_code_t{} + }; +} + +} diff --git a/src/hvpp/hvpp/lib/bitmap.h b/src/hvpp/hvpp/lib/bitmap.h index 72ab288..af892ab 100644 --- a/src/hvpp/hvpp/lib/bitmap.h +++ b/src/hvpp/hvpp/lib/bitmap.h @@ -18,20 +18,20 @@ class bitmap { public: - bitmap() noexcept : buffer_(nullptr), size_in_bits_(0) { }; + bitmap() noexcept : buffer_{ nullptr }, size_in_bits_{ 0 } { }; bitmap(const bitmap& other) noexcept = delete; bitmap(bitmap&& other) noexcept = default; bitmap& operator=(const bitmap& other) = delete; bitmap& operator=(bitmap&& other) = default; bitmap(void* buffer, int size_in_bits) noexcept - : buffer_(reinterpret_cast(buffer)) - , size_in_bits_(size_in_bits) { } + : buffer_{ reinterpret_cast(buffer) } + , size_in_bits_{ size_in_bits } { } template bitmap(T(&buffer)[SIZE], int size_in_bits = SIZE * sizeof(T)) noexcept - : buffer_(reinterpret_cast(buffer)) - , size_in_bits_(size_in_bits) { } + : buffer_{ reinterpret_cast(buffer) } + , size_in_bits_{ size_in_bits } { } ~bitmap() noexcept = default; @@ -90,7 +90,7 @@ class bitmap_local : public bitmap { public: - bitmap_local() : bitmap(buffer_, SIZE_IN_BITS) { } + bitmap_local() : bitmap{ buffer_, SIZE_IN_BITS } { } bitmap_local(const bitmap_local& other) noexcept = delete; bitmap_local(bitmap_local&& other) noexcept = default; bitmap_local& operator=(const bitmap_local& other) noexcept = delete; diff --git a/src/hvpp/hvpp/lib/cr3_guard.h b/src/hvpp/hvpp/lib/cr3_guard.h index 851976f..01d527b 100644 --- a/src/hvpp/hvpp/lib/cr3_guard.h +++ b/src/hvpp/hvpp/lib/cr3_guard.h @@ -28,7 +28,7 @@ class cr3_guard { public: cr3_guard(ia32::cr3_t new_cr3) noexcept - : previous_cr3_(ia32::read()) + : previous_cr3_{ ia32::read() } { ia32::write(::detail::kernel_cr3(new_cr3)); } ~cr3_guard() noexcept diff --git a/src/hvpp/hvpp/lib/device.h b/src/hvpp/hvpp/lib/device.h index 34b5149..2798e7f 100644 --- a/src/hvpp/hvpp/lib/device.h +++ b/src/hvpp/hvpp/lib/device.h @@ -12,12 +12,13 @@ class device { public: - virtual ~device() noexcept {} + device() noexcept {} + virtual ~device() noexcept { destroy(); } - virtual auto initialize() noexcept -> error_code_t; - virtual void destroy() noexcept; + virtual const char* name() const noexcept = 0; - virtual const char* name() const noexcept = 0; + auto create() noexcept -> error_code_t; + void destroy() noexcept; // // Dispatch methods. diff --git a/src/hvpp/hvpp/lib/driver.cpp b/src/hvpp/hvpp/lib/driver.cpp index bba35e5..ede3689 100644 --- a/src/hvpp/hvpp/lib/driver.cpp +++ b/src/hvpp/hvpp/lib/driver.cpp @@ -41,7 +41,7 @@ namespace driver::common return err; } - if (auto err = memory_manager::initialize()) + if (auto err = mm::initialize()) { return err; } @@ -49,8 +49,8 @@ namespace driver::common // // Print memory information to the debugger. // - memory_manager::mtrr().dump(); - memory_manager::physical_memory_descriptor().dump(); + mm::mtrr().dump(); + mm::physical_memory_descriptor().dump(); // // Estimate required memory size. @@ -59,7 +59,7 @@ namespace driver::common // // Default required memory size is 34MB per CPU. // - auto required_memory_size = ( + const auto required_memory_size = ( // // Estimated EPT size: // Make space for 2MB EPT entries for 512 GB of the physical @@ -88,7 +88,7 @@ namespace driver::common // // Allocate memory. // - system_memory_ = memory_manager::system_allocate(required_memory_size); + system_memory_ = mm::system_allocate(required_memory_size); if (!system_memory_) { @@ -98,7 +98,7 @@ namespace driver::common // // Assign allocated memory to the memory manager. // - if (auto err = memory_manager::assign(system_memory_, system_memory_size_)) + if (auto err = mm::assign(system_memory_, system_memory_size_)) { return err; } @@ -122,7 +122,7 @@ namespace driver::common // // Destroy memory manager and logger. // - memory_manager::destroy(); + mm::destroy(); logger::destroy(); // @@ -130,7 +130,7 @@ namespace driver::common // if (system_memory_) { - memory_manager::system_free(system_memory_); + mm::system_free(system_memory_); } } } diff --git a/src/hvpp/hvpp/lib/error.h b/src/hvpp/hvpp/lib/error.h index c285432..af7a7ae 100644 --- a/src/hvpp/hvpp/lib/error.h +++ b/src/hvpp/hvpp/lib/error.h @@ -17,41 +17,44 @@ class error_code_t { public: - error_code_t() noexcept - : value_(0) { } + constexpr error_code_t() noexcept + : value_{ 0 } + { } - error_code_t(int value) noexcept - : value_(value) { } + constexpr error_code_t(int value) noexcept + : value_{ value } + { } template< class EnumT, std::enable_if_t, int> = 0 > - error_code_t(EnumT value) noexcept - : value_((int)value) { } + constexpr error_code_t(EnumT value) noexcept + : value_{ (int)value } + { } template< class EnumT, std::enable_if_t, int> = 0 > - error_code_t& operator=(EnumT value) noexcept + constexpr error_code_t& operator=(EnumT value) noexcept { value_ = (int)value; return *this; } - void assign(int value) noexcept + constexpr void assign(int value) noexcept { value_ = value; } - void clear() noexcept + constexpr void clear() noexcept { value_ = 0; } - int value() const noexcept + constexpr int value() const noexcept { return value_; } - explicit operator bool() const noexcept + constexpr explicit operator bool() const noexcept { return value() != 0; } private: int value_; }; -inline error_code_t make_error_code_t(std::errc value) noexcept +constexpr inline error_code_t make_error_code_t(std::errc value) noexcept { return error_code_t((int)value); } diff --git a/src/hvpp/hvpp/lib/ioctl.h b/src/hvpp/hvpp/lib/ioctl.h index acc4bf5..587c5c2 100644 --- a/src/hvpp/hvpp/lib/ioctl.h +++ b/src/hvpp/hvpp/lib/ioctl.h @@ -10,7 +10,7 @@ enum class ioctl_access : uint32_t read_write = read | write }; -inline constexpr auto +constexpr inline auto make_ioctl_code_windows( uint32_t id, ioctl_access access, @@ -61,7 +61,7 @@ make_ioctl_code_windows( uint32_t(access)); } -inline constexpr auto +constexpr inline auto make_ioctl_code_linux( uint32_t id, ioctl_access access, @@ -86,7 +86,7 @@ make_ioctl_code_linux( return ctl_code_impl(uint32_t(access), 'H', id, size); } -inline constexpr auto +constexpr inline auto make_ioctl_code( uint32_t id, ioctl_access access, diff --git a/src/hvpp/hvpp/lib/mm.cpp b/src/hvpp/hvpp/lib/mm.cpp index a219502..c65a62c 100644 --- a/src/hvpp/hvpp/lib/mm.cpp +++ b/src/hvpp/hvpp/lib/mm.cpp @@ -43,7 +43,7 @@ // 4096 bytes. // -namespace memory_manager +namespace mm { using pgbmp_t = object_t; using pgmap_t = uint16_t; @@ -196,7 +196,7 @@ namespace memory_manager // if (ia32::byte_offset(address) != 0) { - uint32_t lost_bytes = ia32::byte_offset(address); + const auto lost_bytes = ia32::byte_offset(address); address = reinterpret_cast(ia32::page_align(address)) + ia32::page_size; @@ -285,7 +285,7 @@ namespace memory_manager // This should help with debugging uninitialized variables // and class members. // - int reserved_bytes = static_cast(global.page_bitmap_buffer_size + global.page_allocation_map_size); + const auto reserved_bytes = static_cast(global.page_bitmap_buffer_size + global.page_allocation_map_size); memset(global.base_address + reserved_bytes, 0xcc, size - reserved_bytes); // @@ -370,7 +370,7 @@ namespace memory_manager // hvpp_assert(ia32::byte_offset(address) == 0); - int offset = static_cast(ia32::bytes_to_pages(reinterpret_cast(address) - global.base_address)); + const auto offset = static_cast(ia32::bytes_to_pages(reinterpret_cast(address) - global.base_address)); if (address == nullptr) { @@ -404,7 +404,7 @@ namespace memory_manager // // Clear number of allocated pages. // - int page_count = global.page_allocation_map[offset]; + const auto page_count = static_cast(global.page_allocation_map[offset]); global.page_allocation_map[offset] = 0; // @@ -461,17 +461,17 @@ namespace detail { void generic_free(void* address) noexcept { - reinterpret_cast(address) >= memory_manager::global.base_address && - reinterpret_cast(address) < memory_manager::global.base_address + memory_manager::global.available_size - ? memory_manager::free (address) - : memory_manager::system_free(address); + reinterpret_cast(address) >= mm::global.base_address && + reinterpret_cast(address) < mm::global.base_address + mm::global.available_size + ? mm::free (address) + : mm::system_free(address); } } -void* operator new (size_t size) { return memory_manager::global.allocator[mp::cpu_index()].allocate(size); } -void* operator new[](size_t size) { return memory_manager::global.allocator[mp::cpu_index()].allocate(size); } -void* operator new (size_t size, std::align_val_t) { return memory_manager::global.allocator[mp::cpu_index()].allocate(size); } -void* operator new[](size_t size, std::align_val_t) { return memory_manager::global.allocator[mp::cpu_index()].allocate(size); } +void* operator new (size_t size) { return mm::global.allocator[mp::cpu_index()].allocate(size); } +void* operator new[](size_t size) { return mm::global.allocator[mp::cpu_index()].allocate(size); } +void* operator new (size_t size, std::align_val_t) { return mm::global.allocator[mp::cpu_index()].allocate(size); } +void* operator new[](size_t size, std::align_val_t) { return mm::global.allocator[mp::cpu_index()].allocate(size); } void operator delete (void* address) { detail::generic_free(address); } void operator delete[](void* address) { detail::generic_free(address); } diff --git a/src/hvpp/hvpp/lib/mm.h b/src/hvpp/hvpp/lib/mm.h index a0d5627..4142f24 100644 --- a/src/hvpp/hvpp/lib/mm.h +++ b/src/hvpp/hvpp/lib/mm.h @@ -6,7 +6,7 @@ #include -namespace memory_manager +namespace mm { namespace detail { diff --git a/src/hvpp/hvpp/lib/mp.h b/src/hvpp/hvpp/lib/mp.h index 8e72f4e..a628f21 100644 --- a/src/hvpp/hvpp/lib/mp.h +++ b/src/hvpp/hvpp/lib/mp.h @@ -37,8 +37,4 @@ namespace mp template inline void ipi_call(T function) noexcept { ipi_call([](void* context) noexcept { ((T*)context)->operator()(); }, &function); } - - template - inline void ipi_call(T* instance, void (T::*member_function)()) noexcept - { ipi_call([=]() { (instance->*member_function)(); }); } } diff --git a/src/hvpp/hvpp/lib/typelist.h b/src/hvpp/hvpp/lib/typelist.h index bd5a372..c8dc89e 100644 --- a/src/hvpp/hvpp/lib/typelist.h +++ b/src/hvpp/hvpp/lib/typelist.h @@ -53,7 +53,7 @@ template < > void for_each_element(std::tuple& t, F&& f, std::index_sequence) { - int unused[] = { 0, (f(std::get(t), INDEX), void(), 0)... }; + const int unused[] = { 0, (f(std::get(t), INDEX), void(), 0)... }; (void)(unused); } @@ -73,7 +73,7 @@ template < > void for_each_element(const std::tuple& t, F&& f, std::index_sequence) { - int unused[] = { 0, (f(std::get(t), INDEX), void(), 0)... }; + const int unused[] = { 0, (f(std::get(t), INDEX), void(), 0)... }; (void)(unused); } diff --git a/src/hvpp/hvpp/lib/vmware/vmware.cpp b/src/hvpp/hvpp/lib/vmware/vmware.cpp index 0208445..01f75b2 100644 --- a/src/hvpp/hvpp/lib/vmware/vmware.cpp +++ b/src/hvpp/hvpp/lib/vmware/vmware.cpp @@ -103,7 +103,7 @@ try_decode_io_instruction( int size_of_access; bool rep_prefixed; - const uint8_t* rip = reinterpret_cast(ctx.rip); + const auto rip = reinterpret_cast(ctx.rip); if (!try_decode_io_instruction(rip, access_type, size_of_access, rep_prefixed)) { return false; diff --git a/src/hvpp/hvpp/lib/win32/cr3_guard.cpp b/src/hvpp/hvpp/lib/win32/cr3_guard.cpp index f488fdf..eb15920 100644 --- a/src/hvpp/hvpp/lib/win32/cr3_guard.cpp +++ b/src/hvpp/hvpp/lib/win32/cr3_guard.cpp @@ -84,7 +84,7 @@ ia32::cr3_t kernel_cr3(ia32::cr3_t cr3) noexcept hvpp_assert(cr3.pcid == PCID_USER); #endif - auto kprocess = reinterpret_cast(PsGetCurrentProcess()); + const auto kprocess = reinterpret_cast(PsGetCurrentProcess()); return ia32::cr3_t{ kprocess->DirectoryTableBase }; } diff --git a/src/hvpp/hvpp/lib/win32/device.cpp b/src/hvpp/hvpp/lib/win32/device.cpp index 629abb1..1c7cd59 100644 --- a/src/hvpp/hvpp/lib/win32/device.cpp +++ b/src/hvpp/hvpp/lib/win32/device.cpp @@ -5,7 +5,7 @@ // // Definition is located in win32/driver.cpp. // -extern PDRIVER_OBJECT GlobalDriverObject; +EXTERN_C PDRIVER_OBJECT GlobalDriverObject; #define HVPP_DEVICE_TAG 'vdvh' #define MAX_BUFFER_SIZE 64 @@ -21,7 +21,7 @@ typedef struct _DEVICE_IMPL WCHAR DeviceLinkBuffer[MAX_BUFFER_SIZE + sizeof(L"\\DosDevices\\") - 1]; } DEVICE_IMPL, *PDEVICE_IMPL; -auto device::initialize() noexcept -> error_code_t +auto device::create() noexcept -> error_code_t { error_code_t err; @@ -153,9 +153,11 @@ void device::destroy() noexcept { IoDeleteSymbolicLink(&DeviceImpl->DeviceLink); IoDeleteDevice(DeviceImpl->DeviceObject); - } - ExFreePoolWithTag(DeviceImpl, HVPP_DEVICE_TAG); + ExFreePoolWithTag(DeviceImpl, HVPP_DEVICE_TAG); + + impl_ = nullptr; + } } error_code_t device::copy_from_user(void* buffer_to, const void* buffer_from, size_t length) noexcept diff --git a/src/hvpp/hvpp/lib/win32/driver.cpp b/src/hvpp/hvpp/lib/win32/driver.cpp index 65d2036..44b5bef 100644 --- a/src/hvpp/hvpp/lib/win32/driver.cpp +++ b/src/hvpp/hvpp/lib/win32/driver.cpp @@ -20,9 +20,11 @@ // #define ACCESS_FROM_CTL_CODE(ctrlCode) (((ULONG)(ctrlCode & 0x0000c000)) >> 14) -EXTERN_C DRIVER_INITIALIZE DriverEntry; - -PDRIVER_OBJECT GlobalDriverObject = nullptr; +extern "C" +{ + DRIVER_INITIALIZE DriverEntry; + PDRIVER_OBJECT GlobalDriverObject = nullptr; +} namespace driver { diff --git a/src/hvpp/hvpp/lib/win32/log.cpp b/src/hvpp/hvpp/lib/win32/log.cpp index 7af50f3..bbe6454 100644 --- a/src/hvpp/hvpp/lib/win32/log.cpp +++ b/src/hvpp/hvpp/lib/win32/log.cpp @@ -4,8 +4,6 @@ #include "../mp.h" -#include // std::size - #include EXTERN_C @@ -28,7 +26,7 @@ namespace logger::detail level == level_t::error ? "ERR\t" : "###\t"; - strcpy_s(buffer, SIZE, level_string); + strcpy_s(buffer, level_string); } template @@ -49,7 +47,7 @@ namespace logger::detail TIME_FIELDS time_fields; RtlTimeToTimeFields(&local_time, &time_fields); - sprintf_s(buffer, SIZE, "%02hd:%02hd:%02hd.%03hd\t", + sprintf_s(buffer, "%02hd:%02hd:%02hd.%03hd\t", time_fields.Hour, time_fields.Minute, time_fields.Second, time_fields.Milliseconds); } @@ -63,7 +61,7 @@ namespace logger::detail return; } - sprintf_s(buffer, SIZE, "#%u\t", mp::cpu_index()); + sprintf_s(buffer, "#%u\t", mp::cpu_index()); } template @@ -75,13 +73,13 @@ namespace logger::detail return; } - sprintf_s(buffer, SIZE, "%-40s\t", function); + sprintf_s(buffer, "%-40s\t", function); } template void make_log_message(char(&buffer)[SIZE], const char* format, va_list args) noexcept { - vsprintf_s(buffer, SIZE, format, args); + vsprintf_s(buffer, format, args); } void do_print(const char* message) noexcept @@ -114,7 +112,7 @@ namespace logger::detail auto thread_id = static_cast(reinterpret_cast(PsGetCurrentThreadId())); auto process_name = PsGetProcessImageFileName(PsGetCurrentProcess()); - sprintf_s(buffer, std::size(buffer), "%s%s%s%5u\t%5u\t%-15s\t%s%s\r\n", + sprintf_s(buffer, "%s%s%s%5u\t%5u\t%-15s\t%s%s\r\n", time, level_string, processor_number, process_id, thread_id, process_name, function_name, log_message); diff --git a/src/hvpp/hvpp/lib/win32/mm.cpp b/src/hvpp/hvpp/lib/win32/mm.cpp index db3c48d..62be811 100644 --- a/src/hvpp/hvpp/lib/win32/mm.cpp +++ b/src/hvpp/hvpp/lib/win32/mm.cpp @@ -4,7 +4,7 @@ #define HVPP_MEMORY_TAG 'ppvh' -namespace memory_manager::detail +namespace mm::detail { auto system_allocate(size_t size) noexcept -> void* { diff --git a/src/hvpp/hvpp/vcpu.cpp b/src/hvpp/hvpp/vcpu.cpp index da746af..cd4c6bf 100644 --- a/src/hvpp/hvpp/vcpu.cpp +++ b/src/hvpp/hvpp/vcpu.cpp @@ -15,7 +15,48 @@ namespace hvpp { // Public // -auto vcpu_t::initialize(vmexit_handler& handler) noexcept -> error_code_t +vcpu_t::vcpu_t(vmexit_handler& handler) noexcept + // + // Initialize VMXON region and VMCS. + // + : vmxon_{} + , vmcs_{} + + // + // This is not really needed. + // MSR bitmaps and I/O bitmaps are actually copied here from + // user-provided buffers (via msr_bitmap() and io_bitmap() methods) + // before they are enabled. + // + // , msr_bitmap_{} + // , io_bitmap_{} + + , handler_ { handler } + + // + // Signalize that this VCPU is turned off. + // + , state_{ state::off } + + // + // Let EPT be uninitialized. + // VM-exit handler is responsible for EPT setup. + // + , ept_{ nullptr } + , ept_count_{ 0 } + , ept_index_{ 0 } + + // + // Initialize pending-interrupt FIFO queue. + // + , pending_interrupt_first_{ 0 } + , pending_interrupt_count_{ 0 } + + // + // Well, this is also not necessary. + // This member is reset to "false" on each VM-exit in entry_host() method. + // + , suppress_rip_adjust_{ false } { // // Fill out initial stack with garbage. @@ -31,52 +72,6 @@ auto vcpu_t::initialize(vmexit_handler& handler) noexcept -> error_code_t guest_context_.clear(); exit_context_.clear(); - // - // Signalize that this VCPU is turned off. - // - state_ = vcpu_state::off; - - // - // Initialize VM-exit handler. - // - handler_ = &handler; - - // - // Initialize VMXON region and VMCS. - // - memset(&vmxon_, 0, sizeof(vmxon_)); - memset(&vmcs_, 0, sizeof(vmcs_)); - - // - // Let EPT be uninitialized. - // VM-exit handler is responsible for EPT setup. - // - ept_ = nullptr; - ept_count_ = 0; - ept_index_ = 0; - - // - // This is not really needed. - // MSR bitmaps and I/O bitmaps are actually copied here from - // user-provided buffers (via msr_bitmap() and io_bitmap() methods) - // before they are enabled. - // - // memset(&msr_bitmap_, 0, sizeof(msr_bitmap_)); - // memset(&io_bitmap_, 0, sizeof(io_bitmap_)); - // - - // - // Initialize pending-interrupt FIFO queue. - // - pending_interrupt_first_ = 0; - pending_interrupt_count_ = 0; - - // - // Well, this is also not necessary. - // This member is reset to "false" on each VM-exit in entry_host() method. - // - suppress_rip_adjust_ = false; - // // Assertions. // @@ -97,24 +92,108 @@ auto vcpu_t::initialize(vmexit_handler& handler) noexcept -> error_code_t static_assert(VCPU_RSP + VCPU_LAUNCH_CONTEXT_OFFSET == offsetof(vcpu_t, guest_context_)); static_assert(VCPU_RSP + VCPU_EXIT_CONTEXT_OFFSET == offsetof(vcpu_t, exit_context_)); }; - - return error_code_t{}; } -void vcpu_t::destroy() noexcept +vcpu_t::~vcpu_t() noexcept { + // + // When destructor is called, we should be only in one of the states + // metioned in the "assert". + // + // We can't be in: + // - "initializing", because "initializing" goes directly + // to "running" (on success) or "terminated" (on error) + // - "launching", because (as said above), "launching" goes + // directly to "running" + // - "terminating", because "terminating" goes directly to "terminated" + // + hvpp_assert(state_ == state::off || + state_ == state::running || + state_ == state::terminated); + + if (state_ == state::running) + { + stop(); + } +} + +auto vcpu_t::start() noexcept -> error_code_t +{ + // + // Launch of the VCPU is performed via similar principle as setjmp/longjmp: + // - Save current state here (guest_context_.capture() returns 0 if it's + // been called by original code - which is the same as state::off). + // - Call vcpu_t::vmx_enter(), which will enter VMX operation and set up VCMS. + // - Launch the VM. + // Note that vmlaunch() function should NOT return - the next instruction + // after vmlaunch should be at vcpu_t::entry_guest_() (vcpu.asm). + // - The guest will set guest_context_.rax = state::launching (see entry_guest()) + // and perform guest_context_.restore() (see vcpu.asm). + // That will catapult us back here. + // - We'll set state to state::running and exit this function. + // + + switch (static_cast(guest_context_.capture())) + { + case state::off: + if (auto err = vmx_enter()) + { + // + // There was either error with enabling VMX, setting up VMCS, + // or calling vmexit_handler::setup(). + // + return handle_vmx_enter_error(err); + } + + // + // Launch the VM (i.e.: execute "vmlaunch" instruction). + // If succeeded, this function does NOT return. + // + vmx::vmlaunch(); + + // + // If we got here, it means the "vmlaunch" failed. + // + return handle_vmx_launch_error(); + + case state::launching: + // + // The vcpu_t::entry_guest() successfully put this VCPU into + // "launching" state and vcpu.asm called guest_context_.restore(). + // This means that guest is running properly. + // + state_ = state::running; + return error_code_t{}; + + default: + // + // We shouldn't get here. + // + hvpp_assert(0); + return make_error_code_t(std::errc::permission_denied); + } +} + +void vcpu_t::stop() noexcept +{ + // + // Calling this method on any other state than "running" is considered + // error. + // + hvpp_assert(state_ == state::running); + // // Signalize that this VCPU is terminating. // - state_ = vcpu_state::terminating; + state_ = state::terminating; // // Notify the exit handler that we're about to terminate. // Exit handler should invoke VMEXIT in such way, that causes - // handler to call vcpu_t::terminate(); e.g. VMCALL with specific + // handler to call vcpu_t::vmx_leave(); e.g. VMCALL with specific // index. // - handler_->invoke_termination(*this); + handler_.teardown(*this); // // Destroy EPT. @@ -122,69 +201,82 @@ void vcpu_t::destroy() noexcept ept_disable(); } -void vcpu_t::launch() noexcept +auto vcpu_t::vmx_enter() noexcept -> error_code_t { - hvpp_assert(handler_ != nullptr); - // - // Launch of the VCPU is performed via similar principle as setjmp/longjmp: - // - Save current state here (guest_context_.capture() returns 0 if it's - // been called by original code - which is the same as vcpu_state::off). - // - Call setup(), which enters VMX operation, sets up VCMS and launches - // the VM. - // - The guest sets guest_context_.rax = vcpu_state::launching (see entry_guest()) - // and perform guest_context_.restore() (see vcpu.asm). - // That will catapult us back here. - // - We'll set state to vcpu_state::running and exit this function. + // Enter VMX operation, invalidate EPT and VPID, load VMCS, + // set VMCS fields and call handler's setup() method. // - switch (static_cast(guest_context_.capture())) - { - case vcpu_state::off: - setup(); - break; + if (auto err = load_vmxon()) + { return err; } - case vcpu_state::launching: - state_ = vcpu_state::running; - break; + if (auto err = load_vmcs()) + { return err; } - default: - hvpp_assert(0); - break; - } + if (auto err = setup_host()) + { return err; } + + if (auto err = setup_guest()) + { return err; } + + // + // #TODO: This function can fail, make it + // return appropriate error_code_t. + // + + handler_.setup(*this); + + return error_code_t{}; } -void vcpu_t::terminate() noexcept +void vcpu_t::vmx_leave() noexcept { - hvpp_assert(state_ != vcpu_state::off && state_ != vcpu_state::terminated); + // + // This method must be called either: + // - when initialization fails + // - when VCPU is terminating + // + hvpp_assert(state_ == state::initializing || + state_ == state::terminating); // - // Advance RIP before we exit VMX-root mode. This skips the "vmcall" - // instruction. + // If vmx_leave() is called in the initialization phase, + // we don't have to fix-up GDTR/IDTR/CR3, because: + // - no VM-exit occured yet + // - guest_gdtr/guest_idtr/guest_cr3 may still be uninitialized // - exit_context_.rip += exit_instruction_length(); - // - // When running in VMX-root mode, the processor will set limits of the - // GDT and IDT to 0xffff (notice that there are no Host VMCS fields to - // set these values). This causes problems with PatchGuard, which will - // believe that the GDTR and IDTR have been modified by malware, and - // eventually crash the system. Since we know what the original state - // of the GDTR and IDTR was, simply restore it now. - // - write(guest_gdtr()); - write(guest_idtr()); + if (state_ != state::initializing) + { + // + // Advance RIP before we exit VMX-root mode. This skips the "vmcall" + // instruction. + // + exit_context_.rip += exit_instruction_length(); - // - // Our callback routine may have interrupted an arbitrary user process, - // and therefore not a thread running with a systemwide page directory. - // Therefore if we return back to the original caller after turning off - // VMX, it will keep our current "host" CR3 value which we set on entry - // to the PML4 of the SYSTEM process. We want to return back with the - // correct value of the "guest" CR3, so that the currently executing - // process continues to run with its expected address space mappings. - // - write(guest_cr3()); + // + // When running in VMX-root mode, the processor will set limits of the + // GDT and IDT to 0xffff (notice that there are no Host VMCS fields to + // set these values). This causes problems with PatchGuard, which will + // believe that the GDTR and IDTR have been modified by malware, and + // eventually crash the system. Since we know what the original state + // of the GDTR and IDTR was, simply restore it now. + // + write(guest_gdtr()); + write(guest_idtr()); + + // + // Our callback routine may have interrupted an arbitrary user process, + // and therefore not a thread running with a systemwide page directory. + // Therefore if we return back to the original caller after turning off + // VMX, it will keep our current "host" CR3 value which we set on entry + // to the PML4 of the SYSTEM process. We want to return back with the + // correct value of the "guest" CR3, so that the currently executing + // process continues to run with its expected address space mappings. + // + write(guest_cr3()); + } // // Software can use the INVVPID instruction with the "all-context" @@ -226,7 +318,7 @@ void vcpu_t::terminate() noexcept // // Signalize that this VCPU has terminated. // - state_ = vcpu_state::terminated; + state_ = state::terminated; } void vcpu_t::ept_enable(uint16_t count /* = 1 */) noexcept @@ -238,11 +330,7 @@ void vcpu_t::ept_enable(uint16_t count /* = 1 */) noexcept // ept_ = new ept_t[count]; ept_count_ = count; - - for (uint16_t i = 0; i < count; i += 1) - { - ept_[i].initialize(); - } + hvpp_assert(ept_ != nullptr); // // Enable EPT. @@ -264,26 +352,21 @@ void vcpu_t::ept_disable() noexcept return; } - // - // Destroy EPT. - // - for (uint16_t i = 0; i < ept_count_; i++) - { - ept_[i].destroy(); - } - - delete[] ept_; - ept_ = nullptr; - // // Disable EPT functionality. // + if (state_ != state::terminated) + { + auto procbased_ctls2 = processor_based_controls2(); + procbased_ctls2.enable_ept = false; + processor_based_controls2(procbased_ctls2); + } + // - // #TODO: VMX is already disabled when we're here. + // Destroy EPT. // -// auto procbased_ctls2 = processor_based_controls2(); -// procbased_ctls2.enable_ept = false; -// processor_based_controls2(procbased_ctls2); + delete[] ept_; + ept_ = nullptr; } auto vcpu_t::ept_index() noexcept -> uint16_t @@ -320,40 +403,47 @@ void vcpu_t::suppress_rip_adjust() noexcept // Private // -void vcpu_t::error() noexcept +auto vcpu_t::handle_common_error(error_code_t err) noexcept -> error_code_t { - vmx::instruction_error instruction_error = exit_instruction_error(); - hvpp_error("error: %p (%s)\n", instruction_error, vmx::instruction_error_to_string(instruction_error)); - ia32_asm_int3(); - terminate(); + // + // Signalize that this VCPU is terminated and leave the VMX operation. + // + state_ = state::terminated; + vmx_leave(); + + return err; } -void vcpu_t::setup() noexcept +auto vcpu_t::handle_vmx_enter_error(error_code_t err) noexcept -> error_code_t { - // - // Enter VMX operation, invalidate EPT and VPID, load VMCS, - // set VMCS fields, call handler's setup() method, and launch - // the VM. This function should NOT return - the next instruction - // after vmlaunch should be at vcpu_t::entry_guest_ (vcpu.asm). - // - load_vmxon(); - load_vmcs(); - - setup_host(); - setup_guest(); - - handler_->setup(*this); - - vmx::vmlaunch(); - - // - // If we got here, something wrong has happened. - // - error(); + return handle_common_error(err); } -void vcpu_t::load_vmxon() noexcept +auto vcpu_t::handle_vmx_launch_error() noexcept -> error_code_t { + // + // Fetch VMX error from the VMCS and print it to the debugger. + // + const auto instruction_error = exit_instruction_error(); + hvpp_error("error: %u (%s)\n", + static_cast(instruction_error), + vmx::instruction_error_to_string(instruction_error)); + + // + // If debugger is attached, break into it. + // + if (debugger::is_enabled()) + { + debugger::breakpoint(); + } + + return handle_common_error(make_error_code_t(std::errc::permission_denied)); +} + +auto vcpu_t::load_vmxon() noexcept -> error_code_t +{ + hvpp_assert(state_ == state::off); + // // In VMX operation, processors may fix certain bits in CR0 and CR4 // to specific values and not support other values. VMXON fails if @@ -371,71 +461,68 @@ void vcpu_t::load_vmxon() noexcept // write the VMCS revision identifier to the VMXON region. // (ref: Vol3C[24.11.5(VMXON Region)]) // - auto vmx_basic = msr::read(); + const auto vmx_basic = msr::read(); vmxon_.revision_id = vmx_basic.vmcs_revision_id; // // Enter VMX operation. // - - if (vmx::on(pa_t::from_va(&vmxon_)) == vmx::error_code::success) + if (vmx::on(pa_t::from_va(&vmxon_)) != vmx::error_code::success) { - state_ = vcpu_state::initializing; - - // - // Software can use the INVVPID instruction with the "all-context" - // INVVPID type immediately after execution of the VMXON instruction - // or immediately prior to execution of the VMXOFF instruction. - // Either prevents potentially undesired retention of information - // cached from paging structures between separate uses of VMX operation. - // (ref: Vol3C[28.3.3.3(Guidelines for Use of the INVVPID Instruction)]) - // - vmx::invvpid_all_contexts(); - - // - // Software can use the INVEPT instruction with the "all-context" - // INVEPT type immediately after execution of the VMXON instruction - // or immediately prior to execution of the VMXOFF instruction. - // Either prevents potentially undesired retention of information - // cached from EPT paging structures between separate uses of VMX operation. - // (ref: Vol3C[28.3.3.4(Guidelines for Use of the INVEPT Instruction)]) - // - vmx::invept_all_contexts(); - } - else - { - state_ = vcpu_state::terminated; - error(); + return make_error_code_t(std::errc::permission_denied); } + + state_ = state::initializing; + + // + // Software can use the INVVPID instruction with the "all-context" + // INVVPID type immediately after execution of the VMXON instruction + // or immediately prior to execution of the VMXOFF instruction. + // Either prevents potentially undesired retention of information + // cached from paging structures between separate uses of VMX operation. + // (ref: Vol3C[28.3.3.3(Guidelines for Use of the INVVPID Instruction)]) + // + vmx::invvpid_all_contexts(); + + // + // Software can use the INVEPT instruction with the "all-context" + // INVEPT type immediately after execution of the VMXON instruction + // or immediately prior to execution of the VMXOFF instruction. + // Either prevents potentially undesired retention of information + // cached from EPT paging structures between separate uses of VMX operation. + // (ref: Vol3C[28.3.3.4(Guidelines for Use of the INVEPT Instruction)]) + // + vmx::invept_all_contexts(); + + return error_code_t{}; } -void vcpu_t::load_vmcs() noexcept +auto vcpu_t::load_vmcs() noexcept -> error_code_t { - hvpp_assert(state_ == vcpu_state::initializing); + hvpp_assert(state_ == state::initializing); - auto vmx_basic = msr::read(); + const auto vmx_basic = msr::read(); vmcs_.revision_id = vmx_basic.vmcs_revision_id; // // Set VMCS to "clear" state and make the VMCS active. // See Vol3C[24(Virtual Machine Control Structures)] for more information. // + if (vmx::vmclear(pa_t::from_va(&vmcs_)) != vmx::error_code::success || + vmx::vmptrld(pa_t::from_va(&vmcs_)) != vmx::error_code::success) + { + return make_error_code_t(std::errc::permission_denied); + } - if (vmx::vmclear(pa_t::from_va(&vmcs_)) == vmx::error_code::success && - vmx::vmptrld(pa_t::from_va(&vmcs_)) == vmx::error_code::success) - { - /* NOTHING */; - } - else - { - error(); - } + return error_code_t{}; } -void vcpu_t::setup_host() noexcept +auto vcpu_t::setup_host() noexcept -> error_code_t { + hvpp_assert(state_ == state::initializing); + // - // Sets up state of the CPU each time when VM-exit is triggered. + // Sets up what state will the CPU have when VM-exit is triggered. // Notice how these fields mainly consist of descriptor registers, // control registers, and segment registers. This effectively allows us // to run hypervisor in completely separate address space from the OS. @@ -449,14 +536,15 @@ void vcpu_t::setup_host() noexcept // (RAX, RBX, ...) or SSE registers - these registers are preserved from the // guest. // - auto gdtr = read(); + const auto gdtr = read(); + const auto idtr = read(); // // Note that we're setting just base address of GDTR and IDTR. // The limit of these descriptors is fixed at 0xffff for VMX operations. // host_gdtr(gdtr); - host_idtr(read()); + host_idtr(idtr); // // Note that we're setting just selectors (base address - except for FS and @@ -483,10 +571,14 @@ void vcpu_t::setup_host() noexcept // host_rsp(reinterpret_cast(std::end(stack_.data))); host_rip(reinterpret_cast(&vcpu_t::entry_host_)); + + return error_code_t{}; } -void vcpu_t::setup_guest() noexcept +auto vcpu_t::setup_guest() noexcept -> error_code_t { + hvpp_assert(state_ == state::initializing); + // // VPIDs provide a way for software to identify to the processor the // address spaces for different "virtual processors." The processor @@ -589,10 +681,14 @@ void vcpu_t::setup_guest() noexcept // guest_rsp(reinterpret_cast(std::end(stack_.data))); guest_rip(reinterpret_cast(&vcpu_t::entry_guest_)); + + return error_code_t{}; } void vcpu_t::entry_host() noexcept { + hvpp_assert(state_ == state::running); + // // Reset RIP-adjust flag. // @@ -623,13 +719,16 @@ void vcpu_t::entry_host() noexcept { // - // Because we're in VMX-root mode, the system memory allocator - // has to be disabled. + // Because we're in VMX-root mode, we can't use the system allocator + // (ExAllocatePoolWithTag/ExFreePoolWithTag). + // This line will enable "custom allocator" that will be used whenever + // "new"/"delete" operator is executed. + // See lib/mm.cpp for more details. // - memory_manager::allocator_guard _; + mm::allocator_guard _; - auto captured_rsp = exit_context_.rsp; - auto captured_rflags = exit_context_.rflags; + const auto captured_rsp = exit_context_.rsp; + const auto captured_rflags = exit_context_.rflags; { exit_context_.rsp = guest_rsp(); @@ -649,9 +748,9 @@ void vcpu_t::entry_host() noexcept stack_.machine_frame.rsp = exit_context_.rsp; { - handler_->handle(*this); + handler_.handle(*this); - if (state_ == vcpu_state::terminated) + if (state_ == state::terminated) { // // At this point we're not in the VMX-root mode (vmxoff has been @@ -686,7 +785,9 @@ exit: void vcpu_t::entry_guest() noexcept { - guest_context_.rax = static_cast(vcpu_state::launching); + // hvpp_assert(state_ == state::initializing); + + guest_context_.rax = static_cast(state::launching); } } diff --git a/src/hvpp/hvpp/vcpu.h b/src/hvpp/hvpp/vcpu.h index fe82011..9fb6bce 100644 --- a/src/hvpp/hvpp/vcpu.h +++ b/src/hvpp/hvpp/vcpu.h @@ -1,9 +1,8 @@ #pragma once #include "ept.h" +#include "interrupt.h" #include "ia32/arch.h" -#include "ia32/exception.h" -#include "ia32/vmx.h" #include "lib/error.h" @@ -15,138 +14,17 @@ using namespace ia32; class vmexit_handler; -class interrupt_t final -{ - public: - constexpr interrupt_t(vmx::interrupt_type interrupt_type, exception_vector exception_vector, int rip_adjust = -1) noexcept - : interrupt_t(interrupt_type, exception_vector, exception_error_code_t{}, false, rip_adjust) { } - - constexpr interrupt_t(vmx::interrupt_type interrupt_type, exception_vector exception_vector, exception_error_code_t exception_code, int rip_adjust = -1) noexcept - : interrupt_t(interrupt_type, exception_vector, exception_code, true, rip_adjust) { } - - constexpr interrupt_t(const interrupt_t& other) noexcept = default; - constexpr interrupt_t(interrupt_t&& other) noexcept = default; - constexpr interrupt_t& operator=(const interrupt_t& other) noexcept = default; - constexpr interrupt_t& operator=(interrupt_t&& other) noexcept = default; - - constexpr auto vector() const noexcept { return static_cast(info_.vector); } - constexpr auto type() const noexcept { return static_cast(info_.type); } - constexpr bool error_code_valid() const noexcept { return info_.error_code_valid; } - constexpr bool nmi_unblocking() const noexcept { return info_.nmi_unblocking; } - constexpr bool valid() const noexcept { return info_.valid; } - constexpr auto error_code() const noexcept { return error_code_; } - constexpr int rip_adjust() const noexcept { return rip_adjust_; } - - private: - friend class vcpu_t; - - constexpr interrupt_t() noexcept - : info_(), error_code_(), rip_adjust_() { } - - constexpr interrupt_t(vmx::interrupt_type interrupt_type, exception_vector exception_vector, exception_error_code_t exception_code, bool exception_code_valid, int rip_adjust) noexcept - : error_code_(exception_code), rip_adjust_(rip_adjust) - { - info_.flags = 0; - - info_.vector = static_cast(exception_vector); - info_.type = static_cast(interrupt_type); - info_.valid = true; - - // - // Final sanitization of the following fields takes place - // in vcpu::interrupt_inject_force(). - // - - info_.error_code_valid = exception_code_valid; - } - - vmx::interrupt_info_t info_; - exception_error_code_t error_code_; - int rip_adjust_; -}; - -enum class vcpu_state -{ - // - // VCPU is unitialized. - // - off, - - // - // VCPU is in VMX-root mode; host & guest VMCS is being initialized. - // - initializing, - - // - // VCPU successfully performed its initial VMENTRY. - // - launching, - - // - // VCPU is running. - // - running, - - // - // VCPU is terminating; vcpu::destroy has been called. - // - terminating, - - // - // VCPU is terminated, VMX-root mode has been left. - // - terminated, -}; - -// -// Definition of the stack structure. -// See vcpu.asm for more details. -// - -static constexpr int vcpu_stack_size = 0x8000; - -struct vcpu_stack_t -{ - struct machine_frame_t - { - uint64_t rip; - uint64_t cs; - uint64_t eflags; - uint64_t rsp; - uint64_t ss; - }; - - struct shadow_space_t - { - uint64_t dummy[4]; - }; - - union - { - uint8_t data[vcpu_stack_size]; - - struct - { - uint8_t dummy[vcpu_stack_size - - sizeof(shadow_space_t) - - sizeof(machine_frame_t)]; - shadow_space_t shadow_space; - machine_frame_t machine_frame; - }; - }; -}; - -static_assert(sizeof(vcpu_stack_t) == vcpu_stack_size); -static_assert(sizeof(vcpu_stack_t::shadow_space_t) == 32); - class vcpu_t final { public: - auto initialize(vmexit_handler& handler) noexcept -> error_code_t; - void destroy() noexcept; + vcpu_t(vmexit_handler& handler) noexcept; + ~vcpu_t() noexcept; - void launch() noexcept; - void terminate() noexcept; + auto start() noexcept -> error_code_t; + void stop() noexcept; + + auto vmx_enter() noexcept -> error_code_t; + void vmx_leave() noexcept; void ept_enable(uint16_t count = 1) noexcept; void ept_disable() noexcept; @@ -168,7 +46,7 @@ class vcpu_t final // Make storage for up-to 16 pending interrupts. // In practice I haven't seen more than 2 pending interrupts. // - static constexpr int pending_interrupt_queue_size = 16; + static constexpr auto pending_interrupt_queue_size = 16; auto interrupt_info() const noexcept -> interrupt_t; auto idt_vectoring_info() const noexcept -> interrupt_t; @@ -372,14 +250,15 @@ class vcpu_t final // private: - void error() noexcept; - void setup() noexcept; + auto handle_common_error(error_code_t err) noexcept -> error_code_t; + auto handle_vmx_enter_error(error_code_t err) noexcept -> error_code_t; + auto handle_vmx_launch_error() noexcept -> error_code_t; - void load_vmxon() noexcept; - void load_vmcs() noexcept; + auto load_vmxon() noexcept -> error_code_t; + auto load_vmcs() noexcept -> error_code_t; - void setup_host() noexcept; - void setup_guest() noexcept; + auto setup_host() noexcept -> error_code_t; + auto setup_guest() noexcept -> error_code_t; void entry_host() noexcept; void entry_guest() noexcept; @@ -387,11 +266,85 @@ class vcpu_t final static void entry_host_() noexcept; static void entry_guest_() noexcept; + enum class state + { + // + // VCPU is unitialized. + // + off, + + // + // VCPU is in VMX-root mode; host & guest VMCS is being initialized. + // + initializing, + + // + // VCPU successfully performed its initial VMENTRY. + // + launching, + + // + // VCPU is running. + // + running, + + // + // VCPU is terminating; vcpu::destroy has been called. + // + terminating, + + // + // VCPU is terminated, VMX-root mode has been left. + // + terminated, + }; + + // + // Definition of the stack structure. + // See vcpu.asm for more details. + // + + struct stack_t + { + static constexpr auto size = 0x8000; + + struct machine_frame_t + { + uint64_t rip; + uint64_t cs; + uint64_t eflags; + uint64_t rsp; + uint64_t ss; + }; + + struct shadow_space_t + { + uint64_t dummy[4]; + }; + + union + { + uint8_t data[size]; + + struct + { + uint8_t dummy[size + - sizeof(shadow_space_t) + - sizeof(machine_frame_t)]; + shadow_space_t shadow_space; + machine_frame_t machine_frame; + }; + }; + }; + + static_assert(sizeof(stack_t) == stack_t::size); + static_assert(sizeof(stack_t::shadow_space_t) == 32); + // // If you reorder following three members (stack, guest context and exit // context), you have to edit offsets in vcpu.asm. // - vcpu_stack_t stack_; + stack_t stack_; context_t guest_context_; context_t exit_context_; @@ -409,8 +362,8 @@ class vcpu_t final // fxsave_area_t fxsave_area_; - vmexit_handler* handler_; - vcpu_state state_; + vmexit_handler& handler_; + state state_; ept_t* ept_; uint16_t ept_count_; diff --git a/src/hvpp/hvpp/vmexit.cpp b/src/hvpp/hvpp/vmexit.cpp index 36a5088..6cc28e8 100644 --- a/src/hvpp/hvpp/vmexit.cpp +++ b/src/hvpp/hvpp/vmexit.cpp @@ -82,32 +82,22 @@ vmexit_handler::~vmexit_handler() noexcept } -auto vmexit_handler::initialize() noexcept -> error_code_t -{ - return error_code_t{}; -} - -void vmexit_handler::destroy() noexcept -{ - -} - void vmexit_handler::setup(vcpu_t& vp) noexcept { (void)(vp); } +void vmexit_handler::teardown(vcpu_t& vp) noexcept +{ + (void)(vp); +} + void vmexit_handler::handle(vcpu_t& vp) noexcept { - auto handler_index = static_cast(vp.exit_reason()); + const auto handler_index = static_cast(vp.exit_reason()); (this->*handlers_[handler_index])(vp); } -void vmexit_handler::invoke_termination(vcpu_t& vp) noexcept -{ - (void)(vp); -} - // // "Do-nothing" handlers for all VM-exits. // VMX-instruction related VM-exits (VMREAD, VMWRITE, INVEPT, ...) diff --git a/src/hvpp/hvpp/vmexit.h b/src/hvpp/hvpp/vmexit.h index 6d83c62..400ac75 100644 --- a/src/hvpp/hvpp/vmexit.h +++ b/src/hvpp/hvpp/vmexit.h @@ -116,78 +116,53 @@ struct vmexit_storage_t class vmexit_handler { public: - // - // Predefined interrupt structures. - // Helpful when injecting events. + // Constructor & destructor. // - - static constexpr auto interrupt_nmi = interrupt_t { - vmx::interrupt_type::nmi, - exception_vector::nmi_interrupt - }; - - static constexpr auto interrupt_debug = interrupt_t { - vmx::interrupt_type::hardware_exception, - exception_vector::debug - }; - - static constexpr auto interrupt_invalid_opcode = interrupt_t { - vmx::interrupt_type::hardware_exception, - exception_vector::invalid_opcode - }; - - static constexpr auto interrupt_general_protection = interrupt_t { - vmx::interrupt_type::hardware_exception, - exception_vector::general_protection, - exception_error_code_t{} - }; - - public: - vmexit_handler() noexcept; - ~vmexit_handler() noexcept; - + // Note: + // Constructor & destructor is guaranteed to NOT be called + // in VMX-root mode. + // Therefore, avoid execution of any VMX instructions there. // - // Avoid execution of any VMX instructions here, because - // this method is not guaranteed to be called in the VMX-root - // mode. - // - virtual auto initialize() noexcept -> error_code_t; - - // - // Avoid execution of any VMX instructions here, because - // this method is not guaranteed to be called in the VMX-root - // mode. - // - virtual void destroy() noexcept; + vmexit_handler() noexcept; + virtual ~vmexit_handler() noexcept; // // This method allows you to set up VCPU state before VMLAUNCH. // Use this method for setting up VMCS. // + // Note: + // This method is guaranteed to be called in VMX-root mode. + // virtual void setup(vcpu_t& vp) noexcept; + // + // This method is called from vcpu_t::stop() method. + // It should be responsible for initiating VM tear-down + // and disabling the VMX mode. + // + // Note: + // This method is guaranteed to NOT be called in VMX-root mode. + // Therefore, avoid execution of any VMX instructions there + // (including VMXOFF). + // + // If you wish to execute code in VMX-root mode when this method + // is called, use "vmcall". + // + virtual void teardown(vcpu_t& vp) noexcept; + // // This method is called on every VM-exit. // By default this method delegates the execution control // to related VM-exit method (i.e.: for "execute CPUID VM-exit" // it calls handle_execute_cpuid() method). // - // Keep in mind that this method is not called for VM-exits - // that are not enabled in the VMCS. + // Note: + // Keep in mind that this method is not called for VM-exits + // that are not enabled in the VMCS. // virtual void handle(vcpu_t& vp) noexcept; - // - // This method is called from vcpu_t::destroy() method. - // It should be responsible for initiating VM tear-down - // and disabling the VMX mode. - // - // Note that this method is not called in VMX-root mode, - // therefore you should avoid usage of VMXOFF instruction. - // - virtual void invoke_termination(vcpu_t& vp) noexcept; - protected: // // Separate handlers for each VM-exit reason. @@ -271,7 +246,7 @@ class vmexit_handler protected: using handler_fn_t = void (vmexit_handler::*)(vcpu_t&); - std::array handlers_; + const std::array handlers_; }; } diff --git a/src/hvpp/hvpp/vmexit/vmexit_c_wrapper.cpp b/src/hvpp/hvpp/vmexit/vmexit_c_wrapper.cpp index f92ed5a..7647ed0 100644 --- a/src/hvpp/hvpp/vmexit/vmexit_c_wrapper.cpp +++ b/src/hvpp/hvpp/vmexit/vmexit_c_wrapper.cpp @@ -4,7 +4,7 @@ namespace hvpp { -auto vmexit_c_wrapper_handler::initialize(const c_handler_array_t& c_handlers, void* context) noexcept -> error_code_t +vmexit_c_wrapper_handler::vmexit_c_wrapper_handler(const c_handler_array_t& c_handlers, void* context) noexcept { // // Make local copy of the C-handlers. @@ -12,11 +12,9 @@ auto vmexit_c_wrapper_handler::initialize(const c_handler_array_t& c_handlers, v c_handlers_ = c_handlers; context_ = context; - - return error_code_t{}; } -void vmexit_c_wrapper_handler::destroy() noexcept +vmexit_c_wrapper_handler::~vmexit_c_wrapper_handler() noexcept { } @@ -36,11 +34,11 @@ void vmexit_c_wrapper_handler::setup(vcpu_t& vp) noexcept void vmexit_c_wrapper_handler::handle(vcpu_t& vp) noexcept { - auto exit_reason = vp.exit_reason(); - auto exit_reason_index = static_cast(exit_reason); + const auto exit_reason = vp.exit_reason(); + const auto exit_reason_index = static_cast(exit_reason); - auto cpp_handler = handlers_[exit_reason_index]; - auto c_handler = c_handlers_[exit_reason_index]; + const auto cpp_handler = handlers_[exit_reason_index]; + const auto c_handler = c_handlers_[exit_reason_index]; if (c_handler) { @@ -74,9 +72,9 @@ void vmexit_c_wrapper_handler::handle_passthrough(passthrough_context* context) // from the pass-trough context and call that method. // - auto handler_instance = context->handler_instance; - auto handler_method = context->handler_method; - auto& vp = *context->vcpu; + const auto handler_instance = context->handler_instance; + const auto handler_method = context->handler_method; + auto& vp = *context->vcpu; (handler_instance->*handler_method)(vp); } diff --git a/src/hvpp/hvpp/vmexit/vmexit_c_wrapper.h b/src/hvpp/hvpp/vmexit/vmexit_c_wrapper.h index 2bb3904..b15440b 100644 --- a/src/hvpp/hvpp/vmexit/vmexit_c_wrapper.h +++ b/src/hvpp/hvpp/vmexit/vmexit_c_wrapper.h @@ -18,11 +18,10 @@ class vmexit_c_wrapper_handler using c_handler_array_t = std::array; - auto initialize(const c_handler_array_t& c_handlers, void* context = nullptr) noexcept -> error_code_t; - void destroy() noexcept; + vmexit_c_wrapper_handler(const c_handler_array_t& c_handlers, void* context = nullptr) noexcept; + ~vmexit_c_wrapper_handler() noexcept override; void setup(vcpu_t& vp) noexcept override; - void handle(vcpu_t& vp) noexcept override; private: diff --git a/src/hvpp/hvpp/vmexit/vmexit_dbgbreak.cpp b/src/hvpp/hvpp/vmexit/vmexit_dbgbreak.cpp index 968ce86..b2cdb0b 100644 --- a/src/hvpp/hvpp/vmexit/vmexit_dbgbreak.cpp +++ b/src/hvpp/hvpp/vmexit/vmexit_dbgbreak.cpp @@ -19,10 +19,9 @@ namespace hvpp { -auto vmexit_dbgbreak_handler::initialize() noexcept -> error_code_t +vmexit_dbgbreak_handler::vmexit_dbgbreak_handler() noexcept + : storage_{} { - memset(&storage_, 0, sizeof(storage_)); - // // Uncomment this to break on IN 0x64 instruction. // Breakpoints on specific VM-exit reasons can be enabled/disabled @@ -30,18 +29,16 @@ auto vmexit_dbgbreak_handler::initialize() noexcept -> error_code_t // // storage_.io_in[0x64] = true; // - - return error_code_t{}; } -void vmexit_dbgbreak_handler::destroy() noexcept +vmexit_dbgbreak_handler::~vmexit_dbgbreak_handler() noexcept { } void vmexit_dbgbreak_handler::handle(vcpu_t& vp) noexcept { - auto exit_reason = vp.exit_reason(); + const auto exit_reason = vp.exit_reason(); hvpp_break_if(storage_.vmexit[static_cast(exit_reason)]); diff --git a/src/hvpp/hvpp/vmexit/vmexit_dbgbreak.h b/src/hvpp/hvpp/vmexit/vmexit_dbgbreak.h index f98151f..fe4e428 100644 --- a/src/hvpp/hvpp/vmexit/vmexit_dbgbreak.h +++ b/src/hvpp/hvpp/vmexit/vmexit_dbgbreak.h @@ -21,8 +21,8 @@ class vmexit_dbgbreak_handler : public vmexit_handler { public: - auto initialize() noexcept -> error_code_t; - void destroy() noexcept; + vmexit_dbgbreak_handler() noexcept; + ~vmexit_dbgbreak_handler() noexcept override; void handle(vcpu_t& vp) noexcept override; diff --git a/src/hvpp/hvpp/vmexit/vmexit_passthrough.cpp b/src/hvpp/hvpp/vmexit/vmexit_passthrough.cpp index f43c4df..227a8f7 100644 --- a/src/hvpp/hvpp/vmexit/vmexit_passthrough.cpp +++ b/src/hvpp/hvpp/vmexit/vmexit_passthrough.cpp @@ -48,9 +48,11 @@ void vmexit_passthrough_handler::setup(vcpu_t& vp) noexcept vp.guest_dr7(read()); vp.guest_rflags(read()); - auto gdtr = read(); + const auto gdtr = read(); + const auto idtr = read(); + vp.guest_gdtr(gdtr); - vp.guest_idtr(read()); + vp.guest_idtr(idtr); vp.guest_cs(segment_t{ gdtr, read() }); vp.guest_ds(segment_t{ gdtr, read() }); vp.guest_es(segment_t{ gdtr, read() }); @@ -61,7 +63,7 @@ void vmexit_passthrough_handler::setup(vcpu_t& vp) noexcept vp.guest_ldtr(segment_t{ gdtr, read() }); } -void vmexit_passthrough_handler::invoke_termination(vcpu_t& vp) noexcept +void vmexit_passthrough_handler::teardown(vcpu_t& vp) noexcept { (void)(vp); @@ -207,7 +209,7 @@ void vmexit_passthrough_handler::handle_execute_vmcall(vcpu_t& vp) noexcept if (vp.exit_context().rcx == vmcall_terminate_id && vp.guest_cs().selector.request_privilege_level == 0) { - vp.terminate(); + vp.vmx_leave(); } else if (vp.exit_context().rcx == vmcall_breakpoint_id) { @@ -227,136 +229,151 @@ void vmexit_passthrough_handler::handle_mov_cr(vcpu_t& vp) noexcept switch (exit_qualification.access_type) { case vmx::exit_qualification_mov_cr_t::access_to_cr: + { switch (exit_qualification.cr_number) { case 0: + { vp.guest_cr0(cr0_t{ gp_register }); vp.cr0_shadow(cr0_t{ gp_register }); + break; + } case 3: + { + // + // If CR4.PCIDE = 1, bit 63 of the source operand to MOV + // to CR3 determines whether the instruction invalidates + // entries in the TLBs and the paging-structure caches. + // The instruction does not modify bit 63 of CR3, which + // is reserved and always 0. + // (ref: Vol2B(MOV-Move to/from Control Registers) + // (see: Vol3A[4.10.4.1(Operations that Invalidate TLBs and Paging-Structure Caches)] + // + auto cr3 = cr3_t{ gp_register }; + if (vp.guest_cr4().pcid_enable) { // - // If CR4.PCIDE = 1, bit 63 of the source operand to MOV - // to CR3 determines whether the instruction invalidates - // entries in the TLBs and the paging-structure caches. - // The instruction does not modify bit 63 of CR3, which - // is reserved and always 0. - // (ref: Vol2B(MOV-Move to/from Control Registers) - // (see: Vol3A[4.10.4.1(Operations that Invalidate TLBs and Paging-Structure Caches)] + // Equivalent to: + // gp_register &= ~(1ull << 63); // - auto cr3 = cr3_t{ gp_register }; - if (vp.guest_cr4().pcid_enable) - { - // - // Equivalent to: - // gp_register &= ~(1ull << 63); - // - cr3.pcid_invalidate = false; - } - vp.guest_cr3(cr3); - - // - // Some instructions invalidate all entries in the TLBs - // and paging-structure caches-except for global translations. - // An example is the MOV to CR3 instruction. - // Emulation of such an instruction may require execution of - // the INVVPID instruction as follows: - // - The INVVPID type is single-context-retaining-globals (3). - // - The VPID in the INVVPID descriptor is the one assigned to - // the virtual processor whose execution is being emulated. - // (ref: Vol3C[28.3.3.3(Guidelines for Use of the INVVPID Instruction)]) - // - vmx::invvpid_single_context_retaining_globals(vp.vcpu_id()); + cr3.pcid_invalidate = false; } + vp.guest_cr3(cr3); + + // + // Some instructions invalidate all entries in the TLBs + // and paging-structure caches-except for global translations. + // An example is the MOV to CR3 instruction. + // Emulation of such an instruction may require execution of + // the INVVPID instruction as follows: + // - The INVVPID type is single-context-retaining-globals (3). + // - The VPID in the INVVPID descriptor is the one assigned to + // the virtual processor whose execution is being emulated. + // (ref: Vol3C[28.3.3.3(Guidelines for Use of the INVVPID Instruction)]) + // + vmx::invvpid_single_context_retaining_globals(vp.vcpu_id()); + break; + } case 4: + { + // + // Some instructions invalidate all entries in the TLBs and + // paging-structure caches-including for global translations. + // An example is the MOV to CR4 instruction if the value of + // value of bit 4 (page global enable-PGE) is changing. + // Emulation of such an instruction may require execution of + // the INVVPID instruction as follows: + // - The INVVPID type is single-context (1). + // - The VPID in the INVVPID descriptor is the one assigned to + // the virtual processor whose execution is being emulated. + // (ref: Vol3C[28.3.3.3(Guidelines for Use of the INVVPID Instruction)]) + // + cr4_t new_cr4 = cr4_t{ gp_register }; + bool pge_changed = new_cr4.page_global_enable != vp.guest_cr4().page_global_enable; + + if (pge_changed) { - // - // Some instructions invalidate all entries in the TLBs and - // paging-structure caches-including for global translations. - // An example is the MOV to CR4 instruction if the value of - // value of bit 4 (page global enable-PGE) is changing. - // Emulation of such an instruction may require execution of - // the INVVPID instruction as follows: - // - The INVVPID type is single-context (1). - // - The VPID in the INVVPID descriptor is the one assigned to - // the virtual processor whose execution is being emulated. - // (ref: Vol3C[28.3.3.3(Guidelines for Use of the INVVPID Instruction)]) - // - cr4_t new_cr4 = cr4_t{ gp_register }; - bool pge_changed = new_cr4.page_global_enable != vp.guest_cr4().page_global_enable; - - if (pge_changed) - { - vmx::invvpid_single_context(vp.vcpu_id()); - } - - vp.guest_cr4(new_cr4); - vp.cr4_shadow(new_cr4); + vmx::invvpid_single_context(vp.vcpu_id()); } + + vp.guest_cr4(new_cr4); + vp.cr4_shadow(new_cr4); + break; + } case 8: + { /* unimplemented */ break; + } } + break; + } case vmx::exit_qualification_mov_cr_t::access_from_cr: + { switch (exit_qualification.cr_number) { case 3: gp_register = vp.guest_cr3().flags; break; case 8: /* unimplemented */ break; } + break; + } case vmx::exit_qualification_mov_cr_t::access_clts: - { - auto cr0 = vp.guest_cr0(); - cr0.task_switched = false; - vp.guest_cr0(cr0); - vp.cr0_shadow(cr0); - } + { + auto cr0 = vp.guest_cr0(); + cr0.task_switched = false; + vp.guest_cr0(cr0); + vp.cr0_shadow(cr0); + break; + } case vmx::exit_qualification_mov_cr_t::access_lmsw: - { - auto msw = static_cast(exit_qualification.lmsw_source_data); - auto cr0 = vp.guest_cr0(); + { + auto msw = static_cast(exit_qualification.lmsw_source_data); + auto cr0 = vp.guest_cr0(); - // - // Loads the source operand into the machine status word, - // bits 0 through 15 of register CR0. The source operand - // can be a 16-bit general-purpose register or a memory - // location. Only the low-order 4 bits of the source - // operand (which contains the PE, MP, EM, and TS flags) - // are loaded into CR0. The PG, CD, NW, AM, WP, NE, and - // ET flags of CR0 are not affected. The operand-size - // attribute has no effect on this instruction. If the - // PE flag of the source operand (bit 0) is set to 1, the - // instruction causes the processor to switch to protected - // mode. While in protected mode, the LMSW instruction - // cannot be used to clear the PE flag and force a switch - // back to real-address mode. - // (ref: Vol2A[(LMSW-Load Machine Status Word)]) - // - // TL;DR: - // CR0[0:3] <- SRC[0:3]; - // - // ...except if CR0.PE (bit 0) is already 1 - then do not - // change that bit (lmsw can't be used to switch back to - // real mode from the protected mode. - // + // + // Loads the source operand into the machine status word, + // bits 0 through 15 of register CR0. The source operand + // can be a 16-bit general-purpose register or a memory + // location. Only the low-order 4 bits of the source + // operand (which contains the PE, MP, EM, and TS flags) + // are loaded into CR0. The PG, CD, NW, AM, WP, NE, and + // ET flags of CR0 are not affected. The operand-size + // attribute has no effect on this instruction. If the + // PE flag of the source operand (bit 0) is set to 1, the + // instruction causes the processor to switch to protected + // mode. While in protected mode, the LMSW instruction + // cannot be used to clear the PE flag and force a switch + // back to real-address mode. + // (ref: Vol2A[(LMSW-Load Machine Status Word)]) + // + // TL;DR: + // CR0[0:3] <- SRC[0:3]; + // + // ...except if CR0.PE (bit 0) is already 1 - then do not + // change that bit (lmsw can't be used to switch back to + // real mode from the protected mode. + // - cr0.flags &= ~0b1110; - cr0.flags |= msw & 0b1111; + cr0.flags &= ~0b1110; + cr0.flags |= msw & 0b1111; + + vp.guest_cr0(cr0); + vp.cr0_shadow(cr0); - vp.guest_cr0(cr0); - vp.cr0_shadow(cr0); - } break; + } } } @@ -387,7 +404,7 @@ void vmexit_passthrough_handler::handle_mov_dr(vcpu_t& vp) noexcept if (vp.guest_cs().access.descriptor_privilege_level != 0) { - vp.interrupt_inject(interrupt_general_protection); + vp.interrupt_inject(interrupt::general_protection); vp.suppress_rip_adjust(); return; } @@ -407,7 +424,7 @@ void vmexit_passthrough_handler::handle_mov_dr(vcpu_t& vp) noexcept { if (vp.guest_cr4().debugging_extensions) { - vp.interrupt_inject(interrupt_invalid_opcode); + vp.interrupt_inject(interrupt::invalid_opcode); vp.suppress_rip_adjust(); return; } @@ -444,7 +461,7 @@ void vmexit_passthrough_handler::handle_mov_dr(vcpu_t& vp) noexcept dr7.general_detect = false; vp.guest_dr7(dr7); - vp.interrupt_inject(interrupt_debug); + vp.interrupt_inject(interrupt::debug); vp.suppress_rip_adjust(); return; } @@ -460,7 +477,7 @@ void vmexit_passthrough_handler::handle_mov_dr(vcpu_t& vp) noexcept exit_qualification.dr_number == 7) && (gp_register >> 32) != 0) { - vp.interrupt_inject(interrupt_general_protection); + vp.interrupt_inject(interrupt::general_protection); vp.suppress_rip_adjust(); return; } @@ -752,9 +769,9 @@ void vmexit_passthrough_handler::handle_gdtr_idtr_access(vcpu_t& vp) noexcept // (6 bytes) than on x64 (10 bytes). // The size of written bytes must be correctly emulated. // - auto guest_in_long_mode = [&vp]() noexcept -> bool { - auto selector = vp.guest_segment_selector(context_t::seg_cs); - auto& descriptor_entry = vp.guest_gdtr()[selector]; + const auto guest_in_long_mode = [&vp]() noexcept -> bool { + const auto selector = vp.guest_segment_selector(context_t::seg_cs); + const auto& descriptor_entry = vp.guest_gdtr()[selector]; return descriptor_entry.access.long_mode; }; @@ -964,7 +981,7 @@ void vmexit_passthrough_handler::handle_execute_invpcid(vcpu_t& vp) noexcept return; inject_general_protection: - vp.interrupt_inject(interrupt_general_protection); + vp.interrupt_inject(interrupt::general_protection); vp.suppress_rip_adjust(); } @@ -1006,7 +1023,7 @@ void vmexit_passthrough_handler::handle_execute_vmfunc(vcpu_t& vp) noexcept void vmexit_passthrough_handler::handle_vm_fallback(vcpu_t& vp) noexcept { - vp.interrupt_inject(interrupt_invalid_opcode); + vp.interrupt_inject(interrupt::invalid_opcode); vp.suppress_rip_adjust(); } @@ -1016,7 +1033,7 @@ void vmexit_passthrough_handler::handle_interrupt(vcpu_t& vp) noexcept // Common code for handling all exceptions and interrupts. // - auto interrupt = vp.interrupt_info(); + const auto interrupt = vp.interrupt_info(); switch (interrupt.type()) { @@ -1024,49 +1041,52 @@ void vmexit_passthrough_handler::handle_interrupt(vcpu_t& vp) noexcept switch (interrupt.vector()) { case exception_vector::invalid_opcode: - { - cr3_guard _(vp.guest_cr3()); + { + cr3_guard _(vp.guest_cr3()); - if (detail::is_syscall_instruction(vp.exit_context().rip_as_pointer)) - { - handle_emulate_syscall(vp); - vp.suppress_rip_adjust(); - return; - } - else if (detail::is_sysret_instruction(vp.exit_context().rip_as_pointer)) - { - handle_emulate_sysret(vp); - vp.suppress_rip_adjust(); - return; - } + if (detail::is_syscall_instruction(vp.exit_context().rip_as_pointer)) + { + handle_emulate_syscall(vp); + vp.suppress_rip_adjust(); + return; } + else if (detail::is_sysret_instruction(vp.exit_context().rip_as_pointer)) + { + handle_emulate_sysret(vp); + vp.suppress_rip_adjust(); + return; + } + break; + } case exception_vector::general_protection: + { #ifdef HVPP_ENABLE_VMWARE_WORKAROUND - { - // - // VMWare I/O backdoor (port 0x5658/0x5659) workaround. - // - cr3_guard _(vp.guest_cr3()); + // + // VMWare I/O backdoor (port 0x5658/0x5659) workaround. + // + cr3_guard _(vp.guest_cr3()); - vmx::exit_qualification_io_instruction_t exit_qualification; - if (try_decode_io_instruction(vp.exit_context(), exit_qualification)) - { - ia32_asm_io_with_context(exit_qualification, vp.exit_context()); - return; - } + vmx::exit_qualification_io_instruction_t exit_qualification; + if (try_decode_io_instruction(vp.exit_context(), exit_qualification)) + { + ia32_asm_io_with_context(exit_qualification, vp.exit_context()); + return; } #endif break; + } case exception_vector::page_fault: + { write(cr2_t{ vp.exit_qualification().linear_address }); break; + } default: break; @@ -1114,14 +1134,14 @@ void vmexit_passthrough_handler::handle_emulate_syscall(vcpu_t& vp) noexcept // Save the address of the instruction following SYSCALL // into RCX and then load RIP from MSR_LSTAR. // - auto lstar = msr::read(); + const auto lstar = msr::read(); vp.exit_context().rcx = vp.exit_context().rip + vp.exit_instruction_length(); vp.exit_context().rip = lstar; // // Save RFLAGS into R11 and then mask RFLAGS using MSR_FMASK. // - auto fmask = msr::read(); + const auto fmask = msr::read(); vp.exit_context().r11 = vp.exit_context().rflags.flags; vp.exit_context().rflags.flags &= ~fmask.flags; @@ -1129,7 +1149,7 @@ void vmexit_passthrough_handler::handle_emulate_syscall(vcpu_t& vp) noexcept // Load the CS and SS selectors with values derived from // bits 47:32 of MSR_STAR. // - auto star = msr::read(); + const auto star = msr::read(); // // Verbose version of: @@ -1194,7 +1214,7 @@ void vmexit_passthrough_handler::handle_emulate_sysret(vcpu_t& vp) noexcept // SYSRET loads the CS and SS selectors with values // derived from bits 63:48 of MSR_STAR. // - auto star = msr::read(); + const auto star = msr::read(); // // Verbose version of: diff --git a/src/hvpp/hvpp/vmexit/vmexit_passthrough.h b/src/hvpp/hvpp/vmexit/vmexit_passthrough.h index 6d1427e..d279fc3 100644 --- a/src/hvpp/hvpp/vmexit/vmexit_passthrough.h +++ b/src/hvpp/hvpp/vmexit/vmexit_passthrough.h @@ -15,7 +15,7 @@ class vmexit_passthrough_handler { public: void setup(vcpu_t& vp) noexcept override; - void invoke_termination(vcpu_t& vp) noexcept override; + void teardown(vcpu_t& vp) noexcept override; protected: void handle_exception_or_nmi(vcpu_t& vp) noexcept override; diff --git a/src/hvpp/hvpp/vmexit/vmexit_stats.cpp b/src/hvpp/hvpp/vmexit/vmexit_stats.cpp index e80d483..2176efa 100644 --- a/src/hvpp/hvpp/vmexit/vmexit_stats.cpp +++ b/src/hvpp/hvpp/vmexit/vmexit_stats.cpp @@ -2,6 +2,7 @@ #include "hvpp/vcpu.h" +#include "hvpp/lib/assert.h" #include "hvpp/lib/log.h" #include "hvpp/lib/mp.h" // mp::cpu_index() @@ -18,7 +19,9 @@ namespace hvpp { -auto vmexit_stats_handler::initialize() noexcept -> error_code_t +vmexit_stats_handler::vmexit_stats_handler() noexcept + : storage_merged_{} + , vmexit_trace_bitmap_{} { terminated_vcpu_count_ = 0; @@ -26,11 +29,7 @@ auto vmexit_stats_handler::initialize() noexcept -> error_code_t // Allocate memory for statistics (per VCPU). // storage_ = new vmexit_stats_storage_t[mp::cpu_count()]; - - if (!storage_) - { - return make_error_code_t(std::errc::not_enough_memory); - } + hvpp_assert(storage_ != nullptr); memset(storage_, 0, sizeof(*storage_) * mp::cpu_count()); @@ -46,25 +45,20 @@ auto vmexit_stats_handler::initialize() noexcept -> error_code_t // // vmexit_trace_bitmap_.clear(int(vmx::exit_reason::exception_or_nmi)); // - - return error_code_t{}; } -void vmexit_stats_handler::destroy() noexcept +vmexit_stats_handler::~vmexit_stats_handler() noexcept { - if (storage_) - { - // - // Free the memory. - // - delete[] storage_; - } + // + // Free the memory. + // + delete[] storage_; } void vmexit_stats_handler::handle(vcpu_t& vp) noexcept { - auto exit_reason = vp.exit_reason(); - auto& stats = storage_[mp::cpu_index()]; + const auto exit_reason = vp.exit_reason(); + auto& stats = storage_[mp::cpu_index()]; stats.vmexit[static_cast(exit_reason)] += 1; @@ -327,7 +321,7 @@ void vmexit_stats_handler::storage_merge(vmexit_stats_storage_t& lhs, const vmex void vmexit_stats_handler::storage_dump(const vmexit_stats_storage_t& storage_to_dump) const noexcept { - auto& stats = storage_to_dump; + const auto& stats = storage_to_dump; hvpp_info("VMEXIT statistics"); for (uint32_t exit_reason_index = 0; exit_reason_index < std::size(stats.vmexit); ++exit_reason_index) diff --git a/src/hvpp/hvpp/vmexit/vmexit_stats.h b/src/hvpp/hvpp/vmexit/vmexit_stats.h index 8ed9372..38278ee 100644 --- a/src/hvpp/hvpp/vmexit/vmexit_stats.h +++ b/src/hvpp/hvpp/vmexit/vmexit_stats.h @@ -23,8 +23,8 @@ class vmexit_stats_handler : public vmexit_handler { public: - auto initialize() noexcept -> error_code_t; - void destroy() noexcept; + vmexit_stats_handler() noexcept; + ~vmexit_stats_handler() noexcept override; void handle(vcpu_t& vp) noexcept override; diff --git a/src/hvpp/hvpp/vmexit_compositor.h b/src/hvpp/hvpp/vmexit_compositor.h index f18cadf..e3ca041 100644 --- a/src/hvpp/hvpp/vmexit_compositor.h +++ b/src/hvpp/hvpp/vmexit_compositor.h @@ -17,35 +17,11 @@ namespace hvpp using vmexit_handler_tuple_t = std::tuple; vmexit_handler_tuple_t handlers; - auto initialize() noexcept -> error_code_t override - { - error_code_t err; + vmexit_compositor_handler() noexcept + { } - // - // Initialize all handlers. - // If initialization of one or more handlers fail, error - // code of only the first failed initialization is saved - // and returned. Initialization of other handlers doesn't - // stop on the first error. - // - for_each_element(handlers, [&](auto&& handler, int) { - auto local_err = handler.initialize(); - - if (!err) - { - err = local_err; - } - }); - - return err; - } - - void destroy() noexcept override - { - for_each_element(handlers, [&](auto&& handler, int) { - handler.destroy(); - }); - } + ~vmexit_compositor_handler() noexcept override + { } void setup(vcpu_t& vp) noexcept override { @@ -54,19 +30,19 @@ namespace hvpp }); } + void teardown(vcpu_t& vp) noexcept override + { + for_each_element(handlers, [&](auto&& handler, int) { + handler.teardown(vp); + }); + } + void handle(vcpu_t& vp) noexcept override { for_each_element(handlers, [&](auto&& handler, int) { handler.handle(vp); }); } - - void invoke_termination(vcpu_t& vp) noexcept override - { - for_each_element(handlers, [&](auto&& handler, int) { - handler.invoke_termination(vp); - }); - } }; } diff --git a/src/hvppctrl/hvppctrl.vcxproj b/src/hvppctrl/hvppctrl.vcxproj index c36a4bf..76cccab 100644 --- a/src/hvppctrl/hvppctrl.vcxproj +++ b/src/hvppctrl/hvppctrl.vcxproj @@ -15,19 +15,19 @@ {A72DAEF5-C739-4E70-B57E-4310ABA03749} Win32Proj hvppctrl - 10.0.17134.0 + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode diff --git a/src/hvppctrl/main.cpp b/src/hvppctrl/main.cpp index 38a3b68..109bc7a 100644 --- a/src/hvppctrl/main.cpp +++ b/src/hvppctrl/main.cpp @@ -246,7 +246,7 @@ void TestIoControl() UINT16 IoPort = 0x64; DWORD BytesReturned; DeviceIoControl(DeviceHandle, - ioctl_enable_io_debugbreak_t::code(), + ioctl_enable_io_debugbreak_t::code, &IoPort, sizeof(IoPort), &IoPort, diff --git a/src/hvppdrv/main.cpp b/src/hvppdrv/main.cpp index 1d7cac9..70047de 100644 --- a/src/hvppdrv/main.cpp +++ b/src/hvppdrv/main.cpp @@ -28,7 +28,6 @@ namespace driver static_assert(std::is_base_of_v); - hypervisor* hypervisor_ = nullptr; vmexit_handler_t* vmexit_handler_ = nullptr; device_custom* device_ = nullptr; @@ -48,27 +47,7 @@ namespace driver // // Initialize device instance. // - if (auto err = device_->initialize()) - { - destroy(); - return err; - } - - // - // Create hypervisor instance. - // - hypervisor_ = new hypervisor(); - - if (!hypervisor_) - { - destroy(); - return make_error_code_t(std::errc::not_enough_memory); - } - - // - // Initialize hypervisor. - // - if (auto err = hypervisor_->initialize()) + if (auto err = device_->create()) { destroy(); return err; @@ -85,15 +64,6 @@ namespace driver return make_error_code_t(std::errc::not_enough_memory); } - // - // Initialize VM-exit handler. - // - if (auto err = vmexit_handler_->initialize()) - { - destroy(); - return err; - } - // // Assign the vmexit_dbgbreak_handler instance to the device. // @@ -108,13 +78,17 @@ namespace driver // // Start the hypervisor. // - hypervisor_->start(*vmexit_handler_); + if (auto err = hvpp::hypervisor::start(*vmexit_handler_)) + { + destroy(); + return err; + } // // Tell debugger we're started. // hvpp_info("Hypervisor started, current free memory: %" PRIu64 " MB", - memory_manager::free_bytes() / 1024 / 1024); + mm::free_bytes() / 1024 / 1024); return error_code_t{}; } @@ -122,22 +96,9 @@ namespace driver void destroy() noexcept { // - // Stop and destroy hypervisor. + // Stop the hypervisor. // - if (hypervisor_) - { - // - // Stopping the hypervisor is not strictly needed here - - // the destroy() method stops the hypervisor if necessary. - // - if (hypervisor_->is_started()) - { - hypervisor_->stop(); - } - - hypervisor_->destroy(); - delete hypervisor_; - } + hvpp::hypervisor::stop(); // // Destroy VM-exit handler. @@ -149,7 +110,6 @@ namespace driver // std::get(vmexit_handler_->handlers).dump(); - vmexit_handler_->destroy(); delete vmexit_handler_; } @@ -158,7 +118,6 @@ namespace driver // if (device_) { - device_->destroy(); delete device_; } diff --git a/src/hvppdrv_c/main.c b/src/hvppdrv_c/main.c index 75903d1..f27761c 100644 --- a/src/hvppdrv_c/main.c +++ b/src/hvppdrv_c/main.c @@ -10,8 +10,6 @@ // ////////////////////////////////////////////////////////////////////////// -PHVPP Hypervisor; - VOID NTAPI DriverUnload( @@ -20,7 +18,7 @@ DriverUnload( { UNREFERENCED_PARAMETER(DriverObject); - HvppDestroy(Hypervisor); + HvppDestroy(); } NTSTATUS @@ -36,7 +34,7 @@ DriverEntry( DriverObject->DriverUnload = &DriverUnload; - Status = HvppInitialize(&Hypervisor); + Status = HvppInitialize(); if (!NT_SUCCESS(Status)) { @@ -49,11 +47,11 @@ DriverEntry( [VMEXIT_REASON_EPT_VIOLATION] = &HvppHandleEptViolation, } }; - Status = HvppStart(Hypervisor, &VmExitHandler); + Status = HvppStart(&VmExitHandler); if (!NT_SUCCESS(Status)) { - HvppDestroy(Hypervisor); + HvppDestroy(); return Status; }