VirtualBox

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

Last change on this file since 100626 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • 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 99739 2023-05-11 01:01:08Z 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.virtualbox.org.
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 switch (cb)
651 {
652 case 4:
653 {
654 bool const fReadWriteSection = pThis->TestingLockControl.s.fReadWriteSection;
655 int rc;
656#ifndef IN_RING3
657 if (!pThis->TestingLockControl.s.fMustSucceed)
658 {
659 if (!fReadWriteSection)
660 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_IOM_R3_IOPORT_WRITE);
661 else
662 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_IOM_R3_IOPORT_WRITE);
663 if (rc != VINF_SUCCESS)
664 return rc;
665 }
666 else
667#endif
668 {
669 if (!fReadWriteSection)
670 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_SUCCESS);
671 else
672 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
673 AssertRCReturn(rc, rc);
674 }
675
676 if (offPort == VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE)
677 {
678 if (pThis->TestingLockControl.au32[0] != u32)
679 {
680 pThis->TestingLockControl.au32[0] = u32;
681 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTestingLockEvt);
682 }
683 }
684 else
685 {
686 u32 &= ~VMMDEV_TESTING_LOCKED_HI_MBZ_MASK;
687 if (pThis->TestingLockControl.au32[1] != u32)
688 {
689 pThis->TestingLockControl.au32[1] = u32;
690 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTestingLockEvt);
691 }
692 }
693
694 if (!fReadWriteSection)
695 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
696 else
697 PDMDevHlpCritSectRwLeaveExcl(pDevIns, &pThis->CritSectRw);
698 return VINF_SUCCESS;
699 }
700
701 case 2:
702 case 1:
703 ASSERT_GUEST_FAILED();
704 break;
705
706 default:
707 AssertFailed();
708 return VERR_INTERNAL_ERROR_2;
709 }
710
711 default:
712 break;
713 }
714
715 return VERR_IOM_IOPORT_UNUSED;
716}
717
718
719/**
720 * @callback_method_impl{FNIOMIOPORTNEWIN}
721 */
722static DECLCALLBACK(VBOXSTRICTRC)
723vmmdevTestingIoRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
724{
725 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
726 RT_NOREF_PV(pvUser);
727
728 switch (offPort)
729 {
730 /*
731 * The NOP I/O ports are used for performance measurements.
732 */
733 case VMMDEV_TESTING_IOPORT_NOP - VMMDEV_TESTING_IOPORT_BASE:
734 switch (cb)
735 {
736 case 4:
737 case 2:
738 case 1:
739 break;
740 default:
741 AssertFailed();
742 return VERR_INTERNAL_ERROR_2;
743 }
744 *pu32 = VMMDEV_TESTING_NOP_RET;
745 return VINF_SUCCESS;
746
747 case VMMDEV_TESTING_IOPORT_NOP_R3 - VMMDEV_TESTING_IOPORT_BASE:
748 switch (cb)
749 {
750 case 4:
751 case 2:
752 case 1:
753#ifndef IN_RING3
754 return VINF_IOM_R3_IOPORT_READ;
755#else
756 *pu32 = VMMDEV_TESTING_NOP_RET;
757 return VINF_SUCCESS;
758#endif
759 default:
760 AssertFailed();
761 return VERR_INTERNAL_ERROR_2;
762 }
763
764 /*
765 * The timestamp I/O ports are obviously used for getting a good fix
766 * on the current time (as seen by the host?).
767 *
768 * The high word is latched when reading the low, so reading low + high
769 * gives you a 64-bit timestamp value.
770 */
771 case VMMDEV_TESTING_IOPORT_TS_LOW - VMMDEV_TESTING_IOPORT_BASE:
772 if (cb == 4)
773 {
774 uint64_t NowTS = RTTimeNanoTS();
775 *pu32 = (uint32_t)NowTS;
776 pThis->u32TestingHighTimestamp = (uint32_t)(NowTS >> 32);
777 return VINF_SUCCESS;
778 }
779 break;
780
781 case VMMDEV_TESTING_IOPORT_TS_HIGH - VMMDEV_TESTING_IOPORT_BASE:
782 if (cb == 4)
783 {
784 *pu32 = pThis->u32TestingHighTimestamp;
785 return VINF_SUCCESS;
786 }
787 break;
788
789 /*
790 * Just return the current locking configuration value after first
791 * acquiring the lock of course.
792 */
793 case VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE:
794 case VMMDEV_TESTING_IOPORT_LOCKED_HI - VMMDEV_TESTING_IOPORT_BASE:
795 switch (cb)
796 {
797 case 4:
798 case 2:
799 case 1:
800 {
801 /*
802 * Check configuration and enter the designation critical
803 * section in the specific fashion.
804 */
805 bool const fReadWriteSection = pThis->TestingLockControl.s.fReadWriteSection;
806 bool const fEmtShared = pThis->TestingLockControl.s.fEmtShared;
807 int rc;
808#ifndef IN_RING3
809 if (!pThis->TestingLockControl.s.fMustSucceed)
810 {
811 if (!fReadWriteSection)
812 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_IOM_R3_IOPORT_READ);
813 else if (!fEmtShared)
814 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_IOM_R3_IOPORT_READ);
815 else
816 rc = PDMDevHlpCritSectRwEnterShared(pDevIns, &pThis->CritSectRw, VINF_IOM_R3_IOPORT_READ);
817 if (rc != VINF_SUCCESS)
818 return rc;
819 }
820 else
821#endif
822 {
823 if (!fReadWriteSection)
824 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_SUCCESS);
825 else if (!fEmtShared)
826 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
827 else
828 rc = PDMDevHlpCritSectRwEnterShared(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
829 AssertRCReturn(rc, rc);
830 }
831
832 /*
833 * Grab return value and, if requested, hold for a while.
834 */
835 *pu32 = pThis->TestingLockControl.au32[ offPort
836 - (VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE)];
837 uint64_t cTicks = (uint64_t)pThis->TestingLockControl.s.cKiloTicksEmtHold * _1K;
838 if (cTicks)
839 {
840 uint64_t const uStartTick = ASMReadTSC();
841 do
842 {
843 ASMNopPause();
844 ASMNopPause();
845 } while (ASMReadTSC() - uStartTick < cTicks);
846 }
847
848 /*
849 * Leave.
850 */
851 if (!fReadWriteSection)
852 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
853 else if (!fEmtShared)
854 PDMDevHlpCritSectRwLeaveExcl(pDevIns, &pThis->CritSectRw);
855 else
856 PDMDevHlpCritSectRwLeaveShared(pDevIns, &pThis->CritSectRw);
857 return VINF_SUCCESS;
858 }
859
860 default:
861 AssertFailed();
862 return VERR_INTERNAL_ERROR_2;
863 }
864
865 /*
866 * The command registers is write-only.
867 */
868 case VMMDEV_TESTING_IOPORT_CMD - VMMDEV_TESTING_IOPORT_BASE:
869 break;
870
871 /*
872 * The data register is only readable after a query command, otherwise it
873 * behaves as an undefined port. Return zeros if the guest reads too much.
874 */
875 case VMMDEV_TESTING_IOPORT_DATA - VMMDEV_TESTING_IOPORT_BASE:
876 if (pThis->cbReadableTestingData > 0)
877 {
878 if (pThis->offTestingData < pThis->cbReadableTestingData)
879 {
880 switch (RT_MIN(cb, pThis->cbReadableTestingData - pThis->offTestingData))
881 {
882 case 1:
883 *pu32 = pThis->TestingData.ab[pThis->offTestingData++];
884 break;
885 case 2:
886 *pu32 = pThis->TestingData.ab[pThis->offTestingData]
887 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 1] << 8);
888 pThis->offTestingData += 2;
889 break;
890 case 3:
891 *pu32 = pThis->TestingData.ab[pThis->offTestingData]
892 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 1] << 8)
893 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 2] << 16);
894 pThis->offTestingData += 3;
895 break;
896 case 4:
897 *pu32 = pThis->TestingData.ab[pThis->offTestingData]
898 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 1] << 8)
899 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 2] << 16)
900 | ((uint32_t)pThis->TestingData.ab[pThis->offTestingData + 3] << 24);
901 pThis->offTestingData += 4;
902 break;
903 }
904 }
905 else
906 *pu32 = 0;
907 return VINF_SUCCESS;
908 }
909 break;
910
911 default:
912 break;
913 }
914
915 return VERR_IOM_IOPORT_UNUSED;
916}
917
918#ifdef IN_RING3
919
920/**
921 * @callback_method_impl{FNPDMTHREADDEV}
922 */
923static DECLCALLBACK(int) vmmdevR3TestingLockingThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
924{
925 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
926 PVM pVM = PDMDevHlpGetVM(pDevIns);
927 AssertPtr(pVM);
928
929 while (RT_LIKELY(pThread->enmState == PDMTHREADSTATE_RUNNING))
930 {
931 int rc;
932 uint32_t cNsNextWait = 0;
933 uint32_t const fCfgHi = pThis->TestingLockControl.au32[1];
934 if (fCfgHi & VMMDEV_TESTING_LOCKED_HI_ENABLED)
935 {
936 /*
937 * take lock
938 */
939 if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_TYPE_RW))
940 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_SUCCESS);
941 else if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED))
942 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
943 else
944 rc = PDMDevHlpCritSectRwEnterShared(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
945 AssertLogRelRCReturn(rc, rc);
946
947 /*
948 * Delay releasing lock.
949 */
950 cNsNextWait = pThis->TestingLockControl.s.cUsBetween * RT_NS_1US;
951 if (pThis->TestingLockControl.s.cUsHold)
952 {
953 PDMDevHlpSUPSemEventWaitNsRelIntr(pDevIns, pThis->hTestingLockEvt, pThis->TestingLockControl.s.cUsHold);
954 if (pThis->TestingLockControl.s.fPokeBeforeRelease)
955 VMCC_FOR_EACH_VMCPU_STMT(pVM, RTThreadPoke(pVCpu->hThread));
956 }
957
958 /*
959 * Release lock.
960 */
961 if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_TYPE_RW))
962 rc = PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
963 else if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED))
964 rc = PDMDevHlpCritSectRwLeaveExcl(pDevIns, &pThis->CritSectRw);
965 else
966 rc = PDMDevHlpCritSectRwLeaveShared(pDevIns, &pThis->CritSectRw);
967 AssertLogRelRCReturn(rc, rc);
968 }
969
970 /*
971 * Wait for the next iteration.
972 */
973 if (RT_LIKELY(pThread->enmState == PDMTHREADSTATE_RUNNING))
974 { /* likely */ }
975 else
976 break;
977 if (cNsNextWait > 0)
978 PDMDevHlpSUPSemEventWaitNsRelIntr(pDevIns, pThis->hTestingLockEvt, cNsNextWait);
979 else
980 PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hTestingLockEvt, RT_INDEFINITE_WAIT);
981 }
982
983 return VINF_SUCCESS;
984}
985
986
987/**
988 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
989 */
990static DECLCALLBACK(int) vmmdevR3TestingLockingThreadWakeup(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
991{
992 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
993 RT_NOREF(pThread);
994 return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTestingLockEvt);
995}
996
997
998/**
999 * Initializes the testing part of the VMMDev if enabled.
1000 *
1001 * @param pDevIns The VMMDev device instance.
1002 */
1003void vmmdevR3TestingTerminate(PPDMDEVINS pDevIns)
1004{
1005 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
1006 PVMMDEVCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVMMDEVCC);
1007 if (!pThis->fTestingEnabled)
1008 return;
1009
1010 if (pThisCC->hTestingTest != NIL_RTTEST)
1011 {
1012 RTTestFailed(pThisCC->hTestingTest, "Still open at vmmdev destruction.");
1013 RTTestSummaryAndDestroy(pThisCC->hTestingTest);
1014 pThisCC->hTestingTest = NIL_RTTEST;
1015 }
1016}
1017
1018
1019/**
1020 * Initializes the testing part of the VMMDev if enabled.
1021 *
1022 * @returns VBox status code.
1023 * @param pDevIns The VMMDev device instance.
1024 */
1025int vmmdevR3TestingInitialize(PPDMDEVINS pDevIns)
1026{
1027 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
1028 PVMMDEVCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVMMDEVCC);
1029 int rc;
1030
1031 if (!pThis->fTestingEnabled)
1032 return VINF_SUCCESS;
1033
1034 if (pThis->fTestingMMIO)
1035 {
1036 /*
1037 * Register a chunk of MMIO memory that we'll use for various
1038 * tests interfaces. Optional, needs to be explicitly enabled.
1039 */
1040 rc = PDMDevHlpMmioCreateAndMap(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE,
1041 vmmdevTestingMmioWrite, vmmdevTestingMmioRead,
1042 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
1043 "VMMDev Testing", &pThis->hMmioTesting);
1044 AssertRCReturn(rc, rc);
1045 }
1046
1047 /*
1048 * Register the I/O ports used for testing.
1049 */
1050 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT,
1051 vmmdevTestingIoWrite, vmmdevTestingIoRead, "VMMDev Testing", NULL /*paExtDescs*/,
1052 &pThis->hIoPortTesting);
1053 AssertRCReturn(rc, rc);
1054
1055 /*
1056 * Initialize the read/write critical section used for the locking tests.
1057 */
1058 rc = PDMDevHlpCritSectRwInit(pDevIns, &pThis->CritSectRw, RT_SRC_POS, "VMMLockRW");
1059 AssertRCReturn(rc, rc);
1060
1061 /*
1062 * Create the locking thread.
1063 */
1064 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hTestingLockEvt);
1065 AssertRCReturn(rc, rc);
1066 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->pTestingLockThread, NULL /*pvUser*/, vmmdevR3TestingLockingThread,
1067 vmmdevR3TestingLockingThreadWakeup, 0 /*cbStack*/, RTTHREADTYPE_IO, "VMMLockT");
1068 AssertRCReturn(rc, rc);
1069
1070 /*
1071 * Open the XML output file(/pipe/whatever) if specfied.
1072 */
1073 rc = RTTestCreateEx("VMMDevTesting", RTTEST_C_USE_ENV | RTTEST_C_NO_TLS | RTTEST_C_XML_DELAY_TOP_TEST,
1074 RTTESTLVL_DEBUG, -1 /*iNativeTestPipe*/, pThisCC->pszTestingXmlOutput, &pThisCC->hTestingTest);
1075 if (RT_FAILURE(rc))
1076 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, "Error creating testing instance");
1077
1078 return VINF_SUCCESS;
1079}
1080
1081#else /* !IN_RING3 */
1082
1083/**
1084 * Does the ring-0/raw-mode initialization of the testing part if enabled.
1085 *
1086 * @returns VBox status code.
1087 * @param pDevIns The VMMDev device instance.
1088 */
1089int vmmdevRZTestingInitialize(PPDMDEVINS pDevIns)
1090{
1091 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
1092 int rc;
1093
1094 if (!pThis->fTestingEnabled)
1095 return VINF_SUCCESS;
1096
1097 if (pThis->fTestingMMIO)
1098 {
1099 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioTesting, vmmdevTestingMmioWrite, vmmdevTestingMmioRead, NULL);
1100 AssertRCReturn(rc, rc);
1101 }
1102
1103 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortTesting, vmmdevTestingIoWrite, vmmdevTestingIoRead, NULL);
1104 AssertRCReturn(rc, rc);
1105
1106 return VINF_SUCCESS;
1107}
1108
1109#endif /* !IN_RING3 */
1110#endif /* !VBOX_WITHOUT_TESTING_FEATURES */
1111
Note: See TracBrowser for help on using the repository browser.

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