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;
}