VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/CPUMAllSysRegs-armv8.cpp@ 99196

Last change on this file since 99196 was 99196, checked in by vboxsync, 21 months ago

VMM: Start on system register handling for ARMv8 (which is ver similar to how we handle MSR accesses on x86), bugref:10385

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 KB
Line 
1/* $Id: CPUMAllSysRegs-armv8.cpp 99196 2023-03-28 13:06:05Z vboxsync $ */
2/** @file
3 * CPUM - ARMv8 CPU System Registers.
4 */
5
6/*
7 * Copyright (C) 2023 Oracle and/or its affiliates.
8 *
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
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_CPUM
33#include <VBox/vmm/cpum.h>
34#include "CPUMInternal-armv8.h"
35#include <VBox/vmm/vmcc.h>
36#include <VBox/err.h>
37
38
39/*********************************************************************************************************************************
40* Defined Constants And Macros *
41*********************************************************************************************************************************/
42/**
43 * Validates the CPUMSYSREGRANGE::offCpumCpu value and declares a local variable
44 * pointing to it.
45 *
46 * ASSUMES sizeof(a_Type) is a power of two and that the member is aligned
47 * correctly.
48 */
49#define CPUM_SYSREG_ASSERT_CPUMCPU_OFFSET_RETURN(a_pVCpu, a_pRange, a_Type, a_VarName) \
50 AssertMsgReturn( (a_pRange)->offCpumCpu >= 8 \
51 && (a_pRange)->offCpumCpu < sizeof(CPUMCPU) \
52 && !((a_pRange)->offCpumCpu & (RT_MIN(sizeof(a_Type), 8) - 1)) \
53 , ("offCpumCpu=%#x %s\n", (a_pRange)->offCpumCpu, (a_pRange)->szName), \
54 VERR_CPUM_MSR_BAD_CPUMCPU_OFFSET); \
55 a_Type *a_VarName = (a_Type *)((uintptr_t)&(a_pVCpu)->cpum.s + (a_pRange)->offCpumCpu)
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61
62/**
63 * Implements reading one or more system registers.
64 *
65 * @returns VBox status code.
66 * @retval VINF_SUCCESS on success.
67 * @retval VINF_CPUM_R3_MSR_READ if the MSR read could not be serviced in the
68 * current context (raw-mode or ring-0).
69 * @retval VERR_CPUM_RAISE_GP_0 on failure (invalid system register).
70 *
71 * @param pVCpu The cross context virtual CPU structure.
72 * @param idSysReg The system register we're reading.
73 * @param pRange The system register range descriptor.
74 * @param puValue Where to return the value.
75 */
76typedef DECLCALLBACKTYPE(VBOXSTRICTRC, FNCPUMRDSYSREG,(PVMCPUCC pVCpu, uint32_t idSysReg, PCCPUMSYSREGRANGE pRange, uint64_t *puValue));
77/** Pointer to a MRS worker for a specific system register or range of system registers. */
78typedef FNCPUMRDSYSREG *PFNCPUMRDSYSREG;
79
80
81/**
82 * Implements writing one or more system registers.
83 *
84 * @retval VINF_SUCCESS on success.
85 * @retval VINF_CPUM_R3_MSR_WRITE if the MSR write could not be serviced in the
86 * current context (raw-mode or ring-0).
87 * @retval VERR_CPUM_RAISE_GP_0 on failure.
88 *
89 * @param pVCpu The cross context virtual CPU structure.
90 * @param idSysReg The system register we're writing.
91 * @param pRange The system register range descriptor.
92 * @param uValue The value to set, ignored bits masked.
93 * @param uRawValue The raw value with the ignored bits not masked.
94 */
95typedef DECLCALLBACKTYPE(VBOXSTRICTRC, FNCPUMWRSYSREG,(PVMCPUCC pVCpu, uint32_t idSysReg, PCCPUMSYSREGRANGE pRange,
96 uint64_t uValue, uint64_t uRawValue));
97/** Pointer to a MSR worker for a specific system register or range of system registers. */
98typedef FNCPUMWRSYSREG *PFNCPUMWRSYSREG;
99
100
101
102/*
103 * Generic functions.
104 * Generic functions.
105 * Generic functions.
106 */
107
108
109/** @callback_method_impl{FNCPUMRDSYSREG} */
110static DECLCALLBACK(VBOXSTRICTRC) cpumSysRegRd_FixedValue(PVMCPUCC pVCpu, uint32_t idSysReg, PCCPUMSYSREGRANGE pRange, uint64_t *puValue)
111{
112 RT_NOREF_PV(pVCpu); RT_NOREF_PV(idSysReg);
113 *puValue = pRange->uValue;
114 return VINF_SUCCESS;
115}
116
117
118/** @callback_method_impl{FNCPUMWRSYSREG} */
119static DECLCALLBACK(VBOXSTRICTRC) cpumSysRegWr_IgnoreWrite(PVMCPUCC pVCpu, uint32_t idSysReg, PCCPUMSYSREGRANGE pRange, uint64_t uValue, uint64_t uRawValue)
120{
121 RT_NOREF_PV(pVCpu); RT_NOREF_PV(idSysReg); RT_NOREF_PV(pRange); RT_NOREF_PV(uValue); RT_NOREF_PV(uRawValue);
122 Log(("CPUM: Ignoring MSR %#x (%s), %#llx\n", idSysReg, pRange->szName, uValue));
123 return VINF_SUCCESS;
124}
125
126
127/** @callback_method_impl{FNCPUMRDSYSREG} */
128static DECLCALLBACK(VBOXSTRICTRC) cpumSysRegRd_WriteOnly(PVMCPUCC pVCpu, uint32_t idSysReg, PCCPUMSYSREGRANGE pRange, uint64_t *puValue)
129{
130 RT_NOREF_PV(pVCpu); RT_NOREF_PV(idSysReg); RT_NOREF_PV(pRange); RT_NOREF_PV(puValue);
131 return VERR_CPUM_RAISE_GP_0;
132}
133
134
135/** @callback_method_impl{FNCPUMWRSYSREG} */
136static DECLCALLBACK(VBOXSTRICTRC) cpumSysRegWr_ReadOnly(PVMCPUCC pVCpu, uint32_t idSysReg, PCCPUMSYSREGRANGE pRange, uint64_t uValue, uint64_t uRawValue)
137{
138 RT_NOREF_PV(pVCpu); RT_NOREF_PV(idSysReg); RT_NOREF_PV(pRange); RT_NOREF_PV(uValue); RT_NOREF_PV(uRawValue);
139 Assert(pRange->fWrExcpMask == UINT64_MAX);
140 return VERR_CPUM_RAISE_GP_0;
141}
142
143
144
145
146/**
147 * System register read function table.
148 */
149static const struct READSYSREGCLANG11WEIRDNOTHROW { PFNCPUMRDSYSREG pfnRdSysReg; } g_aCpumRdSysRegFns[kCpumSysRegRdFn_End] =
150{
151 { NULL }, /* Invalid */
152 { cpumSysRegRd_FixedValue },
153 { NULL }, /* Alias */
154 { cpumSysRegRd_WriteOnly },
155};
156
157
158/**
159 * System register write function table.
160 */
161static const struct WRITESYSREGCLANG11WEIRDNOTHROW { PFNCPUMWRSYSREG pfnWrSysReg; } g_aCpumWrSysRegFns[kCpumSysRegWrFn_End] =
162{
163 { NULL }, /* Invalid */
164 { cpumSysRegWr_IgnoreWrite },
165 { cpumSysRegWr_ReadOnly },
166 { NULL }, /* Alias */
167};
168
169
170/**
171 * Looks up the range for the given system register.
172 *
173 * @returns Pointer to the range if found, NULL if not.
174 * @param pVM The cross context VM structure.
175 * @param idSysReg The system register to look up.
176 */
177# ifndef IN_RING3
178static
179# endif
180PCPUMSYSREGRANGE cpumLookupSysRegRange(PVM pVM, uint32_t idSysReg)
181{
182 /*
183 * Binary lookup.
184 */
185 uint32_t cRanges = RT_MIN(pVM->cpum.s.GuestInfo.cSysRegRanges, RT_ELEMENTS(pVM->cpum.s.GuestInfo.aSysRegRanges));
186 if (!cRanges)
187 return NULL;
188 PCPUMSYSREGRANGE paRanges = pVM->cpum.s.GuestInfo.aSysRegRanges;
189 for (;;)
190 {
191 uint32_t i = cRanges / 2;
192 if (idSysReg < paRanges[i].uFirst)
193 {
194 if (i == 0)
195 break;
196 cRanges = i;
197 }
198 else if (idSysReg > paRanges[i].uLast)
199 {
200 i++;
201 if (i >= cRanges)
202 break;
203 cRanges -= i;
204 paRanges = &paRanges[i];
205 }
206 else
207 {
208 if (paRanges[i].enmRdFn == kCpumSysRegRdFn_Alias)
209 return cpumLookupSysRegRange(pVM, paRanges[i].uValue);
210 return &paRanges[i];
211 }
212 }
213
214# ifdef VBOX_STRICT
215 /*
216 * Linear lookup to verify the above binary search.
217 */
218 uint32_t cLeft = RT_MIN(pVM->cpum.s.GuestInfo.cSysRegRanges, RT_ELEMENTS(pVM->cpum.s.GuestInfo.aSysRegRanges));
219 PCPUMSYSREGRANGE pCur = pVM->cpum.s.GuestInfo.aSysRegRanges;
220 while (cLeft-- > 0)
221 {
222 if (idSysReg >= pCur->uFirst && idSysReg <= pCur->uLast)
223 {
224 AssertFailed();
225 if (pCur->enmRdFn == kCpumSysRegRdFn_Alias)
226 return cpumLookupSysRegRange(pVM, pCur->uValue);
227 return pCur;
228 }
229 pCur++;
230 }
231# endif
232 return NULL;
233}
234
235
236/**
237 * Query a guest system register.
238 *
239 * The caller is responsible for checking privilege if the call is the result of
240 * a MRS instruction. We'll do the rest.
241 *
242 * @retval VINF_SUCCESS on success.
243 * @retval VINF_CPUM_R3_MSR_READ if the system register read could not be serviced in the
244 * current context (raw-mode or ring-0).
245 * @retval VERR_CPUM_RAISE_GP_0 on failure (invalid system register), the caller is
246 * expected to take the appropriate actions. @a *puValue is set to 0.
247 * @param pVCpu The cross context virtual CPU structure.
248 * @param idSysReg The system register.
249 * @param puValue Where to return the value.
250 *
251 * @remarks This will always return the right values, even when we're in the
252 * recompiler.
253 */
254VMMDECL(VBOXSTRICTRC) CPUMQueryGuestSysReg(PVMCPUCC pVCpu, uint32_t idSysReg, uint64_t *puValue)
255{
256 *puValue = 0;
257
258 VBOXSTRICTRC rcStrict;
259 PVM pVM = pVCpu->CTX_SUFF(pVM);
260 PCPUMSYSREGRANGE pRange = cpumLookupSysRegRange(pVM, idSysReg);
261 if (pRange)
262 {
263 CPUMSYSREGRDFN enmRdFn = (CPUMSYSREGRDFN)pRange->enmRdFn;
264 AssertReturn(enmRdFn > kCpumSysRegRdFn_Invalid && enmRdFn < kCpumSysRegRdFn_End, VERR_CPUM_IPE_1);
265
266 PFNCPUMRDSYSREG pfnRdSysReg = g_aCpumRdSysRegFns[enmRdFn].pfnRdSysReg;
267 AssertReturn(pfnRdSysReg, VERR_CPUM_IPE_2);
268
269 STAM_COUNTER_INC(&pRange->cReads);
270 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegReads);
271
272 rcStrict = pfnRdSysReg(pVCpu, idSysReg, pRange, puValue);
273 if (rcStrict == VINF_SUCCESS)
274 Log2(("CPUM: MRS %#x (%s) -> %#llx\n", idSysReg, pRange->szName, *puValue));
275 else if (rcStrict == VERR_CPUM_RAISE_GP_0)
276 {
277 Log(("CPUM: MRS %#x (%s) -> #GP(0)\n", idSysReg, pRange->szName));
278 STAM_COUNTER_INC(&pRange->cExcp);
279 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegReadsRaiseExcp);
280 }
281#ifndef IN_RING3
282 else if (rcStrict == VINF_CPUM_R3_MSR_READ)
283 Log(("CPUM: MRS %#x (%s) -> ring-3\n", idSysReg, pRange->szName));
284#endif
285 else
286 {
287 Log(("CPUM: MRS %#x (%s) -> rcStrict=%Rrc\n", idSysReg, pRange->szName, VBOXSTRICTRC_VAL(rcStrict)));
288 AssertMsgStmt(RT_FAILURE_NP(rcStrict), ("%Rrc idSysReg=%#x\n", VBOXSTRICTRC_VAL(rcStrict), idSysReg),
289 rcStrict = VERR_IPE_UNEXPECTED_INFO_STATUS);
290 Assert(rcStrict != VERR_EM_INTERPRETER);
291 }
292 }
293 else
294 {
295 Log(("CPUM: Unknown MRS %#x -> Ignore\n", idSysReg));
296 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegReads);
297 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegReadsUnknown);
298 *puValue = 0;
299 rcStrict = VINF_SUCCESS;
300 }
301 return rcStrict;
302}
303
304
305/**
306 * Writes to a guest system register.
307 *
308 * The caller is responsible for checking privilege if the call is the result of
309 * a MSR instruction. We'll do the rest.
310 *
311 * @retval VINF_SUCCESS on success.
312 * @retval VINF_CPUM_R3_MSR_WRITE if the system register write could not be serviced in the
313 * current context (raw-mode or ring-0).
314 * @retval VERR_CPUM_RAISE_GP_0 on failure, the caller is expected to take the
315 * appropriate actions.
316 *
317 * @param pVCpu The cross context virtual CPU structure.
318 * @param idSysReg The system register id.
319 * @param uValue The value to set.
320 *
321 * @remarks Everyone changing system register values, shall do it
322 * by calling this method. This makes sure we have current values and
323 * that we trigger all the right actions when something changes.
324 */
325VMMDECL(VBOXSTRICTRC) CPUMSetGuestSysReg(PVMCPUCC pVCpu, uint32_t idSysReg, uint64_t uValue)
326{
327 VBOXSTRICTRC rcStrict;
328 PVM pVM = pVCpu->CTX_SUFF(pVM);
329 PCPUMSYSREGRANGE pRange = cpumLookupSysRegRange(pVM, idSysReg);
330 if (pRange)
331 {
332 STAM_COUNTER_INC(&pRange->cWrites);
333 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegWrites);
334
335 if (!(uValue & pRange->fWrExcpMask))
336 {
337 CPUMSYSREGWRFN enmWrFn = (CPUMSYSREGWRFN)pRange->enmWrFn;
338 AssertReturn(enmWrFn > kCpumSysRegWrFn_Invalid && enmWrFn < kCpumSysRegWrFn_End, VERR_CPUM_IPE_1);
339
340 PFNCPUMWRSYSREG pfnWrSysReg = g_aCpumWrSysRegFns[enmWrFn].pfnWrSysReg;
341 AssertReturn(pfnWrSysReg, VERR_CPUM_IPE_2);
342
343 uint64_t uValueAdjusted = uValue & ~pRange->fWrIgnMask;
344 if (uValueAdjusted != uValue)
345 {
346 STAM_COUNTER_INC(&pRange->cIgnoredBits);
347 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegWritesToIgnoredBits);
348 }
349
350 rcStrict = pfnWrSysReg(pVCpu, idSysReg, pRange, uValueAdjusted, uValue);
351 if (rcStrict == VINF_SUCCESS)
352 Log2(("CPUM: MSR %#x (%s), %#llx [%#llx]\n", idSysReg, pRange->szName, uValueAdjusted, uValue));
353 else if (rcStrict == VERR_CPUM_RAISE_GP_0)
354 {
355 Log(("CPUM: MSR %#x (%s), %#llx [%#llx] -> #GP(0)\n", idSysReg, pRange->szName, uValueAdjusted, uValue));
356 STAM_COUNTER_INC(&pRange->cExcp);
357 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegWritesRaiseExcp);
358 }
359#ifndef IN_RING3
360 else if (rcStrict == VINF_CPUM_R3_MSR_WRITE)
361 Log(("CPUM: MSR %#x (%s), %#llx [%#llx] -> ring-3\n", idSysReg, pRange->szName, uValueAdjusted, uValue));
362#endif
363 else
364 {
365 Log(("CPUM: MSR %#x (%s), %#llx [%#llx] -> rcStrict=%Rrc\n",
366 idSysReg, pRange->szName, uValueAdjusted, uValue, VBOXSTRICTRC_VAL(rcStrict)));
367 AssertMsgStmt(RT_FAILURE_NP(rcStrict), ("%Rrc idSysReg=%#x\n", VBOXSTRICTRC_VAL(rcStrict), idSysReg),
368 rcStrict = VERR_IPE_UNEXPECTED_INFO_STATUS);
369 Assert(rcStrict != VERR_EM_INTERPRETER);
370 }
371 }
372 else
373 {
374 Log(("CPUM: MSR %#x (%s), %#llx -> #GP(0) - invalid bits %#llx\n",
375 idSysReg, pRange->szName, uValue, uValue & pRange->fWrExcpMask));
376 STAM_COUNTER_INC(&pRange->cExcp);
377 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegWritesRaiseExcp);
378 rcStrict = VERR_CPUM_RAISE_GP_0;
379 }
380 }
381 else
382 {
383 Log(("CPUM: Unknown MSR %#x, %#llx -> #GP(0)\n", idSysReg, uValue));
384 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegWrites);
385 STAM_REL_COUNTER_INC(&pVM->cpum.s.cSysRegWritesUnknown);
386 rcStrict = VERR_CPUM_RAISE_GP_0;
387 }
388 return rcStrict;
389}
390
391
392#if defined(VBOX_STRICT) && defined(IN_RING3)
393/**
394 * Performs some checks on the static data related to MSRs.
395 *
396 * @returns VINF_SUCCESS on success, error on failure.
397 */
398int cpumR3SysRegStrictInitChecks(void)
399{
400#define CPUM_ASSERT_RD_SYSREG_FN(a_Register) \
401 AssertReturn(g_aCpumRdSysRegFns[kCpumSysRegRdFn_##a_Register].pfnRdSysReg == cpumSysRegRd_##a_Register, VERR_CPUM_IPE_2);
402#define CPUM_ASSERT_WR_SYSREG_FN(a_Register) \
403 AssertReturn(g_aCpumWrSysRegFns[kCpumSysRegWrFn_##a_Register].pfnWrSysReg == cpumSysRegWr_##a_Register, VERR_CPUM_IPE_2);
404
405 AssertReturn(g_aCpumRdSysRegFns[kCpumSysRegRdFn_Invalid].pfnRdSysReg == NULL, VERR_CPUM_IPE_2);
406 CPUM_ASSERT_RD_SYSREG_FN(FixedValue);
407 CPUM_ASSERT_RD_SYSREG_FN(WriteOnly);
408
409 AssertReturn(g_aCpumWrSysRegFns[kCpumSysRegWrFn_Invalid].pfnWrSysReg == NULL, VERR_CPUM_IPE_2);
410
411 return VINF_SUCCESS;
412}
413#endif /* VBOX_STRICT && IN_RING3 */
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