VirtualBox

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

Last change on this file since 91548 was 90609, checked in by vboxsync, 3 years ago

VMMDev/Testing: Updated lock thread code with r/w and new config (forgot). bugref:6695

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