VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/CPUMR3Db.cpp

Last change on this file was 102038, checked in by vboxsync, 6 months ago

CPUMR3Db: Removed duplicate entry.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.6 KB
RevLine 
[49893]1/* $Id: CPUMR3Db.cpp 102038 2023-11-09 14:45:36Z vboxsync $ */
2/** @file
3 * CPUM - CPU database part.
4 */
5
6/*
[98103]7 * Copyright (C) 2013-2023 Oracle and/or its affiliates.
[49893]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[49893]26 */
27
[57358]28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[49893]32#define LOG_GROUP LOG_GROUP_CPUM
33#include <VBox/vmm/cpum.h>
34#include "CPUMInternal.h"
35#include <VBox/vmm/vm.h>
[51271]36#include <VBox/vmm/mm.h>
[49893]37
38#include <VBox/err.h>
[87255]39#if !defined(RT_ARCH_ARM64)
40# include <iprt/asm-amd64-x86.h>
41#endif
[49893]42#include <iprt/mem.h>
43#include <iprt/string.h>
44
45
[57358]46/*********************************************************************************************************************************
47* Defined Constants And Macros *
48*********************************************************************************************************************************/
[49893]49/** @def NULL_ALONE
50 * For eliminating an unnecessary data dependency in standalone builds (for
51 * VBoxSVC). */
52/** @def ZERO_ALONE
53 * For eliminating an unnecessary data size dependency in standalone builds (for
54 * VBoxSVC). */
55#ifndef CPUM_DB_STANDALONE
56# define NULL_ALONE(a_aTable) a_aTable
57# define ZERO_ALONE(a_cTable) a_cTable
58#else
59# define NULL_ALONE(a_aTable) NULL
60# define ZERO_ALONE(a_cTable) 0
61#endif
62
63
64/** @name Short macros for the MSR range entries.
65 *
66 * These are rather cryptic, but this is to reduce the attack on the right
67 * margin.
68 *
69 * @{ */
70/** Alias one MSR onto another (a_uTarget). */
71#define MAL(a_uMsr, a_szName, a_uTarget) \
72 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_MsrAlias, kCpumMsrWrFn_MsrAlias, 0, a_uTarget, 0, 0, a_szName)
73/** Functions handles everything. */
74#define MFN(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff) \
75 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, 0, 0, 0, a_szName)
76/** Functions handles everything, with GP mask. */
77#define MFG(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_fWrGpMask) \
78 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, 0, 0, a_fWrGpMask, a_szName)
79/** Function handlers, read-only. */
80#define MFO(a_uMsr, a_szName, a_enmRdFnSuff) \
81 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_ReadOnly, 0, 0, 0, UINT64_MAX, a_szName)
82/** Function handlers, ignore all writes. */
83#define MFI(a_uMsr, a_szName, a_enmRdFnSuff) \
84 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_IgnoreWrite, 0, 0, UINT64_MAX, 0, a_szName)
85/** Function handlers, with value. */
86#define MFV(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_uValue) \
87 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, a_uValue, 0, 0, a_szName)
88/** Function handlers, with write ignore mask. */
89#define MFW(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_fWrIgnMask) \
90 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, 0, a_fWrIgnMask, 0, a_szName)
91/** Function handlers, extended version. */
92#define MFX(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_uValue, a_fWrIgnMask, a_fWrGpMask) \
93 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, a_uValue, a_fWrIgnMask, a_fWrGpMask, a_szName)
94/** Function handlers, with CPUMCPU storage variable. */
95#define MFS(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_CpumCpuMember) \
96 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, \
97 RT_OFFSETOF(CPUMCPU, a_CpumCpuMember), 0, 0, 0, a_szName)
98/** Function handlers, with CPUMCPU storage variable, ignore mask and GP mask. */
99#define MFZ(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_CpumCpuMember, a_fWrIgnMask, a_fWrGpMask) \
100 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, \
101 RT_OFFSETOF(CPUMCPU, a_CpumCpuMember), 0, a_fWrIgnMask, a_fWrGpMask, a_szName)
102/** Read-only fixed value. */
103#define MVO(a_uMsr, a_szName, a_uValue) \
104 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_ReadOnly, 0, a_uValue, 0, UINT64_MAX, a_szName)
105/** Read-only fixed value, ignores all writes. */
106#define MVI(a_uMsr, a_szName, a_uValue) \
107 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_IgnoreWrite, 0, a_uValue, UINT64_MAX, 0, a_szName)
108/** Read fixed value, ignore writes outside GP mask. */
109#define MVG(a_uMsr, a_szName, a_uValue, a_fWrGpMask) \
110 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_IgnoreWrite, 0, a_uValue, 0, a_fWrGpMask, a_szName)
111/** Read fixed value, extended version with both GP and ignore masks. */
112#define MVX(a_uMsr, a_szName, a_uValue, a_fWrIgnMask, a_fWrGpMask) \
113 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_IgnoreWrite, 0, a_uValue, a_fWrIgnMask, a_fWrGpMask, a_szName)
114/** The short form, no CPUM backing. */
115#define MSN(a_uMsr, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask) \
116 RINT(a_uMsr, a_uMsr, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, \
117 a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName)
118
119/** Range: Functions handles everything. */
120#define RFN(a_uFirst, a_uLast, a_szName, a_enmRdFnSuff, a_enmWrFnSuff) \
121 RINT(a_uFirst, a_uLast, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, 0, 0, 0, a_szName)
122/** Range: Read fixed value, read-only. */
123#define RVO(a_uFirst, a_uLast, a_szName, a_uValue) \
124 RINT(a_uFirst, a_uLast, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_ReadOnly, 0, a_uValue, 0, UINT64_MAX, a_szName)
125/** Range: Read fixed value, ignore writes. */
126#define RVI(a_uFirst, a_uLast, a_szName, a_uValue) \
127 RINT(a_uFirst, a_uLast, kCpumMsrRdFn_FixedValue, kCpumMsrWrFn_IgnoreWrite, 0, a_uValue, UINT64_MAX, 0, a_szName)
128/** Range: The short form, no CPUM backing. */
129#define RSN(a_uFirst, a_uLast, a_szName, a_enmRdFnSuff, a_enmWrFnSuff, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask) \
130 RINT(a_uFirst, a_uLast, kCpumMsrRdFn_##a_enmRdFnSuff, kCpumMsrWrFn_##a_enmWrFnSuff, 0, \
131 a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName)
132
133/** Internal form used by the macros. */
134#ifdef VBOX_WITH_STATISTICS
135# define RINT(a_uFirst, a_uLast, a_enmRdFn, a_enmWrFn, a_offCpumCpu, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName) \
136 { a_uFirst, a_uLast, a_enmRdFn, a_enmWrFn, a_offCpumCpu, 0, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName, \
137 { 0 }, { 0 }, { 0 }, { 0 } }
138#else
139# define RINT(a_uFirst, a_uLast, a_enmRdFn, a_enmWrFn, a_offCpumCpu, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName) \
140 { a_uFirst, a_uLast, a_enmRdFn, a_enmWrFn, a_offCpumCpu, 0, a_uInitOrReadValue, a_fWrIgnMask, a_fWrGpMask, a_szName }
141#endif
142/** @} */
143
[63465]144#ifndef CPUM_DB_STANDALONE
[49893]145
[58653]146#include "cpus/Intel_Core_i7_6700K.h"
[58568]147#include "cpus/Intel_Core_i7_5600U.h"
[49893]148#include "cpus/Intel_Core_i7_3960X.h"
[49899]149#include "cpus/Intel_Core_i5_3570.h"
[50656]150#include "cpus/Intel_Core_i7_2635QM.h"
[49927]151#include "cpus/Intel_Xeon_X5482_3_20GHz.h"
[69880]152#include "cpus/Intel_Core2_X6800_2_93GHz.h"
[69900]153#include "cpus/Intel_Core2_T7600_2_33GHz.h"
154#include "cpus/Intel_Core_Duo_T2600_2_16GHz.h"
[49966]155#include "cpus/Intel_Pentium_M_processor_2_00GHz.h"
156#include "cpus/Intel_Pentium_4_3_00GHz.h"
[60762]157#include "cpus/Intel_Pentium_N3530_2_16GHz.h"
[58547]158#include "cpus/Intel_Atom_330_1_60GHz.h"
[70399]159#include "cpus/Intel_80486.h"
[60664]160#include "cpus/Intel_80386.h"
[60411]161#include "cpus/Intel_80286.h"
162#include "cpus/Intel_80186.h"
163#include "cpus/Intel_8086.h"
[49899]164
[85354]165#include "cpus/AMD_Ryzen_7_1800X_Eight_Core.h"
[49893]166#include "cpus/AMD_FX_8150_Eight_Core.h"
[49952]167#include "cpus/AMD_Phenom_II_X6_1100T.h"
[49893]168#include "cpus/Quad_Core_AMD_Opteron_2384.h"
[50653]169#include "cpus/AMD_Athlon_64_X2_Dual_Core_4200.h"
[49966]170#include "cpus/AMD_Athlon_64_3200.h"
[49893]171
[49993]172#include "cpus/VIA_QuadCore_L4700_1_2_GHz.h"
[49893]173
[76886]174#include "cpus/ZHAOXIN_KaiXian_KX_U5581_1_8GHz.h"
[49893]175
[81605]176#include "cpus/Hygon_C86_7185_32_core.h"
[49993]177
[76886]178
[49893]179/**
180 * The database entries.
181 *
[50157]182 * 1. The first entry is special. It is the fallback for unknown
183 * processors. Thus, it better be pretty representative.
184 *
185 * 2. The first entry for a CPU vendor is likewise important as it is
186 * the default entry for that vendor.
187 *
188 * Generally we put the most recent CPUs first, since these tend to have the
189 * most complicated and backwards compatible list of MSRs.
[49893]190 */
191static CPUMDBENTRY const * const g_apCpumDbEntries[] =
192{
[76561]193#ifdef VBOX_CPUDB_Intel_Core_i7_6700K_h
[58569]194 &g_Entry_Intel_Core_i7_6700K,
195#endif
[76561]196#ifdef VBOX_CPUDB_Intel_Core_i7_5600U_h
[58568]197 &g_Entry_Intel_Core_i7_5600U,
198#endif
[76561]199#ifdef VBOX_CPUDB_Intel_Core_i5_3570_h
[49899]200 &g_Entry_Intel_Core_i5_3570,
201#endif
[76561]202#ifdef VBOX_CPUDB_Intel_Core_i7_3960X_h
[49893]203 &g_Entry_Intel_Core_i7_3960X,
204#endif
[76561]205#ifdef VBOX_CPUDB_Intel_Core_i7_2635QM_h
[50656]206 &g_Entry_Intel_Core_i7_2635QM,
207#endif
[76561]208#ifdef VBOX_CPUDB_Intel_Pentium_N3530_2_16GHz_h
[60762]209 &g_Entry_Intel_Pentium_N3530_2_16GHz,
210#endif
[76561]211#ifdef VBOX_CPUDB_Intel_Atom_330_1_60GHz_h
[58547]212 &g_Entry_Intel_Atom_330_1_60GHz,
213#endif
[76561]214#ifdef VBOX_CPUDB_Intel_Pentium_M_processor_2_00GHz_h
[49893]215 &g_Entry_Intel_Pentium_M_processor_2_00GHz,
216#endif
[76561]217#ifdef VBOX_CPUDB_Intel_Xeon_X5482_3_20GHz_h
[49927]218 &g_Entry_Intel_Xeon_X5482_3_20GHz,
219#endif
[76561]220#ifdef VBOX_CPUDB_Intel_Core2_X6800_2_93GHz_h
[69880]221 &g_Entry_Intel_Core2_X6800_2_93GHz,
222#endif
[76561]223#ifdef VBOX_CPUDB_Intel_Core2_T7600_2_33GHz_h
[69900]224 &g_Entry_Intel_Core2_T7600_2_33GHz,
225#endif
[76561]226#ifdef VBOX_CPUDB_Intel_Core_Duo_T2600_2_16GHz_h
[69900]227 &g_Entry_Intel_Core_Duo_T2600_2_16GHz,
228#endif
[76561]229#ifdef VBOX_CPUDB_Intel_Pentium_4_3_00GHz_h
[49966]230 &g_Entry_Intel_Pentium_4_3_00GHz,
231#endif
[70399]232/** @todo pentium, pentium mmx, pentium pro, pentium II, pentium III */
[76561]233#ifdef VBOX_CPUDB_Intel_80486_h
[60411]234 &g_Entry_Intel_80486,
235#endif
[76561]236#ifdef VBOX_CPUDB_Intel_80386_h
[60411]237 &g_Entry_Intel_80386,
238#endif
[76561]239#ifdef VBOX_CPUDB_Intel_80286_h
[60411]240 &g_Entry_Intel_80286,
241#endif
[76561]242#ifdef VBOX_CPUDB_Intel_80186_h
[60411]243 &g_Entry_Intel_80186,
244#endif
[76561]245#ifdef VBOX_CPUDB_Intel_8086_h
[60411]246 &g_Entry_Intel_8086,
247#endif
[49966]248
[85354]249#ifdef VBOX_CPUDB_AMD_Ryzen_7_1800X_Eight_Core_h
250 &g_Entry_AMD_Ryzen_7_1800X_Eight_Core,
251#endif
[76561]252#ifdef VBOX_CPUDB_AMD_FX_8150_Eight_Core_h
[49893]253 &g_Entry_AMD_FX_8150_Eight_Core,
254#endif
[76561]255#ifdef VBOX_CPUDB_AMD_Phenom_II_X6_1100T_h
[49893]256 &g_Entry_AMD_Phenom_II_X6_1100T,
257#endif
[76561]258#ifdef VBOX_CPUDB_Quad_Core_AMD_Opteron_2384_h
[49893]259 &g_Entry_Quad_Core_AMD_Opteron_2384,
260#endif
[76561]261#ifdef VBOX_CPUDB_AMD_Athlon_64_X2_Dual_Core_4200_h
[50653]262 &g_Entry_AMD_Athlon_64_X2_Dual_Core_4200,
263#endif
[76561]264#ifdef VBOX_CPUDB_AMD_Athlon_64_3200_h
[49966]265 &g_Entry_AMD_Athlon_64_3200,
266#endif
[49993]267
[76886]268#ifdef VBOX_CPUDB_ZHAOXIN_KaiXian_KX_U5581_1_8GHz_h
269 &g_Entry_ZHAOXIN_KaiXian_KX_U5581_1_8GHz,
270#endif
271
[76561]272#ifdef VBOX_CPUDB_VIA_QuadCore_L4700_1_2_GHz_h
[49993]273 &g_Entry_VIA_QuadCore_L4700_1_2_GHz,
274#endif
[60411]275
[76561]276#ifdef VBOX_CPUDB_NEC_V20_h
[60411]277 &g_Entry_NEC_V20,
278#endif
[81605]279
280#ifdef VBOX_CPUDB_Hygon_C86_7185_32_core_h
281 &g_Entry_Hygon_C86_7185_32_core,
282#endif
[49893]283};
284
285
[85573]286/**
287 * Returns the number of entries in the CPU database.
288 *
289 * @returns Number of entries.
290 * @sa PFNCPUMDBGETENTRIES
291 */
292VMMR3DECL(uint32_t) CPUMR3DbGetEntries(void)
293{
294 return RT_ELEMENTS(g_apCpumDbEntries);
295}
[49893]296
[85573]297
[49893]298/**
[85573]299 * Returns CPU database entry for the given index.
300 *
301 * @returns Pointer the CPU database entry, NULL if index is out of bounds.
302 * @param idxCpuDb The index (0..CPUMR3DbGetEntries).
303 * @sa PFNCPUMDBGETENTRYBYINDEX
304 */
305VMMR3DECL(PCCPUMDBENTRY) CPUMR3DbGetEntryByIndex(uint32_t idxCpuDb)
306{
[99756]307 AssertReturn(idxCpuDb < RT_ELEMENTS(g_apCpumDbEntries), NULL);
[85573]308 return g_apCpumDbEntries[idxCpuDb];
309}
310
311
312/**
313 * Returns CPU database entry with the given name.
314 *
315 * @returns Pointer the CPU database entry, NULL if not found.
[85578]316 * @param pszName The name of the profile to return.
[85573]317 * @sa PFNCPUMDBGETENTRYBYNAME
318 */
319VMMR3DECL(PCCPUMDBENTRY) CPUMR3DbGetEntryByName(const char *pszName)
320{
321 AssertPtrReturn(pszName, NULL);
322 AssertReturn(*pszName, NULL);
323 for (size_t i = 0; i < RT_ELEMENTS(g_apCpumDbEntries); i++)
324 if (strcmp(g_apCpumDbEntries[i]->pszName, pszName) == 0)
325 return g_apCpumDbEntries[i];
326 return NULL;
327}
328
329
330
331/**
[49893]332 * Binary search used by cpumR3MsrRangesInsert and has some special properties
333 * wrt to mismatches.
334 *
335 * @returns Insert location.
336 * @param paMsrRanges The MSR ranges to search.
337 * @param cMsrRanges The number of MSR ranges.
338 * @param uMsr What to search for.
339 */
340static uint32_t cpumR3MsrRangesBinSearch(PCCPUMMSRRANGE paMsrRanges, uint32_t cMsrRanges, uint32_t uMsr)
341{
342 if (!cMsrRanges)
343 return 0;
344
345 uint32_t iStart = 0;
346 uint32_t iLast = cMsrRanges - 1;
347 for (;;)
348 {
349 uint32_t i = iStart + (iLast - iStart + 1) / 2;
350 if ( uMsr >= paMsrRanges[i].uFirst
351 && uMsr <= paMsrRanges[i].uLast)
352 return i;
353 if (uMsr < paMsrRanges[i].uFirst)
354 {
355 if (i <= iStart)
356 return i;
357 iLast = i - 1;
358 }
359 else
360 {
361 if (i >= iLast)
362 {
363 if (i < cMsrRanges)
364 i++;
365 return i;
366 }
367 iStart = i + 1;
368 }
369 }
370}
371
372
373/**
374 * Ensures that there is space for at least @a cNewRanges in the table,
375 * reallocating the table if necessary.
376 *
377 * @returns Pointer to the MSR ranges on success, NULL on failure. On failure
378 * @a *ppaMsrRanges is freed and set to NULL.
[58122]379 * @param pVM The cross context VM structure. If NULL,
380 * use the process heap, otherwise the VM's hyper heap.
[49893]381 * @param ppaMsrRanges The variable pointing to the ranges (input/output).
382 * @param cMsrRanges The current number of ranges.
383 * @param cNewRanges The number of ranges to be added.
384 */
[51271]385static PCPUMMSRRANGE cpumR3MsrRangesEnsureSpace(PVM pVM, PCPUMMSRRANGE *ppaMsrRanges, uint32_t cMsrRanges, uint32_t cNewRanges)
[49893]386{
[91266]387 if ( cMsrRanges + cNewRanges
388 > RT_ELEMENTS(pVM->cpum.s.GuestInfo.aMsrRanges) + (pVM ? 0 : 128 /* Catch too many MSRs in CPU reporter! */))
[51344]389 {
[91266]390 LogRel(("CPUM: Too many MSR ranges! %#x, max %#x\n",
391 cMsrRanges + cNewRanges, RT_ELEMENTS(pVM->cpum.s.GuestInfo.aMsrRanges)));
392 return NULL;
[51344]393 }
[91266]394 if (pVM)
[49893]395 {
[91266]396 Assert(cMsrRanges == pVM->cpum.s.GuestInfo.cMsrRanges);
397 Assert(*ppaMsrRanges == pVM->cpum.s.GuestInfo.aMsrRanges);
398 }
399 else
400 {
401 if (cMsrRanges + cNewRanges > RT_ALIGN_32(cMsrRanges, 16))
[49893]402 {
[51271]403
[91266]404 uint32_t const cNew = RT_ALIGN_32(cMsrRanges + cNewRanges, 16);
405 void *pvNew = RTMemRealloc(*ppaMsrRanges, cNew * sizeof(**ppaMsrRanges));
406 if (pvNew)
407 *ppaMsrRanges = (PCPUMMSRRANGE)pvNew;
408 else
[51271]409 {
410 RTMemFree(*ppaMsrRanges);
411 *ppaMsrRanges = NULL;
412 return NULL;
413 }
414 }
[49893]415 }
[51344]416
[49893]417 return *ppaMsrRanges;
418}
419
420
421/**
422 * Inserts a new MSR range in into an sorted MSR range array.
423 *
424 * If the new MSR range overlaps existing ranges, the existing ones will be
425 * adjusted/removed to fit in the new one.
426 *
427 * @returns VBox status code.
428 * @retval VINF_SUCCESS
429 * @retval VERR_NO_MEMORY
430 *
[58122]431 * @param pVM The cross context VM structure. If NULL,
432 * use the process heap, otherwise the VM's hyper heap.
[49893]433 * @param ppaMsrRanges The variable pointing to the ranges (input/output).
[51271]434 * Must be NULL if using the hyper heap.
435 * @param pcMsrRanges The variable holding number of ranges. Must be NULL
436 * if using the hyper heap.
[49893]437 * @param pNewRange The new range.
438 */
[51271]439int cpumR3MsrRangesInsert(PVM pVM, PCPUMMSRRANGE *ppaMsrRanges, uint32_t *pcMsrRanges, PCCPUMMSRRANGE pNewRange)
[49893]440{
441 Assert(pNewRange->uLast >= pNewRange->uFirst);
442 Assert(pNewRange->enmRdFn > kCpumMsrRdFn_Invalid && pNewRange->enmRdFn < kCpumMsrRdFn_End);
443 Assert(pNewRange->enmWrFn > kCpumMsrWrFn_Invalid && pNewRange->enmWrFn < kCpumMsrWrFn_End);
444
445 /*
[51271]446 * Validate and use the VM's MSR ranges array if we are using the hyper heap.
447 */
448 if (pVM)
449 {
450 AssertReturn(!ppaMsrRanges, VERR_INVALID_PARAMETER);
451 AssertReturn(!pcMsrRanges, VERR_INVALID_PARAMETER);
[91266]452 AssertReturn(pVM->cpum.s.GuestInfo.paMsrRangesR3 == pVM->cpum.s.GuestInfo.aMsrRanges, VERR_INTERNAL_ERROR_3);
[51271]453
454 ppaMsrRanges = &pVM->cpum.s.GuestInfo.paMsrRangesR3;
455 pcMsrRanges = &pVM->cpum.s.GuestInfo.cMsrRanges;
456 }
[63820]457 else
458 {
459 AssertReturn(ppaMsrRanges, VERR_INVALID_POINTER);
460 AssertReturn(pcMsrRanges, VERR_INVALID_POINTER);
461 }
[51271]462
[51334]463 uint32_t cMsrRanges = *pcMsrRanges;
464 PCPUMMSRRANGE paMsrRanges = *ppaMsrRanges;
465
[51271]466 /*
[49893]467 * Optimize the linear insertion case where we add new entries at the end.
468 */
469 if ( cMsrRanges > 0
470 && paMsrRanges[cMsrRanges - 1].uLast < pNewRange->uFirst)
471 {
[51271]472 paMsrRanges = cpumR3MsrRangesEnsureSpace(pVM, ppaMsrRanges, cMsrRanges, 1);
[49893]473 if (!paMsrRanges)
474 return VERR_NO_MEMORY;
475 paMsrRanges[cMsrRanges] = *pNewRange;
476 *pcMsrRanges += 1;
477 }
478 else
479 {
480 uint32_t i = cpumR3MsrRangesBinSearch(paMsrRanges, cMsrRanges, pNewRange->uFirst);
481 Assert(i == cMsrRanges || pNewRange->uFirst <= paMsrRanges[i].uLast);
482 Assert(i == 0 || pNewRange->uFirst > paMsrRanges[i - 1].uLast);
483
484 /*
485 * Adding an entirely new entry?
486 */
487 if ( i >= cMsrRanges
488 || pNewRange->uLast < paMsrRanges[i].uFirst)
489 {
[51271]490 paMsrRanges = cpumR3MsrRangesEnsureSpace(pVM, ppaMsrRanges, cMsrRanges, 1);
[49893]491 if (!paMsrRanges)
492 return VERR_NO_MEMORY;
493 if (i < cMsrRanges)
494 memmove(&paMsrRanges[i + 1], &paMsrRanges[i], (cMsrRanges - i) * sizeof(paMsrRanges[0]));
495 paMsrRanges[i] = *pNewRange;
496 *pcMsrRanges += 1;
497 }
498 /*
499 * Replace existing entry?
500 */
501 else if ( pNewRange->uFirst == paMsrRanges[i].uFirst
502 && pNewRange->uLast == paMsrRanges[i].uLast)
503 paMsrRanges[i] = *pNewRange;
504 /*
505 * Splitting an existing entry?
506 */
507 else if ( pNewRange->uFirst > paMsrRanges[i].uFirst
508 && pNewRange->uLast < paMsrRanges[i].uLast)
509 {
[51271]510 paMsrRanges = cpumR3MsrRangesEnsureSpace(pVM, ppaMsrRanges, cMsrRanges, 2);
[49893]511 if (!paMsrRanges)
512 return VERR_NO_MEMORY;
513 if (i < cMsrRanges)
514 memmove(&paMsrRanges[i + 2], &paMsrRanges[i], (cMsrRanges - i) * sizeof(paMsrRanges[0]));
515 paMsrRanges[i + 1] = *pNewRange;
516 paMsrRanges[i + 2] = paMsrRanges[i];
517 paMsrRanges[i ].uLast = pNewRange->uFirst - 1;
518 paMsrRanges[i + 2].uFirst = pNewRange->uLast + 1;
519 *pcMsrRanges += 2;
520 }
521 /*
522 * Complicated scenarios that can affect more than one range.
523 *
524 * The current code does not optimize memmove calls when replacing
525 * one or more existing ranges, because it's tedious to deal with and
526 * not expected to be a frequent usage scenario.
527 */
528 else
529 {
530 /* Adjust start of first match? */
531 if ( pNewRange->uFirst <= paMsrRanges[i].uFirst
532 && pNewRange->uLast < paMsrRanges[i].uLast)
533 paMsrRanges[i].uFirst = pNewRange->uLast + 1;
534 else
535 {
536 /* Adjust end of first match? */
537 if (pNewRange->uFirst > paMsrRanges[i].uFirst)
538 {
539 Assert(paMsrRanges[i].uLast >= pNewRange->uFirst);
540 paMsrRanges[i].uLast = pNewRange->uFirst - 1;
541 i++;
542 }
543 /* Replace the whole first match (lazy bird). */
544 else
545 {
546 if (i + 1 < cMsrRanges)
547 memmove(&paMsrRanges[i], &paMsrRanges[i + 1], (cMsrRanges - i - 1) * sizeof(paMsrRanges[0]));
548 cMsrRanges = *pcMsrRanges -= 1;
549 }
550
551 /* Do the new range affect more ranges? */
552 while ( i < cMsrRanges
553 && pNewRange->uLast >= paMsrRanges[i].uFirst)
554 {
555 if (pNewRange->uLast < paMsrRanges[i].uLast)
556 {
557 /* Adjust the start of it, then we're done. */
558 paMsrRanges[i].uFirst = pNewRange->uLast + 1;
559 break;
560 }
561
562 /* Remove it entirely. */
563 if (i + 1 < cMsrRanges)
564 memmove(&paMsrRanges[i], &paMsrRanges[i + 1], (cMsrRanges - i - 1) * sizeof(paMsrRanges[0]));
565 cMsrRanges = *pcMsrRanges -= 1;
566 }
567 }
568
569 /* Now, perform a normal insertion. */
[51271]570 paMsrRanges = cpumR3MsrRangesEnsureSpace(pVM, ppaMsrRanges, cMsrRanges, 1);
[49893]571 if (!paMsrRanges)
572 return VERR_NO_MEMORY;
573 if (i < cMsrRanges)
574 memmove(&paMsrRanges[i + 1], &paMsrRanges[i], (cMsrRanges - i) * sizeof(paMsrRanges[0]));
575 paMsrRanges[i] = *pNewRange;
576 *pcMsrRanges += 1;
577 }
578 }
579
580 return VINF_SUCCESS;
581}
582
583
[49977]584/**
[76678]585 * Reconciles CPUID info with MSRs (selected ones).
586 *
587 * @returns VBox status code.
588 * @param pVM The cross context VM structure.
589 */
590int cpumR3MsrReconcileWithCpuId(PVM pVM)
591{
592 PCCPUMMSRRANGE papToAdd[10];
[78632]593 uint32_t cToAdd = 0;
[76678]594
595 /*
596 * The IA32_FLUSH_CMD MSR was introduced in MCUs for CVS-2018-3646 and associates.
597 */
598 if (pVM->cpum.s.GuestFeatures.fFlushCmd && !cpumLookupMsrRange(pVM, MSR_IA32_FLUSH_CMD))
599 {
600 static CPUMMSRRANGE const s_FlushCmd =
601 {
602 /*.uFirst =*/ MSR_IA32_FLUSH_CMD,
603 /*.uLast =*/ MSR_IA32_FLUSH_CMD,
604 /*.enmRdFn =*/ kCpumMsrRdFn_WriteOnly,
605 /*.enmWrFn =*/ kCpumMsrWrFn_Ia32FlushCmd,
606 /*.offCpumCpu =*/ UINT16_MAX,
607 /*.fReserved =*/ 0,
608 /*.uValue =*/ 0,
609 /*.fWrIgnMask =*/ 0,
610 /*.fWrGpMask =*/ ~MSR_IA32_FLUSH_CMD_F_L1D,
611 /*.szName = */ "IA32_FLUSH_CMD"
612 };
613 papToAdd[cToAdd++] = &s_FlushCmd;
614 }
615
616 /*
[78632]617 * The MSR_IA32_ARCH_CAPABILITIES was introduced in various spectre MCUs, or at least
618 * documented in relation to such.
619 */
620 if (pVM->cpum.s.GuestFeatures.fArchCap && !cpumLookupMsrRange(pVM, MSR_IA32_ARCH_CAPABILITIES))
621 {
622 static CPUMMSRRANGE const s_ArchCaps =
623 {
624 /*.uFirst =*/ MSR_IA32_ARCH_CAPABILITIES,
625 /*.uLast =*/ MSR_IA32_ARCH_CAPABILITIES,
626 /*.enmRdFn =*/ kCpumMsrRdFn_Ia32ArchCapabilities,
627 /*.enmWrFn =*/ kCpumMsrWrFn_ReadOnly,
628 /*.offCpumCpu =*/ UINT16_MAX,
629 /*.fReserved =*/ 0,
630 /*.uValue =*/ 0,
631 /*.fWrIgnMask =*/ 0,
632 /*.fWrGpMask =*/ UINT64_MAX,
633 /*.szName = */ "IA32_ARCH_CAPABILITIES"
634 };
635 papToAdd[cToAdd++] = &s_ArchCaps;
636 }
637
638 /*
[76678]639 * Do the adding.
640 */
641 for (uint32_t i = 0; i < cToAdd; i++)
642 {
643 PCCPUMMSRRANGE pRange = papToAdd[i];
644 LogRel(("CPUM: MSR/CPUID reconciliation insert: %#010x %s\n", pRange->uFirst, pRange->szName));
645 int rc = cpumR3MsrRangesInsert(NULL /* pVM */, &pVM->cpum.s.GuestInfo.paMsrRangesR3, &pVM->cpum.s.GuestInfo.cMsrRanges,
646 pRange);
647 if (RT_FAILURE(rc))
648 return rc;
649 }
650 return VINF_SUCCESS;
651}
652
653
654/**
[49978]655 * Worker for cpumR3MsrApplyFudge that applies one table.
656 *
657 * @returns VBox status code.
[58122]658 * @param pVM The cross context VM structure.
[49978]659 * @param paRanges Array of MSRs to fudge.
660 * @param cRanges Number of MSRs in the array.
661 */
662static int cpumR3MsrApplyFudgeTable(PVM pVM, PCCPUMMSRRANGE paRanges, size_t cRanges)
663{
664 for (uint32_t i = 0; i < cRanges; i++)
665 if (!cpumLookupMsrRange(pVM, paRanges[i].uFirst))
666 {
667 LogRel(("CPUM: MSR fudge: %#010x %s\n", paRanges[i].uFirst, paRanges[i].szName));
[51274]668 int rc = cpumR3MsrRangesInsert(NULL /* pVM */, &pVM->cpum.s.GuestInfo.paMsrRangesR3, &pVM->cpum.s.GuestInfo.cMsrRanges,
[49978]669 &paRanges[i]);
670 if (RT_FAILURE(rc))
671 return rc;
672 }
673 return VINF_SUCCESS;
674}
675
676
677/**
[49977]678 * Fudges the MSRs that guest are known to access in some odd cases.
679 *
680 * A typical example is a VM that has been moved between different hosts where
681 * for instance the cpu vendor differs.
682 *
[60757]683 * Another example is older CPU profiles (e.g. Atom Bonnet) for newer CPUs (e.g.
684 * Atom Silvermont), where features reported thru CPUID aren't present in the
685 * MSRs (e.g. AMD64_TSC_AUX).
686 *
687 *
[49977]688 * @returns VBox status code.
[58122]689 * @param pVM The cross context VM structure.
[49977]690 */
691int cpumR3MsrApplyFudge(PVM pVM)
692{
[49978]693 /*
694 * Basic.
695 */
[49977]696 static CPUMMSRRANGE const s_aFudgeMsrs[] =
697 {
[49978]698 MFO(0x00000000, "IA32_P5_MC_ADDR", Ia32P5McAddr),
[49977]699 MFX(0x00000001, "IA32_P5_MC_TYPE", Ia32P5McType, Ia32P5McType, 0, 0, UINT64_MAX),
700 MVO(0x00000017, "IA32_PLATFORM_ID", 0),
701 MFN(0x0000001b, "IA32_APIC_BASE", Ia32ApicBase, Ia32ApicBase),
702 MVI(0x0000008b, "BIOS_SIGN", 0),
703 MFX(0x000000fe, "IA32_MTRRCAP", Ia32MtrrCap, ReadOnly, 0x508, 0, 0),
704 MFX(0x00000179, "IA32_MCG_CAP", Ia32McgCap, ReadOnly, 0x005, 0, 0),
705 MFX(0x0000017a, "IA32_MCG_STATUS", Ia32McgStatus, Ia32McgStatus, 0, ~(uint64_t)UINT32_MAX, 0),
706 MFN(0x000001a0, "IA32_MISC_ENABLE", Ia32MiscEnable, Ia32MiscEnable),
707 MFN(0x000001d9, "IA32_DEBUGCTL", Ia32DebugCtl, Ia32DebugCtl),
708 MFO(0x000001db, "P6_LAST_BRANCH_FROM_IP", P6LastBranchFromIp),
709 MFO(0x000001dc, "P6_LAST_BRANCH_TO_IP", P6LastBranchToIp),
710 MFO(0x000001dd, "P6_LAST_INT_FROM_IP", P6LastIntFromIp),
711 MFO(0x000001de, "P6_LAST_INT_TO_IP", P6LastIntToIp),
712 MFS(0x00000277, "IA32_PAT", Ia32Pat, Ia32Pat, Guest.msrPAT),
713 MFZ(0x000002ff, "IA32_MTRR_DEF_TYPE", Ia32MtrrDefType, Ia32MtrrDefType, GuestMsrs.msr.MtrrDefType, 0, ~(uint64_t)0xc07),
714 MFN(0x00000400, "IA32_MCi_CTL_STATUS_ADDR_MISC", Ia32McCtlStatusAddrMiscN, Ia32McCtlStatusAddrMiscN),
715 };
[49978]716 int rc = cpumR3MsrApplyFudgeTable(pVM, &s_aFudgeMsrs[0], RT_ELEMENTS(s_aFudgeMsrs));
717 AssertLogRelRCReturn(rc, rc);
[49977]718
[49978]719 /*
720 * XP might mistake opterons and other newer CPUs for P4s.
721 */
722 if (pVM->cpum.s.GuestFeatures.uFamily >= 0xf)
723 {
724 static CPUMMSRRANGE const s_aP4FudgeMsrs[] =
[49977]725 {
[49978]726 MFX(0x0000002c, "P4_EBC_FREQUENCY_ID", IntelP4EbcFrequencyId, IntelP4EbcFrequencyId, 0xf12010f, UINT64_MAX, 0),
727 };
[49979]728 rc = cpumR3MsrApplyFudgeTable(pVM, &s_aP4FudgeMsrs[0], RT_ELEMENTS(s_aP4FudgeMsrs));
[49978]729 AssertLogRelRCReturn(rc, rc);
730 }
[49977]731
[60757]732 if (pVM->cpum.s.GuestFeatures.fRdTscP)
733 {
734 static CPUMMSRRANGE const s_aRdTscPFudgeMsrs[] =
735 {
736 MFX(0xc0000103, "AMD64_TSC_AUX", Amd64TscAux, Amd64TscAux, 0, 0, ~(uint64_t)UINT32_MAX),
737 };
738 rc = cpumR3MsrApplyFudgeTable(pVM, &s_aRdTscPFudgeMsrs[0], RT_ELEMENTS(s_aRdTscPFudgeMsrs));
739 AssertLogRelRCReturn(rc, rc);
740 }
741
[83092]742 /*
743 * Windows 10 incorrectly writes to MSR_IA32_TSX_CTRL without checking
744 * CPUID.ARCH_CAP(EAX=7h,ECX=0):EDX[bit 29] or the MSR feature bits in
745 * MSR_IA32_ARCH_CAPABILITIES[bit 7], see @bugref{9630}.
746 * Ignore writes to this MSR and return 0 on reads.
747 */
748 if (pVM->cpum.s.GuestFeatures.fArchCap)
749 {
750 static CPUMMSRRANGE const s_aTsxCtrl[] =
751 {
752 MVI(MSR_IA32_TSX_CTRL, "IA32_TSX_CTRL", 0),
753 };
754 rc = cpumR3MsrApplyFudgeTable(pVM, &s_aTsxCtrl[0], RT_ELEMENTS(s_aTsxCtrl));
755 AssertLogRelRCReturn(rc, rc);
756 }
757
[49978]758 return rc;
[49977]759}
760
[93519]761#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
[49977]762
[58561]763/**
764 * Do we consider @a enmConsider a better match for @a enmTarget than
765 * @a enmFound?
766 *
767 * Only called when @a enmConsider isn't exactly what we're looking for.
768 *
769 * @returns true/false.
770 * @param enmConsider The new microarch to consider.
771 * @param enmTarget The target microarch.
772 * @param enmFound The best microarch match we've found thus far.
773 */
774DECLINLINE(bool) cpumR3DbIsBetterMarchMatch(CPUMMICROARCH enmConsider, CPUMMICROARCH enmTarget, CPUMMICROARCH enmFound)
775{
776 Assert(enmConsider != enmTarget);
777
778 /*
779 * If we've got an march match, don't bother with enmConsider.
780 */
781 if (enmFound == enmTarget)
782 return false;
783
784 /*
785 * Found is below: Pick 'consider' if it's closer to the target or above it.
786 */
787 if (enmFound < enmTarget)
788 return enmConsider > enmFound;
789
790 /*
791 * Found is above: Pick 'consider' if it's also above (paranoia: or equal)
792 * and but closer to the target.
793 */
794 return enmConsider >= enmTarget && enmConsider < enmFound;
795}
796
797
798/**
799 * Do we consider @a enmConsider a better match for @a enmTarget than
800 * @a enmFound?
801 *
802 * Only called for intel family 06h CPUs.
803 *
804 * @returns true/false.
805 * @param enmConsider The new microarch to consider.
806 * @param enmTarget The target microarch.
807 * @param enmFound The best microarch match we've found thus far.
808 */
809static bool cpumR3DbIsBetterIntelFam06Match(CPUMMICROARCH enmConsider, CPUMMICROARCH enmTarget, CPUMMICROARCH enmFound)
810{
811 /* Check intel family 06h claims. */
812 AssertReturn(enmConsider >= kCpumMicroarch_Intel_P6_Core_Atom_First && enmConsider <= kCpumMicroarch_Intel_P6_Core_Atom_End,
813 false);
[89934]814 AssertReturn( (enmTarget >= kCpumMicroarch_Intel_P6_Core_Atom_First && enmTarget <= kCpumMicroarch_Intel_P6_Core_Atom_End)
815 || enmTarget == kCpumMicroarch_Intel_Unknown,
[58561]816 false);
817
818 /* Put matches out of the way. */
819 if (enmConsider == enmTarget)
820 return true;
821 if (enmFound == enmTarget)
822 return false;
823
824 /* If found isn't a family 06h march, whatever we're considering must be a better choice. */
825 if ( enmFound < kCpumMicroarch_Intel_P6_Core_Atom_First
826 || enmFound > kCpumMicroarch_Intel_P6_Core_Atom_End)
827 return true;
828
829 /*
830 * The family 06h stuff is split into three categories:
831 * - Common P6 heritage
832 * - Core
833 * - Atom
834 *
835 * Determin which of the three arguments are Atom marchs, because that's
836 * all we need to make the right choice.
837 */
838 bool const fConsiderAtom = enmConsider >= kCpumMicroarch_Intel_Atom_First;
839 bool const fTargetAtom = enmTarget >= kCpumMicroarch_Intel_Atom_First;
840 bool const fFoundAtom = enmFound >= kCpumMicroarch_Intel_Atom_First;
841
842 /*
843 * Want atom:
844 */
845 if (fTargetAtom)
846 {
847 /* Pick the atom if we've got one of each.*/
848 if (fConsiderAtom != fFoundAtom)
849 return fConsiderAtom;
850 /* If we haven't got any atoms under consideration, pick a P6 or the earlier core.
851 Note! Not entirely sure Dothan is the best choice, but it'll do for now. */
852 if (!fConsiderAtom)
853 {
854 if (enmConsider > enmFound)
855 return enmConsider <= kCpumMicroarch_Intel_P6_M_Dothan;
856 return enmFound > kCpumMicroarch_Intel_P6_M_Dothan;
857 }
858 /* else: same category, default comparison rules. */
859 Assert(fConsiderAtom && fFoundAtom);
860 }
861 /*
862 * Want non-atom:
863 */
864 /* Pick the non-atom if we've got one of each. */
865 else if (fConsiderAtom != fFoundAtom)
866 return fFoundAtom;
867 /* If we've only got atoms under consideration, pick the older one just to pick something. */
868 else if (fConsiderAtom)
869 return enmConsider < enmFound;
870 else
871 Assert(!fConsiderAtom && !fFoundAtom);
872
873 /*
874 * Same basic category. Do same compare as caller.
875 */
876 return cpumR3DbIsBetterMarchMatch(enmConsider, enmTarget, enmFound);
877}
878
[93519]879#endif /* RT_ARCH_X86 || RT_ARCH_AMD64 */
[58561]880
[49893]881int cpumR3DbGetCpuInfo(const char *pszName, PCPUMINFO pInfo)
882{
883 CPUMDBENTRY const *pEntry = NULL;
884 int rc;
885
886 if (!strcmp(pszName, "host"))
[87255]887#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
[49893]888 {
889 /*
890 * Create a CPU database entry for the host CPU. This means getting
891 * the CPUID bits from the real CPU and grabbing the closest matching
892 * database entry for MSRs.
893 */
894 rc = CPUMR3CpuIdDetectUnknownLeafMethod(&pInfo->enmUnknownCpuIdMethod, &pInfo->DefCpuId);
895 if (RT_FAILURE(rc))
896 return rc;
[94931]897 rc = CPUMCpuIdCollectLeavesX86(&pInfo->paCpuIdLeavesR3, &pInfo->cCpuIdLeaves);
[49893]898 if (RT_FAILURE(rc))
899 return rc;
[66403]900 pInfo->fMxCsrMask = CPUMR3DeterminHostMxCsrMask();
[49893]901
902 /* Lookup database entry for MSRs. */
[94931]903 CPUMCPUVENDOR const enmVendor = CPUMCpuIdDetectX86VendorEx(pInfo->paCpuIdLeavesR3[0].uEax,
904 pInfo->paCpuIdLeavesR3[0].uEbx,
905 pInfo->paCpuIdLeavesR3[0].uEcx,
906 pInfo->paCpuIdLeavesR3[0].uEdx);
[49893]907 uint32_t const uStd1Eax = pInfo->paCpuIdLeavesR3[1].uEax;
[93515]908 uint8_t const uFamily = RTX86GetCpuFamily(uStd1Eax);
909 uint8_t const uModel = RTX86GetCpuModel(uStd1Eax, enmVendor == CPUMCPUVENDOR_INTEL);
910 uint8_t const uStepping = RTX86GetCpuStepping(uStd1Eax);
[94931]911 CPUMMICROARCH const enmMicroarch = CPUMCpuIdDetermineX86MicroarchEx(enmVendor, uFamily, uModel, uStepping);
[49893]912
913 for (unsigned i = 0; i < RT_ELEMENTS(g_apCpumDbEntries); i++)
914 {
915 CPUMDBENTRY const *pCur = g_apCpumDbEntries[i];
916 if ((CPUMCPUVENDOR)pCur->enmVendor == enmVendor)
917 {
[50157]918 /* Match against Family, Microarch, model and stepping. Except
919 for family, always match the closer with preference given to
920 the later/older ones. */
921 if (pCur->uFamily == uFamily)
[49893]922 {
[50157]923 if (pCur->enmMicroarch == enmMicroarch)
[49893]924 {
[50157]925 if (pCur->uModel == uModel)
[49893]926 {
[50157]927 if (pCur->uStepping == uStepping)
928 {
929 /* Perfect match. */
930 pEntry = pCur;
931 break;
932 }
933
934 if ( !pEntry
935 || pEntry->uModel != uModel
936 || pEntry->enmMicroarch != enmMicroarch
937 || pEntry->uFamily != uFamily)
938 pEntry = pCur;
939 else if ( pCur->uStepping >= uStepping
940 ? pCur->uStepping < pEntry->uStepping || pEntry->uStepping < uStepping
941 : pCur->uStepping > pEntry->uStepping)
942 pEntry = pCur;
[49893]943 }
[50157]944 else if ( !pEntry
945 || pEntry->enmMicroarch != enmMicroarch
946 || pEntry->uFamily != uFamily)
[49893]947 pEntry = pCur;
[50157]948 else if ( pCur->uModel >= uModel
949 ? pCur->uModel < pEntry->uModel || pEntry->uModel < uModel
950 : pCur->uModel > pEntry->uModel)
[49893]951 pEntry = pCur;
952 }
[50157]953 else if ( !pEntry
954 || pEntry->uFamily != uFamily)
[49893]955 pEntry = pCur;
[58561]956 /* Special march matching rules applies to intel family 06h. */
957 else if ( enmVendor == CPUMCPUVENDOR_INTEL
958 && uFamily == 6
959 ? cpumR3DbIsBetterIntelFam06Match(pCur->enmMicroarch, enmMicroarch, pEntry->enmMicroarch)
960 : cpumR3DbIsBetterMarchMatch(pCur->enmMicroarch, enmMicroarch, pEntry->enmMicroarch))
[50157]961 pEntry = pCur;
[49893]962 }
[50157]963 /* We don't do closeness matching on family, we use the first
964 entry for the CPU vendor instead. (P4 workaround.) */
965 else if (!pEntry)
966 pEntry = pCur;
[49893]967 }
968 }
969
970 if (pEntry)
[51641]971 LogRel(("CPUM: Matched host CPU %s %#x/%#x/%#x %s with CPU DB entry '%s' (%s %#x/%#x/%#x %s)\n",
[94931]972 CPUMCpuVendorName(enmVendor), uFamily, uModel, uStepping, CPUMMicroarchName(enmMicroarch),
973 pEntry->pszName, CPUMCpuVendorName((CPUMCPUVENDOR)pEntry->enmVendor), pEntry->uFamily, pEntry->uModel,
974 pEntry->uStepping, CPUMMicroarchName(pEntry->enmMicroarch) ));
[49893]975 else
976 {
977 pEntry = g_apCpumDbEntries[0];
[51641]978 LogRel(("CPUM: No matching processor database entry %s %#x/%#x/%#x %s, falling back on '%s'\n",
[94931]979 CPUMCpuVendorName(enmVendor), uFamily, uModel, uStepping, CPUMMicroarchName(enmMicroarch),
[49893]980 pEntry->pszName));
981 }
982 }
983 else
[87255]984#else
985 pszName = g_apCpumDbEntries[0]->pszName; /* Just pick the first entry for non-x86 hosts. */
986#endif
[49893]987 {
988 /*
989 * We're supposed to be emulating a specific CPU that is included in
990 * our CPU database. The CPUID tables needs to be copied onto the
991 * heap so the caller can modify them and so they can be freed like
992 * in the host case above.
993 */
994 for (unsigned i = 0; i < RT_ELEMENTS(g_apCpumDbEntries); i++)
995 if (!strcmp(pszName, g_apCpumDbEntries[i]->pszName))
996 {
997 pEntry = g_apCpumDbEntries[i];
998 break;
999 }
1000 if (!pEntry)
1001 {
1002 LogRel(("CPUM: Cannot locate any CPU by the name '%s'\n", pszName));
1003 return VERR_CPUM_DB_CPU_NOT_FOUND;
1004 }
1005
1006 pInfo->cCpuIdLeaves = pEntry->cCpuIdLeaves;
1007 if (pEntry->cCpuIdLeaves)
1008 {
[60411]1009 /* Must allocate a multiple of 16 here, matching cpumR3CpuIdEnsureSpace. */
1010 size_t cbExtra = sizeof(pEntry->paCpuIdLeaves[0]) * (RT_ALIGN(pEntry->cCpuIdLeaves, 16) - pEntry->cCpuIdLeaves);
1011 pInfo->paCpuIdLeavesR3 = (PCPUMCPUIDLEAF)RTMemDupEx(pEntry->paCpuIdLeaves,
1012 sizeof(pEntry->paCpuIdLeaves[0]) * pEntry->cCpuIdLeaves,
1013 cbExtra);
[49893]1014 if (!pInfo->paCpuIdLeavesR3)
1015 return VERR_NO_MEMORY;
1016 }
1017 else
1018 pInfo->paCpuIdLeavesR3 = NULL;
1019
1020 pInfo->enmUnknownCpuIdMethod = pEntry->enmUnknownCpuId;
[66403]1021 pInfo->DefCpuId = pEntry->DefUnknownCpuId;
1022 pInfo->fMxCsrMask = pEntry->fMxCsrMask;
[49893]1023
[51641]1024 LogRel(("CPUM: Using CPU DB entry '%s' (%s %#x/%#x/%#x %s)\n",
[94931]1025 pEntry->pszName, CPUMCpuVendorName((CPUMCPUVENDOR)pEntry->enmVendor),
1026 pEntry->uFamily, pEntry->uModel, pEntry->uStepping, CPUMMicroarchName(pEntry->enmMicroarch) ));
[49893]1027 }
1028
1029 pInfo->fMsrMask = pEntry->fMsrMask;
1030 pInfo->iFirstExtCpuIdLeaf = 0; /* Set by caller. */
[50590]1031 pInfo->uScalableBusFreq = pEntry->uScalableBusFreq;
[49893]1032
1033 /*
1034 * Copy the MSR range.
1035 */
1036 uint32_t cMsrs = 0;
1037 PCPUMMSRRANGE paMsrs = NULL;
1038
1039 PCCPUMMSRRANGE pCurMsr = pEntry->paMsrRanges;
1040 uint32_t cLeft = pEntry->cMsrRanges;
1041 while (cLeft-- > 0)
1042 {
[51271]1043 rc = cpumR3MsrRangesInsert(NULL /* pVM */, &paMsrs, &cMsrs, pCurMsr);
[49893]1044 if (RT_FAILURE(rc))
1045 {
1046 Assert(!paMsrs); /* The above function frees this. */
1047 RTMemFree(pInfo->paCpuIdLeavesR3);
1048 pInfo->paCpuIdLeavesR3 = NULL;
1049 return rc;
1050 }
1051 pCurMsr++;
1052 }
1053
1054 pInfo->paMsrRangesR3 = paMsrs;
1055 pInfo->cMsrRanges = cMsrs;
1056 return VINF_SUCCESS;
1057}
1058
1059
1060/**
[51271]1061 * Insert an MSR range into the VM.
1062 *
1063 * If the new MSR range overlaps existing ranges, the existing ones will be
1064 * adjusted/removed to fit in the new one.
1065 *
1066 * @returns VBox status code.
[58122]1067 * @param pVM The cross context VM structure.
[51271]1068 * @param pNewRange Pointer to the MSR range being inserted.
1069 */
1070VMMR3DECL(int) CPUMR3MsrRangesInsert(PVM pVM, PCCPUMMSRRANGE pNewRange)
1071{
[51283]1072 AssertReturn(pVM, VERR_INVALID_PARAMETER);
1073 AssertReturn(pNewRange, VERR_INVALID_PARAMETER);
1074
[51271]1075 return cpumR3MsrRangesInsert(pVM, NULL /* ppaMsrRanges */, NULL /* pcMsrRanges */, pNewRange);
1076}
1077
1078
1079/**
[49893]1080 * Register statistics for the MSRs.
1081 *
1082 * This must not be called before the MSRs have been finalized and moved to the
1083 * hyper heap.
1084 *
1085 * @returns VBox status code.
[58122]1086 * @param pVM The cross context VM structure.
[49893]1087 */
1088int cpumR3MsrRegStats(PVM pVM)
1089{
1090 /*
1091 * Global statistics.
1092 */
1093 PCPUM pCpum = &pVM->cpum.s;
1094 STAM_REL_REG(pVM, &pCpum->cMsrReads, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/Reads",
1095 STAMUNIT_OCCURENCES, "All RDMSRs making it to CPUM.");
1096 STAM_REL_REG(pVM, &pCpum->cMsrReadsRaiseGp, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/ReadsRaisingGP",
1097 STAMUNIT_OCCURENCES, "RDMSR raising #GPs, except unknown MSRs.");
1098 STAM_REL_REG(pVM, &pCpum->cMsrReadsUnknown, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/ReadsUnknown",
1099 STAMUNIT_OCCURENCES, "RDMSR on unknown MSRs (raises #GP).");
1100 STAM_REL_REG(pVM, &pCpum->cMsrWrites, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/Writes",
[71805]1101 STAMUNIT_OCCURENCES, "All WRMSRs making it to CPUM.");
[49972]1102 STAM_REL_REG(pVM, &pCpum->cMsrWritesRaiseGp, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/WritesRaisingGP",
[49893]1103 STAMUNIT_OCCURENCES, "WRMSR raising #GPs, except unknown MSRs.");
[49972]1104 STAM_REL_REG(pVM, &pCpum->cMsrWritesToIgnoredBits, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/WritesToIgnoredBits",
[49893]1105 STAMUNIT_OCCURENCES, "Writing of ignored bits.");
1106 STAM_REL_REG(pVM, &pCpum->cMsrWritesUnknown, STAMTYPE_COUNTER, "/CPUM/MSR-Totals/WritesUnknown",
1107 STAMUNIT_OCCURENCES, "WRMSR on unknown MSRs (raises #GP).");
1108
1109
1110# ifdef VBOX_WITH_STATISTICS
1111 /*
1112 * Per range.
1113 */
1114 PCPUMMSRRANGE paRanges = pVM->cpum.s.GuestInfo.paMsrRangesR3;
1115 uint32_t cRanges = pVM->cpum.s.GuestInfo.cMsrRanges;
1116 for (uint32_t i = 0; i < cRanges; i++)
1117 {
1118 char szName[160];
1119 ssize_t cchName;
1120
1121 if (paRanges[i].uFirst == paRanges[i].uLast)
1122 cchName = RTStrPrintf(szName, sizeof(szName), "/CPUM/MSRs/%#010x-%s",
1123 paRanges[i].uFirst, paRanges[i].szName);
1124 else
1125 cchName = RTStrPrintf(szName, sizeof(szName), "/CPUM/MSRs/%#010x-%#010x-%s",
1126 paRanges[i].uFirst, paRanges[i].uLast, paRanges[i].szName);
1127
1128 RTStrCopy(&szName[cchName], sizeof(szName) - cchName, "-reads");
1129 STAMR3Register(pVM, &paRanges[i].cReads, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "RDMSR");
1130
1131 RTStrCopy(&szName[cchName], sizeof(szName) - cchName, "-writes");
1132 STAMR3Register(pVM, &paRanges[i].cWrites, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, "WRMSR");
1133
1134 RTStrCopy(&szName[cchName], sizeof(szName) - cchName, "-GPs");
1135 STAMR3Register(pVM, &paRanges[i].cGps, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, "#GPs");
1136
1137 RTStrCopy(&szName[cchName], sizeof(szName) - cchName, "-ign-bits-writes");
1138 STAMR3Register(pVM, &paRanges[i].cIgnoredBits, STAMTYPE_COUNTER, STAMVISIBILITY_USED, szName, STAMUNIT_OCCURENCES, "WRMSR w/ ignored bits");
1139 }
1140# endif /* VBOX_WITH_STATISTICS */
1141
1142 return VINF_SUCCESS;
1143}
1144
1145#endif /* !CPUM_DB_STANDALONE */
1146
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use