Virtualbox源码分析12 CPU manager4:MSR


上一章里介绍了CPUID的初始化和读取,这一章里,介绍模拟MSR寄存器的初始化和读写操作

12.1 MSR Ranges 初始化

g_apCpumDbEntries里定义了VMMR3\cpus目录下所有已知的CPU列表,这些.h文件里定义了每个CPU的所有MSR寄存器列表

static CPUMDBENTRY const * const g_apCpumDbEntries[] =
{
	&g_Entry_Intel_Core_i7_6700K,
	&g_Entry_Intel_Core_i7_5600U,
	....
}

比如Intel I7-5600U CPU里定义了200多个MSR寄存器

static CPUMMSRRANGE const g_aMsrRanges_Intel_Core_i7_5600U[] =
{
    MFX(0x00000000, "IA32_P5_MC_ADDR", Ia32P5McAddr, Ia32P5McAddr, 0, UINT64_C(0xffffffffffffff00), 0), /* value=0xff */
    MFX(0x00000001, "IA32_P5_MC_TYPE", Ia32P5McType, Ia32P5McType, 0, 0, UINT64_MAX), /* value=0x0 */
    ...
}

cpumR3DbGetCpuInfo

这个函数在cpumR3InitCpuIdAndMsrs() 里调用, 根据CPU类型,初始化MSR Range

int cpumR3DbGetCpuInfo(const char *pszName, PCPUMINFO pInfo)
{
  //如果通过“host” CPU信息决定VCPU型号
  if (!strcmp(pszName, "host"))
  {
    //根据host CPUID里的信息获取当前CPU类型信息
    CPUMCPUVENDOR const enmVendor    = CPUMR3CpuIdDetectVendorEx(pInfo->paCpuIdLeavesR3[0].uEax,
                                                                       pInfo->paCpuIdLeavesR3[0].uEbx,
                                                                       pInfo->paCpuIdLeavesR3[0].uEcx,
                                                                       pInfo->paCpuIdLeavesR3[0].uEdx);
    uint8_t       const uFamily      = ASMGetCpuFamily(uStd1Eax);
    uint8_t       const uModel       = ASMGetCpuModel(uStd1Eax, enmVendor == CPUMCPUVENDOR_INTEL);
    uint8_t       const uStepping    = ASMGetCpuStepping(uStd1Eax);
    CPUMMICROARCH const enmMicroarch = CPUMR3CpuIdDetermineMicroarchEx(enmVendor, uFamily, uModel, uStepping);

    //根据host 信息从g_apCpumDbEntries找到匹配的CPU型号,可以模糊匹配
    for (unsigned i = 0; i < RT_ELEMENTS(g_apCpumDbEntries); i++)
    {
      CPUMDBENTRY const *pCur = g_apCpumDbEntries[i];
      if ((CPUMCPUVENDOR)pCur->enmVendor == enmVendor)
      {
        if (pCur->uFamily == uFamily)
        {
          if (pCur->enmMicroarch == enmMicroarch)
          {
            if (pCur->uModel == uModel)
            {
              if (pCur->uStepping == uStepping)
              {
                /* Perfect match. */
                pEntry = pCur;
                break;
              }
              //下面都是模糊匹配
              if (   !pEntry
                  || pEntry->uModel       != uModel
                  || pEntry->enmMicroarch != enmMicroarch
                  || pEntry->uFamily      != uFamily)
                //当前CPU比之前找到的CPU型号更匹配,替换pEntry
                pEntry = pCur;
              else if (  pCur->uStepping >= uStepping
                       ? pCur->uStepping < pEntry->uStepping || pEntry->uStepping < uStepping
                       : pCur->uStepping > pEntry->uStepping)
                //当前uStepping值更接近,替换pEntry
                pEntry = pCur;
            }
            else if (   !pEntry
                     || pEntry->enmMicroarch != enmMicroarch
                     || pEntry->uFamily      != uFamily)
              pEntry = pCur;
            else if (  pCur->uModel >= uModel
                     ? pCur->uModel < pEntry->uModel || pEntry->uModel < uModel
                     : pCur->uModel > pEntry->uModel)
              //uModel值更接近,替换pEntry
              pEntry = pCur;
          }
          else if (   !pEntry
                   || pEntry->uFamily != uFamily)
            pEntry = pCur;
          /* Special march matching rules applies to intel family 06h. */
          else if (     enmVendor == CPUMCPUVENDOR_INTEL
                   && uFamily   == 6
                   ? cpumR3DbIsBetterIntelFam06Match(pCur->enmMicroarch, enmMicroarch, pEntry->enmMicroarch)
                   : cpumR3DbIsBetterMarchMatch(pCur->enmMicroarch, enmMicroarch, pEntry->enmMicroarch))
            pEntry = pCur;
        }
        //如果模糊匹配都没匹配到,返回第一个CPU信息,这个分支只有第一个循环才可能走到
        else if (!pEntry)
          pEntry = pCur;
      }
    }
  }
  else
  {
    //通过CPU名字来决定VCPU型号
    for (unsigned i = 0; i < RT_ELEMENTS(g_apCpumDbEntries); i++)
      if (!strcmp(pszName, g_apCpumDbEntries[i]->pszName))
      {
        pEntry = g_apCpumDbEntries[i];
        break;
      }
	}
  //获取到了VCPU型号,根据对应CPU头文件里值,创建MsrRange列表
  uint32_t        cMsrs   = 0;
  PCPUMMSRRANGE   paMsrs  = NULL;
  PCCPUMMSRRANGE  pCurMsr = pEntry->paMsrRanges;
  uint32_t        cLeft   = pEntry->cMsrRanges;
  while (cLeft-- > 0)
  {
    //调用cpumR3MsrRangesInsert一个一个MSR寄存器信息加入到数组中
    rc = cpumR3MsrRangesInsert(NULL /* pVM */, &paMsrs, &cMsrs, pCurMsr);
    if (RT_FAILURE(rc))
    {
      RTMemFree(pInfo->paCpuIdLeavesR3);
      pInfo->paCpuIdLeavesR3 = NULL;
      return rc;
    }
    pCurMsr++;
  }
  //更新CPUMINFO里的变量
  pInfo->paMsrRangesR3   = paMsrs;
  pInfo->cMsrRanges      = cMsrs;
  return VINF_SUCCESS;
}

MSR Ranges 是一个CPUMMSRRANGE数组,而且是按照MSR id从小到大排序后的数组。

CPUMMSRRANGE 里都分别有一个uFirst 和 uLast, 而且:

1)paMsrRanges[i-1].ulast < paMsrRanges[i].uFirst

2)paMsrRanges[i].ufirst <= paMsrRanges[i].ulast

3)paMsrRanges[i].ulast < paMsrRanges[i+1].ufirst

cpumR3MsrRangesInsert

加入一项到MSRRange里

VMMR3DECL(int) CPUMR3MsrRangesInsert(PVM pVM, PCCPUMMSRRANGE pNewRange)
{
	cpumR3MsrRangesInsert(pVM, NULL /* ppaMsrRanges */, NULL /* pcMsrRanges */, pNewRange);
}
//insert pNewRange into MsrRange
int cpumR3MsrRangesInsert(PVM pVM, PCPUMMSRRANGE *ppaMsrRanges, uint32_t *pcMsrRanges, PCCPUMMSRRANGE pNewRange)
{
  //如果当前Msr值大于MsrRange中最后一项的MSR值,这项不在MsrRange中
  if (   cMsrRanges > 0
        && paMsrRanges[cMsrRanges - 1].uLast < pNewRange->uFirst)
  {
    //申请内存
    paMsrRanges = cpumR3MsrRangesEnsureSpace(pVM, ppaMsrRanges, cMsrRanges, 1);
    if (!paMsrRanges)
      return VERR_NO_MEMORY;
    //加入到最后一项中
    paMsrRanges[cMsrRanges] = *pNewRange;
    *pcMsrRanges += 1;
  }
  else
  {
    //通过binary search找到对应的MsrRange节点,或者最近的一个MsrRange节点
    uint32_t i = cpumR3MsrRangesBinSearch(paMsrRanges, cMsrRanges, pNewRange->uFirst);
    //新节点
    if (   i >= cMsrRanges
            || pNewRange->uLast < paMsrRanges[i].uFirst)
    {
      //申请内存
      paMsrRanges = cpumR3MsrRangesEnsureSpace(pVM, ppaMsrRanges, cMsrRanges, 1);
      if (!paMsrRanges)
        return VERR_NO_MEMORY;
      //后面的节点全部后移一位
      if (i < cMsrRanges)
        memmove(&paMsrRanges[i + 1], &paMsrRanges[i], (cMsrRanges - i) * sizeof(paMsrRanges[0]));
      //pNewRange加入新节点
      paMsrRanges[i] = *pNewRange;
      *pcMsrRanges += 1;
    }
    //替换已有节点
    else if (   pNewRange->uFirst == paMsrRanges[i].uFirst
                 && pNewRange->uLast  == paMsrRanges[i].uLast)
      paMsrRanges[i] = *pNewRange;
    //新的节点在老的节点中间,老的节点分成两个,然后中间插入新的节点,MSRRange会增加两个节点
    else if (   pNewRange->uFirst > paMsrRanges[i].uFirst
                 && pNewRange->uLast  < paMsrRanges[i].uLast)
    {
      paMsrRanges = cpumR3MsrRangesEnsureSpace(pVM, ppaMsrRanges, cMsrRanges, 2);
      if (!paMsrRanges)
        return VERR_NO_MEMORY;
      if (i < cMsrRanges)
        memmove(&paMsrRanges[i + 2], &paMsrRanges[i], (cMsrRanges - i) * sizeof(paMsrRanges[0]));
      paMsrRanges[i + 1] = *pNewRange;
      paMsrRanges[i + 2] = paMsrRanges[i];
      paMsrRanges[i    ].uLast  = pNewRange->uFirst - 1;
      paMsrRanges[i + 2].uFirst = pNewRange->uLast  + 1;
      *pcMsrRanges += 2;
    }
    else
    {
      //其他的各种情况,最终保证pNewRange加入到MSRRange里而且MSRRange是排好序的。
      ....
    }
  }
}

cpumR3MsrReconcileWithCpuId

根据CPUID信息初始化部分MSRRange,因为microcode update或者CPU配置,会改变部分MSR特性,这个函数在cpumR3InitCpuIdAndMsrs() 里调用。

int cpumR3MsrReconcileWithCpuId(PVM pVM)
{
	//IA32_FLUSH_CMD
  if (pVM->cpum.s.GuestFeatures.fFlushCmd && !cpumLookupMsrRange(pVM, MSR_IA32_FLUSH_CMD))
  {
    static CPUMMSRRANGE const s_FlushCmd =
    {
      /*.uFirst =*/       MSR_IA32_FLUSH_CMD,
      /*.uLast =*/        MSR_IA32_FLUSH_CMD,
      /*.enmRdFn =*/      kCpumMsrRdFn_WriteOnly,
      /*.enmWrFn =*/      kCpumMsrWrFn_Ia32FlushCmd,
      /*.offCpumCpu =*/   UINT16_MAX,
      /*.fReserved =*/    0,
      /*.uValue =*/       0,
      /*.fWrIgnMask =*/   0,
      /*.fWrGpMask =*/    ~MSR_IA32_FLUSH_CMD_F_L1D,
      /*.szName = */      "IA32_FLUSH_CMD"
    };
    papToAdd[cToAdd++] = &s_FlushCmd;
  }
  //MSR_IA32_ARCH_CAPABILITIES
  if (pVM->cpum.s.GuestFeatures.fArchCap && !cpumLookupMsrRange(pVM, MSR_IA32_ARCH_CAPABILITIES))
  {
    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"
    };
    papToAdd[cToAdd++] = &s_ArchCaps;
  }
  //加入MSR Range
  for (uint32_t i = 0; i < cToAdd; i++)
  {
    PCCPUMMSRRANGE pRange = papToAdd[i];
    int rc = cpumR3MsrRangesInsert(NULL /* pVM */, &pVM->cpum.s.GuestInfo.paMsrRangesR3, &pVM->cpum.s.GuestInfo.cMsrRanges,
                                   pRange);
  }
}

cpumR3MsrApplyFudge

为了能让虚拟机能在不同CPU机器上拷贝,需要对部分MSR寄存器做模糊化处理: 如果CPU没有对应MSRRange,设置一个模糊MSRRange,这个函数在cpumR3InitCpuIdAndMsrs() 里调用。

int cpumR3MsrApplyFudge(PVM pVM)
{
    /*
     * Basic.
     */
    static CPUMMSRRANGE const s_aFudgeMsrs[] =
    {
        MFO(0x00000000, "IA32_P5_MC_ADDR",          Ia32P5McAddr),
        MFX(0x00000001, "IA32_P5_MC_TYPE",          Ia32P5McType,   Ia32P5McType,   0, 0, UINT64_MAX),
        MVO(0x00000017, "IA32_PLATFORM_ID",         0),
        MFN(0x0000001b, "IA32_APIC_BASE",           Ia32ApicBase,   Ia32ApicBase),
        MVI(0x0000008b, "BIOS_SIGN",                0),
        MFX(0x000000fe, "IA32_MTRRCAP",             Ia32MtrrCap,    ReadOnly,       0x508, 0, 0),
        MFX(0x00000179, "IA32_MCG_CAP",             Ia32McgCap,     ReadOnly,       0x005, 0, 0),
        MFX(0x0000017a, "IA32_MCG_STATUS",          Ia32McgStatus,  Ia32McgStatus,  0, ~(uint64_t)UINT32_MAX, 0),
        MFN(0x000001a0, "IA32_MISC_ENABLE",         Ia32MiscEnable, Ia32MiscEnable),
        MFN(0x000001d9, "IA32_DEBUGCTL",            Ia32DebugCtl,   Ia32DebugCtl),
        MFO(0x000001db, "P6_LAST_BRANCH_FROM_IP",   P6LastBranchFromIp),
        MFO(0x000001dc, "P6_LAST_BRANCH_TO_IP",     P6LastBranchToIp),
        MFO(0x000001dd, "P6_LAST_INT_FROM_IP",      P6LastIntFromIp),
        MFO(0x000001de, "P6_LAST_INT_TO_IP",        P6LastIntToIp),
        MFS(0x00000277, "IA32_PAT",                 Ia32Pat, Ia32Pat, Guest.msrPAT),
        MFZ(0x000002ff, "IA32_MTRR_DEF_TYPE",       Ia32MtrrDefType, Ia32MtrrDefType, GuestMsrs.msr.MtrrDefType, 0, ~(uint64_t)0xc07),
        MFN(0x00000400, "IA32_MCi_CTL_STATUS_ADDR_MISC", Ia32McCtlStatusAddrMiscN, Ia32McCtlStatusAddrMiscN),
    };
    int rc = cpumR3MsrApplyFudgeTable(pVM, &s_aFudgeMsrs[0], RT_ELEMENTS(s_aFudgeMsrs));
    AssertLogRelRCReturn(rc, rc);

    /*
     * XP might mistake opterons and other newer CPUs for P4s.
     */
    if (pVM->cpum.s.GuestFeatures.uFamily >= 0xf)
    {
        static CPUMMSRRANGE const s_aP4FudgeMsrs[] =
        {
            MFX(0x0000002c, "P4_EBC_FREQUENCY_ID", IntelP4EbcFrequencyId, IntelP4EbcFrequencyId, 0xf12010f, UINT64_MAX, 0),
        };
        rc = cpumR3MsrApplyFudgeTable(pVM, &s_aP4FudgeMsrs[0], RT_ELEMENTS(s_aP4FudgeMsrs));
    }

    if (pVM->cpum.s.GuestFeatures.fRdTscP)
    {
        static CPUMMSRRANGE const s_aRdTscPFudgeMsrs[] =
        {
            MFX(0xc0000103, "AMD64_TSC_AUX", Amd64TscAux, Amd64TscAux, 0, 0, ~(uint64_t)UINT32_MAX),
        };
        rc = cpumR3MsrApplyFudgeTable(pVM, &s_aRdTscPFudgeMsrs[0], RT_ELEMENTS(s_aRdTscPFudgeMsrs));
    }

    return rc;
}

static int cpumR3MsrApplyFudgeTable(PVM pVM, PCCPUMMSRRANGE paRanges, size_t cRanges)
{
    for (uint32_t i = 0; i < cRanges; i++)
        //只有MsrRange里找不到,才会insert into MSRRange,而不会替换已有的MSRRange
        if (!cpumLookupMsrRange(pVM, paRanges[i].uFirst))
        {
            int rc = cpumR3MsrRangesInsert(NULL /* pVM */, &pVM->cpum.s.GuestInfo.paMsrRangesR3, &pVM->cpum.s.GuestInfo.cMsrRanges,
                                           &paRanges[i]);
        }
    return VINF_SUCCESS;
}

cpumR3LoadMsrOverrides

根据配置里的信息,覆盖部分MSR Range,这个函数在cpumR3InitCpuIdAndMsrs() 里调用。

static int cpumR3LoadMsrOverrides(PVM pVM, PCFGMNODE pMsrNode)
{
  //遍历每一个“MSRs”项的子config
	for (PCFGMNODE pNode = CFGMR3GetFirstChild(pMsrNode); pNode; pNode = CFGMR3GetNextChild(pNode))
  {
    //获取CPUMMSRRANGE里的每一项
    CPUMMSRRANGE MsrRange;
    MsrRange.offCpumCpu = 0;
    MsrRange.fReserved  = 0;
    int rc = CFGMR3GetName(pNode, MsrRange.szName, sizeof(MsrRange.szName));
    rc = CFGMR3QueryU32(pNode, "First", &MsrRange.uFirst);
    rc = CFGMR3QueryU32Def(pNode, "Last", &MsrRange.uLast, MsrRange.uFirst);
    char szType[32];
    rc = CFGMR3QueryStringDef(pNode, "Type", szType, sizeof(szType), "FixedValue");
    //Type一定要是FixedValue,否则返回错误
    if (!RTStrICmp(szType, "FixedValue"))
    {
      MsrRange.enmRdFn = kCpumMsrRdFn_FixedValue;
      MsrRange.enmWrFn = kCpumMsrWrFn_IgnoreWrite;
      rc = CFGMR3QueryU64Def(pNode, "Value", &MsrRange.uValue, 0);
      rc = CFGMR3QueryU64Def(pNode, "WrGpMask", &MsrRange.fWrGpMask, 0);
      rc = CFGMR3QueryU64Def(pNode, "WrIgnMask", &MsrRange.fWrIgnMask, 0);
    }
    else
      return VMSetError(pVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
                        "Invalid MSR entry '%s': Unknown type '%s'\n", MsrRange.szName, szType);

		//insert into MsrRange
    rc = cpumR3MsrRangesInsert(NULL /* pVM */, &pVM->cpum.s.GuestInfo.paMsrRangesR3, &pVM->cpum.s.GuestInfo.cMsrRanges,
                               &MsrRange);
  }
}

12.2 MSR寄存器访问

MSR read function table (VMM\VMMAll\CPUMALLMsrs.cpp)

static const PFNCPUMRDMSR g_aCpumRdMsrFns[kCpumMsrRdFn_End] =
{
    NULL, /* Invalid */
    cpumMsrRd_FixedValue,
    NULL, /* Alias */
    cpumMsrRd_WriteOnly,
    cpumMsrRd_Ia32P5McAddr,
    cpumMsrRd_Ia32P5McType,
}

MSR write function table

static const PFNCPUMWRMSR g_aCpumWrMsrFns[kCpumMsrWrFn_End] =
{
    NULL, /* Invalid */
    cpumMsrWr_IgnoreWrite,
    cpumMsrWr_ReadOnly,
    NULL, /* Alias */
    cpumMsrWr_Ia32P5McAddr,
    cpumMsrWr_Ia32P5McType,
    cpumMsrWr_Ia32TimestampCounter,
}

定义了一系列函数读取/写入Guest的MSR寄存器

//IA32_VMX_PINBASED_CTLS 读取
static DECLCALLBACK(VBOXSTRICTRC) cpumMsrRd_Ia32VmxPinbasedCtls(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue)
{
    *puValue = pVCpu->cpum.s.Guest.hwvirt.vmx.Msrs.PinCtls.u;
    return VINF_SUCCESS;
}
//IA32_SYSENTER_CS 读取
static DECLCALLBACK(VBOXSTRICTRC) cpumMsrRd_Ia32SysEnterCs(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t *puValue)
{
    *puValue = pVCpu->cpum.s.Guest.SysEnter.cs;
    return VINF_SUCCESS;
}
//IA32_SYSENTER_CS 写入
static DECLCALLBACK(VBOXSTRICTRC) cpumMsrWr_Ia32SysEnterCs(PVMCPUCC pVCpu, uint32_t idMsr, PCCPUMMSRRANGE pRange, uint64_t uValue, uint64_t uRawValue)
{
    pVCpu->cpum.s.Guest.SysEnter.cs = uValue;
    return VINF_SUCCESS;
}
...

CPUMQueryGuestMsr

VMMDECL(VBOXSTRICTRC) CPUMQueryGuestMsr(PVMCPU pVCpu, uint32_t idMsr, uint64_t *puValue)
{
    //idMsr binary search 找到对应pRange
    PCPUMMSRRANGE   pRange = cpumLookupMsrRange(pVM, idMsr);
    if(pRange)
    {
      CPUMMSRRDFN  enmRdFn = (CPUMMSRRDFN)pRange->enmRdFn;
      //调用对应msr table里的function
      PFNCPUMRDMSR pfnRdMsr = g_aCpumRdMsrFns[enmRdFn];
      pfnRdMsr(pVCpu, idMsr, pRange, puValue);
    }
    else 
    {
      rcStrict = VERR_CPUM_RAISE_GP_0;
    }
}

CPUMSetGuestMsr

VMMDECL(VBOXSTRICTRC) CPUMSetGuestMsr(PVMCPU pVCpu, uint32_t idMsr, uint64_t uValue)
{
    //idMsr binary search 找到对应pRange
    PCPUMMSRRANGE   pRange = cpumLookupMsrRange(pVM, idMsr);
    if(pRange)
    {
      //如果设置了某个bit写入要产生GP异常,返回VERR_CPUM_RAISE_GP_0
      if (!(uValue & pRange->fWrGpMask))
      {
       
        CPUMMSRWRFN  enmWrFn = (CPUMMSRWRFN)pRange->enmWrFn;
        PFNCPUMWRMSR pfnWrMsr = g_aCpumWrMsrFns[enmWrFn];
        //去掉需要写ignore的bit
        uint64_t uValueAdjusted = uValue & ~pRange->fWrIgnMask;
         //调用对应WrFn写入
        rcStrict = pfnWrMsr(pVCpu, idMsr, pRange, uValueAdjusted, uValue);
      }
      else
      {
        rcStrict = VERR_CPUM_RAISE_GP_0;
			}
    }
    else 
    {
      //没找到对应MSR Range,返回VERR_CPUM_RAISE_GP_0
      rcStrict = VERR_CPUM_RAISE_GP_0;
    }
}

cpumLookupMsrRange

//binary Search,这个函数不会做模糊匹配
PCPUMMSRRANGE cpumLookupMsrRange(PVM pVM, uint32_t idMsr)
{
    uint32_t        cRanges   = pVM->cpum.s.GuestInfo.cMsrRanges;
    if (!cRanges)
        return NULL;
    PCPUMMSRRANGE   paRanges  = pVM->cpum.s.GuestInfo.CTX_SUFF(paMsrRanges);
    for (;;)
    {
        uint32_t i = cRanges / 2;
      	
        if (idMsr < paRanges[i].uFirst)
        {
            if (i == 0)
                break;
            cRanges = i;
        }
        else if (idMsr > paRanges[i].uLast)
        {
            i++;
            if (i >= cRanges)
                break;
            cRanges -= i;
            paRanges = &paRanges[i];
        }
        else
        {
            //idMsr 处于paRanges[i].uFirst,paRanges[i].uLast中间,表示找到了,否则,返回NULL
            if (paRanges[i].enmRdFn == kCpumMsrRdFn_MsrAlias)
                return cpumLookupMsrRange(pVM, paRanges[i].uValue);
            return &paRanges[i];
        }
    }
  return NULL;
}
发布了26 篇原创文章 · 获赞 10 · 访问量 1258

猜你喜欢

转载自blog.csdn.net/qq_29684547/article/details/104103360
cpu