EM - Execution Monitor / Manager
14.1 用模拟执行多条指令解决过频繁VMExit
因为发生一次VMExit,CPU需要切换上下文消耗大量性能,为了提升性能,当CPU执行部分执行指令的时候,EM会检查发生VMExit的这个RIP是否曾经发生过VMExit,如果曾经发生过多次VMExit事件,则会从这条指令开始模拟执行最多cMaxInstructions条指令。
可以模拟执行的指令列表:
typedef enum EMEXITTYPE
{
EMEXITTYPE_INVALID = 0,
EMEXITTYPE_IO_PORT_READ,
EMEXITTYPE_IO_PORT_WRITE,
EMEXITTYPE_IO_PORT_STR_READ,
EMEXITTYPE_IO_PORT_STR_WRITE,
EMEXITTYPE_MMIO,
EMEXITTYPE_MMIO_READ,
EMEXITTYPE_MMIO_WRITE,
EMEXITTYPE_MSR_READ,
EMEXITTYPE_MSR_WRITE,
EMEXITTYPE_CPUID,
EMEXITTYPE_RDTSC,
EMEXITTYPE_MOV_CRX,
EMEXITTYPE_MOV_DRX,
//只用于RAW-mode
EMEXITTYPE_INVLPG,
EMEXITTYPE_LLDT,
EMEXITTYPE_RDPMC,
EMEXITTYPE_CLTS,
EMEXITTYPE_STI,
EMEXITTYPE_INT,
EMEXITTYPE_SYSCALL,
EMEXITTYPE_SYSENTER,
EMEXITTYPE_HLT
/** @} */
} EMEXITTYPE;
比如在CPUID的VMExit handle里
HMVMX_EXIT_DECL hmR0VmxExitCpuid(PVMCPUCC pVCpu, PVMXTRANSIENT pVmxTransient)
{
...
//调用EMHistoryUpdateFlagsAndTypeAndPC获取是否需要模拟执行很多指令
VBOXSTRICTRC rcStrict;
PCEMEXITREC pExitRec = EMHistoryUpdateFlagsAndTypeAndPC(pVCpu,
EMEXIT_MAKE_FT(EMEXIT_F_KIND_EM | EMEXIT_F_HM, EMEXITTYPE_CPUID),
pVCpu->cpum.GstCtx.rip + pVCpu->cpum.GstCtx.cs.u64Base);
if (!pExitRec)
{
//不需要模拟执行很多指令,只模拟执行一条指令
rcStrict = IEMExecDecodedCpuid(pVCpu, pVmxTransient->cbExitInstr);
....
}
else
{
//模拟执行多条指令
rcStrict = EMHistoryExec(pVCpu, pExitRec, 0);
ASMAtomicUoOrU64(&pVCpu->hm.s.fCtxChanged, HM_CHANGED_ALL_GUEST);
}
return rcStrict;
}
EM用hash表来保存已发生过的VMExit中断信息,hash表保存在VCpu->em.s.aExitRecords,用1024项,用RIP地址的后20位作为hash表的index。
EMHistoryUpdateFlagsAndTypeAndPC
VMM_INT_DECL(PCEMEXITREC) EMHistoryUpdateFlagsAndTypeAndPC(PVMCPUCC pVCpu, uint32_t uFlagsAndType, uint64_t uFlatPC)
{
uint64_t uExitNo = pVCpu->em.s.iNextExit - 1;
PEMEXITENTRY pHistEntry = &pVCpu->em.s.aExitHistory[(uintptr_t)uExitNo & 0xff];
pHistEntry->uFlagsAndType = uFlagsAndType;
pHistEntry->uFlatPC = uFlatPC;
return emHistoryAddOrUpdateRecord(pVCpu, uFlagsAndType, uFlatPC, pHistEntry, uExitNo);
}
EMHistoryAddExit
指令将指令加入EMHistory hash table里
//PostRun的时候调用这个函数,传入uFlatPC这个是发生VMExit的IP地址
VMM_INT_DECL(PCEMEXITREC) EMHistoryAddExit(PVMCPUCC pVCpu, uint32_t uFlagsAndType, uint64_t uFlatPC, uint64_t uTimestamp)
{
PEMEXITENTRY pHistEntry = &pVCpu->em.s.aExitHistory[(uintptr_t)uExitNo & 0xff];
pHistEntry->uFlatPC = uFlatPC;
pHistEntry->uTimestamp = uTimestamp;
pHistEntry->uFlagsAndType = uFlagsAndType;
pHistEntry->idxSlot = UINT32_MAX;
emHistoryAddOrUpdateRecord(pVCpu, uFlagsAndType, uFlatPC, pHistEntry, uExitNo);
}
static PCEMEXITREC emHistoryAddOrUpdateRecord(PVMCPU pVCpu, uint64_t uFlagsAndType, uint64_t uFlatPC,
PEMEXITENTRY pHistEntry, uint64_t uExitNo)
{
//VMExit RIP地址的后20位作为hash表的index
# define EM_EXIT_RECORDS_IDX_MASK 0x3ff
uintptr_t idxSlot = ((uintptr_t)uFlatPC >> 1) & EM_EXIT_RECORDS_IDX_MASK;
PEMEXITREC pExitRec = &pVCpu->em.s.aExitRecords[idxSlot];
if (pExitRec->uFlatPC == uFlatPC)
{
//如果hash表命中了
pHistEntry->idxSlot = (uint32_t)idxSlot;
//指令类型也相同,找到了已执行过的指令
if (pExitRec->uFlagsAndType == uFlagsAndType)
{
pExitRec->uLastExitNo = uExitNo;
}
//指令变了,说明同一个地址里的指令变成了另一个地址,缓存无效,更新pExitRec里的值成新的值
else
{
//默认enmAction 是EMEXITACTION_NORMAL
return emHistoryRecordInit(pExitRec, uFlatPC, uFlagsAndType, uExitNo);
}
}
//如果标记这个record要被释放掉,重置pExitRec成新的值
else if (pExitRec->enmAction == EMEXITACTION_FREE_RECORD)
{
return emHistoryRecordInitNew(pVCpu, pHistEntry, idxSlot, pExitRec, uFlatPC, uFlagsAndType, uExitNo);
}
else
{
//hash碰撞,用开放地址法解决hash碰撞
uintptr_t idxOldest = idxSlot;
uint64_t uOldestExitNo = pExitRec->uLastExitNo;
unsigned iOldestStep = 0;
unsigned iStep = 1;
uintptr_t const idxAdd = (uintptr_t)(uFlatPC >> 11) & (EM_EXIT_RECORDS_IDX_MASK / 4);
for (;;)
{
//检查下一个hash表
idxSlot += idxAdd;
idxSlot &= EM_EXIT_RECORDS_IDX_MASK;
pExitRec = &pVCpu->em.s.aExitRecords[idxSlot];
//如果下一个hash表的IP和当前IP一样
if (pExitRec->uFlatPC == uFlatPC)
{
pHistEntry->idxSlot = (uint32_t)idxSlot;
//指令也一样,hash命中
if (pExitRec->uFlagsAndType == uFlagsAndType)
{
pExitRec->uLastExitNo = uExitNo;
break;
}
//指令变了,说明同一个地址里的指令变成了另一个地址,缓存无效,更新pExitRec里的值成新的值
return emHistoryRecordInit(pExitRec, uFlatPC, uFlagsAndType, uExitNo);
}
//如果标记这个record要被释放掉,重置pExitRec成新的值
if (pExitRec->enmAction == EMEXITACTION_FREE_RECORD)
{
return emHistoryRecordInitNew(pVCpu, pHistEntry, idxSlot, pExitRec, uFlatPC, uFlagsAndType, uExitNo);
}
/* Is it the least recently used one? */
if (pExitRec->uLastExitNo < uOldestExitNo)
{
uOldestExitNo = pExitRec->uLastExitNo;
idxOldest = idxSlot;
iOldestStep = iStep;
}
//如果找了8步都还是碰撞,替换第8项
iStep++;
if (RT_LIKELY(iStep < 8 + 1))
{ /* likely */ }
else
{
pExitRec = &pVCpu->em.s.aExitRecords[idxOldest];
return emHistoryRecordInitReplacement(pHistEntry, idxOldest, pExitRec, uFlatPC, uFlagsAndType, uExitNo);
}
}
}
//找到了缓存,根据enmAction决定如果执行
switch (pExitRec->enmAction)
{
//只有缓存被命中了256次,才设置成EMEXITACTION_EXEC_PROBE,否则不多条执行
case EMEXITACTION_NORMAL:
{
uint64_t const cHits = ++pExitRec->cHits;
if (cHits < 256)
return NULL;
pExitRec->enmAction = EMEXITACTION_EXEC_PROBE;
return pExitRec;
}
//pExitRec->cHits加1, 但不多条执行
case EMEXITACTION_NORMAL_PROBED:
pExitRec->cHits += 1;
return NULL;
default:
pExitRec->cHits += 1;
return pExitRec;
//缓存连续命中256到512次的时候,是exec_probe状态,但如果在256次exec_probe执行过程中状态没有被改成EMEXITACTION_EXEC_WITH_MAX(执行失败),则状态设置成EMEXITACTION_NORMAL_PROBED,不再多条执行
case EMEXITACTION_EXEC_PROBE:
{
uint64_t const cHits = ++pExitRec->cHits;
if (cHits < 512)
return pExitRec;
pExitRec->enmAction = EMEXITACTION_NORMAL_PROBED;
return NULL;
}
}
}
EMHistoryExec
模拟执行
VMM_INT_DECL(VBOXSTRICTRC) EMHistoryExec(PVMCPUCC pVCpu, PCEMEXITREC pExitRec, uint32_t fWillExit)
{
switch (pExitRec->enmAction)
{
case EMEXITACTION_EXEC_WITH_MAX:
{
//调用IEMExecForExits执行最多pVCpu->em.s.cHistoryExecMaxInstructions,最少pExitRec->cMaxInstructionsWithoutExit条指令
VBOXSTRICTRC rcStrict = IEMExecForExits(pVCpu, fWillExit,
pExitRec->cMaxInstructionsWithoutExit /* cMinInstructions*/,
pVCpu->em.s.cHistoryExecMaxInstructions,
pExitRec->cMaxInstructionsWithoutExit,
&ExecStats);
emHistoryExecSetContinueExitRecIdx(pVCpu, rcStrict, pExitRec);
//如果返回没有实现指令或者已经执行了cHistoryExecMaxInstructions条指令,返回VINF_SUCCESS
//其他返回值直接返回IEMExecForExits的执行结果
if ( ( rcStrict != VERR_IEM_INSTR_NOT_IMPLEMENTED
&& rcStrict != VERR_IEM_ASPECT_NOT_IMPLEMENTED)
|| ExecStats.cInstructions == 0)
{ /* likely */ }
else
rcStrict = VINF_SUCCESS;
return rcStrict;
}
//尝试多条执行,如果成功,设置成EMEXITACTION_EXEC_WITH_MAX,否则这次成EMEXITACTION_NORMAL_PROBED
case EMEXITACTION_EXEC_PROBE:
{
//调用IEMExecForExits执行最多pVCpu->em.s.cHistoryExecMaxInstructions,最少pVCpu->em.s.cHistoryProbeMinInstructions条指令
PEMEXITREC pExitRecUnconst = (PEMEXITREC)pExitRec;
VBOXSTRICTRC rcStrict = IEMExecForExits(pVCpu, fWillExit,
pVCpu->em.s.cHistoryProbeMinInstructions,
pVCpu->em.s.cHistoryExecMaxInstructions,
pVCpu->em.s.cHistoryProbeMaxInstructionsWithoutExit,
&ExecStats);
emHistoryExecSetContinueExitRecIdx(pVCpu, rcStrict, pExitRecUnconst);
//如果执行成功,状态修改成EMEXITACTION_EXEC_WITH_MAX
if ( ExecStats.cExits >= 2
&& RT_SUCCESS(rcStrict))
{
pExitRecUnconst->cMaxInstructionsWithoutExit = ExecStats.cMaxExitDistance;
pExitRecUnconst->enmAction = EMEXITACTION_EXEC_WITH_MAX;
}
else
{
//执行失败,enmAction设置成EMEXITACTION_NORMAL_PROBED,不再多条模拟
pExitRecUnconst->enmAction = EMEXITACTION_NORMAL_PROBED;
pVCpu->em.s.idxContinueExitRec = UINT16_MAX;
if ( rcStrict == VERR_IEM_INSTR_NOT_IMPLEMENTED
|| rcStrict == VERR_IEM_ASPECT_NOT_IMPLEMENTED)
rcStrict = VINF_SUCCESS;
}
return rcStrict;
}
}//end of switch
}
VMMDECL(VBOXSTRICTRC) IEMExecForExits(PVMCPUCC pVCpu, uint32_t fWillExit, uint32_t cMinInstructions, uint32_t cMaxInstructions,
uint32_t cMaxInstructionsWithoutExits, PIEMEXECFOREXITSTATS pStats)
{
VBOXSTRICTRC rcStrict = iemInitDecoderAndPrefetchOpcodes(pVCpu, false);
for (;;)
{
//调用IEM里的函数模拟执行一条指令
uint32_t const cPotentialExits = pVCpu->iem.s.cPotentialExits;
uint8_t b; IEM_OPCODE_GET_NEXT_U8(&b);
rcStrict = FNIEMOP_CALL(g_apfnOneByteMap[b]);
if ( cPotentialExits != pVCpu->iem.s.cPotentialExits
&& cInstructionSinceLastExit > 0 /* don't count the first */ )
{
pStats->cExits += 1;
if (cInstructionSinceLastExit > pStats->cMaxExitDistance)
pStats->cMaxExitDistance = cInstructionSinceLastExit;
cInstructionSinceLastExit = 0;
}
//模拟执行成功
if (RT_LIKELY(rcStrict == VINF_SUCCESS))
{
pVCpu->iem.s.cInstructions++;
pStats->cInstructions++;
cInstructionSinceLastExit++;
if (RT_LIKELY(pVCpu->iem.s.rcPassUp == VINF_SUCCESS))
{
//forceactions里去掉sync cr3,刷新tlb等
uint64_t fCpu = pVCpu->fLocalForcedActions
& ( VMCPU_FF_ALL_MASK & ~( VMCPU_FF_PGM_SYNC_CR3
| VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL
| VMCPU_FF_TLB_FLUSH
| VMCPU_FF_INHIBIT_INTERRUPTS
| VMCPU_FF_BLOCK_NMIS
| VMCPU_FF_UNHALT ));
//如果没有forcesaction,或者没有达到执行条数的下线都会继续执行
if (RT_LIKELY( ( ( !fCpu
|| ( !(fCpu & ~(VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC))
&& !pVCpu->cpum.GstCtx.rflags.Bits.u1IF))
&& !VM_FF_IS_ANY_SET(pVM, VM_FF_ALL_MASK) )
|| pStats->cInstructions < cMinInstructions))
{
//如果没有达到执行条数的上限
if (pStats->cInstructions < cMaxInstructions)
{
if (cInstructionSinceLastExit <= cMaxInstructionsWithoutExits)
{
#ifdef IN_RING0
if ( !fCheckPreemptionPending
|| !RTThreadPreemptIsPending(NIL_RTTHREAD))
#endif
{
//继续执行下一条指令
iemReInitDecoder(pVCpu);
continue;
}
#ifdef IN_RING0
rcStrict = VINF_EM_RAW_INTERRUPT;
break;
#endif
}
}
}
}
//其他情况,一律break,退出模拟执行
break
}
}
}
14.2 保存/加载虚拟机(SSM)的callback
emR3Save
static DECLCALLBACK(int) emR3Save(PVM pVM, PSSMHANDLE pSSM)
{
for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
{
PVMCPU pVCpu = pVM->apCpusR3[idCpu];
//保存前一个状态
SSMR3PutU32(pSSM, pVCpu->em.s.enmPrevState);
//保存MWait/Monitor的设置
SSMR3PutU32(pSSM, pVCpu->em.s.MWait.fWait);
SSMR3PutGCPtr(pSSM, pVCpu->em.s.MWait.uMWaitRAX);
SSMR3PutGCPtr(pSSM, pVCpu->em.s.MWait.uMWaitRCX);
SSMR3PutGCPtr(pSSM, pVCpu->em.s.MWait.uMonitorRAX);
SSMR3PutGCPtr(pSSM, pVCpu->em.s.MWait.uMonitorRCX);
SSMR3PutGCPtr(pSSM, pVCpu->em.s.MWait.uMonitorRDX);
}
}
emR3Load
static DECLCALLBACK(int) emR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
{
PVMCPU pVCpu = pVM->apCpusR3[idCpu];
if (uVersion > EM_SAVED_STATE_VERSION_PRE_SMP)
{
//恢复前一个状态,并且把当前状态设置成suspend
SSM_GET_ENUM32_RET(pSSM, pVCpu->em.s.enmPrevState, EMSTATE);
pVCpu->em.s.enmState = EMSTATE_SUSPENDED;
}
if (uVersion > EM_SAVED_STATE_VERSION_PRE_MWAIT)
{
//恢复MWait和Monitor的设置
rc = SSMR3GetU32(pSSM, &pVCpu->em.s.MWait.fWait);
rc = SSMR3GetGCPtr(pSSM, &pVCpu->em.s.MWait.uMWaitRAX);
rc = SSMR3GetGCPtr(pSSM, &pVCpu->em.s.MWait.uMWaitRCX);
rc = SSMR3GetGCPtr(pSSM, &pVCpu->em.s.MWait.uMonitorRAX);
rc = SSMR3GetGCPtr(pSSM, &pVCpu->em.s.MWait.uMonitorRCX);
rc = SSMR3GetGCPtr(pSSM, &pVCpu->em.s.MWait.uMonitorRDX);
}
}
14.3 MWait和Monitor 指令模拟
SIMD扩展3引入了两条指令(MONITOR和WAIT)以帮助多线程软件提升线程同步
Monitor指令可以指定一个写回属性的内存地址,MWAIT指令让CPU进入hlt状态直到这个指定的内存地址被写入。
下面是一个使用Monitor/MWAIT指令的例子
dword[WorkQueue] = 0;
//设置monitor addr这个地址写入
MONITOR addr // Setup of eax with addr
//注意在调用MWAIT指令之前必须开中断
// LinearAddress, ECX, EDX = 0
//调用MWAIT这条指令监控addr地址写入,如果addr里的值是0,则循环等待addr被写入新的值
IF (dword[addr] == 0) THEN {
MWAIT
}
Virtualbox里模拟MWait和Monitor指令
当VMX设置了VMX_EXIT_MWAIT 和 VMX_EXIT_MONITOR, 当GuestOS执行到这两条指令的时候,会发生VMExit
hmR0VmxExitMonitor call IEMExecDecodedMonitor 最终掉到IEM_CIMPL_DEF_1(iemCImpl_monitor, uint8_t, iEffSeg)函数模拟执行这条指令
IEM_CIMPL_DEF_1(iemCImpl_monitor, uint8_t, iEffSeg)
{
//这条指令只运行在R0执行
if (pVCpu->iem.s.uCpl != 0)
{
return iemRaiseUndefinedOpcode(pVCpu);
}
//如果GuestCPU不支持Monitor/MWait指令,给GuestOS抛UD异常
if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fMonitorMWait)
{
return iemRaiseUndefinedOpcode(pVCpu);
}
/*
* Check VMX guest-intercept.
* This should be considered a fault-like VM-exit.
* See Intel spec. 25.1.1 "Relative Priority of Faults and VM Exits".
*/
if ( IEM_VMX_IS_NON_ROOT_MODE(pVCpu)
&& IEM_VMX_IS_PROCCTLS_SET(pVCpu, VMX_PROC_CTLS_MONITOR_EXIT))
{
IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_MONITOR, cbInstr);
}
//获取Monitor指令参数
RTGCPTR GCPtrMem = pVCpu->iem.s.enmCpuMode == IEMMODE_64BIT ? pVCpu->cpum.GstCtx.rax : pVCpu->cpum.GstCtx.eax;
uint32_t uEcx = pVCpu->cpum.GstCtx.ecx;
uint32_t uEdx = pVCpu->cpum.GstCtx.edx;
//ecx如果是0,则抛GP异常给GuestOS
if (uEcx != 0)
{
return iemRaiseGeneralProtectionFault0(pVCpu);
}
VBOXSTRICTRC rcStrict = iemMemApplySegment(pVCpu, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA, iEffSeg, 1, &GCPtrMem);
if (rcStrict != VINF_SUCCESS)
return rcStrict;
//校验输入的地址是否可访问,并获取对应的虚拟机物理地址
RTGCPHYS GCPhysMem;
rcStrict = iemMemPageTranslateAndCheckAccess(pVCpu, GCPtrMem, IEM_ACCESS_TYPE_READ | IEM_ACCESS_WHAT_DATA, &GCPhysMem);
if (rcStrict != VINF_SUCCESS)
return rcStrict;
//call EM
rcStrict = EMMonitorWaitPrepare(pVCpu, pVCpu->cpum.GstCtx.rax, pVCpu->cpum.GstCtx.rcx, pVCpu->cpum.GstCtx.rdx, GCPhysMem);
Assert(rcStrict == VINF_SUCCESS);
iemRegAddToRipAndClearRF(pVCpu, cbInstr);
return rcStrict;
}
VMM_INT_DECL(int) EMMonitorWaitPrepare(PVMCPU pVCpu, uint64_t rax, uint64_t rcx, uint64_t rdx, RTGCPHYS GCPhys)
{
//记录下3个寄存器,标记active monitor
pVCpu->em.s.MWait.uMonitorRAX = rax;
pVCpu->em.s.MWait.uMonitorRCX = rcx;
pVCpu->em.s.MWait.uMonitorRDX = rdx;
pVCpu->em.s.MWait.fWait |= EMMWAIT_FLAG_MONITOR_ACTIVE;
return VINF_SUCCESS;
}
MWait指令模拟
hmR0VmxExitMwait call IEMExecDecodedMwait最终调用到IEM_CIMPL_DEF_0(iemCImpl_mwait)函数模拟执行这条指令
IEM_CIMPL_DEF_0(iemCImpl_mwait)
{
//这条指令只运行在R0执行
if (pVCpu->iem.s.uCpl != 0)
{
return iemRaiseUndefinedOpcode(pVCpu);
}
//如果GuestCPU不支持Monitor/MWait指令,给GuestOS抛UD异常
if (!IEM_GET_GUEST_CPU_FEATURES(pVCpu)->fMonitorMWait)
{
return iemRaiseUndefinedOpcode(pVCpu);
}
//获取MWait的输入,并检查参数
uint32_t const uEax = pVCpu->cpum.GstCtx.eax;
uint32_t const uEcx = pVCpu->cpum.GstCtx.ecx;
if (uEcx != 0)
{
//uEcx不接受大于1的输入
if (uEcx > 1)
{
return iemRaiseGeneralProtectionFault0(pVCpu);
}
uint32_t fMWaitFeatures = 0;
uint32_t uIgnore = 0;
//如果CPUID(5)中没有设置X86_CPUID_MWAIT_ECX_EXT,不接受uEcx = 1
CPUMGetGuestCpuId(pVCpu, 5, 0, &uIgnore, &uIgnore, &fMWaitFeatures, &uIgnore);
if ( (fMWaitFeatures & (X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0))
!= (X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0))
{
return iemRaiseGeneralProtectionFault0(pVCpu);
}
}
//call EM
VBOXSTRICTRC rcStrict = EMMonitorWaitPerform(pVCpu, uEax, uEcx);
iemRegAddToRipAndClearRF(pVCpu, cbInstr);
return rcStrict;
}
VMM_INT_DECL(int) EMMonitorWaitPerform(PVMCPU pVCpu, uint64_t rax, uint64_t rcx)
{
//保存两个参数
pVCpu->em.s.MWait.uMWaitRAX = rax;
pVCpu->em.s.MWait.uMWaitRCX = rcx;
//标记MWAIT ACTIVE
pVCpu->em.s.MWait.fWait |= EMMWAIT_FLAG_ACTIVE;
if (rcx)
pVCpu->em.s.MWait.fWait |= EMMWAIT_FLAG_BREAKIRQIF0;
else
pVCpu->em.s.MWait.fWait &= ~EMMWAIT_FLAG_BREAKIRQIF0;
//返回需要VCPU halt,等待外部中断
return VINF_EM_HALT;
}
EM同时提供了一系列Monitor/MWait相关信息查询函数
EMMonitorIsArmed: 是否设置了monitor active
VMM_INT_DECL(bool) EMMonitorIsArmed(PVMCPU pVCpu)
{
return RT_BOOL(pVCpu->em.s.MWait.fWait & EMMWAIT_FLAG_MONITOR_ACTIVE);
}
EMMonitorWaitIsActive: 是否有调用了MWait指令等待
VMM_INT_DECL(unsigned) EMMonitorWaitIsActive(PVMCPU pVCpu)
{
uint32_t fWait = pVCpu->em.s.MWait.fWait;
return fWait & (EMMWAIT_FLAG_ACTIVE | ((fWait & EMMWAIT_FLAG_ACTIVE) << 1));
}
EMMonitorWaitClear:clear MWait状态,mwait退出
VMM_INT_DECL(void) EMMonitorWaitClear(PVMCPU pVCpu)
{
pVCpu->em.s.MWait.fWait &= ~(EMMWAIT_FLAG_ACTIVE | EMMWAIT_FLAG_BREAKIRQIF0);
}
EMShouldContinueAfterHalt: 执行完Hlt指令之后是否可以继续执行GuestOS代码
判断是有中断到来
VMM_INT_DECL(bool) EMShouldContinueAfterHalt(PVMCPU pVCpu, PCPUMCTX pCtx)
{
if (CPUMGetGuestGif(pCtx))
{
//GuestOS需要开启中断
if (CPUMIsGuestPhysIntrEnabled(pVCpu))
//返回是否有中断被设置上
return VMCPU_FF_IS_ANY_SET(pVCpu, (VMCPU_FF_UPDATE_APIC | VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC));
//嵌套VMX检查VMCPU_FF_INTERRUPT_NESTED_GUEST中断
if ( CPUMIsGuestInNestedHwvirtMode(pCtx)
&& CPUMIsGuestVirtIntrEnabled(pVCpu))
return VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NESTED_GUEST);
}
return false;
}
EMUnhaltAndWakeUp 唤醒一个VCPU
VMM_INT_DECL(int) EMUnhaltAndWakeUp(PVMCC pVM, PVMCPUCC pVCpuDst)
{
VMCPU_FF_SET(pVCpuDst, VMCPU_FF_UNHALT);
/*
* Wake up the EMT (technically should be abstracted by VMM/VMEmt, but
* just do it here for now).
*/
#ifdef IN_RING0
//如果是R0直接调用GVMMR0SchedWakeUpNoGVMNoLock函数唤醒一个VCPU
int rc = GVMMR0SchedWakeUpNoGVMNoLock(pVM, pVCpuDst->idCpu);
#elif defined(IN_RING3)
//如果是R3调用IOCTL到GVMM唤醒一个VCPU
int rc = SUPR3CallVMMR0(VMCC_GET_VMR0_FOR_CALL(pVM), pVCpuDst->idCpu, VMMR0_DO_GVMM_SCHED_WAKE_UP, NULL /* pvArg */);
#else
//其他状态直接返回success
int rc = VINF_SUCCESS;
#endif
return rc;
}