Virtualbox source code analysis 23 NEM(Hyper-V compatible) 4 VMExit

Native execution manager (VMExit)

Article directory


This article focuses on the function of processing VMExit in NEM mode. The basic framework is exactly the same as hm / raw mode, but the specific implementation code (called API) is different.

23.1 EPT memory management

NEM memory management. The memory management of NEM mode is the same as that of HM mode. The EPT table is also maintained by PGM. However, when the EPT table is modified (allocate EPT memory, release EPT memory, modify EPT memory properties, etc.), the hypercall of HvCallMapGpaPages/HvCallUnmapGpaPages will be called to modify the corresponding relationship between the physical address of virtual machine and the physical address of real machine in the hypervisor

Related knowledge: A20 Gate simulation: detailed introduction of related memory in memory management

https://blog.csdn.net/lightseed/article/details/4305865

23.1.1 allocate memory

int pgmPhysAllocPage(PVMCC pVM, PPGMPAGE pPage, RTGCPHYS GCPhys)
{
    ...
    //Allocate the physical address of the virtual machine. When NEM is turned on, call nemhcnotifyphyspallocated to inform NEM that map memory is needed
	if (VM_IS_NEM_ENABLED(pVM))
    {
        PGMPAGETYPE enmType = (PGMPAGETYPE)PGM_PAGE_GET_TYPE(pPage);
        if (   enmType != PGMPAGETYPE_ROM_SHADOW
            || pgmPhysGetPage(pVM, GCPhys) == pPage)
        {
            uint8_t u2State = PGM_PAGE_GET_NEM_STATE(pPage);
            rc2 = NEMHCNotifyPhysPageAllocated(pVM, GCPhys & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK, HCPhys,
                                               pgmPhysPageCalcNemProtection(pPage, enmType), enmType, &u2State);
            if (RT_SUCCESS(rc))
                PGM_PAGE_SET_NEM_STATE(pPage, u2State);
            else
                rc = rc2;
        }
    }
    ...
}

# define NEM_WIN_IS_SUBJECT_TO_A20(a_GCPhys)    ((RTGCPHYS)((a_GCPhys) - _1M) < (RTGCPHYS)_64K)
# define NEM_WIN_IS_RELEVANT_TO_A20(a_GCPhys)    \
    ( ((RTGCPHYS)((a_GCPhys) - _1M) < (RTGCPHYS)_64K) || ((RTGCPHYS)(a_GCPhys) < (RTGCPHYS)_64K) )

int nemHCNativeNotifyPhysPageAllocated(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS HCPhys, uint32_t fPageProt,
                                       PGMPAGETYPE enmType, uint8_t *pu2State)
{
    PVMCPUCC pVCpu = VMMGetCpu(pVM);
    if (   pVM->nem.s.fA20Enabled
        || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
        rc = nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, true /*fBackingChanged*/);
    else
    {
        rc = nemHCWinUnmapPageForA20Gate(pVM, pVCpu, GCPhys | RT_BIT_32(20));
        if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys) && RT_SUCCESS(rc))
            rc = nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, true /*fBackingChanged*/);

    }
}

nemHCNativeSetPhysPage

Map or modify memory properties and status

NEM_TMPL_STATIC int nemHCNativeSetPhysPage(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhysSrc, RTGCPHYS GCPhysDst,
                                           uint32_t fPageProt, uint8_t *pu2State, bool fBackingChanged)
{
    uint8_t const u2OldState = *pu2State;
    if (fPageProt == NEM_PAGE_PROT_NONE)
    {
        //Need unmap
        if (u2OldState > NEM_WIN_PAGE_STATE_UNMAPPED)
        {
            //If you have mapped before, unmap directly
            rc = nemHCWinHypercallUnmapPage(pVM, pVCpu, GCPhysDst);
            if (RT_SUCCESS(rc))
            {
                *pu2State = NEM_WIN_PAGE_STATE_UNMAPPED;
                //Number of pages minus one
                uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages); NOREF(cMappedPages);
            }
        }
        else
            //There's no map, nothing to do
            rc = VINF_SUCCESS;
    }
    //New page properties need to be writable
    else if (fPageProt & NEM_PAGE_PROT_WRITE)
    {
        //The previous page property is not written. Call hypercall to modify it to read-write executable
        if (u2OldState != NEM_WIN_PAGE_STATE_WRITABLE || fBackingChanged)
        {
            //If the previous page has not been mapped, add one to the number of pages
            rc = nemHCWinHypercallMapPage(pVM, pVCpu, GCPhysSrc, GCPhysDst,
                                            HV_MAP_GPA_READABLE   | HV_MAP_GPA_WRITABLE
                                          | HV_MAP_GPA_EXECUTABLE | HV_MAP_GPA_EXECUTABLE_AGAIN);
            if (RT_SUCCESS(rc))
            {
                *pu2State = NEM_WIN_PAGE_STATE_WRITABLE;
                uint32_t cMappedPages = u2OldState <= NEM_WIN_PAGE_STATE_UNMAPPED
                                      ? ASMAtomicIncU32(&pVM->nem.s.cMappedPages) : pVM->nem.s.cMappedPages;
            }
        }
        else
            rc = VINF_SUCCESS;
	}
    else
    {
        //The rest is readable
        if (u2OldState != NEM_WIN_PAGE_STATE_READABLE || fBackingChanged)
        {
            rc = nemHCWinHypercallMapPage(pVM, pVCpu, GCPhysSrc, GCPhysDst,
                                          HV_MAP_GPA_READABLE | HV_MAP_GPA_EXECUTABLE | HV_MAP_GPA_EXECUTABLE_AGAIN);
            if (RT_SUCCESS(rc))
            {
                *pu2State = NEM_WIN_PAGE_STATE_READABLE;
                //If the previous page has not been mapped, add one to the number of pages
                uint32_t cMappedPages = u2OldState <= NEM_WIN_PAGE_STATE_UNMAPPED
                                      ? ASMAtomicIncU32(&pVM->nem.s.cMappedPages) : pVM->nem.s.cMappedPages;
            }
        }
        else
            rc = VINF_SUCCESS;
    }
}

nemR0WinMapPages

NEM_TMPL_STATIC int nemR0WinMapPages(PGVM pGVM, PGVMCPU pGVCpu, RTGCPHYS GCPhysSrc, RTGCPHYS GCPhysDst,
                                     uint32_t cPages, uint32_t fFlags)
{
    //Do GCPhysSrc and GCPhysDst need to be the same?
    if (GCPhysSrc != GCPhysDst)
    {
        AssertMsgReturn(!(GCPhysSrc & X86_PAGE_OFFSET_MASK), ("GCPhysSrc=%RGp\n", GCPhysSrc), VERR_OUT_OF_RANGE);
        AssertReturn(GCPhysSrc < _1E, VERR_OUT_OF_RANGE);
    }
	//Loop, up to 16 attempts
    for (uint32_t iTries = 0;; iTries++)
    {
        HV_INPUT_MAP_GPA_PAGES *pMapPages = (HV_INPUT_MAP_GPA_PAGES *)pGVCpu->nemr0.s.HypercallData.pbPage;
        pMapPages->TargetPartitionId    = pGVM->nemr0.s.idHvPartition;
        pMapPages->TargetGpaBase        = GCPhysDst >> X86_PAGE_SHIFT;
        pMapPages->MapFlags             = fFlags;
        pMapPages->u32ExplicitPadding   = 0;
        //Get the physical address of each virtual machine and save it in hypercallData
        for (uint32_t iPage = 0; iPage < cPages; iPage++, GCPhysSrc += X86_PAGE_SIZE)
        {
            RTHCPHYS HCPhys = NIL_RTGCPHYS;
            int rc = PGMPhysGCPhys2HCPhys(pGVM, GCPhysSrc, &HCPhys);
            pMapPages->PageList[iPage] = HCPhys >> X86_PAGE_SHIFT;
        }
	    //Call HvCallMapGpaPages hypercall, map virtual machine physical address and real machine physical address
        uint64_t uResult = g_pfnHvlInvokeHypercall(HvCallMapGpaPages | ((uint64_t)cPages << 32),
                                                   pGVCpu->nemr0.s.HypercallData.HCPhysPage, 0);
        if (uResult == ((uint64_t)cPages << 32))
            return VINF_SUCCESS;
        //If the partition has no physical memory, try to call the WinHvDepositMemory function to allocate 512 page s
        if (   uResult != HV_STATUS_INSUFFICIENT_MEMORY
            || iTries > 16
            || g_pfnWinHvDepositMemory == NULL)
        {
            return VERR_NEM_MAP_PAGES_FAILED;
        }
	   //Call the WinHvDepositMemory function to allocate 512 page s and return the actual allocated memory
        size_t cPagesAdded = 0;
        NTSTATUS rcNt = g_pfnWinHvDepositMemory(pGVM->nemr0.s.idHvPartition, 512, 0, &cPagesAdded);
        if (!cPagesAdded)
        {
            //Allocation failed, error returned
            return VERR_NEM_MAP_PAGES_FAILED;
        }
        //Allocation succeeded, try to Map memory again
    }
}

23.1.2 Unmap memory

nemR0WinUnmapPages

Remove the map of GCPhys and HCPhys

NEM_TMPL_STATIC int nemR0WinUnmapPages(PGVM pGVM, PGVMCPU pGVCpu, RTGCPHYS GCPhys, uint32_t cPages)
{
    HV_INPUT_UNMAP_GPA_PAGES *pUnmapPages = (HV_INPUT_UNMAP_GPA_PAGES *)pGVCpu->nemr0.s.HypercallData.pbPage;
    //partitionid
    pUnmapPages->TargetPartitionId    = pGVM->nemr0.s.idHvPartition;
    //Virtual machine physical address start address
    pUnmapPages->TargetGpaBase        = GCPhys >> X86_PAGE_SHIFT;
    pUnmapPages->fFlags               = 0;
    //unmap cPages pages
    uint64_t uResult = g_pfnHvlInvokeHypercall(HvCallUnmapGpaPages | ((uint64_t)cPages << 32),
                                               pGVCpu->nemr0.s.HypercallData.HCPhysPage, 0);
    if (uResult == ((uint64_t)cPages << 32))
    {
        //unmap success, uncomit memory?
        uint64_t volatile uR = g_pfnHvlInvokeHypercall(HvCallUncommitGpaPages | ((uint64_t)cPages << 32),
                                                       pGVCpu->nemr0.s.HypercallData.HCPhysPage, 0);
        return VINF_SUCCESS;
    }
    return VERR_NEM_UNMAP_PAGES_FAILED;
}

nemHCWinUnmapPageForA20Gate

NEM_TMPL_STATIC DECLCALLBACK(int) nemHCWinUnsetForA20CheckerCallback(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys,
                                                                     PPGMPHYSNEMPAGEINFO pInfo, void *pvUser)
{
    if (pInfo->u2NemState > NEM_WIN_PAGE_STATE_UNMAPPED)
    {
        //unmap
        int rc = nemHCWinHypercallUnmapPage(pVM, pVCpu, GCPhys);
        if (RT_SUCCESS(rc))
        {
            //Number of mappd page s minus one
            uint32_t cMappedPages = ASMAtomicDecU32(&pVM->nem.s.cMappedPages);
            pInfo->u2NemState = NEM_WIN_PAGE_STATE_UNMAPPED;
	    }
    }
}
NEM_TMPL_STATIC int nemHCWinUnmapPageForA20Gate(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys)
{
    PGMPHYSNEMPAGEINFO Info;
    return PGMPhysNemPageInfoChecker(pVM, pVCpu, GCPhys, false /*fMakeWritable*/, &Info,
                                     nemHCWinUnsetForA20CheckerCallback, NULL);
}
                                                                     

23.1.3 modify memory attribute and page Map relationship

nemHCNativeNotifyPhysPageProtChanged

Page properties change

void nemHCNativeNotifyPhysPageProtChanged(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS HCPhys, uint32_t fPageProt,
                                          PGMPAGETYPE enmType, uint8_t *pu2State)
{
    PVMCPUCC pVCpu = VMMGetCpu(pVM);
    if (   pVM->nem.s.fA20Enabled
        || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
        //A20 Gate simulation is enabled or the physical address is not within the range of A20 physical address
        nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, false /*fBackingChanged*/);
    else
    {
        //A20 simulation is not enabled, unmap first, then map
        nemHCWinUnmapPageForA20Gate(pVM, pVCpu, GCPhys | RT_BIT_32(20));
        if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
            nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, false /*fBackingChanged*/);
    }
}

nemHCNativeNotifyPhysPageChanged

HCPhys change

void nemHCNativeNotifyPhysPageChanged(PVMCC pVM, RTGCPHYS GCPhys, RTHCPHYS HCPhysPrev, RTHCPHYS HCPhysNew,
                                     uint32_t fPageProt, PGMPAGETYPE enmType, uint8_t *pu2State)
{
    PVMCPUCC pVCpu = VMMGetCpu(pVM);
    if (   pVM->nem.s.fA20Enabled
        || !NEM_WIN_IS_RELEVANT_TO_A20(GCPhys))
        nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, true /*fBackingChanged*/);
    else
    {
        nemHCWinUnmapPageForA20Gate(pVM, pVCpu, GCPhys | RT_BIT_32(20));
        if (!NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
            nemHCNativeSetPhysPage(pVM, pVCpu, GCPhys, GCPhys, fPageProt, pu2State, true /*fBackingChanged*/);
    }
}

23.2 VMExit processing

nemHCWinStopCpu

Stop VCPU and process VMExit

NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinStopCpu(PVMCC pVM, PVMCPUCC pVCpu, VBOXSTRICTRC rcStrict,
                                             VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader)
{
    //Call IOCTL to stop VCPU
    pVCpu->nem.s.uIoCtlBuf.idCpu = pVCpu->idCpu;
    NTSTATUS rcNt = nemR0NtPerformIoControl(pVM, pVCpu, pVM->nemr0.s.IoCtlStopVirtualProcessor.uFunction,
                                            &pVCpu->nem.s.uIoCtlBuf.idCpu, sizeof(pVCpu->nem.s.uIoCtlBuf.idCpu),
                                            NULL, 0);
    if (NT_SUCCESS(rcNt))
    {
        return rcStrict;
    }
    //Call IOCTL to get VMExit event
    //Flags: vid ﹣ mshagn ﹣ f ﹣ handle ﹣ message: the tag message has been processed. You can continue to execute the GuestOS code
    //       Vid? Mshagn? F? Get? Next? Message: get message
    rcNt = nemR0NtPerformIoCtlMessageSlotHandleAndGetNext(pVM, pVCpu, VID_MSHAGN_F_GET_NEXT_MESSAGE, 30000 /*ms*/);
    VID_MESSAGE_TYPE enmVidMsgType = pMappingHeader->enmVidMsgType;
    /*
    Two messagetypes:
    VidMessageHypervisorMessage :  Get information from hypervisor successfully
    VidMessageStopRequestComplete: Stop accepting requests? virtualbox does nothing about this return value
    */
    if (enmVidMsgType != VidMessageStopRequestComplete)
    {
        //Processing VMexit
        VBOXSTRICTRC rcStrict2 = nemHCWinHandleMessage(pVM, pVCpu, pMappingHeader);
        if (rcStrict2 != VINF_SUCCESS && RT_SUCCESS(rcStrict))
            rcStrict = rcStrict2;
        //Why is this place called again?
        rcNt = nemR0NtPerformIoCtlMessageSlotHandleAndGetNext(pVM, pVCpu,
                                                              VID_MSHAGN_F_HANDLE_MESSAGE | VID_MSHAGN_F_GET_NEXT_MESSAGE,
                                                              30000 /*ms*/);
        enmVidMsgType = pMappingHeader->enmVidMsgType;
        //The flag VidMessageStopRequestComplete message has been handle d
        rcNt = nemR0NtPerformIoCtlMessageSlotHandleAndGetNext(pVM, pVCpu, VID_MSHAGN_F_HANDLE_MESSAGE, 30000 /*ms*/);
    }
}

nemHCWinHandleMessage

The total entry of interrupt processing is distributed to the corresponding function according to MsgType for execution

NEM_TMPL_STATIC VBOXSTRICTRC
nemHCWinHandleMessage(PVMCC pVM, PVMCPUCC pVCpu, VID_MESSAGE_MAPPING_HEADER volatile *pMappingHeader)
{
    if (pMappingHeader->enmVidMsgType == VidMessageHypervisorMessage)
    {
        HV_MESSAGE const *pMsg = (HV_MESSAGE const *)(pMappingHeader + 1);
        switch (pMsg->Header.MessageType)
        {
            case HvMessageTypeUnmappedGpa:
                //Physical address of virtual machine does not have corresponding physical address of real machine (VMX ﹣ EPT ﹣ misconfig)
                return nemHCWinHandleMessageMemory(pVM, pVCpu, &pMsg->X64MemoryIntercept);
            case HvMessageTypeGpaIntercept:
                //Physical address access exception of the real machine (permissions are not equal) (VMX? EPT? Virtualization)
                return nemHCWinHandleMessageMemory(pVM, pVCpu, &pMsg->X64MemoryIntercept);
            case HvMessageTypeX64IoPortIntercept:
                return nemHCWinHandleMessageIoPort(pVM, pVCpu, &pMsg->X64IoPortIntercept);
            case HvMessageTypeX64Halt:
                return VINF_EM_HALT;
            case HvMessageTypeX64InterruptWindow:
                return nemHCWinHandleMessageInterruptWindow(pVM, pVCpu, &pMsg->X64InterruptWindow);
            case HvMessageTypeX64CpuidIntercept:
                return nemHCWinHandleMessageCpuId(pVM, pVCpu, &pMsg->X64CpuIdIntercept);
            case HvMessageTypeX64MsrIntercept:
                return nemHCWinHandleMessageMsr(pVCpu, &pMsg->X64MsrIntercept);
            case HvMessageTypeX64ExceptionIntercept:
                return nemHCWinHandleMessageException(pVCpu, &pMsg->X64ExceptionIntercept);
            case HvMessageTypeUnrecoverableException:
                return nemHCWinHandleMessageUnrecoverableException(pVCpu, &pMsg->X64InterceptHeader);
            //Here are the VMExit events that are not supported at present. They are returned directly
            case HvMessageTypeInvalidVpRegisterValue:
            case HvMessageTypeUnsupportedFeature:
            case HvMessageTypeTlbPageSizeMismatch:
                AssertLogRelMsgFailedReturn(("Message type %#x not implemented!\n%.32Rhxd\n", pMsg->Header.MessageType, pMsg),
                                            VERR_NEM_IPE_3);
            case HvMessageTypeX64ApicEoi:
            case HvMessageTypeX64LegacyFpError:
            case HvMessageTypeX64RegisterIntercept:
            case HvMessageTypeApicEoi:
            case HvMessageTypeFerrAsserted:
            case HvMessageTypeEventLogBufferComplete:
            case HvMessageTimerExpired:
                AssertLogRelMsgFailedReturn(("Unexpected message on CPU #%u: %#x\n", pVCpu->idCpu, pMsg->Header.MessageType),VERR_NEM_IPE_3);
            default:
                AssertLogRelMsgFailedReturn(("Unknown message on CPU #%u: %#x\n", pVCpu->idCpu, pMsg->Header.MessageType),
                                            VERR_NEM_IPE_3);
        }
    }
}

nemHCWinCopyStateFromX64Header

Copy related information from VMExit header

typedef struct
{
    //id of VCPU
    HV_VP_INDEX                     VpIndex;                
    //Length of instruction with exception
    uint8_t                         InstructionLength : 4;
    //cr8: irql
    uint8_t                         Cr8 : 4;                
    //Read, write, execute flag
    HV_INTERCEPT_ACCESS_TYPE        InterceptAccessType;    /**< 0x05 */
    //The flags of some VCPU are shown in the following structure
    HV_X64_VP_EXECUTION_STATE       ExecutionState;         /**< 0x06 */
    //Abnormal instruction address information
    HV_X64_SEGMENT_REGISTER         CsSegment;              /**< 0x08 */
    uint64_t                        Rip;                    /**< 0x18 */
    //RFlags of Guest
    uint64_t                        Rflags;                 /**< 0x20 */
} HV_X64_INTERCEPT_MESSAGE_HEADER;
typedef union
{
    uint16_t            AsUINT16;
    struct
    {
        uint16_t        Cpl                 : 2;     //R3 or R0
        uint16_t        Cr0Pe               : 1;     //Open paging or not
        uint16_t        Cr0Am               : 1;
        uint16_t        EferLma             : 1;     //Whether to turn on long mode
        uint16_t        DebugActive         : 1;     //Is it debugged
        uint16_t        InterruptionPending : 1;     //Whether there is a pending interrupt
        uint16_t        Reserved0           : 5;
        uint16_t        InterruptShadow     : 1;     //Block interrupt or not
        uint16_t        Reserved1           : 3;
    };
} HV_X64_VP_EXECUTION_STATE;

DECLINLINE(void) nemHCWinCopyStateFromX64Header(PVMCPUCC pVCpu, HV_X64_INTERCEPT_MESSAGE_HEADER const *pHdr)
{
    //Copy cs, rip, rflags
    NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.cs, pHdr->CsSegment);
    pVCpu->cpum.GstCtx.rip      = pHdr->Rip;
    pVCpu->cpum.GstCtx.rflags.u = pHdr->Rflags;
    //It is used to save the previous interrupt mask state and set the GuestOS register later
    pVCpu->nem.s.fLastInterruptShadow = pHdr->ExecutionState.InterruptShadow;
    if (!pHdr->ExecutionState.InterruptShadow)
    {
        //No mask interrupt
        //Open interrupt
        if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
        { /* likely */ }
        else
            VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
    }
    else
        //Block interrupts, keep rip
        EMSetInhibitInterruptsPC(pVCpu, pHdr->Rip);
}
VMMDECL(void) EMSetInhibitInterruptsPC(PVMCPU pVCpu, RTGCUINTPTR PC)
{
    pVCpu->em.s.GCPtrInhibitInterrupts = PC;
   	//Set masked interrupt
    VMCPU_FF_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
}

nemHCWinHandleMessageMemory

EPT related VMExit

typedef struct
{
    HV_X64_INTERCEPT_MESSAGE_HEADER Header;                 /**< 0x00 */
    HV_CACHE_TYPE                   CacheType;              /**< 0x28 */
    uint8_t                         InstructionByteCount;   /**< 0x2c */
    HV_X64_MEMORY_ACCESS_INFO       MemoryAccessInfo;       /**< 0x2d */
    uint16_t                        Reserved1;              /**< 0x2e */
    uint64_t                        GuestVirtualAddress;    /**< 0x30 */
    uint64_t                        GuestPhysicalAddress;   /**< 0x38 */
    uint8_t                         InstructionBytes[16];   /**< 0x40 */
    /* We don't the following (v5 / WinHvPlatform): */
    HV_X64_SEGMENT_REGISTER         DsSegment;              /**< 0x50 */
    HV_X64_SEGMENT_REGISTER         SsSegment;              /**< 0x60 */
    uint64_t                        Rax;                    /**< 0x70 */
    uint64_t                        Rcx;                    /**< 0x78 */
    uint64_t                        Rdx;                    /**< 0x80 */
    uint64_t                        Rbx;                    /**< 0x88 */
    uint64_t                        Rsp;                    /**< 0x90 */
    uint64_t                        Rbp;                    /**< 0x98 */
    uint64_t                        Rsi;                    /**< 0xa0 */
    uint64_t                        Rdi;                    /**< 0xa8 */
    uint64_t                        R8;                     /**< 0xb0 */
    uint64_t                        R9;                     /**< 0xb8 */
    uint64_t                        R10;                    /**< 0xc0 */
    uint64_t                        R11;                    /**< 0xc8 */
    uint64_t                        R12;                    /**< 0xd0 */
    uint64_t                        R13;                    /**< 0xd8 */
    uint64_t                        R14;                    /**< 0xe0 */
    uint64_t                        R15;                    /**< 0xe8 */
} HV_X64_MEMORY_INTERCEPT_MESSAGE;

NEM_TMPL_STATIC VBOXSTRICTRC
nemHCWinHandleMessageMemory(PVMCC pVM, PVMCPUCC pVCpu, HV_X64_MEMORY_INTERCEPT_MESSAGE const *pMsg)
{
    if (pMsg->Header.ExecutionState.InterruptionPending)
        pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
    
    PGMPHYSNEMPAGEINFO   Info;
    int rc = PGMPhysNemPageInfoChecker(pVM, pVCpu, pMsg->GuestPhysicalAddress, State.fWriteAccess, &Info,
                                       nemHCWinHandleMemoryAccessPageCheckerCallback, &State);
    if (RT_SUCCESS(rc))
    {
        if (Info.fNemProt & (  pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
                             ? NEM_PAGE_PROT_WRITE : NEM_PAGE_PROT_READ))
        {
            //If the map relationship in Hypervisor has been modified, execute this command again
            if (State.fCanResume)
            {
                EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_MEMORY_ACCESS),
                                 pMsg->Header.Rip + pMsg->Header.CsSegment.Base, uHostTsc);
                return VINF_SUCCESS;
            }
        }
    }
    //There is no modification in PGMPhysNemPageInfoChecker. Simulate the execution and submit it to IEM for processing
    //Simulate execution of memory access code    
    PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
                                              pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
                                            ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_WRITE)
                                            : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_READ),
                                            pMsg->Header.Rip + pMsg->Header.CsSegment.Base, uHostTsc);
    //Copy information from GuestOS
    nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
    rcStrict = nemR0WinImportStateStrict(pVM, pVCpu,
                                         NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES, "MemExit");
    if (rcStrict != VINF_SUCCESS)
        return rcStrict;
    if (!pExitRec)
    {
        //Simulate the execution of an instruction
        if (pMsg->InstructionByteCount > 0)
            rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), pMsg->Header.Rip,
                                                    pMsg->InstructionBytes, pMsg->InstructionByteCount);
        else
            rcStrict = IEMExecOne(pVCpu);
    }
    else
    {
        //Simulate execution of multiple instructions
        rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
    }
    return rcStrict;
}

nemHCWinHandleMemoryAccessPageCheckerCallback

NEM_TMPL_STATIC DECLCALLBACK(int)
nemHCWinHandleMemoryAccessPageCheckerCallback(PVMCC pVM, PVMCPUCC pVCpu, RTGCPHYS GCPhys, PPGMPHYSNEMPAGEINFO pInfo, void *pvUser)
{
    NEMHCWINHMACPCCSTATE *pState = (NEMHCWINHMACPCCSTATE *)pvUser;
    pState->fDidSomething = false;
    pState->fCanResume    = false;
    
    //u2NemState: current NEM page properties
    //fNemProt: required page properties
    uint8_t  u2State = pInfo->u2NemState;
    RTGCPHYS GCPhysSrc;
    if (   pVM->nem.s.fA20Enabled
        || !NEM_WIN_IS_SUBJECT_TO_A20(GCPhys))
        GCPhysSrc = GCPhys;
    else
    {
        GCPhysSrc = GCPhys & ~(RTGCPHYS)RT_BIT_32(20);
        PGMPHYSNEMPAGEINFO Info2;
        //Get the current memory property again
        int rc = PGMPhysNemPageInfoChecker(pVM, pVCpu, GCPhysSrc, pState->fWriteAccess, &Info2, NULL, NULL);
        *pInfo = Info2;
        pInfo->u2NemState = u2State;
    }
    
    int rc;
    switch (u2State)
    {
        case NEM_WIN_PAGE_STATE_UNMAPPED:
        case NEM_WIN_PAGE_STATE_NOT_SET:
          	//There is no map before, and the new state does not need map. It returns directly to simulate the execution of memory access instructions
            if (pInfo->fNemProt == NEM_PAGE_PROT_NONE)
            {
                return VINF_SUCCESS;
            }

            //If the new page does not need writable property, return to simulate execution
            if (   pState->fWriteAccess
                && !(pInfo->fNemProt & NEM_PAGE_PROT_WRITE))
            {
                return VINF_SUCCESS;
            }

            //map page
            rc = nemHCNativeSetPhysPage(pVM,
                                        pVCpu,
                                        GCPhysSrc & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK,
                                        GCPhys & ~(RTGCPHYS)X86_PAGE_OFFSET_MASK,
                                        pInfo->fNemProt,
                                        &u2State,
                                        true /*fBackingState*/);
            pInfo->u2NemState = u2State;
            pState->fDidSomething = true;
            //The page processing is completed, and the memory access instruction can be executed again
            pState->fCanResume    = true;
            return rc;
        case NEM_WIN_PAGE_STATE_READABLE:
            //If the memory is readable before, but no memory is needed to be writable, it will return directly to simulate the execution of memory access instructions
            if (   !(pInfo->fNemProt & NEM_PAGE_PROT_WRITE)
                && (pInfo->fNemProt & (NEM_PAGE_PROT_READ | NEM_PAGE_PROT_EXECUTE)))
            {
                return VINF_SUCCESS;
            }
	        //Need memory to be writable, modify page properties plus writable
            if (   (pInfo->fNemProt & NEM_PAGE_PROT_WRITE)
                && pState->fWriteAccess)
            {
                //Modify the page map attribute plus readability
                rc = nemHCWinHypercallMapPage(pVM, pVCpu, GCPhysSrc, GCPhys,
                                              HV_MAP_GPA_READABLE   | HV_MAP_GPA_WRITABLE
                                              | HV_MAP_GPA_EXECUTABLE | HV_MAP_GPA_EXECUTABLE_AGAIN);
                if (RT_SUCCESS(rc))
                {
                    //Tag page property can be written, return to execute memory access instruction again
                    pInfo->u2NemState = NEM_WIN_PAGE_STATE_WRITABLE;
                    pState->fDidSomething = true;
                    pState->fCanResume    = true;
                }
            }
            else
            {
                //Return, simulate execution of memory access instruction
                rc = VINF_SUCCESS;
            }
            return rc;
        case NEM_WIN_PAGE_STATE_WRITABLE:
            //The original memory can be written, and the pages that need memory can be written, do nothing
            if (pInfo->fNemProt & NEM_PAGE_PROT_WRITE)
            {
                //The original memory can be written, nothing has changed, return, simulate the execution of memory access instructions
                if (pInfo->u2OldNemState == NEM_WIN_PAGE_STATE_WRITABLE)
                else
                {
                    //Change the memory property and execute the memory access instruction again
                    pState->fCanResume = true;
                }
                return VINF_SUCCESS;
            }
            AssertFailed();
    }
   
    return VINF_SUCCESS;
}

nemHCWinHandleMessageIoPort

The process is similar to that of the hmR0VmxExitIoInstr function in VMX mode

NEM_TMPL_STATIC VBOXSTRICTRC
nemHCWinHandleMessageIoPort(PVMCC pVM, PVMCPUCC pVCpu, HV_X64_IO_PORT_INTERCEPT_MESSAGE const *pMsg)
{
    if (pMsg->Header.ExecutionState.InterruptionPending)
        pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT;
   	//Determine whether multiple instructions can be simulated
    VBOXSTRICTRC rcStrict;
    PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
                                            !pMsg->AccessInfo.StringOp
                                            ? (  pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
                                               ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_WRITE)
                                               : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_READ))
                                            : (  pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
                                               ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_WRITE)
                                               : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_READ)),
                                            pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
    if (!pExitRec)
    {
        if (!pMsg->AccessInfo.StringOp)
        {
            //in/out directive
            nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
            if (pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE)
            {
                //OUT instruction, call IOMIOPortWrite to write data to IO Port
                rcStrict = IOMIOPortWrite(pVM, pVCpu, pMsg->PortNumber, (uint32_t)pMsg->Rax & fAndMask, pMsg->AccessInfo.AccessSize);
                if (IOM_SUCCESS(rcStrict))
                    nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 1);
                else if (   rcStrict == VINF_IOM_R3_IOPORT_WRITE
                         && !pVCpu->cpum.GstCtx.rflags.Bits.u1TF)
                    //Failed to call IOMIOPortWrite, set pending IO event, return R3 loop processing
                    return EMRZSetPendingIoPortWrite(pVCpu, pMsg->PortNumber, pMsg->Header.InstructionLength,pMsg->AccessInfo.AccessSize, (uint32_t)pMsg->Rax & fAndMask);
                else
                {
                    //Single step set
                    pVCpu->cpum.GstCtx.rax = pMsg->Rax;
                    pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
                }
            }
            else
            {
                //IN instruction, call IOMIOPortRead to read IO Port
                uint32_t uValue = 0;
                rcStrict = IOMIOPortRead(pVM, pVCpu, pMsg->PortNumber, &uValue, pMsg->AccessInfo.AccessSize);
                if (IOM_SUCCESS(rcStrict))
                {
                    //Read IOport successfully, continue to execute next instruction
                    if (pMsg->AccessInfo.AccessSize != 4)
                        pVCpu->cpum.GstCtx.rax = (pMsg->Rax & ~(uint64_t)fAndMask) | (uValue & fAndMask);
                    else
                        pVCpu->cpum.GstCtx.rax = uValue;
                    pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
                    nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 1);
                }
                else
                {
                    //Failed to call IOMIOPortRead, set pending IO event, return R3 loop processing
                    pVCpu->cpum.GstCtx.rax = pMsg->Rax;
                    pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
                    if (   rcStrict == VINF_IOM_R3_IOPORT_READ
                        && !pVCpu->cpum.GstCtx.rflags.Bits.u1TF
                        return EMRZSetPendingIoPortRead(pVCpu, pMsg->PortNumber, pMsg->Header.InstructionLength,pMsg->AccessInfo.AccessSize);
                 }
            }
        }
        else
		{
            //ins/outs instruction
            nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
            pVCpu->cpum.GstCtx.fExtrn &= ~(  CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
                              | CPUMCTX_EXTRN_DS  | CPUMCTX_EXTRN_ES);
            NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ds, pMsg->DsSegment);
            NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.es, pMsg->EsSegment);
            pVCpu->cpum.GstCtx.rax = pMsg->Rax;
            pVCpu->cpum.GstCtx.rcx = pMsg->Rcx;
            pVCpu->cpum.GstCtx.rdi = pMsg->Rdi;
            pVCpu->cpum.GstCtx.rsi = pMsg->Rsi;
            rcStrict = nemR0WinImportStateStrict(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM, "IOExit");
            if (rcStrict != VINF_SUCCESS)
                return rcStrict;
            //Let IEM simulate the execution of this instruction
            //Functions such as fniempop ﹣ def (iempop ﹣ inswd ﹣ YV ﹣ DX) fniempop ﹣ def (iempop ﹣ outswd ﹣ YV ﹣ DX) simulate execution
            rcStrict = IEMExecOne(pVCpu);
		}
         return rcStrict;
    }
    //To this end, you need to simulate multiple code execution
    //Get exception information
    nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
    if (!pMsg->AccessInfo.StringOp)
        pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_RAX;
    else
    {
        pVCpu->cpum.GstCtx.fExtrn &= ~(  CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
                          | CPUMCTX_EXTRN_DS  | CPUMCTX_EXTRN_ES);
        NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.ds, pMsg->DsSegment);
        NEM_WIN_COPY_BACK_SEG(pVCpu->cpum.GstCtx.es, pMsg->EsSegment);
        pVCpu->cpum.GstCtx.rcx = pMsg->Rcx;
        pVCpu->cpum.GstCtx.rdi = pMsg->Rdi;
        pVCpu->cpum.GstCtx.rsi = pMsg->Rsi;
    }
    pVCpu->cpum.GstCtx.rax = pMsg->Rax;
    //Get register memory needed by IEM from GuestOS
    rcStrict = nemR0WinImportStateStrict(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM, "IOExit");
    if (rcStrict != VINF_SUCCESS)
        return rcStrict;
    //Execute multiple instructions at a time
    rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
    return rcStrict;
}

nemHCWinHandleMessageInterruptWindow

The function of hmR0VmxExitIntWindow (VMX? Exit? Int? Window) corresponding to VMX has not done anything now

NEM_TMPL_STATIC VBOXSTRICTRC
nemHCWinHandleMessageInterruptWindow(PVMCC pVM, PVMCPUCC pVCpu, HV_X64_INTERRUPT_WINDOW_MESSAGE const *pMsg)
{
    EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_INTTERRUPT_WINDOW),
                     pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
    nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
    //Nothing is done now
}

nemHCWinHandleMessageCpuId

hmR0VmxExitCpuid (hmR0VmxExitCpuid) function corresponding to VMX

NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleMessageCpuId(PVMCC pVM, PVMCPUCC pVCpu, HV_X64_CPUID_INTERCEPT_MESSAGE const *pMsg)
{
    //Whether multiple instructions need to be executed continuously
    PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_CPUID),
                                            pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
    if (!pExitRec)
    {
        //Simulate execution of only one instruction
        nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
        //Get parameters of CPUID
        pVCpu->cpum.GstCtx.rax = (uint32_t)pMsg->Rax;
        pVCpu->cpum.GstCtx.rcx = (uint32_t)pMsg->Rcx;
        pVCpu->cpum.GstCtx.rdx = (uint32_t)pMsg->Rdx;
        pVCpu->cpum.GstCtx.rbx = (uint32_t)pMsg->Rbx;
        pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
        //Call the API in CPUM to get the return value of CPUID
        CPUMGetGuestCpuId(pVCpu, pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.ecx,
                          &pVCpu->cpum.GstCtx.eax, &pVCpu->cpum.GstCtx.ebx, &pVCpu->cpum.GstCtx.ecx, &pVCpu->cpum.GstCtx.edx);
       	//RIP points to the next instruction
        nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 2);
        return VINF_SUCCESS;
    }
   	//Simulate execution of multiple instructions
    nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
    pVCpu->cpum.GstCtx.rax = pMsg->Rax;
    pVCpu->cpum.GstCtx.rcx = pMsg->Rcx;
    pVCpu->cpum.GstCtx.rdx = pMsg->Rdx;
    pVCpu->cpum.GstCtx.rbx = pMsg->Rbx;
    pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
    
    VBOXSTRICTRC rcStrict = nemR0WinImportStateStrict(pVM, pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM, "CpuIdExit");
    if (rcStrict != VINF_SUCCESS)
        return rcStrict;
    //Simulate execution of multiple instructions
    VBOXSTRICTRC rcStrictExec = EMHistoryExec(pVCpu, pExitRec, 0);
    return rcStrictExec;
}

nemHCWinHandleMessageMsr

The function hmR0VmxExitRdmsr/hmR0VmxExitWrmsr corresponding to VMX obtains the value of MSR register from CPUM

typedef struct _HV_X64_MSR_INTERCEPT_MESSAGE
{
    HV_X64_INTERCEPT_MESSAGE_HEADER     Header;                 /**< 0x00 */
    uint32_t                            MsrNumber;              /**< 0x28 (ecx) */
    uint32_t                            Reserved;               /**< 0x2c */
    uint64_t                            Rdx;                    /**< 0x30 */
    uint64_t                            Rax;                    /**< 0x38 */
} HV_X64_MSR_INTERCEPT_MESSAGE;

NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleMessageMsr(PVMCPUCC pVCpu, HV_X64_MSR_INTERCEPT_MESSAGE const *pMsg)
{
  if (pMsg->Header.ExecutionState.Cpl == 0)
  {
    PCEMEXITREC pExitRec = EMHistoryAddExit(pVCpu,
                                            pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
                                            ? EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_WRITE)
                                            : EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_READ),
                                            pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
	//Copy data from VMExit information
    nemHCWinCopyStateFromX64Header(pVCpu, &pMsg->Header);
	//Call hypercall to get the required Guest register information
    rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu,
                                                 (!pExitRec ? 0 : IEM_CPUMCTX_EXTRN_MUST_MASK)
                                                 | CPUMCTX_EXTRN_ALL_MSRS | CPUMCTX_EXTRN_CR0
                                                 | CPUMCTX_EXTRN_CR3 | CPUMCTX_EXTRN_CR4,
                                                 "MSRs");
    if (rcStrict == VINF_SUCCESS)
    {
        if (!pExitRec)
        {
            //Discontinuous execution of multiple instructions
            if (pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE)
            {
                //wrmsr
                rcStrict = CPUMSetGuestMsr(pVCpu, pMsg->MsrNumber, RT_MAKE_U64((uint32_t)pMsg->Rax, (uint32_t)pMsg->Rdx));
                if (rcStrict == VINF_SUCCESS)
                {
                    nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 2);
                    return VINF_SUCCESS;
                }
                //If injection interrupt is needed for simulation execution return, return to enable R3 simulation execution?
                if (rcStrict == VERR_CPUM_RAISE_GP_0)
                    rcStrict = VINF_CPUM_R3_MSR_WRITE;
                return rcStrict;
            }
            else
            {
                //rdmsr
                uint64_t uValue = 0;
                //Call the function in CPUM to read msr
                rcStrict = CPUMQueryGuestMsr(pVCpu, pMsg->MsrNumber, &uValue);
                if (rcStrict == VINF_SUCCESS)
                {
                    //Write to cpum, and then write to GuestOS
                    pVCpu->cpum.GstCtx.rax = (uint32_t)uValue;
                    pVCpu->cpum.GstCtx.rdx = uValue >> 32;
                    pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RDX);
                    nemHCWinAdvanceGuestRipAndClearRF(pVCpu, &pMsg->Header, 2);
                    return VINF_SUCCESS;
                }
            }
        }
        else
        {
            //Simulate execution of multiple instructions
            rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
            return rcStrict;
        }
    }
  }
  //R3 calls msr instruction, inject GP
  //First read the required register from GuestOS
  rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_ALL_MSRS, "MSR");
  if (rcStrict == VINF_SUCCESS)
  {		
      //Simulate execution inject a GP
      rcStrict = IEMInjectTrap(pVCpu, X86_XCPT_GP, TRPM_TRAP, 0, 0, 0);
      if (rcStrict == VINF_IEM_RAISED_XCPT)
          rcStrict = VINF_SUCCESS;
  }
}

nemHCWinHandleMessageException

NEM_TMPL_STATIC VBOXSTRICTRC
nemHCWinHandleMessageException(PVMCPUCC pVCpu, HV_X64_EXCEPTION_INTERCEPT_MESSAGE const *pMsg)
{
    //Read the relevant register information required for exception handling from GuestOS
    nemHCWinCopyStateFromExceptionMessage(pVCpu, pMsg, true /*fClearXcpt*/);
    uint64_t fWhat = NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM;
    //If it is a DB exception, you also need to obtain the relevant DRx register
    if (pMsg->ExceptionVector == X86_XCPT_DB)
        fWhat |= CPUMCTX_EXTRN_DR0_DR3 | CPUMCTX_EXTRN_DR7 | CPUMCTX_EXTRN_DR6;
    VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, fWhat, "Xcpt");
    //Handling exceptions
    TRPMEVENT enmEvtType = TRPM_TRAP;
    switch (pMsg->ExceptionVector)
    {
        //UD
        case X86_XCPT_UD:
            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_UD),
                             pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
		   //If it is a vmmcall (AMD) & vmcall (Intel) instruction, it needs to simulate the execution of this instruction, and other UD exceptions are directly input to Guest
            if (nemHcWinIsInterestingUndefinedOpcode(pMsg->InstructionByteCount, pMsg->InstructionBytes,
                                                     pMsg->Header.ExecutionState.EferLma && pMsg->Header.CsSegment.Long ))
            {
                //Vmmcall (AMD) & vmcall (Intel) is a multibyte instruction that calls iemexecenewithprefetchedbypc to simulate execution
                rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), pMsg->Header.Rip,
                                                        pMsg->InstructionBytes, pMsg->InstructionByteCount);
                return rcStrict;
            }
       case X86_XCPT_DB:
            //Debugging interrupt
            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_DB),
                             pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
            break;
       case X86_XCPT_BP:
            //BP interrupt
            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_BP),
                             pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
            //Set interrupt type to INT 3
            enmEvtType = TRPM_SOFTWARE_INT; 
            break;
    }
    //Simulation execution injection exception
    rcStrict = IEMInjectTrap(pVCpu, pMsg->ExceptionVector, enmEvtType, pMsg->ErrorCode,
                             pMsg->ExceptionParameter /*??*/, pMsg->Header.InstructionLength);
}

//Determine whether it is a VMCALL instruction
DECLINLINE(bool) nemHcWinIsInterestingUndefinedOpcode(uint8_t cbOpcodes, uint8_t const *pbOpcodes, bool f64BitMode)
{
    //Instruction operands up to 3 bytes
    while (cbOpcodes >= 3)
    {
        switch (pbOpcodes[0])
        {
            case 0x0f:
                switch (pbOpcodes[1])
                {
                    case 0x01:
                        switch (pbOpcodes[2])
                        {
                            //VMCALL instruction found, return true
                            case 0xc1: /* 0f 01 c1  VMCALL */
                                return true;
                            case 0xd9: /* 0f 01 d9  VMMCALL */
                                return true;
                            default:
                                break;
                        }
                        break;
                }
                break;
            default:
                return false;
            //Filter out prefixes
            case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
            case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
                if (!f64BitMode)
                    return false;
                RT_FALL_THRU();
            case X86_OP_PRF_CS:
            case X86_OP_PRF_SS:
            case X86_OP_PRF_DS:
            case X86_OP_PRF_ES:
            case X86_OP_PRF_FS:
            case X86_OP_PRF_GS:
            case X86_OP_PRF_SIZE_OP:
            case X86_OP_PRF_SIZE_ADDR:
            case X86_OP_PRF_LOCK:
            case X86_OP_PRF_REPZ:
            case X86_OP_PRF_REPNZ:
                cbOpcodes--;
                pbOpcodes++;
                continue;
        }
        break;
    }
}

nemHCWinHandleMessageUnrecoverableException

hmR0VmxExitTripleFault corresponding to VMX

NEM_TMPL_STATIC VBOXSTRICTRC
nemHCWinHandleMessageUnrecoverableException(PVMCPUCC pVCpu, HV_X64_INTERCEPT_MESSAGE_HEADER const *pMsgHdr)
{
    EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FT(EMEXIT_F_KIND_NEM, NEMEXITTYPE_UNRECOVERABLE_EXCEPTION),
                     pMsgHdr->Rip + pMsgHdr->CsSegment.Base, ASMReadTSC());
    //Copy information from GuestOS
    nemHCWinCopyStateFromX64Header(pVCpu, pMsgHdr);
    VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_ALL, "TripleExit");
    if (rcStrict == VINF_SUCCESS)
    {
        //Let IEM simulate this instruction
        rcStrict = IEMExecOne(pVCpu);
        if (rcStrict == VINF_SUCCESS)
        {
            //Successful simulation execution (what kind of TripleFault can simulate successful execution?) , return successfully continue to execute GuestOS code
            pVCpu->cpum.GstCtx.fExtrn &= ~CPUMCTX_EXTRN_NEM_WIN_EVENT_INJECT; 
            return VINF_SUCCESS;
        }
    }
    //Return error code
    return rcStrict;
}
Published 24 original articles, won praise 1, visited 801
Private letter follow

Tags: Attribute REST VirtualBox

Posted on Thu, 13 Feb 2020 06:01:29 -0800 by Sanoz0r