Type 2 windows hypervisor bugcheck when vmlaunch is called

so im making a type 2 hypervisor and im having some issues. I think its my vmcs setup because when i try to load my vmcs i first call setup_vcms() and then call vmptrld and until now, it works fine and vmptrld returns success but the moment i try to do vmlaunch, my system freezes. When i try to load a vmcs that only sets up cs and tr register (as those are the only relevant ones according to ia32) i get kmode exception i can load it but vmlaunch fails. also, when i try to load only vmcs link pointer and nothing else then i dont bsod but vmlaunch says 0x7 or 0x1

also here is some other functions

void convert_gdt_entry(void* gdt_base, std::uint16_t selector, PVMX_GDTENTRY64 gdt_entry)
{
	PKGDTENTRY64 gdt_entry_1;
 
	if ((selector == 0) ||
		(selector & SELECTOR_TABLE_INDEX) != 0)
	{
		gdt_entry->Limit = gdt_entry->AccessRights = 0;
		gdt_entry->Base = 0;
		gdt_entry->Selector = 0;
		gdt_entry->Bits.Unusable = TRUE;
		return;
	}
 
	gdt_entry_1 = (PKGDTENTRY64)((uintptr_t)gdt_base + (selector & ~RPL_MASK));
 
	gdt_entry->Selector = selector;
 
	gdt_entry->Limit = __segmentlimit(selector);
 
	gdt_entry->Base = ((gdt_entry_1->Bytes.BaseHigh << 24) |
		(gdt_entry_1->Bytes.BaseMiddle << 16) |
		(gdt_entry_1->BaseLow)) & 0xFFFFFFFF;
	gdt_entry->Base |= ((gdt_entry_1->Bits.Type & 0x10) == 0) ?
		((uintptr_t)gdt_entry_1->BaseUpper << 32) : 0;
 
	gdt_entry->AccessRights = 0;
	gdt_entry->Bytes.Flags1 = gdt_entry_1->Bytes.Flags1;
	gdt_entry->Bytes.Flags2 = gdt_entry_1->Bytes.Flags2;
 
	gdt_entry->Bits.Reserved = 0;
	gdt_entry->Bits.Unusable = !gdt_entry_1->Bits.Present;
}
 
void capture_registers(special_registers_t* SpecialRegisters)
{
 
	SpecialRegisters->Cr0 = __readcr0();
	SpecialRegisters->Cr3 = __readcr3();
	SpecialRegisters->Cr4 = __readcr4();
	SpecialRegisters->DebugControl = __readmsr(MSR_DEBUG_CTL);
	SpecialRegisters->MsrGsBase = __readmsr(MSR_GS_BASE);
	SpecialRegisters->KernelDr7 = __readdr(7);
	asm_sgdt(&SpecialRegisters->Gdtr.Limit);
	__sidt(&SpecialRegisters->Idtr.Limit);
 
 
	SpecialRegisters->Tr = get_tr();
	SpecialRegisters->Ldtr = get_ldtr();
}
bool setup_vmcs(vcpu_data* vcpu, CONTEXT* ctx)
	{
		if (!vcpu)
			return false;
 
		special_registers_t registers;
		capture_registers(&registers);
 
		//initialize vmx related msrs
		for (std::uint64_t i = 0; i < sizeof(vcpu->msr_data) / sizeof(vcpu->msr_data[0]); i++)
		{
			vcpu->msr_data[i].QuadPart = __readmsr(MSR_IA32_VMX_BASIC + i);
		}
 
 
 
 
		__vmx_vmwrite(VMCS_LINK_POINTER, ~0ULL);
 
		//ept not supported yet
 
		__vmx_vmwrite(MSR_BITMAP, vcpu->physical_msr_bitmap.QuadPart);
 
		__vmx_vmwrite(PIN_BASED_VM_EXEC_CONTROL, adjust_controls(0, vcpu->msr_data[13].QuadPart));
 
		__vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, adjust_controls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, vcpu->msr_data[14].QuadPart));
 
		__vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, adjust_controls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS | CPU_BASED_CPUID_EXITING, vcpu->msr_data[14].QuadPart));
 
		__vmx_vmwrite(VM_EXIT_CONTROLS, adjust_controls(VM_EXIT_IA32E_MODE, vcpu->msr_data[15].QuadPart));
 
		__vmx_vmwrite(VM_ENTRY_CONTROLS, adjust_controls(VM_EXIT_IA32E_MODE, vcpu->msr_data[16].QuadPart));
 
 
		__vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, adjust_controls(SECONDARY_EXEC_ENABLE_RDTSCP | SECONDARY_EXEC_ENABLE_INVPCID | SECONDARY_EXEC_XSAVES, vcpu->msr_data[11].QuadPart));
 
 
 
		_KDESCRIPTOR gdt, idt;
		VMX_GDTENTRY64 vmx_gdt_entry;
 
		convert_gdt_entry(registers.Gdtr.Base, ctx->SegCs, &vmx_gdt_entry);
		__vmx_vmwrite(GUEST_CS_SELECTOR, vmx_gdt_entry.Selector);
		__vmx_vmwrite(GUEST_CS_LIMIT, vmx_gdt_entry.Limit);
		__vmx_vmwrite(GUEST_CS_AR_BYTES, vmx_gdt_entry.AccessRights);
		__vmx_vmwrite(GUEST_CS_BASE, vmx_gdt_entry.Base);
		__vmx_vmwrite(HOST_CS_SELECTOR, ctx->SegCs & ~RPL_MASK);
 
 
		DbgPrint("gdtr base -> 0x%llx", registers.Gdtr.Base);
 
		convert_gdt_entry(registers.Gdtr.Base, ctx->SegSs, &vmx_gdt_entry);
		__vmx_vmwrite(GUEST_SS_SELECTOR, vmx_gdt_entry.Selector);
		__vmx_vmwrite(GUEST_SS_LIMIT, vmx_gdt_entry.Limit);
		__vmx_vmwrite(GUEST_SS_AR_BYTES, vmx_gdt_entry.AccessRights);
		__vmx_vmwrite(GUEST_SS_BASE, vmx_gdt_entry.Base);
		__vmx_vmwrite(HOST_SS_SELECTOR, ctx->SegSs & ~RPL_MASK);
 
 
		convert_gdt_entry(registers.Gdtr.Base, ctx->SegDs, &vmx_gdt_entry);
		__vmx_vmwrite(GUEST_DS_SELECTOR, vmx_gdt_entry.Selector);
		__vmx_vmwrite(GUEST_DS_LIMIT, vmx_gdt_entry.Limit);
		__vmx_vmwrite(GUEST_DS_AR_BYTES, vmx_gdt_entry.AccessRights);
		__vmx_vmwrite(GUEST_DS_BASE, vmx_gdt_entry.Base);
		__vmx_vmwrite(HOST_DS_SELECTOR, ctx->SegDs & ~RPL_MASK);
 
 
		convert_gdt_entry(registers.Gdtr.Base, ctx->SegEs, &vmx_gdt_entry);
		__vmx_vmwrite(GUEST_ES_SELECTOR, vmx_gdt_entry.Selector);
		__vmx_vmwrite(GUEST_ES_LIMIT, vmx_gdt_entry.Limit);
		__vmx_vmwrite(GUEST_ES_AR_BYTES, vmx_gdt_entry.AccessRights);
		__vmx_vmwrite(GUEST_ES_BASE, vmx_gdt_entry.Base);
		__vmx_vmwrite(HOST_ES_SELECTOR, ctx->SegEs & ~RPL_MASK);
 
 
		convert_gdt_entry(registers.Gdtr.Base, ctx->SegFs, &vmx_gdt_entry);
		__vmx_vmwrite(GUEST_FS_SELECTOR, vmx_gdt_entry.Selector);
		__vmx_vmwrite(GUEST_FS_LIMIT, vmx_gdt_entry.Limit);
		__vmx_vmwrite(GUEST_FS_AR_BYTES, vmx_gdt_entry.AccessRights);
		__vmx_vmwrite(GUEST_FS_BASE, vmx_gdt_entry.Base);
		__vmx_vmwrite(HOST_FS_BASE, vmx_gdt_entry.Base);
		__vmx_vmwrite(HOST_FS_SELECTOR, ctx->SegFs & ~RPL_MASK);
 
		convert_gdt_entry(registers.Gdtr.Base, ctx->SegGs, &vmx_gdt_entry);
		__vmx_vmwrite(GUEST_GS_SELECTOR, vmx_gdt_entry.Selector);
		__vmx_vmwrite(GUEST_GS_LIMIT, vmx_gdt_entry.Limit);
		__vmx_vmwrite(GUEST_GS_AR_BYTES, vmx_gdt_entry.AccessRights);
		__vmx_vmwrite(GUEST_GS_BASE, registers.MsrGsBase);
		__vmx_vmwrite(HOST_GS_BASE, registers.MsrGsBase);
		__vmx_vmwrite(HOST_GS_SELECTOR, ctx->SegGs & ~RPL_MASK);
 
		convert_gdt_entry(registers.Gdtr.Base, registers.Tr, &vmx_gdt_entry);
		__vmx_vmwrite(GUEST_TR_SELECTOR, vmx_gdt_entry.Selector);
		__vmx_vmwrite(GUEST_TR_LIMIT, vmx_gdt_entry.Limit);
		__vmx_vmwrite(GUEST_TR_AR_BYTES, vmx_gdt_entry.AccessRights);
		__vmx_vmwrite(GUEST_TR_BASE, vmx_gdt_entry.Base);
		__vmx_vmwrite(HOST_TR_BASE, vmx_gdt_entry.Base);
		__vmx_vmwrite(HOST_TR_SELECTOR, registers.Tr & ~RPL_MASK);
 
		convert_gdt_entry(registers.Gdtr.Base, registers.Ldtr, &vmx_gdt_entry);
		__vmx_vmwrite(GUEST_LDTR_SELECTOR, vmx_gdt_entry.Selector);
		__vmx_vmwrite(GUEST_LDTR_LIMIT, vmx_gdt_entry.Limit);
		__vmx_vmwrite(GUEST_LDTR_AR_BYTES, vmx_gdt_entry.AccessRights);
		__vmx_vmwrite(GUEST_LDTR_BASE, vmx_gdt_entry.Base);
 
		__vmx_vmwrite(GUEST_GDTR_BASE, (uintptr_t)registers.Gdtr.Base);
		__vmx_vmwrite(GUEST_GDTR_LIMIT, registers.Gdtr.Limit);
		__vmx_vmwrite(HOST_GDTR_BASE, (uintptr_t)registers.Gdtr.Base);
 
		__vmx_vmwrite(GUEST_IDTR_BASE, (uintptr_t)registers.Idtr.Base);
		__vmx_vmwrite(GUEST_IDTR_LIMIT, registers.Idtr.Limit);
		__vmx_vmwrite(HOST_IDTR_BASE, (uintptr_t)registers.Idtr.Base);
 
		__vmx_vmwrite(CR0_READ_SHADOW, registers.Cr0);
		__vmx_vmwrite(HOST_CR0, registers.Cr0);
		__vmx_vmwrite(GUEST_CR0, registers.Cr0);
 
		__vmx_vmwrite(HOST_CR3, get_host_cr3());
		__vmx_vmwrite(GUEST_CR3, registers.Cr3);
 
		__vmx_vmwrite(HOST_CR4, registers.Cr4);
		__vmx_vmwrite(GUEST_CR4, registers.Cr4);
		__vmx_vmwrite(CR4_READ_SHADOW, registers.Cr4);
 
		__vmx_vmwrite(GUEST_IA32_DEBUGCTL, registers.DebugControl);
		__vmx_vmwrite(GUEST_DR7, registers.KernelDr7);
 
		__vmx_vmwrite(GUEST_RSP, (uintptr_t)vcpu->host_stack + KERNEL_STACK_SIZE - sizeof(CONTEXT));
		__vmx_vmwrite(GUEST_RIP, (uintptr_t)restore_after_launch);
		__vmx_vmwrite(GUEST_RFLAGS, ctx->EFlags);
 
		C_ASSERT((KERNEL_STACK_SIZE - sizeof(CONTEXT)) % 16 == 0);
		__vmx_vmwrite(HOST_RSP, (uintptr_t)vcpu->host_stack + 0x6000 - sizeof(CONTEXT));
		__vmx_vmwrite(HOST_RIP, (uintptr_t)vmexit_handler);
 
		//new
 
		__vmx_vmwrite(GUEST_ACTIVITY_STATE, 0);  
		__vmx_vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, 0);
		__vmx_vmwrite(GUEST_SYSENTER_CS, __readmsr(IA32_SYSENTER_CS));
		__vmx_vmwrite(GUEST_SYSENTER_ESP, __readmsr(IA32_SYSENTER_ESP));
		__vmx_vmwrite(GUEST_SYSENTER_EIP, __readmsr(IA32_SYSENTER_EIP));
		__vmx_vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
 
		return true;
	}

bool execute_vmclear(vcpu_data* cpu_data)
{
	int status = __vmx_vmclear(reinterpret_cast<std::uint64_t*>(&cpu_data->vmcs_region_physical.QuadPart));
 
	if (status != 0)
	{
		DbgPrint("vmclear failed with status -> 0x%llx", status);
		return false;
	}
 
	DbgPrint("vmclear succesfull");
	return true;
}
 
bool execute_vmxon(vcpu_data* cpu_data)
{
	unsigned char status = __vmx_on((unsigned long long*) & cpu_data->vmxon_region_physical.QuadPart);
 
	if (status != 0)
	{
		DbgPrint("vmxon failed with status: %d", status);
		return false;
	}
 
	DbgPrint("vmxon succesfull");
	cpu_data->vmx_enabled = true;
	return true;
 
}
 
 
bool execute_vmptrld(vcpu_data* cpu_data)
{
	int status = __vmx_vmptrld(reinterpret_cast<std::uint64_t*>(&cpu_data->vmcs_region_physical.QuadPart));
 
	if (status != 0)
	{
		DbgPrint("vmptrld failed with status -> 0x%llx", status);
		__vmx_off();
		return false;
	}
	DbgPrint("vmptrld succcesfull");
	return true;
}
 
bool execute_vmlaunch(vcpu_data* cpu_data)
{
 
	int status = __vmx_vmlaunch();
 
	ULONG64 error = 0;
	__vmx_vmread(VM_INSTRUCTION_ERROR, &error);
 
	DbgPrint("vmlaunch error -> 0x%llx", error);
 
	return true;  
}
 
 
unsigned long long initialize_vmx_on_core(unsigned long long rcx)
{
	unsigned long core_id = KeGetCurrentProcessorNumber();
	DbgPrint("initializing vmx on core id -> 0x%llx", core_id);
 
	if (!vmx::enable_vmx())
	{
		DbgPrint("failed to enable vmx on core id -> 0x%llx", core_id);
		return 0;
	}
 
	vcpu_data* cpu_data = vmx::allocate_vmx_regions();
	if (!cpu_data)
	{
		DbgPrint("failed to allocate vmx regions on core id -> 0x%llx", core_id);
		return 0;
	}
 
	if (!vmx::execute_vmxon(cpu_data))
	{
		DbgPrint("failed to execute vmxon on core id -> 0x%llx", core_id);
		return 0;
	}
 
	if (!vmx::execute_vmclear(cpu_data))
	{
		DbgPrint("failed to execute vmclear on core id -> 0x%llx", core_id);
		return 0;
	}
 
	if (!vmx::execute_vmptrld(cpu_data))
	{
		DbgPrint("failed to execute vmptrld on core id -> 0x%llx", core_id);
		return 0;
	}
 
	g_cpu_data[core_id] = cpu_data;
 
	capture_registers(&cpu_data->special_registers);
 
	cpu_data->ctx_frame.ContextFlags = CONTEXT_ALL;
	capture_ctx(&cpu_data->ctx_frame);
 
	DbgPrint("entering check");
	DbgPrint("eflags -> 0x%llx", __readeflags());
 
	if (!vmx::setup_vmcs(cpu_data, &cpu_data->ctx_frame))
	{
		DbgPrint("failed to setup vmcs on core id -> 0x%llx", core_id);
		return 0x0;
	}
 
	if (!vmx::execute_vmlaunch(cpu_data))
	{
		DbgPrint("vmlaunch failed on core id -> 0x%llx", core_id);
		return 0x0;
	}
 
 
	DbgPrint("successfully entered vmx root mode on core id -> 0x%llx", core_id);
	return 1;
}
 
bool virtualise_all_cores()
{
	if (!vmx::is_vmx_supported())
	{
		DbgPrint("vmx not supported");
		return false;
	}
 
	g_core_count = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
	DbgPrint("initializing vmx on %d cores", g_core_count);
 
	KeIpiGenericCall(initialize_vmx_on_core, 0);
 
	DbgPrint("vmx initialization complete");
 
	return true;
 
}

btw, hyper-v is disabled.

When in doubt go look at the virtualbox source code.

Right before a VMLAUNCH, vmread all of the values you wrote and compare them to a working hypervisor. Like Hypervisor from scratch. Also use "dg" to check your segments. It's almost always the damned segments.

hi thanks for your reply luckily i was able to fix it now the hypervisor can virtualise all my cores but after 5-10 seconds, my system either freezes or fully crashes. Do you know why this is happening and how to fix it? Thanks.

What does the bugcheck say? Did you enable USER_WAIT_PAUSE in secondary controls? That's mandatory for win11.

there is no bugcheck. Its either a freeze or crash and by crash i mean like my whole pc turning off. Also, im on windows 10.

That's probably a triple fault. Did you add that secondary exex control? Compare your initialization to this:

Try putting DbgPrint's everywhere. Does guest execute from where you captured the context?

Hi yes i added the VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE thing to secondary vmexec control and i tested my hypervisor driver on both my main machine and vmware virtual machine.

on my host machine, when i run the driver my system slowly gets corrupted. For the first 5 seconds its fine then i get some weird errors like filesystem error code -1235653 then my background disappears and then the taskbar and at the end, there is only a white screen and my cursor.

anyways, as im still learning hypervisors i have no intent of publishing this anyway so heres the full code

#pragma once
#include <intrin.h>
#include <ntifs.h>
#include "memory.h"
#include "ia32.h"


#define KERNEL_STACK_SIZE 0x6000

#include "vmx_structs.h"

typedef struct vmx_region
{
    unsigned long revision_id;
    unsigned char data[PAGE_SIZE - sizeof(unsigned long)];
} vmx_region;



typedef struct vcpu_data
{
    PHYSICAL_ADDRESS vmxon_region_physical;
    PHYSICAL_ADDRESS vmcs_region_physical;

    vmx_region* vmxon_region;
    vmx_region* vmcs_region;

    void* host_stack;
    bool vmx_enabled;

    void* msr_bitmap;
    PHYSICAL_ADDRESS physical_msr_bitmap;

    vmx_capabilities caps;

    CONTEXT ctx_frame;
    special_registers_t special_registers;

    volatile vmx_state vmx_state;
} vcpu_data;


namespace vmx
{
    extern "C" void vm_restore_context(CONTEXT* ctx);

    unsigned long get_vmx_revision_id()
    {
        auto vmx_basic = __readmsr(MSR_IA32_VMX_BASIC);
        unsigned long revision_id = vmx_basic & 0x7FFFFFFF;
        DbgPrint("vmx revision id 0x%X\n", revision_id);
        return revision_id;
    }

   

    unsigned long adjust_controls(unsigned long requested, std::uint64_t msr_value)
    {
        requested |= (msr_value & 0xFFFFFFFF);
        requested &= (msr_value >> 32);
        return requested;
    }

    extern "C"
    {
        void capture_ctx(CONTEXT* context);
        DECLSPEC_NORETURN void restore_ctx(CONTEXT* context);
        void vmexit_handler();
        DECLSPEC_NORETURN void vmentry_handler(PCONTEXT context);
    }

    void vmx_resume()
    {
        __vmx_vmresume();

        ULONG64 error = 0;
        __vmx_vmread(VM_INSTRUCTION_ERROR, &error);
        DbgPrint("vmresume failed with error 0x%llx\n", error);
        __debugbreak();
    }

    void vmx_handle_cpuid(vp_state* vp_state)
    {
        INT32 cpu_info[4];

        if ((vp_state->vp_regs->Rax == 0x41414141) &&
            (vp_state->vp_regs->Rcx == 0x42424242) &&
            ((vmx_read(GUEST_CS_SELECTOR) & RPL_MASK) == DPL_SYSTEM))
        {
            vp_state->exit_vm = TRUE;
            return;
        }

        __cpuidex(cpu_info, (INT32)vp_state->vp_regs->Rax, (INT32)vp_state->vp_regs->Rcx);

        if (vp_state->vp_regs->Rax == 1)
        {
            cpu_info[2] |= (1 << 31);
        }

        vp_state->vp_regs->Rax = cpu_info[0];
        vp_state->vp_regs->Rbx = cpu_info[1];
        vp_state->vp_regs->Rcx = cpu_info[2];
        vp_state->vp_regs->Rdx = cpu_info[3];
    }

    void vmx_handle_invd(vp_state* vp_state)
    {
        __wbinvd();
    }

    void vmx_handle_xsetbv(vp_state* vp_state)
    {
        _xsetbv((UINT32)vp_state->vp_regs->Rcx,
            ((UINT64)vp_state->vp_regs->Rdx << 32) | (UINT32)vp_state->vp_regs->Rax);
    }

    void vmx_handle_vmcall(vp_state* vp_state)
    {
        ULONG32 hypercall_number = (ULONG32)(vp_state->vp_regs->Rcx & 0xFFFF);

        switch (hypercall_number)
        {
        case 0x1337:
            vp_state->exit_vm = TRUE;
            break;
        default:
            DbgPrint("unknown hypercall 0x%x\n", hypercall_number);
            break;
        }
    }

    void vmx_handle_cr_access(vp_state* vp_state)
    {
        UINT64 exit_qual = vp_state->exit_qualification;
        UINT64 cr_num = (exit_qual >> 0) & 0xF;
        UINT64 access_type = (exit_qual >> 4) & 0x3;
        UINT64 reg_num = (exit_qual >> 8) & 0xF;

        PULONG64 reg_ptr = (PULONG64)&vp_state->vp_regs->Rax + reg_num;

        switch (access_type)
        {
        case 0:
            switch (cr_num)
            {
            case 0:
                vmx_write(GUEST_CR0, *reg_ptr);
                vmx_write(CR0_READ_SHADOW, *reg_ptr);
                break;
            case 3:
                vmx_write(GUEST_CR3, *reg_ptr);
                break;
            case 4:
                vmx_write(GUEST_CR4, *reg_ptr);
                vmx_write(CR4_READ_SHADOW, *reg_ptr);
                break;
            }
            break;

        case 1:
            switch (cr_num)
            {
            case 0:
                *reg_ptr = vmx_read(GUEST_CR0);
                break;
            case 3:
                *reg_ptr = vmx_read(GUEST_CR3);
                break;
            case 4:
                *reg_ptr = vmx_read(GUEST_CR4);
                break;
            }
            break;
        }
    }

    void vmx_handle_msr_read(vp_state* vp_state)
    {
        UINT32 msr = (UINT32)vp_state->vp_regs->Rcx;
        UINT64 msr_value = __readmsr(msr);

        vp_state->vp_regs->Rax = msr_value & 0xFFFFFFFF;
        vp_state->vp_regs->Rdx = msr_value >> 32;
    }

    void vmx_handle_msr_write(vp_state* vp_state)
    {
        UINT32 msr = (UINT32)vp_state->vp_regs->Rcx;
        UINT64 msr_value = ((UINT64)vp_state->vp_regs->Rdx << 32) | (UINT32)vp_state->vp_regs->Rax;

        __writemsr(msr, msr_value);
    }

    void vmx_handle_rdtsc(vp_state* vp_state)
    {
        UINT64 tsc = __rdtsc();
        vp_state->vp_regs->Rax = tsc & 0xFFFFFFFF;
        vp_state->vp_regs->Rdx = tsc >> 32;
    }

    void vmx_handle_rdtscp(vp_state* vp_state)
    {
        UINT32 aux;
        UINT64 tsc = __rdtscp(&aux);
        vp_state->vp_regs->Rax = tsc & 0xFFFFFFFF;
        vp_state->vp_regs->Rdx = tsc >> 32;
        vp_state->vp_regs->Rcx = aux;
    }

    void vmx_handle_exit(vp_state* vp_state)
    {
        bool advance_rip = true;

        switch (vp_state->exit_reason)
        {
        case EXIT_REASON_CPUID:
            vmx_handle_cpuid(vp_state);
            break;
        case EXIT_REASON_INVD:
        case EXIT_REASON_WBINVD:
            vmx_handle_invd(vp_state);
            break;
        case EXIT_REASON_XSETBV:
            vmx_handle_xsetbv(vp_state);
            break;
        case EXIT_REASON_VMCALL:
            vmx_handle_vmcall(vp_state);
            break;
        case EXIT_REASON_CR_ACCESS:
            vmx_handle_cr_access(vp_state);
            break;
        case EXIT_REASON_MSR_READ:
            vmx_handle_msr_read(vp_state);
            break;
        case EXIT_REASON_MSR_WRITE:
            vmx_handle_msr_write(vp_state);
            break;
        case EXIT_REASON_RDTSC:
            vmx_handle_rdtsc(vp_state);
            break;
        case EXIT_REASON_RDTSCP:
            vmx_handle_rdtscp(vp_state);
            break;
        case EXIT_REASON_EXCEPTION_NMI:
        {
            UINT32 intr_info = (UINT32)vmx_read(VM_EXIT_INTR_INFO);
            UINT32 vector = intr_info & 0xFF;
            UINT32 intr_type = (intr_info >> 8) & 0x7;
            UINT32 error_valid = (intr_info >> 11) & 0x1;
            UINT32 valid = (intr_info >> 31) & 0x1;

            if (valid)
            {
                UINT32 error_code = 0;
                if (error_valid)
                    error_code = (UINT32)vmx_read(VM_EXIT_INTR_ERROR_CODE);

                UINT32 instr_len = (UINT32)vmx_read(VM_EXIT_INSTRUCTION_LEN);

                vmx_write(0x00004016, intr_info);
                if (error_valid)
                    vmx_write(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
                vmx_write(VM_ENTRY_INSTRUCTION_LEN, instr_len);

                advance_rip = false;
            }
            break;
        }
        default:
            DbgPrint("unhandled exit reason 0x%x at rip 0x%llx\n",
                vp_state->exit_reason, vp_state->guest_rip);
            vp_state->exit_vm = TRUE;
            advance_rip = false;
            break;
        }

        if (advance_rip && !vp_state->exit_vm)
        {
            vp_state->guest_rip += vmx_read(VM_EXIT_INSTRUCTION_LEN);
            vmx_write(GUEST_RIP, vp_state->guest_rip);
        }
    }

    extern "C" DECLSPEC_NORETURN void vmentry_handler(PCONTEXT context)
    {
        vp_state guest_context;
        vcpu_data* vp_data;

        context->Rcx = *(UINT64*)((uintptr_t)context - sizeof(context->Rcx));

        vp_data = (vcpu_data*)((uintptr_t)(context + 1) - KERNEL_STACK_SIZE);

        guest_context.guest_eflags = vmx::vmx_read(GUEST_RFLAGS);
        guest_context.guest_rip = vmx::vmx_read(GUEST_RIP);
        guest_context.guest_rsp = vmx::vmx_read(GUEST_RSP);
        guest_context.exit_reason = vmx::vmx_read(VM_EXIT_REASON) & 0xFFFF;
        guest_context.exit_qualification = vmx::vmx_read(EXIT_QUALIFICATION);
        guest_context.vp_regs = context;
        guest_context.exit_vm = FALSE;

        vmx::vmx_handle_exit(&guest_context);

        if (guest_context.exit_vm != FALSE)
        {
            context->Rax = (uintptr_t)vp_data >> 32;
            context->Rbx = (uintptr_t)vp_data & 0xFFFFFFFF;
            context->Rcx = 0x43434343;

            __writecr3(vmx::vmx_read(GUEST_CR3));

            context->Rsp = guest_context.guest_rsp;
            context->Rip = (UINT64)guest_context.guest_rip;
            context->EFlags = (UINT32)guest_context.guest_eflags;

            __vmx_off();
            vp_data->vmx_enabled = false;
            vp_data->vmx_state = VMX_STATE_OFF;

            DbgPrint("exited vmx root mode on core %lu\n", KeGetCurrentProcessorNumber());
        }
        else
        {
            context->Rsp += sizeof(context->Rcx);
            context->Rip = (UINT64)vmx::vmx_resume;
        }

        vm_restore_context(context);
    }

    ULONG vmx_get_segment_access_rights(USHORT selector)
    {
        if (selector == 0)
            return 0x10000;

        ULONG_PTR gdt_base = get_gdt_base();

        struct segment_descriptor_entry
        {
            USHORT limit_low;
            USHORT base_low;
            UCHAR base_mid;
            UCHAR access;
            UCHAR granularity;
            UCHAR base_high;
        } *descriptor;

        descriptor = (segment_descriptor_entry*)(gdt_base + (selector & ~7));

        ULONG access_rights = descriptor->access;
        access_rights |= (descriptor->granularity & 0xF0) << 4;

        return access_rights;
    }

    void convert_gdt_entry(void* gdt_base, std::uint16_t selector, PVMX_GDTENTRY64 gdt_entry)
    {
        PKGDTENTRY64 gdt_entry_1;

        if ((selector == 0) || (selector & SELECTOR_TABLE_INDEX) != 0)
        {
            gdt_entry->Limit = gdt_entry->AccessRights = 0;
            gdt_entry->Base = 0;
            gdt_entry->Selector = 0;
            gdt_entry->Bits.Unusable = TRUE;
            return;
        }

        gdt_entry_1 = (PKGDTENTRY64)((uintptr_t)gdt_base + (selector & ~RPL_MASK));

        gdt_entry->Selector = selector;
        gdt_entry->Limit = __segmentlimit(selector);

        gdt_entry->Base = ((gdt_entry_1->Bytes.BaseHigh << 24) |
            (gdt_entry_1->Bytes.BaseMiddle << 16) |
            (gdt_entry_1->BaseLow)) & 0xFFFFFFFF;
        gdt_entry->Base |= ((gdt_entry_1->Bits.Type & 0x10) == 0) ?
            ((uintptr_t)gdt_entry_1->BaseUpper << 32) : 0;

        gdt_entry->AccessRights = 0;
        gdt_entry->Bytes.Flags1 = gdt_entry_1->Bytes.Flags1;
        gdt_entry->Bytes.Flags2 = gdt_entry_1->Bytes.Flags2;

        gdt_entry->Bits.Reserved = 0;
        gdt_entry->Bits.Unusable = !gdt_entry_1->Bits.Present;
    }

    void capture_registers(special_registers_t* special_registers)
    {
        special_registers->Cr0 = __readcr0();
        special_registers->Cr3 = __readcr3();
        special_registers->Cr4 = __readcr4();
        special_registers->DebugControl = __readmsr(MSR_DEBUG_CTL);
        special_registers->MsrGsBase = __readmsr(MSR_GS_BASE);
        special_registers->KernelDr7 = __readdr(7);
        asm_sgdt(&special_registers->Gdtr.Limit);
        __sidt(&special_registers->Idtr.Limit);
        special_registers->Tr = get_tr();
        special_registers->Ldtr = get_ldtr();
    }

    void read_vmx_capabilities(vmx_capabilities* caps)
    {
        caps->vmx_basic = __readmsr(MSR_IA32_VMX_BASIC);
        caps->pinbased_ctls = __readmsr(MSR_IA32_VMX_PINBASED_CTLS);
        caps->procbased_ctls = __readmsr(MSR_IA32_VMX_PROCBASED_CTLS);
        caps->exit_ctls = __readmsr(MSR_IA32_VMX_EXIT_CTLS);
        caps->entry_ctls = __readmsr(MSR_IA32_VMX_ENTRY_CTLS);
        caps->misc = __readmsr(MSR_IA32_VMX_MISC);
        caps->cr0_fixed0 = __readmsr(MSR_IA32_VMX_CR0_FIXED0);
        caps->cr0_fixed1 = __readmsr(MSR_IA32_VMX_CR0_FIXED1);
        caps->cr4_fixed0 = __readmsr(MSR_IA32_VMX_CR4_FIXED0);
        caps->cr4_fixed1 = __readmsr(MSR_IA32_VMX_CR4_FIXED1);
        caps->vmcs_enum = __readmsr(MSR_IA32_VMX_VMCS_ENUM);
        caps->procbased_ctls2 = __readmsr(MSR_IA32_VMX_PROCBASED_CTLS2);

        if (caps->vmx_basic & (1ULL << 55))
        {
            caps->true_pinbased_ctls = __readmsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS);
            caps->true_procbased_ctls = __readmsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS);
            caps->true_exit_ctls = __readmsr(MSR_IA32_VMX_TRUE_EXIT_CTLS);
            caps->true_entry_ctls = __readmsr(MSR_IA32_VMX_TRUE_ENTRY_CTLS);
        }
        else
        {
            caps->true_pinbased_ctls = caps->pinbased_ctls;
            caps->true_procbased_ctls = caps->procbased_ctls;
            caps->true_exit_ctls = caps->exit_ctls;
            caps->true_entry_ctls = caps->entry_ctls;
        }
    }

    bool setup_vmcs(vcpu_data* vcpu, CONTEXT* ctx)
    {
        if (!vcpu)
            return false;

        special_registers_t registers;
        capture_registers(&registers);

        read_vmx_capabilities(&vcpu->caps);

        registers.Cr0 &= vcpu->caps.cr0_fixed1;
        registers.Cr0 |= vcpu->caps.cr0_fixed0;
        registers.Cr4 &= vcpu->caps.cr4_fixed1;
        registers.Cr4 |= vcpu->caps.cr4_fixed0;

        __writecr0(registers.Cr0);
        __writecr4(registers.Cr4);

        vmx_write(VMCS_LINK_POINTER, ~0ULL);
        vmx_write(MSR_BITMAP, vcpu->physical_msr_bitmap.QuadPart);

        vmx_write(PIN_BASED_VM_EXEC_CONTROL,
            adjust_controls(0, vcpu->caps.true_pinbased_ctls));

        vmx_write(CPU_BASED_VM_EXEC_CONTROL,
            adjust_controls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS,
                vcpu->caps.true_procbased_ctls));
        //VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE   
        vmx_write(SECONDARY_VM_EXEC_CONTROL,
            adjust_controls(SECONDARY_EXEC_ENABLE_RDTSCP | SECONDARY_EXEC_ENABLE_INVPCID | SECONDARY_EXEC_XSAVES | 0x04000000,
                vcpu->caps.procbased_ctls2));

        vmx_write(VM_EXIT_CONTROLS,
            adjust_controls(VM_EXIT_ACK_INTR_ON_EXIT | VM_EXIT_HOST_ADDR_SPACE_SIZE,
                vcpu->caps.true_exit_ctls));

        vmx_write(VM_ENTRY_CONTROLS,
            adjust_controls(VM_ENTRY_IA32E_MODE, vcpu->caps.true_entry_ctls));

        vmx_write(EXCEPTION_BITMAP, 0);

        VMX_GDTENTRY64 vmx_gdt_entry;

        convert_gdt_entry(registers.Gdtr.Base, ctx->SegCs, &vmx_gdt_entry);
        vmx_write(GUEST_CS_SELECTOR, vmx_gdt_entry.Selector);
        vmx_write(GUEST_CS_LIMIT, vmx_gdt_entry.Limit);
        vmx_write(GUEST_CS_AR_BYTES, vmx_gdt_entry.AccessRights);
        vmx_write(GUEST_CS_BASE, vmx_gdt_entry.Base);

        convert_gdt_entry(registers.Gdtr.Base, ctx->SegSs, &vmx_gdt_entry);
        vmx_write(GUEST_SS_SELECTOR, vmx_gdt_entry.Selector);
        vmx_write(GUEST_SS_LIMIT, vmx_gdt_entry.Limit);
        vmx_write(GUEST_SS_AR_BYTES, vmx_gdt_entry.AccessRights);
        vmx_write(GUEST_SS_BASE, vmx_gdt_entry.Base);

        convert_gdt_entry(registers.Gdtr.Base, ctx->SegDs, &vmx_gdt_entry);
        vmx_write(GUEST_DS_SELECTOR, vmx_gdt_entry.Selector);
        vmx_write(GUEST_DS_LIMIT, vmx_gdt_entry.Limit);
        vmx_write(GUEST_DS_AR_BYTES, vmx_gdt_entry.AccessRights);
        vmx_write(GUEST_DS_BASE, vmx_gdt_entry.Base);

        convert_gdt_entry(registers.Gdtr.Base, ctx->SegEs, &vmx_gdt_entry);
        vmx_write(GUEST_ES_SELECTOR, vmx_gdt_entry.Selector);
        vmx_write(GUEST_ES_LIMIT, vmx_gdt_entry.Limit);
        vmx_write(GUEST_ES_AR_BYTES, vmx_gdt_entry.AccessRights);
        vmx_write(GUEST_ES_BASE, vmx_gdt_entry.Base);

        convert_gdt_entry(registers.Gdtr.Base, ctx->SegFs, &vmx_gdt_entry);
        vmx_write(GUEST_FS_SELECTOR, vmx_gdt_entry.Selector);
        vmx_write(GUEST_FS_LIMIT, vmx_gdt_entry.Limit);
        vmx_write(GUEST_FS_AR_BYTES, vmx_gdt_entry.AccessRights);
        vmx_write(GUEST_FS_BASE, vmx_gdt_entry.Base);

        convert_gdt_entry(registers.Gdtr.Base, ctx->SegGs, &vmx_gdt_entry);
        vmx_write(GUEST_GS_SELECTOR, vmx_gdt_entry.Selector);
        vmx_write(GUEST_GS_LIMIT, vmx_gdt_entry.Limit);
        vmx_write(GUEST_GS_AR_BYTES, vmx_gdt_entry.AccessRights);
        vmx_write(GUEST_GS_BASE, registers.MsrGsBase);

        convert_gdt_entry(registers.Gdtr.Base, registers.Tr, &vmx_gdt_entry);
        vmx_write(GUEST_TR_SELECTOR, vmx_gdt_entry.Selector);
        vmx_write(GUEST_TR_LIMIT, vmx_gdt_entry.Limit);
        vmx_write(GUEST_TR_AR_BYTES, vmx_gdt_entry.AccessRights);
        vmx_write(GUEST_TR_BASE, vmx_gdt_entry.Base);

        convert_gdt_entry(registers.Gdtr.Base, registers.Ldtr, &vmx_gdt_entry);
        vmx_write(GUEST_LDTR_SELECTOR, vmx_gdt_entry.Selector);
        vmx_write(GUEST_LDTR_LIMIT, vmx_gdt_entry.Limit);
        vmx_write(GUEST_LDTR_AR_BYTES, vmx_gdt_entry.AccessRights);
        vmx_write(GUEST_LDTR_BASE, vmx_gdt_entry.Base);

        vmx_write(GUEST_GDTR_BASE, (uintptr_t)registers.Gdtr.Base);
        vmx_write(GUEST_GDTR_LIMIT, registers.Gdtr.Limit);

        vmx_write(GUEST_IDTR_BASE, (uintptr_t)registers.Idtr.Base);
        vmx_write(GUEST_IDTR_LIMIT, registers.Idtr.Limit);

        vmx_write(CR0_READ_SHADOW, registers.Cr0);
        vmx_write(GUEST_CR0, registers.Cr0);

        vmx_write(GUEST_CR3, registers.Cr3);

        vmx_write(GUEST_CR4, registers.Cr4);
        vmx_write(CR4_READ_SHADOW, registers.Cr4);

        vmx_write(GUEST_IA32_DEBUGCTL, registers.DebugControl);
        vmx_write(GUEST_DR7, registers.KernelDr7);

        vmx_write(GUEST_SYSENTER_CS, __readmsr(IA32_SYSENTER_CS));
        vmx_write(GUEST_SYSENTER_ESP, __readmsr(IA32_SYSENTER_ESP));
        vmx_write(GUEST_SYSENTER_EIP, __readmsr(IA32_SYSENTER_EIP));

        vmx_write(GUEST_RSP, ctx->Rsp);
        vmx_write(GUEST_RIP, ctx->Rip);
        vmx_write(GUEST_RFLAGS, ctx->EFlags);
        vmx_write(GUEST_ACTIVITY_STATE, 0);
        vmx_write(GUEST_INTERRUPTIBILITY_INFO, 0);
        vmx_write(GUEST_PENDING_DBG_EXCEPTIONS, 0);

        vmx_write(HOST_CS_SELECTOR, ctx->SegCs & ~RPL_MASK);
        vmx_write(HOST_SS_SELECTOR, ctx->SegSs & ~RPL_MASK);
        vmx_write(HOST_DS_SELECTOR, ctx->SegDs & ~RPL_MASK);
        vmx_write(HOST_ES_SELECTOR, ctx->SegEs & ~RPL_MASK);
        vmx_write(HOST_FS_SELECTOR, ctx->SegFs & ~RPL_MASK);
        vmx_write(HOST_GS_SELECTOR, ctx->SegGs & ~RPL_MASK);
        vmx_write(HOST_TR_SELECTOR, registers.Tr & ~RPL_MASK);

        convert_gdt_entry(registers.Gdtr.Base, ctx->SegFs, &vmx_gdt_entry);
        vmx_write(HOST_FS_BASE, vmx_gdt_entry.Base);

        vmx_write(HOST_GS_BASE, registers.MsrGsBase);

        convert_gdt_entry(registers.Gdtr.Base, registers.Tr, &vmx_gdt_entry);
        vmx_write(HOST_TR_BASE, vmx_gdt_entry.Base);

        vmx_write(HOST_GDTR_BASE, (uintptr_t)registers.Gdtr.Base);
        vmx_write(HOST_IDTR_BASE, (uintptr_t)registers.Idtr.Base);

        vmx_write(HOST_CR0, registers.Cr0);
        vmx_write(HOST_CR3, registers.Cr3);
        vmx_write(HOST_CR4, registers.Cr4);


        vmx_write(HOST_IA32_SYSENTER_CS, __readmsr(IA32_SYSENTER_CS));
        vmx_write(HOST_IA32_SYSENTER_ESP, __readmsr(IA32_SYSENTER_ESP));
        vmx_write(HOST_IA32_SYSENTER_EIP, __readmsr(IA32_SYSENTER_EIP));

        C_ASSERT((KERNEL_STACK_SIZE - sizeof(CONTEXT)) % 16 == 0);
        vmx_write(HOST_RSP, (uintptr_t)vcpu->host_stack + KERNEL_STACK_SIZE - sizeof(CONTEXT));
        vmx_write(HOST_RIP, (uintptr_t)vmexit_handler);

        DbgPrint("vmcs setup completed on core %lu\n", KeGetCurrentProcessorNumber());
        return true;
    }

    vcpu_data* allocate_vmx_regions()
    {
        vcpu_data* cpu_data = reinterpret_cast<vcpu_data*>(
            ExAllocatePool(NonPagedPool, sizeof(vcpu_data)));

        if (!cpu_data)
        {
            DbgPrint("failed to allocate vcpu data\n");
            return nullptr;
        }

        RtlZeroMemory(cpu_data, sizeof(vcpu_data));

        cpu_data->vmxon_region = reinterpret_cast<vmx_region*>(
            memory::MmAllocateIndependentPages(PAGE_SIZE));

        if (!cpu_data->vmxon_region)
        {
            DbgPrint("failed to allocate vmxon region\n");
            ExFreePool(cpu_data);
            return nullptr;
        }

        cpu_data->vmcs_region = reinterpret_cast<vmx_region*>(
            memory::MmAllocateIndependentPages(PAGE_SIZE));

        if (!cpu_data->vmcs_region)
        {
            DbgPrint("failed to allocate vmcs region\n");
            memory::MmFreeIndependentPages(cpu_data->vmxon_region, PAGE_SIZE);
            ExFreePool(cpu_data);
            return nullptr;
        }

        cpu_data->host_stack = ExAllocatePool(NonPagedPool, KERNEL_STACK_SIZE);
        
        if (!cpu_data->host_stack)
        {
            DbgPrint("failed to allocate host stack\n");
            memory::MmFreeIndependentPages(cpu_data->vmxon_region, PAGE_SIZE);
            memory::MmFreeIndependentPages(cpu_data->vmcs_region, PAGE_SIZE);
            ExFreePool(cpu_data);
            return nullptr;
        }

        cpu_data->msr_bitmap = ExAllocatePool(NonPagedPool, PAGE_SIZE);

        if (!cpu_data->msr_bitmap)
        {
            DbgPrint("failed to allocate msr bitmap\n");
            ExFreePool(cpu_data->host_stack);
            memory::MmFreeIndependentPages(cpu_data->vmxon_region, PAGE_SIZE);
            memory::MmFreeIndependentPages(cpu_data->vmcs_region, PAGE_SIZE);
            ExFreePool(cpu_data);
            return nullptr;
        }

        RtlZeroMemory(cpu_data->vmxon_region, PAGE_SIZE);
        RtlZeroMemory(cpu_data->vmcs_region, PAGE_SIZE);
        RtlZeroMemory(cpu_data->host_stack, KERNEL_STACK_SIZE);
        RtlZeroMemory(cpu_data->msr_bitmap, PAGE_SIZE);

        cpu_data->vmxon_region_physical = MmGetPhysicalAddress(cpu_data->vmxon_region);
        cpu_data->vmcs_region_physical = MmGetPhysicalAddress(cpu_data->vmcs_region);
        cpu_data->physical_msr_bitmap = MmGetPhysicalAddress(cpu_data->msr_bitmap);

        unsigned long rev_id = get_vmx_revision_id();
        cpu_data->vmxon_region->revision_id = rev_id;
        cpu_data->vmcs_region->revision_id = rev_id;

        return cpu_data;
    }

    bool execute_vmclear(vcpu_data* cpu_data)
    {
        int status = __vmx_vmclear(
            reinterpret_cast<std::uint64_t*>(&cpu_data->vmcs_region_physical.QuadPart));

        if (status != 0)
        {
            DbgPrint("vmclear failed with status %d\n", status);
            return false;
        }

        return true;
    }

    bool execute_vmxon(vcpu_data* cpu_data)
    {
        ULONG64 cr0 = __readcr0();
        ULONG64 cr4 = __readcr4();

        ULONG64 cr0_fixed0 = __readmsr(MSR_IA32_VMX_CR0_FIXED0);
        ULONG64 cr0_fixed1 = __readmsr(MSR_IA32_VMX_CR0_FIXED1);
        ULONG64 cr4_fixed0 = __readmsr(MSR_IA32_VMX_CR4_FIXED0);
        ULONG64 cr4_fixed1 = __readmsr(MSR_IA32_VMX_CR4_FIXED1);

        cr0 &= cr0_fixed1;
        cr0 |= cr0_fixed0;
        cr4 &= cr4_fixed1;
        cr4 |= cr4_fixed0;

        __writecr0(cr0);
        __writecr4(cr4);

        unsigned char status = __vmx_on(
            (unsigned long long*) & cpu_data->vmxon_region_physical.QuadPart);

        if (status != 0)
        {
            DbgPrint("vmxon failed with status %d\n", status);
            return false;
        }

        cpu_data->vmx_enabled = true;
        return true;
    }

    bool execute_vmptrld(vcpu_data* cpu_data)
    {
        int status = __vmx_vmptrld(
            reinterpret_cast<std::uint64_t*>(&cpu_data->vmcs_region_physical.QuadPart));

        if (status != 0)
        {
            DbgPrint("vmptrld failed with status %d\n", status);
            __vmx_off();
            return false;
        }

        return true;
    }

    unsigned long long initialize_vmx_on_core(unsigned long long argument)
    {
        unsigned long core_id = KeGetCurrentProcessorNumber();


        vcpu_data* cpu_data = g_cpu_data[core_id];

        if (cpu_data == nullptr)
        {
            if (!vmx::enable_vmx())
            {
                DbgPrint("failed to enable vmx on core %lu\n", core_id);
                return 0;
            }

            cpu_data = vmx::allocate_vmx_regions();
            if (!cpu_data)
            {
                DbgPrint("failed to allocate vmx regions on core %lu\n", core_id);
                return 0;
            }

            cpu_data->vmx_state = VMX_STATE_OFF;

            if (!vmx::execute_vmxon(cpu_data))
            {
                DbgPrint("failed to execute vmxon on core %lu\n", core_id);
                return 0;
            }

            if (!vmx::execute_vmclear(cpu_data))
            {
                DbgPrint("failed to execute vmclear on core %lu\n", core_id);
                return 0;
            }

            if (!vmx::execute_vmptrld(cpu_data))
            {
                DbgPrint("failed to execute vmptrld on core %lu\n", core_id);
                return 0;
            }

            g_cpu_data[core_id] = cpu_data;
        }

        cpu_data->ctx_frame.ContextFlags = CONTEXT_ALL;
        RtlCaptureContext(&cpu_data->ctx_frame);

        if (cpu_data->vmx_state == VMX_STATE_OFF)
        {
            capture_registers(&cpu_data->special_registers);

            if (!vmx::setup_vmcs(cpu_data, &cpu_data->ctx_frame))
            {
                DbgPrint("failed to setup vmcs on core %lu\n", core_id);
                return 0;
            }

            cpu_data->vmx_state = VMX_STATE_TRANSITION;

            int status = __vmx_vmlaunch();

            cpu_data->vmx_state = VMX_STATE_OFF;

            ULONG64 error = 0;
            __vmx_vmread(VM_INSTRUCTION_ERROR, &error);
            DbgPrint("vmlaunch failed on core %lu with error 0x%llx\n", core_id, error);

            return 0;
        }
        else if (cpu_data->vmx_state == VMX_STATE_TRANSITION)
        {
            cpu_data->vmx_state = VMX_STATE_ON;

            DbgPrint("vmlaunch succeeded on core %lu\n", core_id);

            vm_restore_context(&cpu_data->ctx_frame);
        }
        return 0;
    }

    bool virtualise_all_cores()
    {
        if (!vmx::is_vmx_supported())
        {
            DbgPrint("vmx not supported\n");
            return false;
        }

        g_core_count = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
        DbgPrint("initializing vmx on %lu cores\n", g_core_count);

        KeIpiGenericCall(initialize_vmx_on_core, 0);

        ULONG virtualized_cores = 0;
        for (ULONG i = 0; i < g_core_count; i++)
        {
            if (g_cpu_data[i] && g_cpu_data[i]->vmx_state == VMX_STATE_ON)
            {
                virtualized_cores++;
            }
        }

        if (virtualized_cores == g_core_count)
        {
            DbgPrint("successfully virtualized all %lu cores\n", g_core_count);
            return true;
        }
        else
        {
            DbgPrint("virtualized %lu out of %lu cores\n", virtualized_cores, g_core_count);
            return false;
        }
    }

    unsigned long long turn_off_vmx_on_core(unsigned long long argument)
    {
        unsigned long core_id = KeGetCurrentProcessorNumber();

        vcpu_data* cpu_data = g_cpu_data[core_id];
        if (cpu_data && cpu_data->vmx_enabled)
        {
            __vmx_off();
            cpu_data->vmx_enabled = false;
            cpu_data->vmx_state = VMX_STATE_OFF;
            DbgPrint("disabled vmx on core %lu\n", core_id);
        }

        return 0;
    }

    bool devirtualise_all_cores()
    {
        DbgPrint("disabling vmx on all cores\n");
        KeIpiGenericCall(turn_off_vmx_on_core, 0);

        for (ULONG i = 0; i < g_core_count; i++)
        {
            if (g_cpu_data[i])
            {
                if (g_cpu_data[i]->msr_bitmap)
                    ExFreePool(g_cpu_data[i]->msr_bitmap);
                if (g_cpu_data[i]->host_stack)
                    ExFreePool(g_cpu_data[i]->host_stack);
                if (g_cpu_data[i]->vmcs_region)
                    memory::MmFreeIndependentPages(g_cpu_data[i]->vmcs_region, PAGE_SIZE);
                if (g_cpu_data[i]->vmxon_region)
                    memory::MmFreeIndependentPages(g_cpu_data[i]->vmxon_region, PAGE_SIZE);

                ExFreePool(g_cpu_data[i]);
                g_cpu_data[i] = nullptr;
            }
        }

        return true;
    }
}

some parts were taken from simplevisor and hyperbone

vm_restore_context PROC

	push rbp
	push rsi
	push rdi
	sub rsp, 30h
	mov rbp, rsp
	movaps  xmm0, xmmword ptr [rcx+1A0h]
	movaps  xmm1, xmmword ptr [rcx+1B0h]
	movaps  xmm2, xmmword ptr [rcx+1C0h]
	movaps  xmm3, xmmword ptr [rcx+1D0h]
	movaps  xmm4, xmmword ptr [rcx+1E0h]
	movaps  xmm5, xmmword ptr [rcx+1F0h]
	movaps  xmm6, xmmword ptr [rcx+200h]
	movaps  xmm7, xmmword ptr [rcx+210h]
	movaps  xmm8, xmmword ptr [rcx+220h]
	movaps  xmm9, xmmword ptr [rcx+230h]
	movaps  xmm10, xmmword ptr [rcx+240h]
	movaps  xmm11, xmmword ptr [rcx+250h]
	movaps  xmm12, xmmword ptr [rcx+260h]
	movaps  xmm13, xmmword ptr [rcx+270h]
	movaps  xmm14, xmmword ptr [rcx+280h]
	movaps  xmm15, xmmword ptr [rcx+290h]
	ldmxcsr dword ptr [rcx+34h]

	mov     ax, [rcx+42h]
	mov     [rsp+20h], ax
	mov     rax, [rcx+98h] ; RSP
	mov     [rsp+18h], rax
	mov     eax, [rcx+44h]
	mov     [rsp+10h], eax
	mov     ax, [rcx+38h]
	mov     [rsp+08h], ax
	mov     rax, [rcx+0F8h] ; RIP
	mov     [rsp+00h], rax ;

	mov     rax, [rcx+78h]
	mov     rdx, [rcx+88h]
	mov     r8, [rcx+0B8h]
	mov     r9, [rcx+0C0h]
	mov     r10, [rcx+0C8h]
	mov     r11, [rcx+0D0h]
	cli
	mov     rbx, [rcx+90h]
	mov     rsi, [rcx+0A8h]
	mov     rdi, [rcx+0B0h]
	mov     rbp, [rcx+0A0h]
	mov     r12, [rcx+0D8h]
	mov     r13, [rcx+0E0h]
	mov     r14, [rcx+0E8h]
	mov     r15, [rcx+0F0h]
	mov     rcx, [rcx+80h]
	iretq

vm_restore_context ENDP
PUBLIC vmexit_handler
vmexit_handler PROC
    push rcx
    lea rcx, [rsp + 8h]
    call capture_ctx
    jmp vmentry_handler
vmexit_handler ENDP
END
PUBLIC capture_ctx
capture_ctx PROC
    pushfq
    
    mov [rcx + 78h], rax
    mov [rcx + 80h], rcx
    mov [rcx + 88h], rdx
    mov [rcx + 0B8h], r8
    mov [rcx + 0C0h], r9
    mov [rcx + 0C8h], r10
    mov [rcx + 0D0h], r11
    
    mov word ptr [rcx + 38h], cs
    mov word ptr [rcx + 3Ah], ds
    mov word ptr [rcx + 3Ch], es
    mov word ptr [rcx + 42h], ss
    mov word ptr [rcx + 3Eh], fs
    mov word ptr [rcx + 40h], gs
    
    mov [rcx + 90h], rbx
    mov [rcx + 0A0h], rbp
    mov [rcx + 0A8h], rsi
    mov [rcx + 0B0h], rdi
    mov [rcx + 0D8h], r12
    mov [rcx + 0E0h], r13
    mov [rcx + 0E8h], r14
    mov [rcx + 0F0h], r15
    
    lea rax, [rsp + 10h]
    mov [rcx + 98h], rax
    
    mov rax, [rsp + 8]
    mov [rcx + 0F8h], rax
    
    mov eax, [rsp]
    mov [rcx + 44h], eax
    
    add rsp, 8
    ret
    
capture_ctx ENDP

Had the same issue. Do the absolute minimum for handling a VMEXIT. Make sure the stack in ctx stays same.
See if it persists.

Thanks ill try this tommorow and hopefully i can get my hypervisor to finally work. Also you had the same specific issues as me? Like weird file errors and background disappearing?

Yep. Couldn't quite remember how did I fix it. But you are certainly corrupting guest memory

I see. Atleast vmlaunch is finally working, previously when i called vmlaunch i instantly blue screened.