VirtualBox

source: vbox/trunk/src/VBox/VMM/testcase/tstPGMAllGst-armv8.cpp

Last change on this file was 108951, checked in by vboxsync, 4 weeks ago

VBox/VMM/testcase/tstPGMAllGst-armv8.cpp: scm fix, bugref:10388

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.4 KB
Line 
1/* $Id: tstPGMAllGst-armv8.cpp 108951 2025-04-11 13:17:02Z vboxsync $ */
2/** @file
3 * PGM page table walking testcase - ARMv8 variant.
4 */
5
6/*
7 * Copyright (C) 2025 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#include "VMInternal.h"
33#include <VBox/vmm/cpum.h>
34#include "../include/CPUMInternal-armv8.h"
35#include "../include/PGMInternal.h"
36
37#include <VBox/vmm/vm.h>
38#include <VBox/vmm/uvm.h>
39#include <VBox/vmm/vmm.h>
40#include <VBox/vmm/pgm.h>
41#include <VBox/vmm/mm.h>
42#include <VBox/vmm/stam.h>
43
44#include <VBox/err.h>
45#include <VBox/log.h>
46#include <iprt/avl.h>
47#include <iprt/assert.h>
48#include <iprt/initterm.h>
49#include <iprt/json.h>
50#include <iprt/message.h>
51#include <iprt/mem.h>
52#include <iprt/string.h>
53#include <iprt/thread.h>
54#include <iprt/test.h>
55#include <iprt/zero.h>
56
57#include "tstPGMAllGst-armv8-tests.h"
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63
64/**
65 * Named value.
66 */
67typedef struct TSTCFGNAMEDVALUE
68{
69 /** The name of the value. */
70 const char *pszName;
71 /** The integer value. */
72 uint64_t u64Val;
73} TSTCFGNAMEDVALUE;
74typedef TSTCFGNAMEDVALUE *PTSTCFGNAMEDVALUE;
75typedef const TSTCFGNAMEDVALUE *PCTSTCFGNAMEDVALUE;
76
77
78/**
79 * A config bitfield.
80 */
81typedef struct TSTCFGBITFIELD
82{
83 /** The bitfield name. */
84 const char *pszName;
85 /** The bitfield offset. */
86 uint8_t offBitfield;
87 /** Number of bits for the bitfield. */
88 uint8_t cBits;
89 /** Optional array of named values. */
90 PCTSTCFGNAMEDVALUE paNamedValues;
91} TSTCFGBITFIELD;
92typedef TSTCFGBITFIELD *PTSTCFGBITFIELD;
93typedef const TSTCFGBITFIELD *PCTSTCFGBITFIELD;
94
95
96/**
97 * Chunk of physical memory containing data.
98 */
99typedef struct TSTMEMCHUNK
100{
101 /** AVL tree code. */
102 /** @todo Too lazy to introduce support for a ranged RT_GCPHYS based AVL tree right now, so just use uint64_t. */
103 AVLRU64NODECORE Core;
104 /** The memory - variable in size. */
105 uint8_t abMem[1];
106} TSTMEMCHUNK;
107/** Pointer to a physical memory chunk. */
108typedef TSTMEMCHUNK *PTSTMEMCHUNK;
109/** Pointer to a const physical memory chunk. */
110typedef const TSTMEMCHUNK *PCTSTMEMCHUNK;
111
112
113/**
114 * The current testcase data.
115 */
116typedef struct TSTPGMARMV8MMU
117{
118 /** The address space layout. */
119 AVLRU64TREE TreeMem;
120 /** The fake VM structure. */
121 PVM pVM;
122 /** TTBR0 value. */
123 uint64_t u64RegTtbr0;
124 /** TTBR1 value. */
125 uint64_t u64RegTtbr1;
126 /** The current exception level. */
127 uint8_t bEl;
128} TSTPGMARMV8MMU;
129typedef TSTPGMARMV8MMU *PTSTPGMARMV8MMU;
130typedef const TSTPGMARMV8MMU *PCTSTPGMARMV8MMU;
131
132
133/*********************************************************************************************************************************
134* Global Variables *
135*********************************************************************************************************************************/
136static RTTEST g_hTest;
137/** The currently executing testcase config. */
138static TSTPGMARMV8MMU g_MmuCfg;
139
140
141static int pgmPhysGCPhys2CCPtr(RTGCPHYS GCPhys, void **ppv)
142{
143 PCTSTMEMCHUNK pChunk = (PCTSTMEMCHUNK)RTAvlrU64RangeGet(&g_MmuCfg.TreeMem, GCPhys);
144 if (!pChunk)
145 return VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
146
147 uint64_t const off = GCPhys - pChunk->Core.Key;
148 *ppv = (void *)&pChunk->abMem[off];
149 return VINF_SUCCESS;
150}
151
152
153int pgmPhysGCPhys2CCPtrLockless(PVMCPUCC pVCpu, RTGCPHYS GCPhys, void **ppv)
154{
155 RT_NOREF(pVCpu, GCPhys, ppv);
156 return pgmPhysGCPhys2CCPtr(GCPhys, ppv);
157}
158
159
160int pgmPhysGCPhys2R3Ptr(PVMCC pVM, RTGCPHYS GCPhys, PRTR3PTR pR3Ptr)
161{
162 RT_NOREF(pVM, GCPhys, pR3Ptr);
163 return pgmPhysGCPhys2CCPtr(GCPhys, (void **)pR3Ptr);
164}
165
166
167VMM_INT_DECL(uint8_t) CPUMGetGuestEL(PVMCPUCC pVCpu)
168{
169 RT_NOREF(pVCpu);
170 return g_MmuCfg.bEl;
171}
172
173
174VMM_INT_DECL(RTGCPHYS) CPUMGetEffectiveTtbr(PVMCPUCC pVCpu, RTGCPTR GCPtr)
175{
176 RT_NOREF(pVCpu);
177 return (GCPtr & RT_BIT_64(55))
178 ? ARMV8_TTBR_EL1_AARCH64_BADDR_GET(g_MmuCfg.u64RegTtbr1)
179 : ARMV8_TTBR_EL1_AARCH64_BADDR_GET(g_MmuCfg.u64RegTtbr0);
180}
181
182
183/** Include and instantiate the page table walking code. */
184#include "../VMMAll/PGMAllGst-armv8.cpp.h"
185
186
187/**
188 * Creates a mockup VM structure for testing SSM.
189 *
190 * @returns 0 on success, 1 on failure.
191 * @param pMmuCfg The MMU config to initialize.
192 */
193static int tstMmuCfgInit(PTSTPGMARMV8MMU pMmuCfg)
194{
195 /*
196 * Allocate and init the UVM structure.
197 */
198 PUVM pUVM = (PUVM)RTMemPageAllocZ(sizeof(*pUVM));
199 AssertReturn(pUVM, 1);
200 pUVM->u32Magic = UVM_MAGIC;
201 pUVM->vm.s.idxTLS = RTTlsAlloc();
202 int rc = RTTlsSet(pUVM->vm.s.idxTLS, &pUVM->aCpus[0]);
203 if (RT_SUCCESS(rc))
204 {
205 pUVM->aCpus[0].pUVM = pUVM;
206 pUVM->aCpus[0].vm.s.NativeThreadEMT = RTThreadNativeSelf();
207
208 /*
209 * Allocate and init the VM structure.
210 */
211 PVM pVM = (PVM)RTMemPageAllocZ(sizeof(VM) + sizeof(VMCPU));
212 rc = pVM ? VINF_SUCCESS : VERR_NO_PAGE_MEMORY;
213 if (RT_SUCCESS(rc))
214 {
215 pVM->enmVMState = VMSTATE_CREATED;
216 pVM->pVMR3 = pVM;
217 pVM->pUVM = pUVM;
218 pVM->cCpus = 1;
219
220 PVMCPU pVCpu = (PVMCPU)(pVM + 1);
221 pVCpu->pVMR3 = pVM;
222 pVCpu->hNativeThread = RTThreadNativeSelf();
223 pVM->apCpusR3[0] = pVCpu;
224
225 pUVM->pVM = pVM;
226 pMmuCfg->pVM = pVM;
227 return VINF_SUCCESS;
228 }
229
230 RTTestIFailed("Fatal error: failed to allocated pages for the VM structure, rc=%Rrc\n", rc);
231 }
232 else
233 RTTestIFailed("Fatal error: RTTlsSet failed, rc=%Rrc\n", rc);
234
235 return rc;
236}
237
238
239static int tstMmuCfgReadS64(RTTEST hTest, RTJSONVAL hObj, const char *pszName, PCTSTCFGNAMEDVALUE paNamedValues, int64_t *pi64Result)
240{
241 RTJSONVAL hValue = NIL_RTJSONVAL;
242 int rc = RTJsonValueQueryByName(hObj, pszName, &hValue);
243 if (RT_FAILURE(rc))
244 {
245 RTTestFailed(hTest, "Failed to query \"%s\" with %Rrc", pszName, rc);
246 return rc;
247 }
248
249 RTJSONVALTYPE enmType = RTJsonValueGetType(hValue);
250 switch (enmType)
251 {
252 case RTJSONVALTYPE_INTEGER:
253 rc = RTJsonValueQueryInteger(hValue, pi64Result);
254 break;
255 case RTJSONVALTYPE_STRING:
256 {
257 if (paNamedValues)
258 {
259 const char *pszNamedValue = RTJsonValueGetString(hValue);
260 PCTSTCFGNAMEDVALUE pNamedValue = &paNamedValues[0];
261 while (pNamedValue->pszName)
262 {
263 if (!RTStrICmp(pszNamedValue, pNamedValue->pszName))
264 {
265 *pi64Result = (int64_t)pNamedValue->u64Val;
266 break;
267 }
268 pNamedValue++;
269 }
270 if (!pNamedValue->pszName)
271 {
272 RTTestFailed(hTest, "\"%s\" ist not a known named value for '%s'", pszNamedValue, pszName);
273 rc = VERR_NOT_FOUND;
274 }
275 }
276 else
277 {
278 RTTestFailed(hTest, "Integer \"%s\" doesn't support named values", pszName);
279 rc = VERR_NOT_SUPPORTED;
280 }
281 break;
282 }
283 default:
284 rc = VERR_NOT_SUPPORTED;
285 RTTestFailed(hTest, "JSON value type %d is not supported\n", enmType);
286 break;
287 }
288
289 RTJsonValueRelease(hValue);
290 return rc;
291}
292
293
294static int tstMmuCfgReadRc(RTTEST hTest, RTJSONVAL hObj, const char *pszName, int32_t *pi32Result)
295{
296 static const TSTCFGNAMEDVALUE s_aRc[] =
297 {
298#define CREATE_RC(a_Rc) \
299 {#a_Rc, (uint64_t)a_Rc}
300 CREATE_RC(VINF_SUCCESS),
301 CREATE_RC(VERR_RESERVED_PAGE_TABLE_BITS),
302 CREATE_RC(VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS),
303 CREATE_RC(VERR_PAGE_TABLE_NOT_PRESENT),
304 CREATE_RC(VERR_ACCESS_DENIED),
305 { NULL, 0 }
306 };
307
308
309 int64_t i64 = 0;
310 int rc = tstMmuCfgReadS64(hTest, hObj, pszName, &s_aRc[0], &i64);
311 if (RT_SUCCESS(rc))
312 {
313 if (i64 >= INT32_MIN && i64 <= INT32_MAX)
314 *pi32Result = (int32_t)i64;
315 else
316 RTTestFailed(hTest, "%RI64 for '%s' is out of range for a 32-bit signed integer", i64, pszName);
317 }
318
319 return rc;
320}
321
322
323static int tstMmuCfgReadBitfieldU64(RTTEST hTest, RTJSONVAL hObj, PCTSTCFGBITFIELD paBitfields, uint64_t *pu64Result)
324{
325 uint64_t u64 = 0;
326
327 uint32_t idx = 0;
328 while (paBitfields[idx].pszName)
329 {
330 RTJSONVAL hValue = NIL_RTJSONVAL;
331 int rc = RTJsonValueQueryByName(hObj, paBitfields[idx].pszName, &hValue);
332 if (rc == VERR_NOT_FOUND)
333 {
334 idx++;
335 continue;
336 }
337 if (RT_FAILURE(rc))
338 {
339 RTTestFailed(hTest, "Failed to query \"%s\" with %Rrc", paBitfields[idx].pszName, rc);
340 return rc;
341 }
342
343 RTJSONVALTYPE enmType = RTJsonValueGetType(hValue);
344 switch (enmType)
345 {
346 case RTJSONVALTYPE_TRUE:
347 case RTJSONVALTYPE_FALSE:
348 {
349 if (paBitfields[idx].cBits == 1)
350 u64 |= (enmType == RTJSONVALTYPE_TRUE ? 1 : 0) << paBitfields[idx].offBitfield;
351 else
352 RTTestFailed(hTest, "Bitfield '%s' is more than 1 bit wide", paBitfields[idx].pszName);
353
354 break;
355 }
356 case RTJSONVALTYPE_INTEGER:
357 {
358 int64_t i64Tmp = 0;
359 rc = RTJsonValueQueryInteger(hValue, &i64Tmp);
360 if (RT_FAILURE(rc))
361 {
362 RTTestFailed(hTest, "Failed to query \"%s\" as an integer with %Rrc", paBitfields[idx].pszName, rc);
363 break;
364 }
365 else if ( i64Tmp < 0
366 || ( paBitfields[idx].cBits < 64
367 && (uint64_t)i64Tmp > (RT_BIT_64(paBitfields[idx].cBits) - 1)))
368 {
369 RTTestFailed(hTest, "Value of \"%s\" is out of bounds, got %#RX64, maximum is %#RX64",
370 paBitfields[idx].pszName, (uint64_t)i64Tmp, (RT_BIT_64(paBitfields[idx].cBits) - 1));
371 rc = VERR_INVALID_PARAMETER;
372 }
373 else
374 u64 |= ((uint64_t)i64Tmp) << paBitfields[idx].offBitfield;
375 break;
376 }
377 case RTJSONVALTYPE_STRING:
378 {
379 if (paBitfields[idx].paNamedValues)
380 {
381 const char *pszNamedValue = RTJsonValueGetString(hValue);
382 PCTSTCFGNAMEDVALUE pNamedValue = &paBitfields[idx].paNamedValues[0];
383 while (pNamedValue->pszName)
384 {
385 if (!RTStrICmp(pszNamedValue, pNamedValue->pszName))
386 {
387 u64 |= pNamedValue->u64Val << paBitfields[idx].offBitfield;
388 break;
389 }
390 pNamedValue++;
391 }
392 if (!pNamedValue->pszName)
393 {
394 RTTestFailed(hTest, "\"%s\" ist not a known named value for bitfield '%s'", pszNamedValue, paBitfields[idx].pszName);
395 rc = VERR_NOT_FOUND;
396 }
397 }
398 else
399 {
400 RTTestFailed(hTest, "Bitfield \"%s\" doesn't support named values", paBitfields[idx].pszName);
401 rc = VERR_NOT_SUPPORTED;
402 }
403 break;
404 }
405 default:
406 rc = VERR_NOT_SUPPORTED;
407 RTTestFailed(hTest, "JSON value type %d is not supported\n", enmType);
408 break;
409 }
410 RTJsonValueRelease(hValue);
411
412 if (RT_FAILURE(rc))
413 return rc;
414
415 idx++;
416 }
417
418 *pu64Result = u64;
419 return VINF_SUCCESS;
420}
421
422
423static int tstMmuCfgReadU64(RTTEST hTest, RTJSONVAL hObj, const char *pszName, PCTSTCFGBITFIELD paBitfields, uint64_t *pu64Result)
424{
425 RTJSONVAL hValue = NIL_RTJSONVAL;
426 int rc = RTJsonValueQueryByName(hObj, pszName, &hValue);
427 if (RT_FAILURE(rc))
428 {
429 RTTestFailed(hTest, "Failed to query \"%s\" with %Rrc", pszName, rc);
430 return rc;
431 }
432
433 RTJSONVALTYPE enmType = RTJsonValueGetType(hValue);
434 switch (enmType)
435 {
436 case RTJSONVALTYPE_INTEGER:
437 rc = RTJsonValueQueryInteger(hValue, (int64_t *)pu64Result);
438 break;
439 case RTJSONVALTYPE_OBJECT:
440 rc = tstMmuCfgReadBitfieldU64(hTest, hValue, paBitfields, pu64Result);
441 break;
442 default:
443 rc = VERR_NOT_SUPPORTED;
444 RTTestFailed(hTest, "JSON value type %d is not supported\n", enmType);
445 break;
446 }
447
448 RTJsonValueRelease(hValue);
449 return rc;
450}
451
452
453static int tstMmuCfgReadU32(RTTEST hTest, RTJSONVAL hObj, const char *pszName, PCTSTCFGBITFIELD paBitfields, uint32_t *pu32Result)
454{
455 uint64_t u64 = 0;
456 int rc = tstMmuCfgReadU64(hTest, hObj, pszName, paBitfields, &u64);
457 if (RT_FAILURE(rc))
458 return rc;
459
460 *pu32Result = (uint32_t)u64;
461
462 return VINF_SUCCESS;
463}
464
465
466static DECLCALLBACK(int) tstZeroChunk(PAVLRU64NODECORE pCore, void *pvParam)
467{
468 RT_NOREF(pvParam);
469 PTSTMEMCHUNK pChunk = (PTSTMEMCHUNK)pCore;
470 memset(&pChunk->abMem, 0, _64K);
471 return VINF_SUCCESS;
472}
473
474
475static void tstMmuCfgReset(PTSTPGMARMV8MMU pMmuCfg)
476{
477 RTAvlrU64DoWithAll(&pMmuCfg->TreeMem, true /*fFromLeft*/, tstZeroChunk, NULL);
478}
479
480
481static DECLCALLBACK(int) tstDestroyChunk(PAVLRU64NODECORE pCore, void *pvParam)
482{
483 RT_NOREF(pvParam);
484 RTMemPageFree(pCore, _64K);
485 return VINF_SUCCESS;
486}
487
488
489static void tstMmuCfgDestroy(PTSTPGMARMV8MMU pMmuCfg)
490{
491 RTMemPageFree(pMmuCfg->pVM->pUVM, sizeof(*pMmuCfg->pVM->pUVM));
492 RTMemPageFree(pMmuCfg->pVM, sizeof(VM) + sizeof(VMCPU));
493 RTAvlrU64Destroy(&pMmuCfg->TreeMem, tstDestroyChunk, NULL);
494}
495
496
497static int tstTestcaseMmuMemoryWrite(RTTEST hTest, PTSTPGMARMV8MMU pMmuCfg, uint64_t GCPhysAddr, const void *pvData, size_t cbData)
498{
499 size_t cbLeft = cbData;
500 const uint8_t *pbData = (const uint8_t *)pvData;
501 while (cbLeft)
502 {
503 PTSTMEMCHUNK pChunk = (PTSTMEMCHUNK)RTAvlrU64RangeGet(&pMmuCfg->TreeMem, GCPhysAddr);
504 if (!pChunk)
505 {
506 /* Allocate a new chunk (64KiB chunks). */
507 pChunk = (PTSTMEMCHUNK)RTMemPageAllocZ(_64K);
508 if (!pChunk)
509 {
510 RTTestFailed(hTest, "Failed to allocate 64KiB of memory for memory chunk at %#RX64\n", GCPhysAddr);
511 return VERR_NO_MEMORY;
512 }
513
514 pChunk->Core.Key = GCPhysAddr & ~((uint64_t)_64K - 1);
515 pChunk->Core.KeyLast = pChunk->Core.Key + _64K - 1;
516 bool fInsert = RTAvlrU64Insert(&pMmuCfg->TreeMem, &pChunk->Core);
517 AssertRelease(fInsert);
518 }
519
520 uint64_t const off = GCPhysAddr - pChunk->Core.Key;
521 size_t const cbThisCopy = RT_MIN(cbLeft, pChunk->Core.KeyLast - off + 1);
522 memcpy(&pChunk->abMem[off], pbData, cbThisCopy);
523 cbLeft -= cbThisCopy;
524 GCPhysAddr += cbThisCopy;
525 pbData += cbThisCopy;
526 }
527 return VINF_SUCCESS;
528}
529
530
531static int tstTestcaseMmuMemoryAdd(RTTEST hTest, PTSTPGMARMV8MMU pMmuCfg, uint64_t GCPhysAddr, RTJSONVAL hMemObj)
532{
533 int rc;
534 RTJSONVALTYPE enmType = RTJsonValueGetType(hMemObj);
535 switch (enmType)
536 {
537 case RTJSONVALTYPE_ARRAY:
538 {
539 RTJSONIT hIt = NIL_RTJSONIT;
540 rc = RTJsonIteratorBeginArray(hMemObj, &hIt);
541 if (RT_SUCCESS(rc))
542 {
543 for (;;)
544 {
545 RTJSONVAL hData = NIL_RTJSONVAL;
546 rc = RTJsonIteratorQueryValue(hIt, &hData, NULL /*ppszName*/);
547 if (RT_SUCCESS(rc))
548 {
549 if (RTJsonValueGetType(hData) == RTJSONVALTYPE_INTEGER)
550 {
551 int64_t i64Data = 0;
552 rc = RTJsonValueQueryInteger(hData, &i64Data);
553 if (RT_SUCCESS(rc))
554 {
555 if (i64Data >= 0 && i64Data <= 255)
556 {
557 uint8_t bVal = (uint8_t)i64Data;
558 rc = tstTestcaseMmuMemoryWrite(hTest, pMmuCfg, GCPhysAddr, &bVal, sizeof(bVal));
559 }
560 else
561 {
562 RTTestFailed(hTest, "Data %#RX64 for address %#RX64 is not a valid byte value", i64Data, GCPhysAddr);
563 break;
564 }
565 }
566 else
567 {
568 RTTestFailed(hTest, "Failed to query byte value for address %#RX64", GCPhysAddr);
569 break;
570 }
571 }
572 else
573 {
574 RTTestFailed(hTest, "Data for address %#RX64 contains an invalid value", GCPhysAddr);
575 break;
576 }
577
578 RTJsonValueRelease(hData);
579 }
580 else
581 RTTestFailed(hTest, "Failed to retrieve byte value with %Rrc", rc);
582
583 rc = RTJsonIteratorNext(hIt);
584 if (RT_FAILURE(rc))
585 break;
586
587 GCPhysAddr++;
588 }
589 if (rc == VERR_JSON_ITERATOR_END)
590 rc = VINF_SUCCESS;
591 RTJsonIteratorFree(hIt);
592 }
593 else /* An empty array is also an error */
594 RTTestFailed(hTest, "Failed to traverse JSON array with %Rrc", rc);
595 break;
596 }
597 case RTJSONVALTYPE_INTEGER:
598 {
599 uint64_t u64Val = 0;
600 rc = RTJsonValueQueryInteger(hMemObj, (int64_t *)&u64Val);
601 if (RT_SUCCESS(rc))
602 rc = tstTestcaseMmuMemoryWrite(hTest, pMmuCfg, GCPhysAddr, &u64Val, sizeof(u64Val));
603 else
604 RTTestFailed(hTest, "Querying data for address %#RX64 failed with %Rrc\n", GCPhysAddr, u64Val);
605 break;
606 }
607 case RTJSONVALTYPE_OBJECT:
608 {
609 static const TSTCFGNAMEDVALUE s_aApPerm[] =
610 {
611 { "PRW", 0 },
612 { "UPRW", 1 },
613 { "PR", 2 },
614 { "UPR", 3 },
615 { NULL, 0 }
616 };
617 static const TSTCFGBITFIELD s_aTblBitfields[] =
618 {
619#define BITFIELD_CREATE_BOOL(a_Name, a_offBit) \
620 { a_Name, a_offBit, 1, NULL }
621
622 { "Raw", 0, 64, NULL },
623 { "SwUse", 55, 4, NULL },
624 BITFIELD_CREATE_BOOL("UXN", 54),
625 BITFIELD_CREATE_BOOL("PXN", 53),
626 { "PhysAddr", 0, 64, NULL },
627 BITFIELD_CREATE_BOOL("AF", 10),
628 { "AP", 6, 2, &s_aApPerm[0] },
629 BITFIELD_CREATE_BOOL("T", 1),
630 BITFIELD_CREATE_BOOL("V", 0),
631 { NULL, 0, 0, NULL }
632
633#undef BITFIELD_CREATE_BOOL
634 };
635
636 uint64_t u64Val = 0;
637 rc = tstMmuCfgReadBitfieldU64(hTest, hMemObj, &s_aTblBitfields[0], &u64Val);
638 if (RT_SUCCESS(rc))
639 rc = tstTestcaseMmuMemoryWrite(hTest, pMmuCfg, GCPhysAddr, &u64Val, sizeof(u64Val));
640 break;
641 }
642 default:
643 RTTestFailed(hTest, "Memory object has an invalid type %d\n", enmType);
644 rc = VERR_NOT_SUPPORTED;
645 }
646
647 return rc;
648}
649
650
651static int tstTestcaseAddressSpacePrepare(RTTEST hTest, RTJSONVAL hTestcase)
652{
653 /* Prepare the memory space. */
654 RTJSONVAL hVal = NIL_RTJSONVAL;
655 int rc = RTJsonValueQueryByName(hTestcase, "AddressSpace", &hVal);
656 if (RT_SUCCESS(rc))
657 {
658 RTJSONIT hIt = NIL_RTJSONIT;
659 rc = RTJsonIteratorBeginObject(hVal, &hIt);
660 if (RT_SUCCESS(rc))
661 {
662 for (;;)
663 {
664 RTJSONVAL hMemObj = NIL_RTJSONVAL;
665 const char *pszAddress = NULL;
666 rc = RTJsonIteratorQueryValue(hIt, &hMemObj, &pszAddress);
667 if (RT_SUCCESS(rc))
668 {
669 uint64_t GCPhysAddr = 0;
670 rc = RTStrToUInt64Full(pszAddress, 0, &GCPhysAddr);
671 if (rc == VINF_SUCCESS)
672 rc = tstTestcaseMmuMemoryAdd(hTest, &g_MmuCfg, GCPhysAddr, hMemObj);
673 else
674 {
675 RTTestFailed(hTest, "Address '%s' is not a valid 64-bit physical address", pszAddress);
676 break;
677 }
678
679 RTJsonValueRelease(hMemObj);
680 }
681 else
682 RTTestFailed(hTest, "Failed to retrieve memory range with %Rrc", rc);
683
684 rc = RTJsonIteratorNext(hIt);
685 if (RT_FAILURE(rc))
686 break;
687 }
688 if (rc == VERR_JSON_ITERATOR_END)
689 rc = VINF_SUCCESS;
690 RTJsonIteratorFree(hIt);
691 }
692 else if (rc == VERR_JSON_IS_EMPTY) /* Empty address space is valid. */
693 rc = VINF_SUCCESS;
694 else
695 RTTestFailed(hTest, "Failed to traverse JSON object with %Rrc", rc);
696
697 RTJsonValueRelease(hVal);
698 }
699 else
700 RTTestFailed(hTest, "Failed to query \"AddressSpace\" containing the address space layout %Rrc", rc);
701
702 return rc;
703}
704
705
706static int tstTestcaseMmuConfigPrepare(RTTEST hTest, PTSTPGMARMV8MMU pMmuCfg, RTJSONVAL hTestcase)
707{
708 PVMCPUCC pVCpu = pMmuCfg->pVM->apCpusR3[0];
709
710 /* Set MMU config (SCTLR, TCR, TTBR, etc.). */
711 uint64_t u64RegSctlrEl1 = 0;
712 int rc = tstMmuCfgReadU64(hTest, hTestcase, "SCTLR_EL1", NULL, &u64RegSctlrEl1);
713 if (RT_FAILURE(rc))
714 return rc;
715
716 uint64_t u64RegTcrEl1 = 0;
717 static const TSTCFGNAMEDVALUE s_aTgSizes[] =
718 {
719 { "4K", 2 },
720 { "64K", 1 },
721 { "16K", 3 },
722 { NULL, 0 }
723 };
724 static const TSTCFGNAMEDVALUE s_aIpsSizes[] =
725 {
726 { "4G", 0 },
727 { "64G", 1 },
728 { "1T", 2 },
729 { "4T", 3 },
730 { "16T", 4 },
731 { "256T", 5 },
732 { "4P", 6 },
733 { "64P", 7 },
734
735 { NULL, 0 }
736 };
737 static const TSTCFGBITFIELD s_aTcrEl1Bitfields[] =
738 {
739#define BITFIELD_CREATE_BOOL(a_Name, a_offBit) \
740 { a_Name, a_offBit, 1, NULL }
741
742 BITFIELD_CREATE_BOOL("MTX1", 61),
743 BITFIELD_CREATE_BOOL("MTX0", 60),
744 BITFIELD_CREATE_BOOL("DS", 59),
745 BITFIELD_CREATE_BOOL("TCMA1", 58),
746 BITFIELD_CREATE_BOOL("TCMA0", 57),
747 BITFIELD_CREATE_BOOL("E0PD1", 56),
748 BITFIELD_CREATE_BOOL("E0PD0", 55),
749 BITFIELD_CREATE_BOOL("NFD1", 54),
750 BITFIELD_CREATE_BOOL("NFD0", 53),
751 BITFIELD_CREATE_BOOL("TBID1", 52),
752 BITFIELD_CREATE_BOOL("TBID0", 51),
753 BITFIELD_CREATE_BOOL("HWU162", 50),
754 BITFIELD_CREATE_BOOL("HWU161", 49),
755 BITFIELD_CREATE_BOOL("HWU160", 48),
756 BITFIELD_CREATE_BOOL("HWU159", 47),
757 BITFIELD_CREATE_BOOL("HWU062", 46),
758 BITFIELD_CREATE_BOOL("HWU061", 45),
759 BITFIELD_CREATE_BOOL("HWU060", 44),
760 BITFIELD_CREATE_BOOL("HWU059", 43),
761 BITFIELD_CREATE_BOOL("HPD1", 42),
762 BITFIELD_CREATE_BOOL("HPD0", 41),
763 BITFIELD_CREATE_BOOL("HD", 40),
764 BITFIELD_CREATE_BOOL("HA", 39),
765 BITFIELD_CREATE_BOOL("TBI1", 38),
766 BITFIELD_CREATE_BOOL("TBI0", 37),
767 { "IPS", 32, 3, &s_aIpsSizes[0] },
768 { "TG1", 30, 2, &s_aTgSizes[0] },
769 { "SH1", 28, 2, NULL },
770 { "ORGN1", 26, 2, NULL },
771 { "IRGN1", 24, 2, NULL },
772 BITFIELD_CREATE_BOOL("EPD1", 33),
773 BITFIELD_CREATE_BOOL("A1", 22),
774 { "T1SZ", 16, 6, NULL },
775
776 { "TG0", 14, 2, &s_aTgSizes[0] },
777 { "SH0", 12, 2, NULL },
778 { "ORGN0", 10, 2, NULL },
779 { "IRGN0", 8, 2, NULL },
780 BITFIELD_CREATE_BOOL("EPD0", 7),
781 { "T0SZ", 0, 6, NULL },
782 { NULL, 0, 0, NULL }
783
784#undef BITFIELD_CREATE_BOOL
785 };
786 rc = tstMmuCfgReadU64(hTest, hTestcase, "TCR_EL1", &s_aTcrEl1Bitfields[0], &u64RegTcrEl1);
787 if (RT_FAILURE(rc))
788 return rc;
789
790 rc = tstMmuCfgReadU64(hTest, hTestcase, "TTBR0_EL1", NULL, &pVCpu->cpum.s.Guest.Ttbr0.u64);
791 if (RT_FAILURE(rc))
792 return rc;
793
794 rc = tstMmuCfgReadU64(hTest, hTestcase, "TTBR1_EL1", NULL, &pVCpu->cpum.s.Guest.Ttbr1.u64);
795 if (RT_FAILURE(rc))
796 return rc;
797
798 uintptr_t const idxNewGstTtbr0 = pgmR3DeduceTypeFromTcr<ARMV8_TCR_EL1_AARCH64_T0SZ_SHIFT, ARMV8_TCR_EL1_AARCH64_TG0_SHIFT,
799 ARMV8_TCR_EL1_AARCH64_TBI0_BIT, ARMV8_TCR_EL1_AARCH64_EPD0_BIT, false>
800 (u64RegSctlrEl1, u64RegTcrEl1, &pVCpu->pgm.s.afLookupMaskTtbr0[1]);
801 uintptr_t const idxNewGstTtbr1 = pgmR3DeduceTypeFromTcr<ARMV8_TCR_EL1_AARCH64_T1SZ_SHIFT, ARMV8_TCR_EL1_AARCH64_TG1_SHIFT,
802 ARMV8_TCR_EL1_AARCH64_TBI1_BIT, ARMV8_TCR_EL1_AARCH64_EPD1_BIT, true>
803 (u64RegSctlrEl1, u64RegTcrEl1, &pVCpu->pgm.s.afLookupMaskTtbr1[1]);
804 Assert(idxNewGstTtbr0 != 0 && idxNewGstTtbr1 != 0);
805
806 /*
807 * Change the paging mode data indexes.
808 */
809 AssertReturn(idxNewGstTtbr0 < RT_ELEMENTS(g_aPgmGuestModeData), VERR_PGM_MODE_IPE);
810 AssertReturn(g_aPgmGuestModeData[idxNewGstTtbr0].uType == idxNewGstTtbr0, VERR_PGM_MODE_IPE);
811 AssertPtrReturn(g_aPgmGuestModeData[idxNewGstTtbr0].pfnGetPage, VERR_PGM_MODE_IPE);
812 AssertPtrReturn(g_aPgmGuestModeData[idxNewGstTtbr0].pfnModifyPage, VERR_PGM_MODE_IPE);
813 AssertPtrReturn(g_aPgmGuestModeData[idxNewGstTtbr0].pfnExit, VERR_PGM_MODE_IPE);
814 AssertPtrReturn(g_aPgmGuestModeData[idxNewGstTtbr0].pfnEnter, VERR_PGM_MODE_IPE);
815
816 AssertReturn(idxNewGstTtbr1 < RT_ELEMENTS(g_aPgmGuestModeData), VERR_PGM_MODE_IPE);
817 AssertReturn(g_aPgmGuestModeData[idxNewGstTtbr1].uType == idxNewGstTtbr1, VERR_PGM_MODE_IPE);
818 AssertPtrReturn(g_aPgmGuestModeData[idxNewGstTtbr1].pfnGetPage, VERR_PGM_MODE_IPE);
819 AssertPtrReturn(g_aPgmGuestModeData[idxNewGstTtbr1].pfnModifyPage, VERR_PGM_MODE_IPE);
820 AssertPtrReturn(g_aPgmGuestModeData[idxNewGstTtbr1].pfnExit, VERR_PGM_MODE_IPE);
821 AssertPtrReturn(g_aPgmGuestModeData[idxNewGstTtbr1].pfnEnter, VERR_PGM_MODE_IPE);
822
823 rc = g_aPgmGuestModeData[idxNewGstTtbr0].pfnEnter(pVCpu);
824 int rc2 = g_aPgmGuestModeData[idxNewGstTtbr1].pfnEnter(pVCpu);
825
826 /* status codes. */
827 AssertRC(rc);
828 AssertRC(rc2);
829 if (RT_SUCCESS(rc))
830 {
831 rc = rc2;
832 if (RT_SUCCESS(rc)) /* no informational status codes. */
833 rc = VINF_SUCCESS;
834 }
835
836 pVCpu->pgm.s.aidxGuestModeDataTtbr0[1] = idxNewGstTtbr0;
837 pVCpu->pgm.s.aidxGuestModeDataTtbr1[1] = idxNewGstTtbr1;
838
839 /* Also set the value for EL0, saves us an if condition in the hot paths later on. */
840 pVCpu->pgm.s.aidxGuestModeDataTtbr0[0] = idxNewGstTtbr0;
841 pVCpu->pgm.s.aidxGuestModeDataTtbr1[0] = idxNewGstTtbr1;
842
843 pVCpu->pgm.s.afLookupMaskTtbr0[0] = pVCpu->pgm.s.afLookupMaskTtbr0[1];
844 pVCpu->pgm.s.afLookupMaskTtbr1[0] = pVCpu->pgm.s.afLookupMaskTtbr1[1];
845
846 pVCpu->pgm.s.aenmGuestMode[1] = (u64RegSctlrEl1 & ARMV8_SCTLR_EL1_M) ? PGMMODE_VMSA_V8_64 : PGMMODE_NONE;
847 return rc;
848}
849
850
851DECLINLINE(int) tstResultQueryGCPhysDef(RTTEST hTest, RTJSONVAL hMemResult, const char *pszName, RTGCPHYS *pGCPhys, RTGCPHYS GCPhysDef)
852{
853 int64_t i64 = 0;
854 int rc = RTJsonValueQueryIntegerByName(hMemResult, pszName, &i64);
855 if (rc == VERR_NOT_FOUND)
856 {
857 *pGCPhys = GCPhysDef;
858 rc = VINF_SUCCESS;
859 }
860 else if (RT_FAILURE(rc))
861 RTTestFailed(hTest, "Querying '%s' failed with %Rrc", pszName, rc);
862 else
863 *pGCPhys = (RTGCPHYS)i64;
864
865 return rc;
866}
867
868
869DECLINLINE(int) tstResultQueryGCPhys(RTTEST hTest, RTJSONVAL hMemResult, const char *pszName, RTGCPHYS *pGCPhys)
870{
871 int64_t i64 = 0;
872 int rc = RTJsonValueQueryIntegerByName(hMemResult, pszName, &i64);
873 if (RT_FAILURE(rc))
874 RTTestFailed(hTest, "Querying '%s' failed with %Rrc", pszName, rc);
875 else
876 *pGCPhys = (RTGCPHYS)i64;
877
878 return rc;
879}
880
881
882static int tstResultInit(RTTEST hTest, RTJSONVAL hMemResult, PPGMPTWALKFAST pWalkResult, int *prcQueryPageFast,
883 int *prcGetPage)
884{
885 int rc = tstMmuCfgReadRc(hTest, hMemResult, "rcQueryPageFast", prcQueryPageFast);
886 if (RT_SUCCESS(rc))
887 rc = tstMmuCfgReadRc(hTest, hMemResult, "rcGetPage", prcGetPage);
888 if (RT_SUCCESS(rc))
889 rc = tstResultQueryGCPhys(hTest, hMemResult, "GCPhys", &pWalkResult->GCPhys);
890 if (RT_SUCCESS(rc))
891 rc = tstResultQueryGCPhysDef(hTest, hMemResult, "GCPhysNested", &pWalkResult->GCPhysNested, 0);
892 if (RT_SUCCESS(rc))
893 {
894 static const TSTCFGBITFIELD s_aInfo[] =
895 {
896#define BITFIELD_CREATE_BOOL(a_Name, a_offBit) \
897 { #a_Name, a_offBit, 1, NULL }
898 BITFIELD_CREATE_BOOL(Succeeded, 0),
899 BITFIELD_CREATE_BOOL(IsSlat, 1),
900 BITFIELD_CREATE_BOOL(BigPage, 7),
901 BITFIELD_CREATE_BOOL(GiganticPage, 8),
902 BITFIELD_CREATE_BOOL(IsLinearAddrValid, 10),
903 { NULL, 0, 0, NULL }
904 };
905 AssertCompile(PGM_WALKINFO_SUCCEEDED == RT_BIT_32(0));
906 AssertCompile(PGM_WALKINFO_IS_SLAT == RT_BIT_32(1));
907 AssertCompile(PGM_WALKINFO_BIG_PAGE == RT_BIT_32(7));
908 AssertCompile(PGM_WALKINFO_GIGANTIC_PAGE == RT_BIT_32(8));
909 AssertCompile(PGM_WALKINFO_IS_LINEAR_ADDR_VALID == RT_BIT_32(10));
910
911 rc = tstMmuCfgReadU32(hTest, hMemResult, "Info", &s_aInfo[0], &pWalkResult->fInfo);
912 }
913 if (RT_SUCCESS(rc))
914 {
915 static const TSTCFGBITFIELD s_aFailed[] =
916 {
917 BITFIELD_CREATE_BOOL(NotPresent, 0),
918 BITFIELD_CREATE_BOOL(ReservedBits, 1),
919 BITFIELD_CREATE_BOOL(BadPhysicalAddress, 2),
920 BITFIELD_CREATE_BOOL(NotWritable, 6),
921 BITFIELD_CREATE_BOOL(NotExecutable, 7),
922 BITFIELD_CREATE_BOOL(NotAccessibleByMode, 8),
923 { "Level", 11, 5, NULL },
924 { NULL, 0, 0, NULL }
925
926#undef BITFIELD_CREATE_BOOL
927 };
928 AssertCompile(PGM_WALKFAIL_NOT_PRESENT == RT_BIT_32(0));
929 AssertCompile(PGM_WALKFAIL_RESERVED_BITS == RT_BIT_32(1));
930 AssertCompile(PGM_WALKFAIL_BAD_PHYSICAL_ADDRESS == RT_BIT_32(2));
931 AssertCompile(PGM_WALKFAIL_NOT_WRITABLE == RT_BIT_32(6));
932 AssertCompile(PGM_WALKFAIL_NOT_EXECUTABLE == RT_BIT_32(7));
933 AssertCompile(PGM_WALKFAIL_NOT_ACCESSIBLE_BY_MODE == RT_BIT_32(8));
934
935 rc = tstMmuCfgReadU32(hTest, hMemResult, "Failed", &s_aFailed[0], &pWalkResult->fFailed);
936 }
937 if (RT_SUCCESS(rc))
938 {
939 static const TSTCFGBITFIELD s_aPtAttrs[] =
940 {
941#define BITFIELD_CREATE_BOOL(a_Name) \
942 { #a_Name, PGM_PTATTRS_##a_Name##_SHIFT, 1, NULL }
943
944 BITFIELD_CREATE_BOOL(PR),
945 BITFIELD_CREATE_BOOL(PW),
946 BITFIELD_CREATE_BOOL(PX),
947 BITFIELD_CREATE_BOOL(PGCS),
948 BITFIELD_CREATE_BOOL(UR),
949 BITFIELD_CREATE_BOOL(UW),
950 BITFIELD_CREATE_BOOL(UX),
951 BITFIELD_CREATE_BOOL(UGCS),
952 { NULL, 0, 0, NULL }
953
954#undef BITFIELD_CREATE_BOOL
955 };
956
957 rc = tstMmuCfgReadU64(hTest, hMemResult, "Effective", &s_aPtAttrs[0], &pWalkResult->fEffective);
958 }
959
960 return rc;
961}
962
963
964static void tstExecuteQueryPageFast(RTTEST hTest, PVM pVM, RTGCPTR GCPtr, uint32_t fFlags, int rcExpected, PPGMPTWALKFAST pWalkResult)
965{
966 PVMCPUCC pVCpu = pVM->apCpusR3[0];
967
968 /** @todo Incorporate EL (for nested virt and EL3 later on). */
969 uintptr_t idx = (GCPtr & RT_BIT_64(55))
970 ? pVCpu->pgm.s.aidxGuestModeDataTtbr1[1]
971 : pVCpu->pgm.s.aidxGuestModeDataTtbr0[1];
972
973 PGMPTWALKFAST Walk; PGMPTWALKFAST_ZERO(&Walk);
974 AssertReleaseReturnVoid(idx < RT_ELEMENTS(g_aPgmGuestModeData));
975 AssertReleaseReturnVoid(g_aPgmGuestModeData[idx].pfnQueryPageFast);
976 int rc = g_aPgmGuestModeData[idx].pfnQueryPageFast(pVCpu, GCPtr, fFlags, &Walk);
977 if (rc != rcExpected)
978 RTTestFailed(hTest, "QueryPageFast: Result rc=%Rrc != Expected rc=%Rrc", rc, rcExpected);
979
980 if (memcmp(&Walk, pWalkResult, sizeof(Walk)))
981 {
982 if (Walk.GCPtr != pWalkResult->GCPtr)
983 RTTestFailed(hTest, "QueryPageFast: Result GCPtr=%RGv != Expected GCPtr=%RGv", Walk.GCPtr, pWalkResult->GCPtr);
984 if (Walk.GCPhys != pWalkResult->GCPhys)
985 RTTestFailed(hTest, "QueryPageFast: Result GCPhys=%RGp != Expected GCPhys=%RGp", Walk.GCPhys, pWalkResult->GCPhys);
986 if (Walk.GCPhysNested != pWalkResult->GCPhysNested)
987 RTTestFailed(hTest, "QueryPageFast: Result GCPhysNested=%RGp != Expected GCPhysNested=%RGp", Walk.GCPhysNested, pWalkResult->GCPhysNested);
988 if (Walk.fInfo != pWalkResult->fInfo)
989 RTTestFailed(hTest, "QueryPageFast: Result fInfo=%#RX32 != Expected fInfo=%#RX32", Walk.fInfo, pWalkResult->fInfo);
990 if (Walk.fFailed != pWalkResult->fFailed)
991 RTTestFailed(hTest, "QueryPageFast: Result fFailed=%#RX32 != Expected fFailed=%#RX32", Walk.fFailed, pWalkResult->fFailed);
992 if (Walk.fEffective != pWalkResult->fEffective)
993 RTTestFailed(hTest, "QueryPageFast: Result fEffective=%#RX64 != Expected fEffective=%#RX64", Walk.fEffective, pWalkResult->fEffective);
994 }
995}
996
997
998static void tstExecuteGetPage(RTTEST hTest, PVM pVM, RTGCPTR GCPtr, uint8_t bEl, int rcExpected, PPGMPTWALKFAST pWalkResult)
999{
1000 PVMCPUCC pVCpu = pVM->apCpusR3[0];
1001
1002 g_MmuCfg.bEl = bEl;
1003
1004 /* Need to convert the expected result to the PGMPTWALK format. */
1005 PGMPTWALK WalkResult; RT_ZERO(WalkResult);
1006 WalkResult.GCPtr = pWalkResult->GCPtr;
1007 WalkResult.GCPhysNested = pWalkResult->GCPhysNested;
1008 WalkResult.GCPhys = pWalkResult->GCPhys;
1009 WalkResult.fEffective = pWalkResult->fEffective;
1010
1011 if (pWalkResult->fInfo & PGM_WALKINFO_IS_SLAT)
1012 WalkResult.fIsSlat = true;
1013 if (pWalkResult->fInfo & PGM_WALKINFO_BIG_PAGE)
1014 WalkResult.fBigPage = true;
1015 if (pWalkResult->fInfo & PGM_WALKINFO_GIGANTIC_PAGE)
1016 WalkResult.fGigantPage = true;
1017 if (pWalkResult->fInfo & PGM_WALKINFO_IS_LINEAR_ADDR_VALID)
1018 WalkResult.fIsLinearAddrValid = true;
1019 if (pWalkResult->fFailed & PGM_WALKFAIL_NOT_PRESENT)
1020 WalkResult.fNotPresent = true;
1021 if (pWalkResult->fFailed & PGM_WALKFAIL_RESERVED_BITS)
1022 WalkResult.fRsvdError = true;
1023 if (pWalkResult->fFailed & PGM_WALKFAIL_BAD_PHYSICAL_ADDRESS)
1024 WalkResult.fBadPhysAddr = true;
1025
1026 /*
1027 * QueryPageFast() can return VERR_ACCESS_DENIED, which GetPage() doesn't,
1028 * so only copy the failed result if GetPage() is expected to fail as well.
1029 */
1030 if (RT_FAILURE(rcExpected))
1031 {
1032 WalkResult.fFailed = pWalkResult->fFailed;
1033 WalkResult.uLevel = (pWalkResult->fFailed & PGM_WALKFAIL_LEVEL_MASK) >> PGM_WALKFAIL_LEVEL_SHIFT;
1034 WalkResult.fSucceeded = false;
1035 }
1036 else
1037 WalkResult.fSucceeded = true;
1038
1039 /** @todo Incorporate EL (for nested virt and EL3 later on). */
1040 uintptr_t idx = (GCPtr & RT_BIT_64(55))
1041 ? pVCpu->pgm.s.aidxGuestModeDataTtbr1[1]
1042 : pVCpu->pgm.s.aidxGuestModeDataTtbr0[1];
1043
1044 PGMPTWALK Walk; RT_ZERO(Walk);
1045 AssertReleaseReturnVoid(idx < RT_ELEMENTS(g_aPgmGuestModeData));
1046 AssertReleaseReturnVoid(g_aPgmGuestModeData[idx].pfnGetPage);
1047 int rc = g_aPgmGuestModeData[idx].pfnGetPage(pVCpu, GCPtr, &Walk);
1048 if (rc != rcExpected)
1049 RTTestFailed(hTest, "GetPage: Result rc=%Rrc != Expected rc=%Rrc", rc, rcExpected);
1050
1051 if (memcmp(&Walk, &WalkResult, sizeof(Walk)))
1052 {
1053 if (Walk.GCPtr != WalkResult.GCPtr)
1054 RTTestFailed(hTest, "GetPage: Result GCPtr=%RGv != Expected GCPtr=%RGv", Walk.GCPtr, WalkResult.GCPtr);
1055 if (Walk.GCPhysNested != WalkResult.GCPhysNested)
1056 RTTestFailed(hTest, "GetPage: Result GCPhysNested=%RGp != Expected GCPhysNested=%RGp", Walk.GCPhysNested, WalkResult.GCPhysNested);
1057 if (Walk.GCPhys != WalkResult.GCPhys)
1058 RTTestFailed(hTest, "GetPage: Result GCPhys=%RGp != Expected GCPhys=%RGp", Walk.GCPhys, WalkResult.GCPhys);
1059 if (Walk.fSucceeded != WalkResult.fSucceeded)
1060 RTTestFailed(hTest, "GetPage: Result fSucceeded=%RTbool != Expected fSucceeded=%RTbool", Walk.fSucceeded, WalkResult.fSucceeded);
1061 if (Walk.fIsSlat != WalkResult.fIsSlat)
1062 RTTestFailed(hTest, "GetPage: Result fIsSlat=%RTbool != Expected fIsSlat=%RTbool", Walk.fIsSlat, WalkResult.fIsSlat);
1063 if (Walk.fIsLinearAddrValid != WalkResult.fIsLinearAddrValid)
1064 RTTestFailed(hTest, "GetPage: Result fIsLinearAddrValid=%RTbool != Expected fIsLinearAddrValid=%RTbool", Walk.fIsLinearAddrValid, WalkResult.fIsLinearAddrValid);
1065 if (Walk.uLevel != WalkResult.uLevel)
1066 RTTestFailed(hTest, "GetPage: Result uLevel=%RU8 != Expected uLevel=%RU8", Walk.uLevel, WalkResult.uLevel);
1067 if (Walk.fNotPresent != WalkResult.fNotPresent)
1068 RTTestFailed(hTest, "GetPage: Result fNotPresent=%RTbool != Expected fNotPresent=%RTbool", Walk.fNotPresent, WalkResult.fNotPresent);
1069 if (Walk.fBadPhysAddr != WalkResult.fBadPhysAddr)
1070 RTTestFailed(hTest, "GetPage: Result fBadPhysAddr=%RTbool != Expected fBadPhysAddr=%RTbool", Walk.fBadPhysAddr, WalkResult.fBadPhysAddr);
1071 if (Walk.fRsvdError != WalkResult.fRsvdError)
1072 RTTestFailed(hTest, "GetPage: Result fRsvdError=%RTbool != Expected fRsvdError=%RTbool", Walk.fRsvdError, WalkResult.fRsvdError);
1073 if (Walk.fBigPage != WalkResult.fBigPage)
1074 RTTestFailed(hTest, "GetPage: Result fBigPage=%RTbool != Expected fBigPage=%RTbool", Walk.fBigPage, WalkResult.fBigPage);
1075 if (Walk.fGigantPage != WalkResult.fGigantPage)
1076 RTTestFailed(hTest, "GetPage: Result fGigantPage=%RTbool != Expected fGigantPage=%RTbool", Walk.fGigantPage, WalkResult.fGigantPage);
1077 if (Walk.fFailed != WalkResult.fFailed)
1078 RTTestFailed(hTest, "GetPage: Result fFailed=%#RX32 != Expected fFailed=%#RX32", Walk.fFailed, WalkResult.fFailed);
1079 if (Walk.fEffective != WalkResult.fEffective)
1080 RTTestFailed(hTest, "GetPage: Result fEffective=%#RX64 != Expected fEffective=%#RX64", Walk.fEffective, WalkResult.fEffective);
1081 }
1082}
1083
1084
1085static int tstTestcaseMmuRun(RTTEST hTest, RTJSONVAL hTestcase)
1086{
1087 RTJSONVAL hVal = NIL_RTJSONVAL;
1088 int rc = RTJsonValueQueryByName(hTestcase, "Tests", &hVal);
1089 if (RT_SUCCESS(rc))
1090 {
1091 RTJSONIT hIt = NIL_RTJSONIT;
1092 rc = RTJsonIteratorBeginArray(hVal, &hIt);
1093 if (RT_SUCCESS(rc))
1094 {
1095 for (;;)
1096 {
1097 RTJSONVAL hMemObj = NIL_RTJSONVAL;
1098 rc = RTJsonIteratorQueryValue(hIt, &hMemObj, NULL);
1099 if (RT_SUCCESS(rc))
1100 {
1101 uint64_t GCPtr = 0;
1102 rc = RTJsonValueQueryIntegerByName(hMemObj, "GCPtr", (int64_t *)&GCPtr);
1103 if (rc == VINF_SUCCESS)
1104 {
1105 static const TSTCFGBITFIELD s_aQPage[] =
1106 {
1107#define BITFIELD_CREATE_BOOL(a_Name, a_offBit) \
1108 { #a_Name, a_offBit, 1, NULL }
1109
1110 BITFIELD_CREATE_BOOL(READ, 0),
1111 BITFIELD_CREATE_BOOL(WRITE, 1),
1112 BITFIELD_CREATE_BOOL(EXECUTE, 2),
1113 BITFIELD_CREATE_BOOL(USER, 3),
1114 { NULL, 0, 0, NULL }
1115
1116#undef BITFIELD_CREATE_BOOL
1117 };
1118 AssertCompile(PGMQPAGE_F_READ == RT_BIT_32(0));
1119 AssertCompile(PGMQPAGE_F_WRITE == RT_BIT_32(1));
1120 AssertCompile(PGMQPAGE_F_EXECUTE == RT_BIT_32(2));
1121 AssertCompile(PGMQPAGE_F_USER_MODE == RT_BIT_32(3));
1122
1123 uint32_t fFlags = 0;
1124 rc = tstMmuCfgReadU32(hTest, hMemObj, "Flags", &s_aQPage[0], &fFlags);
1125 if (RT_SUCCESS(rc))
1126 {
1127 RTJSONVAL hMemResult = NIL_RTJSONVAL;
1128 rc = RTJsonValueQueryByName(hMemObj, "Result", &hMemResult);
1129 if (RT_SUCCESS(rc))
1130 {
1131 int rcQueryPageFast = VINF_SUCCESS;
1132 int rcGetPage = VINF_SUCCESS;
1133
1134 PGMPTWALKFAST WalkResult; PGMPTWALKFAST_ZERO(&WalkResult);
1135 WalkResult.GCPtr = GCPtr;
1136
1137 rc = tstResultInit(hTest, hMemResult, &WalkResult, &rcQueryPageFast, &rcGetPage);
1138 if (RT_SUCCESS(rc))
1139 {
1140 tstExecuteQueryPageFast(hTest, g_MmuCfg.pVM, GCPtr, fFlags, rcQueryPageFast, &WalkResult);
1141 tstExecuteGetPage(hTest, g_MmuCfg.pVM, GCPtr,
1142 (fFlags & PGMQPAGE_F_USER_MODE) ? 0 : 1,
1143 rcGetPage, &WalkResult);
1144 }
1145 RTJsonValueRelease(hMemResult);
1146 }
1147 else
1148 {
1149 RTTestFailed(hTest, "Querying 'Result' failed with %Rrc", rc);
1150 break;
1151 }
1152 }
1153 }
1154 else
1155 {
1156 RTTestFailed(hTest, "Querying 'GCPtr' failed with %Rrc", rc);
1157 break;
1158 }
1159
1160 RTJsonValueRelease(hMemObj);
1161 }
1162 else
1163 RTTestFailed(hTest, "Failed to retrieve memory range with %Rrc", rc);
1164
1165 rc = RTJsonIteratorNext(hIt);
1166 if (RT_FAILURE(rc))
1167 break;
1168 }
1169 if (rc == VERR_JSON_ITERATOR_END)
1170 rc = VINF_SUCCESS;
1171 RTJsonIteratorFree(hIt);
1172 }
1173 else
1174 RTTestFailed(hTest, "Failed to traverse JSON array with %Rrc", rc);
1175
1176
1177 RTJsonValueRelease(hVal);
1178 }
1179 else if (rc == VERR_NOT_FOUND)
1180 rc = VINF_SUCCESS;
1181 else
1182 RTTestFailed(hTest, "Failed to query \"Tests\" %Rrc", rc);
1183
1184 return rc;
1185}
1186
1187
1188static void tstExecuteTestcase(RTTEST hTest, RTJSONVAL hTestcase)
1189{
1190 RTJSONVAL hVal = NIL_RTJSONVAL;
1191 int rc = RTJsonValueQueryByName(hTestcase, "Name", &hVal);
1192 if (RT_SUCCESS(rc))
1193 {
1194 const char *pszTestcaseName = RTJsonValueGetString(hVal);
1195 if (pszTestcaseName)
1196 {
1197 RTTestSub(hTest, pszTestcaseName);
1198
1199 /* Reset the config for each testcase. */
1200 tstMmuCfgReset(&g_MmuCfg);
1201
1202 rc = tstTestcaseAddressSpacePrepare(hTest, hTestcase);
1203 if (RT_SUCCESS(rc))
1204 rc = tstTestcaseMmuConfigPrepare(hTest, &g_MmuCfg, hTestcase);
1205 if (RT_SUCCESS(rc))
1206 rc = tstTestcaseMmuRun(hTest, hTestcase);
1207 }
1208 else
1209 RTTestFailed(hTest, "The testcase name is not a string");
1210 RTJsonValueRelease(hVal);
1211 }
1212 else
1213 RTTestFailed(hTest, "Failed to query the testcase name with %Rrc", rc);
1214}
1215
1216
1217static void tstLoadAndRun(RTTEST hTest, RTJSONVAL hRoot)
1218{
1219 int rc = tstMmuCfgInit(&g_MmuCfg);
1220 if (RT_FAILURE(rc))
1221 {
1222 RTTestFailed(hTest, "Failed to initialize MMU config %Rrc", rc);
1223 return;
1224 }
1225
1226 RTJSONVALTYPE enmType = RTJsonValueGetType(hRoot);
1227 if (enmType == RTJSONVALTYPE_ARRAY)
1228 {
1229 /* Array of testcases. */
1230 RTJSONIT hIt = NIL_RTJSONIT;
1231 rc = RTJsonIteratorBeginArray(hRoot, &hIt);
1232 if (RT_SUCCESS(rc))
1233 {
1234 for (;;)
1235 {
1236 RTJSONVAL hTestcase = NIL_RTJSONVAL;
1237 rc = RTJsonIteratorQueryValue(hIt, &hTestcase, NULL /*ppszName*/);
1238 if (RT_SUCCESS(rc))
1239 {
1240 tstExecuteTestcase(hTest, hTestcase);
1241 RTJsonValueRelease(hTestcase);
1242 }
1243 else
1244 RTTestFailed(hTest, "Failed to retrieve testcase with %Rrc", rc);
1245
1246 rc = RTJsonIteratorNext(hIt);
1247 if (RT_FAILURE(rc))
1248 break;
1249 }
1250 if (rc == VERR_JSON_ITERATOR_END)
1251 rc = VINF_SUCCESS;
1252 RTJsonIteratorFree(hIt);
1253 }
1254 else /* An empty array is also an error */
1255 RTTestFailed(hTest, "Failed to traverse JSON array with %Rrc", rc);
1256 }
1257 else if (enmType == RTJSONVALTYPE_OBJECT)
1258 {
1259 /* Single testcase. */
1260 tstExecuteTestcase(hTest, hRoot);
1261 }
1262 else
1263 RTTestFailed(hTest, "JSON root is not an array or object containing a testcase");
1264 RTJsonValueRelease(hRoot);
1265 tstMmuCfgDestroy(&g_MmuCfg);
1266}
1267
1268
1269static void tstLoadFromFile(RTTEST hTest, const char *pszFilename)
1270{
1271 /* Load the configuration from the JSON config file. */
1272 RTERRINFOSTATIC ErrInfo;
1273 RTJSONVAL hRoot = NIL_RTJSONVAL;
1274 int rc = RTJsonParseFromFile(&hRoot, RTJSON_PARSE_F_JSON5, pszFilename, RTErrInfoInitStatic(&ErrInfo));
1275 if (RT_SUCCESS(rc))
1276 tstLoadAndRun(hTest, hRoot);
1277 else
1278 {
1279 if (RTErrInfoIsSet(&ErrInfo.Core))
1280 RTTestFailed(hTest, "RTJsonParseFromFile() for \"%s\" failed with %Rrc\n%s",
1281 pszFilename, rc, ErrInfo.Core.pszMsg);
1282 else
1283 RTTestFailed(hTest, "RTJsonParseFromFile() for \"%s\" failed with %Rrc",
1284 pszFilename, rc);
1285 }
1286}
1287
1288
1289static void tstBasic(RTTEST hTest)
1290{
1291 RTERRINFOSTATIC ErrInfo;
1292 RTJSONVAL hRoot = NIL_RTJSONVAL;
1293 int rc = RTJsonParseFromBuf(&hRoot, RTJSON_PARSE_F_JSON5,
1294 g_abtstPGMAllGst_armv8_1, g_cbtstPGMAllGst_armv8_1,
1295 RTErrInfoInitStatic(&ErrInfo));
1296 if (RT_SUCCESS(rc))
1297 tstLoadAndRun(hTest, hRoot);
1298 else
1299 {
1300 if (RTErrInfoIsSet(&ErrInfo.Core))
1301 RTTestFailed(hTest, "RTJsonParseFromBuf() failed with %Rrc\n%s", rc, ErrInfo.Core.pszMsg);
1302 else
1303 RTTestFailed(hTest, "RTJsonParseFromBuf() failed with %Rrc", rc);
1304 }
1305}
1306
1307
1308int main(int argc, char **argv)
1309{
1310 /*
1311 * We run the VMM in driverless mode to avoid needing to hardened the testcase
1312 */
1313 RTEXITCODE rcExit;
1314 int rc = RTR3InitExe(argc, &argv, SUPR3INIT_F_DRIVERLESS << RTR3INIT_FLAGS_SUPLIB_SHIFT);
1315 if (RT_SUCCESS(rc))
1316 {
1317 rc = RTTestCreate("tstPGMAllGst-armv8", &g_hTest);
1318 if (RT_SUCCESS(rc))
1319 {
1320 RTTestBanner(g_hTest);
1321 if (argc == 2)
1322 tstLoadFromFile(g_hTest, argv[1]);
1323 else
1324 tstBasic(g_hTest);
1325 rcExit = RTTestSummaryAndDestroy(g_hTest);
1326 }
1327 else
1328 rcExit = RTMsgErrorExitFailure("RTTestCreate failed: %Rrc", rc);
1329 }
1330 else
1331 rcExit = RTMsgInitFailure(rc);
1332 return rcExit;
1333}
1334
Note: See TracBrowser for help on using the repository browser.

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