VirtualBox

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

Last change on this file since 45761 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.4 KB
Line 
1/* $Id: VMMDevTesting.cpp 44528 2013-02-04 14:27:54Z 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#ifdef IN_RING3
35# include <iprt/stream.h>
36#endif
37
38#include "VMMDevState.h"
39#include "VMMDevTesting.h"
40
41
42#ifndef VBOX_WITHOUT_TESTING_FEATURES
43
44#define VMMDEV_TESTING_OUTPUT(a) \
45 do \
46 { \
47 LogAlways(a);\
48 LogRel(a);\
49 RTPrintf a; \
50 } while (0)
51
52/**
53 * @callback_method_impl{FNIOMMMIOWRITE}
54 */
55PDMBOTHCBDECL(int) vmmdevTestingMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
56{
57 switch (GCPhysAddr)
58 {
59 case VMMDEV_TESTING_MMIO_NOP:
60 switch (cb)
61 {
62 case 8:
63 case 4:
64 case 2:
65 case 1:
66 break;
67 default:
68 AssertFailed();
69 return VERR_INTERNAL_ERROR_5;
70 }
71 return VINF_SUCCESS;
72
73 default:
74 break;
75 }
76 return VINF_SUCCESS;
77}
78
79
80/**
81 * @callback_method_impl{FNIOMMMIOREAD}
82 */
83PDMBOTHCBDECL(int) vmmdevTestingMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
84{
85 switch (GCPhysAddr)
86 {
87 case VMMDEV_TESTING_MMIO_NOP:
88 switch (cb)
89 {
90 case 8:
91 *(uint64_t *)pv = VMMDEV_TESTING_NOP_RET | ((uint64_t)VMMDEV_TESTING_NOP_RET << 32);
92 break;
93 case 4:
94 *(uint32_t *)pv = VMMDEV_TESTING_NOP_RET;
95 break;
96 case 2:
97 *(uint16_t *)pv = (uint16_t)VMMDEV_TESTING_NOP_RET;
98 break;
99 case 1:
100 *(uint8_t *)pv = (uint8_t)VMMDEV_TESTING_NOP_RET;
101 break;
102 default:
103 AssertFailed();
104 return VERR_INTERNAL_ERROR_5;
105 }
106 return VINF_SUCCESS;
107
108
109 default:
110 break;
111 }
112
113 return VINF_IOM_MMIO_UNUSED_FF;
114}
115
116#ifdef IN_RING3
117
118/**
119 * Executes the VMMDEV_TESTING_CMD_VALUE_REG command when the data is ready.
120 *
121 * @param pDevIns The PDM device instance.
122 * @param pThis The instance VMMDev data.
123 */
124static void vmmdevTestingCmdExec_ValueReg(PPDMDEVINS pDevIns, VMMDevState *pThis)
125{
126 char *pszRegNm = strchr(pThis->TestingData.String.sz, ':');
127 if (pszRegNm)
128 {
129 *pszRegNm++ = '\0';
130 pszRegNm = RTStrStrip(pszRegNm);
131 }
132 char *pszValueNm = RTStrStrip(pThis->TestingData.String.sz);
133 size_t const cchValueNm = strlen(pszValueNm);
134 if (cchValueNm && pszRegNm && *pszRegNm)
135 {
136 PUVM pUVM = PDMDevHlpGetUVM(pDevIns);
137 PVM pVM = PDMDevHlpGetVM(pDevIns);
138 VMCPUID idCpu = VMMGetCpuId(pVM);
139 uint64_t u64Value;
140 int rc2 = DBGFR3RegNmQueryU64(pUVM, idCpu, pszRegNm, &u64Value);
141 if (RT_SUCCESS(rc2))
142 {
143 const char *pszWarn = rc2 == VINF_DBGF_TRUNCATED_REGISTER ? " truncated" : "";
144#if 1 /*!RTTestValue format*/
145 char szFormat[128], szValue[128];
146 RTStrPrintf(szFormat, sizeof(szFormat), "%%VR{%s}", pszRegNm);
147 rc2 = DBGFR3RegPrintf(pUVM, idCpu, szValue, sizeof(szValue), szFormat);
148 if (RT_SUCCESS(rc2))
149 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %16s {reg=%s}%s\n",
150 pszValueNm,
151 (ssize_t)cchValueNm - 12 > 48 ? 0 : 48 - ((ssize_t)cchValueNm - 12), "",
152 szValue, pszRegNm, pszWarn));
153 else
154#endif
155 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %'9llu (%#llx) [0] {reg=%s}%s\n",
156 pszValueNm,
157 (ssize_t)cchValueNm - 12 > 48 ? 0 : 48 - ((ssize_t)cchValueNm - 12), "",
158 u64Value, u64Value, pszRegNm, pszWarn));
159 }
160 else
161 VMMDEV_TESTING_OUTPUT(("testing: error querying register '%s' for value '%s': %Rrc\n",
162 pszRegNm, pszValueNm, rc2));
163 }
164 else
165 VMMDEV_TESTING_OUTPUT(("testing: malformed register value '%s'/'%s'\n", pszValueNm, pszRegNm));
166}
167
168#endif /* IN_RING3 */
169
170/**
171 * @callback_method_impl{FNIOMIOPORTOUT}
172 */
173PDMBOTHCBDECL(int) vmmdevTestingIoWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
174{
175 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
176
177 switch (Port)
178 {
179 /*
180 * The NOP I/O ports are used for performance measurements.
181 */
182 case VMMDEV_TESTING_IOPORT_NOP:
183 switch (cb)
184 {
185 case 4:
186 case 2:
187 case 1:
188 break;
189 default:
190 AssertFailed();
191 return VERR_INTERNAL_ERROR_2;
192 }
193 return VINF_SUCCESS;
194
195 /* The timestamp I/O ports are read-only. */
196 case VMMDEV_TESTING_IOPORT_TS_LOW:
197 case VMMDEV_TESTING_IOPORT_TS_HIGH:
198 break;
199
200 /*
201 * The command port (DWORD write only).
202 */
203 case VMMDEV_TESTING_IOPORT_CMD:
204 if (cb == 4)
205 {
206 pThis->u32TestingCmd = u32;
207 pThis->offTestingData = 0;
208 RT_ZERO(pThis->TestingData);
209 return VINF_SUCCESS;
210 }
211 break;
212
213 /*
214 * The data port. Used of providing data for a command.
215 */
216 case VMMDEV_TESTING_IOPORT_DATA:
217 {
218 uint32_t uCmd = pThis->u32TestingCmd;
219 uint32_t off = pThis->offTestingData;
220 switch (uCmd)
221 {
222 case VMMDEV_TESTING_CMD_INIT:
223 case VMMDEV_TESTING_CMD_SUB_NEW:
224 case VMMDEV_TESTING_CMD_FAILED:
225 case VMMDEV_TESTING_CMD_SKIPPED:
226 if ( off < sizeof(pThis->TestingData.String.sz) - 1
227 && cb == 1)
228 {
229 if (u32)
230 {
231 pThis->TestingData.String.sz[off] = u32;
232 pThis->offTestingData = off + 1;
233 }
234 else
235 {
236#ifdef IN_RING3
237 switch (uCmd)
238 {
239 case VMMDEV_TESTING_CMD_INIT:
240 VMMDEV_TESTING_OUTPUT(("testing: INIT '%.*s'\n",
241 sizeof(pThis->TestingData.String.sz) - 1, pThis->TestingData.String.sz));
242 break;
243 case VMMDEV_TESTING_CMD_SUB_NEW:
244 VMMDEV_TESTING_OUTPUT(("testing: SUB_NEW '%.*s'\n",
245 sizeof(pThis->TestingData.String.sz) - 1, pThis->TestingData.String.sz));
246 break;
247 case VMMDEV_TESTING_CMD_FAILED:
248 VMMDEV_TESTING_OUTPUT(("testing: FAILED '%.*s'\n",
249 sizeof(pThis->TestingData.String.sz) - 1, pThis->TestingData.String.sz));
250 break;
251 case VMMDEV_TESTING_CMD_SKIPPED:
252 VMMDEV_TESTING_OUTPUT(("testing: SKIPPED '%.*s'\n",
253 sizeof(pThis->TestingData.String.sz) - 1, pThis->TestingData.String.sz));
254 break;
255 }
256#else
257 return VINF_IOM_R3_IOPORT_WRITE;
258#endif
259 }
260 return VINF_SUCCESS;
261 }
262 break;
263
264 case VMMDEV_TESTING_CMD_TERM:
265 case VMMDEV_TESTING_CMD_SUB_DONE:
266 if ( off == 0
267 && cb == 4)
268 {
269#ifdef IN_RING3
270 pThis->TestingData.Error.c = u32;
271 if (uCmd == VMMDEV_TESTING_CMD_TERM)
272 VMMDEV_TESTING_OUTPUT(("testing: TERM - %u errors\n", u32));
273 else
274 VMMDEV_TESTING_OUTPUT(("testing: SUB_DONE - %u errors\n", u32));
275 return VINF_SUCCESS;
276#else
277 return VINF_IOM_R3_IOPORT_WRITE;
278#endif
279 }
280 break;
281
282 case VMMDEV_TESTING_CMD_VALUE:
283 if (cb == 4)
284 {
285 if (off == 0)
286 pThis->TestingData.Value.u64Value.s.Lo = u32;
287 else if (off == 4)
288 pThis->TestingData.Value.u64Value.s.Hi = u32;
289 else if (off == 8)
290 pThis->TestingData.Value.u32Unit = u32;
291 else
292 break;
293 pThis->offTestingData = off + 4;
294 return VINF_SUCCESS;
295 }
296 if ( off >= 12
297 && cb == 1
298 && off < sizeof(pThis->TestingData.Value.szName) - 1 - 12)
299 {
300 if (u32)
301 {
302 pThis->TestingData.Value.szName[off - 12] = u32;
303 pThis->offTestingData = off + 1;
304 }
305 else
306 {
307#ifdef IN_RING3
308 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%.*s'%*s: %'9llu (%#llx) [%u]\n",
309 sizeof(pThis->TestingData.Value.szName) - 1, pThis->TestingData.Value.szName,
310 off - 12 > 48 ? 0 : 48 - (off - 12), "",
311 pThis->TestingData.Value.u64Value.u, pThis->TestingData.Value.u64Value.u,
312 pThis->TestingData.Value.u32Unit));
313#else
314 return VINF_IOM_R3_IOPORT_WRITE;
315#endif
316 }
317 return VINF_SUCCESS;
318
319#ifdef IN_RING3
320 pThis->TestingData.Error.c = u32;
321 if (uCmd == VMMDEV_TESTING_CMD_TERM)
322 VMMDEV_TESTING_OUTPUT(("testing: TERM - %u errors\n", u32));
323 else
324 VMMDEV_TESTING_OUTPUT(("testing: SUB_DONE - %u errors\n", u32));
325 return VINF_SUCCESS;
326#else
327 return VINF_IOM_R3_IOPORT_WRITE;
328#endif
329 }
330 break;
331
332
333 /*
334 * RTTestValue with the return from DBGFR3RegNmQuery.
335 */
336 case VMMDEV_TESTING_CMD_VALUE_REG:
337 {
338 if ( off < sizeof(pThis->TestingData.String.sz) - 1
339 && cb == 1)
340 {
341 pThis->TestingData.String.sz[off] = u32;
342 if (u32)
343 pThis->offTestingData = off + 1;
344 else
345#ifdef IN_RING3
346 vmmdevTestingCmdExec_ValueReg(pDevIns, pThis);
347#else
348 return VINF_IOM_R3_IOPORT_WRITE;
349#endif
350 return VINF_SUCCESS;
351 }
352 break;
353
354 }
355
356 default:
357 break;
358 }
359 Log(("VMMDEV_TESTING_IOPORT_CMD: bad access; cmd=%#x off=%#x cb=%#x u32=%#x\n", uCmd, off, cb, u32));
360 return VINF_SUCCESS;
361 }
362
363 default:
364 break;
365 }
366
367 return VERR_IOM_IOPORT_UNUSED;
368}
369
370
371/**
372 * @callback_method_impl{FNIOMIOPORTIN}
373 */
374PDMBOTHCBDECL(int) vmmdevTestingIoRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
375{
376 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
377
378 switch (Port)
379 {
380 /*
381 * The NOP I/O ports are used for performance measurements.
382 */
383 case VMMDEV_TESTING_IOPORT_NOP:
384 switch (cb)
385 {
386 case 4:
387 case 2:
388 case 1:
389 break;
390 default:
391 AssertFailed();
392 return VERR_INTERNAL_ERROR_2;
393 }
394 *pu32 = VMMDEV_TESTING_NOP_RET;
395 return VINF_SUCCESS;
396
397 /*
398 * The timestamp I/O ports are obviously used for getting a good fix
399 * on the current time (as seen by the host?).
400 *
401 * The high word is latched when reading the low, so reading low + high
402 * gives you a 64-bit timestamp value.
403 */
404 case VMMDEV_TESTING_IOPORT_TS_LOW:
405 if (cb == 4)
406 {
407 uint64_t NowTS = RTTimeNanoTS();
408 *pu32 = (uint32_t)NowTS;
409 pThis->u32TestingHighTimestamp = (uint32_t)(NowTS >> 32);
410 return VINF_SUCCESS;
411 }
412 break;
413
414 case VMMDEV_TESTING_IOPORT_TS_HIGH:
415 if (cb == 4)
416 {
417 *pu32 = pThis->u32TestingHighTimestamp;
418 return VINF_SUCCESS;
419 }
420 break;
421
422 /*
423 * The command and data registers are write-only.
424 */
425 case VMMDEV_TESTING_IOPORT_CMD:
426 case VMMDEV_TESTING_IOPORT_DATA:
427 break;
428
429 default:
430 break;
431 }
432
433 return VERR_IOM_IOPORT_UNUSED;
434}
435
436
437#ifdef IN_RING3
438
439/**
440 * Initializes the testing part of the VMMDev if enabled.
441 *
442 * @returns VBox status code.
443 * @param pDevIns The VMMDev device instance.
444 */
445int vmmdevTestingInitialize(PPDMDEVINS pDevIns)
446{
447 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
448 if (!pThis->fTestingEnabled)
449 return VINF_SUCCESS;
450
451 /*
452 * Register a chunk of MMIO memory that we'll use for various
453 * tests interfaces.
454 */
455 int rc = PDMDevHlpMMIORegister(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE, NULL /*pvUser*/,
456 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
457 vmmdevTestingMmioWrite, vmmdevTestingMmioRead, "VMMDev Testing");
458 AssertRCReturn(rc, rc);
459 if (pThis->fRZEnabled)
460 {
461 rc = PDMDevHlpMMIORegisterR0(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE, NIL_RTR0PTR /*pvUser*/,
462 "vmmdevTestingMmioWrite", "vmmdevTestingMmioRead");
463 AssertRCReturn(rc, rc);
464 rc = PDMDevHlpMMIORegisterRC(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE, NIL_RTRCPTR /*pvUser*/,
465 "vmmdevTestingMmioWrite", "vmmdevTestingMmioRead");
466 AssertRCReturn(rc, rc);
467 }
468
469
470 /*
471 * Register the I/O ports used for testing.
472 */
473 rc = PDMDevHlpIOPortRegister(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT, NULL,
474 vmmdevTestingIoWrite,
475 vmmdevTestingIoRead,
476 NULL /*pfnOutStr*/,
477 NULL /*pfnInStr*/,
478 "VMMDev Testing");
479 AssertRCReturn(rc, rc);
480 if (pThis->fRZEnabled)
481 {
482 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT, NIL_RTR0PTR /*pvUser*/,
483 "vmmdevTestingIoWrite",
484 "vmmdevTestingIoRead",
485 NULL /*pszOutStr*/,
486 NULL /*pszInStr*/,
487 "VMMDev Testing");
488 AssertRCReturn(rc, rc);
489 rc = PDMDevHlpIOPortRegisterRC(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT, NIL_RTRCPTR /*pvUser*/,
490 "vmmdevTestingIoWrite",
491 "vmmdevTestingIoRead",
492 NULL /*pszOutStr*/,
493 NULL /*pszInStr*/,
494 "VMMDev Testing");
495 AssertRCReturn(rc, rc);
496 }
497
498 return VINF_SUCCESS;
499}
500
501#endif /* IN_RING3 */
502#endif /* !VBOX_WITHOUT_TESTING_FEATURES */
503
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