文章目录
这一章里,介绍模拟CPUID和CPU寄存器访问
11.1 GuestOS CpuId/MSR初始化
cpumR3InitCpuIdAndMsrs
在CpumInit()函数里调用
int cpumR3InitCpuIdAndMsrs(PVM pVM, PCCPUMMSRS pHostMsrs)
{
// 读取配置
CPUMCPUIDCONFIG Config;
cpumR3CpuIdReadConfig(pVM, &Config, pCpumCfg, HMAreNestedPagingAndFullGuestExecEnabled(pVM));
//从CPUDatabase里获取当前CPU型号,初始化MSRRange和CPUIDRange
cpumR3DbGetCpuInfo(Config.szCpuName, &pCpum->GuestInfo);
//去掉Guest CPU的MxCsrMask里Host不支持的特性
if (pCpum->GuestInfo.fMxCsrMask & ~pVM->cpum.s.fHostMxCsrMask)
{
pCpum->GuestInfo.fMxCsrMask &= pVM->cpum.s.fHostMxCsrMask;
}
//从CPU Database里找到对应的CPU型号,并初始化MSRRange
cpumR3DbGetCpuInfo(Config.szCpuName, &pCpum->GuestInfo);
//通过config里的配置覆盖部分MSRRange里的内容
cpumR3LoadMsrOverrides(pVM, CFGMR3GetChild(pCpumCfg, "MSRs"));
//通过config里的配置覆盖部分CPUID leaves里的内容
cpumR3LoadCpuIdOverrides(pVM, CFGMR3GetChild(pCpumCfg, "HostCPUID"), "HostCPUID");
CFGMR3GetChild(pCpumCfg, "CPUID")
//先保存到全局变量里
CPUMMSRS GuestMsrs;
RT_ZERO(GuestMsrs);
//根据CPU Leaves里的数据,初始化pCpum->GuestFeatures,和GuestMsrs里的部分值
cpumR3CpuIdExplodeFeatures(pCpum->GuestInfo.paCpuIdLeavesR3, pCpum->GuestInfo.cCpuIdLeaves, &GuestMsrs,
&pCpum->GuestFeatures);
//初始化部分CPUID leaves里的值,本篇后面介绍
if (RT_SUCCESS(rc))
{
rc = cpumR3CpuIdSanitize(pVM, pCpum, &Config);
if (RT_SUCCESS(rc))
{
cpumR3CpuIdLimitLeaves(pCpum, &Config);
cpumR3CpuIdLimitIntelFamModStep(pCpum, &Config);
}
}
//根据CPUID的值初始化部分MSRRange,下一篇里介绍这个函数
cpumR3MsrReconcileWithCpuId(pVM);
//为了能让虚拟机能在不同CPU机器上拷贝,需要对部分MSR寄存器做模糊化处理: 如果CPU没有对应MSRRange,设置一个模糊MSRRange,下一篇里介绍这个函数
bool fEnable;
rc = CFGMR3QueryBoolDef(pCpumCfg, "FudgeMSRs", &fEnable, true); AssertRC(rc);
if (RT_SUCCESS(rc) && fEnable)
{
rc = cpumR3MsrApplyFudge(pVM);
}
//把MSR ranges和CPUID leaves的内存拷贝到用MM(Memory manager申请出来的内存里
void *pvFree = pCpum->GuestInfo.paCpuIdLeavesR3;
//这个函数后面介绍
int rc1 = cpumR3CpuIdInstallAndExplodeLeaves(pVM, pCpum, pCpum->GuestInfo.paCpuIdLeavesR3,
pCpum->GuestInfo.cCpuIdLeaves, &GuestMsrs);
RTMemFree(pvFree);
pvFree = pCpum->GuestInfo.paMsrRangesR3;
int rc2 = MMHyperDupMem(pVM, pvFree,
sizeof(pCpum->GuestInfo.paMsrRangesR3[0]) * pCpum->GuestInfo.cMsrRanges, 32,
MM_TAG_CPUM_MSRS, (void **)&pCpum->GuestInfo.paMsrRangesR3);
RTMemFree(pvFree);
pCpum->GuestInfo.paMsrRangesR0 = MMHyperR3ToR0(pVM, pCpum->GuestInfo.paMsrRangesR3);
//初始化VMX的msr
if (pVM->cpum.s.GuestFeatures.fVmx)
{
cpumR3InitVmxGuestFeaturesAndMsrs(pVM, &pHostMsrs->hwvirt.vmx, &GuestMsrs.hwvirt.vmx);
//从全局变量里拷贝msr值到VCPU里
PCVMXMSRS pVmxMsrs = &GuestMsrs.hwvirt.vmx;
for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
{
PVMCPU pVCpu = pVM->apCpusR3[idCpu];
memcpy(&pVCpu->cpum.s.Guest.hwvirt.vmx.Msrs, pVmxMsrs, sizeof(*pVmxMsrs));
}
}
//根据配置设置部分CPUID
//PAE
bool fEnable;
rc = CFGMR3QueryBoolDef(CFGMR3GetRoot(pVM), "EnablePAE", &fEnable, false);
AssertRCReturn(rc, rc);
if (fEnable)
CPUMR3SetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_PAE);
//NX
rc = CFGMR3QueryBoolDef(pCpumCfg, "EnableNX", &fEnable, false);
if (fEnable)
CPUMR3SetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_NX);
//speculation control
rc = CFGMR3QueryBoolDef(pCpumCfg, "SpecCtrl", &fEnable, false);
if (fEnable)
CPUMR3SetGuestCpuIdFeature(pVM, CPUMCPUIDFEATURE_SPEC_CTRL);
}
cpumR3LoadCpuIdOverrides
//从配置文件里获取CPUID leaves 并覆盖已有的CPUID leaves
static int cpumR3LoadCpuIdOverrides(PVM pVM, PCFGMNODE pParentNode, const char *pszLabel)
{
for (PCFGMNODE pNode = CFGMR3GetFirstChild(pParentNode); pNode; pNode = CFGMR3GetNextChild(pNode))
{
//获取Leaf和SubLeaf的值
CFGMR3QueryU32(pNode, "Leaf", &uLeaf);
CFGMR3QueryU32Def(pNode, "SubLeaf", &uSubLeaf, 0);
CFGMR3QueryU32Def(pNode, "SubLeafMask", &fSubLeafMask, 0);
//从CPUID leaves array里获取已有的Leaf
PCCPUMCPUIDLEAF pLeaf = cpumR3CpuIdGetExactLeaf(&pVM->cpum.s, uLeaf, uSubLeaf);
//根据配置文件里的值重新赋值这个Leaf
if (pLeaf)
Leaf = *pLeaf;
else
RT_ZERO(Leaf);
Leaf.uLeaf = uLeaf;
Leaf.uSubLeaf = uSubLeaf;
Leaf.fSubLeafMask = fSubLeafMask;
CFGMR3QueryU32Def(pNode, "eax", &Leaf.uEax, Leaf.uEax);
CFGMR3QueryU32Def(pNode, "ebx", &Leaf.uEbx, Leaf.uEbx);
CFGMR3QueryU32Def(pNode, "ecx", &Leaf.uEcx, Leaf.uEcx);
CFGMR3QueryU32Def(pNode, "edx", &Leaf.uEdx, Leaf.uEdx);
//加入到paCpuIdLeavesR3 array里,如果已存在这个leaf,覆盖已有的leaf
cpumR3CpuIdInsert(NULL /* pVM */, &pVM->cpum.s.GuestInfo.paCpuIdLeavesR3, &pVM->cpum.s.GuestInfo.cCpuIdLeaves,
&Leaf);
}
}
cpumR3CpuIdExplodeFeatures
根据CPU Leaves里的数据,初始化PCPUMFEATURES结构体
//根据CPUID的返回值,赋值PCPUMFEATURES
int cpumR3CpuIdExplodeFeatures(PCCPUMCPUIDLEAF paLeaves, uint32_t cLeaves, PCCPUMMSRS pMsrs, PCPUMFEATURES pFeatures)
{
//CPUID(0)的返回值
PCCPUMCPUIDLEAF const pStd0Leaf = cpumR3CpuIdFindLeafEx(paLeaves, cLeaves, 0, 0);
//CPUID (1)的返回值
PCCPUMCPUIDLEAF const pStd1Leaf = cpumR3CpuIdFindLeafEx(paLeaves, cLeaves, 1, 0);
//CPUID (80000008)的返回值 :
//Intel: 物理地址相关的信息,AMD: X86_CPUID_AMD_EFEID_EBX_IBPB
CCPUMCPUIDLEAF const pExtLeaf8 = cpumR3CpuIdFindLeaf(paLeaves, cLeaves, 0x80000008);
//获取物理地址,和线性地址的位数
if (pExtLeaf8)
{
pFeatures->cMaxPhysAddrWidth = pExtLeaf8->uEax & 0xff;
pFeatures->cMaxLinearAddrWidth = (pExtLeaf8->uEax >> 8) & 0xff;
}
//32位PageSizeExtersions开启
else if (pStd1Leaf->uEdx & X86_CPUID_FEATURE_EDX_PSE36)
{
pFeatures->cMaxPhysAddrWidth = 36;
pFeatures->cMaxLinearAddrWidth = 36;
}
else
{
pFeatures->cMaxPhysAddrWidth = 32;
pFeatures->cMaxLinearAddrWidth = 32;
}
//CPU的基础信息
pFeatures->fMsr = RT_BOOL(pStd1Leaf->uEdx & X86_CPUID_FEATURE_EDX_MSR);
pFeatures->fApic = RT_BOOL(pStd1Leaf->uEdx & X86_CPUID_FEATURE_EDX_APIC);
pFeatures->fX2Apic = RT_BOOL(pStd1Leaf->uEcx & X86_CPUID_FEATURE_ECX_X2APIC);
pFeatures->fPse = RT_BOOL(pStd1Leaf->uEdx & X86_CPUID_FEATURE_EDX_PSE);
pFeatures->fPse36 = RT_BOOL(pStd1Leaf->uEdx & X86_CPUID_FEATURE_EDX_PSE36);
...
//VMX
if (pFeatures->fVmx)
cpumR3ExplodeVmxFeatures(&pMsrs->hwvirt.vmx, pFeatures);
//CPUID (7)的返回值: Structured Extended Feature Flags
PCCPUMCPUIDLEAF const pSxfLeaf0 = cpumR3CpuIdFindLeafEx(paLeaves, cLeaves, 7, 0);
if (pSxfLeaf0)
{
pFeatures->fFsGsBase = RT_BOOL(pSxfLeaf0->uEbx & X86_CPUID_STEXT_FEATURE_EBX_FSGSBASE);
pFeatures->fAvx2 = RT_BOOL(pSxfLeaf0->uEbx & X86_CPUID_STEXT_FEATURE_EBX_AVX2);
pFeatures->fAvx512Foundation = RT_BOOL(pSxfLeaf0->uEbx & X86_CPUID_STEXT_FEATURE_EBX_AVX512F);
pFeatures->fClFlushOpt = RT_BOOL(pSxfLeaf0->uEbx & X86_CPUID_STEXT_FEATURE_EBX_CLFLUSHOPT);
}
//CPUID (5)的返回值:MONITOR/MWAIT Leaf
PCCPUMCPUIDLEAF const pMWaitLeaf = cpumR3CpuIdFindLeaf(paLeaves, cLeaves, 5);
if (pMWaitLeaf)
pFeatures->fMWaitExtensions = (pMWaitLeaf->uEcx & (X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0))
== (X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0);
//CPUID (0x80000001)的返回值 Extended Processor Signature and Feature Bits.
PCCPUMCPUIDLEAF const pExtLeaf = cpumR3CpuIdFindLeaf(paLeaves, cLeaves, 0x80000001);
if (pExtLeaf)
{
pFeatures->fLongMode = RT_BOOL(pExtLeaf->uEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE);
pFeatures->fSysCall = RT_BOOL(pExtLeaf->uEdx & X86_CPUID_EXT_FEATURE_EDX_SYSCALL);
pFeatures->fNoExecute = RT_BOOL(pExtLeaf->uEdx & X86_CPUID_EXT_FEATURE_EDX_NX);
...
}
//如果不支持长模式(64位),则cVmxMaxPhysAddrWidth = 32
pFeatures->cVmxMaxPhysAddrWidth = pFeatures->fLongMode ? pFeatures->cMaxPhysAddrWidth : 32;
//AMD CPU
if ( pExtLeaf
&& ( pFeatures->enmCpuVendor == CPUMCPUVENDOR_AMD
|| pFeatures->enmCpuVendor == CPUMCPUVENDOR_HYGON))
{
pFeatures->fMsr |= RT_BOOL(pExtLeaf->uEdx & X86_CPUID_AMD_FEATURE_EDX_MSR);
pFeatures->fApic |= RT_BOOL(pExtLeaf->uEdx & X86_CPUID_AMD_FEATURE_EDX_APIC);
pFeatures->fPse |= RT_BOOL(pExtLeaf->uEdx & X86_CPUID_AMD_FEATURE_EDX_PSE);
....
}
//AMD CPU特性
pFeatures->fLeakyFxSR = pExtLeaf
&& (pExtLeaf->uEdx & X86_CPUID_AMD_FEATURE_EDX_FFXSR)
&& ( ( pFeatures->enmCpuVendor == CPUMCPUVENDOR_AMD
&& pFeatures->uFamily >= 6 /* K7 and up */)
|| pFeatures->enmCpuVendor == CPUMCPUVENDOR_HYGON)
pFeatures->cbMaxExtendedState = pFeatures->fFxSaveRstor ? sizeof(X86FXSTATE) : sizeof(X86FPUSTATE);
if (pFeatures->fXSaveRstor)
{
//Processor Extended State Enumeration
PCCPUMCPUIDLEAF const pXStateLeaf0 = cpumR3CpuIdFindLeafEx(paLeaves, cLeaves, 13, 0);
if (pXStateLeaf0)
{
if ( pXStateLeaf0->uEcx >= sizeof(X86FXSTATE)
&& pXStateLeaf0->uEcx <= CPUM_MAX_XSAVE_AREA_SIZE
&& RT_ALIGN_32(pXStateLeaf0->uEcx, 8) == pXStateLeaf0->uEcx
&& pXStateLeaf0->uEbx >= sizeof(X86FXSTATE)
&& pXStateLeaf0->uEbx <= pXStateLeaf0->uEcx
&& RT_ALIGN_32(pXStateLeaf0->uEbx, 8) == pXStateLeaf0->uEbx)
{
pFeatures->cbMaxExtendedState = pXStateLeaf0->uEcx;
PCCPUMCPUIDLEAF const pXStateLeaf1 = cpumR3CpuIdFindLeafEx(paLeaves, cLeaves, 13, 1);
if ( pXStateLeaf1
&& pXStateLeaf1->uEbx > pFeatures->cbMaxExtendedState
&& pXStateLeaf1->uEbx <= CPUM_MAX_XSAVE_AREA_SIZE
&& (pXStateLeaf1->uEcx || pXStateLeaf1->uEdx) )
pFeatures->cbMaxExtendedState = pXStateLeaf1->uEbx;
}
}
}
}
11.2 CPUID Leaves 初始化
VirtualBox的VCPU运行用户自己定义CPU的功能,所以需要提供API读取和设置CPU信息,并且需要模拟CPUID指令,GuestOS里调用CPUID指令,能够获取正确的返回值。代码位于CPUMR3Cpuid.cpp。
所有CPUID leaves安装uleaf大小从小到大排好序保存到pVM->cpum.s.GuestInfo.paCpuIdLeavesR3内存里,如果某个uleaf有subleaf,则按照subleaf从小到大顺序排列在一起。
这块内存同时会映射到pVM->cpum.s.GuestInfo.paCpuIdLeavesR0这个R0地址里,pVM->cpum.s.GuestInfo.cCpuIdLeaves保存了CPUID leaves的个数。
cpumR3CpuIdReadConfig
读取虚拟机对CPU的配置,可以控制CPU是否支持一些特殊功能,比如CMPXCHG16B/MONITOR/XSAVE指令等。
static int cpumR3CpuIdReadConfig(PVM pVM, PCPUMCPUIDCONFIG pConfig, PCFGMNODE pCpumCfg, bool fNestedPagingAndFullGuestExec)
{
CFGMR3QueryU8Def(pCpumCfg, "PortableCpuIdLevel", &pVM->cpum.s.u8PortableCpuIdLevel, 0);
CFGMR3QueryStringDef(pCpumCfg, "GuestCpuName", pConfig->szCpuName, sizeof(pConfig->szCpuName), "host");
....
}
CPUMR3CpuIdCollectLeaves
初始化CPUID leaves内存
VMMR3DECL(int) CPUMR3CpuIdCollectLeaves(PCPUMCPUIDLEAF *ppaLeaves, uint32_t *pcLeaves)
{
//最后一位是0的CPUID input一般都是query当前CPU有多少子项,try下面这些值
//Intel CPU支持0x00000000,0x40000000,0x80000000和0xc0000000
static struct { uint32_t uMsr; bool fSpecial; } const s_aCandidates[] =
{
{ UINT32_C(0x00000000), false },
{ UINT32_C(0x10000000), false },
{ UINT32_C(0x20000000), false },
{ UINT32_C(0x30000000), false },
{ UINT32_C(0x40000000), false },
{ UINT32_C(0x50000000), false },
{ UINT32_C(0x60000000), false },
{ UINT32_C(0x70000000), false },
{ UINT32_C(0x80000000), false },
{ UINT32_C(0x80860000), false },
{ UINT32_C(0x8ffffffe), true },
{ UINT32_C(0x8fffffff), true },
{ UINT32_C(0x90000000), false },
{ UINT32_C(0xa0000000), false },
{ UINT32_C(0xb0000000), false },
{ UINT32_C(0xc0000000), false },
{ UINT32_C(0xd0000000), false },
{ UINT32_C(0xe0000000), false },
{ UINT32_C(0xf0000000), false },
};
for (uint32_t iOuter = 0; iOuter < RT_ELEMENTS(s_aCandidates); iOuter++)
{
//获取每一项的leaf个数
uint32_t uLeaf = s_aCandidates[iOuter].uMsr;
uint32_t uEax, uEbx, uEcx, uEdx;
ASMCpuIdExSlow(uLeaf, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
//返回的uEax值正确
if ( uEax > uLeaf && uEax - uLeaf < UINT32_C(0xff))
{
uint32_t cLeaves = uEax - uLeaf + 1;
while (cLeaves-- > 0)
{
//获取每个子leaf的host返回值
ASMCpuIdExSlow(uLeaf, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
//是否有APIC ID
if (uLeaf == 1)
fFlags |= CPUMCPUIDLEAF_F_CONTAINS_APIC_ID;
else if (uLeaf == 0xb && uEcx != 0)
fFlags |= CPUMCPUIDLEAF_F_CONTAINS_APIC_ID;
else if ( uLeaf == UINT32_C(0x8000001e)
&& ( uEax
|| uEbx
|| uEdx
|| ASMIsAmdCpuEx((*ppaLeaves)[0].uEbx, (*ppaLeaves)[0].uEcx, (*ppaLeaves)[0].uEdx)
|| ASMIsHygonCpuEx((*ppaLeaves)[0].uEbx, (*ppaLeaves)[0].uEcx, (*ppaLeaves)[0].uEdx)) )
fFlags |= CPUMCPUIDLEAF_F_CONTAINS_APIC_ID;
//获取subleaf个数,获取3次
if ( cpumR3IsEcxRelevantForCpuIdLeaf(uLeaf, &cSubLeaves, &fFinalEcxUnchanged)
&& cpumR3IsEcxRelevantForCpuIdLeaf(uLeaf, &cSubLeaves, &fFinalEcxUnchanged)
&& cpumR3IsEcxRelevantForCpuIdLeaf(uLeaf, &cSubLeaves, &fFinalEcxUnchanged))
{
//subleave个数不正确,返回错误
if (cSubLeaves > (uLeaf == 0xd ? 68U : 16U))
{
return VERR_CPUM_TOO_MANY_CPUID_SUBLEAVES;
}
if (fFinalEcxUnchanged)
fFlags |= CPUMCPUIDLEAF_F_INTEL_TOPOLOGY_SUBLEAVES;
//遍历每个subleaf,加入到ppaLeaves里
for (uint32_t uSubLeaf = 0; uSubLeaf < cSubLeaves; uSubLeaf++)
{
ASMCpuIdExSlow(uLeaf, 0, uSubLeaf, 0, &uEax, &uEbx, &uEcx, &uEdx);
cpumR3CollectCpuIdInfoAddOne(ppaLeaves, pcLeaves,
uLeaf, uSubLeaf, UINT32_MAX, uEax, uEbx, uEcx, uEdx, fFlags);
}
}
else
{
//当前leaf没有subleaf,只加入当前leaf到ppaLeaves里
cpumR3CollectCpuIdInfoAddOne(ppaLeaves, pcLeaves,
uLeaf, 0, 0, uEax, uEbx, uEcx, uEdx, fFlags);
}
uLeaf++;
}
}
else if (s_aCandidates[iOuter].fSpecial)
{
//几个特殊的CPUID
bool fKeep = false;
//AMD K6 Unofficial CPU signature, Returns the string "DEI"
if (uLeaf == 0x8ffffffe && uEax == UINT32_C(0x00494544))
fKeep = true;
//AMD CPU用这个CPUID,其实是个彩蛋,把输入的寄存器每一位用asci吗转化一下,输出是“IT'S HAMMER TIME” ^_^
else if ( uLeaf == 0x8fffffff (AMD CPU用这个CPUID)
&& RT_C_IS_PRINT(RT_BYTE1(uEax))
&& RT_C_IS_PRINT(RT_BYTE2(uEax))
&& RT_C_IS_PRINT(RT_BYTE3(uEax))
&& RT_C_IS_PRINT(RT_BYTE4(uEax))
&& RT_C_IS_PRINT(RT_BYTE1(uEbx))
&& RT_C_IS_PRINT(RT_BYTE2(uEbx))
&& RT_C_IS_PRINT(RT_BYTE3(uEbx))
&& RT_C_IS_PRINT(RT_BYTE4(uEbx))
&& RT_C_IS_PRINT(RT_BYTE1(uEcx))
&& RT_C_IS_PRINT(RT_BYTE2(uEcx))
&& RT_C_IS_PRINT(RT_BYTE3(uEcx))
&& RT_C_IS_PRINT(RT_BYTE4(uEcx))
&& RT_C_IS_PRINT(RT_BYTE1(uEdx))
&& RT_C_IS_PRINT(RT_BYTE2(uEdx))
&& RT_C_IS_PRINT(RT_BYTE3(uEdx))
&& RT_C_IS_PRINT(RT_BYTE4(uEdx)) )
fKeep = true;
if (fKeep)
{
// 加入当前leaf到ppaLeaves里
cpumR3CollectCpuIdInfoAddOne(ppaLeaves, pcLeaves,
uLeaf, 0, 0, uEax, uEbx, uEcx, uEdx, 0);
}
}
}
}
cpumR3CpuIdSanitize
初始化部分CPUID leaves
static int cpumR3CpuIdSanitize(PVM pVM, PCPUM pCpum, PCPUMCPUIDCONFIG pConfig)
{
//Cpuid 1:
//从全局CPUIDLeaves数组里获取对应的项,用于写入数据
PCPUMCPUIDLEAF pStdFeatureLeaf = cpumR3CpuIdGetExactLeaf(pCpum, 1, 0);
pStdFeatureLeaf = cpumR3CpuIdMakeSingleLeaf(pCpum, pStdFeatureLeaf);
//edx值
pStdFeatureLeaf->uEdx &= X86_CPUID_FEATURE_EDX_FPU
| X86_CPUID_FEATURE_EDX_VME
| X86_CPUID_FEATURE_EDX_DE
| X86_CPUID_FEATURE_EDX_PSE
...
;
//部分ecx值,根据config里的值赋值
pStdFeatureLeaf->uEcx &= 0
| X86_CPUID_FEATURE_ECX_SSE3
| (pConfig->enmPClMul ? X86_CPUID_FEATURE_ECX_PCLMUL : 0)
| ((pConfig->enmMonitor && pVM->cCpus == 1) ? X86_CPUID_FEATURE_ECX_MONITOR : 0)
....
;
//u8PortableCpuIdLevel是config里的配置,配置越高,CPU支持功能越少
if (pCpum->u8PortableCpuIdLevel > 0)
{
//去掉一些CPU高级功能
PORTABLE_CLEAR_BITS_WHEN(1, pStdFeatureLeaf->uEax, ProcessorType, (UINT32_C(3) << 12), (UINT32_C(2) << 12));
PORTABLE_DISABLE_FEATURE_BIT( 1, pStdFeatureLeaf->uEcx, SSSE3, X86_CPUID_FEATURE_ECX_SSSE3);
PORTABLE_DISABLE_FEATURE_BIT_CFG(1, pStdFeatureLeaf->uEcx, PCID, X86_CPUID_FEATURE_ECX_PCID, pConfig->enmPcid);
PORTABLE_DISABLE_FEATURE_BIT_CFG(1, pStdFeatureLeaf->uEcx, SSE4_1, X86_CPUID_FEATURE_ECX_SSE4_1, pConfig->enmSse41);
PORTABLE_DISABLE_FEATURE_BIT_CFG(1, pStdFeatureLeaf->uEcx, SSE4_2, X86_CPUID_FEATURE_ECX_SSE4_2, pConfig->enmSse42);
....
}
//AMD 两个CPU根据microcode version决定是否支持VME
uint32_t uMicrocodeRev;
int rc = SUPR3QueryMicrocodeRev(&uMicrocodeRev);
if ( ( pVM->cpum.s.GuestFeatures.enmMicroarch == kCpumMicroarch_AMD_Zen_Ryzen
|| pVM->cpum.s.GuestFeatures.enmMicroarch == kCpumMicroarch_Hygon_Dhyana)
&& uMicrocodeRev < 0x8001126
&& !pConfig->fForceVme)
{
pStdFeatureLeaf->uEdx &= ~X86_CPUID_FEATURE_EDX_VME;
}
//初始化APIC-ID
pStdFeatureLeaf->uEbx &= UINT32_C(0x0000ffff); /* (APIC-ID := 0 and #LogCpus := 0) */
if (pConfig->enmPClMul == CPUMISAEXTCFG_ENABLED_ALWAYS)
pStdFeatureLeaf->uEcx |= X86_CPUID_FEATURE_ECX_PCLMUL;
if (pConfig->enmMonitor == CPUMISAEXTCFG_ENABLED_ALWAYS)
pStdFeatureLeaf->uEcx |= X86_CPUID_FEATURE_ECX_MONITOR;
if (pConfig->enmCmpXchg16b == CPUMISAEXTCFG_ENABLED_ALWAYS)
pStdFeatureLeaf->uEcx |= X86_CPUID_FEATURE_ECX_CX16;
...
// CPUID 0x80000001
//从全局CPUIDLeaves数组里获取对应的项,用于写入数据
PCPUMCPUIDLEAF pExtFeatureLeaf = cpumR3CpuIdGetExactLeaf(pCpum, UINT32_C(0x80000001), 0);
//去掉所有的subleaf
pExtFeatureLeaf = cpumR3CpuIdMakeSingleLeaf(pCpum, pExtFeatureLeaf);
//edx赋值,Intel CPU edx很多是reserve,所以只对AMD CPU有用
pExtFeatureLeaf->uEdx &= X86_CPUID_AMD_FEATURE_EDX_FPU
| X86_CPUID_AMD_FEATURE_EDX_VME
| X86_CPUID_AMD_FEATURE_EDX_DE
| X86_CPUID_AMD_FEATURE_EDX_PSE
| X86_CPUID_AMD_FEATURE_EDX_TSC
...
//ecx
pExtFeatureLeaf->uEcx &= X86_CPUID_EXT_FEATURE_ECX_LAHF_SAHF
| (pConfig->fNestedHWVirt ? X86_CPUID_AMD_FEATURE_ECX_SVM : 0)
| X86_CPUID_AMD_FEATURE_ECX_CR8L
| (pConfig->enmAbm ? X86_CPUID_AMD_FEATURE_ECX_ABM : 0)
| (pConfig->enmSse4A ? X86_CPUID_AMD_FEATURE_ECX_SSE4A : 0)
...
//多核AMD和HYGON CPU需要打开X86_CPUID_AMD_FEATURE_ECX_CMPL
if ( pVM->cCpus > 1
&& ( pCpum->GuestFeatures.enmCpuVendor == CPUMCPUVENDOR_AMD
|| pCpum->GuestFeatures.enmCpuVendor == CPUMCPUVENDOR_HYGON))
pExtFeatureLeaf->uEcx |= X86_CPUID_AMD_FEATURE_ECX_CMPL; /* CmpLegacy */
//去掉一些高级功能如果u8PortableCpuIdLevel > 0
if (pCpum->u8PortableCpuIdLevel > 0)
{
PORTABLE_DISABLE_FEATURE_BIT( 1, pExtFeatureLeaf->uEcx, CR8L, X86_CPUID_AMD_FEATURE_ECX_CR8L);
PORTABLE_DISABLE_FEATURE_BIT( 1, pExtFeatureLeaf->uEcx, SVM, X86_CPUID_AMD_FEATURE_ECX_SVM);
....
}
//根据配置设置一些bit位
if (pConfig->enmAbm == CPUMISAEXTCFG_ENABLED_ALWAYS)
pExtFeatureLeaf->uEcx |= X86_CPUID_AMD_FEATURE_ECX_ABM;
if (pConfig->enmSse4A == CPUMISAEXTCFG_ENABLED_ALWAYS)
pExtFeatureLeaf->uEcx |= X86_CPUID_AMD_FEATURE_ECX_SSE4A;
//CPUID 2: cache和TLB相关信息 (多少级缓存,缓存大小,几个通道等)
while ((pCurLeaf = cpumR3CpuIdGetExactLeaf(pCpum, 2, uSubLeaf)) != NULL)
{
if ((pCurLeaf->uEax & 0xff) > 1)
{
pCurLeaf->uEax &= UINT32_C(0xffffff01);
}
uSubLeaf++;
}
//CPUID 3: CPU序列号
pStdFeatureLeaf = cpumR3CpuIdGetExactLeaf(pCpum, 1, 0);
if (!(pStdFeatureLeaf->uEdx & X86_CPUID_FEATURE_EDX_PSN))
{
uSubLeaf = 0;
while ((pCurLeaf = cpumR3CpuIdGetExactLeaf(pCpum, 3, uSubLeaf)) != NULL)
{
pCurLeaf->uEcx = pCurLeaf->uEdx = 0;
if (pCpum->u8PortableCpuIdLevel > 0)
pCurLeaf->uEax = pCurLeaf->uEbx = 0;
uSubLeaf++;
}
}
//CPUID 4 : Cache相关的参数(数据cache/指令cache,是否写直达等)
uSubLeaf = 0;
while ((pCurLeaf = cpumR3CpuIdGetExactLeaf(pCpum, 4, uSubLeaf)) != NULL)
{
pCurLeaf->uEax &= UINT32_C(0x00003fff);
if ( pVM->cCpus > 1
&& pCpum->GuestFeatures.enmCpuVendor == CPUMCPUVENDOR_INTEL)
{
pCurLeaf->uEax |= pVM->cCpus <= 0x40 ? ((pVM->cCpus - 1) << 26) : UINT32_C(0xfc000000); /* 6 bits only -> 64 cores! */
}
uSubLeaf++;
}
//CPUID 5: Monitor/mwaits
while ((pCurLeaf = cpumR3CpuIdGetExactLeaf(pCpum, 5, uSubLeaf)) != NULL)
{
//如果CPUID 0 里设置了X86_CPUID_FEATURE_ECX_MONITOR, eax, ebx 设置成0
pStdFeatureLeaf = cpumR3CpuIdGetExactLeaf(pCpum, 1, 0);
if (!(pStdFeatureLeaf->uEcx & X86_CPUID_FEATURE_ECX_MONITOR))
pCurLeaf->uEax = pCurLeaf->uEbx = 0;
pCurLeaf->uEcx = pCurLeaf->uEdx = 0;
//如果配置里开启了enmMWaitExtensions,设置对应的ecx, 但是edx没有设置
if (pConfig->enmMWaitExtensions)
{
pCurLeaf->uEcx = X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0;
}
else
pCurLeaf->uEcx = pCurLeaf->uEdx = 0;
uSubLeaf++;
}
//CPUID 6,8-10,14-22: 都返回zero leaf,不支持对应功能
cpumR3CpuIdZeroLeaf(pCpum, cpuid);
//CPUID 7;Structured Extended Feature Flags
//只有第一个subleaf有值
while ((pCurLeaf = cpumR3CpuIdGetExactLeaf(pCpum, 7, uSubLeaf)) != NULL)
{
switch (uSubLeaf)
{
case 0:
{
pCurLeaf->uEax = 0; /* Max ECX input is 0. */
ebx是一些功能的开关
pCurLeaf->uEbx &= 0
| (pConfig->enmFsGsBase ? X86_CPUID_STEXT_FEATURE_EBX_FSGSBASE : 0)
| (pConfig->enmAvx2 ? X86_CPUID_STEXT_FEATURE_EBX_AVX2 : 0)
....
ecx是一些AVX512之类的高级功能,这边先初始化成0
pCurLeaf->uEcx &= 0
pCurLeaf->uEdx &= 0
| (pConfig->enmMdsClear ? X86_CPUID_STEXT_FEATURE_EDX_MD_CLEAR : 0)
| (pConfig->enmFlushCmdMsr ? X86_CPUID_STEXT_FEATURE_EDX_FLUSH_CMD : 0)
...
//根据配置里的值设置开关(感觉有部分是重复的,上面已经设置了)
if (pConfig->enmFsGsBase == CPUMISAEXTCFG_ENABLED_ALWAYS)
pCurLeaf->uEbx |= X86_CPUID_STEXT_FEATURE_EBX_FSGSBASE;
if (pConfig->enmAvx2 == CPUMISAEXTCFG_ENABLED_ALWAYS)
pCurLeaf->uEbx |= X86_CPUID_STEXT_FEATURE_EBX_AVX2;
}
//其他subleaf,返回0
default:
pCurLeaf->uEax = 0;
pCurLeaf->uEbx = 0;
pCurLeaf->uEcx = 0;
pCurLeaf->uEdx = 0;
}
uSubLeaf++;
}
//80000002h 80000003h 80000004h:返回处理器名称/商标字符串, 不设置在这里
//80000005h, 80000006h : L1, L2, L3和tlb相关信息,直接从host里获取
//后面针对每个CPUID,都有不同的初始化代码,这边不一一介绍。
....
}
CPUMR3CpuIdInsert
VMMR3DECL(int) CPUMR3CpuIdInsert(PVM pVM, PCPUMCPUIDLEAF pNewLeaf)
{
//查找CPUID leaves数组,找到需要插入的下标
uint32_t i;
if ( cLeaves > 0
&& paLeaves[cLeaves - 1].uLeaf < pNewLeaf->uLeaf)
{
//在最后加入新节点
i = cLeaves;
}
else if ( cLeaves > 0
&& paLeaves[cLeaves - 1].uLeaf == pNewLeaf->uLeaf)
{
//最后一个uLeaf相同,找到uLeaf的subleaf的第一项的前一项
i = cLeaves - 1;
while ( i > 0
&& paLeaves[i - 1].uLeaf == pNewLeaf->uLeaf)
i--;
}
else
{
//从头开始linear search,找到比当前uleaf小而去最接近的一个节点
i = 0;
while ( i < cLeaves
&& paLeaves[i].uLeaf < pNewLeaf->uLeaf)
i++;
}
//找到uleaf,开始查找SubLeaf
if ( i < cLeaves
&& paLeaves[i].uLeaf == pNewLeaf->uLeaf)
{
if (paLeaves[i].fSubLeafMask != pNewLeaf->fSubLeafMask)
{
//fSubLeafMask和原始值不同
uint32_t c = 1;
while ( i + c < cLeaves
&& paLeaves[i + c].uLeaf == pNewLeaf->uLeaf)
c++;
if (c > 1 && i + c < cLeaves)
{
memmove(&paLeaves[i + c], &paLeaves[i + 1], (cLeaves - i - c) * sizeof(paLeaves[0]));
*pcLeaves = cLeaves -= c - 1;
}
paLeaves[i] = *pNewLeaf;
return VINF_SUCCESS;
}
//linesearch找到subleaf可以插入的位置
while ( i < cLeaves
&& paLeaves[i].uSubLeaf < pNewLeaf->uSubLeaf
&& paLeaves[i].uLeaf == pNewLeaf->uLeaf)
i++;
//如果uLeaf已存在,直接替换
if ( i < cLeaves
&& paLeaves[i].uLeaf == pNewLeaf->uLeaf
&& paLeaves[i].uSubLeaf == pNewLeaf->uSubLeaf)
{
paLeaves[i] = *pNewLeaf;
return VINF_SUCCESS;
}
}
//确保CPUID Leaves有足够的空间增加一个新的CPUID leaf
paLeaves = cpumR3CpuIdEnsureSpace(pVM, ppaLeaves, cLeaves);
if (!paLeaves)
return VERR_NO_MEMORY;
//移动后面的Leaves
if (i < cLeaves)
memmove(&paLeaves[i + 1], &paLeaves[i], (cLeaves - i) * sizeof(paLeaves[0]));
//加入pNewLeaf
*pcLeaves += 1;
paLeaves[i] = *pNewLeaf;
}
11.3 CPUID Leaves 访问
CPUMR3SetGuestCpuIdFeature
各种Manager初始化的时候根据配置动态设置CPUID/MSR leaves里的值,现在只支持部分CPU功能设置
VMMR3_INT_DECL(void) CPUMR3SetGuestCpuIdFeature(PVM pVM, CPUMCPUIDFEATURE enmFeature)
{
switch (enmFeature)
{
//APIC
case CPUMCPUIDFEATURE_APIC:
//设置CPUID(1)里APIC的设置
Leaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x00000001));
if (pLeaf && (pLeaf->fFlags & CPUMCPUIDLEAF_F_CONTAINS_APIC))
pVM->cpum.s.aGuestCpuIdPatmStd[1].uEdx = pLeaf->uEdx |= X86_CPUID_FEATURE_EDX_APIC;
//AMDCPU 设置CPUID(80000001)里的设置
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x80000001));
if (pLeaf && (pLeaf->fFlags & CPUMCPUIDLEAF_F_CONTAINS_APIC))
pVM->cpum.s.aGuestCpuIdPatmExt[1].uEdx = pLeaf->uEdx |= X86_CPUID_AMD_FEATURE_EDX_APIC;
//设置MSR_IA32_APICBASE 的leaves
pMsrRange = cpumLookupMsrRange(pVM, MSR_IA32_APICBASE);
if (!pMsrRange)
{
static CPUMMSRRANGE const s_ApicBase =
{
/*.uFirst =*/ MSR_IA32_APICBASE, /*.uLast =*/ MSR_IA32_APICBASE,
/*.enmRdFn =*/ kCpumMsrRdFn_Ia32ApicBase, /*.enmWrFn =*/ kCpumMsrWrFn_Ia32ApicBase,
/*.offCpumCpu =*/ UINT16_MAX, /*.fReserved =*/ 0, /*.uValue =*/ 0, /*.fWrIgnMask =*/ 0, /*.fWrGpMask =*/ 0,
/*.szName = */ "IA32_APIC_BASE"
};
int rc = CPUMR3MsrRangesInsert(pVM, &s_ApicBase);
}
//x2APIC
case CPUMCPUIDFEATURE_X2APIC:
//设置CPUID(1)里x2APIC的设置
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x00000001));
if (pLeaf)
pVM->cpum.s.aGuestCpuIdPatmStd[1].uEcx = pLeaf->uEcx |= X86_CPUID_FEATURE_ECX_X2APIC;
pVM->cpum.s.GuestFeatures.fX2Apic = 1;
//修改MSR_IA32_APICBASE里的mask,支持x2APIC
pMsrRange = cpumLookupMsrRange(pVM, MSR_IA32_APICBASE);
if (pMsrRange)
{
pMsrRange->fWrGpMask &= ~MSR_IA32_APICBASE_EXTD;
pMsrRange->fWrIgnMask &= ~MSR_IA32_APICBASE_EXTD;
}
//Sysenter/sysexit
case CPUMCPUIDFEATURE_SEP:
//修改CPUID(1)里对应的bit位
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x00000001));
if (pLeaf)
pVM->cpum.s.aGuestCpuIdPatmStd[1].uEdx = pLeaf->uEdx |= X86_CPUID_FEATURE_EDX_SEP;
pVM->cpum.s.GuestFeatures.fSysEnter = 1;
//syscall/sysret
case CPUMCPUIDFEATURE_SYSCALL:
//修改CPUID(80000001)里对应的bit位
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x80000001));
pVM->cpum.s.aGuestCpuIdPatmExt[1].uEdx = pLeaf->uEdx |= X86_CPUID_EXT_FEATURE_EDX_SYSCALL;
pVM->cpum.s.GuestFeatures.fSysCall = 1;
//PAE 32位页表扩展
case CPUMCPUIDFEATURE_PAE:
//修改CPUID(1)里对应的bit位
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x00000001));
if (pLeaf)
pVM->cpum.s.aGuestCpuIdPatmStd[1].uEdx = pLeaf->uEdx |= X86_CPUID_FEATURE_EDX_PAE;
//AMD CPU修改CPUID(80000001)里对应的bit位
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x80000001));
pVM->cpum.s.aGuestCpuIdPatmExt[1].uEdx = pLeaf->uEdx |= X86_CPUID_AMD_FEATURE_EDX_PAE;
//长模式,支持64位Guest
case CPUMCPUIDFEATURE_LONG_MODE:
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x80000001));
pVM->cpum.s.aGuestCpuIdPatmExt[1].uEdx = pLeaf->uEdx |= X86_CPUID_EXT_FEATURE_EDX_LONG_MODE;
pVM->cpum.s.GuestFeatures.fLongMode = 1;
pVM->cpum.s.GuestFeatures.cVmxMaxPhysAddrWidth = pVM->cpum.s.GuestFeatures.cMaxPhysAddrWidth;
if (pVM->cpum.s.GuestFeatures.fVmx)
for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
{
PVMCPU pVCpu = pVM->apCpusR3[idCpu];
pVCpu->cpum.s.Guest.hwvirt.vmx.Msrs.u64Basic &= ~VMX_BASIC_PHYSADDR_WIDTH_32BIT;
}
//NX,内存不可执行
case CPUMCPUIDFEATURE_NX:
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x80000001));
pVM->cpum.s.aGuestCpuIdPatmExt[1].uEdx = pLeaf->uEdx |= X86_CPUID_EXT_FEATURE_EDX_NX;
pVM->cpum.s.GuestFeatures.fNoExecute = 1;
//LAHF/SAHF指令支持
case CPUMCPUIDFEATURE_LAHF:
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x80000001));
pVM->cpum.s.aGuestCpuIdPatmExt[1].uEcx = pLeaf->uEcx |= X86_CPUID_EXT_FEATURE_ECX_LAHF_SAHF;
pVM->cpum.s.GuestFeatures.fLahfSahf = 1;
//the page attribute table bit.
case CPUMCPUIDFEATURE_PAT:
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x00000001));
if (pLeaf)
pVM->cpum.s.aGuestCpuIdPatmStd[1].uEdx = pLeaf->uEdx |= X86_CPUID_FEATURE_EDX_PAT;
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x80000001));
pVM->cpum.s.aGuestCpuIdPatmExt[1].uEdx = pLeaf->uEdx |= X86_CPUID_AMD_FEATURE_EDX_PAT;
pVM->cpum.s.GuestFeatures.fPat = 1;
//RDTSCP指令支持
case CPUMCPUIDFEATURE_RDTSCP:
//CPU修改CPUID(80000001)里对应的bit位
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x80000001));
pVM->cpum.s.aGuestCpuIdPatmExt[1].uEdx = pLeaf->uEdx |= X86_CPUID_EXT_FEATURE_EDX_RDTSCP;
pVM->cpum.s.HostFeatures.fRdTscP = 1;
//Hypervisor Present bit
case CPUMCPUIDFEATURE_HVP:
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x00000001));
pVM->cpum.s.aGuestCpuIdPatmStd[1].uEcx = pLeaf->uEcx |= X86_CPUID_FEATURE_ECX_HVP;
pVM->cpum.s.GuestFeatures.fHypervisorPresent = 1;
// MWAIT extensions
case CPUMCPUIDFEATURE_MWAIT_EXTS:
pLeaf = cpumCpuIdGetLeaf(pVM, UINT32_C(0x00000005));
pVM->cpum.s.aGuestCpuIdPatmStd[5].uEcx = pLeaf->uEcx |= X86_CPUID_MWAIT_ECX_EXT | X86_CPUID_MWAIT_ECX_BREAKIRQIF0;
pVM->cpum.s.GuestFeatures.fMWaitExtensions = 1;
/*
speculation control: 幽灵漏洞预防
ibrs: Indirect Branch Restricted Speculation
当CPUID中存在SPEC_CTRL特性的时候,可以通过"noibrs"/ibrs_enabled来控制IBRS特性。是否支持这个特性可以通过SPEC_CTRL MSR来获取
当ibrs_enabled设置为1(也就是spectre_v2=ibrs),内核态将“间接分支限制预测”。当IBRS设置为2(spectre_v2=ibrs_always),那么它同时限制用户态和内核态的“间接分支预测”。当该值设为3的时候(spectre_v2=retpoline,ibrs_user),它仅仅限制用户态“间接分支预测”功能。
ibpb: Indirect Branch Prediction Barriers
当ibpb_enabled 设置为1的时候,IBPB barrier会在guest mode或user mode(用户态)的上下文切换的时候去刷新间接分支预测器中的内容,以阻止同主机上的其他虚拟机攻击或是同主机上的其他进程攻击。
如果CPUID中支持IBPB_SUPPORT 或者 SPEC_CTRL的话,那么可以通过控制PRED_CMD MSR来控制IBPB功能。
*/
case CPUMCPUIDFEATURE_SPEC_CTRL:
//暂时只支持INTEL CPU
if (pVM->cpum.s.GuestFeatures.enmCpuVendor == CPUMCPUVENDOR_INTEL)
{
//CPUID(7)
pLeaf = cpumR3CpuIdGetExactLeaf(&pVM->cpum.s, UINT32_C(0x00000007), 0);
//需要host支持ibpb和ibrs
if ( !pLeaf
|| !(pVM->cpum.s.HostFeatures.fIbpb || pVM->cpum.s.HostFeatures.fIbrs))
{
return;
}
pVM->cpum.s.GuestFeatures.fSpeculationControl = 1;
if (pVM->cpum.s.HostFeatures.fIbrs)
{
//如果host支持ibrs。设置对应CPUID(7)bit位
pLeaf->uEdx |= X86_CPUID_STEXT_FEATURE_EDX_IBRS_IBPB;
pVM->cpum.s.GuestFeatures.fIbrs = 1;
//stibp位: allow software to set IA32_SPEC_CTRL MSR
if (pVM->cpum.s.HostFeatures.fStibp)
{
pLeaf->uEdx |= X86_CPUID_STEXT_FEATURE_EDX_STIBP;
pVM->cpum.s.GuestFeatures.fStibp = 1;
}
//设置MSR_IA32_SPEC_CTRL
pMsrRange = cpumLookupMsrRange(pVM, MSR_IA32_SPEC_CTRL);
if (!pMsrRange)
{
static CPUMMSRRANGE const s_SpecCtrl =
{
/*.uFirst =*/ MSR_IA32_SPEC_CTRL, /*.uLast =*/ MSR_IA32_SPEC_CTRL,
/*.enmRdFn =*/ kCpumMsrRdFn_Ia32SpecCtrl, /*.enmWrFn =*/ kCpumMsrWrFn_Ia32SpecCtrl,
/*.offCpumCpu =*/ UINT16_MAX, /*.fReserved =*/ 0, /*.uValue =*/ 0, /*.fWrIgnMask =*/ 0, /*.fWrGpMask =*/ 0,
/*.szName = */ "IA32_SPEC_CTRL"
};
CPUMR3MsrRangesInsert(pVM, &s_SpecCtrl);
}
//设置MSR_IA32_PRED_CMD msr leaf
pMsrRange = cpumLookupMsrRange(pVM, MSR_IA32_PRED_CMD);
if (!pMsrRange)
{
static CPUMMSRRANGE const s_SpecCtrl =
{
/*.uFirst =*/ MSR_IA32_PRED_CMD, /*.uLast =*/ MSR_IA32_PRED_CMD,
/*.enmRdFn =*/ kCpumMsrRdFn_WriteOnly, /*.enmWrFn =*/ kCpumMsrWrFn_Ia32PredCmd,
/*.offCpumCpu =*/ UINT16_MAX, /*.fReserved =*/ 0, /*.uValue =*/ 0, /*.fWrIgnMask =*/ 0, /*.fWrGpMask =*/ 0,
/*.szName = */ "IA32_PRED_CMD"
};
CPUMR3MsrRangesInsert(pVM, &s_SpecCtrl);
}
}
}
//设置MSR_IA32_ARCH_CAPABILITIES
if (pVM->cpum.s.HostFeatures.fArchCap)
{
pMsrRange = cpumLookupMsrRange(pVM, MSR_IA32_ARCH_CAPABILITIES);
if (!pMsrRange)
{
static CPUMMSRRANGE const s_ArchCaps =
{
/*.uFirst =*/ MSR_IA32_ARCH_CAPABILITIES, /*.uLast =*/ MSR_IA32_ARCH_CAPABILITIES,
/*.enmRdFn =*/ kCpumMsrRdFn_Ia32ArchCapabilities, /*.enmWrFn =*/ kCpumMsrWrFn_ReadOnly,
/*.offCpumCpu =*/ UINT16_MAX, /*.fReserved =*/ 0, /*.uValue =*/ 0, /*.fWrIgnMask =*/ 0, /*.fWrGpMask =*/ UINT64_MAX,
/*.szName = */ "IA32_ARCH_CAPABILITIES"
};
CPUMR3MsrRangesInsert(pVM, &s_ArchCaps);
}
}
}
//设置CPUID change flag
for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
{
PVMCPU pVCpu = pVM->apCpusR3[idCpu];
pVCpu->cpum.s.fChanged |= CPUM_CHANGED_CPUID;
}
}
speculation control:详细可以参考下面的链接
https://software.intel.com/security-software-guidance/insights/deep-dive-cpuid-enumeration-and-architectural-msrs
CPUMR3ClearGuestCpuIdFeature
CPUMR3SetGuestCpuIdFeature的相反函数
11.4 VMExit里模拟CPUID
上面看到一列函数初始化CPUID leaves 数组,当GuestOS里调用CPUID指令的时候,是怎么返回这些CPUID值的呢?
在之前的文章中介绍了GuestOS里调用CPUID的时候会触发VMExit事件,hmR0VmxExitCpuid->IEMExecDecodedCpuid->iemCImpl_cpuid 最终调用IEM里的CPUID模拟执行函数
iemCImpl_cpuid
IEM_CIMPL_DEF_0(iemCImpl_cpuid)
{
//如果当前代码运行在non-root模式,返回
if (IEM_VMX_IS_NON_ROOT_MODE(pVCpu))
{
IEM_VMX_VMEXIT_INSTR_RET(pVCpu, VMX_EXIT_CPUID, cbInstr);
}
if (IEM_SVM_IS_CTRL_INTERCEPT_SET(pVCpu, SVM_CTRL_INTERCEPT_CPUID))
{
IEM_SVM_UPDATE_NRIP(pVCpu);
IEM_SVM_VMEXIT_RET(pVCpu, SVM_EXIT_CPUID, 0 /* uExitInfo1 */, 0 /* uExitInfo2 */);
}
//获取CPUID
CPUMGetGuestCpuId(pVCpu, pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.ecx,
&pVCpu->cpum.GstCtx.eax, &pVCpu->cpum.GstCtx.ebx, &pVCpu->cpum.GstCtx.ecx, &pVCpu->cpum.GstCtx.edx);
//高位清零
pVCpu->cpum.GstCtx.rax &= UINT32_C(0xffffffff);
pVCpu->cpum.GstCtx.rbx &= UINT32_C(0xffffffff);
pVCpu->cpum.GstCtx.rcx &= UINT32_C(0xffffffff);
pVCpu->cpum.GstCtx.rdx &= UINT32_C(0xffffffff);
pVCpu->cpum.GstCtx.fExtrn &= ~(CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDX | CPUMCTX_EXTRN_RBX);
iemRegAddToRipAndClearRF(pVCpu, cbInstr);
return VINF_SUCCESS;
}
CPUMGetGuestCpuId
VMMDECL(void) CPUMGetGuestCpuId(PVMCPUCC pVCpu, uint32_t uLeaf, uint32_t uSubLeaf,
uint32_t *pEax, uint32_t *pEbx, uint32_t *pEcx, uint32_t *pEdx)
{
//根据uLeaf和uSubLeaf从CPUID leaves数组里获取对应的CPUID leaf, fExactSubLeafHit表示是否找到exact match的节点
PCCPUMCPUIDLEAF pLeaf = cpumCpuIdGetLeafEx(pVM, uLeaf, uSubLeaf, &fExactSubLeafHit);
if (pLeaf)
{
if (fExactSubLeafHit)
{
//精确找到了对应的leaf,读取4个寄存器即可
*pEax = pLeaf->uEax;
*pEbx = pLeaf->uEbx;
*pEcx = pLeaf->uEcx;
*pEdx = pLeaf->uEdx;
//剩下一些需要特殊处理的,因为GuestOS 的APIC是虚拟APIC,所以需要特殊处理
if (pLeaf->fFlags & ( CPUMCPUIDLEAF_F_CONTAINS_APIC_ID
| CPUMCPUIDLEAF_F_CONTAINS_OSXSAVE
| CPUMCPUIDLEAF_F_CONTAINS_APIC ))
{
if (uLeaf == 1)
{
//特殊处理APIC ID
*pEbx = (pLeaf->uEbx & UINT32_C(0x00ffffff)) | (pVCpu->idCpu << 24);
//根据CPUFeature,修改相应bit位
if (!pVCpu->cpum.s.fCpuIdApicFeatureVisible && (pLeaf->fFlags & CPUMCPUIDLEAF_F_CONTAINS_APIC))
*pEdx &= ~X86_CPUID_FEATURE_EDX_APIC;
//根据pVCpu->cpum.s.Guest.cr4设置X86_CPUID_FEATURE_ECX_OSXSAVEbit位
*pEcx = (pLeaf->uEcx & ~X86_CPUID_FEATURE_ECX_OSXSAVE)
| (pVCpu->cpum.s.Guest.cr4 & X86_CR4_OSXSAVE ? X86_CPUID_FEATURE_ECX_OSXSAVE : 0);
}
else if (uLeaf == 0xb)
{
//修改APIC ID
*pEdx = pVCpu->idCpu;
}
else if (uLeaf == UINT32_C(0x8000001e))
{
//AMD CPU的APIC ID
*pEax = pVCpu->idCpu;
}
else if (uLeaf == UINT32_C(0x80000001))
{
//AMD CPU:APIC 是否开启
if (!pVCpu->cpum.s.fCpuIdApicFeatureVisible)
*pEdx &= ~X86_CPUID_AMD_FEATURE_EDX_APIC;
}
}
else
{
//感觉这边也不太知道要怎么处理
*pEax = *pEbx = *pEcx = *pEdx = 0;
if (pLeaf->fFlags & CPUMCPUIDLEAF_F_INTEL_TOPOLOGY_SUBLEAVES)
{
*pEcx = uSubLeaf & 0xff;
*pEdx = pVCpu->idCpu;
}
}
}
else
{
//没有找到对应的leaf, 根据enmUnknownCpuIdMethod里的设置不同处理
switch (pVM->cpum.s.GuestInfo.enmUnknownCpuIdMethod)
{
default:
AssertFailed();
RT_FALL_THRU();
//DefCpuId返回DefCpuId里的值
case CPUMUNKNOWNCPUID_DEFAULTS:
case CPUMUNKNOWNCPUID_LAST_STD_LEAF: /* ASSUME this is executed */
case CPUMUNKNOWNCPUID_LAST_STD_LEAF_WITH_ECX:
*pEax = pVM->cpum.s.GuestInfo.DefCpuId.uEax;
*pEbx = pVM->cpum.s.GuestInfo.DefCpuId.uEbx;
*pEcx = pVM->cpum.s.GuestInfo.DefCpuId.uEcx;
*pEdx = pVM->cpum.s.GuestInfo.DefCpuId.uEdx;
break;
//返回原始的输入
case CPUMUNKNOWNCPUID_PASSTHRU:
*pEax = uLeaf;
*pEbx = 0;
*pEcx = uSubLeaf;
*pEdx = 0;
break;
}
}
}
11.5 虚拟机保存/加载的callback函数
cpumR3SaveCpuId
void cpumR3SaveCpuId(PVM pVM, PSSMHANDLE pSSM)
{
//save CPUID leaves
//每个leaf的大小
SSMR3PutU32(pSSM, sizeof(pVM->cpum.s.GuestInfo.paCpuIdLeavesR3[0]));
//一共有多少个leave是
SSMR3PutU32(pSSM, pVM->cpum.s.GuestInfo.cCpuIdLeaves);
//把真块CPUID leaves内存保存起来
SSMR3PutMem(pSSM, pVM->cpum.s.GuestInfo.paCpuIdLeavesR3,
sizeof(pVM->cpum.s.GuestInfo.paCpuIdLeavesR3[0]) * pVM->cpum.s.GuestInfo.cCpuIdLeaves);
//保存CPUMCPUID default CPUID
SSMR3PutMem(pSSM, &pVM->cpum.s.GuestInfo.DefCpuId, sizeof(pVM->cpum.s.GuestInfo.DefCpuId));
//保存CPUID (0~F)和 CPUID(80000000到8000001F)的原始返回值,用于检查恢复后的值是否正确
CPUMCPUID aRawStd[16];
for (unsigned i = 0; i < RT_ELEMENTS(aRawStd); i++)
ASMCpuIdExSlow(i, 0, 0, 0, &aRawStd[i].uEax, &aRawStd[i].uEbx, &aRawStd[i].uEcx, &aRawStd[i].uEdx);
SSMR3PutU32(pSSM, RT_ELEMENTS(aRawStd));
SSMR3PutMem(pSSM, &aRawStd[0], sizeof(aRawStd));
CPUMCPUID aRawExt[32];
for (unsigned i = 0; i < RT_ELEMENTS(aRawExt); i++)
ASMCpuIdExSlow(i | UINT32_C(0x80000000), 0, 0, 0, &aRawExt[i].uEax, &aRawExt[i].uEbx, &aRawExt[i].uEcx, &aRawExt[i].uEdx);
SSMR3PutU32(pSSM, RT_ELEMENTS(aRawExt));
SSMR3PutMem(pSSM, &aRawExt[0], sizeof(aRawExt));
}
cpumR3LoadCpuId
int cpumR3LoadCpuId(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, PCCPUMMSRS pMsrs)
{
//从SSM里恢复CPUID leaves array
int rc = cpumR3LoadGuestCpuIdArray(pVM, pSSM, uVersion, &paLeaves, &cLeaves);
if (RT_SUCCESS(rc))
{
//恢复CPUID leaves里的数据
rc = cpumR3LoadCpuIdInner(pVM, pSSM, uVersion, paLeaves, cLeaves, pMsrs);
RTMemFree(paLeaves);
}
}
//从SSM里恢复CPUID leaves 数组,并保存在normal heap里
static int cpumR3LoadGuestCpuIdArray(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, PCPUMCPUIDLEAF *ppaLeaves, uint32_t *pcLeaves)
{
if (uVersion > CPUM_SAVED_STATE_VERSION_PUT_STRUCT)
{
//获取leaf个数和大小
SSMR3GetU32(pSSM, &cbLeaf);
SSMR3GetU32(pSSM, &cLeaves);
uint32_t uPrev = 0;
//对每个leaf
for (uint32_t i = 0; i < cLeaves && RT_SUCCESS(rc); i++)
{
CPUMCPUIDLEAF Leaf;
//从SSM里恢复一个Leaf
rc = SSMR3GetMem(pSSM, &Leaf, sizeof(Leaf));
if (RT_SUCCESS(rc))
{
//CPUID值必须大于等于前一个leaf的CPUID值
if ( uVersion != CPUM_SAVED_STATE_VERSION_BAD_CPUID_COUNT
|| Leaf.uLeaf >= uPrev)
{
//insert CPUID leaves 数组里
rc = cpumR3CpuIdInsert(NULL /* pVM */, ppaLeaves, pcLeaves, &Leaf);
uPrev = Leaf.uLeaf;
}
else
uPrev = UINT32_MAX;
}
}
}
}
//检查CPUID的值是否正确,如果正确,提交到hyper heap 内存里
int cpumR3LoadCpuIdInner(PVM pVM, PSSMHANDLE pSSM, uint32_t uVersion, PCPUMCPUIDLEAF paLeaves, uint32_t cLeaves, PCCPUMMSRS pMsrs)
{
//default CPUID
CPUMCPUID GuestDefCpuId;
int rc = SSMR3GetMem(pSSM, &GuestDefCpuId, sizeof(GuestDefCpuId));
//恢复保存的CPUID (0~F)和 CPUID(80000000到8000001F)的原始返回值
CPUMCPUID aRawStd[16];
uint32_t cRawStd;
rc = SSMR3GetU32(pSSM, &cRawStd);
rc = SSMR3GetMem(pSSM, &aRawStd[0], cRawStd * sizeof(aRawStd[0]));
//如果没有保存,则从CPUID里获取
for (uint32_t i = cRawStd; i < RT_ELEMENTS(aRawStd); i++)
ASMCpuIdExSlow(i, 0, 0, 0, &aRawStd[i].uEax, &aRawStd[i].uEbx, &aRawStd[i].uEcx, &aRawStd[i].uEdx);
CPUMCPUID aRawExt[32];
uint32_t cRawExt;
rc = SSMR3GetU32(pSSM, &cRawExt); AssertRCReturn(rc, rc);
rc = SSMR3GetMem(pSSM, &aRawExt[0], cRawExt * sizeof(aRawExt[0]));
//如果没有保存,则从CPUID里获取
for (uint32_t i = cRawExt; i < RT_ELEMENTS(aRawExt); i++)
ASMCpuIdExSlow(i | UINT32_C(0x80000000), 0, 0, 0, &aRawExt[i].uEax, &aRawExt[i].uEbx, &aRawExt[i].uEcx, &aRawExt[i].uEdx);
//从配置文件里恢复部分CPUID leaves
PCFGMNODE pOverrideCfg = CFGMR3GetChild(CFGMR3GetRoot(pVM), "CPUM/HostCPUID");
CPUMCPUID aHostOverrideStd[2];
memcpy(&aHostOverrideStd[0], &aHostRawStd[0], sizeof(aHostOverrideStd));
cpumR3CpuIdInitLoadOverrideSet(UINT32_C(0x00000000), &aHostOverrideStd[0], RT_ELEMENTS(aHostOverrideStd), pOverrideCfg);
CPUMCPUID aHostOverrideExt[2];
memcpy(&aHostOverrideExt[0], &aHostRawExt[0], sizeof(aHostOverrideExt));
cpumR3CpuIdInitLoadOverrideSet(UINT32_C(0x80000000), &aHostOverrideExt[0], RT_ELEMENTS(aHostOverrideExt), pOverrideCfg);
//检查恢复到CPUID leaves是否正确, Guest CPU和Host CPU的feature是否能匹配,和CPUM里恢复的值是否匹配等
/* CPUID(1).ecx */
CPUID_GST_FEATURE_RET(Std, uEcx, X86_CPUID_FEATURE_ECX_SSE3);
CPUID_GST_FEATURE_RET(Std, uEcx, X86_CPUID_FEATURE_ECX_PCLMUL);
CPUID_GST_FEATURE_RET(Std, uEcx, X86_CPUID_FEATURE_ECX_DTES64);
。。。
//CPUID leaves恢复正确,拷贝到hypervisor heap内存里
//hypervisor heap 是 MM (memory manager)申请的内存
MMHyperFree(pVM, pVM->cpum.s.GuestInfo.paCpuIdLeavesR3);
pVM->cpum.s.GuestInfo.paCpuIdLeavesR3 = NULL;
pVM->cpum.s.GuestInfo.paCpuIdLeavesR0 = NIL_RTR0PTR;
pVM->cpum.s.GuestInfo.DefCpuId = GuestDefCpuId;
rc = cpumR3CpuIdInstallAndExplodeLeaves(pVM, &pVM->cpum.s, paLeaves, cLeaves, pMsrs);
}
static int cpumR3CpuIdInstallAndExplodeLeaves(PVM pVM, PCPUM pCpum, PCPUMCPUIDLEAF paLeaves, uint32_t cLeaves, PCCPUMMSRS pMsrs)
{
//申请一块hyper Mem,然后把paLeaves里的值拷贝到新的内存里,新的指针地址保存到pCpum->GuestInfo.paCpuIdLeavesR3里
int rc = MMHyperDupMem(pVM, paLeaves, sizeof(paLeaves[0]) * cLeaves, 32,
MM_TAG_CPUM_CPUID, (void **)&pCpum->GuestInfo.paCpuIdLeavesR3);
pCpum->GuestInfo.cCpuIdLeaves = cLeaves;
pCpum->GuestInfo.paCpuIdLeavesR0 = MMHyperR3ToR0(pVM, pCpum->GuestInfo.paCpuIdLeavesR3);
//根据CPU Leaves里的数据,初始化pCpum->GuestFeatures,和GuestMsrs里的部分值
cpumR3CpuIdExplodeFeatures(pCpum->GuestInfo.paCpuIdLeavesR3, pCpum->GuestInfo.cCpuIdLeaves, pMsrs,
&pCpum->GuestFeatures);
//部分寄存器,从真机中获取并保存在pCpum的局部变量里 (好像是用于检查用?)
struct { PCPUMCPUID paCpuIds; uint32_t cCpuIds, uBase; } aOldRanges[] =
{
{ pCpum->aGuestCpuIdPatmStd, RT_ELEMENTS(pCpum->aGuestCpuIdPatmStd), 0x00000000 },
{ pCpum->aGuestCpuIdPatmExt, RT_ELEMENTS(pCpum->aGuestCpuIdPatmExt), 0x80000000 },
{ pCpum->aGuestCpuIdPatmCentaur, RT_ELEMENTS(pCpum->aGuestCpuIdPatmCentaur), 0xc0000000 },
};
for (uint32_t i = 0; i < RT_ELEMENTS(aOldRanges); i++)
{
uint32_t cLeft = aOldRanges[i].cCpuIds;
uint32_t uLeaf = aOldRanges[i].uBase + cLeft;
PCPUMCPUID pLegacyLeaf = &aOldRanges[i].paCpuIds[cLeft];
while (cLeft-- > 0)
{
uLeaf--;
pLegacyLeaf--;
PCCPUMCPUIDLEAF pLeaf = cpumR3CpuIdGetExactLeaf(pCpum, uLeaf, 0 /* uSubLeaf */);
if (pLeaf)
{
pLegacyLeaf->uEax = pLeaf->uEax;
pLegacyLeaf->uEbx = pLeaf->uEbx;
pLegacyLeaf->uEcx = pLeaf->uEcx;
pLegacyLeaf->uEdx = pLeaf->uEdx;
}
else
*pLegacyLeaf = pCpum->GuestInfo.DefCpuId;
}
}
//根据CPUID的返回值设置具体寄存器在X86XSAVEAREA里的偏移
//详情可见X86XSAVEAREA定义(include\iprt\x64.h)文件里
//先设置VCPU0
PVMCPU pVCpu0 = pVM->apCpusR3[0];
memset(&pVCpu0->cpum.s.Guest.aoffXState[0], 0xff, sizeof(pVCpu0->cpum.s.Guest.aoffXState));
//x87 和SSE在XSAVEAREA的第一个结构体X86FXSTATE里
pVCpu0->cpum.s.Guest.aoffXState[XSAVE_C_X87_BIT] = 0;
pVCpu0->cpum.s.Guest.aoffXState[XSAVE_C_SSE_BIT] = 0;
//从第二位开始,获取subleaf 的 ebx项,获取对应寄存器在XSAVE内存中的偏移
for (uint32_t iComponent = XSAVE_C_SSE_BIT + 1; iComponent < 63; iComponent++)
if (pCpum->fXStateGuestMask & RT_BIT_64(iComponent))
{
PCPUMCPUIDLEAF pSubLeaf = cpumR3CpuIdGetExactLeaf(pCpum, 0xd, iComponent);
pVCpu0->cpum.s.Guest.aoffXState[iComponent] = pSubLeaf->uEbx;
}
//其他几个VCPU拷贝VCPU0里的值即可
for (VMCPUID idCpu = 1; idCpu < pVM->cCpus; idCpu++)
{
PVMCPU pVCpu = pVM->apCpusR3[idCpu];
memcpy(&pVCpu->cpum.s.Guest.aoffXState[0], &pVCpu0->cpum.s.Guest.aoffXState[0], sizeof(pVCpu0->cpum.s.Guest.aoffXState));
}
}
11.6 Guest寄存器访问
CPUMAllRegs.cpp里同时提供了一系列getter/setter读取和写入Guest Regs的API,直接操作pVCpu->cpum.s.Guest里的寄存器
VMMDECL(int) CPUMGetGuestCRx(PCVMCPUCC pVCpu, unsigned iReg, uint64_t *pValue);
VMMDECL(uint32_t) CPUMGetGuestEFlags(PCVMCPU pVCpu);
VMMDECL(uint32_t) CPUMGetGuestEIP(PCVMCPU pVCpu);
VMMDECL(int) CPUMSetGuestTR(PVMCPU pVCpu, uint16_t tr);
VMMDECL(int) CPUMSetGuestLDTR(PVMCPU pVCpu, uint16_t ldtr);
...
一系列API获取Host/Guest CPU信息
VMMDECL(CPUMCPUVENDOR) CPUMGetGuestCpuVendor(PVM pVM);
VMMDECL(CPUMMICROARCH) CPUMGetGuestMicroarch(PCVM pVM);
VMMDECL(CPUMCPUVENDOR) CPUMGetHostCpuVendor(PVM pVM);
VMMDECL(CPUMMICROARCH) CPUMGetHostMicroarch(PCVM pVM);
VMMDECL(bool) CPUMIsGuestIn64BitCode(PVMCPU pVCpu);
VMMDECL(bool) CPUMIsGuestNXEnabled(PCVMCPU pVCpu);
....
参考资料
https://www.amd.com/system/files/TechDocs/25481.pdf
https://software.intel.com/security-software-guidance/insights/deep-dive-cpuid-enumeration-and-architectural-msrs
Intel指令手册