Even more cleanup

This commit is contained in:
momo5502 2022-04-03 14:22:03 +02:00
parent 17e10a93ea
commit 01ed54e8a2
5 changed files with 110 additions and 136 deletions

View File

@ -71,27 +71,40 @@ LEAF_END restore_context, _TEXT$00
; -----------------------------------------------------
extern ShvVmxEntryHandler:proc
extern vm_exit_handler:proc
extern vm_launch_handler:proc
extern RtlCaptureContext:proc
; -----------------------------------------------------
ShvVmxEntry PROC
vm_launch PROC
mov rcx, rsp
sub rsp, 30h
jmp vm_launch_handler
vm_launch ENDP
; -----------------------------------------------------
vm_exit PROC
; Load CONTEXT pointer
push rcx
lea rcx, [rsp+8h]
sub rsp, 30h
sub rsp, 30h ; Home-space
call RtlCaptureContext
add rsp, 30h
mov rcx, [rsp+CxRsp+8h]
add rcx, 8h
add rcx, 8h ; Fixup push rcx
add rcx, 30h ; Fixup home-space
mov [rsp+CxRsp+8h], rcx
pop rcx
mov [rsp+CxRcx], rcx
jmp ShvVmxEntryHandler
ShvVmxEntry ENDP
mov rcx, rsp
sub rsp, 30h
jmp vm_exit_handler
vm_exit ENDP
end

View File

@ -1,6 +1,5 @@
#pragma once
#include "std_include.hpp"
#include "stdint.hpp"
extern "C" {
@ -11,6 +10,8 @@ void __lgdt(void* gdtr);
void _sgdt(void*);
[[ noreturn ]] void vm_launch();
[[ noreturn ]] void vm_exit();
[[ noreturn ]] void restore_context(CONTEXT* context);
}

View File

@ -59,24 +59,24 @@ namespace
_sldt(&special_registers.ldtr);
}
void capture_cpu_context(vmx::vm_launch_context& launch_context)
void capture_cpu_context(vmx::launch_context& launch_context)
{
cpature_special_registers(launch_context.special_registers);
RtlCaptureContext(&launch_context.context_frame);
}
void restore_descriptor_tables(vmx::vm_launch_context& launch_context)
void restore_descriptor_tables(vmx::launch_context& launch_context)
{
__lgdt(&launch_context.special_registers.gdtr.limit);
__lidt(&launch_context.special_registers.idtr.limit);
}
vmx::vm_state* resolve_vm_state_from_context(CONTEXT& context)
vmx::state* resolve_vm_state_from_context(CONTEXT& context)
{
auto* context_address = reinterpret_cast<uint8_t*>(&context);
auto* vm_state_address = context_address + sizeof(CONTEXT) - KERNEL_STACK_SIZE;
return reinterpret_cast<vmx::vm_state*>(vm_state_address);
return reinterpret_cast<vmx::state*>(vm_state_address);
}
uintptr_t read_vmx(const uint32_t vmcs_field_id)
@ -101,9 +101,8 @@ namespace
return error_code;
}
[[ noreturn ]] void restore_post_launch()
extern "C" [[ noreturn ]] void vm_launch_handler(CONTEXT* context)
{
auto* context = static_cast<CONTEXT*>(_AddressOfReturnAddress());
auto* vm_state = resolve_vm_state_from_context(*context);
vm_state->launch_context.context_frame.EFlags |= EFLAGS_ALIGNMENT_CHECK_FLAG_FLAG;
@ -203,7 +202,7 @@ bool hypervisor::try_enable_core(const uint64_t system_directory_table_base)
#define MTRR_PAGE_SIZE 4096
#define MTRR_PAGE_MASK (~(MTRR_PAGE_SIZE-1))
VOID ShvVmxMtrrInitialize(vmx::vm_state* VpData)
VOID ShvVmxMtrrInitialize(vmx::state* VpData)
{
ia32_mtrr_capabilities_register mtrrCapabilities;
ia32_mtrr_physbase_register mtrrBase;
@ -254,7 +253,7 @@ VOID ShvVmxMtrrInitialize(vmx::vm_state* VpData)
UINT32
ShvVmxMtrrAdjustEffectiveMemoryType(
vmx::vm_state* VpData,
vmx::state* VpData,
_In_ UINT64 LargePageAddress,
_In_ UINT32 CandidateMemoryType
)
@ -292,7 +291,7 @@ ShvVmxMtrrAdjustEffectiveMemoryType(
return CandidateMemoryType;
}
void ShvVmxEptInitialize(vmx::vm_state* VpData)
void ShvVmxEptInitialize(vmx::state* VpData)
{
//
// Fill out the EPML4E which covers the first 512GB of RAM
@ -355,7 +354,7 @@ void ShvVmxEptInitialize(vmx::vm_state* VpData)
UINT8
ShvVmxEnterRootModeOnVp(vmx::vm_state* VpData)
ShvVmxEnterRootModeOnVp(vmx::state* VpData)
{
auto* launch_context = &VpData->launch_context;
auto* Registers = &launch_context->special_registers;
@ -473,7 +472,7 @@ VOID
ShvUtilConvertGdtEntry(
_In_ uint64_t GdtBase,
_In_ UINT16 Selector,
_Out_ vmx_gdt_entry* VmxGdtEntry
_Out_ vmx::gdt_entry* VmxGdtEntry
)
{
//
@ -562,9 +561,7 @@ ShvUtilAdjustMsr(
return DesiredValue;
}
VOID
ShvVmxHandleInvd(
VOID)
void vmx_handle_invd()
{
//
// This is the handler for the INVD instruction. Technically it may be more
@ -579,20 +576,7 @@ ShvVmxHandleInvd(
#define DPL_USER 3
#define DPL_SYSTEM 0
typedef struct _SHV_VP_STATE
{
PCONTEXT VpRegs;
uintptr_t GuestRip;
uintptr_t GuestRsp;
uintptr_t GuestEFlags;
UINT16 ExitReason;
UINT8 ExitVm;
} SHV_VP_STATE, *PSHV_VP_STATE;
VOID
ShvVmxHandleCpuid(
_In_ PSHV_VP_STATE VpState
)
void vmx_handle_cpuid(vmx::guest_context& guest_context)
{
INT32 cpu_info[4];
@ -602,11 +586,11 @@ ShvVmxHandleCpuid(
// in the expected function, but we may want to allow a separate "unload"
// driver or code at some point.
//
if ((VpState->VpRegs->Rax == 0x41414141) &&
(VpState->VpRegs->Rcx == 0x42424242) &&
if ((guest_context.vp_regs->Rax == 0x41414141) &&
(guest_context.vp_regs->Rcx == 0x42424242) &&
((read_vmx(VMCS_GUEST_CS_SELECTOR) & SEGMENT_ACCESS_RIGHTS_DESCRIPTOR_PRIVILEGE_LEVEL_MASK) == DPL_SYSTEM))
{
VpState->ExitVm = TRUE;
guest_context.exit_vm = true;
return;
}
@ -614,12 +598,12 @@ ShvVmxHandleCpuid(
// Otherwise, issue the CPUID to the logical processor based on the indexes
// on the VP's GPRs.
//
__cpuidex(cpu_info, (INT32)VpState->VpRegs->Rax, (INT32)VpState->VpRegs->Rcx);
__cpuidex(cpu_info, (INT32)guest_context.vp_regs->Rax, (INT32)guest_context.vp_regs->Rcx);
//
// Check if this was CPUID 1h, which is the features request.
//
if (VpState->VpRegs->Rax == 1)
if (guest_context.vp_regs->Rax == 1)
{
//
// Set the Hypervisor Present-bit in RCX, which Intel and AMD have both
@ -627,7 +611,7 @@ ShvVmxHandleCpuid(
//
cpu_info[2] |= HYPERV_HYPERVISOR_PRESENT_BIT;
}
else if (VpState->VpRegs->Rax == HYPERV_CPUID_INTERFACE)
else if (guest_context.vp_regs->Rax == HYPERV_CPUID_INTERFACE)
{
//
// Return our interface identifier
@ -638,46 +622,36 @@ ShvVmxHandleCpuid(
//
// Copy the values from the logical processor registers into the VP GPRs.
//
VpState->VpRegs->Rax = cpu_info[0];
VpState->VpRegs->Rbx = cpu_info[1];
VpState->VpRegs->Rcx = cpu_info[2];
VpState->VpRegs->Rdx = cpu_info[3];
guest_context.vp_regs->Rax = cpu_info[0];
guest_context.vp_regs->Rbx = cpu_info[1];
guest_context.vp_regs->Rcx = cpu_info[2];
guest_context.vp_regs->Rdx = cpu_info[3];
}
VOID
ShvVmxHandleXsetbv(
_In_ PSHV_VP_STATE VpState
)
void vmx_handle_xsetbv(const vmx::guest_context& guest_contex)
{
//
// Simply issue the XSETBV instruction on the native logical processor.
//
_xsetbv((UINT32)VpState->VpRegs->Rcx,
VpState->VpRegs->Rdx << 32 |
VpState->VpRegs->Rax);
_xsetbv(static_cast<uint32_t>(guest_contex.vp_regs->Rcx),
guest_contex.vp_regs->Rdx << 32 | guest_contex.vp_regs->Rax);
}
VOID
ShvVmxHandleVmx(
_In_ PSHV_VP_STATE VpState
)
void vmx_handle_vmx(vmx::guest_context& guest_contex)
{
//
// Set the CF flag, which is how VMX instructions indicate failure
//
VpState->GuestEFlags |= 0x1; // VM_FAIL_INVALID
guest_contex.guest_e_flags |= 0x1; // VM_FAIL_INVALID
//
// RFLAGs is actually restored from the VMCS, so update it here
//
__vmx_vmwrite(VMCS_GUEST_RFLAGS, VpState->GuestEFlags);
__vmx_vmwrite(VMCS_GUEST_RFLAGS, guest_contex.guest_e_flags);
}
VOID
ShvVmxHandleExit(
_In_ PSHV_VP_STATE VpState
)
void vmx_dispatch_vm_exit(vmx::guest_context& guest_contex)
{
//
// This is the generic VM-Exit handler. Decode the reason for the exit and
@ -686,16 +660,16 @@ ShvVmxHandleExit(
// INVD, XSETBV and other VMX instructions. GETSEC cannot happen as we do
// not run in SMX context.
//
switch (VpState->ExitReason)
switch (guest_contex.exit_reason)
{
case VMX_EXIT_REASON_EXECUTE_CPUID:
ShvVmxHandleCpuid(VpState);
vmx_handle_cpuid(guest_contex);
break;
case VMX_EXIT_REASON_EXECUTE_INVD:
ShvVmxHandleInvd();
vmx_handle_invd();
break;
case VMX_EXIT_REASON_EXECUTE_XSETBV:
ShvVmxHandleXsetbv(VpState);
vmx_handle_xsetbv(guest_contex);
break;
case VMX_EXIT_REASON_EXECUTE_VMCALL:
case VMX_EXIT_REASON_EXECUTE_VMCLEAR:
@ -707,7 +681,7 @@ ShvVmxHandleExit(
case VMX_EXIT_REASON_EXECUTE_VMWRITE:
case VMX_EXIT_REASON_EXECUTE_VMXOFF:
case VMX_EXIT_REASON_EXECUTE_VMXON:
ShvVmxHandleVmx(VpState);
vmx_handle_vmx(guest_contex);
break;
default:
break;
@ -718,28 +692,13 @@ ShvVmxHandleExit(
// caused the exit. Since we are not doing any special handling or changing
// of execution, this can be done for any exit reason.
//
VpState->GuestRip += read_vmx(VMCS_VMEXIT_INSTRUCTION_LENGTH);
__vmx_vmwrite(VMCS_GUEST_RIP, VpState->GuestRip);
guest_contex.guest_rip += read_vmx(VMCS_VMEXIT_INSTRUCTION_LENGTH);
__vmx_vmwrite(VMCS_GUEST_RIP, guest_contex.guest_rip);
}
extern "C" DECLSPEC_NORETURN
VOID
ShvVmxEntryHandler()
extern "C" [[ noreturn ]] void vm_exit_handler(CONTEXT* context)
{
PCONTEXT Context = (PCONTEXT)_AddressOfReturnAddress();
SHV_VP_STATE guestContext;
//
// Because we had to use RCX when calling ShvOsCaptureContext, its value
// was actually pushed on the stack right before the call. Go dig into the
// stack to find it, and overwrite the bogus value that's there now.
//
//Context->Rcx = *(UINT64*)((uintptr_t)Context - sizeof(Context->Rcx));
//
// Get the per-VP data for this processor.
//
auto* vpData = (vmx::vm_state*)((uintptr_t)(Context + 1) - KERNEL_STACK_SIZE);
auto* vm_state = resolve_vm_state_from_context(*context);
//
// Build a little stack context to make it easier to keep track of certain
@ -747,37 +706,31 @@ ShvVmxEntryHandler()
// of the general purpose registers come from the context structure that we
// captured on our own with RtlCaptureContext in the assembly entrypoint.
//
guestContext.GuestEFlags = read_vmx(VMCS_GUEST_RFLAGS);
guestContext.GuestRip = read_vmx(VMCS_GUEST_RIP);
guestContext.GuestRsp = read_vmx(VMCS_GUEST_RSP);
guestContext.ExitReason = read_vmx(VMCS_EXIT_REASON) & 0xFFFF;
guestContext.VpRegs = Context;
guestContext.ExitVm = FALSE;
vmx::guest_context guest_context{};
guest_context.guest_e_flags = read_vmx(VMCS_GUEST_RFLAGS);
guest_context.guest_rip = read_vmx(VMCS_GUEST_RIP);
guest_context.guest_rsp = read_vmx(VMCS_GUEST_RSP);
guest_context.exit_reason = read_vmx(VMCS_EXIT_REASON) & 0xFFFF;
guest_context.vp_regs = context;
guest_context.exit_vm = false;
//
// Call the generic handler
//
ShvVmxHandleExit(&guestContext);
vmx_dispatch_vm_exit(guest_context);
//
// Did we hit the magic exit sequence, or should we resume back to the VM
// context?
//
if (guestContext.ExitVm != FALSE)
if (guest_context.exit_vm)
{
//
// Return the VP Data structure in RAX:RBX which is going to be part of
// the CPUID response that the caller (ShvVpUninitialize) expects back.
// Return confirmation in RCX that we are loaded
//
Context->Rax = (uintptr_t)vpData >> 32;
Context->Rbx = (uintptr_t)vpData & 0xFFFFFFFF;
Context->Rcx = 0x43434343;
context->Rcx = 0x43434343;
//
// Perform any OS-specific CPU uninitialization work
//
restore_descriptor_tables(vpData->launch_context);
restore_descriptor_tables(vm_state->launch_context);
//
// Our callback routine may have interrupted an arbitrary user process,
@ -796,9 +749,9 @@ ShvVmxEntryHandler()
// execute (such as ShvVpUninitialize). This will effectively act as
// a longjmp back to that location.
//
Context->Rsp = guestContext.GuestRsp;
Context->Rip = (UINT64)guestContext.GuestRip;
Context->EFlags = (UINT32)guestContext.GuestEFlags;
context->Rsp = guest_context.guest_rsp;
context->Rip = guest_context.guest_rip;
context->EFlags = static_cast<uint32_t>(guest_context.guest_e_flags);
//
// Turn off VMX root mode on this logical processor. We're done here.
@ -813,7 +766,7 @@ ShvVmxEntryHandler()
// needed as RtlRestoreContext will fix all the GPRs, and what we just
// did to RSP will take care of the rest.
//
Context->Rip = reinterpret_cast<uint64_t>(resume_vmx);
context->Rip = reinterpret_cast<uint64_t>(resume_vmx);
}
//
@ -823,15 +776,10 @@ ShvVmxEntryHandler()
// which we use in case an exit was requested. In this case VMX must now be
// off, and this will look like a longjmp to the original stack and RIP.
//
restore_context(Context);
restore_context(context);
}
extern "C" VOID
ShvVmxEntry(
VOID);
void ShvVmxSetupVmcsForVp(vmx::vm_state* VpData)
void ShvVmxSetupVmcsForVp(vmx::state* VpData)
{
auto* launch_context = &VpData->launch_context;
auto* state = &launch_context->special_registers;
@ -920,7 +868,7 @@ void ShvVmxSetupVmcsForVp(vmx::vm_state* VpData)
//
// Load the CS Segment (Ring 0 Code)
//
vmx_gdt_entry vmx_gdt_entry{};
vmx::gdt_entry vmx_gdt_entry{};
ShvUtilConvertGdtEntry(state->gdtr.base_address, context->SegCs, &vmx_gdt_entry);
__vmx_vmwrite(VMCS_GUEST_CS_SELECTOR, vmx_gdt_entry.selector);
__vmx_vmwrite(VMCS_GUEST_CS_LIMIT, vmx_gdt_entry.limit);
@ -1047,8 +995,10 @@ void ShvVmxSetupVmcsForVp(vmx::vm_state* VpData)
// corresponds exactly to the location where RtlCaptureContext will return
// to inside of ShvVpInitialize.
//
__vmx_vmwrite(VMCS_GUEST_RSP, (uintptr_t)VpData->stack_buffer + KERNEL_STACK_SIZE - sizeof(CONTEXT));
__vmx_vmwrite(VMCS_GUEST_RIP, reinterpret_cast<size_t>(restore_post_launch));
const auto stack_pointer = reinterpret_cast<uintptr_t>(VpData->stack_buffer) + KERNEL_STACK_SIZE - sizeof(CONTEXT);
__vmx_vmwrite(VMCS_GUEST_RSP, stack_pointer);
__vmx_vmwrite(VMCS_GUEST_RIP, reinterpret_cast<uintptr_t>(vm_launch));
__vmx_vmwrite(VMCS_GUEST_RFLAGS, context->EFlags);
//
@ -1061,11 +1011,11 @@ void ShvVmxSetupVmcsForVp(vmx::vm_state* VpData)
// the ones that RtlCaptureContext will perform.
//
C_ASSERT((KERNEL_STACK_SIZE - sizeof(CONTEXT)) % 16 == 0);
__vmx_vmwrite(VMCS_HOST_RSP, (uintptr_t)VpData->stack_buffer + KERNEL_STACK_SIZE - sizeof(CONTEXT));
__vmx_vmwrite(VMCS_HOST_RIP, (uintptr_t)ShvVmxEntry);
__vmx_vmwrite(VMCS_HOST_RSP, stack_pointer);
__vmx_vmwrite(VMCS_HOST_RIP, reinterpret_cast<uintptr_t>(vm_exit));
}
INT32 ShvVmxLaunchOnVp(vmx::vm_state* VpData)
INT32 ShvVmxLaunchOnVp(vmx::state* VpData)
{
//
// Initialize all the VMX-related MSRs by reading their value
@ -1153,11 +1103,11 @@ void hypervisor::allocate_vm_states()
// As Windows technically supports cpu hot-plugging, keep track of the allocation count.
// However virtualizing the hot-plugged cpu won't be supported here.
this->vm_state_count_ = thread::get_processor_count();
this->vm_states_ = new vmx::vm_state*[this->vm_state_count_]{};
this->vm_states_ = new vmx::state*[this->vm_state_count_]{};
for (auto i = 0u; i < this->vm_state_count_; ++i)
{
this->vm_states_[i] = memory::allocate_aligned_object<vmx::vm_state>();
this->vm_states_[i] = memory::allocate_aligned_object<vmx::state>();
if (!this->vm_states_[i])
{
throw std::runtime_error("Failed to allocate VM state entries");
@ -1182,7 +1132,7 @@ void hypervisor::free_vm_states()
this->vm_state_count_ = 0;
}
vmx::vm_state* hypervisor::get_current_vm_state() const
vmx::state* hypervisor::get_current_vm_state() const
{
const auto current_core = thread::get_processor_index();
if (current_core >= this->vm_state_count_)

View File

@ -21,7 +21,7 @@ public:
private:
uint32_t vm_state_count_{0};
vmx::vm_state** vm_states_{nullptr};
vmx::state** vm_states_{nullptr};
void enable_core(uint64_t system_directory_table_base);
bool try_enable_core(uint64_t system_directory_table_base);
@ -30,5 +30,5 @@ private:
void allocate_vm_states();
void free_vm_states();
vmx::vm_state* get_current_vm_state() const;
vmx::state* get_current_vm_state() const;
};

View File

@ -39,7 +39,7 @@ namespace vmx
#define DECLSPEC_PAGE_ALIGN DECLSPEC_ALIGN(PAGE_SIZE)
struct vm_launch_context
struct launch_context
{
special_registers special_registers;
CONTEXT context_frame;
@ -53,7 +53,7 @@ namespace vmx
ia32_vmx_procbased_ctls2_register ept_controls;
};
struct vm_state
struct state
{
DECLSPEC_PAGE_ALIGN uint8_t stack_buffer[KERNEL_STACK_SIZE]{};
DECLSPEC_PAGE_ALIGN uint8_t msr_bitmap[PAGE_SIZE]{};
@ -63,14 +63,24 @@ namespace vmx
DECLSPEC_PAGE_ALIGN vmcs vmx_on{};
DECLSPEC_PAGE_ALIGN vmcs vmcs{};
DECLSPEC_PAGE_ALIGN vm_launch_context launch_context{};
DECLSPEC_PAGE_ALIGN launch_context launch_context{};
};
struct gdt_entry
{
uint64_t base;
uint32_t limit;
vmx_segment_access_rights access_rights;
uint16_t selector;
};
struct guest_context
{
PCONTEXT vp_regs;
uintptr_t guest_rip;
uintptr_t guest_rsp;
uintptr_t guest_e_flags;
uint16_t exit_reason;
bool exit_vm;
};
}
struct vmx_gdt_entry
{
uint64_t base;
uint32_t limit;
vmx_segment_access_rights access_rights;
uint16_t selector;
};