VirtualBox

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

Last change on this file was 99956, checked in by vboxsync, 12 months ago

VMM/CPUM-armv8: Implement OSDLR_EL1, OSLAR_EL1 and OSLSR_EL1 accessed by Linux guests, bugref:10387

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

© 2023 Oracle
ContactPrivacy policyTerms of Use