CPUMR3Db.cpp revision 6a0359b8230a1b91fe49967c124a75191c3dfbf9
3853N/A/* $Id$ */
3853N/A/** @file
3853N/A * CPUM - CPU database part.
3853N/A */
3853N/A
3853N/A/*
3853N/A * Copyright (C) 2013 Oracle Corporation
3853N/A *
3853N/A * This file is part of VirtualBox Open Source Edition (OSE), as
3853N/A * available from http://www.virtualbox.org. This file is free software;
3853N/A * you can redistribute it and/or modify it under the terms of the GNU
3853N/A * General Public License (GPL) as published by the Free Software
3853N/A * Foundation, in version 2 as it comes in the "COPYING" file of the
3853N/A * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
3853N/A * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
3853N/A */
3853N/A
3853N/A/*******************************************************************************
3853N/A* Header Files *
3853N/A*******************************************************************************/
3853N/A#define LOG_GROUP LOG_GROUP_CPUM
3853N/A#include <VBox/vmm/cpum.h>
3853N/A#include "CPUMInternal.h"
3853N/A#include <VBox/vmm/vm.h>
5061N/A
5287N/A#include <VBox/err.h>
3853N/A#include <iprt/asm-amd64-x86.h>
3853N/A#include <iprt/mem.h>
3853N/A#include <iprt/string.h>
3853N/A
3853N/A
3853N/A/*******************************************************************************
4968N/A* Structures and Typedefs *
4968N/A*******************************************************************************/
3853N/Atypedef struct CPUMDBENTRY
3853N/A{
3853N/A /** The CPU name. */
3853N/A const char *pszName;
3853N/A /** The full CPU name. */
4968N/A const char *pszFullName;
4968N/A /** The CPU vendor (CPUMCPUVENDOR). */
4968N/A uint8_t enmVendor;
4968N/A /** The CPU family. */
4968N/A uint8_t uFamily;
4968N/A /** The CPU model. */
4968N/A uint8_t uModel;
3853N/A /** The CPU stepping. */
4968N/A uint8_t uStepping;
4968N/A /** The microarchitecture. */
4968N/A CPUMMICROARCH enmMicroarch;
4968N/A /** Flags (TBD). */
4968N/A uint32_t fFlags;
4968N/A /** The maximum physical address with of the CPU. This should correspond to
4968N/A * the value in CPUID leaf 0x80000008 when present. */
4968N/A uint8_t cMaxPhysAddrWidth;
4968N/A /** Pointer to an array of CPUID leaves. */
3853N/A PCCPUMCPUIDLEAF paCpuIdLeaves;
4968N/A /** The number of CPUID leaves in the array paCpuIdLeaves points to. */
4968N/A uint32_t cCpuIdLeaves;
3853N/A /** The method used to deal with unknown CPUID leaves. */
4968N/A CPUMUKNOWNCPUID enmUnknownCpuId;
4968N/A /** The default unknown CPUID value. */
4968N/A CPUMCPUID DefUnknownCpuId;
4968N/A
3853N/A /** MSR mask. Several microarchitectures ignore higher bits of the */
3853N/A uint32_t fMsrMask;
4968N/A
3853N/A /** The number of ranges in the table pointed to b paMsrRanges. */
4968N/A uint32_t cMsrRanges;
4968N/A /** MSR ranges for this CPU. */
4968N/A PCCPUMMSRRANGE paMsrRanges;
4968N/A} CPUMDBENTRY;
3853N/A
4500N/A
3853N/A/*******************************************************************************
4968N/A* Defined Constants And Macros *
4968N/A*******************************************************************************/
3853N/A
3853N/A/** @def NULL_ALONE
4968N/A * For eliminating an unnecessary data dependency in standalone builds (for
3853N/A * VBoxSVC). */
4968N/A/** @def ZERO_ALONE
4968N/A * For eliminating an unnecessary data size dependency in standalone builds (for
4968N/A * VBoxSVC). */
4968N/A#ifndef CPUM_DB_STANDALONE
4968N/A# define NULL_ALONE(a_aTable) a_aTable
3853N/A# define ZERO_ALONE(a_cTable) a_cTable
3853N/A#else
4968N/A# define NULL_ALONE(a_aTable) NULL
4968N/A# define ZERO_ALONE(a_cTable) 0
4968N/A#endif
4968N/A
4968N/A
4968N/A/** @name Short macros for the MSR range entries.
4968N/A *
3853N/A * These are rather cryptic, but this is to reduce the attack on the right
4968N/A * margin.
3853N/A *
3853N/A * @{ */
4968N/A/** Alias one MSR onto another (a_uTarget). */
4968N/A#define MAL(a_uMsr, a_szName, a_uTarget) \
3853N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_MsrAlias, kCpumMsrWrFn_MsrAlias, 0, a_uTarget, 0, 0, a_szName)
3853N/A/** Functions handles everything. */
3853N/A#define MFN(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff) \
3853N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, 0, 0, 0, a_szName)
3853N/A/** Functions handles everything, with GP mask. */
4968N/A#define MFG(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_fWrGpMask) \
3853N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, 0, 0, a_fWrGpMask, a_szName)
3853N/A/** Function handlers, read-only. */
3853N/A#define MFO(a_uMsr, a_szName, a_enmRdFnSuff) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_ReadOnly, 0, 0, 0, UINT64_MAX, a_szName)
3853N/A/** Function handlers, ignore all writes. */
3853N/A#define MFI(a_uMsr, a_szName, a_enmRdFnSuff) \
3853N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_IgnoreWrite, 0, 0, UINT64_MAX, 0, a_szName)
3853N/A/** Function handlers, with value. */
4968N/A#define MFV(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_uValue) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, a_uValue, 0, 0, a_szName)
4968N/A/** Function handlers, with write ignore mask. */
4968N/A#define MFW(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_fWrIgnMask) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, 0, a_fWrIgnMask, 0, a_szName)
4968N/A/** Function handlers, extended version. */
4968N/A#define MFX(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_uValue, a_fWrIgnMask, a_fWrGpMask) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, a_uValue, a_fWrIgnMask, a_fWrGpMask, a_szName)
4968N/A/** Function handlers, with CPUMCPU storage variable. */
4968N/A#define MFS(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_CpumCpuMember) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, \
4968N/A RT_OFFSETOF(CPUMCPU, a_CpumCpuMember), 0, 0, 0, a_szName)
4968N/A/** Function handlers, with CPUMCPU storage variable, ignore mask and GP mask. */
4968N/A#define MFZ(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_CpumCpuMember, a_fWrIgnMask, a_fWrGpMask) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, \
4968N/A RT_OFFSETOF(CPUMCPU, a_CpumCpuMember), 0, a_fWrIgnMask, a_fWrGpMask, a_szName)
4968N/A/** Read-only fixed value. */
4968N/A#define MVO(a_uMsr, a_szName, a_uValue) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_ReadOnly, 0, a_uValue, 0, UINT64_MAX, a_szName)
4968N/A/** Read-only fixed value, ignores all writes. */
4968N/A#define MVI(a_uMsr, a_szName, a_uValue) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_IgnoreWrite, 0, a_uValue, UINT64_MAX, 0, a_szName)
4968N/A/** Read fixed value, ignore writes outside GP mask. */
4968N/A#define MVG(a_uMsr, a_szName, a_uValue, a_fWrGpMask) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_IgnoreWrite, 0, a_uValue, 0, a_fWrGpMask, a_szName)
4968N/A/** Read fixed value, extended version with both GP and ignore masks. */
4968N/A#define MVX(a_uMsr, a_szName, a_uValue, a_fWrIgnMask, a_fWrGpMask) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_IgnoreWrite, 0, a_uValue, a_fWrIgnMask, a_fWrGpMask, a_szName)
4968N/A/** The short form, no CPUM backing. */
4968N/A#define MSN(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask) \
4968N/A RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, \
4968N/A a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName)
4968N/A
4968N/A/** Range: Functions handles everything. */
4968N/A#define RFN(a_uFirst, a_uLast, a_szName, a_enmRdFnSuff, a_enmWrFnSuff) \
4968N/A RINT(a_uFirst, a_uLast, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, 0, 0, 0, a_szName)
4968N/A/** Range: Read fixed value, read-only. */
4968N/A#define RVO(a_uFirst, a_uLast, a_szName, a_uValue) \
4968N/A RINT(a_uFirst, a_uLast, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_ReadOnly, 0, a_uValue, 0, UINT64_MAX, a_szName)
4968N/A/** Range: Read fixed value, ignore writes. */
4968N/A#define RVI(a_uFirst, a_uLast, a_szName, a_uValue) \
4968N/A RINT(a_uFirst, a_uLast, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_IgnoreWrite, 0, a_uValue, UINT64_MAX, 0, a_szName)
4968N/A/** Range: The short form, no CPUM backing. */
4968N/A#define RSN(a_uFirst, a_uLast, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask) \
4968N/A RINT(a_uFirst, a_uLast, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, \
4968N/A a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName)
4968N/A
4968N/A/** Internal form used by the macros. */
4968N/A#ifdef VBOX_WITH_STATISTICS
4968N/A# define RINT(a_uFirst, a_uLast, a_enmRdFn, a_enmWrFn, a_offCpumCpu, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName) \
4968N/A { a_uFirst, a_uLast, a_enmRdFn, a_enmWrFn, a_offCpumCpu, 0, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName, \
4968N/A { 0 }, { 0 }, { 0 }, { 0 } }
4968N/A#else
4968N/A# define RINT(a_uFirst, a_uLast, a_enmRdFn, a_enmWrFn, a_offCpumCpu, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName) \
4968N/A { a_uFirst, a_uLast, a_enmRdFn, a_enmWrFn, a_offCpumCpu, 0, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName }
4968N/A#endif
4968N/A/** @} */
4968N/A
4968N/A
4968N/A#include "cpus/Intel_Pentium_M_processor_2_00GHz.h"
4968N/A#include "cpus/Intel_Core_i7_3960X.h"
4968N/A#include "cpus/Intel_Core_i5_3570.h"
4968N/A
4968N/A#include "cpus/AMD_FX_8150_Eight_Core.h"
4968N/A#include "cpus/Quad_Core_AMD_Opteron_2384.h"
4968N/A
4968N/A
4968N/A
4968N/A/**
4968N/A * The database entries.
4968N/A *
4968N/A * Warning! The first entry is special. It is the fallback for unknown
4968N/A * processors. Thus, it better be pretty representative.
4968N/A */
4968N/Astatic CPUMDBENTRY const * const g_apCpumDbEntries[] =
4968N/A{
4968N/A#ifdef VBOX_CPUDB_Intel_Core_i5_3570
4968N/A &g_Entry_Intel_Core_i5_3570,
4968N/A#endif
3853N/A#ifdef VBOX_CPUDB_Intel_Core_i7_3960X
3853N/A &g_Entry_Intel_Core_i7_3960X,
3853N/A#endif
3853N/A#ifdef Intel_Pentium_M_processor_2_00GHz
3853N/A &g_Entry_Intel_Pentium_M_processor_2_00GHz,
3853N/A#endif
3853N/A#ifdef VBOX_CPUDB_AMD_FX_8150_Eight_Core
3853N/A &g_Entry_AMD_FX_8150_Eight_Core,
3853N/A#endif
3853N/A#ifdef VBOX_CPUDB_AMD_Phenom_II_X6_1100T
3853N/A &g_Entry_AMD_Phenom_II_X6_1100T,
3853N/A#endif
3853N/A#ifdef VBOX_CPUDB_Quad_Core_AMD_Opteron_2384
3853N/A &g_Entry_Quad_Core_AMD_Opteron_2384,
3853N/A#endif
3853N/A};
3853N/A
4968N/A
3853N/A#ifndef CPUM_DB_STANDALONE
3853N/A
3853N/A/**
4968N/A * Binary search used by cpumR3MsrRangesInsert and has some special properties
3853N/A * wrt to mismatches.
3853N/A *
3853N/A * @returns Insert location.
3853N/A * @param paMsrRanges The MSR ranges to search.
4174N/A * @param cMsrRanges The number of MSR ranges.
4174N/A * @param uMsr What to search for.
3853N/A */
3853N/Astatic uint32_t cpumR3MsrRangesBinSearch(PCCPUMMSRRANGE paMsrRanges, uint32_t cMsrRanges, uint32_t uMsr)
3853N/A{
3853N/A if (!cMsrRanges)
3853N/A return 0;
3853N/A
3853N/A uint32_t iStart = 0;
4968N/A uint32_t iLast = cMsrRanges - 1;
3853N/A for (;;)
3853N/A {
3853N/A uint32_t i = iStart + (iLast - iStart + 1) / 2;
3853N/A if ( uMsr >= paMsrRanges[i].uFirst
3853N/A && uMsr <= paMsrRanges[i].uLast)
3853N/A return i;
3853N/A if (uMsr < paMsrRanges[i].uFirst)
3853N/A {
3853N/A if (i <= iStart)
3853N/A return i;
3853N/A iLast = i - 1;
3853N/A }
3853N/A else
3853N/A {
4968N/A if (i >= iLast)
4968N/A {
4968N/A if (i < cMsrRanges)
4968N/A i++;
4968N/A return i;
4968N/A }
4968N/A iStart = i + 1;
4968N/A }
4968N/A }
4968N/A}
4968N/A
4968N/A
4968N/A/**
4968N/A * Ensures that there is space for at least @a cNewRanges in the table,
4968N/A * reallocating the table if necessary.
4968N/A *
4968N/A * @returns Pointer to the MSR ranges on success, NULL on failure. On failure
4968N/A * @a *ppaMsrRanges is freed and set to NULL.
4968N/A * @param ppaMsrRanges The variable pointing to the ranges (input/output).
4968N/A * @param cMsrRanges The current number of ranges.
4968N/A * @param cNewRanges The number of ranges to be added.
4968N/A */
4968N/Astatic PCPUMMSRRANGE cpumR3MsrRangesEnsureSpace(PCPUMMSRRANGE *ppaMsrRanges, uint32_t cMsrRanges, uint32_t cNewRanges)
4968N/A{
4968N/A uint32_t cMsrRangesAllocated = RT_ALIGN_32(cMsrRanges, 16);
4968N/A if (cMsrRangesAllocated < cMsrRanges + cNewRanges)
4968N/A {
4968N/A uint32_t cNew = RT_ALIGN_32(cMsrRanges + cNewRanges, 16);
4968N/A void *pvNew = RTMemRealloc(*ppaMsrRanges, cNew * sizeof(**ppaMsrRanges));
4968N/A if (!pvNew)
4968N/A {
4968N/A RTMemFree(*ppaMsrRanges);
4968N/A *ppaMsrRanges = NULL;
4968N/A return NULL;
4968N/A }
4968N/A *ppaMsrRanges = (PCPUMMSRRANGE)pvNew;
4968N/A }
4968N/A return *ppaMsrRanges;
4968N/A}
4968N/A
4968N/A
4968N/A/**
4968N/A * Inserts a new MSR range in into an sorted MSR range array.
4968N/A *
4968N/A * If the new MSR range overlaps existing ranges, the existing ones will be
4968N/A * adjusted/removed to fit in the new one.
4968N/A *
4968N/A * @returns VBox status code.
4968N/A * @retval VINF_SUCCESS
4968N/A * @retval VERR_NO_MEMORY
4968N/A *
4968N/A * @param ppaMsrRanges The variable pointing to the ranges (input/output).
4968N/A * @param pcMsrRanges The variable holding number of ranges.
4968N/A * @param pNewRange The new range.
4968N/A */
4968N/Aint cpumR3MsrRangesInsert(PCPUMMSRRANGE *ppaMsrRanges, uint32_t *pcMsrRanges, PCCPUMMSRRANGE pNewRange)
4968N/A{
4968N/A uint32_t cMsrRanges = *pcMsrRanges;
4968N/A PCPUMMSRRANGE paMsrRanges = *ppaMsrRanges;
4968N/A
4968N/A Assert(pNewRange->uLast >= pNewRange->uFirst);
4968N/A Assert(pNewRange->enmRdFn > kCpumMsrRdFn_Invalid && pNewRange->enmRdFn < kCpumMsrRdFn_End);
4968N/A Assert(pNewRange->enmWrFn > kCpumMsrWrFn_Invalid && pNewRange->enmWrFn < kCpumMsrWrFn_End);
4968N/A
4968N/A /*
4968N/A * Optimize the linear insertion case where we add new entries at the end.
4968N/A */
4968N/A if ( cMsrRanges > 0
4968N/A && paMsrRanges[cMsrRanges - 1].uLast < pNewRange->uFirst)
4968N/A {
4968N/A paMsrRanges = cpumR3MsrRangesEnsureSpace(ppaMsrRanges, cMsrRanges, 1);
4968N/A if (!paMsrRanges)
4968N/A return VERR_NO_MEMORY;
4968N/A paMsrRanges[cMsrRanges] = *pNewRange;
4968N/A *pcMsrRanges += 1;
4968N/A }
4968N/A else
4968N/A {
4968N/A uint32_t i = cpumR3MsrRangesBinSearch(paMsrRanges, cMsrRanges, pNewRange->uFirst);
4968N/A Assert(i == cMsrRanges || pNewRange->uFirst <= paMsrRanges[i].uLast);
4968N/A Assert(i == 0 || pNewRange->uFirst > paMsrRanges[i - 1].uLast);
4968N/A
4968N/A /*
4968N/A * Adding an entirely new entry?
4968N/A */
4968N/A if ( i >= cMsrRanges
4968N/A || pNewRange->uLast < paMsrRanges[i].uFirst)
4968N/A {
4968N/A paMsrRanges = cpumR3MsrRangesEnsureSpace(ppaMsrRanges, cMsrRanges, 1);
4968N/A if (!paMsrRanges)
5287N/A return VERR_NO_MEMORY;
4968N/A if (i < cMsrRanges)
4968N/A memmove(&paMsrRanges[i + 1], &paMsrRanges[i], (cMsrRanges - i) * sizeof(paMsrRanges[0]));
4968N/A paMsrRanges[i] = *pNewRange;
4968N/A *pcMsrRanges += 1;
4968N/A }
4968N/A /*
4968N/A * Replace existing entry?
4968N/A */
4968N/A else if ( pNewRange->uFirst == paMsrRanges[i].uFirst
4968N/A && pNewRange->uLast == paMsrRanges[i].uLast)
4968N/A paMsrRanges[i] = *pNewRange;
4968N/A /*
4968N/A * Splitting an existing entry?
4968N/A */
4968N/A else if ( pNewRange->uFirst > paMsrRanges[i].uFirst
4968N/A && pNewRange->uLast < paMsrRanges[i].uLast)
4968N/A {
4968N/A paMsrRanges = cpumR3MsrRangesEnsureSpace(ppaMsrRanges, cMsrRanges, 2);
4968N/A if (!paMsrRanges)
4968N/A return VERR_NO_MEMORY;
4968N/A if (i < cMsrRanges)
4968N/A memmove(&paMsrRanges[i + 2], &paMsrRanges[i], (cMsrRanges - i) * sizeof(paMsrRanges[0]));
4968N/A paMsrRanges[i + 1] = *pNewRange;
4968N/A paMsrRanges[i + 2] = paMsrRanges[i];
4968N/A paMsrRanges[i ].uLast = pNewRange->uFirst - 1;
4968N/A paMsrRanges[i + 2].uFirst = pNewRange->uLast + 1;
4968N/A *pcMsrRanges += 2;
4968N/A }
4968N/A /*
4968N/A * Complicated scenarios that can affect more than one range.
4968N/A *
4968N/A * The current code does not optimize memmove calls when replacing
4968N/A * one or more existing ranges, because it's tedious to deal with and
4968N/A * not expected to be a frequent usage scenario.
4968N/A */
5287N/A else
4968N/A {
4968N/A /* Adjust start of first match? */
4968N/A if ( pNewRange->uFirst <= paMsrRanges[i].uFirst
4968N/A && pNewRange->uLast < paMsrRanges[i].uLast)
4968N/A paMsrRanges[i].uFirst = pNewRange->uLast + 1;
4968N/A else
4968N/A {
4968N/A /* Adjust end of first match? */
4968N/A if (pNewRange->uFirst > paMsrRanges[i].uFirst)
4968N/A {
4968N/A Assert(paMsrRanges[i].uLast >= pNewRange->uFirst);
4968N/A paMsrRanges[i].uLast = pNewRange->uFirst - 1;
4968N/A i++;
4968N/A }
4968N/A /* Replace the whole first match (lazy bird). */
4968N/A else
4968N/A {
4968N/A if (i + 1 < cMsrRanges)
4968N/A memmove(&paMsrRanges[i], &paMsrRanges[i + 1], (cMsrRanges - i - 1) * sizeof(paMsrRanges[0]));
4968N/A cMsrRanges = *pcMsrRanges -= 1;
4968N/A }
4968N/A
4968N/A /* Do the new range affect more ranges? */
4968N/A while ( i < cMsrRanges
4968N/A && pNewRange->uLast >= paMsrRanges[i].uFirst)
4968N/A {
4968N/A if (pNewRange->uLast < paMsrRanges[i].uLast)
4968N/A {
4968N/A /* Adjust the start of it, then we're done. */
4968N/A paMsrRanges[i].uFirst = pNewRange->uLast + 1;
4968N/A break;
4968N/A }
4968N/A
4968N/A /* Remove it entirely. */
4968N/A if (i + 1 < cMsrRanges)
4968N/A memmove(&paMsrRanges[i], &paMsrRanges[i + 1], (cMsrRanges - i - 1) * sizeof(paMsrRanges[0]));
4968N/A cMsrRanges = *pcMsrRanges -= 1;
4968N/A }
4968N/A }
4968N/A
4968N/A /* Now, perform a normal insertion. */
4968N/A paMsrRanges = cpumR3MsrRangesEnsureSpace(ppaMsrRanges, cMsrRanges, 1);
4968N/A if (!paMsrRanges)
4968N/A return VERR_NO_MEMORY;
4968N/A if (i < cMsrRanges)
4968N/A memmove(&paMsrRanges[i + 1], &paMsrRanges[i], (cMsrRanges - i) * sizeof(paMsrRanges[0]));
4968N/A paMsrRanges[i] = *pNewRange;
4968N/A *pcMsrRanges += 1;
4968N/A }
4968N/A }
4968N/A
4968N/A return VINF_SUCCESS;
4968N/A}
4968N/A
4968N/A
4968N/Aint cpumR3DbGetCpuInfo(const char *pszName, PCPUMINFO pInfo)
4968N/A{
4968N/A CPUMDBENTRY const *pEntry = NULL;
4968N/A int rc;
4968N/A
4968N/A if (!strcmp(pszName, "host"))
4968N/A {
4968N/A /*
4968N/A * Create a CPU database entry for the host CPU. This means getting
4968N/A * the CPUID bits from the real CPU and grabbing the closest matching
4968N/A * database entry for MSRs.
4968N/A */
4968N/A rc = CPUMR3CpuIdDetectUnknownLeafMethod(&pInfo->enmUnknownCpuIdMethod, &pInfo->DefCpuId);
4968N/A if (RT_FAILURE(rc))
4968N/A return rc;
4968N/A rc = CPUMR3CpuIdCollectLeaves(&pInfo->paCpuIdLeavesR3, &pInfo->cCpuIdLeaves);
4968N/A if (RT_FAILURE(rc))
4968N/A return rc;
4968N/A
4968N/A /* Lookup database entry for MSRs. */
4968N/A CPUMCPUVENDOR const enmVendor = CPUMR3CpuIdDetectVendorEx(pInfo->paCpuIdLeavesR3[0].uEax,
5287N/A pInfo->paCpuIdLeavesR3[0].uEbx,
4968N/A pInfo->paCpuIdLeavesR3[0].uEcx,
4968N/A pInfo->paCpuIdLeavesR3[0].uEdx);
4968N/A uint32_t const uStd1Eax = pInfo->paCpuIdLeavesR3[1].uEax;
4968N/A uint8_t const uFamily = ASMGetCpuFamily(uStd1Eax);
4968N/A uint8_t const uModel = ASMGetCpuModel(uStd1Eax, enmVendor == CPUMCPUVENDOR_INTEL);
4968N/A uint8_t const uStepping = ASMGetCpuStepping(uStd1Eax);
4968N/A CPUMMICROARCH const enmMicroarch = CPUMR3CpuIdDetermineMicroarchEx(enmVendor, uFamily, uModel, uStepping);
4968N/A
4968N/A for (unsigned i = 0; i < RT_ELEMENTS(g_apCpumDbEntries); i++)
4968N/A {
4968N/A CPUMDBENTRY const *pCur = g_apCpumDbEntries[i];
4968N/A if ((CPUMCPUVENDOR)pCur->enmVendor == enmVendor)
4968N/A {
4968N/A /* Anything from the same vendor is better than nothing: */
4968N/A if (!pEntry)
5287N/A pEntry = pCur;
4968N/A /* Newer micro arch is better than an older one: */
4968N/A else if ( pEntry->enmMicroarch < enmMicroarch
4968N/A && pCur->enmMicroarch >= enmMicroarch)
4968N/A pEntry = pCur;
4968N/A /* Prefer a micro arch match: */
4968N/A else if ( pEntry->enmMicroarch != enmMicroarch
4968N/A && pCur->enmMicroarch == enmMicroarch)
4968N/A pEntry = pCur;
4968N/A /* If the micro arch matches, check model and stepping. Stop
4968N/A looping if we get an exact match. */
4968N/A else if ( pEntry->enmMicroarch == enmMicroarch
4968N/A && pCur->enmMicroarch == enmMicroarch)
4968N/A {
4968N/A if (pCur->uModel == uModel)
4968N/A {
4968N/A /* Perfect match? */
4968N/A if (pCur->uStepping == uStepping)
4968N/A {
4968N/A pEntry = pCur;
4968N/A break;
4968N/A }
4968N/A
4968N/A /* Better model match? */
4968N/A if (pEntry->uModel != uModel)
4968N/A pEntry = pCur;
4968N/A /* The one with the closest stepping, prefering ones over earlier ones. */
4968N/A else if ( pCur->uStepping > uStepping
4968N/A ? pCur->uStepping < pEntry->uStepping || pEntry->uStepping < uStepping
4968N/A : pCur->uStepping > pEntry->uStepping)
4968N/A pEntry = pCur;
4968N/A }
4968N/A /* The one with the closest model, prefering later ones over earlier ones. */
4968N/A else if ( pCur->uModel > uModel
4968N/A ? pCur->uModel < pEntry->uModel || pEntry->uModel < uModel
4968N/A : pCur->uModel > pEntry->uModel)
4968N/A pEntry = pCur;
4968N/A }
4968N/A }
4968N/A }
4968N/A
4968N/A if (pEntry)
4968N/A LogRel(("CPUM: Matched host CPU %s %#x/%#x/%#x %s with CPU DB entry '%s' (%s %#x/%#x/%#x %s).\n",
4968N/A CPUMR3CpuVendorName(enmVendor), uFamily, uModel, uStepping, CPUMR3MicroarchName(enmMicroarch),
4968N/A pEntry->pszName, CPUMR3CpuVendorName((CPUMCPUVENDOR)pEntry->enmVendor), pEntry->uFamily, pEntry->uModel,
4968N/A pEntry->uStepping, CPUMR3MicroarchName(pEntry->enmMicroarch) ));
4968N/A else
4968N/A {
4968N/A pEntry = g_apCpumDbEntries[0];
4968N/A LogRel(("CPUM: No matching processor database entry %s %#x/%#x/%#x %s, falling back on '%s'.\n",
4968N/A CPUMR3CpuVendorName(enmVendor), uFamily, uModel, uStepping, CPUMR3MicroarchName(enmMicroarch),
4968N/A pEntry->pszName));
4968N/A }
4968N/A }
4968N/A else
4968N/A {
4968N/A /*
4968N/A * We're supposed to be emulating a specific CPU that is included in
4968N/A * our CPU database. The CPUID tables needs to be copied onto the
4968N/A * heap so the caller can modify them and so they can be freed like
4968N/A * in the host case above.
4968N/A */
4968N/A for (unsigned i = 0; i < RT_ELEMENTS(g_apCpumDbEntries); i++)
4968N/A if (!strcmp(pszName, g_apCpumDbEntries[i]->pszName))
4968N/A {
4968N/A pEntry = g_apCpumDbEntries[i];
4968N/A break;
4968N/A }
4968N/A if (!pEntry)
4968N/A {
4968N/A LogRel(("CPUM: Cannot locate any CPU by the name '%s'\n", pszName));
5287N/A return VERR_CPUM_DB_CPU_NOT_FOUND;
4968N/A }
4968N/A
4968N/A pInfo->cCpuIdLeaves = pEntry->cCpuIdLeaves;
4968N/A if (pEntry->cCpuIdLeaves)
4968N/A {
4968N/A pInfo->paCpuIdLeavesR3 = (PCPUMCPUIDLEAF)RTMemDup(pEntry->paCpuIdLeaves,
4968N/A sizeof(pEntry->paCpuIdLeaves[0]) * pEntry->cCpuIdLeaves);
4968N/A if (!pInfo->paCpuIdLeavesR3)
5287N/A return VERR_NO_MEMORY;
4968N/A }
4968N/A else
4968N/A pInfo->paCpuIdLeavesR3 = NULL;
4968N/A
4968N/A pInfo->enmUnknownCpuIdMethod = pEntry->enmUnknownCpuId;
4968N/A pInfo->DefCpuId = pEntry->DefUnknownCpuId;
4968N/A
4968N/A LogRel(("CPUM: Using CPU DB entry '%s' (%s %#x/%#x/%#x %s).\n",
4968N/A pEntry->pszName, CPUMR3CpuVendorName((CPUMCPUVENDOR)pEntry->enmVendor),
4968N/A pEntry->uFamily, pEntry->uModel, pEntry->uStepping, CPUMR3MicroarchName(pEntry->enmMicroarch) ));
4968N/A }
4968N/A
4968N/A pInfo->fMsrMask = pEntry->fMsrMask;
4968N/A pInfo->iFirstExtCpuIdLeaf = 0; /* Set by caller. */
4968N/A pInfo->uPadding = 0;
4968N/A pInfo->paCpuIdLeavesR0 = NIL_RTR0PTR;
4968N/A pInfo->paMsrRangesR0 = NIL_RTR0PTR;
4968N/A pInfo->paCpuIdLeavesRC = NIL_RTRCPTR;
4968N/A pInfo->paMsrRangesRC = NIL_RTRCPTR;
4968N/A
4968N/A /*
4968N/A * Copy the MSR range.
4968N/A */
3853N/A uint32_t cMsrs = 0;
3853N/A PCPUMMSRRANGE paMsrs = NULL;
3853N/A
3853N/A PCCPUMMSRRANGE pCurMsr = pEntry->paMsrRanges;
3853N/A uint32_t cLeft = pEntry->cMsrRanges;
5287N/A while (cLeft-- > 0)
3853N/A {
3853N/A rc = cpumR3MsrRangesInsert(&paMsrs, &cMsrs, pCurMsr);
3853N/A if (RT_FAILURE(rc))
3853N/A {
3853N/A Assert(!paMsrs); /* The above function frees this. */
3853N/A RTMemFree(pInfo->paCpuIdLeavesR3);
3853N/A pInfo->paCpuIdLeavesR3 = NULL;
3853N/A return rc;
3853N/A }
3853N/A pCurMsr++;
4968N/A }
4968N/A
4968N/A pInfo->paMsrRangesR3 = paMsrs;
4968N/A pInfo->cMsrRanges = cMsrs;
4968N/A return VINF_SUCCESS;
4968N/A}
4968N/A
4968N/A
4968N/A/**
4968N/A * Register statistics for the MSRs.
4968N/A *
4968N/A * This must not be called before the MSRs have been finalized and moved to the
4968N/A * hyper heap.
4968N/A *
4968N/A * @returns VBox status code.
4968N/A * @param pVM Pointer to the cross context VM structure.
4968N/A */
4968N/Aint cpumR3MsrRegStats(PVM pVM)
4968N/A{
4968N/A /*
4968N/A * Global statistics.
4968N/A */
4968N/A PCPUM pCpum = &pVM->cpum.s;
4968N/A STAM_REL_REG(pVM, &pCpum->cMsrReads, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/Reads",
4968N/A STAMUNIT_OCCURENCES, "All RDMSRs making it to CPUM.");
4968N/A STAM_REL_REG(pVM, &pCpum->cMsrReadsRaiseGp, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/ReadsRaisingGP",
4968N/A STAMUNIT_OCCURENCES, "RDMSR raising #GPs, except unknown MSRs.");
4968N/A STAM_REL_REG(pVM, &pCpum->cMsrReadsUnknown, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/ReadsUnknown",
5287N/A STAMUNIT_OCCURENCES, "RDMSR on unknown MSRs (raises #GP).");
4968N/A STAM_REL_REG(pVM, &pCpum->cMsrWrites, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/Writes",
5287N/A STAMUNIT_OCCURENCES, "All RDMSRs making it to CPUM.");
5287N/A STAM_REL_REG(pVM, &pCpum->cMsrWritesToIgnoredBits, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/WritesRaisingGP",
4968N/A STAMUNIT_OCCURENCES, "WRMSR raising #GPs, except unknown MSRs.");
4968N/A STAM_REL_REG(pVM, &pCpum->cMsrWritesRaiseGp, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/WritesToIgnoredBits",
4968N/A STAMUNIT_OCCURENCES, "Writing of ignored bits.");
4968N/A STAM_REL_REG(pVM, &pCpum->cMsrWritesUnknown, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/WritesUnknown",
4968N/A STAMUNIT_OCCURENCES, "WRMSR on unknown MSRs (raises #GP).");
4968N/A
4968N/A
4968N/A# ifdef VBOX_WITH_STATISTICS
4968N/A /*
4968N/A * Per range.
4968N/A */
4968N/A PCPUMMSRRANGE paRanges = pVM->cpum.s.GuestInfo.paMsrRangesR3;
4968N/A uint32_t cRanges = pVM->cpum.s.GuestInfo.cMsrRanges;
4968N/A for (uint32_t i = 0; i < cRanges; i++)
4968N/A {
4968N/A char szName[160];
4968N/A ssize_t cchName;
4968N/A
4968N/A if (paRanges[i].uFirst == paRanges[i].uLast)
4968N/A cchName = RTStrPrintf(szName, sizeof(szName), "/CPUM/MSRs/%#010x-%s",
4968N/A paRanges[i].uFirst, paRanges[i].szName);
4968N/A else
4968N/A cchName = RTStrPrintf(szName, sizeof(szName), "/CPUM/MSRs/%#010x-%#010x-%s",
4968N/A paRanges[i].uFirst, paRanges[i].uLast, paRanges[i].szName);
4968N/A
4968N/A RTStrCopy(&szName[cchName], sizeof(szName) - cchName, "-reads");
4968N/A STAMR3Register(pVM, &paRanges[i].cReads, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "RDMSR");
4968N/A
4968N/A RTStrCopy(&szName[cchName], sizeof(szName) - cchName, "-writes");
4968N/A STAMR3Register(pVM, &paRanges[i].cWrites, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, "WRMSR");
4968N/A
4968N/A RTStrCopy(&szName[cchName], sizeof(szName) - cchName, "-GPs");
4968N/A STAMR3Register(pVM, &paRanges[i].cGps, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, "#GPs");
4968N/A
4968N/A RTStrCopy(&szName[cchName], sizeof(szName) - cchName, "-ign-bits-writes");
4968N/A STAMR3Register(pVM, &paRanges[i].cIgnoredBits, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, "WRMSR w/ ignored bits");
4968N/A }
4968N/A# endif /* VBOX_WITH_STATISTICS */
4968N/A
4968N/A return VINF_SUCCESS;
4968N/A}
4968N/A
4968N/A#endif /* !CPUM_DB_STANDALONE */
4968N/A
4968N/A