Virtualbox源码分析18 APIC虚拟化3 中断发送

18.1 利用APIC发送硬件中断到VCPU中

中断处理一般分成3个阶段:发送中断(保存中断信息到APIC里),获取中断(从APIC里获取一个最高优先级的中断),处理中断(设置VMCS里异常相关内容)。下面分别看看这3个类型的函数

Virtualbox在其他Manager发送中断到APIC的时候,会把中断向量先保存到APICCPU变量里的PIB内存里,APICUpdatePendingInterrupts把PIB里的中断送到APIC的IRR寄存器里排队,APICGetInterrupt从IRR里获取优先级最高的中断移动到ISR寄存器里,进入GuestOS执行中断,中断处理函数执行完毕之后,设置EOI寄存器(不是必须),然后从ISR里删除已完成的中断。

18.1.1 发送中断相关函数

apicSendIntr:
static VBOXSTRICTRC apicSendIntr(PVMCC pVM, PVMCPUCC pVCpu, uint8_t uVector, XAPICTRIGGERMODE enmTriggerMode,
                                 XAPICDELIVERYMODE enmDeliveryMode, PCVMCPUSET pDestCpuSet, bool *pfIntrAccepted,
                                 uint32_t uSrcTag, int rcRZ)
{
	switch (enmDeliveryMode)
  {
    case XAPICDELIVERYMODE_FIXED:
      //发送Fixed mode 中断到对应VCPU上,根据pDestCpuSet里的CPU设置,发送给目标CPU的
      {
        for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
          if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
          {
            PVMCPUCC pItVCpu = pVM->CTX_SUFF(apCpus)[idCpu];
            if (APICIsEnabled(pItVCpu))
              fAccepted = apicPostInterrupt(pItVCpu, uVector, enmTriggerMode, uSrcTag);
          }
        break;
      }
    case XAPICDELIVERYMODE_LOWEST_PRIO:
      //发送LOWEST_PRIO mode中断
      {
        //找pDestCpuSet里index最小的VCPU作为目标VCPU
        VMCPUID const idCpu = VMCPUSET_FIND_FIRST_PRESENT(pDestCpuSet);
        PVMCPUCC pVCpuDst = pVM->CTX_SUFF(apCpus)[idCpu];
        if (APICIsEnabled(pVCpuDst))
          fAccepted = apicPostInterrupt(pVCpuDst, uVector, enmTriggerMode, uSrcTag);
        break;
      }

    case XAPICDELIVERYMODE_SMI:
      //SMI中断,高优先级,直接发送到pDestCpuSet对应VCPU
      {
        for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
          if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
          {
            apicSetInterruptFF(pVM->CTX_SUFF(apCpus)[idCpu], PDMAPICIRQ_SMI);
            fAccepted = true;
          }
        break;
      }

    case XAPICDELIVERYMODE_NMI:
      //NMI中断,高优先级,直接发送到pDestCpuSet对应VCPU
      {
        for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
          if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
          {
            PVMCPUCC pItVCpu = pVM->CTX_SUFF(apCpus)[idCpu];
            if (APICIsEnabled(pItVCpu))
            {
              apicSetInterruptFF(pItVCpu, PDMAPICIRQ_NMI);
              fAccepted = true;
            }
          }
        break;
      }
   case XAPICDELIVERYMODE_INIT:
      {
        //ini IPI
#ifdef IN_RING3
        for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
          if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
          {
            VMMR3SendInitIpi(pVM, idCpu);
            fAccepted = true;
          }
#else
        rcStrict = rcRZ;
        fAccepted = true;
#endif
        break;
      }
    case XAPICDELIVERYMODE_STARTUP:
      {
        //startup IPI (SIPI)
#ifdef IN_RING3
        for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
          if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
          {
            VMMR3SendStartupIpi(pVM, idCpu, uVector);
            fAccepted = true;
          }
#else
        rcStrict = rcRZ;
        fAccepted = true;
#endif
        break;
      }
    case XAPICDELIVERYMODE_EXTINT:
      //外部中断,直接发生到对应VCPU
      {
        for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
          if (VMCPUSET_IS_PRESENT(pDestCpuSet, idCpu))
          {
            apicSetInterruptFF(pVM->CTX_SUFF(apCpus)[idCpu], PDMAPICIRQ_EXTINT);
            fAccepted = true;
          }
        break;
      }
}
apicSetInterruptFF:

设置中断标记并唤醒对应VCPU执行中断

static void apicSetInterruptFF(PVMCPUCC pVCpu, PDMAPICIRQ enmType)
{
	//根据传入的中断类型设置VCPU的全局变量
	switch (enmType)
  {
    case PDMAPICIRQ_HARDWARE:
       VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC);
   		 break;
    case PDMAPICIRQ_UPDATE_PENDING: VMCPU_FF_SET(pVCpu, VMCPU_FF_UPDATE_APIC);    break;
    case PDMAPICIRQ_NMI:            VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_NMI);  break;
    case PDMAPICIRQ_SMI:            VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_SMI);  break;
    case PDMAPICIRQ_EXTINT:         VMCPU_FF_SET(pVCpu, VMCPU_FF_INTERRUPT_PIC);  break;
    default:
    	AssertMsgFailed(("enmType=%d\n", enmType));
   		break;
  }
  //唤醒target VCPU的emulation thread继续执行代码
 #if defined(IN_RING0)
    PVMCC   pVM   = pVCpu->CTX_SUFF(pVM);
    VMCPUID idCpu = pVCpu->idCpu;
    if (   enmType != PDMAPICIRQ_HARDWARE
        && VMMGetCpuId(pVM) != idCpu)
    {
        switch (VMCPU_GET_STATE(pVCpu))
        {
            //VCPU已经在运行GuestOS代码,Poke
            case VMCPUSTATE_STARTED_EXEC:
                GVMMR0SchedPokeNoGVMNoLock(pVM, idCpu);
                break;
						//VCPU在唤醒状态,唤醒这个VCPU
            case VMCPUSTATE_STARTED_HALTED:
                GVMMR0SchedWakeUpNoGVMNoLock(pVM, idCpu);
                break;
            default:
                break; /* nothing to do in other states. */
        }
    }
#elif defined(IN_RING3)
    if (enmType != PDMAPICIRQ_HARDWARE)
        VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_DONE_REM | VMNOTIFYFF_FLAGS_POKE);
#endif
}
apicPostInterrupt

把一个中断vector保存在PIB里

bool apicPostInterrupt(PVMCPUCC pVCpu, uint8_t uVector, XAPICTRIGGERMODE enmTriggerMode, uint32_t uSrcTag)
{
	if (RT_LIKELY(uVector > XAPIC_ILLEGAL_VECTOR_END))
  {
    PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
    if (!apicTestVectorInReg(&pXApicPage->irr, uVector))    
    {
      //uSrcTag保存在auSrcTags里(调试用)
      if (!pApicCpu->auSrcTags[uVector])
        pApicCpu->auSrcTags[uVector]  = uSrcTag;
      else
        pApicCpu->auSrcTags[uVector] |= RT_BIT_32(31);

      //edge triggle mode
      if (enmTriggerMode == XAPICTRIGGERMODE_EDGE)
      {
        //VMCS里开启了VMX_PIN_CTLS_POSTED_INT
        if (pApic->fPostedIntrsEnabled)
        { //现在什么都没做,而且VirtualBox里没有开启VMX_PIN_CTLS_POSTED_INT 
        }
        else
        {
          //vector信息保存在pvApicPibRx内存里
          apicSetVectorInPib(pApicCpu->CTX_SUFF(pvApicPib), uVector);
          //设置有pending中断需要处理
          uint32_t const fAlreadySet = apicSetNotificationBitInPib((PAPICPIB)pApicCpu->CTX_SUFF(pvApicPib));
          if (!fAlreadySet)
          {
            apicSetInterruptFF(pVCpu, PDMAPICIRQ_UPDATE_PENDING);
          }
        }
      }
      else
      {
        //level triggle mode
        //vector信息保存在ApicPibLevel内存里
        apicSetVectorInPib(&pApicCpu->ApicPibLevel, uVector);
        //设置有pending中断需要处理
        uint32_t const fAlreadySet = apicSetNotificationBitInPib(&pApicCpu->ApicPibLevel);
        if (!fAlreadySet)
        {
          apicSetInterruptFF(pVCpu, PDMAPICIRQ_UPDATE_PENDING);
        }
      }
    }
  }
}
APICLocalInterrupt

通过Local APIC的LINT0/LINT1引脚发送中断

//u8Pin : 发送给LNT0还是LINT1
//u8Level: 0表示clear 1表示set
VMM_INT_DECL(VBOXSTRICTRC) APICLocalInterrupt(PVMCPUCC pVCpu, uint8_t u8Pin, uint8_t u8Level, int rcRZ)
{
  if (APICIsEnabled(pVCpu))
  {
    //根据u8Pin值获取对应的LVT表
    static const uint16_t s_au16LvtOffsets[] =
    {
      XAPIC_OFF_LVT_LINT0,
      XAPIC_OFF_LVT_LINT1
    };
    Assert(u8Pin < RT_ELEMENTS(s_au16LvtOffsets));
    uint16_t const offLvt = s_au16LvtOffsets[u8Pin];
    uint32_t const uLvt   = apicReadRaw32(pXApicPage, offLvt);
	
    //获取LVT里的参数
    XAPICDELIVERYMODE const enmDeliveryMode = XAPIC_LVT_GET_DELIVERY_MODE(uLvt);
    XAPICTRIGGERMODE        enmTriggerMode  = XAPIC_LVT_GET_TRIGGER_MODE(uLvt);
    switch (enmDeliveryMode)
    {
      case XAPICDELIVERYMODE_INIT:
        {
          //INIT只支持发IPI        
        }
        RT_FALL_THRU();
      case XAPICDELIVERYMODE_FIXED:
        {
          PAPICCPU       pApicCpu = VMCPU_TO_APICCPU(pVCpu);
          uint8_t const  uVector  = XAPIC_LVT_GET_VECTOR(uLvt);
          bool           fActive  = RT_BOOL(u8Level & 1);
          bool volatile *pfActiveLine = u8Pin == 0 ? &pApicCpu->fActiveLint0 : &pApicCpu->fActiveLint1;
            if (!fActive)
            {
              ASMAtomicCmpXchgBool(pfActiveLine, false, true);
              break;
            }

          //LINT1的中断不支持电平触发,修改成edge触发
          if (offLvt == XAPIC_OFF_LVT_LINT1)
            enmTriggerMode = XAPICTRIGGERMODE_EDGE;

          bool fSendIntr;
          if (enmTriggerMode == XAPICTRIGGERMODE_EDGE)
          {
            /* Recognize and send the interrupt only on an edge transition. */
            fSendIntr = ASMAtomicCmpXchgBool(pfActiveLine, true, false);
          }
          else
          {
            /* For level-triggered interrupts, redundant interrupts are not a problem. */
            ASMAtomicCmpXchgBool(pfActiveLine, true, false);

            /* Only when the remote IRR isn't set, set it and send the interrupt. */
            if (!(pXApicPage->lvt_lint0.all.u32LvtLint0 & XAPIC_LVT_REMOTE_IRR))
            {
              Assert(offLvt == XAPIC_OFF_LVT_LINT0);
              ASMAtomicOrU32((volatile uint32_t *)&pXApicPage->lvt_lint0.all.u32LvtLint0, XAPIC_LVT_REMOTE_IRR);
              fSendIntr = true;
            }
            else
              fSendIntr = false;
          }

          //发送中断
          if (fSendIntr)
          {
            VMCPUSET DestCpuSet;
            VMCPUSET_EMPTY(&DestCpuSet);
            VMCPUSET_ADD(&DestCpuSet, pVCpu->idCpu);
            rcStrict = apicSendIntr(pVCpu->CTX_SUFF(pVM), pVCpu, uVector, enmTriggerMode, enmDeliveryMode, &DestCpuSet, NULL /* pfIntrAccepted */, 0 /* uSrcTag */, rcRZ);
          }
          break;
        }
      case XAPICDELIVERYMODE_SMI:
      case XAPICDELIVERYMODE_NMI:
        {
          //SMI和NMI不可屏蔽中断,直接发送到目标VCPU
          VMCPUSET DestCpuSet;
          VMCPUSET_EMPTY(&DestCpuSet);
          VMCPUSET_ADD(&DestCpuSet, pVCpu->idCpu);
          uint8_t const uVector = XAPIC_LVT_GET_VECTOR(uLvt);
          rcStrict = apicSendIntr(pVCpu->CTX_SUFF(pVM), pVCpu, uVector, enmTriggerMode, enmDeliveryMode, &DestCpuSet,NULL /* pfIntrAccepted */, 0 /* uSrcTag */, rcRZ);
          break;
        }

      case XAPICDELIVERYMODE_EXTINT:
        {
          if (u8Level) //set
            apicSetInterruptFF(pVCpu, PDMAPICIRQ_EXTINT);
          else //clear
            apicClearInterruptFF(pVCpu, PDMAPICIRQ_EXTINT);
          break;
        }
   		//不接受的参数
      case XAPICDELIVERYMODE_LOWEST_PRIO:
      case XAPICDELIVERYMODE_STARTUP:
      default:
        {
          rcStrict = VERR_INTERNAL_ERROR_3;
          break;
        }
  }
  else
  {
    //APIC disable,说明该中断是从是从PIC设备发送来的
    if (u8Pin == 0)
    {
      //LINT0 当作外部中断处理
      if (u8Level)  //set
        apicSetInterruptFF(pVCpu, PDMAPICIRQ_EXTINT);
      else  //clear
        apicClearInterruptFF(pVCpu, PDMAPICIRQ_EXTINT);
    }
    else
    {
      //LINT1当时NMI来处理
      apicSetInterruptFF(pVCpu, PDMAPICIRQ_NMI);
    }
  }
}
APICBusDeliver

通过bus总线发送中断

VMM_INT_DECL(int) APICBusDeliver(PVMCC pVM, uint8_t uDest, uint8_t uDestMode, uint8_t uDeliveryMode, uint8_t uVector,
                                 uint8_t uPolarity, uint8_t uTriggerMode, uint32_t uSrcTag)
{
		XAPICTRIGGERMODE  enmTriggerMode  = (XAPICTRIGGERMODE)uTriggerMode;
    XAPICDELIVERYMODE enmDeliveryMode = (XAPICDELIVERYMODE)uDeliveryMode;
    XAPICDESTMODE     enmDestMode     = (XAPICDESTMODE)uDestMode;
    uint32_t          fDestMask       = uDest;
    uint32_t          fBroadcastMa sk  = UINT32_C(0xff);

    bool     fIntrAccepted;
    VMCPUSET DestCpuSet;
    apicGetDestCpuSet(pVM, fDestMask, fBroadcastMask, enmDestMode, enmDeliveryMode, &DestCpuSet);
    //发送中断到指定VCPU
    VBOXSTRICTRC rcStrict = apicSendIntr(pVM, NULL /* pVCpu */, uVector, enmTriggerMode, enmDeliveryMode, &DestCpuSet,
                                         &fIntrAccepted, uSrcTag, VINF_SUCCESS /* rcRZ */);
    if (fIntrAccepted)
        return VBOXSTRICTRC_VAL(rcStrict);
    return VERR_APIC_INTR_DISCARDED;
}

18.1.2 获取中断

APICGetInterrupt

获取pending队列里最高优先级的中断

VMM_INT_DECL(int) APICGetInterrupt(PVMCPUCC pVCpu, uint8_t *pu8Vector, uint32_t *puSrcTag)
{
	PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
  bool const fApicHwEnabled = APICIsEnabled(pVCpu);
  //如果开启了软件和硬件中断
  if (   fApicHwEnabled
      && pXApicPage->svr.u.fApicSoftwareEnable)
  {
    //获取IRR队列里最高优先级的中断
    int const irrv = apicGetHighestSetBitInReg(&pXApicPage->irr, -1);
    uint8_t const uVector = irrv;
    //如果被选择的中断优先级小于等于当前VCPU设置的中断级,中断被屏蔽,返回错误
    uint8_t const uTpr = pXApicPage->tpr.u8Tpr;
    if (   uTpr > 0
        && XAPIC_TPR_GET_TP(uVector) <= XAPIC_TPR_GET_TP(uTpr))
    {
      *pu8Vector = uVector;
      *puSrcTag  = 0;
      return VERR_APIC_INTR_MASKED_BY_TPR;
    }
    //打断当前正在运行的任务
    uint8_t const uPpr = pXApicPage->ppr.u8Ppr;
    if (   !uPpr
        || XAPIC_PPR_GET_PP(uVector) > XAPIC_PPR_GET_PP(uPpr))
    {
      //被选中CPU调度到ISR中
      //从IRR里清除
      apicClearVectorInReg(&pXApicPage->irr, uVector);
      //加入ISR
      apicSetVectorInReg(&pXApicPage->isr, uVector);
      //更新Ppr
      apicUpdatePpr(pVCpu);
      //如果IRR还有中断没有处理,设置Guest VCPU状态有中断已经准备好运行
      apicSignalNextPendingIntr(pVCpu);

      /* Retrieve the interrupt source tag associated with this interrupt. */
      PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
      *puSrcTag = pApicCpu->auSrcTags[uVector];
      pApicCpu->auSrcTags[uVector] = 0;

      *pu8Vector = uVector;
      return VINF_SUCCESS;
    }
  }
}

18.1.3 处理中断

APICUpdatePendingInterrupts

把中断从PIB里更新到IRR里

VMMDECL(void) APICUpdatePendingInterrupts(PVMCPUCC pVCpu)
{
	PAPICCPU   pApicCpu         = VMCPU_TO_APICCPU(pVCpu);
  PXAPICPAGE pXApicPage       = VMCPU_TO_XAPICPAGE(pVCpu);
	
  //是否有edge-triggered pending interrupts
  //获取edge-triggered中断到PIB
  PAPICPIB pPib = (PAPICPIB)pApicCpu->CTX_SUFF(pvApicPib);
  for (;;)
  {
    //clear  PIB notification bit,表示已经处理过了
    uint32_t const fAlreadySet = apicClearNotificationBitInPib((PAPICPIB)pApicCpu->CTX_SUFF(pvApicPib));
    if (!fAlreadySet)
      break;

   	//遍历PIB里的每一项
    for (size_t idxPib = 0, idxReg = 0; idxPib < RT_ELEMENTS(pPib->au64VectorBitmap); idxPib++, idxReg += 2)
    {
      //读取vector
      uint64_t const u64Fragment = ASMAtomicXchgU64(&pPib->au64VectorBitmap[idxPib], 0);
      if (u64Fragment)
      {
        uint32_t const u32FragmentLo = RT_LO_U32(u64Fragment);
        uint32_t const u32FragmentHi = RT_HI_U32(u64Fragment);
				//赋值到IRR对应的项
        pXApicPage->irr.u[idxReg].u32Reg     |=  u32FragmentLo;
        pXApicPage->irr.u[idxReg + 1].u32Reg |=  u32FragmentHi;
				//同时需要更新到tmr中
        pXApicPage->tmr.u[idxReg].u32Reg     &= ~u32FragmentLo;
        pXApicPage->tmr.u[idxReg + 1].u32Reg &= ~u32FragmentHi;
        fHasPendingIntrs = true;
      }
    }
  }
  //是否有level-triggered pending interrupts,ApicPibLevel里保存了level-triggered的PIB
  pPib = (PAPICPIB)&pApicCpu->ApicPibLevel;
  for (;;)
  {
    //clear  PIB notification bit,表示已经处理过了
    uint32_t const fAlreadySet = apicClearNotificationBitInPib((PAPICPIB)&pApicCpu->ApicPibLevel);
    if (!fAlreadySet)
      break;
    for (size_t idxPib = 0, idxReg = 0; idxPib < RT_ELEMENTS(pPib->au64VectorBitmap); idxPib++, idxReg += 2)
    {
      //读取vector
      uint64_t const u64Fragment = ASMAtomicXchgU64(&pPib->au64VectorBitmap[idxPib], 0);
      if (u64Fragment)
      {
        uint32_t const u32FragmentLo = RT_LO_U32(u64Fragment);
        uint32_t const u32FragmentHi = RT_HI_U32(u64Fragment);
				//写入irr和tmr对应项
        pXApicPage->irr.u[idxReg].u32Reg     |= u32FragmentLo;
        pXApicPage->irr.u[idxReg + 1].u32Reg |= u32FragmentHi;
        pXApicPage->tmr.u[idxReg].u32Reg     |= u32FragmentLo;
        pXApicPage->tmr.u[idxReg + 1].u32Reg |= u32FragmentHi;
        fHasPendingIntrs = true;
      }
    }
  }
  //如果发现有中断没有处理,选择一个最高优先级的中断,发送给VCPU处理
  if (   fHasPendingIntrs
      && !VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC))
    apicSignalNextPendingIntr(pVCpu);
}
hmR0VmxEvaluatePendingEvent

在进入GuestOS之前,查询是否有pending的event需要发送个GuestOS,如果有,则设置VMCS里相关项发送中断到GuestOS里

static VBOXSTRICTRC hmR0VmxEvaluatePendingEvent(PVMCPUCC pVCpu, PCVMXTRANSIENT pVmxTransient, uint32_t *pfIntrState)
{
	*pfIntrState = hmR0VmxGetGuestIntrStateAndUpdateFFs(pVCpu);
  PVMXVMCSINFO pVmcsInfo      = pVmxTransient->pVmcsInfo;
  bool const   fIsNestedGuest = pVmxTransient->fIsNestedGuest;
  if (   !pVCpu->hm.s.Event.fPending
      && !VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
  {
    //先查询高优先级的中断
    //NMI
    PCCPUMCTX pCtx = &pVCpu->cpum.GstCtx;
    if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NMI))
    {
      if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_BLOCK_NMIS))
      {
        //在VMCS里设置pending Nmi中断信息
        hmR0VmxSetPendingXcptNmi(pVCpu);
        //去掉NMI中断标记
        VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_NMI);
        return VINF_SUCCESS;
      }
      else if (!fIsNestedGuest)
        hmR0VmxSetNmiWindowExitVmcs(pVCpu, pVmcsInfo);
    }
    
    //PIC/APIC中断
    if (    VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC)
            && !pVCpu->hm.s.fSingleInstruction)
    {
      //获取rflags值
      int rc = hmR0VmxImportGuestState(pVCpu, pVmcsInfo, CPUMCTX_EXTRN_RFLAGS);
      //是否开启了中断
      if (pCtx->eflags.u32 & X86_EFL_IF)
      {
        //获取优先级最高的中断
        uint8_t u8Interrupt;
        rc = PDMGetInterrupt(pVCpu, &u8Interrupt);
        if (RT_SUCCESS(rc))
        {
          //设置VMCS项发送对应中断到GuestOS里
          hmR0VmxSetPendingExtInt(pVCpu, u8Interrupt);
        }
        else if (rc == VERR_APIC_INTR_MASKED_BY_TPR)
        {
          //中断被屏蔽了,设置TPR threshold
          if (   !fIsNestedGuest
              && (pVmcsInfo->u32ProcCtls & VMX_PROC_CTLS_USE_TPR_SHADOW))
            hmR0VmxApicSetTprThreshold(pVmcsInfo, u8Interrupt >> 4);
        }
      }
    }
    else if (!fIsNestedGuest)
  		//发送interrupt-window中断,让GuestOS立刻触发一个收到中断的vmexit
      hmR0VmxSetIntWindowExitVmcs(pVCpu, pVmcsInfo);
  } 
  else if (!fIsNestedGuest)
  {
    if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NMI))
      //发送NMI-window中断,让GuestOS立刻触发一个收到nmi中断的vmexit
      hmR0VmxSetNmiWindowExitVmcs(pVCpu, pVmcsInfo);
    else if (   VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC)
             && !pVCpu->hm.s.fSingleInstruction)
      //fSingleInstruction = false表示是在连续模拟执行多条指令中间发生异常
      hmR0VmxSetIntWindowExitVmcs(pVCpu, pVmcsInfo);
  }
  
}
//设置VMCS里的 TPR threshold,当当前TPR大于 TPR threhold的时候,会触发VMExit
DECLINLINE(void) hmR0VmxApicSetTprThreshold(PVMXVMCSINFO pVmcsInfo, uint32_t u32TprThreshold)
{
    int rc = VMXWriteVmcs32(VMX_VMCS32_CTRL_TPR_THRESHOLD, u32TprThreshold);
}

18.2 IPI中断相关函数

vmmR3SendInitIpi

CPU加电之后会发送 Startup IPI中断,处理函数是vmmR3SendInitIpi

static DECLCALLBACK(int) vmmR3SendInitIpi(PVM pVM, VMCPUID idCpu)
{
  //CPU被重置,调用所有Manager里的resetcpu函数
  PGMR3ResetCpu(pVM, pVCpu);
  PDMR3ResetCpu(pVCpu);   
  APICR3InitIpi(pVCpu);
  TRPMR3ResetCpu(pVCpu);
  CPUMR3ResetCpu(pVM, pVCpu);
  EMR3ResetCpu(pVCpu);
  HMR3ResetCpu(pVCpu);
  NEMR3ResetCpu(pVCpu, true /*fInitIpi*/);
}

APICR3InitIpi

初始化所有APIC寄存器

void apicInitIpi(PVMCPUCC pVCpu)
{
	//所有APIC寄存器全部设置成初始状态
  RT_ZERO(pXApicPage->irr);
  RT_ZERO(pXApicPage->irr);
  RT_ZERO(pXApicPage->isr);
  ...
  //lvt清空
	RT_ZERO(pXApicPage->lvt_timer);
  //u1Mask = 1表示屏蔽中断
  pXApicPage->lvt_timer.u.u1Mask = 1;
  RT_ZERO(pXApicPage->lvt_perf);
 	pXApicPage->lvt_perf.u.u1Mask = 1;
  ..
  //reset self-ipi寄存器
  RT_ZERO(pX2ApicPage->self_ipi);
  //clear pib
  RT_BZERO(&pApicCpu->ApicPibLevel, sizeof(APICPIB));
  RT_BZERO(pApicCpu->CTX_SUFF(pvApicPib), sizeof(APICPIB));
  pApicCpu->fActiveLint0 = false;
  pApicCpu->fActiveLint1 = false;
}

apicSendIpi

通过Interrupt Command Register (ICR).向其他核发送IPI中断

DECLINLINE(VBOXSTRICTRC) apicSendIpi(PVMCPUCC pVCpu, int rcRZ)
{
  //获取ICR里的异常信息 (具体信息可以参考16.2节中的LVT中断向量表的描述)
  XAPICDELIVERYMODE const  enmDeliveryMode  = (XAPICDELIVERYMODE)pXApicPage->icr_lo.u.u3DeliveryMode;
  XAPICDESTMODE const      enmDestMode      = (XAPICDESTMODE)pXApicPage->icr_lo.u.u1DestMode;
  XAPICINITLEVEL const     enmInitLevel     = (XAPICINITLEVEL)pXApicPage->icr_lo.u.u1Level;
  XAPICTRIGGERMODE const   enmTriggerMode   = (XAPICTRIGGERMODE)pXApicPage->icr_lo.u.u1TriggerMode;
  XAPICDESTSHORTHAND const enmDestShorthand = (XAPICDESTSHORTHAND)pXApicPage->icr_lo.u.u2DestShorthand;
  //中断向量表
  uint8_t const            uVector          = pXApicPage->icr_lo.u.u8Vector;
  
  //根据enmDestShorthand,决定发送给哪些VCPU
  VMCPUSET DestCpuSet;
  switch (enmDestShorthand)
  {
    case XAPICDESTSHORTHAND_NONE:
      {
        PVMCC pVM = pVCpu->CTX_SUFF(pVM);
        uint32_t const fBroadcastMask = XAPIC_IN_X2APIC_MODE(pVCpu) ? X2APIC_ID_BROADCAST_MASK : XAPIC_ID_BROADCAST_MASK;
        //使用enmDestMode和fDest决定发送给哪些CPU
        apicGetDestCpuSet(pVM, fDest, fBroadcastMask, enmDestMode, enmDeliveryMode, &DestCpuSet);
        break;
      }
    case XAPICDESTSHORTHAND_SELF:
      {
        //只发送给当前VCPU
        VMCPUSET_EMPTY(&DestCpuSet);
        VMCPUSET_ADD(&DestCpuSet, pVCpu->idCpu);
        break;
      }
    case XAPIDDESTSHORTHAND_ALL_INCL_SELF:
      {
        //发送给所有VCPU包括自己
        VMCPUSET_FILL(&DestCpuSet);
        break;
      }
    case XAPICDESTSHORTHAND_ALL_EXCL_SELF:
      {
        //发送给所有VCPU不包括自己
        VMCPUSET_FILL(&DestCpuSet);
        VMCPUSET_DEL(&DestCpuSet, pVCpu->idCpu);
        break;
      }
  }
  //发送中断到目标VCPU
  return apicSendIntr(pVCpu->CTX_SUFF(pVM), pVCpu, uVector, enmTriggerMode, enmDeliveryMode, &DestCpuSet,
                        NULL /* pfIntrAccepted */, 0 /* uSrcTag */, rcRZ);
}

apicGetDestCpuSet:

决定IPI发送给哪些VCPU

static void apicGetDestCpuSet(PVMCC pVM, uint32_t fDestMask, uint32_t fBroadcastMask, XAPICDESTMODE enmDestMode,
                              XAPICDELIVERYMODE enmDeliveryMode, PVMCPUSET pDestCpuSet)
{
  //Physical Destination Mode 不支持最低优先级传输模式启动的中断
	if (   enmDestMode == XAPICDESTMODE_PHYSICAL
        && enmDeliveryMode == XAPICDELIVERYMODE_LOWEST_PRIO)
  {
    //修改传送模式成Fixed模式
    enmDeliveryMode = XAPICDELIVERYMODE_FIXED;
  }	
  //最低优先级传送模式
  if (enmDeliveryMode == XAPICDELIVERYMODE_LOWEST_PRIO)
  {
    //必须是logical model
    Assert(enmDestMode == XAPICDESTMODE_LOGICAL);
    VMCPUID idCpuLowestTpr = NIL_VMCPUID;
    uint8_t u8LowestTpr    = UINT8_C(0xff);
    //遍历每个VCPU,找到优先级(TPR)最低的那个VCPU
    for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
    {
      PVMCPUCC pVCpuDst = pVM->CTX_SUFF(apCpus)[idCpu];
      if (apicIsLogicalDest(pVCpuDst, fDestMask))
      {
        PCXAPICPAGE   pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpuDst);
        //获取当前VCPU的APIC Page 的TPR(当前优先级)
        uint8_t const u8Tpr      = pXApicPage->tpr.u8Tpr;        
        if (u8Tpr <= u8LowestTpr)
        {
          u8LowestTpr    = u8Tpr;
          idCpuLowestTpr = idCpu;
        }
      }
    }
    //发送给最低优先级的那个VCPU
    if (idCpuLowestTpr != NIL_VMCPUID)
      VMCPUSET_ADD(pDestCpuSet, idCpuLowestTpr);
    return;
  }
  
  //广播
  if ((fDestMask & fBroadcastMask) == fBroadcastMask)
  {
    VMCPUSET_FILL(pDestCpuSet);
    return;
  }
  
  if (enmDestMode == XAPICDESTMODE_PHYSICAL)
  {
    //Physical Destination Mode 根据DestMask设置对应到目录CPU
    if (RT_LIKELY(fDestMask < cCpus))
      VMCPUSET_ADD(pDestCpuSet, fDestMask);
  }
  else
  {
    //Logical Destination Mode需要根据LDR和DFR寄存器决定发送给哪些CPU
    for (VMCPUID idCpu = 0; idCpu < cCpus; idCpu++)
    {
      PVMCPUCC pVCpuDst = pVM->CTX_SUFF(apCpus)[idCpu];
      if (apicIsLogicalDest(pVCpuDst, fDestMask))
        VMCPUSET_ADD(pDestCpuSet, pVCpuDst->idCpu);
    }
  }
}
static bool apicIsLogicalDest(PVMCPUCC pVCpu, uint32_t fDest)
{
  if (XAPIC_IN_X2APIC_MODE(pVCpu))
  {
    //x2APIC模式不支持Flat logical mode
    //clustered logical mode 
    //根据fDest,LDR寄存器的低16位值确定目标CPU, LDR的高16位是clusterID,必须和fDest里的clusterID相同
    PCX2APICPAGE pX2ApicPage = VMCPU_TO_CX2APICPAGE(pVCpu);
    uint32_t const u32Ldr    = pX2ApicPage->ldr.u32LogicalApicId;
    if (X2APIC_LDR_GET_CLUSTER_ID(u32Ldr) == (fDest & X2APIC_LDR_CLUSTER_ID))
      return RT_BOOL(u32Ldr & fDest & X2APIC_LDR_LOGICAL_ID);
    return false;
  }
  //xAPIC模式
  //广播,全返回true
  if ((fDest & XAPIC_LDR_FLAT_LOGICAL_ID) == XAPIC_LDR_FLAT_LOGICAL_ID)
    return true;

  PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
 	//从DFR寄存器里获取是flat还是cluster model
  XAPICDESTFORMAT enmDestFormat = (XAPICDESTFORMAT)pXApicPage->dfr.u.u4Model;
  if (enmDestFormat == XAPICDESTFORMAT_FLAT)
  {
    //根据fDest,LDR寄存器的24到31位值确定目标CPU
    uint8_t const u8Ldr = pXApicPage->ldr.u.u8LogicalApicId;
    return RT_BOOL(u8Ldr & fDest & XAPIC_LDR_FLAT_LOGICAL_ID);
  }
  //cluster model
  uint8_t const u8Ldr = pXApicPage->ldr.u.u8LogicalApicId;
	//根据fDest,LDR寄存器的28到21位值确定目标CPU, LDR的24到27位是clusterID,必须和fDest里的clusterID相同
  if (XAPIC_LDR_CLUSTERED_GET_CLUSTER_ID(u8Ldr) == (fDest & XAPIC_LDR_CLUSTERED_CLUSTER_ID))
    return RT_BOOL(u8Ldr & fDest & XAPIC_LDR_CLUSTERED_LOGICAL_ID);
  return false;
}

参考资料:

https://blog.csdn.net/omnispace/article/details/61415994
Intel指令手册

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值