/*!
    @file HostUtils.c

    @brief Utility functions and structures for the host.

    @author Satoshi Tanda

    @copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
 */
#include "HostUtils.h"
#include "Logger.h"
#include "ExtendedPageTables.h"
#include "Utils.h"
#include "MemoryAccess.h"

/*!
    @brief Dumps the segment access rights value.

    @param[in] AccessRights - The segment access rights value to dump.
 */
static
VOID
DumpAccessRights (
    _In_ UINT64 AccessRights
    )
{
    VMX_SEGMENT_ACCESS_RIGHTS rights;

    rights.Flags = (UINT32)AccessRights;
    LOG_ERROR(" - Type      = %ul", rights.Type);
    LOG_ERROR(" - S         = %ul", rights.DescriptorType);
    LOG_ERROR(" - DPL       = %ul", rights.DescriptorPrivilegeLevel);
    LOG_ERROR(" - P         = %ul", rights.Present);
    LOG_ERROR(" - Reserved1 = %ul", rights.Reserved1);
    LOG_ERROR(" - Available = %ul", rights.AvailableBit);
    LOG_ERROR(" - L         = %ul", rights.LongMode);
    LOG_ERROR(" - D/B       = %ul", rights.DefaultBig);
    LOG_ERROR(" - G         = %ul", rights.Granularity);
    LOG_ERROR(" - Unusable  = %ul", rights.Unusable);
    LOG_ERROR(" - Reserved2 = %ul", rights.Reserved2);
}

VOID
DumpHostState (
    )
{
    //
    // 16-Bit Host-State Fields
    //
    LOG_ERROR("Host ES Selector              = %016llx", VmxRead(VMCS_HOST_ES_SELECTOR));
    LOG_ERROR("Host CS Selector              = %016llx", VmxRead(VMCS_HOST_CS_SELECTOR));
    LOG_ERROR("Host SS Selector              = %016llx", VmxRead(VMCS_HOST_SS_SELECTOR));
    LOG_ERROR("Host DS Selector              = %016llx", VmxRead(VMCS_HOST_DS_SELECTOR));
    LOG_ERROR("Host FS Selector              = %016llx", VmxRead(VMCS_HOST_FS_SELECTOR));
    LOG_ERROR("Host GS Selector              = %016llx", VmxRead(VMCS_HOST_GS_SELECTOR));
    LOG_ERROR("Host TR Selector              = %016llx", VmxRead(VMCS_HOST_TR_SELECTOR));

    //
    // 64-Bit Host-State Fields
    //
    LOG_ERROR("Host IA32_PAT                 = %016llx", VmxRead(VMCS_HOST_PAT));
    LOG_ERROR("Host IA32_EFER                = %016llx", VmxRead(VMCS_HOST_EFER));
    LOG_ERROR("Host IA32_PERF_GLOBAL_CTRL    = %016llx", VmxRead(VMCS_HOST_PERF_GLOBAL_CTRL));

    //
    // 32-Bit Host-State Fields
    //
    LOG_ERROR("Host IA32_SYSENTER_CS         = %016llx", VmxRead(VMCS_HOST_SYSENTER_CS));

    //
    // Natural-Width Host-State Fields
    //
    LOG_ERROR("Host CR0                      = %016llx", VmxRead(VMCS_HOST_CR0));
    LOG_ERROR("Host CR3                      = %016llx", VmxRead(VMCS_HOST_CR3));
    LOG_ERROR("Host CR4                      = %016llx", VmxRead(VMCS_HOST_CR4));
    LOG_ERROR("Host FS Base                  = %016llx", VmxRead(VMCS_HOST_FS_BASE));
    LOG_ERROR("Host GS Base                  = %016llx", VmxRead(VMCS_HOST_GS_BASE));
    LOG_ERROR("Host TR base                  = %016llx", VmxRead(VMCS_HOST_TR_BASE));
    LOG_ERROR("Host GDTR base                = %016llx", VmxRead(VMCS_HOST_GDTR_BASE));
    LOG_ERROR("Host IDTR base                = %016llx", VmxRead(VMCS_HOST_IDTR_BASE));
    LOG_ERROR("Host IA32_SYSENTER_ESP        = %016llx", VmxRead(VMCS_HOST_SYSENTER_ESP));
    LOG_ERROR("Host IA32_SYSENTER_EIP        = %016llx", VmxRead(VMCS_HOST_SYSENTER_EIP));
    LOG_ERROR("Host RSP                      = %016llx", VmxRead(VMCS_HOST_RSP));
    LOG_ERROR("Host RIP                      = %016llx", VmxRead(VMCS_HOST_RIP));
}

VOID
DumpGuestState (
    )
{
    //
    // 16-Bit Guest-State Fields
    //
    LOG_ERROR("Guest ES Selector              = %016llx", VmxRead(VMCS_GUEST_ES_SELECTOR));
    LOG_ERROR("Guest CS Selector              = %016llx", VmxRead(VMCS_GUEST_CS_SELECTOR));
    LOG_ERROR("Guest SS Selector              = %016llx", VmxRead(VMCS_GUEST_SS_SELECTOR));
    LOG_ERROR("Guest DS Selector              = %016llx", VmxRead(VMCS_GUEST_DS_SELECTOR));
    LOG_ERROR("Guest FS Selector              = %016llx", VmxRead(VMCS_GUEST_FS_SELECTOR));
    LOG_ERROR("Guest GS Selector              = %016llx", VmxRead(VMCS_GUEST_GS_SELECTOR));
    LOG_ERROR("Guest LDTR Selector            = %016llx", VmxRead(VMCS_GUEST_LDTR_SELECTOR));
    LOG_ERROR("Guest TR Selector              = %016llx", VmxRead(VMCS_GUEST_TR_SELECTOR));
    LOG_ERROR("Guest interrupt status         = %016llx", VmxRead(VMCS_GUEST_INTERRUPT_STATUS));
    LOG_ERROR("PML index                      = %016llx", VmxRead(VMCS_GUEST_PML_INDEX));

    //
    // 64-Bit Guest-State Fields
    //
    LOG_ERROR("VMCS link pointer              = %016llx", VmxRead(VMCS_GUEST_VMCS_LINK_POINTER));
    LOG_ERROR("Guest IA32_DEBUGCTL            = %016llx", VmxRead(VMCS_GUEST_DEBUGCTL));
    LOG_ERROR("Guest IA32_PAT                 = %016llx", VmxRead(VMCS_GUEST_PAT));
    LOG_ERROR("Guest IA32_EFER                = %016llx", VmxRead(VMCS_GUEST_EFER));
    LOG_ERROR("Guest IA32_PERF_GLOBAL_CTRL    = %016llx", VmxRead(VMCS_GUEST_PERF_GLOBAL_CTRL));
    LOG_ERROR("Guest PDPTE0                   = %016llx", VmxRead(VMCS_GUEST_PDPTE0));
    LOG_ERROR("Guest PDPTE1                   = %016llx", VmxRead(VMCS_GUEST_PDPTE1));
    LOG_ERROR("Guest PDPTE2                   = %016llx", VmxRead(VMCS_GUEST_PDPTE2));
    LOG_ERROR("Guest PDPTE3                   = %016llx", VmxRead(VMCS_GUEST_PDPTE3));
    LOG_ERROR("Guest IA32_BNDCFGS             = %016llx", VmxRead(VMCS_GUEST_BNDCFGS));
    LOG_ERROR("Guest IA32_RTIT_CTL            = %016llx", VmxRead(VMCS_GUEST_RTIT_CTL));

    //
    // 32-Bit Guest-State Fields
    //
    LOG_ERROR("Guest ES Limit                 = %016llx", VmxRead(VMCS_GUEST_ES_LIMIT));
    LOG_ERROR("Guest CS Limit                 = %016llx", VmxRead(VMCS_GUEST_CS_LIMIT));
    LOG_ERROR("Guest SS Limit                 = %016llx", VmxRead(VMCS_GUEST_SS_LIMIT));
    LOG_ERROR("Guest DS Limit                 = %016llx", VmxRead(VMCS_GUEST_DS_LIMIT));
    LOG_ERROR("Guest FS Limit                 = %016llx", VmxRead(VMCS_GUEST_FS_LIMIT));
    LOG_ERROR("Guest GS Limit                 = %016llx", VmxRead(VMCS_GUEST_GS_LIMIT));
    LOG_ERROR("Guest LDTR Limit               = %016llx", VmxRead(VMCS_GUEST_LDTR_LIMIT));
    LOG_ERROR("Guest TR Limit                 = %016llx", VmxRead(VMCS_GUEST_TR_LIMIT));
    LOG_ERROR("Guest GDTR limit               = %016llx", VmxRead(VMCS_GUEST_GDTR_LIMIT));
    LOG_ERROR("Guest IDTR limit               = %016llx", VmxRead(VMCS_GUEST_IDTR_LIMIT));
    LOG_ERROR("Guest ES access rights         = %016llx", VmxRead(VMCS_GUEST_ES_ACCESS_RIGHTS));
    LOG_ERROR("Guest CS access rights         = %016llx", VmxRead(VMCS_GUEST_CS_ACCESS_RIGHTS));
    LOG_ERROR("Guest SS access rights         = %016llx", VmxRead(VMCS_GUEST_SS_ACCESS_RIGHTS));
    LOG_ERROR("Guest DS access rights         = %016llx", VmxRead(VMCS_GUEST_DS_ACCESS_RIGHTS));
    LOG_ERROR("Guest FS access rights         = %016llx", VmxRead(VMCS_GUEST_FS_ACCESS_RIGHTS));
    LOG_ERROR("Guest GS access rights         = %016llx", VmxRead(VMCS_GUEST_GS_ACCESS_RIGHTS));
    LOG_ERROR("Guest LDTR access rights       = %016llx", VmxRead(VMCS_GUEST_LDTR_ACCESS_RIGHTS));
    LOG_ERROR("Guest TR access rights         = %016llx", VmxRead(VMCS_GUEST_TR_ACCESS_RIGHTS));
    LOG_ERROR("Guest interruptibility state   = %016llx", VmxRead(VMCS_GUEST_INTERRUPTIBILITY_STATE));
    LOG_ERROR("Guest activity state           = %016llx", VmxRead(VMCS_GUEST_ACTIVITY_STATE));
    LOG_ERROR("Guest SMBASE                   = %016llx", VmxRead(VMCS_GUEST_SMBASE));
    LOG_ERROR("Guest IA32_SYSENTER_CS         = %016llx", VmxRead(VMCS_GUEST_SYSENTER_CS));
    LOG_ERROR("VMX-preemption timer value     = %016llx", VmxRead(VMCS_GUEST_VMX_PREEMPTION_TIMER_VALUE));

    //
    // Natural-Width Guest-State Fields
    //
    LOG_ERROR("Guest CR0                      = %016llx", VmxRead(VMCS_GUEST_CR0));
    LOG_ERROR("Guest CR3                      = %016llx", VmxRead(VMCS_GUEST_CR3));
    LOG_ERROR("Guest CR4                      = %016llx", VmxRead(VMCS_GUEST_CR4));
    LOG_ERROR("Guest ES Base                  = %016llx", VmxRead(VMCS_GUEST_ES_BASE));
    LOG_ERROR("Guest CS Base                  = %016llx", VmxRead(VMCS_GUEST_CS_BASE));
    LOG_ERROR("Guest SS Base                  = %016llx", VmxRead(VMCS_GUEST_SS_BASE));
    LOG_ERROR("Guest DS Base                  = %016llx", VmxRead(VMCS_GUEST_DS_BASE));
    LOG_ERROR("Guest FS Base                  = %016llx", VmxRead(VMCS_GUEST_FS_BASE));
    LOG_ERROR("Guest GS Base                  = %016llx", VmxRead(VMCS_GUEST_GS_BASE));
    LOG_ERROR("Guest LDTR base                = %016llx", VmxRead(VMCS_GUEST_LDTR_BASE));
    LOG_ERROR("Guest TR base                  = %016llx", VmxRead(VMCS_GUEST_TR_BASE));
    LOG_ERROR("Guest GDTR base                = %016llx", VmxRead(VMCS_GUEST_GDTR_BASE));
    LOG_ERROR("Guest IDTR base                = %016llx", VmxRead(VMCS_GUEST_IDTR_BASE));
    LOG_ERROR("Guest DR7                      = %016llx", VmxRead(VMCS_GUEST_DR7));
    LOG_ERROR("Guest RSP                      = %016llx", VmxRead(VMCS_GUEST_RSP));
    LOG_ERROR("Guest RIP                      = %016llx", VmxRead(VMCS_GUEST_RIP));
    LOG_ERROR("Guest RFLAGS                   = %016llx", VmxRead(VMCS_GUEST_RFLAGS));
    LOG_ERROR("Guest pending debug exceptions = %016llx", VmxRead(VMCS_GUEST_PENDING_DEBUG_EXCEPTIONS));
    LOG_ERROR("Guest IA32_SYSENTER_ESP        = %016llx", VmxRead(VMCS_GUEST_SYSENTER_ESP));
    LOG_ERROR("Guest IA32_SYSENTER_EIP        = %016llx", VmxRead(VMCS_GUEST_SYSENTER_EIP));
}

VOID
DumpControl (
    )
{
    //
    // 16-Bit Control Fields
    //
    LOG_ERROR("Virtual-processor identifier   = %016llx", VmxRead(VMCS_CTRL_VIRTUAL_PROCESSOR_IDENTIFIER));
    LOG_ERROR("Posted-interrupt notification vector = %016llx", VmxRead(VMCS_CTRL_POSTED_INTERRUPT_NOTIFICATION_VECTOR));
    LOG_ERROR("EPTP index                     = %016llx", VmxRead(VMCS_CTRL_EPTP_INDEX));

    //
    // 64-Bit Control Fields
    //
    LOG_ERROR("Address of I/O bitmap A        = %016llx", VmxRead(VMCS_CTRL_IO_BITMAP_A_ADDRESS));
    LOG_ERROR("Address of I/O bitmap B        = %016llx", VmxRead(VMCS_CTRL_IO_BITMAP_B_ADDRESS));
    LOG_ERROR("Address of MSR bitmaps         = %016llx", VmxRead(VMCS_CTRL_MSR_BITMAP_ADDRESS));
    LOG_ERROR("VM-exit MSR-store address      = %016llx", VmxRead(VMCS_CTRL_VMEXIT_MSR_STORE_ADDRESS));
    LOG_ERROR("VM-exit MSR-load address       = %016llx", VmxRead(VMCS_CTRL_VMEXIT_MSR_LOAD_ADDRESS));
    LOG_ERROR("VM-entry MSR-load address      = %016llx", VmxRead(VMCS_CTRL_VMENTRY_MSR_LOAD_ADDRESS));
    LOG_ERROR("Executive-VMCS pointer         = %016llx", VmxRead(VMCS_CTRL_EXECUTIVE_VMCS_POINTER));
    LOG_ERROR("PML address                    = %016llx", VmxRead(VMCS_CTRL_PML_ADDRESS));
    LOG_ERROR("TSC offset                     = %016llx", VmxRead(VMCS_CTRL_TSC_OFFSET));
    LOG_ERROR("Virtual-APIC address           = %016llx", VmxRead(VMCS_CTRL_VIRTUAL_APIC_ADDRESS));
    LOG_ERROR("APIC-access address            = %016llx", VmxRead(VMCS_CTRL_APIC_ACCESS_ADDRESS));
    LOG_ERROR("Posted-interrupt descriptor address = %016llx", VmxRead(VMCS_CTRL_POSTED_INTERRUPT_DESCRIPTOR_ADDRESS));
    LOG_ERROR("VM-function controls           = %016llx", VmxRead(VMCS_CTRL_VMFUNC_CONTROLS));
    LOG_ERROR("EPT pointer                    = %016llx", VmxRead(VMCS_CTRL_EPT_POINTER));
    LOG_ERROR("EOI-exit bitmap 0              = %016llx", VmxRead(VMCS_CTRL_EOI_EXIT_BITMAP_0));
    LOG_ERROR("EOI-exit bitmap 1              = %016llx", VmxRead(VMCS_CTRL_EOI_EXIT_BITMAP_1));
    LOG_ERROR("EOI-exit bitmap 2              = %016llx", VmxRead(VMCS_CTRL_EOI_EXIT_BITMAP_2));
    LOG_ERROR("EOI-exit bitmap 3              = %016llx", VmxRead(VMCS_CTRL_EOI_EXIT_BITMAP_3));
    LOG_ERROR("EPTP-list address              = %016llx", VmxRead(VMCS_CTRL_EPT_POINTER_LIST_ADDRESS));
    LOG_ERROR("VMREAD-bitmap address          = %016llx", VmxRead(VMCS_CTRL_VMREAD_BITMAP_ADDRESS));
    LOG_ERROR("VMWRITE-bitmap address         = %016llx", VmxRead(VMCS_CTRL_VMWRITE_BITMAP_ADDRESS));
    LOG_ERROR("Virtualization-exception information address = %016llx", VmxRead(VMCS_CTRL_VIRTUALIZATION_EXCEPTION_INFORMATION_ADDRESS));
    LOG_ERROR("XSS-exiting bitmap             = %016llx", VmxRead(VMCS_CTRL_XSS_EXITING_BITMAP));
    LOG_ERROR("ENCLS-exiting bitmap           = %016llx", VmxRead(VMCS_CTRL_ENCLS_EXITING_BITMAP));
    LOG_ERROR("TSC multiplier                 = %016llx", VmxRead(VMCS_CTRL_TSC_MULTIPLIER));

    //
    // 32-Bit Control Fields
    //
    LOG_ERROR("Pin-based VM-execution controls = %016llx", VmxRead(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS));
    LOG_ERROR("Primary processor-based VM-execution controls = %016llx", VmxRead(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS));
    LOG_ERROR("Exception bitmap               = %016llx", VmxRead(VMCS_CTRL_EXCEPTION_BITMAP));
    LOG_ERROR("Page-fault error-code mask     = %016llx", VmxRead(VMCS_CTRL_PAGEFAULT_ERROR_CODE_MASK));
    LOG_ERROR("Page-fault error-code match    = %016llx", VmxRead(VMCS_CTRL_PAGEFAULT_ERROR_CODE_MATCH));
    LOG_ERROR("CR3-target count               = %016llx", VmxRead(VMCS_CTRL_CR3_TARGET_COUNT));
    LOG_ERROR("VM-exit controls               = %016llx", VmxRead(VMCS_CTRL_VMEXIT_CONTROLS));
    LOG_ERROR("VM-exit MSR-store count        = %016llx", VmxRead(VMCS_CTRL_VMEXIT_MSR_STORE_COUNT));
    LOG_ERROR("VM-exit MSR-load count         = %016llx", VmxRead(VMCS_CTRL_VMEXIT_MSR_LOAD_COUNT));
    LOG_ERROR("VM-entry controls              = %016llx", VmxRead(VMCS_CTRL_VMENTRY_CONTROLS));
    LOG_ERROR("VM-entry MSR-load count        = %016llx", VmxRead(VMCS_CTRL_VMENTRY_MSR_LOAD_COUNT));
    LOG_ERROR("VM-entry interruption-information field = %016llx", VmxRead(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD));
    LOG_ERROR("VM-entry exception error code  = %016llx", VmxRead(VMCS_CTRL_VMENTRY_EXCEPTION_ERROR_CODE));
    LOG_ERROR("VM-entry instruction length    = %016llx", VmxRead(VMCS_CTRL_VMENTRY_INSTRUCTION_LENGTH));
    LOG_ERROR("TPR threshold                  = %016llx", VmxRead(VMCS_CTRL_TPR_THRESHOLD));
    LOG_ERROR("Secondary processor-based VM-execution controls = %016llx", VmxRead(VMCS_CTRL_SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS));
    LOG_ERROR("PLE_Gap                        = %016llx", VmxRead(VMCS_CTRL_PLE_GAP));
    LOG_ERROR("PLE_Window                     = %016llx", VmxRead(VMCS_CTRL_PLE_WINDOW));

    //
    // Natural-Width Control Fields
    //
    LOG_ERROR("CR0 guest/host mask            = %016llx", VmxRead(VMCS_CTRL_CR0_GUEST_HOST_MASK));
    LOG_ERROR("CR4 guest/host mask            = %016llx", VmxRead(VMCS_CTRL_CR4_GUEST_HOST_MASK));
    LOG_ERROR("CR0 read shadow                = %016llx", VmxRead(VMCS_CTRL_CR0_READ_SHADOW));
    LOG_ERROR("CR4 read shadow                = %016llx", VmxRead(VMCS_CTRL_CR4_READ_SHADOW));
    LOG_ERROR("CR3-target value 0             = %016llx", VmxRead(VMCS_CTRL_CR3_TARGET_VALUE_0));
    LOG_ERROR("CR3-target value 1             = %016llx", VmxRead(VMCS_CTRL_CR3_TARGET_VALUE_1));
    LOG_ERROR("CR3-target value 2             = %016llx", VmxRead(VMCS_CTRL_CR3_TARGET_VALUE_2));
    LOG_ERROR("CR3-target value 3             = %016llx", VmxRead(VMCS_CTRL_CR3_TARGET_VALUE_3));
}

_Use_decl_annotations_
VOID
VmxWrite (
    VMCS_FIELD Field,
    UINT64 FieldValue
    )
{
    VMX_RESULT result;

    result = __vmx_vmwrite(Field, FieldValue);
    if (result != VmxResultOk)
    {
        VMX_ERROR_NUMBER vmxErrorStatus;

        vmxErrorStatus = (result == VmxResultErrorWithStatus) ?
            (VMX_ERROR_NUMBER)VmxRead(VMCS_VM_INSTRUCTION_ERROR) : 0;
        if (vmxErrorStatus != VMX_ERROR_VMREAD_VMWRITE_INVALID_COMPONENT)
        {
            MV_PANIC();
        }
    }
}

_Use_decl_annotations_
UINT64
VmxRead (
    VMCS_FIELD Field
    )
{
    VMX_RESULT result;
    UINT64 fieldValue;

    result = __vmx_vmread(Field, &fieldValue);
    if (result != VmxResultOk)
    {
        VMX_ERROR_NUMBER vmxErrorStatus;

        vmxErrorStatus = (result == VmxResultErrorWithStatus) ?
            (VMX_ERROR_NUMBER)VmxRead(VMCS_VM_INSTRUCTION_ERROR) : 0;
        if (vmxErrorStatus != VMX_ERROR_VMREAD_VMWRITE_INVALID_COMPONENT)
        {
            MV_PANIC();
        }
        fieldValue = MAXUINT64;
    }
    return fieldValue;
}

_Use_decl_annotations_
VOID
AdvanceGuestInstructionPointer (
    GUEST_CONTEXT* GuestContext
    )
{
    UINT64 exitInstructionLength;

    exitInstructionLength = VmxRead(VMCS_VMEXIT_INSTRUCTION_LENGTH);
    GuestContext->VmcsBasedRegisters.Rip += exitInstructionLength;
    VmxWrite(VMCS_GUEST_RIP, GuestContext->VmcsBasedRegisters.Rip);
}

_Use_decl_annotations_
BOOLEAN
IsGuestInKernelMode (
    VOID
    )
{
    VMX_SEGMENT_ACCESS_RIGHTS accessRight;

    accessRight.Flags = (UINT32)VmxRead(VMCS_GUEST_SS_ACCESS_RIGHTS);
    return (accessRight.DescriptorPrivilegeLevel == 0);
}

_Use_decl_annotations_
VOID
InjectInterruption (
    INTERRUPTION_TYPE InterruptionType,
    EXCEPTION_VECTOR Vector,
    BOOLEAN DeliverErrorCode,
    UINT32 ErrorCode
    )
{
    VMENTRY_INTERRUPT_INFORMATION interruptToInject;

    interruptToInject.Flags = 0;
    interruptToInject.Valid = TRUE;
    interruptToInject.InterruptionType = (UINT32)InterruptionType;
    interruptToInject.Vector = (UINT32)Vector;
    interruptToInject.DeliverErrorCode = DeliverErrorCode;
    VmxWrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interruptToInject.Flags);

    if (DeliverErrorCode != FALSE)
    {
        VmxWrite(VMCS_CTRL_VMENTRY_EXCEPTION_ERROR_CODE, ErrorCode);
    }
}

_Use_decl_annotations_
VOID
SwitchGuestPagingMode (
    CR0 NewGuestCr0
    )
{
    IA32_EFER_REGISTER guestEfer;
    IA32_VMX_ENTRY_CTLS_REGISTER vmEntryControls;

    //
    // "Enable paging by setting CR0.PG = 1. This causes the processor to set the
    //  IA32_EFER.LMA bit to 1."
    // See: 9.8.5 Initializing IA-32e Mode
    //
    // "The processor always sets IA32_EFER.LMA to CR0.PG & IA32_EFER.LME.
    //  Software cannot directly modify IA32_EFER.LMA; an execution of WRMSR to
    //  the IA32_EFER MSR ignores bit 10 of its source operand."
    // See: 4.1.1 Three Paging Modes
    //
    guestEfer.Flags = VmxRead(VMCS_GUEST_EFER);
    guestEfer.Ia32EModeActive = (NewGuestCr0.PagingEnable & guestEfer.Ia32EModeEnable);
    VmxWrite(VMCS_GUEST_EFER, guestEfer.Flags);

    //
    // Apply the paging mode change in the VM-entry control VMCS field too.
    //
    vmEntryControls.Flags = VmxRead(VMCS_CTRL_VMENTRY_CONTROLS);
    vmEntryControls.Ia32EModeGuest = guestEfer.Ia32EModeActive;
    VmxWrite(VMCS_CTRL_VMENTRY_CONTROLS, vmEntryControls.Flags);

    //
    // Changing the paging mode results in invalidating TLB. Emulate this by
    // invalidating combined caches (GVA to HPA translation caches).
    //
    InvalidateVpidDerivedCache((UINT16)VmxRead(VMCS_CTRL_VIRTUAL_PROCESSOR_IDENTIFIER));
}

_Use_decl_annotations_
CR0
AdjustGuestCr0 (
    CR0 Cr0
    )
{
    CR0 newCr0;
    IA32_VMX_PROCBASED_CTLS2_REGISTER secondaryProcBasedControls;

    newCr0 = AdjustCr0(Cr0);

    //
    // When the UnrestrictedGuest bit is set, ProtectionEnable and PagingEnable
    // bits are allowed to be zero. Make this adjustment, by setting them 1 only
    // when the guest did indeed requested them to be 1 (ie,
    // Cr0.ProtectionEnable == 1) and the FIXED0 MSR indicated them to be 1 (ie,
    // newCr0.ProtectionEnable == 1).
    //
    secondaryProcBasedControls.Flags = VmxRead(
                    VMCS_CTRL_SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS);
    if (secondaryProcBasedControls.UnrestrictedGuest != FALSE)
    {
        newCr0.ProtectionEnable &= Cr0.ProtectionEnable;
        newCr0.PagingEnable &= Cr0.PagingEnable;
    }
    return newCr0;
}

_Use_decl_annotations_
CR4
AdjustGuestCr4 (
    CR4 Cr4
    )
{
    return AdjustCr4(Cr4);
}

_Use_decl_annotations_
UINT64
FindImageBase (
    GUEST_CONTEXT* GuestContext,
    UINT64 GuestVirtualAddress
    )
{
    //
    // Starting with the 1MB aligned address, and search up IMAGE_DOS_SIGNATURE
    // every 1MB.
    //
    for (UINT64 imageBase = (GuestVirtualAddress & ~(0x10000 - 1));
         /**/;
         imageBase -= 0x10000)
    {
        BOOLEAN ok;
        UINT16 contents;
        MEMORY_ACCESS_ERROR_INFORMATION errorInfo;

        ok = ReadGuestVirtualAddress(GuestContext->Contexts->MemoryAccessContext,
                                     TRUE,
                                     imageBase,
                                     &contents,
                                     sizeof(contents),
                                     &errorInfo);
        if (ok == FALSE)
        {
            return 0;
        }

        if (contents == 0x5A4D)
        {
            return imageBase;
        }
    }
}
