VirtualBox

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

Last change on this file since 103914 was 103262, checked in by vboxsync, 8 months ago

IPRT,VMMDev,Bs3Kit: Added support for sub-sub-tests to better deal with bs3-cpu-generated-1 and others with too many sub-tests for the test manager.

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