diff --git a/bluepill.vcxproj b/bluepill.vcxproj
index 6b4936c..656c42c 100644
--- a/bluepill.vcxproj
+++ b/bluepill.vcxproj
@@ -91,6 +91,7 @@
+
@@ -102,6 +103,7 @@
+
diff --git a/bluepill.vcxproj.filters b/bluepill.vcxproj.filters
index 84c04e5..f4f9837 100644
--- a/bluepill.vcxproj.filters
+++ b/bluepill.vcxproj.filters
@@ -44,6 +44,9 @@
Source Files
+
+ Source Files
+
@@ -85,6 +88,9 @@
Header Files
+
+ Header Files
+
diff --git a/exit_handler.cpp b/exit_handler.cpp
index ff1014c..b39d324 100644
--- a/exit_handler.cpp
+++ b/exit_handler.cpp
@@ -24,8 +24,6 @@ auto exit_handler(hv::pguest_registers regs) -> void
regs->rdx = result[3];
goto advance_rip;
}
- // shouldnt get an exit when the LP is already executing an NMI...
- // so it should be safe to inject an NMI here...
case VMX_EXIT_REASON_NMI_WINDOW:
{
exception::injection(interruption_type::non_maskable_interrupt, EXCEPTION_NMI);
@@ -40,166 +38,36 @@ auto exit_handler(hv::pguest_registers regs) -> void
}
case VMX_EXIT_REASON_EXECUTE_XSETBV:
{
- hv::msr_split value{};
- value.high = regs->rdx;
- value.low = regs->rax;
-
- __try
- {
- /*
- EXCEPTION WARNING:
- #GP If the current privilege level is not 0.
- If an invalid XCR is specified in ECX.
- If the value in EDX:EAX sets bits that are reserved in the XCR specified by ECX.
- If an attempt is made to clear bit 0 of XCR0.
- If an attempt is made to set XCR0[2:1] to 10b.
-
- #UD If CPUID.01H:ECX.XSAVE[bit 26] = 0.
- If CR4.OSXSAVE[bit 18] = 0.
- If the LOCK prefix is used.
- */
- _xsetbv(regs->rcx, value.value);
+ if (handle::xsetbv(regs))
goto advance_rip;
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- exception::injection(interruption_type::hardware_exception,
- EXCEPTION_GP_FAULT, { true, g_vcpu.error_code });
+ else
goto dont_advance;
- }
}
case VMX_EXIT_REASON_EXECUTE_RDMSR:
{
- __try
- {
- /*
- EXCEPTION WARNING:
- #GP(0) If the current privilege level is not 0.
- If the value in ECX specifies a reserved or unimplemented MSR address.
- #UD If the LOCK prefix is used.
- */
- const auto result =
- hv::msr_split{ __readmsr(regs->rcx) };
-
- regs->rdx = result.high;
- regs->rax = result.low;
+ if (handle::rdmsr(regs))
goto advance_rip;
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- exception::injection(interruption_type::hardware_exception,
- EXCEPTION_GP_FAULT, { true, g_vcpu.error_code });
+ else
goto dont_advance;
- }
}
case VMX_EXIT_REASON_EXECUTE_WRMSR:
{
- hv::msr_split value;
- value.low = regs->rax;
- value.high = regs->rdx;
-
- __try
- {
- /*
- EXCEPTION WARNING:
- #GP(0) If the current privilege level is not 0.
- If the value in ECX specifies a reserved or unimplemented MSR address.
- #UD If the LOCK prefix is used.
- */
- __writemsr(regs->rcx, value.value);
+ if (handle::wrmsr(regs))
goto advance_rip;
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- exception::injection(interruption_type::hardware_exception,
- EXCEPTION_GP_FAULT, { true, g_vcpu.error_code });
+ else
goto dont_advance;
- }
- }
- case VMX_EXIT_REASON_EXECUTE_INVD:
- {
- __wbinvd();
- goto advance_rip;
}
case VMX_EXIT_REASON_EXECUTE_VMCALL:
{
- if (regs->rcx == VMCALL_KEY)
- {
- cr3 dirbase;
- __vmx_vmread(VMCS_GUEST_CR3, &dirbase.flags);
-
- auto command = command::get(
- dirbase.pml4_pfn << 12, regs->rdx);
-
- if (!command.present)
- {
- exception::injection(interruption_type::hardware_exception, EXCEPTION_INVALID_OPCODE);
- goto dont_advance;
- }
-
- switch (command.option)
- {
- case command::vmcall_option::copy_virt:
- {
- command.result =
- mm::copy_virt(
- command.copy_virt.dirbase_src,
- command.copy_virt.virt_src,
- command.copy_virt.dirbase_dest,
- command.copy_virt.virt_dest,
- command.copy_virt.size);
- break;
- }
- case command::vmcall_option::translate:
- {
- command.translate.phys_addr =
- mm::translate(mm::virt_addr_t{
- command.translate.virt_addr },
- command.translate.dirbase);
-
- // true if address is not null...
- command.result = command.translate.phys_addr;
- break;
- }
- case command::vmcall_option::read_phys:
- {
- command.result =
- mm::read_phys(
- command.read_phys.dirbase_dest,
- command.read_phys.phys_src,
- command.read_phys.virt_dest,
- command.read_phys.size);
- break;
- }
- case command::vmcall_option::write_phys:
- {
- command.result =
- mm::write_phys(
- command.write_phys.dirbase_src,
- command.write_phys.phys_dest,
- command.write_phys.virt_src,
- command.write_phys.size);
- break;
- }
- case command::vmcall_option::dirbase:
- {
- command.result = true;
- command.dirbase = dirbase.pml4_pfn << 12;
- break;
- }
- default:
- // check to see why the option was invalid...
- __debugbreak();
- }
-
- command::set(dirbase.pml4_pfn << 12, regs->rdx, command);
+ if (handle::vmcall(regs))
goto advance_rip;
- }
else
- {
- exception::injection(interruption_type::hardware_exception, EXCEPTION_INVALID_OPCODE);
goto dont_advance;
- }
+ }
+ case VMX_EXIT_REASON_EXECUTE_INVD:
+ {
+ __wbinvd();
+ goto advance_rip;
}
case VMX_EXIT_REASON_EXECUTE_VMWRITE:
case VMX_EXIT_REASON_EXECUTE_VMREAD:
diff --git a/handlers.cpp b/handlers.cpp
new file mode 100644
index 0000000..c12a179
--- /dev/null
+++ b/handlers.cpp
@@ -0,0 +1,166 @@
+#include "handlers.hpp"
+
+namespace handle
+{
+ auto xsetbv(hv::pguest_registers regs) -> bool
+ {
+ hv::msr_split value{};
+ value.high = regs->rdx;
+ value.low = regs->rax;
+
+ __try
+ {
+ /*
+ EXCEPTION WARNING:
+ #GP If the current privilege level is not 0.
+ If an invalid XCR is specified in ECX.
+ If the value in EDX:EAX sets bits that are reserved in the XCR specified by ECX.
+ If an attempt is made to clear bit 0 of XCR0.
+ If an attempt is made to set XCR0[2:1] to 10b.
+
+ #UD If CPUID.01H:ECX.XSAVE[bit 26] = 0.
+ If CR4.OSXSAVE[bit 18] = 0.
+ If the LOCK prefix is used.
+ */
+ _xsetbv(regs->rcx, value.value);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ exception::injection(interruption_type::hardware_exception,
+ EXCEPTION_GP_FAULT, { true, g_vcpu.error_code });
+ return false;
+ }
+ return true;
+ }
+
+ auto rdmsr(hv::pguest_registers regs) -> bool
+ {
+ __try
+ {
+ /*
+ EXCEPTION WARNING:
+ #GP(0) If the current privilege level is not 0.
+ If the value in ECX specifies a reserved or unimplemented MSR address.
+ #UD If the LOCK prefix is used.
+ */
+ const auto result =
+ hv::msr_split{ __readmsr(regs->rcx) };
+
+ regs->rdx = result.high;
+ regs->rax = result.low;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ exception::injection(interruption_type::hardware_exception,
+ EXCEPTION_GP_FAULT, { true, g_vcpu.error_code });
+ return false;
+ }
+ return true;
+ }
+
+ auto wrmsr(hv::pguest_registers regs) -> bool
+ {
+ hv::msr_split value;
+ value.low = regs->rax;
+ value.high = regs->rdx;
+
+ __try
+ {
+ /*
+ EXCEPTION WARNING:
+ #GP(0) If the current privilege level is not 0.
+ If the value in ECX specifies a reserved or unimplemented MSR address.
+ #UD If the LOCK prefix is used.
+ */
+ __writemsr(regs->rcx, value.value);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ exception::injection(interruption_type::hardware_exception,
+ EXCEPTION_GP_FAULT, { true, g_vcpu.error_code });
+ return false;
+ }
+ return true;
+ }
+
+ auto vmcall(hv::pguest_registers regs) -> bool
+ {
+ if (regs->rcx == VMCALL_KEY)
+ {
+ cr3 dirbase;
+ __vmx_vmread(VMCS_GUEST_CR3, &dirbase.flags);
+
+ auto command = command::get(
+ dirbase.pml4_pfn << 12, regs->rdx);
+
+ if (!command.present)
+ {
+ exception::injection(interruption_type::hardware_exception, EXCEPTION_INVALID_OPCODE);
+ return false;
+ }
+
+ switch (command.option)
+ {
+ case command::vmcall_option::copy_virt:
+ {
+ command.result =
+ mm::copy_virt(
+ command.copy_virt.dirbase_src,
+ command.copy_virt.virt_src,
+ command.copy_virt.dirbase_dest,
+ command.copy_virt.virt_dest,
+ command.copy_virt.size);
+ break;
+ }
+ case command::vmcall_option::translate:
+ {
+ command.translate.phys_addr =
+ mm::translate(mm::virt_addr_t{
+ command.translate.virt_addr },
+ command.translate.dirbase);
+
+ // true if address is not null...
+ command.result = command.translate.phys_addr;
+ break;
+ }
+ case command::vmcall_option::read_phys:
+ {
+ command.result =
+ mm::read_phys(
+ command.read_phys.dirbase_dest,
+ command.read_phys.phys_src,
+ command.read_phys.virt_dest,
+ command.read_phys.size);
+ break;
+ }
+ case command::vmcall_option::write_phys:
+ {
+ command.result =
+ mm::write_phys(
+ command.write_phys.dirbase_src,
+ command.write_phys.phys_dest,
+ command.write_phys.virt_src,
+ command.write_phys.size);
+ break;
+ }
+ case command::vmcall_option::dirbase:
+ {
+ command.result = true;
+ command.dirbase = dirbase.pml4_pfn << 12;
+ break;
+ }
+ default:
+ // check to see why the option was invalid...
+ __debugbreak();
+ }
+
+ command::set(dirbase.pml4_pfn << 12, regs->rdx, command);
+ }
+ else
+ {
+ exception::injection(interruption_type::hardware_exception, EXCEPTION_INVALID_OPCODE);
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/handlers.hpp b/handlers.hpp
new file mode 100644
index 0000000..a12b631
--- /dev/null
+++ b/handlers.hpp
@@ -0,0 +1,13 @@
+#pragma once
+#include "vmxon.hpp"
+#include "hv_types.hpp"
+#include "command.hpp"
+#include "exception.hpp"
+
+namespace handle
+{
+ auto xsetbv(hv::pguest_registers regs) -> bool;
+ auto rdmsr(hv::pguest_registers regs) -> bool;
+ auto wrmsr(hv::pguest_registers regs) -> bool;
+ auto vmcall(hv::pguest_registers regs) -> bool;
+}
\ No newline at end of file
diff --git a/vmxexit_handler.h b/vmxexit_handler.h
index aae3d5a..cfd0ed3 100644
--- a/vmxexit_handler.h
+++ b/vmxexit_handler.h
@@ -1,10 +1,7 @@
#pragma once
-#include "hv_types.hpp"
+#include "handlers.hpp"
#include "debug.hpp"
#include "mm.hpp"
-#include "vmxon.hpp"
-#include "command.hpp"
-#include "exception.hpp"
extern "C" auto vmxexit_handler() -> void;
extern "C" auto vmresume_failure() -> void;