VirtualBox

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

Last change on this file since 47918 was 46919, checked in by vboxsync, 11 years ago

VMMDev: report failure if the test wasn't closed correctly by the guest.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.5 KB
Line 
1/* $Id: VMMDevTesting.cpp 46919 2013-07-02 18:46:43Z 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-2013 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*******************************************************************************
22* Header Files *
23*******************************************************************************/
24#define LOG_GROUP LOG_GROUP_DEV_VMM
25#include <VBox/VMMDev.h>
26#include <VBox/vmm/vmapi.h>
27#include <VBox/log.h>
28#include <VBox/err.h>
29
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32#include <iprt/string.h>
33#include <iprt/time.h>
34#include <iprt/test.h>
35
36#include "VMMDevState.h"
37#include "VMMDevTesting.h"
38
39
40#ifndef VBOX_WITHOUT_TESTING_FEATURES
41
42#define VMMDEV_TESTING_OUTPUT(a) \
43 do \
44 { \
45 LogAlways(a);\
46 LogRel(a);\
47 } while (0)
48
49/**
50 * @callback_method_impl{FNIOMMMIOWRITE}
51 */
52PDMBOTHCBDECL(int) vmmdevTestingMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
53{
54 switch (GCPhysAddr)
55 {
56 case VMMDEV_TESTING_MMIO_NOP:
57 switch (cb)
58 {
59 case 8:
60 case 4:
61 case 2:
62 case 1:
63 break;
64 default:
65 AssertFailed();
66 return VERR_INTERNAL_ERROR_5;
67 }
68 return VINF_SUCCESS;
69
70 default:
71 break;
72 }
73 return VINF_SUCCESS;
74}
75
76
77/**
78 * @callback_method_impl{FNIOMMMIOREAD}
79 */
80PDMBOTHCBDECL(int) vmmdevTestingMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
81{
82 switch (GCPhysAddr)
83 {
84 case VMMDEV_TESTING_MMIO_NOP:
85 switch (cb)
86 {
87 case 8:
88 *(uint64_t *)pv = VMMDEV_TESTING_NOP_RET | ((uint64_t)VMMDEV_TESTING_NOP_RET << 32);
89 break;
90 case 4:
91 *(uint32_t *)pv = VMMDEV_TESTING_NOP_RET;
92 break;
93 case 2:
94 *(uint16_t *)pv = (uint16_t)VMMDEV_TESTING_NOP_RET;
95 break;
96 case 1:
97 *(uint8_t *)pv = (uint8_t)VMMDEV_TESTING_NOP_RET;
98 break;
99 default:
100 AssertFailed();
101 return VERR_INTERNAL_ERROR_5;
102 }
103 return VINF_SUCCESS;
104
105
106 default:
107 break;
108 }
109
110 return VINF_IOM_MMIO_UNUSED_FF;
111}
112
113#ifdef IN_RING3
114
115/**
116 * Executes the VMMDEV_TESTING_CMD_VALUE_REG command when the data is ready.
117 *
118 * @param pDevIns The PDM device instance.
119 * @param pThis The instance VMMDev data.
120 */
121static void vmmdevTestingCmdExec_ValueReg(PPDMDEVINS pDevIns, VMMDevState *pThis)
122{
123 char *pszRegNm = strchr(pThis->TestingData.String.sz, ':');
124 if (pszRegNm)
125 {
126 *pszRegNm++ = '\0';
127 pszRegNm = RTStrStrip(pszRegNm);
128 }
129 char *pszValueNm = RTStrStrip(pThis->TestingData.String.sz);
130 size_t const cchValueNm = strlen(pszValueNm);
131 if (cchValueNm && pszRegNm && *pszRegNm)
132 {
133 PUVM pUVM = PDMDevHlpGetUVM(pDevIns);
134 PVM pVM = PDMDevHlpGetVM(pDevIns);
135 VMCPUID idCpu = VMMGetCpuId(pVM);
136 uint64_t u64Value;
137 int rc2 = DBGFR3RegNmQueryU64(pUVM, idCpu, pszRegNm, &u64Value);
138 if (RT_SUCCESS(rc2))
139 {
140 const char *pszWarn = rc2 == VINF_DBGF_TRUNCATED_REGISTER ? " truncated" : "";
141#if 1 /*!RTTestValue format*/
142 char szFormat[128], szValue[128];
143 RTStrPrintf(szFormat, sizeof(szFormat), "%%VR{%s}", pszRegNm);
144 rc2 = DBGFR3RegPrintf(pUVM, idCpu, szValue, sizeof(szValue), szFormat);
145 if (RT_SUCCESS(rc2))
146 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %16s {reg=%s}%s\n",
147 pszValueNm,
148 (ssize_t)cchValueNm - 12 > 48 ? 0 : 48 - ((ssize_t)cchValueNm - 12), "",
149 szValue, pszRegNm, pszWarn));
150 else
151#endif
152 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %'9llu (%#llx) [0] {reg=%s}%s\n",
153 pszValueNm,
154 (ssize_t)cchValueNm - 12 > 48 ? 0 : 48 - ((ssize_t)cchValueNm - 12), "",
155 u64Value, u64Value, pszRegNm, pszWarn));
156 }
157 else
158 VMMDEV_TESTING_OUTPUT(("testing: error querying register '%s' for value '%s': %Rrc\n",
159 pszRegNm, pszValueNm, rc2));
160 }
161 else
162 VMMDEV_TESTING_OUTPUT(("testing: malformed register value '%s'/'%s'\n", pszValueNm, pszRegNm));
163}
164
165#endif /* IN_RING3 */
166
167/**
168 * @callback_method_impl{FNIOMIOPORTOUT}
169 */
170PDMBOTHCBDECL(int) vmmdevTestingIoWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
171{
172 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
173
174 switch (Port)
175 {
176 /*
177 * The NOP I/O ports are used for performance measurements.
178 */
179 case VMMDEV_TESTING_IOPORT_NOP:
180 switch (cb)
181 {
182 case 4:
183 case 2:
184 case 1:
185 break;
186 default:
187 AssertFailed();
188 return VERR_INTERNAL_ERROR_2;
189 }
190 return VINF_SUCCESS;
191
192 /* The timestamp I/O ports are read-only. */
193 case VMMDEV_TESTING_IOPORT_TS_LOW:
194 case VMMDEV_TESTING_IOPORT_TS_HIGH:
195 break;
196
197 /*
198 * The command port (DWORD write only).
199 */
200 case VMMDEV_TESTING_IOPORT_CMD:
201 if (cb == 4)
202 {
203 pThis->u32TestingCmd = u32;
204 pThis->offTestingData = 0;
205 RT_ZERO(pThis->TestingData);
206 return VINF_SUCCESS;
207 }
208 break;
209
210 /*
211 * The data port. Used of providing data for a command.
212 */
213 case VMMDEV_TESTING_IOPORT_DATA:
214 {
215 uint32_t uCmd = pThis->u32TestingCmd;
216 uint32_t off = pThis->offTestingData;
217 switch (uCmd)
218 {
219 case VMMDEV_TESTING_CMD_INIT:
220 case VMMDEV_TESTING_CMD_SUB_NEW:
221 case VMMDEV_TESTING_CMD_FAILED:
222 case VMMDEV_TESTING_CMD_SKIPPED:
223 if ( off < sizeof(pThis->TestingData.String.sz) - 1
224 && cb == 1)
225 {
226 if (u32)
227 {
228 pThis->TestingData.String.sz[off] = u32;
229 pThis->offTestingData = off + 1;
230 }
231 else
232 {
233#ifdef IN_RING3
234 pThis->TestingData.String.sz[off] = '\0';
235 switch (uCmd)
236 {
237 case VMMDEV_TESTING_CMD_INIT:
238 VMMDEV_TESTING_OUTPUT(("testing: INIT '%s'\n", pThis->TestingData.String.sz));
239 if (pThis->hTestingTest != NIL_RTTEST)
240 {
241 RTTestChangeName(pThis->hTestingTest, pThis->TestingData.String.sz);
242 RTTestBanner(pThis->hTestingTest);
243 }
244 break;
245 case VMMDEV_TESTING_CMD_SUB_NEW:
246 VMMDEV_TESTING_OUTPUT(("testing: SUB_NEW '%s'\n", pThis->TestingData.String.sz));
247 if (pThis->hTestingTest != NIL_RTTEST)
248 RTTestSub(pThis->hTestingTest, pThis->TestingData.String.sz);
249 break;
250 case VMMDEV_TESTING_CMD_FAILED:
251 if (pThis->hTestingTest != NIL_RTTEST)
252 RTTestFailed(pThis->hTestingTest, "%s", pThis->TestingData.String.sz);
253 VMMDEV_TESTING_OUTPUT(("testing: FAILED '%s'\n", pThis->TestingData.String.sz));
254 break;
255 case VMMDEV_TESTING_CMD_SKIPPED:
256 if (pThis->hTestingTest != NIL_RTTEST)
257 {
258 if (off)
259 RTTestSkipped(pThis->hTestingTest, "%s", pThis->TestingData.String.sz);
260 else
261 RTTestSkipped(pThis->hTestingTest, NULL);
262 }
263 VMMDEV_TESTING_OUTPUT(("testing: SKIPPED '%s'\n", pThis->TestingData.String.sz));
264 break;
265 }
266#else
267 return VINF_IOM_R3_IOPORT_WRITE;
268#endif
269 }
270 return VINF_SUCCESS;
271 }
272 break;
273
274 case VMMDEV_TESTING_CMD_TERM:
275 case VMMDEV_TESTING_CMD_SUB_DONE:
276 if ( off == 0
277 && cb == 4)
278 {
279#ifdef IN_RING3
280 pThis->TestingData.Error.c = u32;
281 if (uCmd == VMMDEV_TESTING_CMD_TERM)
282 {
283 if (pThis->hTestingTest != NIL_RTTEST)
284 {
285 while (RTTestErrorCount(pThis->hTestingTest) < u32)
286 RTTestErrorInc(pThis->hTestingTest); /* A bit stupid, but does the trick. */
287 RTTestSubDone(pThis->hTestingTest);
288 RTTestSummaryAndDestroy(pThis->hTestingTest);
289 pThis->hTestingTest = NIL_RTTEST;
290 }
291 VMMDEV_TESTING_OUTPUT(("testing: TERM - %u errors\n", u32));
292 }
293 else
294 {
295 if (pThis->hTestingTest != NIL_RTTEST)
296 {
297 while (RTTestSubErrorCount(pThis->hTestingTest) < u32)
298 RTTestErrorInc(pThis->hTestingTest); /* A bit stupid, but does the trick. */
299 RTTestSubDone(pThis->hTestingTest);
300 }
301 VMMDEV_TESTING_OUTPUT(("testing: SUB_DONE - %u errors\n", u32));
302 }
303 return VINF_SUCCESS;
304#else
305 return VINF_IOM_R3_IOPORT_WRITE;
306#endif
307 }
308 break;
309
310 case VMMDEV_TESTING_CMD_VALUE:
311 if (cb == 4)
312 {
313 if (off == 0)
314 pThis->TestingData.Value.u64Value.s.Lo = u32;
315 else if (off == 4)
316 pThis->TestingData.Value.u64Value.s.Hi = u32;
317 else if (off == 8)
318 pThis->TestingData.Value.u32Unit = u32;
319 else
320 break;
321 pThis->offTestingData = off + 4;
322 return VINF_SUCCESS;
323 }
324 if ( off >= 12
325 && cb == 1
326 && off - 12 < sizeof(pThis->TestingData.Value.szName) - 1)
327 {
328 if (u32)
329 {
330 pThis->TestingData.Value.szName[off - 12] = u32;
331 pThis->offTestingData = off + 1;
332 }
333 else
334 {
335#ifdef IN_RING3
336 pThis->TestingData.Value.szName[off - 12] = '\0';
337
338 RTTESTUNIT enmUnit = (RTTESTUNIT)pThis->TestingData.Value.u32Unit;
339 if (enmUnit <= RTTESTUNIT_INVALID || enmUnit >= RTTESTUNIT_END)
340 {
341 VMMDEV_TESTING_OUTPUT(("Invalid log value unit %#x\n", pThis->TestingData.Value.u32Unit));
342 enmUnit = RTTESTUNIT_NONE;
343 }
344 if (pThis->hTestingTest != NIL_RTTEST)
345 RTTestValue(pThis->hTestingTest, pThis->TestingData.Value.szName,
346 pThis->TestingData.Value.u64Value.u, enmUnit);
347
348 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %'9llu (%#llx) [%u]\n",
349 pThis->TestingData.Value.szName,
350 off - 12 > 48 ? 0 : 48 - (off - 12), "",
351 pThis->TestingData.Value.u64Value.u, pThis->TestingData.Value.u64Value.u,
352 pThis->TestingData.Value.u32Unit));
353#else
354 return VINF_IOM_R3_IOPORT_WRITE;
355#endif
356 }
357 return VINF_SUCCESS;
358 }
359 break;
360
361
362 /*
363 * RTTestValue with the return from DBGFR3RegNmQuery.
364 */
365 case VMMDEV_TESTING_CMD_VALUE_REG:
366 {
367 if ( off < sizeof(pThis->TestingData.String.sz) - 1
368 && cb == 1)
369 {
370 pThis->TestingData.String.sz[off] = u32;
371 if (u32)
372 pThis->offTestingData = off + 1;
373 else
374#ifdef IN_RING3
375 vmmdevTestingCmdExec_ValueReg(pDevIns, pThis);
376#else
377 return VINF_IOM_R3_IOPORT_WRITE;
378#endif
379 return VINF_SUCCESS;
380 }
381 break;
382
383 }
384
385 default:
386 break;
387 }
388 Log(("VMMDEV_TESTING_IOPORT_CMD: bad access; cmd=%#x off=%#x cb=%#x u32=%#x\n", uCmd, off, cb, u32));
389 return VINF_SUCCESS;
390 }
391
392 default:
393 break;
394 }
395
396 return VERR_IOM_IOPORT_UNUSED;
397}
398
399
400/**
401 * @callback_method_impl{FNIOMIOPORTIN}
402 */
403PDMBOTHCBDECL(int) vmmdevTestingIoRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
404{
405 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
406
407 switch (Port)
408 {
409 /*
410 * The NOP I/O ports are used for performance measurements.
411 */
412 case VMMDEV_TESTING_IOPORT_NOP:
413 switch (cb)
414 {
415 case 4:
416 case 2:
417 case 1:
418 break;
419 default:
420 AssertFailed();
421 return VERR_INTERNAL_ERROR_2;
422 }
423 *pu32 = VMMDEV_TESTING_NOP_RET;
424 return VINF_SUCCESS;
425
426 /*
427 * The timestamp I/O ports are obviously used for getting a good fix
428 * on the current time (as seen by the host?).
429 *
430 * The high word is latched when reading the low, so reading low + high
431 * gives you a 64-bit timestamp value.
432 */
433 case VMMDEV_TESTING_IOPORT_TS_LOW:
434 if (cb == 4)
435 {
436 uint64_t NowTS = RTTimeNanoTS();
437 *pu32 = (uint32_t)NowTS;
438 pThis->u32TestingHighTimestamp = (uint32_t)(NowTS >> 32);
439 return VINF_SUCCESS;
440 }
441 break;
442
443 case VMMDEV_TESTING_IOPORT_TS_HIGH:
444 if (cb == 4)
445 {
446 *pu32 = pThis->u32TestingHighTimestamp;
447 return VINF_SUCCESS;
448 }
449 break;
450
451 /*
452 * The command and data registers are write-only.
453 */
454 case VMMDEV_TESTING_IOPORT_CMD:
455 case VMMDEV_TESTING_IOPORT_DATA:
456 break;
457
458 default:
459 break;
460 }
461
462 return VERR_IOM_IOPORT_UNUSED;
463}
464
465
466#ifdef IN_RING3
467
468/**
469 * Initializes the testing part of the VMMDev if enabled.
470 *
471 * @returns VBox status code.
472 * @param pDevIns The VMMDev device instance.
473 */
474void vmmdevTestingTerminate(PPDMDEVINS pDevIns)
475{
476 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
477 if (!pThis->fTestingEnabled)
478 return;
479
480 if (pThis->hTestingTest != NIL_RTTEST)
481 {
482 RTTestFailed(pThis->hTestingTest, "Still open at vmmdev destruction.");
483 RTTestSummaryAndDestroy(pThis->hTestingTest);
484 pThis->hTestingTest = NIL_RTTEST;
485 }
486}
487
488
489/**
490 * Initializes the testing part of the VMMDev if enabled.
491 *
492 * @returns VBox status code.
493 * @param pDevIns The VMMDev device instance.
494 */
495int vmmdevTestingInitialize(PPDMDEVINS pDevIns)
496{
497 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
498 int rc;
499
500 if (!pThis->fTestingEnabled)
501 return VINF_SUCCESS;
502
503 if (pThis->fTestingMMIO)
504 {
505 /*
506 * Register a chunk of MMIO memory that we'll use for various
507 * tests interfaces. Optional, needs to be explicitly enabled.
508 */
509 rc = PDMDevHlpMMIORegister(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE, NULL /*pvUser*/,
510 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
511 vmmdevTestingMmioWrite, vmmdevTestingMmioRead, "VMMDev Testing");
512 AssertRCReturn(rc, rc);
513 if (pThis->fRZEnabled)
514 {
515 rc = PDMDevHlpMMIORegisterR0(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE, NIL_RTR0PTR /*pvUser*/,
516 "vmmdevTestingMmioWrite", "vmmdevTestingMmioRead");
517 AssertRCReturn(rc, rc);
518 rc = PDMDevHlpMMIORegisterRC(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE, NIL_RTRCPTR /*pvUser*/,
519 "vmmdevTestingMmioWrite", "vmmdevTestingMmioRead");
520 AssertRCReturn(rc, rc);
521 }
522 }
523
524
525 /*
526 * Register the I/O ports used for testing.
527 */
528 rc = PDMDevHlpIOPortRegister(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT, NULL,
529 vmmdevTestingIoWrite,
530 vmmdevTestingIoRead,
531 NULL /*pfnOutStr*/,
532 NULL /*pfnInStr*/,
533 "VMMDev Testing");
534 AssertRCReturn(rc, rc);
535 if (pThis->fRZEnabled)
536 {
537 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT, NIL_RTR0PTR /*pvUser*/,
538 "vmmdevTestingIoWrite",
539 "vmmdevTestingIoRead",
540 NULL /*pszOutStr*/,
541 NULL /*pszInStr*/,
542 "VMMDev Testing");
543 AssertRCReturn(rc, rc);
544 rc = PDMDevHlpIOPortRegisterRC(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT, NIL_RTRCPTR /*pvUser*/,
545 "vmmdevTestingIoWrite",
546 "vmmdevTestingIoRead",
547 NULL /*pszOutStr*/,
548 NULL /*pszInStr*/,
549 "VMMDev Testing");
550 AssertRCReturn(rc, rc);
551 }
552
553 /*
554 * Open the XML output file(/pipe/whatever) if specfied.
555 */
556 rc = RTTestCreateEx("VMMDevTesting", RTTEST_C_USE_ENV | RTTEST_C_NO_TLS | RTTEST_C_XML_DELAY_TOP_TEST,
557 RTTESTLVL_INVALID, -1 /*iNativeTestPipe*/, pThis->pszTestingXmlOutput, &pThis->hTestingTest);
558 if (RT_FAILURE(rc))
559 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, "Error creating testing instance");
560
561 return VINF_SUCCESS;
562}
563
564#endif /* IN_RING3 */
565#endif /* !VBOX_WITHOUT_TESTING_FEATURES */
566
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