VirtualBox

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

Last change on this file since 99385 was 99385, checked in by vboxsync, 20 months ago

VMM/ArmV8: Skeleton of the GICv3 interrupt controller emulation, bugref:10404

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