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
Intel指令手册