编程语言
首页 > 编程语言> > Virtualbox源码分析17 APIC虚拟化2.APIC设备模拟md

Virtualbox源码分析17 APIC虚拟化2.APIC设备模拟md

作者:互联网

17.1 APIC设备模拟

VirtualBox里,把APIC作为一个R0的PNP设备来模拟:

const PDMDEVREG g_DeviceAPIC =
{
	 /* .u32Version = */             PDM_DEVREG_VERSION,
    /* .uReserved0 = */             0,
    /* .szName = */                 "apic",
    /* .fFlags = */                 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE
                                    | PDM_DEVREG_FLAGS_REQUIRE_R0 | PDM_DEVREG_FLAGS_REQUIRE_RC,
    /* .fClass = */                 PDM_DEVREG_CLASS_PIC,
    /* .cMaxInstances = */          1,
    /* .uSharedVersion = */         42,
    /* .cbInstanceShared = */       sizeof(APICDEV),
    /* .cbInstanceCC = */           0,
    /* .cbInstanceRC = */           0,
    /* .cMaxPciDevices = */         0,
    /* .cMaxMsixVectors = */        0,
    /* .pszDescription = */         "Advanced Programmable Interrupt Controller",
#if defined(IN_RING3)
    /* .szRCMod = */                "VMMRC.rc",
    /* .szR0Mod = */                "VMMR0.r0",
    /* .pfnConstruct = */           apicR3Construct,
    /* .pfnDestruct = */            apicR3Destruct,
    /* .pfnRelocate = */            apicR3Relocate,
    /* .pfnMemSetup = */            NULL,
    /* .pfnPowerOn = */             NULL,
    /* .pfnReset = */               apicR3Reset,
    /* .pfnSuspend = */             NULL,
    /* .pfnResume = */              NULL,
    /* .pfnAttach = */              NULL,
    /* .pfnDetach = */              NULL,
    /* .pfnQueryInterface = */      NULL,
    /* .pfnInitComplete = */        apicR3InitComplete,
    /* .pfnPowerOff = */            NULL,
    ....
#elif defined(IN_RING0)
    /* .pfnEarlyConstruct = */      NULL,
    /* .pfnConstruct = */           apicRZConstruct,
    /* .pfnDestruct = */            NULL,
    /* .pfnFinalDestruct = */       NULL,
    /* .pfnRequest = */             NULL,
    ...
#elif defined(IN_RC)
    /* .pfnConstruct = */           apicRZConstruct,
    ...
#else
# error "Not in IN_RING3, IN_RING0 or IN_RC!"
#endif
    /* .u32VersionEnd = */          PDM_DEVREG_VERSION
};

并且在PDMR0Init初始化的时候加入到g_PDMDevModList里,PDMR0DeviceCreateReqHandler函数会遍历g_PDMDevModList,create APIC devices。(虚拟机设备的创建过程在PDM一章里说明)

static const PDMDEVREGR0 *g_apVMM0DevRegs[] =
{
    &g_DeviceAPIC,
};

/**
 * Module device registration record for VMMR0.
 */
static PDMDEVMODREGR0 g_VBoxDDR0ModDevReg =
{
    /* .u32Version = */ PDM_DEVMODREGR0_VERSION,
    /* .cDevRegs = */   RT_ELEMENTS(g_apVMM0DevRegs),
    /* .papDevRegs = */ &g_apVMM0DevRegs[0],
    /* .hMod = */       NULL,
    /* .ListEntry = */  { NULL, NULL },
};

VMMR0_INT_DECL(void) PDMR0Init(void *hMod)
{
    RTListInit(&g_PDMDevModList);
    g_VBoxDDR0ModDevReg.hMod = hMod;
    RTListAppend(&g_PDMDevModList, &g_VBoxDDR0ModDevReg.ListEntry);
}

实现代码在VMM\VMMAlll\APICAll.cpp,VMM\VMMR3\APIC.cpp中

apicR3Construct

R3的初始化函数

DECLCALLBACK(int) apicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
	//读取配置,是否支持IOAPIC
	int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IOAPIC", &pApic->fIoApicPresent, true);
  //获取MAX APIC模式
  uint8_t uMaxMode;
  rc = pHlp->pfnCFGMQueryU8Def(pCfg, "Mode", &uMaxMode, PDMAPICMODE_APIC);
  switch ((PDMAPICMODE)uMaxMode)
  {
    case PDMAPICMODE_NONE:
    case PDMAPICMODE_APIC:
    case PDMAPICMODE_X2APIC:
      break;
    default:
      return VMR3SetError(pVM->pUVM, VERR_INVALID_PARAMETER, RT_SRC_POS, "APIC mode %d unknown.", uMaxMode);
  }
  pApic->enmMaxMode = (PDMAPICMODE)uMaxMode;
  
  //向PNP设备管理器里注册APIC设备
  rc = PDMDevHlpApicRegister(pDevIns);
  //如果支持x2APIC,加入x2APIC对应的MSR寄存器到MSRRange数组里
  if (pApic->enmMaxMode == PDMAPICMODE_X2APIC)
  {
    rc = CPUMR3MsrRangesInsert(pVM, &g_MsrRange_x2Apic);
    AssertLogRelRCReturn(rc, rc);
  }
  else
  {
   	//不支持x2APIC,加入一个会产生GP的handle
    rc = CPUMR3MsrRangesInsert(pVM, &g_MsrRange_x2Apic_Invalid);
    AssertLogRelRCReturn(rc, rc);
  }
  apicR3SetCpuIdFeatureLevel(pVM, pApic->enmMaxMode);
  //初始化APIC的相关数据
  rc = apicR3InitState(pVM);

  //注册MMIO范围,GCPhysApicBase开头的XAPICPAGE结构体大小都是MMIO地址
  //APIC内存的读写调用到apicWriteMmio/apicReadMmio这两个函数
  PAPICCPU pApicCpu0 = VMCPU_TO_APICCPU(pVM->apCpusR3[0]);
  RTGCPHYS GCPhysApicBase = MSR_IA32_APICBASE_GET_ADDR(pApicCpu0->uApicBaseMsr);
  rc = PDMDevHlpMmioCreateAndMap(pDevIns, GCPhysApicBase, sizeof(XAPICPAGE), apicWriteMmio, apicReadMmio,
                                 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED, "APIC", &pApicDev->hMmio);
  //给每个VCPU 创建APIC timer 
  for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
  {
    PVMCPU   pVCpu    = pVM->apCpusR3[idCpu];
    PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
    rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, apicR3TimerCallback, pVCpu, TMTIMER_FLAGS_NO_CRIT_SECT,
                              pApicCpu->szTimerDesc, &pApicCpu->hTimer);
  }
  //注册SSM的callback
  rc = PDMDevHlpSSMRegister(pDevIns, APIC_SAVED_STATE_VERSION, sizeof(*pApicDev), apicR3SaveExec, apicR3LoadExec);
}

apicR3InitState

//APIC Pending-Interrupt Bitmap (PIB). 里面保存了所有pending的中断
//pending的中断有两种类型,一种是edge trigglemode,一种是level trigglemode
typedef struct APICPIB
{
  uint64_t volatile au64VectorBitmap[4];
  uint32_t volatile fOutstandingNotification;
  uint8_t           au8Reserved[APIC_CACHE_LINE_SIZE - sizeof(uint32_t) - (sizeof(uint64_t) * 4)];
} APICPIB;
static int apicR3InitState(PVM pVM)
{
  //分配保存edge trigglemode用的内存,这个内存同时映射到R0和R3
  //计算需要多少个页面,每个VCPU都有自己的PIB
	pApic->cbApicPib    = RT_ALIGN_Z(pVM->cCpus * sizeof(APICPIB), PAGE_SIZE);
  size_t const cPages = pApic->cbApicPib >> PAGE_SHIFT;
  if (cPages == 1)
  {
    SUPPAGE SupApicPib;
    RT_ZERO(SupApicPib);
    SupApicPib.Phys = NIL_RTHCPHYS;
    //分配1个page大小的页面
    int rc = SUPR3PageAllocEx(1 /* cPages */, 0 /* fFlags */, &pApic->pvApicPibR3, &pApic->pvApicPibR0, &SupApicPib);
    if (RT_SUCCESS(rc))
    {
      pApic->HCPhysApicPib = SupApicPib.Phys;
    }
  }
  else
    //分配物理地址连续的cPage大小页面
  	pApic->pvApicPibR3 = SUPR3ContAlloc(cPages, &pApic->pvApicPibR0, &pApic->HCPhysApicPib);
	if (pApic->pvApicPibR3)
  {
    RT_BZERO(pApic->pvApicPibR3, pApic->cbApicPib);
    for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
    {
      //给每个VCPU分配申请一个Virtual APIC page
      SUPPAGE SupApicPage;
      RT_ZERO(SupApicPage);
      SupApicPage.Phys = NIL_RTHCPHYS;
      pApicCpu->cbApicPage = sizeof(XAPICPAGE);
      int rc = SUPR3PageAllocEx(1 /* cPages */, 0 /* fFlags */, &pApicCpu->pvApicPageR3, &pApicCpu->pvApicPageR0, &SupApicPage);
      if (RT_SUCCESS(rc))
      {
        pApicCpu->HCPhysApicPage = SupApicPage.Phys;
        //根据CPUID获取自己的PIB内存地址
        uint32_t const offApicPib  = idCpu * sizeof(APICPIB);
        pApicCpu->pvApicPibR0      = (RTR0PTR)((RTR0UINTPTR)pApic->pvApicPibR0 + offApicPib);
        pApicCpu->pvApicPibR3      = (RTR3PTR)((RTR3UINTPTR)pApic->pvApicPibR3 + offApicPib);
        //初始化APIC
        RT_BZERO(pApicCpu->pvApicPageR3, pApicCpu->cbApicPage);
        apicResetCpu(pVCpu, true /* fResetApicBaseMsr */);
      }
    }
  }
}

apicRZConstruct:

R0的设备初始化函数

static DECLCALLBACK(int) apicRZConstruct(PPDMDEVINS pDevIns)
{
    PAPICDEV pThis = PDMDEVINS_2_DATA(pDevIns, PAPICDEV);
    PVMCC    pVM   = PDMDevHlpGetVM(pDevIns);
    pVM->apicr0.s.pDevInsR0 = pDevIns;
    int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
  	//设置APIC设备
    rc = PDMDevHlpApicSetUpContext(pDevIns);
		//设置MMIO内存读写的callback函数
    rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, apicWriteMmio, apicReadMmio, NULL /*pvUser*/);
    return VINF_SUCCESS;
}

static DECLCALLBACK(int) pdmR0DevHlp_ApicSetUpContext(PPDMDEVINS pDevIns)
{
  pGVM->pdm.s.Apic.pDevInsR0 = pDevIns;
  return VINF_SUCCESS;
}

static DECLCALLBACK(int) pdmR0DevHlp_MmioSetUpContextEx(PPDMDEVINS pDevIns, IOMMMIOHANDLE hRegion, PFNIOMMMIONEWWRITE pfnWrite,
                                                        PFNIOMMMIONEWREAD pfnRead, PFNIOMMMIONEWFILL pfnFill, void *pvUser)
{
  PGVM pGVM = pDevIns->Internal.s.pGVM;
  //调用IOM里的函数设置mmio内存对应的read/write的函数
  int rc = IOMR0MmioSetUpContext(pGVM, pDevIns, hRegion, pfnWrite, pfnRead, pfnFill, pvUser);
  return rc;
}

apicR3Reset

R3的重置函数

DECLCALLBACK(void) apicR3Reset(PPDMDEVINS pDevIns)
{
	for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
  {
    PVMCPU   pVCpuDest = pVM->apCpusR3[idCpu];
    PAPICCPU pApicCpu  = VMCPU_TO_APICCPU(pVCpuDest);

    //如果当前CPU开启了APIC timer,先停掉timer
    if (PDMDevHlpTimerIsActive(pDevIns, pApicCpu->hTimer))
      PDMDevHlpTimerStop(pDevIns, pApicCpu->hTimer);
    apicResetCpu(pVCpuDest, true /* fResetApicBaseMsr */);
    //clear APIC相关的中断信息
    apicClearInterruptFF(pVCpuDest, PDMAPICIRQ_HARDWARE);
  }
}

//重新初始化每个APIC CPU
void apicResetCpu(PVMCPUCC pVCpu, bool fResetApicBaseMsr)
{
  //初始化Ipi
  apicInitIpi(pVCpu);
  PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
  pXApicPage->version.u.u8MaxLvtEntry = XAPIC_MAX_LVT_ENTRIES_P4 - 1;
  pXApicPage->version.u.u8Version     = XAPIC_HARDWARE_VERSION_P4;
  if (fResetApicBaseMsr)
    apicResetBaseMsr(pVCpu);
  pXApicPage->id.u8ApicId = pVCpu->idCpu;
}
//重置APICBase MSR的值,其实就是保存到ApicCPU的全局变量里
static void apicResetBaseMsr(PVMCPUCC pVCpu)
{
  PAPICCPU pApicCpu     = VMCPU_TO_APICCPU(pVCpu);
  PAPIC    pApic        = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
  //msrbase设置成默认值(fee00000)
  uint64_t uApicBaseMsr = MSR_IA32_APICBASE_ADDR;
  //0号CPU设置成启动的核: BSP: the bootstrap processor
  if (pVCpu->idCpu == 0)
    uApicBaseMsr |= MSR_IA32_APICBASE_BSP;
  //非APICMODE_NONE,表示开启了LAPIC
  if (pApic->enmMaxMode != PDMAPICMODE_NONE)
  {
    //设置APICBASE enable
    uApicBaseMsr |= MSR_IA32_APICBASE_EN;
    //设置CPUM
    CPUMSetGuestCpuIdPerCpuApicFeature(pVCpu, true /*fVisible*/);
  }
  //设置ApicBase到ApicCpu中
  ASMAtomicWriteU64(&pApicCpu->uApicBaseMsr, uApicBaseMsr);
}

apicR3Destruct

//释放掉apicR3InitState里申请的内存
static void apicR3TermState(PVM pVM)
{
	//释放PIB内存
  if (pApic->pvApicPibR3 != NIL_RTR3PTR)
  {
    size_t const cPages = pApic->cbApicPib >> PAGE_SHIFT;
    if (cPages == 1)
    	SUPR3PageFreeEx(pApic->pvApicPibR3, cPages);
    else
    	SUPR3ContFree(pApic->pvApicPibR3, cPages);
    pApic->pvApicPibR3 = NIL_RTR3PTR;
    pApic->pvApicPibR0 = NIL_RTR0PTR;
  }

  //释放Virtual APIC page
  for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
  {
    PVMCPU   pVCpu    = pVM->apCpusR3[idCpu];
    PAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
    pApicCpu->pvApicPibR3 = NIL_RTR3PTR;
    pApicCpu->pvApicPibR0 = NIL_RTR0PTR;
    if (pApicCpu->pvApicPageR3 != NIL_RTR3PTR)
    {
      SUPR3PageFreeEx(pApicCpu->pvApicPageR3, 1 /* cPages */);
      pApicCpu->pvApicPageR3 = NIL_RTR3PTR;
      pApicCpu->pvApicPageR0 = NIL_RTR0PTR;
    }
  }
}

17.2 设置APIC Base MSR

VMM_INT_DECL(int) APICSetBaseMsr(PVMCPUCC pVCpu, uint64_t u64BaseMsr)
{
  APICMODE enmOldMode = apicGetMode(pApicCpu->uApicBaseMsr);
  APICMODE enmNewMode = apicGetMode(u64BaseMsr);
  uint64_t uBaseMsr   = pApicCpu->uApicBaseMsr;
  
 //如果修改了APIC模式
 if (enmNewMode != enmOldMode)
 {
   switch (enmNewMode)
   {
       //关闭APIC
     case APICMODE_DISABLED:
       {
         //重置APIC CPU信息
         apicResetCpu(pVCpu, false /* fResetApicBaseMsr */);
         uBaseMsr &= ~(MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD);
         //通知CPUM APIC已被关闭
         CPUMSetGuestCpuIdPerCpuApicFeature(pVCpu, false /*fVisible*/);
         break;
       }
     //切换成xAPIC
     case APICMODE_XAPIC:
       {
         //只能从disable模式切换到xAPIC模式
         if (enmOldMode != APICMODE_DISABLED)
         {
           return apicMsrAccessError(pVCpu, MSR_IA32_APICBASE, APICMSRACCESS_WRITE_INVALID);
         }
         uBaseMsr |= MSR_IA32_APICBASE_EN;
         //设置开启APIC
         CPUMSetGuestCpuIdPerCpuApicFeature(pVCpu, true /*fVisible*/);
         break;
       }
     case APICMODE_X2APIC:
       {
         //配置不支持x2APIC,返回错误
         if (pApic->enmMaxMode != PDMAPICMODE_X2APIC)
         {
           return apicMsrAccessError(pVCpu, MSR_IA32_APICBASE, APICMSRACCESS_WRITE_INVALID);
         }
				 //只能从xAPIC模式切换到x2APIC模式
         if (enmOldMode != APICMODE_XAPIC)
         {
           return apicMsrAccessError(pVCpu, MSR_IA32_APICBASE, APICMSRACCESS_WRITE_INVALID);
         }

         uBaseMsr |= MSR_IA32_APICBASE_EN | MSR_IA32_APICBASE_EXTD;
         //u32ApicId设置成当前VCPUID,x2APIC不支持软件设置APIC ID
         PX2APICPAGE pX2ApicPage = VMCPU_TO_X2APICPAGE(pVCpu);
         ASMMemZero32(&pX2ApicPage->id, sizeof(pX2ApicPage->id));
         pX2ApicPage->id.u32ApicId = pVCpu->idCpu;

         //LDR initialization occurs when entering x2APIC mode.
         pX2ApicPage->ldr.u32LogicalApicId = ((pX2ApicPage->id.u32ApicId & UINT32_C(0xffff0)) << 16)
           | (UINT32_C(1) << pX2ApicPage->id.u32ApicId & UINT32_C(0xf));
         break;
       }
   }
 	}
}

17.3 APIC Page访问的相关函数

apicReadMmio

DECLCALLBACK(VBOXSTRICTRC) apicReadMmio(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
{
  VBOXSTRICTRC rc = VBOXSTRICTRC_VAL(apicReadRegister(pDevIns, pVCpu, offReg, &uValue));
}
//大部分APIC寄存器直接读取
DECLINLINE(VBOXSTRICTRC) apicReadRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t *puValue)
{
    PXAPICPAGE   pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
    uint32_t     uValue = 0;
    VBOXSTRICTRC rc = VINF_SUCCESS;
    switch (offReg)
    {
        case XAPIC_OFF_ID:
        case XAPIC_OFF_VERSION:
        case XAPIC_OFF_TPR:
        case XAPIC_OFF_EOI:
        case XAPIC_OFF_RRD:
        case XAPIC_OFF_LDR:
        case XAPIC_OFF_DFR:
        case XAPIC_OFF_SVR:
        case XAPIC_OFF_ISR0:    case XAPIC_OFF_ISR1:    case XAPIC_OFF_ISR2:    case XAPIC_OFF_ISR3:
        case XAPIC_OFF_ISR4:    case XAPIC_OFF_ISR5:    case XAPIC_OFF_ISR6:    case XAPIC_OFF_ISR7:
        case XAPIC_OFF_TMR0:    case XAPIC_OFF_TMR1:    case XAPIC_OFF_TMR2:    case XAPIC_OFF_TMR3:
        case XAPIC_OFF_TMR4:    case XAPIC_OFF_TMR5:    case XAPIC_OFF_TMR6:    case XAPIC_OFF_TMR7:
        case XAPIC_OFF_IRR0:    case XAPIC_OFF_IRR1:    case XAPIC_OFF_IRR2:    case XAPIC_OFF_IRR3:
        case XAPIC_OFF_IRR4:    case XAPIC_OFF_IRR5:    case XAPIC_OFF_IRR6:    case XAPIC_OFF_IRR7:
        case XAPIC_OFF_ESR:
        case XAPIC_OFF_ICR_LO:
        case XAPIC_OFF_ICR_HI:
        case XAPIC_OFF_LVT_TIMER:
#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
        case XAPIC_OFF_LVT_THERMAL:
#endif
        case XAPIC_OFF_LVT_PERF:
        case XAPIC_OFF_LVT_LINT0:
        case XAPIC_OFF_LVT_LINT1:
        case XAPIC_OFF_LVT_ERROR:
        case XAPIC_OFF_TIMER_ICR:
        case XAPIC_OFF_TIMER_DCR:
        {
          	//直接读取Virtual APIC page里的值
            uValue = apicReadRaw32(pXApicPage, offReg);
            break;
        }
        case XAPIC_OFF_PPR:
        {
          	//获取当前进程的优先级
            uValue = apicGetPpr(pVCpu);
            break;
        }
        case XAPIC_OFF_TIMER_CCR:
        {
          	//获取APIC时间计时器
            rc = apicGetTimerCcr(pDevIns, pVCpu, VINF_IOM_R3_MMIO_READ, &uValue);
            break;
        }
        case XAPIC_OFF_APR:
        {
#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
            /* Unsupported on Pentium 4 and Xeon CPUs, invalid in x2APIC mode. */
            Assert(!XAPIC_IN_X2APIC_MODE(pVCpu));
#else
# error "Implement Pentium and P6 family APIC architectures"
#endif
            break;
        }
        default:
        {
            //设置错误标记
            rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "VCPU[%u]: offReg=%#RX16\n", pVCpu->idCpu, offReg);
            apicSetError(pVCpu, XAPIC_ESR_ILLEGAL_REG_ADDRESS);
            break;
        }
    }
    *puValue = uValue;
    return rc;
}

apicWriteMmio

APIC寄存器的写操作就要复杂很多,很多项都需要特殊处理

DECLINLINE(VBOXSTRICTRC) apicWriteRegister(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, uint16_t offReg, uint32_t uValue)
{
    VMCPU_ASSERT_EMT(pVCpu);
    Assert(offReg <= XAPIC_OFF_MAX_VALID);
    Assert(!XAPIC_IN_X2APIC_MODE(pVCpu));

    VBOXSTRICTRC rcStrict = VINF_SUCCESS;
    switch (offReg)
    {
        case XAPIC_OFF_TPR:
        {
            rcStrict = apicSetTprEx(pVCpu, uValue, false /* fForceX2ApicBehaviour */);
            break;
        }
        case XAPIC_OFF_LVT_TIMER:
#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
        case XAPIC_OFF_LVT_THERMAL:
#endif
        case XAPIC_OFF_LVT_PERF:
        case XAPIC_OFF_LVT_LINT0:
        case XAPIC_OFF_LVT_LINT1:
        case XAPIC_OFF_LVT_ERROR:
        {
            rcStrict = apicSetLvtEntry(pVCpu, offReg, uValue);
            break;
        }
        case XAPIC_OFF_TIMER_ICR:
        {
            rcStrict = apicSetTimerIcr(pDevIns, pVCpu, VINF_IOM_R3_MMIO_WRITE, uValue);
            break;
        }
        case XAPIC_OFF_EOI:
        {
            rcStrict = apicSetEoi(pVCpu, uValue, VINF_IOM_R3_MMIO_WRITE, false /* fForceX2ApicBehaviour */);
            break;
        }
        case XAPIC_OFF_LDR:
        {
            rcStrict = apicSetLdr(pVCpu, uValue);
            break;
        }
        case XAPIC_OFF_DFR:
        {
            rcStrict = apicSetDfr(pVCpu, uValue);
            break;
        }
        case XAPIC_OFF_SVR:
        {
            rcStrict = apicSetSvr(pVCpu, uValue);
            break;
        }
        case XAPIC_OFF_ICR_LO:
        {
            rcStrict = apicSetIcrLo(pVCpu, uValue, VINF_IOM_R3_MMIO_WRITE, true /* fUpdateStat */);
            break;
        }
        case XAPIC_OFF_ICR_HI:
        {
            rcStrict = apicSetIcrHi(pVCpu, uValue);
            break;
        }
        case XAPIC_OFF_TIMER_DCR:
        {
            rcStrict = apicSetTimerDcr(pVCpu, uValue);
            break;
        }
        case XAPIC_OFF_ESR:
        {
            rcStrict = apicSetEsr(pVCpu, uValue);
            break;
        }
        case XAPIC_OFF_APR:
        case XAPIC_OFF_RRD:
        {
          //暂时的不支持这两个寄存器写入
#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
#else
# error "Implement Pentium and P6 family APIC architectures"
#endif
            break;
        }
        /* Read-only, write ignored: */
        case XAPIC_OFF_VERSION:
        case XAPIC_OFF_ID:
            break;
        /* Unavailable/reserved in xAPIC mode: */
        case X2APIC_OFF_SELF_IPI:
        /* Read-only registers: */
        case XAPIC_OFF_PPR:
        case XAPIC_OFF_ISR0:    case XAPIC_OFF_ISR1:    case XAPIC_OFF_ISR2:    case XAPIC_OFF_ISR3:
        case XAPIC_OFF_ISR4:    case XAPIC_OFF_ISR5:    case XAPIC_OFF_ISR6:    case XAPIC_OFF_ISR7:
        case XAPIC_OFF_TMR0:    case XAPIC_OFF_TMR1:    case XAPIC_OFF_TMR2:    case XAPIC_OFF_TMR3:
        case XAPIC_OFF_TMR4:    case XAPIC_OFF_TMR5:    case XAPIC_OFF_TMR6:    case XAPIC_OFF_TMR7:
        case XAPIC_OFF_IRR0:    case XAPIC_OFF_IRR1:    case XAPIC_OFF_IRR2:    case XAPIC_OFF_IRR3:
        case XAPIC_OFF_IRR4:    case XAPIC_OFF_IRR5:    case XAPIC_OFF_IRR6:    case XAPIC_OFF_IRR7:
        case XAPIC_OFF_TIMER_CCR:
        default:
        {
          	//只读的寄存器读取需要设置异常标记
            rcStrict = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "APIC%u: offReg=%#RX16\n", pVCpu->idCpu, offReg);
            apicSetError(pVCpu, XAPIC_ESR_ILLEGAL_REG_ADDRESS);
            break;
        }
    }
    return rcStrict;
}

下面一个一个看所有的set函数

apicSetTprEx

static int apicSetTprEx(PVMCPUCC pVCpu, uint32_t uTpr, bool fForceX2ApicBehaviour)
{
    bool const fX2ApicMode = XAPIC_IN_X2APIC_MODE(pVCpu) || fForceX2ApicBehaviour;
    if (   fX2ApicMode
        && (uTpr & ~XAPIC_TPR_VALID))
        return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_TPR, APICMSRACCESS_WRITE_RSVD_BITS);

    PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
 		//写入Tpr
    pXApicPage->tpr.u8Tpr = uTpr;
  	//更新ppr
    apicUpdatePpr(pVCpu);
  	//检查是否有pending的中断可以唤醒,发送中断到VCPU中
    apicSignalNextPendingIntr(pVCpu);
    return VINF_SUCCESS;
}
//设置了Tpr或者有有中断被唤醒的时候,都会调用这个函数更新Ppr
static void apicUpdatePpr(PVMCPUCC pVCpu)
{
  PXAPICPAGE    pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
  //获取当前正在处理的中断的最高优先级
  uint8_t const uIsrv      = apicGetHighestSetBitInReg(&pXApicPage->isr, 0 /* rcNotFound */);
  uint8_t       uPpr;
  //PPR赋值成TPR的值和ISR中最大优先级中较大的一个
  if (XAPIC_TPR_GET_TP(pXApicPage->tpr.u8Tpr) >= XAPIC_PPR_GET_PP(uIsrv))
    uPpr = pXApicPage->tpr.u8Tpr;
  else
    uPpr = XAPIC_PPR_GET_PP(uIsrv);
  pXApicPage->ppr.u8Ppr = uPpr;
}
//唤醒下一个高优先级中断
static void apicSignalNextPendingIntr(PVMCPUCC pVCpu)
{
    VMCPU_ASSERT_EMT_OR_NOT_RUNNING(pVCpu);

    PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
    if (pXApicPage->svr.u.fApicSoftwareEnable)
    {
      	//从irr里获取优先级最高的pending中断
        int const irrv = apicGetHighestSetBitInReg(&pXApicPage->irr, -1 /* rcNotFound */);
        if (irrv >= 0)
        {
            uint8_t const uVector = irrv;
            uint8_t const uPpr    = pXApicPage->ppr.u8Ppr;
          	//如果这个中断优先级大于当前CPU的中断优先级,设置标记位表示有中断可以被唤醒
            if (   !uPpr
                ||  XAPIC_PPR_GET_PP(uVector) > XAPIC_PPR_GET_PP(uPpr))
            {
                apicSetInterruptFF(pVCpu, PDMAPICIRQ_HARDWARE);
            }
        }
    }
}

apicSetLvtEntry

static VBOXSTRICTRC apicSetLvtEntry(PVMCPUCC pVCpu, uint16_t offLvt, uint32_t uLvt)
{
  PCAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
  if (offLvt == XAPIC_OFF_LVT_TIMER)
  {
    //如果是APIC Timer,不支持Tsc Deadline模式,但是Lvt里又设置了Tsc Deadline模式
    if (   !pApic->fSupportsTscDeadline
        && (uLvt & XAPIC_LVT_TIMER_TSCDEADLINE))
    {
      //x2APIC返回错误
      if (XAPIC_IN_X2APIC_MODE(pVCpu))
        return apicMsrAccessError(pVCpu, XAPIC_GET_X2APIC_MSR(offLvt), APICMSRACCESS_WRITE_RSVD_BITS);
      //xAPIC模式直接去掉这个bit
      uLvt &= ~XAPIC_LVT_TIMER_TSCDEADLINE;
    }
  }
  //获取7个LVT里的顺序
  uint16_t const idxLvt = (offLvt - XAPIC_OFF_LVT_START) >> 4;
  //检查输入的值是否正确,不同的LVT中断,LVT里有效位是不同的。具体有效位见上一篇的图3
  if (   XAPIC_IN_X2APIC_MODE(pVCpu)
      && (uLvt & ~g_au32LvtValidMasks[idxLvt]))
    return apicMsrAccessError(pVCpu, XAPIC_GET_X2APIC_MSR(offLvt), APICMSRACCESS_WRITE_RSVD_BITS);
  //获取LVT Mask
  uLvt &= g_au32LvtValidMasks[idxLvt];
  //如果没有开启SoftwareEnable(只运行硬件中断)需要设置上XAPIC_LVT_MASK
  PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
  if (!pXApicPage->svr.u.fApicSoftwareEnable)
       uLvt |= XAPIC_LVT_MASK;
  //检查通过,写入LVT寄存器
  apicWriteRaw32(pXApicPage, offLvt, uLvt);
  return VINF_SUCCESS;
}

apicSetIcrHi/apicSetIcrLo

ICR写入相当于发送一个IPI中断,先写入高位,后写入低位,写入低位的时候发送IPI中断

static VBOXSTRICTRC apicSetIcrHi(PVMCPUCC pVCpu, uint32_t uIcrHi)
{
  	//写入寄存器高位
    PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
    pXApicPage->icr_hi.all.u32IcrHi = uIcrHi & XAPIC_ICR_HI_DEST;
    return VINF_SUCCESS;
}
static VBOXSTRICTRC apicSetIcrLo(PVMCPUCC pVCpu, uint32_t uIcrLo, int rcRZ, bool fUpdateStat)
{
  	//写入寄存器低位
    PXAPICPAGE pXApicPage  = VMCPU_TO_XAPICPAGE(pVCpu);
    pXApicPage->icr_lo.all.u32IcrLo = uIcrLo & XAPIC_ICR_LO_WR_VALID;
  	//发送IPI中断
    return apicSendIpi(pVCpu, rcRZ);
}

apicSetSvr :

当处理器将其PPR(任务优先级)提高到大于或等于当前断言处理器INTR信号的中断级别时,可能会出现特殊情况。如果在发出INTA周期时,要分配的中断已被屏蔽(由软件编程),则本地APIC将传递一个虚假的中断向量。分配假中断向量不会影响ISR,因此该向量的处理程序应在没有EOI的情况下返回。

static int apicSetSvr(PVMCPUCC pVCpu, uint32_t uSvr)
{
	uint32_t   uValidMask = XAPIC_SVR_VALID;
  PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
  //如果开启了禁止EOI broadcast,加上XAPIC_SVR_SUPRESS_EOI_BROADCAST标记
  if (pXApicPage->version.u.fEoiBroadcastSupression)
    uValidMask |= XAPIC_SVR_SUPRESS_EOI_BROADCAST;
	//x2APIC模式不允许非有效位是1
  if (   XAPIC_IN_X2APIC_MODE(pVCpu)
      && (uSvr & ~uValidMask))
    return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_SVR, APICMSRACCESS_WRITE_RSVD_BITS);
	//直接写入SVR寄存器
  apicWriteRaw32(pXApicPage, XAPIC_OFF_SVR, uSvr);
  //如果关闭了软件中断,需要重置LVT里的mask
  if (!pXApicPage->svr.u.fApicSoftwareEnable)
  {
    pXApicPage->lvt_timer.u.u1Mask   = 1;
#if XAPIC_HARDWARE_VERSION == XAPIC_HARDWARE_VERSION_P4
    pXApicPage->lvt_thermal.u.u1Mask = 1;
#endif
    pXApicPage->lvt_perf.u.u1Mask    = 1;
    pXApicPage->lvt_lint0.u.u1Mask   = 1;
    pXApicPage->lvt_lint1.u.u1Mask   = 1;
    pXApicPage->lvt_error.u.u1Mask   = 1;
  }
}

apicSetEsr

static int apicSetEsr(PVMCPUCC pVCpu, uint32_t uEsr)
{
  	//x2APIC模式不允许非有效位是1
    if (   XAPIC_IN_X2APIC_MODE(pVCpu)
        && (uEsr & ~XAPIC_ESR_WO_VALID))
        return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_ESR, APICMSRACCESS_WRITE_RSVD_BITS);

    //这边只是清除所有内部错误,并没有真正设置ESR寄存器
    PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
    pXApicPage->esr.all.u32Errors = apicClearAllErrors(pVCpu);
    return VINF_SUCCESS;
}

apicSetTimerDcr

Timer Divide Configuration Register (DCR).

static VBOXSTRICTRC apicSetTimerDcr(PVMCPUCC pVCpu, uint32_t uTimerDcr)
{
  //如果是x2APIC模式,只接受有效位有值的输入
	if (   XAPIC_IN_X2APIC_MODE(pVCpu)
      && (uTimerDcr & ~XAPIC_TIMER_DCR_VALID))
    return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_TIMER_DCR, APICMSRACCESS_WRITE_RSVD_BITS);
 	//写入DCR值
  PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
  apicWriteRaw32(pXApicPage, XAPIC_OFF_TIMER_DCR, uTimerDcr);
  return VINF_SUCCESS;
}

apicSetTimerIcr

timer’s Initial-Count Register (ICR).

static VBOXSTRICTRC apicSetTimerIcr(PPDMDEVINS pDevIns, PVMCPUCC pVCpu, int rcBusy, uint32_t uInitialCount)
{
    PAPIC      pApic      = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
    PAPICCPU   pApicCpu   = VMCPU_TO_APICCPU(pVCpu);
    PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
    // TSC-deadline mode不使用ICR,忽略
    if (   pApic->fSupportsTscDeadline
        && pXApicPage->lvt_timer.u.u2TimerMode == XAPIC_TIMER_MODE_TSC_DEADLINE)
        return VINF_SUCCESS;

    TMTIMERHANDLE hTimer = pApicCpu->hTimer;
    VBOXSTRICTRC rc = PDMDevHlpTimerLockClock(pDevIns, hTimer, rcBusy);
    if (rc == VINF_SUCCESS)
    {
        pXApicPage->timer_icr.u32InitialCount = uInitialCount;
      	//设置CCR的值等于ICR
        pXApicPage->timer_ccr.u32CurrentCount = uInitialCount;
      	//如果ICR不等于0,启动Timer
        if (uInitialCount)
            apicStartTimer(pVCpu, uInitialCount);
        else
        //ICR等于0,停止Timer
            apicStopTimer(pVCpu);
        PDMDevHlpTimerUnlockClock(pDevIns, hTimer);
    }
    return rc;
}

apicSetEoi

中断完成设置

static VBOXSTRICTRC apicSetEoi(PVMCPUCC pVCpu, uint32_t uEoi, int rcBusy, bool fForceX2ApicBehaviour)
{
	bool const fX2ApicMode = XAPIC_IN_X2APIC_MODE(pVCpu) || fForceX2ApicBehaviour;
  if (   fX2ApicMode
      && (uEoi & ~XAPIC_EOI_WO_VALID))
    return apicMsrAccessError(pVCpu, MSR_IA32_X2APIC_EOI, APICMSRACCESS_WRITE_RSVD_BITS);
  int isrv = apicGetHighestSetBitInReg(&pXApicPage->isr, -1 /* rcNotFound */);
  //获取下一个需要处理的中断
  if (isrv >= 0)
  {
    uint8_t const uVector      = isrv;
    //是否是电平触发的中断
    bool const fLevelTriggered = apicTestVectorInReg(&pXApicPage->tmr, uVector);
    if (fLevelTriggered)
    {
      //广播EOI
      VBOXSTRICTRC rc = PDMIoApicBroadcastEoi(pVCpu->CTX_SUFF(pVM), uVector);
      if (rc == VINF_SUCCESS)
      { /* likely */ }
      else
        return rcBusy;
      //clear TMR寄存器 (中断完成)
      apicClearVectorInReg(&pXApicPage->tmr, uVector);
      //LINT0上的, 电平触发,fixedmode的中断需要在收到EOI命令之后清除Remote IRR标记
      uint32_t const uLvtLint0 = pXApicPage->lvt_lint0.all.u32LvtLint0;
      if (   XAPIC_LVT_GET_REMOTE_IRR(uLvtLint0)
          && XAPIC_LVT_GET_VECTOR(uLvtLint0) == uVector
          && XAPIC_LVT_GET_DELIVERY_MODE(uLvtLint0) == XAPICDELIVERYMODE_FIXED)
      {
        ASMAtomicAndU32((volatile uint32_t *)&pXApicPage->lvt_lint0.all.u32LvtLint0, ~XAPIC_LVT_REMOTE_IRR);
      }
    }
    //清除ISR寄存器(当前中断已处理完成)
    apicClearVectorInReg(&pXApicPage->isr, uVector);
    //更新Ppr
    apicUpdatePpr(pVCpu);
    //选择下一个要处理的中断
    apicSignalNextPendingIntr(pVCpu);
  }
}

apicSetLdr

设置Logical Destination Register (LDR).

static VBOXSTRICTRC apicSetLdr(PVMCPUCC pVCpu, uint32_t uLdr)
{
  	//直接写入Virtual APIC-page
    PCAPIC pApic = VM_TO_APIC(pVCpu->CTX_SUFF(pVM));
    PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
    apicWriteRaw32(pXApicPage, XAPIC_OFF_LDR, uLdr & XAPIC_LDR_VALID);
    return VINF_SUCCESS;
}

apicSetDfr

Destination Format Register (DFR).

static VBOXSTRICTRC apicSetDfr(PVMCPUCC pVCpu, uint32_t uDfr)
{
    uDfr &= XAPIC_DFR_VALID;
    uDfr |= XAPIC_DFR_RSVD_MB1;

    PXAPICPAGE pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
    apicWriteRaw32(pXApicPage, XAPIC_OFF_DFR, uDfr);
    return VINF_SUCCESS;
}

APICGetTpr

VMMDECL(int) APICGetTpr(PCVMCPUCC pVCpu, uint8_t *pu8Tpr, bool *pfPending, uint8_t *pu8PendingIntr)
{
    VMCPU_ASSERT_EMT(pVCpu);
    if (APICIsEnabled(pVCpu))
    {
        PCXAPICPAGE pXApicPage = VMCPU_TO_CXAPICPAGE(pVCpu);
        if (pfPending)
        {
            //获取IRR里pending中断中最高的一个优先级
            *pfPending = apicGetHighestPendingInterrupt(pVCpu, pu8PendingIntr);
        }
				//返回这个中断的TPR寄存器里的值
        *pu8Tpr = pXApicPage->tpr.u8Tpr;
        return VINF_SUCCESS;
    }

    *pu8Tpr = 0;
    return VERR_PDM_NO_APIC_INSTANCE;
}

17.4 APIC Timer相关的函数

当ICR写入时,会调用这些函数

apicStartTimer

void apicStartTimer(PVMCPUCC pVCpu, uint32_t uInitialCount)
{
	PCXAPICPAGE    pXApicPage   = APICCPU_TO_CXAPICPAGE(pApicCpu);
  uint8_t  const uTimerShift  = apicGetTimerShift(pXApicPage);
  uint64_t const cTicksToNext = (uint64_t)uInitialCount << uTimerShift;
  //最终调用到TM里的TMTimerSetRelative,这个函数在TM(Time Manager)里介绍
  PDMDevHlpTimerSetRelative(pDevIns, pApicCpu->hTimer, cTicksToNext, &pApicCpu->u64TimerInitial);
  apicHintTimerFreq(pDevIns, pApicCpu, uInitialCount, uTimerShift);
}

apicStopTimer

static void apicStopTimer(PVMCPUCC pVCpu)
{
 	//调用TM里的TMTimerStop函数停止定时器
  PDMDevHlpTimerStop(pDevIns, pApicCpu->hTimer);
  pApicCpu->uHintedTimerInitialCount = 0;
  pApicCpu->uHintedTimerShift = 0;
}

apicHintTimerFreq

告诉TM当前APIC的频率

void apicHintTimerFreq(PPDMDEVINS pDevIns, PAPICCPU pApicCpu, uint32_t uInitialCount, uint8_t uTimerShift)
{
  	//Timer启动之后只会设置一次
    if (   pApicCpu->uHintedTimerInitialCount != uInitialCount
        || pApicCpu->uHintedTimerShift        != uTimerShift)
    {
        uint32_t uHz;
        if (uInitialCount)
        {
          	//开启了定时器,调用TMTimerGetFreq函数获取频率
            uint64_t cTicksPerPeriod = (uint64_t)uInitialCount << uTimerShift;
            uHz = PDMDevHlpTimerGetFreq(pDevIns, pApicCpu->hTimer) / cTicksPerPeriod;
        }
        else
            uHz = 0;
				//调用TMTimerSetFrequencyHint设置时间频率
        PDMDevHlpTimerSetFrequencyHint(pDevIns, pApicCpu->hTimer, uHz);
        pApicCpu->uHintedTimerInitialCount = uInitialCount;
        pApicCpu->uHintedTimerShift = uTimerShift;
    }
}

APICGetTimerFreq

获取当前APIC的频率

VMM_INT_DECL(int) APICGetTimerFreq(PVMCC pVM, uint64_t *pu64Value)
{
    PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[0];
    if (APICIsEnabled(pVCpu))
    {
        PCAPICCPU pApicCpu = VMCPU_TO_APICCPU(pVCpu);
        // 调用TMTimerGetFreq函数获取频率
        *pu64Value = PDMDevHlpTimerGetFreq(VMCPU_TO_DEVINS(pVCpu), pApicCpu->hTimer);
        return VINF_SUCCESS;
    }
    return VERR_PDM_NO_APIC_INSTANCE;
}

apicR3TimerCallback

时间片到之后的callback

static DECLCALLBACK(void) apicR3TimerCallback(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
  PXAPICPAGE     pXApicPage = VMCPU_TO_XAPICPAGE(pVCpu);
  uint32_t const uLvtTimer  = pXApicPage->lvt_timer.all.u32LvtTimer;
  if (!XAPIC_LVT_IS_MASKED(uLvtTimer))
  {
    //时间片到, 根据LVT Timer里的信息发送中断到目标VCPU
    uint8_t uVector = XAPIC_LVT_GET_VECTOR(uLvtTimer);
    apicPostInterrupt(pVCpu, uVector, XAPICTRIGGERMODE_EDGE, 0 /* uSrcTag */);
  }
	//根据Timer mode决定是否重置定时器
  XAPICTIMERMODE enmTimerMode = XAPIC_LVT_GET_TIMER_MODE(uLvtTimer);
  switch (enmTimerMode)
  {
    case XAPICTIMERMODE_PERIODIC:
      {
        //PERIODIC需要重置定时器并重新把u32CurrentCount赋值
        uint32_t const uInitialCount = pXApicPage->timer_icr.u32InitialCount;
        pXApicPage->timer_ccr.u32CurrentCount = uInitialCount;
        if (uInitialCount)
        {
          //重新启动定时器
          apicStartTimer(pVCpu, uInitialCount);
        }
        break;
      }
    case XAPICTIMERMODE_ONESHOT:
      {
        //时间片到,不会重置,所以只设置u32CurrentCount成0
        pXApicPage->timer_ccr.u32CurrentCount = 0;
        break;
      }

    case XAPICTIMERMODE_TSC_DEADLINE:
      {
        //暂时不支持创建TSC deadline的timer
        break;
      }
  }
}

参考资料:

https://blog.csdn.net/omnispace/article/details/61415994

https://blog.csdn.net/ustc_dylan/article/details/4132046

yangrong的blog 发布了18 篇原创文章 · 获赞 0 · 访问量 387 私信 关注

标签:case,md,OFF,源码,APIC,pVCpu,pXApicPage,XAPIC
来源: https://blog.csdn.net/qq_29684547/article/details/104201078