VirtualBox

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

Last change on this file since 93625 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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