/*!
    @file MiniVisor.c

    @brief MiniVisor initialization.

    @author Satoshi Tanda

    @copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
 */
#include "MiniVisor.h"
#include "Asm.h"
#include "HostInitialization.h"
#include "ExtendedPageTables.h"
#include "Platform.h"
#include "MemoryManager.h"
#include "Logger.h"
#include "Public.h"
#include "MemoryType.h"
#include "HostNesting.h"
#include "Utils.h"
#include "MemoryAccess.h"

//
// Memory layout of hypervisor stack which is constructed by the kernel-mode
// code.
//
typedef struct _HYPERVISOR_INITIAL_STACK_DATA
{
    union
    {
        //
        //  Low     StackLimit[0]                        StackLimit
        //  ^       ...
        //  ^       ...                                  Layout.Context (StackBase)
        //  ^       ...
        //  ^       StackLimit[KERNEL_STACK_SIZE - 2]
        //  High    StackLimit[KERNEL_STACK_SIZE - 1]
        //
        DECLSPEC_ALIGN(PAGE_SIZE) UINT8 StackLimit[KERNEL_STACK_SIZE];
        struct
        {
            //
            // Available for the hypervisor to freely use.
            //
            UINT8 AvailableAsStack[KERNEL_STACK_SIZE - sizeof(HYPERVISOR_CONTEXT)];

            //
            // Set up by the kernel-mode code before starting the hypervisor.
            // The hypervisor never overwrites this contents.
            //
            HYPERVISOR_CONTEXT Context;
        } Layout;
    } u;
} HYPERVISOR_INITIAL_STACK_DATA;
C_ASSERT(sizeof(HYPERVISOR_INITIAL_STACK_DATA) == KERNEL_STACK_SIZE);

//
// Data structure used for virtualizing a processor. Allocated one for each
// processor on the system within the ALL_PROCESSORS_CONTEXTS structure.
// This structure is freed after virtualization is terminated because many of
// those data is used by the hypervisor or the processors indirectly. For example,
// th VmcsRegion member must be valid for the processor to perform VMX operations,
// and HypervisorStack too for the hypervisor to run.
//
typedef struct _PER_PROCESSOR_CONTEXT
{
    //
    // Indicates a result of the EnableHypervisor function.
    //
    MV_STATUS Status;

    //
    // The initial RSP value used for the guest (i.e., the initial value set
    // right after VMLAUNCH).
    //
    UINT64 GuestStackPointer;

    //
    // The initial RIP value used for the guest (i.e., the initial value set
    // right after VMLAUNCH).
    //
    UINT64 GuestInstructionPointer;

    //
    // The EPT related context for this processor.
    //
    EPT_CONTEXT EptContext;

    //
    // The context structure used to track state related to accessing guest
    // virtual memory address space from the hypervisor.
    //
    MEMORY_ACCESS_CONTEXT MemoryAccessContext;

    //
    // The context structure used to track state of the nested hypervisor.
    //
    NEXTED_VMX_CONTEXT NestedVmxContext;

    //
    // The global descriptor table (GDT) for the host. The size (16 entries) is
    // just a large enough size for systems seen during development. One could
    // dynamically allocate this table according with the Limit field of the GDTR
    // if wish to do so.
    //
    SEGMENT_DESCRIPTOR_64 HostGuestGdt[16];

    //
    // The task state segment (TSS) used for both the guest and the host if the
    // system does not configure it. This is the case on EFI environment.
    //
    TASK_STATE_SEGMENT_64 HostGuestTss;

    //
    // The copy of GDTR taken before MiniVisor is installed. Used during unload
    // to restore the value to original.
    //
    GDTR OriginalGdtr;

    //
    // The page-aligned, 4KB-size region used for the VMXON instruction.
    //
    DECLSPEC_ALIGN(PAGE_SIZE) VMXON VmxOnRegion;

    //
    // The page-aligned, 4KB size region used for the VMX operations such as
    // VMREAD, VM-exits and -entry.
    //
    DECLSPEC_ALIGN(PAGE_SIZE) VMCS VmcsRegion;

    //
    // The page-aligned region used as a hypervisor's stack. Some data is
    // populated by set up code before hypervisor starts.
    //
    DECLSPEC_ALIGN(PAGE_SIZE) HYPERVISOR_INITIAL_STACK_DATA HypervisorStack;
} PER_PROCESSOR_CONTEXT;

//
// Data shared across all processors during virtualization of them and execution
// of the hypervisor. Allocated once in the EnableHypervisorOnAllProcessors
// function and freed in the DisableHypervisorOnAllProcessors function.
//
typedef struct _SHARED_PROCESSOR_CONTEXT
{
    //
    // The number of the PER_PROCESSOR_CONTEXT data in the Contexts member. It
    // equals to the number of logical processors on the system.
    //
    UINT32 NumberOfContexts;

    //
    // The page-aligned, 4KB size region used as a MSR bitmap. The MSR bitmap is
    // used to indicate which MSR should cause VM-exit on RDMSR and WRMSR. Each
    // bit in this 4KB region represents ON or OFF of VM-exit, where 0 indicates
    // not to trigger, and 1 indicates to trigger VM-exit. This hypervisor does
    // not intend to handle MSR accesses and so, all bits are left as 0. It is
    // important that this bitmap governs VM-exit behavior only for certain sets
    // of MSRs. An access to any MSR that is not governed by this bitmap still
    // causes VM-exit unconditionally. For this reason, this hypervisor still
    // has RDMSR and WRMSR handling logic.
    //
    // See: 24.6.9 MSR-Bitmap Address
    //
    DECLSPEC_ALIGN(PAGE_SIZE) VOID* MsrBitmap;

    //
    // An array of PER_PROCESSOR_CONTEXTs. Each context is associated with and
    // used by one logical processor exclusively.
    //
    PER_PROCESSOR_CONTEXT Contexts[ANYSIZE_ARRAY];
} SHARED_PROCESSOR_CONTEXT;

/*!
    @brief Returns the VM control value that is adjusted in consideration with
        the VMX capability MSR.

    @param[in] VmxCapabilityMsr - A VMX capability MSR to consult to adjust the
        RequestedValue,

    @param[in] RequestedValue - A VM control value that needs adjustment.

    @return The adjusted control value.
 */
static
UINT32
AdjustControlValue (
    _In_ IA32_MSR_ADDRESS VmxCapabilityMsr,
    _In_ UINT32 RequestedValue
    )
{
    IA32_VMX_TRUE_CTLS_REGISTER capabilities;
    UINT32 effectiveValue;

    MV_ASSERT((VmxCapabilityMsr == IA32_VMX_PINBASED_CTLS) ||
              (VmxCapabilityMsr == IA32_VMX_PROCBASED_CTLS) ||
              (VmxCapabilityMsr == IA32_VMX_EXIT_CTLS) ||
              (VmxCapabilityMsr == IA32_VMX_ENTRY_CTLS) ||
              (VmxCapabilityMsr == IA32_VMX_TRUE_PINBASED_CTLS) ||
              (VmxCapabilityMsr == IA32_VMX_TRUE_PROCBASED_CTLS) ||
              (VmxCapabilityMsr == IA32_VMX_TRUE_EXIT_CTLS) ||
              (VmxCapabilityMsr == IA32_VMX_TRUE_ENTRY_CTLS) ||
              (VmxCapabilityMsr == IA32_VMX_PROCBASED_CTLS2));

    capabilities.Flags = __readmsr(VmxCapabilityMsr);
    effectiveValue = RequestedValue;

    //
    // Each bit of the following VMCS values might have to be set or cleared
    // according with the value indicated by the VMX capability MSRs.
    //  - pin-based VM-execution controls,
    //  - primary processor-based VM-execution controls,
    //  - secondary processor-based VM-execution controls.
    //
    // The VMX capability MSR is composed of two 32bit values, the lower 32bits
    // indicate bits can be 0, and the higher 32bits indicates bits can be 1.
    // In other words, they indicate bits MUST BE 1 and MUST BE 0 respectively.
    // The following logic enforces this logic by setting bits that must be 1,
    // and clearing bits that must be 0.
    //
    // See: A.3.1 Pin-Based VM-Execution Controls
    // See: A.3.2 Primary Processor-Based VM-Execution Controls
    // See: A.3.3 Secondary Processor-Based VM-Execution Controls
    //
    effectiveValue |= capabilities.Allowed0Settings;
    effectiveValue &= capabilities.Allowed1Settings;
    return effectiveValue;
}

/*!
    @brief Adjusts a pin-based control value in consideration with the VMX
        capability MSR.

    @param[in,out] PinBasedControls - A pointer to the pin-based control value
        to adjust.
 */
static
VOID
AdjustPinBasedControls (
    _Inout_ IA32_VMX_PINBASED_CTLS_REGISTER* PinBasedControls
    )
{
    IA32_MSR_ADDRESS vmxCapabilityMsr;
    IA32_VMX_BASIC_REGISTER vmxBasicMsr;

    //
    // This determine the right VMX capability MSR based on the value of
    // IA32_VMX_BASIC. With the right VMX capability MSR, the
    // AdjustControlValue function implements the logic described under
    // "It is necessary for software to consult only one of the capability MSRs
    //  to determine the allowed settings of the pin based VM-execution controls:"
    // See: A.3.1 Pin-Based VM-Execution Controls
    //
    vmxBasicMsr.Flags = __readmsr(IA32_VMX_BASIC);
    vmxCapabilityMsr = (vmxBasicMsr.VmxControls != FALSE) ?
        IA32_VMX_TRUE_PINBASED_CTLS : IA32_VMX_PINBASED_CTLS;

    PinBasedControls->Flags = AdjustControlValue(vmxCapabilityMsr,
                                                 (UINT32)PinBasedControls->Flags);
}

/*!
    @brief Adjusts a processor-based control value in consideration with the VMX
        capability MSR.

    @param[in,out] PrimaryProcBasedControls - A pointer to the processor-based
        control value to adjust.
 */
static
VOID
AdjustProcessorBasedControls (
    _Inout_ IA32_VMX_PROCBASED_CTLS_REGISTER* PrimaryProcBasedControls
    )
{
    IA32_MSR_ADDRESS vmxCapabilityMsr;
    IA32_VMX_BASIC_REGISTER vmxBasicMsr;

    //
    // See AdjustPinBasedControls for the details of the below logic.
    //
    vmxBasicMsr.Flags = __readmsr(IA32_VMX_BASIC);
    vmxCapabilityMsr = (vmxBasicMsr.VmxControls != FALSE) ?
        IA32_VMX_TRUE_PROCBASED_CTLS : IA32_VMX_PROCBASED_CTLS;

    PrimaryProcBasedControls->Flags = AdjustControlValue(
                                        vmxCapabilityMsr,
                                        (UINT32)PrimaryProcBasedControls->Flags);
}

/*!
    @brief Adjusts a VM-exit control value in consideration with the VMX
        capability MSR.

    @param[in,out] VmExitControls - A pointer to the VM-exit control value to
        adjust.
 */
static
VOID
AdjustVmExitControls (
    _Inout_ IA32_VMX_EXIT_CTLS_REGISTER* VmExitControls
    )
{
    IA32_MSR_ADDRESS vmxCapabilityMsr;
    IA32_VMX_BASIC_REGISTER vmxBasicMsr;

    //
    // See AdjustPinBasedControls for the details of the below logic.
    //
    vmxBasicMsr.Flags = __readmsr(IA32_VMX_BASIC);
    vmxCapabilityMsr = (vmxBasicMsr.VmxControls != FALSE) ?
        IA32_VMX_TRUE_EXIT_CTLS : IA32_VMX_EXIT_CTLS;

    VmExitControls->Flags = AdjustControlValue(vmxCapabilityMsr,
                                               (UINT32)VmExitControls->Flags);
}

/*!
    @brief Adjusts a VM-entry control value in consideration with the VMX
        capability MSR.

    @param[in,out] VmEntryControls - A pointer to the VM-entry control value to
        adjust.
 */
static
VOID
AdjustVmEntryControls (
    _Inout_ IA32_VMX_ENTRY_CTLS_REGISTER* VmEntryControls
    )
{
    IA32_MSR_ADDRESS vmxCapabilityMsr;
    IA32_VMX_BASIC_REGISTER vmxBasicMsr;

    //
    // See AdjustPinBasedControls for the details of the below logic.
    //
    vmxBasicMsr.Flags = __readmsr(IA32_VMX_BASIC);
    vmxCapabilityMsr = (vmxBasicMsr.VmxControls != FALSE) ?
        IA32_VMX_TRUE_ENTRY_CTLS : IA32_VMX_ENTRY_CTLS;

    VmEntryControls->Flags = AdjustControlValue(vmxCapabilityMsr,
                                                (UINT32)VmEntryControls->Flags);
}

/*!
    @brief Adjusts a secondary processor-based control value in consideration
        with the VMX capability MSR.

    @param[in,out] SecondaryProcBasedControls - A pointer to the secondary
        processor-based control value to adjust.
 */
static
VOID
AdjustSecondaryProcessorBasedControls (
    _Inout_ IA32_VMX_PROCBASED_CTLS2_REGISTER* SecondaryProcBasedControls
    )
{
    //
    // There is no TRUE MSR for IA32_VMX_PROCBASED_CTLS2. Just use
    // IA32_VMX_PROCBASED_CTLS2 unconditionally.
    //
    SecondaryProcBasedControls->Flags = AdjustControlValue(
                                        IA32_VMX_PROCBASED_CTLS2,
                                        (UINT32)SecondaryProcBasedControls->Flags);
}

/*!
    @brief Tests whether our hypervisor is installed on the system.

    @return TRUE when our hypervisor is installed on the system; otherwise FALSE.
 */
static
_Must_inspect_result_
BOOLEAN
IsMiniVisorInstalled (
    VOID
    )
{
    int registers[4];   // EAX, EBX, ECX, and EDX
    char vendorId[13];

    //
    // When our hypervisor is installed, CPUID leaf 40000000h will return
    // "MiniVisor   " as the vendor name.
    //
    __cpuid(registers, CPUID_HV_VENDOR_AND_MAX_FUNCTIONS);
    RtlCopyMemory(vendorId + 0, &registers[1], sizeof(registers[1]));
    RtlCopyMemory(vendorId + 4, &registers[2], sizeof(registers[2]));
    RtlCopyMemory(vendorId + 8, &registers[3], sizeof(registers[3]));
    vendorId[12] = ANSI_NULL;

    return (strcmp(vendorId, "MiniVisor   ") == 0);
}

/*!
    @brief Disables hypervisor on the current processor.

    @param[in,out] Context - A pointer to the location to receive the address of
        the all-processors context.
 */
static
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
DisableHypervisor (
    _Inout_ VOID* Context
    )
{
    SHARED_PROCESSOR_CONTEXT* returnedAddress;
    SHARED_PROCESSOR_CONTEXT** vpContextsAddress;
    PER_PROCESSOR_CONTEXT* vpContext;

    if (IsMiniVisorInstalled() == FALSE)
    {
        goto Exit;
    }

    //
    // Issues the hypercall to uninstall the hypervisor. This hypercall returns
    // the address of the all-processors context on success. If the hypervisor
    // is not installed, this (VMCALL instruction) raises #UD.
    //
    returnedAddress = (SHARED_PROCESSOR_CONTEXT*)AsmVmxCall(VmcallUninstall, 0, 0, 0);
    MV_ASSERT(returnedAddress != NULL);

    //
    // Context here is shared across all processors and could already be set the
    // address of the all-processors context by the other processors. This assert
    // says the address specified by the Context is either not updated yet (== NULL)
    // or updated by the same value as this processor has received (== returnedAddress
    // == the address of the all-processors context).
    //
    vpContextsAddress = Context;
    MV_ASSERT((*vpContextsAddress == NULL) ||
              (*vpContextsAddress == returnedAddress));
    *vpContextsAddress = returnedAddress;

    //
    // Clean up the per-processor stuff, to be precise, EPT setup.
    //
    vpContext = &(*vpContextsAddress)->Contexts[GetCurrentProcessorNumber()];
    CleanupExtendedPageTables(&vpContext->EptContext);
    CleanupMemoryAccess(&vpContext->MemoryAccessContext);
    CleanupGdt(&vpContext->OriginalGdtr);

Exit:
    MV_ASSERT(IsMiniVisorInstalled() == FALSE);
}

/*!
    @brief Enables hypervisor on the all processors.
 */
MV_SECTION_PAGED
static
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
DisableHypervisorOnAllProcessors (
    VOID
    )
{
    SHARED_PROCESSOR_CONTEXT* vpContexts;

    PAGED_CODE()

    vpContexts = NULL;
    RunOnAllProcessors(DisableHypervisor, &vpContexts);
    if (vpContexts != NULL)
    {
        MmFreePages(vpContexts);
    }
}

/*!
    @brief Tests whether VT-x is available with required capabilities for this
        hypervisor.

    @return TRUE when VT-x is available for this hypervisor; otherwise FALSE.
 */
static
_Must_inspect_result_
BOOLEAN
IsVmxAvailable (
    VOID
    )
{
    BOOLEAN vmxAvailable;
    int registers[4];
    CPUID_EAX_01 cpuidVersionInfo;
    IA32_VMX_BASIC_REGISTER vmxBasicMsr;
    IA32_FEATURE_CONTROL_REGISTER vmxFeatureControlMsr;
    IA32_VMX_EPT_VPID_CAP_REGISTER eptVpidCapabilityMsr;

    vmxAvailable = FALSE;

    //
    // "If CPUID.1:ECX.VMX[bit 5] = 1, then VMX operation is supported."
    // See: 23.6 DISCOVERING SUPPORT FOR VMX
    //
    __cpuid(registers, CPUID_VERSION_INFORMATION);
    cpuidVersionInfo.CpuidFeatureInformationEcx.Flags = (UINT32)registers[2];
    if (cpuidVersionInfo.CpuidFeatureInformationEcx.VirtualMachineExtensions == FALSE)
    {
        LOG_ERROR("VT-x is not available.");
        goto Exit;
    }

    //
    // Check the processor support the write-back type for VMCS. We do not
    // support the processor that does not support the write-back type for
    // simplicity. It is not practically an issue since
    // "As of this writing, all processors that support VMX operation indicate
    //  the write-back type."
    //
    // Note that supporting the write-back type means we can access virtual
    // memory holding VMCS as Windows allocates, as the write-back is the default
    // memory type Windows uses.
    //
    // See: A.1 BASIC VMX INFORMATION
    //
    vmxBasicMsr.Flags = __readmsr(IA32_VMX_BASIC);
    if (vmxBasicMsr.MemoryType != MEMORY_TYPE_WRITE_BACK)
    {
        LOG_ERROR("The write-back type is not supported on this processor.");
        goto Exit;
    }

    //
    // Check that:
    //  - the lock bit is set
    //  - the VMXON outside SMX operation bit is set
    //
    // "To enable VMX support in a platform, BIOS must set bit 1, bit 2, or both
    //  (see below), as well as the lock bit."
    // See: 23.7 ENABLING AND ENTERING VMX OPERATION
    //
    vmxFeatureControlMsr.Flags = __readmsr(IA32_FEATURE_CONTROL);
    if ((vmxFeatureControlMsr.LockBit == FALSE) ||
        (vmxFeatureControlMsr.EnableVmxOutsideSmx == FALSE))
    {
        LOG_ERROR("The lock bit is not set, or VMXON outside SMX is unsupported.");
        goto Exit;
    }

    //
    // Check the followings to confirm availability of EPT related capabilities:
    // - page walk length is 4 steps
    // - extended page tables can be laid out in write-back memory
    // - INVEPT instruction for global context is supported
    // - INVVPID instruction for global context is supported
    //
    eptVpidCapabilityMsr.Flags = __readmsr(IA32_VMX_EPT_VPID_CAP);
    if ((eptVpidCapabilityMsr.PageWalkLength4 == FALSE) ||
        (eptVpidCapabilityMsr.MemoryTypeWriteBack == FALSE) ||
        (eptVpidCapabilityMsr.Pde2MbPages == FALSE) ||
        (eptVpidCapabilityMsr.Invept == FALSE) ||
        (eptVpidCapabilityMsr.InveptAllContexts == FALSE) ||
        (eptVpidCapabilityMsr.Invvpid == FALSE) ||
        (eptVpidCapabilityMsr.InvvpidAllContexts == FALSE))
    {
        LOG_ERROR("EPT is not supported.");
        goto Exit;
    }

    vmxAvailable = TRUE;

Exit:
    return vmxAvailable;
}

/*!
    @brief Sets up a VMCS and initial hypervisor stack values.

    @details This function roughly follows the sequence stated in the Intel SDM
        to set up hypervisor.
        See: 31.5 VMM SETUP & TEAR DOWN
        See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE

    @param[in] VpContexts - A pointer to the all-processors context.

    @param[in,out] VpContext - A pointer to the per-processor context for this
        processor.

    @return MV_STATUS_SUCCESS on success, or an appropriate status code on error.
 */
static
_Must_inspect_result_
MV_STATUS
SetupVmcs (
    _In_ SHARED_PROCESSOR_CONTEXT* VpContexts,
    _Inout_ PER_PROCESSOR_CONTEXT* VpContext
    )
{
    //
    // Masks RPL (bits 1:0) and the TI flag (bit 2) of segment selectors.
    //
    static const UINT32 hostSegmentSelectorMask = 0x7;

    MV_STATUS status;
    CR0 newCr0;
    CR4 newCr4;
    IA32_VMX_BASIC_REGISTER vmxBasicMsr;
    UINT64 vmxOnPa, vmcsPa;
    BOOLEAN vmxOn, eptInitialized;
    GDTR gdtr;
    IDTR idtr;
    IA32_VMX_ENTRY_CTLS_REGISTER vmEntryControls;
    IA32_VMX_EXIT_CTLS_REGISTER vmExitControls;
    IA32_VMX_PINBASED_CTLS_REGISTER pinBasedControls;
    IA32_VMX_PROCBASED_CTLS_REGISTER primaryProcBasedControls;
    IA32_VMX_PROCBASED_CTLS2_REGISTER secondaryProcBasedControls;
    UINT64 hypervisorStackPointer;
    UINT32 exceptionBitmap;

    vmxOn = FALSE;
    eptInitialized = FALSE;

    //
    // First things first. Check the availability of VT-x to run MiniVisor.
    //
    // "Check VMX support in processor using CPUID."
    // "Determine the VMX capabilities supported by the processor through the
    //  VMX capability MSRs."
    // See: 31.5 VMM SETUP & TEAR DOWN
    //
    if (IsVmxAvailable() == FALSE)
    {
        LOG_ERROR("IsVmxAvailable failed.");
        status = MV_STATUS_HV_OPERATION_FAILED;
        goto Exit;
    }

    //
    // VMX requires TR to be configured properly (ie, non zero). This requirement
    // is not already satisfied in case of EFI environment. Set it up by updating
    // GDT as necessary.
    //
    // "The selector fields for CS and TR cannot be 0000H."
    // See: 26.2.3 Checks on Host Segment and Descriptor-Table Registers
    //
    // "TR. The different sub-fields are considered separately:"
    // See: 26.3.1.2 Checks on Guest Segment Registers
    //
    InitializeGdt(&VpContext->HostGuestTss,
                  VpContext->HostGuestGdt,
                  sizeof(VpContext->HostGuestGdt),
                  &VpContext->OriginalGdtr);
    MV_ASSERT(AsmReadTr() != 0);

    //
    // The per-processor context contains the VMXON region that is aligned to a
    // 4-KByte boundary. We also made sure that VMXON region can be accessed
    // through the write-back memory type in the IsVmxAvailable function.
    //
    // "Create a VMXON region in non-pageable memory of a size specified by
    //  IA32_VMX_BASIC MSR and aligned to a 4-KByte boundary. (...) Also,
    //  software must ensure that the VMXON region is hosted in cache-coherent
    //  memory."
    // See: 31.5 VMM SETUP & TEAR DOWN
    //
    MV_ASSERT(PAGE_ALIGN(&VpContext->VmxOnRegion) == &VpContext->VmxOnRegion);

    //
    // "Initialize the version identifier in the VMXON region (the first 31 bits)
    //  with the VMCS revision identifier reported by capability MSRs. Clear bit
    //  31 of the first 4 bytes of the VMXON region"
    // See: 31.5 VMM SETUP & TEAR DOWN
    //
    vmxBasicMsr.Flags = __readmsr(IA32_VMX_BASIC);
    VpContext->VmxOnRegion.RevisionId = (UINT32)vmxBasicMsr.VmcsRevisionId;
    MV_ASSERT(VpContext->VmxOnRegion.MustBeZero == 0);

    //
    // In order to enter the VMX-mode, the bits in CR0 and CR4 have to be
    // certain values as indicated by the FIXED0 and FIXED1 MSRs. The rule is
    // summarized as below:
    //
    //        IA32_VMX_CRx_FIXED0 IA32_VMX_CRx_FIXED1 Meaning
    // Bit X  1                   (Always 1)          The bit X of CRx is fixed to 1
    // Bit X  0                   1                   The bit X of CRx is flexible
    // Bit X  (Always 0)          0                   The bit X of CRx is fixed to 0
    //
    // See: A.7 VMX-FIXED BITS IN CR0
    //
    // "Ensure the current processor operating mode meets the required CR0 fixed
    //  bits (...). Other required CR0 fixed bits can be detected through the
    //  IA32_VMX_CR0_FIXED0 and IA32_VMX_CR0_FIXED1 MSRs."
    // See: 31.5 VMM SETUP & TEAR DOWN
    //
    newCr0.Flags = __readcr0();
    newCr0 = AdjustCr0(newCr0);
    __writecr0(newCr0.Flags);
    MV_ASSERT(newCr0.PagingEnable != FALSE);
    MV_ASSERT(newCr0.ProtectionEnable != FALSE);

    //
    // "Enable VMX operation by setting CR4.VMXE = 1. Ensure the resultant CR4
    //  value supports all the CR4 fixed bits reported in the IA32_VMX_CR4_FIXED0
    //  and IA32_VMX_CR4_FIXED1 MSRs".
    // See: 31.5 VMM SETUP & TEAR DOWN
    //
    newCr4.Flags = __readcr4();
    newCr4 = AdjustCr4(newCr4);
    __writecr4(newCr4.Flags);
    MV_ASSERT(newCr4.VmxEnable != FALSE);

    //
    // The below has been made sure with the IsVmxAvailable function.
    //
    // "Ensure that the IA32_FEATURE_CONTROL MSR (MSR index 3AH) has been
    //  properly programmed and that its lock bit is set (Bit 0 = 1)."
    // See: 31.5 VMM SETUP & TEAR DOWN
    //

    //
    // Enter VMX root operation.
    //
    // "Execute VMXON with the physical address of the VMXON region as the
    //  operand."
    // See: 31.5 VMM SETUP & TEAR DOWN
    //
    vmxOnPa = GetPhysicalAddress(&VpContext->VmxOnRegion);
    if (__vmx_on(&vmxOnPa) != VmxResultOk)
    {
        LOG_ERROR("__vmx_on failed");
        status = MV_STATUS_HV_OPERATION_FAILED;
        goto Exit;
    }
    vmxOn = TRUE;

    //
    // Clear stale cache that potentially exists.
    //
    // "Software can use the INVVPID instruction with the "all-context" INVVPID
    //  type immediately after execution of the VMXON instruction (...)."
    // "Software can use the INVEPT instruction with the "all-context" INVEPT
    //  type immediately after execution of the VMXON instruction (...)."
    // See: 28.3.3.3 Guidelines for Use of the INVVPID Instruction
    // See: 28.3.3.4 Guidelines for Use of the INVEPT Instruction
    //
    InvalidateEptDerivedCache(0);
    InvalidateVpidDerivedCache(0);

    //
    // The per-processor context contains the VMCS region that is aligned to a
    // 4-KByte boundary.
    //
    // "Create a VMCS region in non-pageable memory of size specified by the VMX
    //  capability MSR IA32_VMX_BASIC and aligned to 4-KBytes.
    // See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
    //
    MV_ASSERT(PAGE_ALIGN(&VpContext->VmcsRegion) == &VpContext->VmcsRegion);

    //
    // "Initialize the version identifier in the VMCS (first 31 bits) with the
    //  VMCS revision identifier reported by the VMX capability MSR
    //  IA32_VMX_BASIC. Clear bit 31 of the first 4 bytes of the VMCS region."
    // See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
    //
    VpContext->VmcsRegion.RevisionId = (UINT32)vmxBasicMsr.VmcsRevisionId;
    MV_ASSERT(VpContext->VmcsRegion.ShadowVmcsIndicator == FALSE);

    //
    // "The term "guest-VMCS address" refers to the physical address of the new
    //  VMCS region for the following steps.
    // "Execute the VMCLEAR instruction by supplying the guest-VMCS address."
    // "Execute the VMPTRLD instruction by supplying the guest-VMCS address."
    // See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
    //
    vmcsPa = GetPhysicalAddress(&VpContext->VmcsRegion);
    if ((__vmx_vmclear(&vmcsPa) != VmxResultOk) ||
        (__vmx_vmptrld(&vmcsPa) != VmxResultOk))
    {
        LOG_ERROR("__vmx_vmclear or __vmx_vmptrld failed");
        status = MV_STATUS_HV_OPERATION_FAILED;
        goto Exit;
    }

    //
    // The processor is in VMX root operation now. This means that the processor
    // can execute VMREAD, VMWRITE to configure VMCS and VMLAUNCH to start a VM.
    // Before start issuing VMWRITE, let us prepare data to write.
    //

    //
    // Initialize EPT specific data structure, which we will referred to as the
    // EPT-context.
    //
    status = InitializeExtendedPageTables(&VpContext->EptContext);
    if (MV_ERROR(status))
    {
        LOG_ERROR("InitializeExtendedPageTables failed : %08x", status);
        goto Exit;
    }
    eptInitialized = TRUE;

    //
    // Take the address of the initial stack pointer for hypervisor.
    //
    hypervisorStackPointer = (UINT64)&VpContext->HypervisorStack.u.Layout.Context;
    MV_ASSERT((hypervisorStackPointer % 0x10) == 0);

    //
    // Capture the current GDTR and IDTR.
    //
    _sgdt(&gdtr);
    __sidt(&idtr);

    //
    // Intercept #DB. This is purely for demonstration and can be removed.
    //
    exceptionBitmap = (1 << DivideError);

    //
    // VM-entry and -exit controls define how processor should operate on
    // VM-entry and exit. The following configurations are to achieve that:
    //  - Host always runs on the 64bit mode by setting vmExitControls.LoadIa32Efer
    //    and vmExitControls.HostAddressSpaceSize.
    //  - Guest always runs with IA32_EFER as it writes to it.
    //  - Guest starts as on the 64bit mode as the system is currently so.
    //
    vmEntryControls.Flags = 0;
    vmEntryControls.Ia32EModeGuest = TRUE;
    vmEntryControls.LoadIa32Efer = TRUE;
    AdjustVmEntryControls(&vmEntryControls);

    vmExitControls.Flags = 0;
    vmExitControls.HostAddressSpaceSize = TRUE;
    vmExitControls.LoadIa32Efer = TRUE;
    vmExitControls.SaveIa32Efer = TRUE;
    AdjustVmExitControls(&vmExitControls);

    //
    // The pin-based VM-execution controls governs the handling of asynchronous
    // events (for example: interrupts). We do not need any of them.
    //
    pinBasedControls.Flags = 0;
    AdjustPinBasedControls(&pinBasedControls);

    //
    // The processor-based VM-execution controls govern the handling of
    // synchronous events, mainly those caused by the execution of specific
    // instructions.
    //
    // - MSR bitmaps are used; this is not to cause VM-exit as much as possible.
    //   We are setting the MSR bitmaps that are all cleared below (see code
    //   around the "64-Bit Control Fields" comment). This prevents VM-exits from
    //   occurring when 0x0 - 0x1fff and 0xc0000000 - 0xc0001fff are accessed.
    //   Note that VM-exit still occurs if outside the range is accessed, and it
    //   is not possible to prevent this.
    //
    // - The secondary processor-based controls are used; this is to let the
    //   guest (Windows) executes RDTSCP, INVPCID and the XSAVE/XRSTORS family
    //   instructions. Those instructions are used in Windows 10. If those are
    //   not set, attempt to execute them causes #UD, which results in a bug
    //   check. VPID is enabled, which could lead to better performance for free
    //   by not flushing all TLB on every VM-exit. Finally, to enable EPT and
    //   unrestricted guest which are required for the UEFI hypervisor to handle
    //   the real-mode guest.
    //
    primaryProcBasedControls.Flags = 0;
    primaryProcBasedControls.UseMsrBitmaps = TRUE;
    primaryProcBasedControls.ActivateSecondaryControls = TRUE;
    AdjustProcessorBasedControls(&primaryProcBasedControls);

    secondaryProcBasedControls.Flags = 0;
    secondaryProcBasedControls.EnableEpt = TRUE;
    secondaryProcBasedControls.EnableVpid = TRUE;
    secondaryProcBasedControls.EnableRdtscp = TRUE;
    secondaryProcBasedControls.UnrestrictedGuest = TRUE;
    secondaryProcBasedControls.EnableInvpcid = TRUE;
    secondaryProcBasedControls.EnableXsaves = TRUE;
    AdjustSecondaryProcessorBasedControls(&secondaryProcBasedControls);

    //
    // "Issue a sequence of VMWRITEs to initialize various host-state area
    //  fields in the working VMCS."
    // See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
    //

    /* 16-Bit Host-State Fields */
    //
    // "In the selector field for each of CS, SS, DS, ES, FS, GS and TR, the
    //  RPL (bits 1:0) and the TI flag (bit 2) must be 0"
    // See: 26.2.3 Checks on Host Segment and Descriptor-Table Registers
    //
    VmxWrite(VMCS_HOST_ES_SELECTOR, AsmReadEs() & ~hostSegmentSelectorMask);
    VmxWrite(VMCS_HOST_CS_SELECTOR, AsmReadCs() & ~hostSegmentSelectorMask);
    VmxWrite(VMCS_HOST_SS_SELECTOR, AsmReadSs() & ~hostSegmentSelectorMask);
    VmxWrite(VMCS_HOST_DS_SELECTOR, AsmReadDs() & ~hostSegmentSelectorMask);
    VmxWrite(VMCS_HOST_FS_SELECTOR, AsmReadFs() & ~hostSegmentSelectorMask);
    VmxWrite(VMCS_HOST_GS_SELECTOR, AsmReadGs() & ~hostSegmentSelectorMask);
    VmxWrite(VMCS_HOST_TR_SELECTOR, AsmReadTr() & ~hostSegmentSelectorMask);

    /* 64-Bit Host-State Fields */
    VmxWrite(VMCS_HOST_EFER, __readmsr(IA32_EFER));

    /* 32-Bit Host-State Field */
    VmxWrite(VMCS_HOST_SYSENTER_CS, __readmsr(IA32_SYSENTER_CS));

    /* Natural-Width Host-State Fields */
    VmxWrite(VMCS_HOST_CR0, newCr0.Flags);
    VmxWrite(VMCS_HOST_CR3, GetHostCr3().Flags);
    VmxWrite(VMCS_HOST_CR4, newCr4.Flags);
    VmxWrite(VMCS_HOST_FS_BASE, __readmsr((IA32_MSR_ADDRESS)IA32_FS_BASE));
    VmxWrite(VMCS_HOST_GS_BASE, __readmsr((IA32_MSR_ADDRESS)IA32_GS_BASE));
    VmxWrite(VMCS_HOST_TR_BASE, GetSegmentBase(gdtr.BaseAddress, AsmReadTr()));
    VmxWrite(VMCS_HOST_GDTR_BASE, gdtr.BaseAddress);
    VmxWrite(VMCS_HOST_IDTR_BASE, GetHostIdtr()->BaseAddress);
    VmxWrite(VMCS_HOST_SYSENTER_ESP, __readmsr(IA32_SYSENTER_ESP));
    VmxWrite(VMCS_HOST_SYSENTER_EIP, __readmsr(IA32_SYSENTER_EIP));
    VmxWrite(VMCS_HOST_RSP, hypervisorStackPointer);
    VmxWrite(VMCS_HOST_RIP, (UINT64)AsmHypervisorEntryPoint);

    //
    // "Use VMWRITEs to set up the various VM-exit control fields, VM-entry
    //  control fields, and VM-execution control fields in the VMCS."
    // See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
    //

    /* 16-Bit Control Field */
    VmxWrite(VMCS_CTRL_VIRTUAL_PROCESSOR_IDENTIFIER, 1);

    /* 64-Bit Control Fields */
    VmxWrite(VMCS_CTRL_MSR_BITMAP_ADDRESS, GetPhysicalAddress(&VpContexts->MsrBitmap));
    VmxWrite(VMCS_CTRL_EPT_POINTER, VpContext->EptContext.EptPointer.Flags);

    /* 32-Bit Control Fields */
    VmxWrite(VMCS_CTRL_EXCEPTION_BITMAP, exceptionBitmap);
    VmxWrite(VMCS_CTRL_PIN_BASED_VM_EXECUTION_CONTROLS, pinBasedControls.Flags);
    VmxWrite(VMCS_CTRL_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, primaryProcBasedControls.Flags);
    VmxWrite(VMCS_CTRL_VMEXIT_CONTROLS, vmExitControls.Flags);
    VmxWrite(VMCS_CTRL_VMENTRY_CONTROLS, vmEntryControls.Flags);
    VmxWrite(VMCS_CTRL_SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS,
             secondaryProcBasedControls.Flags);

    /* Natural-Width Control Fields */
    VmxWrite(VMCS_CTRL_CR0_GUEST_HOST_MASK, CR0_NUMERIC_ERROR_FLAG | CR0_PAGING_ENABLE_FLAG);
    VmxWrite(VMCS_CTRL_CR0_READ_SHADOW, newCr0.Flags);
    VmxWrite(VMCS_CTRL_CR4_GUEST_HOST_MASK, CR4_VMX_ENABLE_FLAG);
    VmxWrite(VMCS_CTRL_CR4_READ_SHADOW, newCr4.Flags);

    //
    // "Use VMWRITE to initialize various guest-state area fields in the working
    //  VMCS."
    // See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
    //

    /* 16-Bit Guest-State Fields */
    VmxWrite(VMCS_GUEST_ES_SELECTOR, AsmReadEs());
    VmxWrite(VMCS_GUEST_CS_SELECTOR, AsmReadCs());
    VmxWrite(VMCS_GUEST_SS_SELECTOR, AsmReadSs());
    VmxWrite(VMCS_GUEST_DS_SELECTOR, AsmReadDs());
    VmxWrite(VMCS_GUEST_FS_SELECTOR, AsmReadFs());
    VmxWrite(VMCS_GUEST_GS_SELECTOR, AsmReadGs());
    VmxWrite(VMCS_GUEST_LDTR_SELECTOR, AsmReadLdtr());
    VmxWrite(VMCS_GUEST_TR_SELECTOR, AsmReadTr());

    /* 64-Bit Guest-State Fields */
    //
    // "If the “VMCS shadowing” VM-execution control is 1, (...). Otherwise,
    //  software should set this field to FFFFFFFF_FFFFFFFFH to avoid VM-entry
    //  failures."
    // See: 24.4.2 Guest Non-Register State
    //
    VmxWrite(VMCS_GUEST_VMCS_LINK_POINTER, MAXUINT64);
    VmxWrite(VMCS_GUEST_EFER, __readmsr(IA32_EFER));

    /* 32-Bit Guest-State Fields */
    VmxWrite(VMCS_GUEST_ES_LIMIT, __segmentlimit(AsmReadEs()));
    VmxWrite(VMCS_GUEST_CS_LIMIT, __segmentlimit(AsmReadCs()));
    VmxWrite(VMCS_GUEST_SS_LIMIT, __segmentlimit(AsmReadSs()));
    VmxWrite(VMCS_GUEST_DS_LIMIT, __segmentlimit(AsmReadDs()));
    VmxWrite(VMCS_GUEST_FS_LIMIT, __segmentlimit(AsmReadFs()));
    VmxWrite(VMCS_GUEST_GS_LIMIT, __segmentlimit(AsmReadGs()));
    VmxWrite(VMCS_GUEST_LDTR_LIMIT, __segmentlimit(AsmReadLdtr()));
    VmxWrite(VMCS_GUEST_TR_LIMIT, __segmentlimit(AsmReadTr()));
    VmxWrite(VMCS_GUEST_GDTR_LIMIT, gdtr.Limit);
    VmxWrite(VMCS_GUEST_IDTR_LIMIT, idtr.Limit);
    VmxWrite(VMCS_GUEST_ES_ACCESS_RIGHTS, GetSegmentAccessRight(AsmReadEs()));
    VmxWrite(VMCS_GUEST_CS_ACCESS_RIGHTS, GetSegmentAccessRight(AsmReadCs()));
    VmxWrite(VMCS_GUEST_SS_ACCESS_RIGHTS, GetSegmentAccessRight(AsmReadSs()));
    VmxWrite(VMCS_GUEST_DS_ACCESS_RIGHTS, GetSegmentAccessRight(AsmReadDs()));
    VmxWrite(VMCS_GUEST_FS_ACCESS_RIGHTS, GetSegmentAccessRight(AsmReadFs()));
    VmxWrite(VMCS_GUEST_GS_ACCESS_RIGHTS, GetSegmentAccessRight(AsmReadGs()));
    VmxWrite(VMCS_GUEST_LDTR_ACCESS_RIGHTS, GetSegmentAccessRight(AsmReadLdtr()));
    VmxWrite(VMCS_GUEST_TR_ACCESS_RIGHTS, GetSegmentAccessRight(AsmReadTr()));
    VmxWrite(VMCS_GUEST_SYSENTER_CS, __readmsr(IA32_SYSENTER_CS));

    /* Natural-Width Guest-State Fields */
    VmxWrite(VMCS_GUEST_CR0, newCr0.Flags);
    VmxWrite(VMCS_GUEST_CR3, __readcr3());
    VmxWrite(VMCS_GUEST_CR4, newCr4.Flags);
    VmxWrite(VMCS_GUEST_FS_BASE, __readmsr((IA32_MSR_ADDRESS)IA32_FS_BASE));
    VmxWrite(VMCS_GUEST_GS_BASE, __readmsr((IA32_MSR_ADDRESS)IA32_GS_BASE));
    VmxWrite(VMCS_GUEST_LDTR_BASE, GetSegmentBase(gdtr.BaseAddress, AsmReadLdtr()));
    VmxWrite(VMCS_GUEST_TR_BASE, GetSegmentBase(gdtr.BaseAddress, AsmReadTr()));
    VmxWrite(VMCS_GUEST_GDTR_BASE, gdtr.BaseAddress);
    VmxWrite(VMCS_GUEST_IDTR_BASE, idtr.BaseAddress);
    VmxWrite(VMCS_GUEST_RSP, VpContext->GuestStackPointer);
    VmxWrite(VMCS_GUEST_RIP, VpContext->GuestInstructionPointer);
    VmxWrite(VMCS_GUEST_RFLAGS, __readeflags());
    VmxWrite(VMCS_GUEST_SYSENTER_ESP, __readmsr(IA32_SYSENTER_ESP));
    VmxWrite(VMCS_GUEST_SYSENTER_EIP, __readmsr(IA32_SYSENTER_EIP));

    //
    // Finally, place necessary data for hypervisor into the hypervisor stack.
    //
    VpContext->HypervisorStack.u.Layout.Context.ProcessorNumber = GetCurrentProcessorNumber();
    VpContext->HypervisorStack.u.Layout.Context.VpContexts = VpContexts;
    VpContext->HypervisorStack.u.Layout.Context.EptContext = &VpContext->EptContext;
    VpContext->HypervisorStack.u.Layout.Context.MemoryAccessContext = &VpContext->MemoryAccessContext;
    VpContext->HypervisorStack.u.Layout.Context.NestedVmxContext = &VpContext->NestedVmxContext;
    status = MV_STATUS_SUCCESS;

Exit:
    if (MV_ERROR(status))
    {
        if (eptInitialized != FALSE)
        {
            CleanupExtendedPageTables(&VpContext->EptContext);
        }
        if (vmxOn != FALSE)
        {
            __vmx_off();
        }
        if (VpContext->OriginalGdtr.BaseAddress != 0)
        {
            CleanupGdt(&VpContext->OriginalGdtr);
        }
    }
    return status;
}

/*!
    @brief Enables hypervisor on the current processor.

    @param[in,out] Context - A pointer to the all-processors context.
 */
static
_IRQL_requires_max_(PASSIVE_LEVEL)
VOID
EnableHypervisor (
    _Inout_ VOID* Context
    )
{
    UINT32 processorNumber;
    SHARED_PROCESSOR_CONTEXT* vpContexts;
    PER_PROCESSOR_CONTEXT* vpContext;
    UINT64 guestRsp;
    UINT64 guestRip;
    BOOLEAN maCtxInitialized;

    maCtxInitialized = FALSE;

    vpContexts = Context;
    processorNumber = GetCurrentProcessorNumber();
    MV_ASSERT(processorNumber < vpContexts->NumberOfContexts);
    vpContext = &vpContexts->Contexts[processorNumber];

    //
    // Initialize the context of API to access guest virtual address from the
    // host.
    //
    vpContext->Status = InitializeMemoryAccess(&vpContext->MemoryAccessContext,
                                               GetHostCr3());
    if (MV_ERROR(vpContext->Status))
    {
        LOG_ERROR("InitializeMemoryAccess failed : %08x", vpContext->Status);
        goto Exit;
    }
    maCtxInitialized = TRUE;

    //
    // Save the current stack and instruction pointers. When the processor
    // successfully runs VMLAUNCH, it sets the stack and instruction pointers to
    // those values since we will set them to the guest state in the VMCS as we
    // see in the SetupVmcs function. This means, after VMLAUNCH, the
    // processor starts running at right after those function calls with the same
    // stack pointer value again.
    //
    guestRsp = AsmGetCurrentStackPointer();
    guestRip = AsmGetCurrentInstructionPointer();

    //
    // Check whether our hypervisor is already installed. At the first time, it
    // should not, and so, we will enable hypervisor ending with VMLAUNCH. After
    // successful VMLAUNCH, the processor starts running at here. At that time,
    // hypervisor is already installed, so the below block is skipped and this
    // function returns MV_STATUS_SUCCESS.
    //
    // Comment in the below debug break if you would like to see that this place
    // is executed twice.
    //
    //MV_DEBUG_BREAK();

    if (IsMiniVisorInstalled() == FALSE)
    {
        VMX_RESULT result;
        VMX_ERROR_NUMBER vmxErrorStatus;

        vpContext->GuestStackPointer = guestRsp;
        vpContext->GuestInstructionPointer = guestRip;
        vpContext->Status = SetupVmcs(vpContexts, vpContext);
        if (MV_ERROR(vpContext->Status))
        {
            LOG_ERROR("SetupVmcs failed : %08x", vpContext->Status);
            goto Exit;
        }

        //
        // It is important to be aware of that several registers are not saved
        // to and loaded from VMCS on VMLAUNCH, and the current register values
        // are used. This means that such register values must be the same between
        // when VMLAUNCH is executed and when RIP and RSP are captured. Otherwise,
        // when RIP and RSP are loaded from VMCS with VMLAUNCH, code running
        // after that will operate with unexpected register values. Think the
        // situation when you step through a program with a Windbg and just
        // overwrite RIP to rerun the same code again, but without paying
        // attention to the general purpose registers. The code will not run
        // proper if one of general purpose registers used in the code has stale
        // values.
        //
        // We avoid this issue by trying not to change any register values
        // between when RIP and RSP are captured and when VMLAUNCH is executed
        // in a way that breaks execution after successful VMLAUNCH. That is,
        // 1) executing VMLAUNCH in the same function, which ensures that all
        // non-volatile registers remain unchanged, and 2) avoiding dependency
        // on volatile registers in the code path after successful VMLAUNCH
        // (notice that it only calls a function without parameters and return).
        //
        // "The VMM may need to initialize additional guest execution state that is
        //  not captured in the VMCS guest state area by loading them directly on
        //  the respective processor registers. Examples include general purpose
        //  registers, the CR2 control register, debug registers, floating point
        //  registers and so forth."
        // See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
        //

        //
        // Enable hypervisor. If it is successful, this call does not return and
        // the processor starts running at the above point.
        //
        // "Execute VMLAUNCH to launch the guest VM."
        // See: 31.6 PREPARATION AND LAUNCHING A VIRTUAL MACHINE
        //
        result = __vmx_vmlaunch();

        //
        // We have failed to run VMLAUNCH. Retrieve and log the error number.
        // See: Table 30-1. VM-Instruction Error Numbers
        //
        MV_ASSERT(result != VmxResultOk);
        vmxErrorStatus = (result == VmxResultErrorWithStatus) ?
            (VMX_ERROR_NUMBER)VmxRead(VMCS_VM_INSTRUCTION_ERROR) : 0;
        LOG_ERROR("__vmx_vmlaunch failed : %u", vmxErrorStatus);
        vpContext->Status = MV_STATUS_HV_OPERATION_FAILED;
        CleanupExtendedPageTables(&vpContext->EptContext);
        __vmx_off();
        CleanupGdt(&vpContext->OriginalGdtr);
        goto Exit;
    }

Exit:
    if (MV_ERROR(vpContext->Status) && (maCtxInitialized != FALSE))
    {
        CleanupMemoryAccess(&vpContext->MemoryAccessContext);
    }
}

/*!
   @brief Enables hypervisor on the all processors.

   @return MV_STATUS_SUCCESS on success; otherwise, an appropriate error code.
 */
MV_SECTION_PAGED
static
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
MV_STATUS
EnableHypervisorOnAllProcessors (
    VOID
    )
{
    MV_STATUS status;
    UINT32 numberOfProcessors;
    UINT32 allocationSize;
    SHARED_PROCESSOR_CONTEXT* vpContexts;
    BOOLEAN virtualized;

    PAGED_CODE()

    virtualized = FALSE;

    //
    // Compute the size of all-processors contexts. The all-processors contexts
    // contain VT-x related data shared across all processors and as well as
    // a per-processor context for each processor. The per-processor context
    // contains VT-x related data that are specific to the processor.
    //
    // "numberOfProcessors - 1" because "sizeof(ALL_PROCESSORS_CONTEXTS)" includes
    // one PER_PROCESSOR_CONTEXT.
    //
    numberOfProcessors = GetActiveProcessorCount();
    allocationSize = sizeof(SHARED_PROCESSOR_CONTEXT) +
        (sizeof(PER_PROCESSOR_CONTEXT) * (numberOfProcessors - 1));

    //
    // Allocate the context.
    //
    vpContexts = MmAllocatePages((UINT8)BYTES_TO_PAGES(allocationSize));
    if (vpContexts == NULL)
    {
        status = MV_STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }
    vpContexts->NumberOfContexts = numberOfProcessors;

    //
    // Start virtualizing processors one-by-one. This is done by changing
    // thread affinity and executing callback on each processor at PASSIVE_LEVEL.
    // This is slow as virtualization code is not parallelized but makes debugging
    // MUCH easier exactly because of the reason. Production code normally use
    // DPC or IPI instead, so that this operation is parallelized and eliminates
    // any chance of race.
    //
    RunOnAllProcessors(EnableHypervisor, vpContexts);

    //
    // Assume success first, then inspect the results from each processor and
    // update status as necessary.
    //
    status = MV_STATUS_SUCCESS;
    for (UINT32 i = 0; i < vpContexts->NumberOfContexts; ++i)
    {
        if (MV_ERROR(vpContexts->Contexts[i].Status))
        {
            //
            // At least one processor failed to enable hypervisor.
            //
            LOG_ERROR("EnableHypervisor on processor %lu failed : %08x",
                      i,
                      vpContexts->Contexts[i].Status);
            status = vpContexts->Contexts[i].Status;
        }
        else
        {
            //
            // At least one processor enabled hypervisor and so needs clean up
            // in case of failure.
            //
            virtualized = TRUE;
            LOG_INFO("EnableHypervisor on processor %lu succeeded.", i);
        }
    }

Exit:
    if (MV_ERROR(status))
    {
        if (virtualized != FALSE)
        {
            //
            // Disable hypervisor if one or more processors enabled but also the
            // other processor(s) failed. This takes care of freeing the
            // all-processors contexts.
            //
            DisableHypervisorOnAllProcessors();
        }
        else
        {
            if (vpContexts != NULL)
            {
                MmFreePages(vpContexts);
            }
        }
    }
    return status;
}

_Use_decl_annotations_
MV_STATUS
InitializeMiniVisor (
    )
{
    MV_STATUS status;
    BOOLEAN platformInitialized;
    BOOLEAN memoryManagerInitialized;

    platformInitialized = FALSE;
    memoryManagerInitialized = FALSE;

    MV_DEBUG_BREAK();

    //
    // Initialize platform dependent bits, including the logging facility. Until
    // this succeeds, only the LOG_EARLY_ERROR macro can be used for logging.
    //
    status = InitializePlatform();
    if (MV_ERROR(status))
    {
        LOG_EARLY_ERROR("InitializePlatform failed : %08x", status);
        goto Exit;
    }
    platformInitialized = TRUE;

    //
    // Bail out if MiniVisor is already installed.
    //
    if (IsMiniVisorInstalled() != FALSE)
    {
        LOG_ERROR("MiniVisor already installed");
        status = MV_STATUS_HV_OPERATION_FAILED;
        goto Exit;
    }

    LOG_INFO("Installing MiniVisor.");

    //
    // Compute the required memory size and initialize the memory manager with it.
    //
    // The memory manager is the component that allocates the specified size of
    // non-paged pool at first and uses it as a pool to process memory allocation
    // requests. This pre-allocation approach is required because host code
    // cannot call almost any kernel API including memory allocation functions.
    //
    // The page count allocated are based on the fact that EPT paging structures
    // for 0-1GB of physical memory addresses often need to be constructed without
    // large page EPT entries because of fixed MTRRs. This requires 512 pages (
    // 512 EPT page tables). Add 64 pages for the rest of EPT tables, control
    // structures etc.
    //
    status = MmInitializeMemoryManager(GetActiveProcessorCount() * (512 + 64));
    if (MV_ERROR(status))
    {
        LOG_ERROR("MmInitializeMemoryManager failed : %08x", status);
        goto Exit;
    }
    memoryManagerInitialized = TRUE;

    //
    // Prepare environments for the host. This includes initialization of CR3 and
    // IDTR, but how those set up depend on the platform.
    //
    InitializeHostEnvironment();

    //
    // Build the mapping of "physical address to memory type", based on the memory
    // type range registers (MTRRs). This information is required to set up
    // extended page tables.
    //
    InitializeMemoryTypeMapping();

    //
    // Install the hypervisor.
    //
    status = EnableHypervisorOnAllProcessors();
    if (MV_ERROR(status))
    {
        LOG_ERROR("EnableHypervisorOnAllProcessors failed : %08x", status);
        goto Exit;
    }

    LOG_INFO("MiniVisor installed successfully.");

Exit:
    if (MV_ERROR(status))
    {
        if (memoryManagerInitialized != FALSE)
        {
            MmCleanupMemoryManager();
        }
        if (platformInitialized != FALSE)
        {
            CleanupPlatform();
        }
    }
    return status;
}

_Use_decl_annotations_
VOID
CleanupMiniVisor (
    )
{
    MV_DEBUG_BREAK();

    DisableHypervisorOnAllProcessors();
    MmCleanupMemoryManager();
    LOG_INFO("MiniVisor uninstalled successfully.");
    CleanupPlatform();
}
