VirtualBox

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

Last change on this file was 107215, checked in by vboxsync, 7 weeks ago

VMM/PDM,VMMDevTesting: Don't include vm.h in VMMDevTesting, replacing it by a new devhlp function for poking the EMTs. jiraref:VBP-1466

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