VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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