VirtualBox

source: vbox/trunk/src/VBox/Devices/VMMDev/VMMDevTesting.cpp@ 103245

Last change on this file since 103245 was 103245, checked in by vboxsync, 16 months ago

VMMDev/VMMDevTesting: Fixed implicit fallthrough (found by Parfait). bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.0 KB
Line 
1/* $Id: VMMDevTesting.cpp 103245 2024-02-07 11:53:07Z vboxsync $ */
2/** @file
3 * VMMDev - Testing Extensions.
4 *
5 * To enable: VBoxManage setextradata vmname VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled 1
6 */
7
8/*
9 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.215389.xyz.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30
31/*********************************************************************************************************************************
32* Header Files *
33*********************************************************************************************************************************/
34#define LOG_GROUP LOG_GROUP_DEV_VMM
35#include <VBox/VMMDev.h>
36#include <VBox/vmm/vmapi.h>
37#include <VBox/log.h>
38#include <VBox/err.h>
39
40#include <iprt/asm.h>
41#include <iprt/assert.h>
42#include <iprt/string.h>
43#include <iprt/time.h>
44#include <iprt/test.h>
45
46#ifdef IN_RING3
47# define USING_VMM_COMMON_DEFS /* HACK ALERT! We ONLY want the EMT thread handles, so the common defs doesn't matter. */
48# include <VBox/vmm/vmcc.h>
49#endif
50#include <VBox/AssertGuest.h>
51
52#include "VMMDevState.h"
53#include "VMMDevTesting.h"
54
55
56#ifndef VBOX_WITHOUT_TESTING_FEATURES
57
58#define VMMDEV_TESTING_OUTPUT(a) \
59 do \
60 { \
61 LogAlways(a);\
62 LogRel(a);\
63 } while (0)
64
65/**
66 * @callback_method_impl{FNIOMMMIONEWWRITE}
67 */
68static DECLCALLBACK(VBOXSTRICTRC) vmmdevTestingMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
69{
70 RT_NOREF_PV(pvUser);
71
72 switch (off)
73 {
74 case VMMDEV_TESTING_MMIO_OFF_NOP_R3:
75#ifndef IN_RING3
76 return VINF_IOM_R3_MMIO_WRITE;
77#endif
78 case VMMDEV_TESTING_MMIO_OFF_NOP:
79 return VINF_SUCCESS;
80
81 default:
82 {
83 /*
84 * Readback register (64 bytes wide).
85 */
86 if ( ( off >= VMMDEV_TESTING_MMIO_OFF_READBACK
87 && off + cb <= VMMDEV_TESTING_MMIO_OFF_READBACK + VMMDEV_TESTING_READBACK_SIZE)
88#ifndef IN_RING3
89 || ( off >= VMMDEV_TESTING_MMIO_OFF_READBACK_R3
90 && off + cb <= VMMDEV_TESTING_MMIO_OFF_READBACK_R3 + VMMDEV_TESTING_READBACK_SIZE)
91#endif
92 )
93 {
94 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
95 off &= VMMDEV_TESTING_READBACK_SIZE - 1;
96 switch (cb)
97 {
98 case 8: *(uint64_t *)&pThis->TestingData.abReadBack[off] = *(uint64_t const *)pv; break;
99 case 4: *(uint32_t *)&pThis->TestingData.abReadBack[off] = *(uint32_t const *)pv; break;
100 case 2: *(uint16_t *)&pThis->TestingData.abReadBack[off] = *(uint16_t const *)pv; break;
101 case 1: *(uint8_t *)&pThis->TestingData.abReadBack[off] = *(uint8_t const *)pv; break;
102 default: memcpy(&pThis->TestingData.abReadBack[off], pv, cb); break;
103 }
104 return VINF_SUCCESS;
105 }
106#ifndef IN_RING3
107 if ( off >= VMMDEV_TESTING_MMIO_OFF_READBACK_R3
108 && off + cb <= VMMDEV_TESTING_MMIO_OFF_READBACK_R3 + 64)
109 return VINF_IOM_R3_MMIO_WRITE;
110#endif
111
112 break;
113 }
114
115 /*
116 * Odd NOP accesses.
117 */
118 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 1:
119 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 2:
120 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 3:
121 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 4:
122 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 5:
123 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 6:
124 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 7:
125#ifndef IN_RING3
126 return VINF_IOM_R3_MMIO_WRITE;
127#endif
128 case VMMDEV_TESTING_MMIO_OFF_NOP + 1:
129 case VMMDEV_TESTING_MMIO_OFF_NOP + 2:
130 case VMMDEV_TESTING_MMIO_OFF_NOP + 3:
131 case VMMDEV_TESTING_MMIO_OFF_NOP + 4:
132 case VMMDEV_TESTING_MMIO_OFF_NOP + 5:
133 case VMMDEV_TESTING_MMIO_OFF_NOP + 6:
134 case VMMDEV_TESTING_MMIO_OFF_NOP + 7:
135 return VINF_SUCCESS;
136 }
137 return VINF_SUCCESS;
138}
139
140
141/**
142 * @callback_method_impl{FNIOMMMIONEWREAD}
143 */
144static DECLCALLBACK(VBOXSTRICTRC) vmmdevTestingMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
145{
146 RT_NOREF_PV(pvUser);
147
148 switch (off)
149 {
150 case VMMDEV_TESTING_MMIO_OFF_NOP_R3:
151#ifndef IN_RING3
152 return VINF_IOM_R3_MMIO_READ;
153#endif
154 /* fall thru. */
155 case VMMDEV_TESTING_MMIO_OFF_NOP:
156 switch (cb)
157 {
158 case 8:
159 *(uint64_t *)pv = VMMDEV_TESTING_NOP_RET | ((uint64_t)VMMDEV_TESTING_NOP_RET << 32);
160 break;
161 case 4:
162 *(uint32_t *)pv = VMMDEV_TESTING_NOP_RET;
163 break;
164 case 2:
165 *(uint16_t *)pv = RT_LO_U16(VMMDEV_TESTING_NOP_RET);
166 break;
167 case 1:
168 *(uint8_t *)pv = (uint8_t)(VMMDEV_TESTING_NOP_RET & UINT8_MAX);
169 break;
170 default:
171 AssertFailed();
172 return VERR_INTERNAL_ERROR_5;
173 }
174 return VINF_SUCCESS;
175
176
177 default:
178 {
179 /*
180 * Readback register (64 bytes wide).
181 */
182 if ( ( off >= VMMDEV_TESTING_MMIO_OFF_READBACK
183 && off + cb <= VMMDEV_TESTING_MMIO_OFF_READBACK + 64)
184#ifndef IN_RING3
185 || ( off >= VMMDEV_TESTING_MMIO_OFF_READBACK_R3
186 && off + cb <= VMMDEV_TESTING_MMIO_OFF_READBACK_R3 + 64)
187#endif
188 )
189 {
190 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
191 off &= 0x3f;
192 switch (cb)
193 {
194 case 8: *(uint64_t *)pv = *(uint64_t const *)&pThis->TestingData.abReadBack[off]; break;
195 case 4: *(uint32_t *)pv = *(uint32_t const *)&pThis->TestingData.abReadBack[off]; break;
196 case 2: *(uint16_t *)pv = *(uint16_t const *)&pThis->TestingData.abReadBack[off]; break;
197 case 1: *(uint8_t *)pv = *(uint8_t const *)&pThis->TestingData.abReadBack[off]; break;
198 default: memcpy(pv, &pThis->TestingData.abReadBack[off], cb); break;
199 }
200 return VINF_SUCCESS;
201 }
202#ifndef IN_RING3
203 if ( off >= VMMDEV_TESTING_MMIO_OFF_READBACK_R3
204 && off + cb <= VMMDEV_TESTING_MMIO_OFF_READBACK_R3 + 64)
205 return VINF_IOM_R3_MMIO_READ;
206#endif
207 break;
208 }
209
210 /*
211 * Odd NOP accesses (for 16-bit code mainly).
212 */
213 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 1:
214 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 2:
215 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 3:
216 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 4:
217 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 5:
218 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 6:
219 case VMMDEV_TESTING_MMIO_OFF_NOP_R3 + 7:
220#ifndef IN_RING3
221 return VINF_IOM_R3_MMIO_READ;
222#endif
223 case VMMDEV_TESTING_MMIO_OFF_NOP + 1:
224 case VMMDEV_TESTING_MMIO_OFF_NOP + 2:
225 case VMMDEV_TESTING_MMIO_OFF_NOP + 3:
226 case VMMDEV_TESTING_MMIO_OFF_NOP + 4:
227 case VMMDEV_TESTING_MMIO_OFF_NOP + 5:
228 case VMMDEV_TESTING_MMIO_OFF_NOP + 6:
229 case VMMDEV_TESTING_MMIO_OFF_NOP + 7:
230 {
231 static uint8_t const s_abNopValue[8] =
232 {
233 VMMDEV_TESTING_NOP_RET & 0xff,
234 (VMMDEV_TESTING_NOP_RET >> 8) & 0xff,
235 (VMMDEV_TESTING_NOP_RET >> 16) & 0xff,
236 (VMMDEV_TESTING_NOP_RET >> 24) & 0xff,
237 VMMDEV_TESTING_NOP_RET & 0xff,
238 (VMMDEV_TESTING_NOP_RET >> 8) & 0xff,
239 (VMMDEV_TESTING_NOP_RET >> 16) & 0xff,
240 (VMMDEV_TESTING_NOP_RET >> 24) & 0xff,
241 };
242
243 memset(pv, 0xff, cb);
244 memcpy(pv, &s_abNopValue[off & 7], RT_MIN(8 - (off & 7), cb));
245 return VINF_SUCCESS;
246 }
247 }
248
249 return VINF_IOM_MMIO_UNUSED_FF;
250}
251
252#ifdef IN_RING3
253
254/**
255 * Executes the VMMDEV_TESTING_CMD_VALUE_REG command when the data is ready.
256 *
257 * @param pDevIns The PDM device instance.
258 * @param pThis The instance VMMDev data.
259 */
260static void vmmdevTestingCmdExec_ValueReg(PPDMDEVINS pDevIns, PVMMDEV pThis)
261{
262 char *pszRegNm = strchr(pThis->TestingData.String.sz, ':');
263 if (pszRegNm)
264 {
265 *pszRegNm++ = '\0';
266 pszRegNm = RTStrStrip(pszRegNm);
267 }
268 char *pszValueNm = RTStrStrip(pThis->TestingData.String.sz);
269 size_t const cchValueNm = strlen(pszValueNm);
270 if (cchValueNm && pszRegNm && *pszRegNm)
271 {
272 VMCPUID idCpu = PDMDevHlpGetCurrentCpuId(pDevIns);
273 uint64_t u64Value;
274 int rc2 = PDMDevHlpDBGFRegNmQueryU64(pDevIns, idCpu, pszRegNm, &u64Value);
275 if (RT_SUCCESS(rc2))
276 {
277 const char *pszWarn = rc2 == VINF_DBGF_TRUNCATED_REGISTER ? " truncated" : "";
278#if 1 /*!RTTestValue format*/
279 char szFormat[128], szValue[128];
280 RTStrPrintf(szFormat, sizeof(szFormat), "%%VR{%s}", pszRegNm);
281 rc2 = PDMDevHlpDBGFRegPrintf(pDevIns, idCpu, szValue, sizeof(szValue), szFormat);
282 if (RT_SUCCESS(rc2))
283 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %16s {reg=%s}%s\n",
284 pszValueNm,
285 (ssize_t)cchValueNm - 12 > 48 ? 0 : 48 - ((ssize_t)cchValueNm - 12), "",
286 szValue, pszRegNm, pszWarn));
287 else
288#endif
289 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %'9llu (%#llx) [0] {reg=%s}%s\n",
290 pszValueNm,
291 (ssize_t)cchValueNm - 12 > 48 ? 0 : 48 - ((ssize_t)cchValueNm - 12), "",
292 u64Value, u64Value, pszRegNm, pszWarn));
293 }
294 else
295 VMMDEV_TESTING_OUTPUT(("testing: error querying register '%s' for value '%s': %Rrc\n",
296 pszRegNm, pszValueNm, rc2));
297 }
298 else
299 VMMDEV_TESTING_OUTPUT(("testing: malformed register value '%s'/'%s'\n", pszValueNm, pszRegNm));
300}
301
302#endif /* IN_RING3 */
303
304/**
305 * @callback_method_impl{FNIOMIOPORTNEWOUT}
306 */
307static DECLCALLBACK(VBOXSTRICTRC)
308vmmdevTestingIoWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
309{
310 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
311#ifdef IN_RING3
312 PVMMDEVCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVMMDEVCC);
313#endif
314 RT_NOREF_PV(pvUser);
315
316 switch (offPort)
317 {
318 /*
319 * The NOP I/O ports are used for performance measurements.
320 */
321 case VMMDEV_TESTING_IOPORT_NOP - VMMDEV_TESTING_IOPORT_BASE:
322 switch (cb)
323 {
324 case 4:
325 case 2:
326 case 1:
327 break;
328 default:
329 AssertFailed();
330 return VERR_INTERNAL_ERROR_2;
331 }
332 return VINF_SUCCESS;
333
334 case VMMDEV_TESTING_IOPORT_NOP_R3 - VMMDEV_TESTING_IOPORT_BASE:
335 switch (cb)
336 {
337 case 4:
338 case 2:
339 case 1:
340#ifndef IN_RING3
341 return VINF_IOM_R3_IOPORT_WRITE;
342#else
343 return VINF_SUCCESS;
344#endif
345 default:
346 AssertFailed();
347 return VERR_INTERNAL_ERROR_2;
348 }
349
350 /* The timestamp I/O ports are read-only. */
351 case VMMDEV_TESTING_IOPORT_TS_LOW - VMMDEV_TESTING_IOPORT_BASE:
352 case VMMDEV_TESTING_IOPORT_TS_HIGH - VMMDEV_TESTING_IOPORT_BASE:
353 break;
354
355 /*
356 * The command port (DWORD and WORD write only).
357 * (We have to allow WORD writes for 286, 186 and 8086 execution modes.)
358 */
359 case VMMDEV_TESTING_IOPORT_CMD - VMMDEV_TESTING_IOPORT_BASE:
360 if (cb == 2)
361 {
362 u32 |= VMMDEV_TESTING_CMD_MAGIC_HI_WORD;
363 cb = 4;
364 }
365 if (cb == 4)
366 {
367 pThis->u32TestingCmd = u32;
368 pThis->offTestingData = 0;
369 pThis->cbReadableTestingData = 0;
370 RT_ZERO(pThis->TestingData);
371 return VINF_SUCCESS;
372 }
373 break;
374
375 /*
376 * The data port. Used of providing data for a command.
377 */
378 case VMMDEV_TESTING_IOPORT_DATA - VMMDEV_TESTING_IOPORT_BASE:
379 {
380 uint32_t uCmd = pThis->u32TestingCmd;
381 uint32_t off = pThis->offTestingData;
382 switch (uCmd)
383 {
384 case VMMDEV_TESTING_CMD_INIT:
385 case VMMDEV_TESTING_CMD_SUB_NEW:
386 case VMMDEV_TESTING_CMD_FAILED:
387 case VMMDEV_TESTING_CMD_SKIPPED:
388 case VMMDEV_TESTING_CMD_PRINT:
389 if ( off < sizeof(pThis->TestingData.String.sz) - 1
390 && cb == 1)
391 {
392 if (u32)
393 {
394 pThis->TestingData.String.sz[off] = u32;
395 pThis->offTestingData = off + 1;
396 }
397 else
398 {
399#ifdef IN_RING3
400 pThis->TestingData.String.sz[off] = '\0';
401 switch (uCmd)
402 {
403 case VMMDEV_TESTING_CMD_INIT:
404 VMMDEV_TESTING_OUTPUT(("testing: INIT '%s'\n", pThis->TestingData.String.sz));
405 if (pThisCC->hTestingTest != NIL_RTTEST)
406 {
407 RTTestChangeName(pThisCC->hTestingTest, pThis->TestingData.String.sz);
408 RTTestBanner(pThisCC->hTestingTest);
409 }
410 break;
411 case VMMDEV_TESTING_CMD_SUB_NEW:
412 VMMDEV_TESTING_OUTPUT(("testing: SUB_NEW '%s'\n", pThis->TestingData.String.sz));
413 if (pThisCC->hTestingTest != NIL_RTTEST)
414 RTTestSub(pThisCC->hTestingTest, pThis->TestingData.String.sz);
415 break;
416 case VMMDEV_TESTING_CMD_FAILED:
417 if (pThisCC->hTestingTest != NIL_RTTEST)
418 RTTestFailed(pThisCC->hTestingTest, "%s", pThis->TestingData.String.sz);
419 VMMDEV_TESTING_OUTPUT(("testing: FAILED '%s'\n", pThis->TestingData.String.sz));
420 break;
421 case VMMDEV_TESTING_CMD_SKIPPED:
422 if (pThisCC->hTestingTest != NIL_RTTEST)
423 {
424 if (off)
425 RTTestSkipped(pThisCC->hTestingTest, "%s", pThis->TestingData.String.sz);
426 else
427 RTTestSkipped(pThisCC->hTestingTest, NULL);
428 }
429 VMMDEV_TESTING_OUTPUT(("testing: SKIPPED '%s'\n", pThis->TestingData.String.sz));
430 break;
431 case VMMDEV_TESTING_CMD_PRINT:
432 if (pThisCC->hTestingTest != NIL_RTTEST && off)
433 RTTestPrintf(pThisCC->hTestingTest, RTTESTLVL_ALWAYS, "%s", pThis->TestingData.String.sz);
434 VMMDEV_TESTING_OUTPUT(("testing: '%s'\n", pThis->TestingData.String.sz));
435 break;
436 }
437#else
438 return VINF_IOM_R3_IOPORT_WRITE;
439#endif
440 }
441 return VINF_SUCCESS;
442 }
443 break;
444
445 case VMMDEV_TESTING_CMD_TERM:
446 case VMMDEV_TESTING_CMD_SUB_DONE:
447 if (cb == 2)
448 {
449 if (off == 0)
450 {
451 pThis->TestingData.Error.c = u32;
452 pThis->offTestingData = 2;
453 break;
454 }
455 if (off == 2)
456 {
457 u32 <<= 16;
458 u32 |= pThis->TestingData.Error.c & UINT16_MAX;
459 cb = 4;
460 off = 0;
461 }
462 else
463 break;
464 }
465
466 if ( off == 0
467 && cb == 4)
468 {
469#ifdef IN_RING3
470 pThis->TestingData.Error.c = u32;
471 if (uCmd == VMMDEV_TESTING_CMD_TERM)
472 {
473 if (pThisCC->hTestingTest != NIL_RTTEST)
474 {
475 while (RTTestErrorCount(pThisCC->hTestingTest) < u32)
476 RTTestErrorInc(pThisCC->hTestingTest); /* A bit stupid, but does the trick. */
477 RTTestSubDone(pThisCC->hTestingTest);
478 RTTestSummaryAndDestroy(pThisCC->hTestingTest);
479 pThisCC->hTestingTest = NIL_RTTEST;
480 }
481 VMMDEV_TESTING_OUTPUT(("testing: TERM - %u errors\n", u32));
482 }
483 else
484 {
485 if (pThisCC->hTestingTest != NIL_RTTEST)
486 {
487 while (RTTestSubErrorCount(pThisCC->hTestingTest) < u32)
488 RTTestErrorInc(pThisCC->hTestingTest); /* A bit stupid, but does the trick. */
489 RTTestSubDone(pThisCC->hTestingTest);
490 }
491 VMMDEV_TESTING_OUTPUT(("testing: SUB_DONE - %u errors\n", u32));
492 }
493 return VINF_SUCCESS;
494#else
495 return VINF_IOM_R3_IOPORT_WRITE;
496#endif
497 }
498 break;
499
500 case VMMDEV_TESTING_CMD_VALUE:
501 if (cb == 4)
502 {
503 if (off == 0)
504 pThis->TestingData.Value.u64Value.s.Lo = u32;
505 else if (off == 4)
506 pThis->TestingData.Value.u64Value.s.Hi = u32;
507 else if (off == 8)
508 pThis->TestingData.Value.u32Unit = u32;
509 else
510 break;
511 pThis->offTestingData = off + 4;
512 return VINF_SUCCESS;
513 }
514 if (cb == 2)
515 {
516 if (off == 0)
517 pThis->TestingData.Value.u64Value.Words.w0 = (uint16_t)u32;
518 else if (off == 2)
519 pThis->TestingData.Value.u64Value.Words.w1 = (uint16_t)u32;
520 else if (off == 4)
521 pThis->TestingData.Value.u64Value.Words.w2 = (uint16_t)u32;
522 else if (off == 6)
523 pThis->TestingData.Value.u64Value.Words.w3 = (uint16_t)u32;
524 else if (off == 8)
525 pThis->TestingData.Value.u32Unit = (uint16_t)u32;
526 else if (off == 10)
527 pThis->TestingData.Value.u32Unit |= u32 << 16;
528 else
529 break;
530 pThis->offTestingData = off + 2;
531 return VINF_SUCCESS;
532 }
533
534 if ( off >= 12
535 && cb == 1
536 && off - 12 < sizeof(pThis->TestingData.Value.szName) - 1)
537 {
538 if (u32)
539 {
540 pThis->TestingData.Value.szName[off - 12] = u32;
541 pThis->offTestingData = off + 1;
542 }
543 else
544 {
545#ifdef IN_RING3
546 pThis->TestingData.Value.szName[off - 12] = '\0';
547
548 RTTESTUNIT enmUnit = (RTTESTUNIT)pThis->TestingData.Value.u32Unit;
549 if (enmUnit <= RTTESTUNIT_INVALID || enmUnit >= RTTESTUNIT_END)
550 {
551 VMMDEV_TESTING_OUTPUT(("Invalid log value unit %#x\n", pThis->TestingData.Value.u32Unit));
552 enmUnit = RTTESTUNIT_NONE;
553 }
554 if (pThisCC->hTestingTest != NIL_RTTEST)
555 RTTestValue(pThisCC->hTestingTest, pThis->TestingData.Value.szName,
556 pThis->TestingData.Value.u64Value.u, enmUnit);
557
558 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %'9llu (%#llx) [%u]\n",
559 pThis->TestingData.Value.szName,
560 off - 12 > 48 ? 0 : 48 - (off - 12), "",
561 pThis->TestingData.Value.u64Value.u, pThis->TestingData.Value.u64Value.u,
562 pThis->TestingData.Value.u32Unit));
563#else
564 return VINF_IOM_R3_IOPORT_WRITE;
565#endif
566 }
567 return VINF_SUCCESS;
568 }
569 break;
570
571
572 /*
573 * RTTestValue with the output from DBGFR3RegNmQuery.
574 */
575 case VMMDEV_TESTING_CMD_VALUE_REG:
576 {
577 if ( off < sizeof(pThis->TestingData.String.sz) - 1
578 && cb == 1)
579 {
580 pThis->TestingData.String.sz[off] = u32;
581 if (u32)
582 pThis->offTestingData = off + 1;
583 else
584#ifdef IN_RING3
585 vmmdevTestingCmdExec_ValueReg(pDevIns, pThis);
586#else
587 return VINF_IOM_R3_IOPORT_WRITE;
588#endif
589 return VINF_SUCCESS;
590 }
591 break;
592 }
593
594 /*
595 * Query configuration.
596 */
597 case VMMDEV_TESTING_CMD_QUERY_CFG:
598 {
599 switch (u32)
600 {
601 case VMMDEV_TESTING_CFG_DWORD0:
602 case VMMDEV_TESTING_CFG_DWORD1:
603 case VMMDEV_TESTING_CFG_DWORD2:
604 case VMMDEV_TESTING_CFG_DWORD3:
605 case VMMDEV_TESTING_CFG_DWORD4:
606 case VMMDEV_TESTING_CFG_DWORD5:
607 case VMMDEV_TESTING_CFG_DWORD6:
608 case VMMDEV_TESTING_CFG_DWORD7:
609 case VMMDEV_TESTING_CFG_DWORD8:
610 case VMMDEV_TESTING_CFG_DWORD9:
611 pThis->cbReadableTestingData = sizeof(pThis->TestingData.u32);
612 pThis->TestingData.u32 = pThis->au32TestingCfgDwords[u32 - VMMDEV_TESTING_CFG_DWORD0];
613 break;
614
615 case VMMDEV_TESTING_CFG_IS_NEM_LINUX:
616 case VMMDEV_TESTING_CFG_IS_NEM_WINDOWS:
617 case VMMDEV_TESTING_CFG_IS_NEM_DARWIN:
618 {
619 pThis->cbReadableTestingData = sizeof(pThis->TestingData.b);
620#if defined(RT_OS_DARWIN)
621 pThis->TestingData.b = u32 == VMMDEV_TESTING_CFG_IS_NEM_DARWIN
622 && PDMDevHlpGetMainExecutionEngine(pDevIns) == VM_EXEC_ENGINE_NATIVE_API;
623#elif defined(RT_OS_LINUX)
624 pThis->TestingData.b = u32 == VMMDEV_TESTING_CFG_IS_NEM_LINUX
625 && PDMDevHlpGetMainExecutionEngine(pDevIns) == VM_EXEC_ENGINE_NATIVE_API;
626#elif defined(RT_OS_WINDOWS)
627 pThis->TestingData.b = u32 == VMMDEV_TESTING_CFG_IS_NEM_WINDOWS
628 && PDMDevHlpGetMainExecutionEngine(pDevIns) == VM_EXEC_ENGINE_NATIVE_API;
629#else
630 pThis->TestingData.b = false;
631#endif
632 break;
633 }
634 }
635 break;
636 }
637
638 default:
639 break;
640 }
641 Log(("VMMDEV_TESTING_IOPORT_CMD: bad access; cmd=%#x off=%#x cb=%#x u32=%#x\n", uCmd, off, cb, u32));
642 return VINF_SUCCESS;
643 }
644
645 /*
646 * Configure the locking contention test.
647 */
648 case VMMDEV_TESTING_IOPORT_LOCKED_HI - VMMDEV_TESTING_IOPORT_BASE:
649 case VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE:
650 {
651 switch (cb)
652 {
653 case 4:
654 {
655 bool const fReadWriteSection = pThis->TestingLockControl.s.fReadWriteSection;
656 int rc;
657#ifndef IN_RING3
658 if (!pThis->TestingLockControl.s.fMustSucceed)
659 {
660 if (!fReadWriteSection)
661 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_IOM_R3_IOPORT_WRITE);
662 else
663 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_IOM_R3_IOPORT_WRITE);
664 if (rc != VINF_SUCCESS)
665 return rc;
666 }
667 else
668#endif
669 {
670 if (!fReadWriteSection)
671 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_SUCCESS);
672 else
673 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
674 AssertRCReturn(rc, rc);
675 }
676
677 if (offPort == VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE)
678 {
679 if (pThis->TestingLockControl.au32[0] != u32)
680 {
681 pThis->TestingLockControl.au32[0] = u32;
682 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTestingLockEvt);
683 }
684 }
685 else
686 {
687 u32 &= ~VMMDEV_TESTING_LOCKED_HI_MBZ_MASK;
688 if (pThis->TestingLockControl.au32[1] != u32)
689 {
690 pThis->TestingLockControl.au32[1] = u32;
691 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTestingLockEvt);
692 }
693 }
694
695 if (!fReadWriteSection)
696 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
697 else
698 PDMDevHlpCritSectRwLeaveExcl(pDevIns, &pThis->CritSectRw);
699 return VINF_SUCCESS;
700 }
701
702 case 2:
703 case 1:
704 ASSERT_GUEST_FAILED();
705 break;
706
707 default:
708 AssertFailed();
709 return VERR_INTERNAL_ERROR_2;
710 }
711 break;
712 }
713
714 default:
715 break;
716 }
717
718 return VERR_IOM_IOPORT_UNUSED;
719}
720
721
722/**
723 * @callback_method_impl{FNIOMIOPORTNEWIN}
724 */
725static DECLCALLBACK(VBOXSTRICTRC)
726vmmdevTestingIoRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
727{
728 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
729 RT_NOREF_PV(pvUser);
730
731 switch (offPort)
732 {
733 /*
734 * The NOP I/O ports are used for performance measurements.
735 */
736 case VMMDEV_TESTING_IOPORT_NOP - VMMDEV_TESTING_IOPORT_BASE:
737 switch (cb)
738 {
739 case 4:
740 case 2:
741 case 1:
742 break;
743 default:
744 AssertFailed();
745 return VERR_INTERNAL_ERROR_2;
746 }
747 *pu32 = VMMDEV_TESTING_NOP_RET;
748 return VINF_SUCCESS;
749
750 case VMMDEV_TESTING_IOPORT_NOP_R3 - VMMDEV_TESTING_IOPORT_BASE:
751 switch (cb)
752 {
753 case 4:
754 case 2:
755 case 1:
756#ifndef IN_RING3
757 return VINF_IOM_R3_IOPORT_READ;
758#else
759 *pu32 = VMMDEV_TESTING_NOP_RET;
760 return VINF_SUCCESS;
761#endif
762 default:
763 AssertFailed();
764 return VERR_INTERNAL_ERROR_2;
765 }
766
767 /*
768 * The timestamp I/O ports are obviously used for getting a good fix
769 * on the current time (as seen by the host?).
770 *
771 * The high word is latched when reading the low, so reading low + high
772 * gives you a 64-bit timestamp value.
773 */
774 case VMMDEV_TESTING_IOPORT_TS_LOW - VMMDEV_TESTING_IOPORT_BASE:
775 if (cb == 4)
776 {
777 uint64_t NowTS = RTTimeNanoTS();
778 *pu32 = (uint32_t)NowTS;
779 pThis->u32TestingHighTimestamp = (uint32_t)(NowTS >> 32);
780 return VINF_SUCCESS;
781 }
782 break;
783
784 case VMMDEV_TESTING_IOPORT_TS_HIGH - VMMDEV_TESTING_IOPORT_BASE:
785 if (cb == 4)
786 {
787 *pu32 = pThis->u32TestingHighTimestamp;
788 return VINF_SUCCESS;
789 }
790 break;
791
792 /*
793 * Just return the current locking configuration value after first
794 * acquiring the lock of course.
795 */
796 case VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE:
797 case VMMDEV_TESTING_IOPORT_LOCKED_HI - VMMDEV_TESTING_IOPORT_BASE:
798 switch (cb)
799 {
800 case 4:
801 case 2:
802 case 1:
803 {
804 /*
805 * Check configuration and enter the designation critical
806 * section in the specific fashion.
807 */
808 bool const fReadWriteSection = pThis->TestingLockControl.s.fReadWriteSection;
809 bool const fEmtShared = pThis->TestingLockControl.s.fEmtShared;
810 int rc;
811#ifndef IN_RING3
812 if (!pThis->TestingLockControl.s.fMustSucceed)
813 {
814 if (!fReadWriteSection)
815 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_IOM_R3_IOPORT_READ);
816 else if (!fEmtShared)
817 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_IOM_R3_IOPORT_READ);
818 else
819 rc = PDMDevHlpCritSectRwEnterShared(pDevIns, &pThis->CritSectRw, VINF_IOM_R3_IOPORT_READ);
820 if (rc != VINF_SUCCESS)
821 return rc;
822 }
823 else
824#endif
825 {
826 if (!fReadWriteSection)
827 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_SUCCESS);
828 else if (!fEmtShared)
829 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
830 else
831 rc = PDMDevHlpCritSectRwEnterShared(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
832 AssertRCReturn(rc, rc);
833 }
834
835 /*
836 * Grab return value and, if requested, hold for a while.
837 */
838 *pu32 = pThis->TestingLockControl.au32[ offPort
839 - (VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE)];
840 uint64_t cTicks = (uint64_t)pThis->TestingLockControl.s.cKiloTicksEmtHold * _1K;
841 if (cTicks)
842 {
843 uint64_t const uStartTick = ASMReadTSC();
844 do
845 {
846 ASMNopPause();
847 ASMNopPause();
848 } while (ASMReadTSC() - uStartTick < cTicks);
849 }
850
851 /*
852 * Leave.
853 */
854 if (!fReadWriteSection)
855 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
856 else if (!fEmtShared)
857 PDMDevHlpCritSectRwLeaveExcl(pDevIns, &pThis->CritSectRw);
858 else
859 PDMDevHlpCritSectRwLeaveShared(pDevIns, &pThis->CritSectRw);
860 return VINF_SUCCESS;
861 }
862
863 default:
864 AssertFailed();
865 return VERR_INTERNAL_ERROR_2;
866 }
867
868 /*
869 * The command registers is write-only.
870 */
871 case VMMDEV_TESTING_IOPORT_CMD - VMMDEV_TESTING_IOPORT_BASE:
872 break;
873
874 /*
875 * The data register is only readable after a query command, otherwise it
876 * behaves as an undefined port. Return zeros if the guest reads too much.
877 */
878 case VMMDEV_TESTING_IOPORT_DATA - VMMDEV_TESTING_IOPORT_BASE:
879 if (pThis->cbReadableTestingData > 0)
880 {
881 if (pThis->offTestingData < pThis->cbReadableTestingData)
882 {
883 switch (RT_MIN(cb, pThis->cbReadableTestingData - pThis->offTestingData))
884 {
885 case 1:
886 *pu32 = pThis->TestingData.ab[pThis->offTestingData++];
887 break;
888 case 2:
889 *pu32 = pThis->TestingData.ab[pThis->offTestingData]
890 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 1] << 8);
891 pThis->offTestingData += 2;
892 break;
893 case 3:
894 *pu32 = pThis->TestingData.ab[pThis->offTestingData]
895 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 1] << 8)
896 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 2] << 16);
897 pThis->offTestingData += 3;
898 break;
899 case 4:
900 *pu32 = pThis->TestingData.ab[pThis->offTestingData]
901 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 1] << 8)
902 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 2] << 16)
903 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 3] << 24);
904 pThis->offTestingData += 4;
905 break;
906 }
907 }
908 else
909 *pu32 = 0;
910 return VINF_SUCCESS;
911 }
912 break;
913
914 default:
915 break;
916 }
917
918 return VERR_IOM_IOPORT_UNUSED;
919}
920
921#ifdef IN_RING3
922
923/**
924 * @callback_method_impl{FNPDMTHREADDEV}
925 */
926static DECLCALLBACK(int) vmmdevR3TestingLockingThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
927{
928 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
929 PVM pVM = PDMDevHlpGetVM(pDevIns);
930 AssertPtr(pVM);
931
932 while (RT_LIKELY(pThread->enmState == PDMTHREADSTATE_RUNNING))
933 {
934 int rc;
935 uint32_t cNsNextWait = 0;
936 uint32_t const fCfgHi = pThis->TestingLockControl.au32[1];
937 if (fCfgHi & VMMDEV_TESTING_LOCKED_HI_ENABLED)
938 {
939 /*
940 * take lock
941 */
942 if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_TYPE_RW))
943 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_SUCCESS);
944 else if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED))
945 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
946 else
947 rc = PDMDevHlpCritSectRwEnterShared(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
948 AssertLogRelRCReturn(rc, rc);
949
950 /*
951 * Delay releasing lock.
952 */
953 cNsNextWait = pThis->TestingLockControl.s.cUsBetween * RT_NS_1US;
954 if (pThis->TestingLockControl.s.cUsHold)
955 {
956 PDMDevHlpSUPSemEventWaitNsRelIntr(pDevIns, pThis->hTestingLockEvt, pThis->TestingLockControl.s.cUsHold);
957 if (pThis->TestingLockControl.s.fPokeBeforeRelease)
958 VMCC_FOR_EACH_VMCPU_STMT(pVM, RTThreadPoke(pVCpu->hThread));
959 }
960
961 /*
962 * Release lock.
963 */
964 if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_TYPE_RW))
965 rc = PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
966 else if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED))
967 rc = PDMDevHlpCritSectRwLeaveExcl(pDevIns, &pThis->CritSectRw);
968 else
969 rc = PDMDevHlpCritSectRwLeaveShared(pDevIns, &pThis->CritSectRw);
970 AssertLogRelRCReturn(rc, rc);
971 }
972
973 /*
974 * Wait for the next iteration.
975 */
976 if (RT_LIKELY(pThread->enmState == PDMTHREADSTATE_RUNNING))
977 { /* likely */ }
978 else
979 break;
980 if (cNsNextWait > 0)
981 PDMDevHlpSUPSemEventWaitNsRelIntr(pDevIns, pThis->hTestingLockEvt, cNsNextWait);
982 else
983 PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hTestingLockEvt, RT_INDEFINITE_WAIT);
984 }
985
986 return VINF_SUCCESS;
987}
988
989
990/**
991 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
992 */
993static DECLCALLBACK(int) vmmdevR3TestingLockingThreadWakeup(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
994{
995 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
996 RT_NOREF(pThread);
997 return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTestingLockEvt);
998}
999
1000
1001/**
1002 * Initializes the testing part of the VMMDev if enabled.
1003 *
1004 * @param pDevIns The VMMDev device instance.
1005 */
1006void vmmdevR3TestingTerminate(PPDMDEVINS pDevIns)
1007{
1008 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
1009 PVMMDEVCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVMMDEVCC);
1010 if (!pThis->fTestingEnabled)
1011 return;
1012
1013 if (pThisCC->hTestingTest != NIL_RTTEST)
1014 {
1015 RTTestFailed(pThisCC->hTestingTest, "Still open at vmmdev destruction.");
1016 RTTestSummaryAndDestroy(pThisCC->hTestingTest);
1017 pThisCC->hTestingTest = NIL_RTTEST;
1018 }
1019}
1020
1021
1022/**
1023 * Initializes the testing part of the VMMDev if enabled.
1024 *
1025 * @returns VBox status code.
1026 * @param pDevIns The VMMDev device instance.
1027 */
1028int vmmdevR3TestingInitialize(PPDMDEVINS pDevIns)
1029{
1030 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
1031 PVMMDEVCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVMMDEVCC);
1032 int rc;
1033
1034 if (!pThis->fTestingEnabled)
1035 return VINF_SUCCESS;
1036
1037 if (pThis->fTestingMMIO)
1038 {
1039 /*
1040 * Register a chunk of MMIO memory that we'll use for various
1041 * tests interfaces. Optional, needs to be explicitly enabled.
1042 */
1043 rc = PDMDevHlpMmioCreateAndMap(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE,
1044 vmmdevTestingMmioWrite, vmmdevTestingMmioRead,
1045 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
1046 "VMMDev Testing", &pThis->hMmioTesting);
1047 AssertRCReturn(rc, rc);
1048 }
1049
1050 /*
1051 * Register the I/O ports used for testing.
1052 */
1053 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT,
1054 vmmdevTestingIoWrite, vmmdevTestingIoRead, "VMMDev Testing", NULL /*paExtDescs*/,
1055 &pThis->hIoPortTesting);
1056 AssertRCReturn(rc, rc);
1057
1058 /*
1059 * Initialize the read/write critical section used for the locking tests.
1060 */
1061 rc = PDMDevHlpCritSectRwInit(pDevIns, &pThis->CritSectRw, RT_SRC_POS, "VMMLockRW");
1062 AssertRCReturn(rc, rc);
1063
1064 /*
1065 * Create the locking thread.
1066 */
1067 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hTestingLockEvt);
1068 AssertRCReturn(rc, rc);
1069 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->pTestingLockThread, NULL /*pvUser*/, vmmdevR3TestingLockingThread,
1070 vmmdevR3TestingLockingThreadWakeup, 0 /*cbStack*/, RTTHREADTYPE_IO, "VMMLockT");
1071 AssertRCReturn(rc, rc);
1072
1073 /*
1074 * Open the XML output file(/pipe/whatever) if specfied.
1075 */
1076 rc = RTTestCreateEx("VMMDevTesting", RTTEST_C_USE_ENV | RTTEST_C_NO_TLS | RTTEST_C_XML_DELAY_TOP_TEST,
1077 RTTESTLVL_DEBUG, -1 /*iNativeTestPipe*/, pThisCC->pszTestingXmlOutput, &pThisCC->hTestingTest);
1078 if (RT_FAILURE(rc))
1079 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, "Error creating testing instance");
1080
1081 return VINF_SUCCESS;
1082}
1083
1084#else /* !IN_RING3 */
1085
1086/**
1087 * Does the ring-0/raw-mode initialization of the testing part if enabled.
1088 *
1089 * @returns VBox status code.
1090 * @param pDevIns The VMMDev device instance.
1091 */
1092int vmmdevRZTestingInitialize(PPDMDEVINS pDevIns)
1093{
1094 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
1095 int rc;
1096
1097 if (!pThis->fTestingEnabled)
1098 return VINF_SUCCESS;
1099
1100 if (pThis->fTestingMMIO)
1101 {
1102 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioTesting, vmmdevTestingMmioWrite, vmmdevTestingMmioRead, NULL);
1103 AssertRCReturn(rc, rc);
1104 }
1105
1106 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortTesting, vmmdevTestingIoWrite, vmmdevTestingIoRead, NULL);
1107 AssertRCReturn(rc, rc);
1108
1109 return VINF_SUCCESS;
1110}
1111
1112#endif /* !IN_RING3 */
1113#endif /* !VBOX_WITHOUT_TESTING_FEATURES */
1114
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