VirtualBox

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

Last change on this file since 94617 was 93519, checked in by vboxsync, 3 years ago

VMM/CPUM: Quick arm64 build adjustments. bugref:9898

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette