VirtualBox

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

Last change on this file since 91952 was 91944, checked in by vboxsync, 3 years ago

VMM,Devices: Eliminate direct calls to DBGFR3* and use the device helper callbacks, bugref:10074

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.5 KB
Line 
1/* $Id: VMMDevTesting.cpp 91944 2021-10-21 13:02:36Z 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-2020 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 RT_ZERO(pThis->TestingData);
360 return VINF_SUCCESS;
361 }
362 break;
363
364 /*
365 * The data port. Used of providing data for a command.
366 */
367 case VMMDEV_TESTING_IOPORT_DATA - VMMDEV_TESTING_IOPORT_BASE:
368 {
369 uint32_t uCmd = pThis->u32TestingCmd;
370 uint32_t off = pThis->offTestingData;
371 switch (uCmd)
372 {
373 case VMMDEV_TESTING_CMD_INIT:
374 case VMMDEV_TESTING_CMD_SUB_NEW:
375 case VMMDEV_TESTING_CMD_FAILED:
376 case VMMDEV_TESTING_CMD_SKIPPED:
377 case VMMDEV_TESTING_CMD_PRINT:
378 if ( off < sizeof(pThis->TestingData.String.sz) - 1
379 && cb == 1)
380 {
381 if (u32)
382 {
383 pThis->TestingData.String.sz[off] = u32;
384 pThis->offTestingData = off + 1;
385 }
386 else
387 {
388#ifdef IN_RING3
389 pThis->TestingData.String.sz[off] = '\0';
390 switch (uCmd)
391 {
392 case VMMDEV_TESTING_CMD_INIT:
393 VMMDEV_TESTING_OUTPUT(("testing: INIT '%s'\n", pThis->TestingData.String.sz));
394 if (pThisCC->hTestingTest != NIL_RTTEST)
395 {
396 RTTestChangeName(pThisCC->hTestingTest, pThis->TestingData.String.sz);
397 RTTestBanner(pThisCC->hTestingTest);
398 }
399 break;
400 case VMMDEV_TESTING_CMD_SUB_NEW:
401 VMMDEV_TESTING_OUTPUT(("testing: SUB_NEW '%s'\n", pThis->TestingData.String.sz));
402 if (pThisCC->hTestingTest != NIL_RTTEST)
403 RTTestSub(pThisCC->hTestingTest, pThis->TestingData.String.sz);
404 break;
405 case VMMDEV_TESTING_CMD_FAILED:
406 if (pThisCC->hTestingTest != NIL_RTTEST)
407 RTTestFailed(pThisCC->hTestingTest, "%s", pThis->TestingData.String.sz);
408 VMMDEV_TESTING_OUTPUT(("testing: FAILED '%s'\n", pThis->TestingData.String.sz));
409 break;
410 case VMMDEV_TESTING_CMD_SKIPPED:
411 if (pThisCC->hTestingTest != NIL_RTTEST)
412 {
413 if (off)
414 RTTestSkipped(pThisCC->hTestingTest, "%s", pThis->TestingData.String.sz);
415 else
416 RTTestSkipped(pThisCC->hTestingTest, NULL);
417 }
418 VMMDEV_TESTING_OUTPUT(("testing: SKIPPED '%s'\n", pThis->TestingData.String.sz));
419 break;
420 case VMMDEV_TESTING_CMD_PRINT:
421 if (pThisCC->hTestingTest != NIL_RTTEST && off)
422 RTTestPrintf(pThisCC->hTestingTest, RTTESTLVL_ALWAYS, "%s", pThis->TestingData.String.sz);
423 VMMDEV_TESTING_OUTPUT(("testing: '%s'\n", pThis->TestingData.String.sz));
424 break;
425 }
426#else
427 return VINF_IOM_R3_IOPORT_WRITE;
428#endif
429 }
430 return VINF_SUCCESS;
431 }
432 break;
433
434 case VMMDEV_TESTING_CMD_TERM:
435 case VMMDEV_TESTING_CMD_SUB_DONE:
436 if (cb == 2)
437 {
438 if (off == 0)
439 {
440 pThis->TestingData.Error.c = u32;
441 pThis->offTestingData = 2;
442 break;
443 }
444 if (off == 2)
445 {
446 u32 <<= 16;
447 u32 |= pThis->TestingData.Error.c & UINT16_MAX;
448 cb = 4;
449 off = 0;
450 }
451 else
452 break;
453 }
454
455 if ( off == 0
456 && cb == 4)
457 {
458#ifdef IN_RING3
459 pThis->TestingData.Error.c = u32;
460 if (uCmd == VMMDEV_TESTING_CMD_TERM)
461 {
462 if (pThisCC->hTestingTest != NIL_RTTEST)
463 {
464 while (RTTestErrorCount(pThisCC->hTestingTest) < u32)
465 RTTestErrorInc(pThisCC->hTestingTest); /* A bit stupid, but does the trick. */
466 RTTestSubDone(pThisCC->hTestingTest);
467 RTTestSummaryAndDestroy(pThisCC->hTestingTest);
468 pThisCC->hTestingTest = NIL_RTTEST;
469 }
470 VMMDEV_TESTING_OUTPUT(("testing: TERM - %u errors\n", u32));
471 }
472 else
473 {
474 if (pThisCC->hTestingTest != NIL_RTTEST)
475 {
476 while (RTTestSubErrorCount(pThisCC->hTestingTest) < u32)
477 RTTestErrorInc(pThisCC->hTestingTest); /* A bit stupid, but does the trick. */
478 RTTestSubDone(pThisCC->hTestingTest);
479 }
480 VMMDEV_TESTING_OUTPUT(("testing: SUB_DONE - %u errors\n", u32));
481 }
482 return VINF_SUCCESS;
483#else
484 return VINF_IOM_R3_IOPORT_WRITE;
485#endif
486 }
487 break;
488
489 case VMMDEV_TESTING_CMD_VALUE:
490 if (cb == 4)
491 {
492 if (off == 0)
493 pThis->TestingData.Value.u64Value.s.Lo = u32;
494 else if (off == 4)
495 pThis->TestingData.Value.u64Value.s.Hi = u32;
496 else if (off == 8)
497 pThis->TestingData.Value.u32Unit = u32;
498 else
499 break;
500 pThis->offTestingData = off + 4;
501 return VINF_SUCCESS;
502 }
503 if (cb == 2)
504 {
505 if (off == 0)
506 pThis->TestingData.Value.u64Value.Words.w0 = (uint16_t)u32;
507 else if (off == 2)
508 pThis->TestingData.Value.u64Value.Words.w1 = (uint16_t)u32;
509 else if (off == 4)
510 pThis->TestingData.Value.u64Value.Words.w2 = (uint16_t)u32;
511 else if (off == 6)
512 pThis->TestingData.Value.u64Value.Words.w3 = (uint16_t)u32;
513 else if (off == 8)
514 pThis->TestingData.Value.u32Unit = (uint16_t)u32;
515 else if (off == 10)
516 pThis->TestingData.Value.u32Unit |= u32 << 16;
517 else
518 break;
519 pThis->offTestingData = off + 2;
520 return VINF_SUCCESS;
521 }
522
523 if ( off >= 12
524 && cb == 1
525 && off - 12 < sizeof(pThis->TestingData.Value.szName) - 1)
526 {
527 if (u32)
528 {
529 pThis->TestingData.Value.szName[off - 12] = u32;
530 pThis->offTestingData = off + 1;
531 }
532 else
533 {
534#ifdef IN_RING3
535 pThis->TestingData.Value.szName[off - 12] = '\0';
536
537 RTTESTUNIT enmUnit = (RTTESTUNIT)pThis->TestingData.Value.u32Unit;
538 if (enmUnit <= RTTESTUNIT_INVALID || enmUnit >= RTTESTUNIT_END)
539 {
540 VMMDEV_TESTING_OUTPUT(("Invalid log value unit %#x\n", pThis->TestingData.Value.u32Unit));
541 enmUnit = RTTESTUNIT_NONE;
542 }
543 if (pThisCC->hTestingTest != NIL_RTTEST)
544 RTTestValue(pThisCC->hTestingTest, pThis->TestingData.Value.szName,
545 pThis->TestingData.Value.u64Value.u, enmUnit);
546
547 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %'9llu (%#llx) [%u]\n",
548 pThis->TestingData.Value.szName,
549 off - 12 > 48 ? 0 : 48 - (off - 12), "",
550 pThis->TestingData.Value.u64Value.u, pThis->TestingData.Value.u64Value.u,
551 pThis->TestingData.Value.u32Unit));
552#else
553 return VINF_IOM_R3_IOPORT_WRITE;
554#endif
555 }
556 return VINF_SUCCESS;
557 }
558 break;
559
560
561 /*
562 * RTTestValue with the output from DBGFR3RegNmQuery.
563 */
564 case VMMDEV_TESTING_CMD_VALUE_REG:
565 {
566 if ( off < sizeof(pThis->TestingData.String.sz) - 1
567 && cb == 1)
568 {
569 pThis->TestingData.String.sz[off] = u32;
570 if (u32)
571 pThis->offTestingData = off + 1;
572 else
573#ifdef IN_RING3
574 vmmdevTestingCmdExec_ValueReg(pDevIns, pThis);
575#else
576 return VINF_IOM_R3_IOPORT_WRITE;
577#endif
578 return VINF_SUCCESS;
579 }
580 break;
581 }
582
583 default:
584 break;
585 }
586 Log(("VMMDEV_TESTING_IOPORT_CMD: bad access; cmd=%#x off=%#x cb=%#x u32=%#x\n", uCmd, off, cb, u32));
587 return VINF_SUCCESS;
588 }
589
590 /*
591 * Configure the locking contention test.
592 */
593 case VMMDEV_TESTING_IOPORT_LOCKED_HI - VMMDEV_TESTING_IOPORT_BASE:
594 case VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE:
595 switch (cb)
596 {
597 case 4:
598 {
599 bool const fReadWriteSection = pThis->TestingLockControl.s.fReadWriteSection;
600 int rc;
601#ifndef IN_RING3
602 if (!pThis->TestingLockControl.s.fMustSucceed)
603 {
604 if (!fReadWriteSection)
605 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_IOM_R3_IOPORT_WRITE);
606 else
607 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_IOM_R3_IOPORT_WRITE);
608 if (rc != VINF_SUCCESS)
609 return rc;
610 }
611 else
612#endif
613 {
614 if (!fReadWriteSection)
615 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_SUCCESS);
616 else
617 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
618 AssertRCReturn(rc, rc);
619 }
620
621 if (offPort == VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE)
622 {
623 if (pThis->TestingLockControl.au32[0] != u32)
624 {
625 pThis->TestingLockControl.au32[0] = u32;
626 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTestingLockEvt);
627 }
628 }
629 else
630 {
631 u32 &= ~VMMDEV_TESTING_LOCKED_HI_MBZ_MASK;
632 if (pThis->TestingLockControl.au32[1] != u32)
633 {
634 pThis->TestingLockControl.au32[1] = u32;
635 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTestingLockEvt);
636 }
637 }
638
639 if (!fReadWriteSection)
640 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
641 else
642 PDMDevHlpCritSectRwLeaveExcl(pDevIns, &pThis->CritSectRw);
643 return VINF_SUCCESS;
644 }
645
646 case 2:
647 case 1:
648 ASSERT_GUEST_FAILED();
649 break;
650
651 default:
652 AssertFailed();
653 return VERR_INTERNAL_ERROR_2;
654 }
655
656 default:
657 break;
658 }
659
660 return VERR_IOM_IOPORT_UNUSED;
661}
662
663
664/**
665 * @callback_method_impl{FNIOMIOPORTNEWIN}
666 */
667static DECLCALLBACK(VBOXSTRICTRC)
668vmmdevTestingIoRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
669{
670 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
671 RT_NOREF_PV(pvUser);
672
673 switch (offPort)
674 {
675 /*
676 * The NOP I/O ports are used for performance measurements.
677 */
678 case VMMDEV_TESTING_IOPORT_NOP - VMMDEV_TESTING_IOPORT_BASE:
679 switch (cb)
680 {
681 case 4:
682 case 2:
683 case 1:
684 break;
685 default:
686 AssertFailed();
687 return VERR_INTERNAL_ERROR_2;
688 }
689 *pu32 = VMMDEV_TESTING_NOP_RET;
690 return VINF_SUCCESS;
691
692 case VMMDEV_TESTING_IOPORT_NOP_R3 - VMMDEV_TESTING_IOPORT_BASE:
693 switch (cb)
694 {
695 case 4:
696 case 2:
697 case 1:
698#ifndef IN_RING3
699 return VINF_IOM_R3_IOPORT_READ;
700#else
701 *pu32 = VMMDEV_TESTING_NOP_RET;
702 return VINF_SUCCESS;
703#endif
704 default:
705 AssertFailed();
706 return VERR_INTERNAL_ERROR_2;
707 }
708
709 /*
710 * The timestamp I/O ports are obviously used for getting a good fix
711 * on the current time (as seen by the host?).
712 *
713 * The high word is latched when reading the low, so reading low + high
714 * gives you a 64-bit timestamp value.
715 */
716 case VMMDEV_TESTING_IOPORT_TS_LOW - VMMDEV_TESTING_IOPORT_BASE:
717 if (cb == 4)
718 {
719 uint64_t NowTS = RTTimeNanoTS();
720 *pu32 = (uint32_t)NowTS;
721 pThis->u32TestingHighTimestamp = (uint32_t)(NowTS >> 32);
722 return VINF_SUCCESS;
723 }
724 break;
725
726 case VMMDEV_TESTING_IOPORT_TS_HIGH - VMMDEV_TESTING_IOPORT_BASE:
727 if (cb == 4)
728 {
729 *pu32 = pThis->u32TestingHighTimestamp;
730 return VINF_SUCCESS;
731 }
732 break;
733
734 /*
735 * Just return the current locking configuration value after first
736 * acquiring the lock of course.
737 */
738 case VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE:
739 case VMMDEV_TESTING_IOPORT_LOCKED_HI - VMMDEV_TESTING_IOPORT_BASE:
740 switch (cb)
741 {
742 case 4:
743 case 2:
744 case 1:
745 {
746 /*
747 * Check configuration and enter the designation critical
748 * section in the specific fashion.
749 */
750 bool const fReadWriteSection = pThis->TestingLockControl.s.fReadWriteSection;
751 bool const fEmtShared = pThis->TestingLockControl.s.fEmtShared;
752 int rc;
753#ifndef IN_RING3
754 if (!pThis->TestingLockControl.s.fMustSucceed)
755 {
756 if (!fReadWriteSection)
757 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_IOM_R3_IOPORT_READ);
758 else if (!fEmtShared)
759 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_IOM_R3_IOPORT_READ);
760 else
761 rc = PDMDevHlpCritSectRwEnterShared(pDevIns, &pThis->CritSectRw, VINF_IOM_R3_IOPORT_READ);
762 if (rc != VINF_SUCCESS)
763 return rc;
764 }
765 else
766#endif
767 {
768 if (!fReadWriteSection)
769 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_SUCCESS);
770 else if (!fEmtShared)
771 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
772 else
773 rc = PDMDevHlpCritSectRwEnterShared(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
774 AssertRCReturn(rc, rc);
775 }
776
777 /*
778 * Grab return value and, if requested, hold for a while.
779 */
780 *pu32 = pThis->TestingLockControl.au32[ offPort
781 - (VMMDEV_TESTING_IOPORT_LOCKED_LO - VMMDEV_TESTING_IOPORT_BASE)];
782 uint64_t cTicks = (uint64_t)pThis->TestingLockControl.s.cKiloTicksEmtHold * _1K;
783 if (cTicks)
784 {
785 uint64_t const uStartTick = ASMReadTSC();
786 do
787 {
788 ASMNopPause();
789 ASMNopPause();
790 } while (ASMReadTSC() - uStartTick < cTicks);
791 }
792
793 /*
794 * Leave.
795 */
796 if (!fReadWriteSection)
797 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
798 else if (!fEmtShared)
799 PDMDevHlpCritSectRwLeaveExcl(pDevIns, &pThis->CritSectRw);
800 else
801 PDMDevHlpCritSectRwLeaveShared(pDevIns, &pThis->CritSectRw);
802 return VINF_SUCCESS;
803 }
804
805 default:
806 AssertFailed();
807 return VERR_INTERNAL_ERROR_2;
808 }
809
810 /*
811 * The command and data registers are write-only.
812 */
813 case VMMDEV_TESTING_IOPORT_CMD - VMMDEV_TESTING_IOPORT_BASE:
814 case VMMDEV_TESTING_IOPORT_DATA - VMMDEV_TESTING_IOPORT_BASE:
815 break;
816
817 default:
818 break;
819 }
820
821 return VERR_IOM_IOPORT_UNUSED;
822}
823
824#ifdef IN_RING3
825
826/**
827 * @callback_method_impl{FNPDMTHREADDEV}
828 */
829static DECLCALLBACK(int) vmmdevR3TestingLockingThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
830{
831 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
832 PVM pVM = PDMDevHlpGetVM(pDevIns);
833 AssertPtr(pVM);
834
835 while (RT_LIKELY(pThread->enmState == PDMTHREADSTATE_RUNNING))
836 {
837 int rc;
838 uint32_t cNsNextWait = 0;
839 uint32_t const fCfgHi = pThis->TestingLockControl.au32[1];
840 if (fCfgHi & VMMDEV_TESTING_LOCKED_HI_ENABLED)
841 {
842 /*
843 * take lock
844 */
845 if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_TYPE_RW))
846 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VINF_SUCCESS);
847 else if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED))
848 rc = PDMDevHlpCritSectRwEnterExcl(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
849 else
850 rc = PDMDevHlpCritSectRwEnterShared(pDevIns, &pThis->CritSectRw, VINF_SUCCESS);
851 AssertLogRelRCReturn(rc, rc);
852
853 /*
854 * Delay releasing lock.
855 */
856 cNsNextWait = pThis->TestingLockControl.s.cUsBetween * RT_NS_1US;
857 if (pThis->TestingLockControl.s.cUsHold)
858 {
859 PDMDevHlpSUPSemEventWaitNsRelIntr(pDevIns, pThis->hTestingLockEvt, pThis->TestingLockControl.s.cUsHold);
860 if (pThis->TestingLockControl.s.fPokeBeforeRelease)
861 VMCC_FOR_EACH_VMCPU_STMT(pVM, RTThreadPoke(pVCpu->hThread));
862 }
863
864 /*
865 * Release lock.
866 */
867 if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_TYPE_RW))
868 rc = PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
869 else if (!(fCfgHi & VMMDEV_TESTING_LOCKED_HI_THREAD_SHARED))
870 rc = PDMDevHlpCritSectRwLeaveExcl(pDevIns, &pThis->CritSectRw);
871 else
872 rc = PDMDevHlpCritSectRwLeaveShared(pDevIns, &pThis->CritSectRw);
873 AssertLogRelRCReturn(rc, rc);
874 }
875
876 /*
877 * Wait for the next iteration.
878 */
879 if (RT_LIKELY(pThread->enmState == PDMTHREADSTATE_RUNNING))
880 { /* likely */ }
881 else
882 break;
883 if (cNsNextWait > 0)
884 PDMDevHlpSUPSemEventWaitNsRelIntr(pDevIns, pThis->hTestingLockEvt, cNsNextWait);
885 else
886 PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hTestingLockEvt, RT_INDEFINITE_WAIT);
887 }
888
889 return VINF_SUCCESS;
890}
891
892
893/**
894 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
895 */
896static DECLCALLBACK(int) vmmdevR3TestingLockingThreadWakeup(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
897{
898 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
899 RT_NOREF(pThread);
900 return PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hTestingLockEvt);
901}
902
903
904/**
905 * Initializes the testing part of the VMMDev if enabled.
906 *
907 * @returns VBox status code.
908 * @param pDevIns The VMMDev device instance.
909 */
910void vmmdevR3TestingTerminate(PPDMDEVINS pDevIns)
911{
912 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
913 PVMMDEVCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVMMDEVCC);
914 if (!pThis->fTestingEnabled)
915 return;
916
917 if (pThisCC->hTestingTest != NIL_RTTEST)
918 {
919 RTTestFailed(pThisCC->hTestingTest, "Still open at vmmdev destruction.");
920 RTTestSummaryAndDestroy(pThisCC->hTestingTest);
921 pThisCC->hTestingTest = NIL_RTTEST;
922 }
923}
924
925
926/**
927 * Initializes the testing part of the VMMDev if enabled.
928 *
929 * @returns VBox status code.
930 * @param pDevIns The VMMDev device instance.
931 */
932int vmmdevR3TestingInitialize(PPDMDEVINS pDevIns)
933{
934 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
935 PVMMDEVCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVMMDEVCC);
936 int rc;
937
938 if (!pThis->fTestingEnabled)
939 return VINF_SUCCESS;
940
941 if (pThis->fTestingMMIO)
942 {
943 /*
944 * Register a chunk of MMIO memory that we'll use for various
945 * tests interfaces. Optional, needs to be explicitly enabled.
946 */
947 rc = PDMDevHlpMmioCreateAndMap(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE,
948 vmmdevTestingMmioWrite, vmmdevTestingMmioRead,
949 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
950 "VMMDev Testing", &pThis->hMmioTesting);
951 AssertRCReturn(rc, rc);
952 }
953
954 /*
955 * Register the I/O ports used for testing.
956 */
957 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT,
958 vmmdevTestingIoWrite, vmmdevTestingIoRead, "VMMDev Testing", NULL /*paExtDescs*/,
959 &pThis->hIoPortTesting);
960 AssertRCReturn(rc, rc);
961
962 /*
963 * Initialize the read/write critical section used for the locking tests.
964 */
965 rc = PDMDevHlpCritSectRwInit(pDevIns, &pThis->CritSectRw, RT_SRC_POS, "VMMLockRW");
966 AssertRCReturn(rc, rc);
967
968 /*
969 * Create the locking thread.
970 */
971 rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hTestingLockEvt);
972 AssertRCReturn(rc, rc);
973 rc = PDMDevHlpThreadCreate(pDevIns, &pThisCC->pTestingLockThread, NULL /*pvUser*/, vmmdevR3TestingLockingThread,
974 vmmdevR3TestingLockingThreadWakeup, 0 /*cbStack*/, RTTHREADTYPE_IO, "VMMLockT");
975 AssertRCReturn(rc, rc);
976
977 /*
978 * Open the XML output file(/pipe/whatever) if specfied.
979 */
980 rc = RTTestCreateEx("VMMDevTesting", RTTEST_C_USE_ENV | RTTEST_C_NO_TLS | RTTEST_C_XML_DELAY_TOP_TEST,
981 RTTESTLVL_INVALID, -1 /*iNativeTestPipe*/, pThisCC->pszTestingXmlOutput, &pThisCC->hTestingTest);
982 if (RT_FAILURE(rc))
983 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, "Error creating testing instance");
984
985 return VINF_SUCCESS;
986}
987
988#else /* !IN_RING3 */
989
990/**
991 * Does the ring-0/raw-mode initialization of the testing part if enabled.
992 *
993 * @returns VBox status code.
994 * @param pDevIns The VMMDev device instance.
995 */
996int vmmdevRZTestingInitialize(PPDMDEVINS pDevIns)
997{
998 PVMMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PVMMDEV);
999 int rc;
1000
1001 if (!pThis->fTestingEnabled)
1002 return VINF_SUCCESS;
1003
1004 if (pThis->fTestingMMIO)
1005 {
1006 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioTesting, vmmdevTestingMmioWrite, vmmdevTestingMmioRead, NULL);
1007 AssertRCReturn(rc, rc);
1008 }
1009
1010 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortTesting, vmmdevTestingIoWrite, vmmdevTestingIoRead, NULL);
1011 AssertRCReturn(rc, rc);
1012
1013 return VINF_SUCCESS;
1014}
1015
1016#endif /* !IN_RING3 */
1017#endif /* !VBOX_WITHOUT_TESTING_FEATURES */
1018
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