VirtualBox

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

Last change on this file since 44023 was 43876, checked in by vboxsync, 12 years ago

fixed a few format specifier bugs

  • 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 43876 2012-11-15 13:44:09Z 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-2012 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 PVM pVM = PDMDevHlpGetVM(pDevIns);
137 VMCPUID idCpu = VMMGetCpuId(pVM);
138 uint64_t u64Value;
139 int rc2 = DBGFR3RegNmQueryU64(pVM, idCpu, pszRegNm, &u64Value);
140 if (RT_SUCCESS(rc2))
141 {
142 const char *pszWarn = rc2 == VINF_DBGF_TRUNCATED_REGISTER ? " truncated" : "";
143#if 1 /*!RTTestValue format*/
144 char szFormat[128], szValue[128];
145 RTStrPrintf(szFormat, sizeof(szFormat), "%%VR{%s}", pszRegNm);
146 rc2 = DBGFR3RegPrintf(pVM, idCpu, szValue, sizeof(szValue), szFormat);
147 if (RT_SUCCESS(rc2))
148 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %16s {reg=%s}%s\n",
149 pszValueNm,
150 (ssize_t)cchValueNm - 12 > 48 ? 0 : 48 - ((ssize_t)cchValueNm - 12), "",
151 szValue, pszRegNm, pszWarn));
152 else
153#endif
154 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%s'%*s: %'9llu (%#llx) [0] {reg=%s}%s\n",
155 pszValueNm,
156 (ssize_t)cchValueNm - 12 > 48 ? 0 : 48 - ((ssize_t)cchValueNm - 12), "",
157 u64Value, u64Value, pszRegNm, pszWarn));
158 }
159 else
160 VMMDEV_TESTING_OUTPUT(("testing: error querying register '%s' for value '%s': %Rrc\n",
161 pszRegNm, pszValueNm, rc2));
162 }
163 else
164 VMMDEV_TESTING_OUTPUT(("testing: malformed register value '%s'/'%s'\n", pszValueNm, pszRegNm));
165}
166
167#endif /* IN_RING3 */
168
169/**
170 * @callback_method_impl{FNIOMIOPORTOUT}
171 */
172PDMBOTHCBDECL(int) vmmdevTestingIoWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
173{
174 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
175
176 switch (Port)
177 {
178 /*
179 * The NOP I/O ports are used for performance measurements.
180 */
181 case VMMDEV_TESTING_IOPORT_NOP:
182 switch (cb)
183 {
184 case 4:
185 case 2:
186 case 1:
187 break;
188 default:
189 AssertFailed();
190 return VERR_INTERNAL_ERROR_2;
191 }
192 return VINF_SUCCESS;
193
194 /* The timestamp I/O ports are read-only. */
195 case VMMDEV_TESTING_IOPORT_TS_LOW:
196 case VMMDEV_TESTING_IOPORT_TS_HIGH:
197 break;
198
199 /*
200 * The command port (DWORD write only).
201 */
202 case VMMDEV_TESTING_IOPORT_CMD:
203 if (cb == 4)
204 {
205 pThis->u32TestingCmd = u32;
206 pThis->offTestingData = 0;
207 RT_ZERO(pThis->TestingData);
208 return VINF_SUCCESS;
209 }
210 break;
211
212 /*
213 * The data port. Used of providing data for a command.
214 */
215 case VMMDEV_TESTING_IOPORT_DATA:
216 {
217 uint32_t uCmd = pThis->u32TestingCmd;
218 uint32_t off = pThis->offTestingData;
219 switch (uCmd)
220 {
221 case VMMDEV_TESTING_CMD_INIT:
222 case VMMDEV_TESTING_CMD_SUB_NEW:
223 case VMMDEV_TESTING_CMD_FAILED:
224 case VMMDEV_TESTING_CMD_SKIPPED:
225 if ( off < sizeof(pThis->TestingData.String.sz) - 1
226 && cb == 1)
227 {
228 if (u32)
229 {
230 pThis->TestingData.String.sz[off] = u32;
231 pThis->offTestingData = off + 1;
232 }
233 else
234 {
235#ifdef IN_RING3
236 switch (uCmd)
237 {
238 case VMMDEV_TESTING_CMD_INIT:
239 VMMDEV_TESTING_OUTPUT(("testing: INIT '%.*s'\n",
240 sizeof(pThis->TestingData.String.sz) - 1, pThis->TestingData.String.sz));
241 break;
242 case VMMDEV_TESTING_CMD_SUB_NEW:
243 VMMDEV_TESTING_OUTPUT(("testing: SUB_NEW '%.*s'\n",
244 sizeof(pThis->TestingData.String.sz) - 1, pThis->TestingData.String.sz));
245 break;
246 case VMMDEV_TESTING_CMD_FAILED:
247 VMMDEV_TESTING_OUTPUT(("testing: FAILED '%.*s'\n",
248 sizeof(pThis->TestingData.String.sz) - 1, pThis->TestingData.String.sz));
249 break;
250 case VMMDEV_TESTING_CMD_SKIPPED:
251 VMMDEV_TESTING_OUTPUT(("testing: SKIPPED '%.*s'\n",
252 sizeof(pThis->TestingData.String.sz) - 1, pThis->TestingData.String.sz));
253 break;
254 }
255#else
256 return VINF_IOM_R3_IOPORT_WRITE;
257#endif
258 }
259 return VINF_SUCCESS;
260 }
261 break;
262
263 case VMMDEV_TESTING_CMD_TERM:
264 case VMMDEV_TESTING_CMD_SUB_DONE:
265 if ( off == 0
266 && cb == 4)
267 {
268#ifdef IN_RING3
269 pThis->TestingData.Error.c = u32;
270 if (uCmd == VMMDEV_TESTING_CMD_TERM)
271 VMMDEV_TESTING_OUTPUT(("testing: TERM - %u errors\n", u32));
272 else
273 VMMDEV_TESTING_OUTPUT(("testing: SUB_DONE - %u errors\n", u32));
274 return VINF_SUCCESS;
275#else
276 return VINF_IOM_R3_IOPORT_WRITE;
277#endif
278 }
279 break;
280
281 case VMMDEV_TESTING_CMD_VALUE:
282 if (cb == 4)
283 {
284 if (off == 0)
285 pThis->TestingData.Value.u64Value.s.Lo = u32;
286 else if (off == 4)
287 pThis->TestingData.Value.u64Value.s.Hi = u32;
288 else if (off == 8)
289 pThis->TestingData.Value.u32Unit = u32;
290 else
291 break;
292 pThis->offTestingData = off + 4;
293 return VINF_SUCCESS;
294 }
295 if ( off >= 12
296 && cb == 1
297 && off < sizeof(pThis->TestingData.Value.szName) - 1 - 12)
298 {
299 if (u32)
300 {
301 pThis->TestingData.Value.szName[off - 12] = u32;
302 pThis->offTestingData = off + 1;
303 }
304 else
305 {
306#ifdef IN_RING3
307 VMMDEV_TESTING_OUTPUT(("testing: VALUE '%.*s'%*s: %'9llu (%#llx) [%u]\n",
308 sizeof(pThis->TestingData.Value.szName) - 1, pThis->TestingData.Value.szName,
309 off - 12 > 48 ? 0 : 48 - (off - 12), "",
310 pThis->TestingData.Value.u64Value.u, pThis->TestingData.Value.u64Value.u,
311 pThis->TestingData.Value.u32Unit));
312#else
313 return VINF_IOM_R3_IOPORT_WRITE;
314#endif
315 }
316 return VINF_SUCCESS;
317
318#ifdef IN_RING3
319 pThis->TestingData.Error.c = u32;
320 if (uCmd == VMMDEV_TESTING_CMD_TERM)
321 VMMDEV_TESTING_OUTPUT(("testing: TERM - %u errors\n", u32));
322 else
323 VMMDEV_TESTING_OUTPUT(("testing: SUB_DONE - %u errors\n", u32));
324 return VINF_SUCCESS;
325#else
326 return VINF_IOM_R3_IOPORT_WRITE;
327#endif
328 }
329 break;
330
331
332 /*
333 * RTTestValue with the return from DBGFR3RegNmQuery.
334 */
335 case VMMDEV_TESTING_CMD_VALUE_REG:
336 {
337 if ( off < sizeof(pThis->TestingData.String.sz) - 1
338 && cb == 1)
339 {
340 pThis->TestingData.String.sz[off] = u32;
341 if (u32)
342 pThis->offTestingData = off + 1;
343 else
344#ifdef IN_RING3
345 vmmdevTestingCmdExec_ValueReg(pDevIns, pThis);
346#else
347 return VINF_IOM_R3_IOPORT_WRITE;
348#endif
349 return VINF_SUCCESS;
350 }
351 break;
352
353 }
354
355 default:
356 break;
357 }
358 Log(("VMMDEV_TESTING_IOPORT_CMD: bad access; cmd=%#x off=%#x cb=%#x u32=%#x\n", uCmd, off, cb, u32));
359 return VINF_SUCCESS;
360 }
361
362 default:
363 break;
364 }
365
366 return VERR_IOM_IOPORT_UNUSED;
367}
368
369
370/**
371 * @callback_method_impl{FNIOMIOPORTIN}
372 */
373PDMBOTHCBDECL(int) vmmdevTestingIoRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
374{
375 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
376
377 switch (Port)
378 {
379 /*
380 * The NOP I/O ports are used for performance measurements.
381 */
382 case VMMDEV_TESTING_IOPORT_NOP:
383 switch (cb)
384 {
385 case 4:
386 case 2:
387 case 1:
388 break;
389 default:
390 AssertFailed();
391 return VERR_INTERNAL_ERROR_2;
392 }
393 *pu32 = VMMDEV_TESTING_NOP_RET;
394 return VINF_SUCCESS;
395
396 /*
397 * The timestamp I/O ports are obviously used for getting a good fix
398 * on the current time (as seen by the host?).
399 *
400 * The high word is latched when reading the low, so reading low + high
401 * gives you a 64-bit timestamp value.
402 */
403 case VMMDEV_TESTING_IOPORT_TS_LOW:
404 if (cb == 4)
405 {
406 uint64_t NowTS = RTTimeNanoTS();
407 *pu32 = (uint32_t)NowTS;
408 pThis->u32TestingHighTimestamp = (uint32_t)(NowTS >> 32);
409 return VINF_SUCCESS;
410 }
411 break;
412
413 case VMMDEV_TESTING_IOPORT_TS_HIGH:
414 if (cb == 4)
415 {
416 *pu32 = pThis->u32TestingHighTimestamp;
417 return VINF_SUCCESS;
418 }
419 break;
420
421 /*
422 * The command and data registers are write-only.
423 */
424 case VMMDEV_TESTING_IOPORT_CMD:
425 case VMMDEV_TESTING_IOPORT_DATA:
426 break;
427
428 default:
429 break;
430 }
431
432 return VERR_IOM_IOPORT_UNUSED;
433}
434
435
436#ifdef IN_RING3
437
438/**
439 * Initializes the testing part of the VMMDev if enabled.
440 *
441 * @returns VBox status code.
442 * @param pDevIns The VMMDev device instance.
443 */
444int vmmdevTestingInitialize(PPDMDEVINS pDevIns)
445{
446 VMMDevState *pThis = PDMINS_2_DATA(pDevIns, VMMDevState *);
447 if (!pThis->fTestingEnabled)
448 return VINF_SUCCESS;
449
450 /*
451 * Register a chunk of MMIO memory that we'll use for various
452 * tests interfaces.
453 */
454 int rc = PDMDevHlpMMIORegister(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE, NULL /*pvUser*/,
455 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
456 vmmdevTestingMmioWrite, vmmdevTestingMmioRead, "VMMDev Testing");
457 AssertRCReturn(rc, rc);
458 if (pThis->fRZEnabled)
459 {
460 rc = PDMDevHlpMMIORegisterR0(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE, NIL_RTR0PTR /*pvUser*/,
461 "vmmdevTestingMmioWrite", "vmmdevTestingMmioRead");
462 AssertRCReturn(rc, rc);
463 rc = PDMDevHlpMMIORegisterRC(pDevIns, VMMDEV_TESTING_MMIO_BASE, VMMDEV_TESTING_MMIO_SIZE, NIL_RTRCPTR /*pvUser*/,
464 "vmmdevTestingMmioWrite", "vmmdevTestingMmioRead");
465 AssertRCReturn(rc, rc);
466 }
467
468
469 /*
470 * Register the I/O ports used for testing.
471 */
472 rc = PDMDevHlpIOPortRegister(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT, NULL,
473 vmmdevTestingIoWrite,
474 vmmdevTestingIoRead,
475 NULL /*pfnOutStr*/,
476 NULL /*pfnInStr*/,
477 "VMMDev Testing");
478 AssertRCReturn(rc, rc);
479 if (pThis->fRZEnabled)
480 {
481 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT, NIL_RTR0PTR /*pvUser*/,
482 "vmmdevTestingIoWrite",
483 "vmmdevTestingIoRead",
484 NULL /*pszOutStr*/,
485 NULL /*pszInStr*/,
486 "VMMDev Testing");
487 AssertRCReturn(rc, rc);
488 rc = PDMDevHlpIOPortRegisterRC(pDevIns, VMMDEV_TESTING_IOPORT_BASE, VMMDEV_TESTING_IOPORT_COUNT, NIL_RTRCPTR /*pvUser*/,
489 "vmmdevTestingIoWrite",
490 "vmmdevTestingIoRead",
491 NULL /*pszOutStr*/,
492 NULL /*pszInStr*/,
493 "VMMDev Testing");
494 AssertRCReturn(rc, rc);
495 }
496
497 return VINF_SUCCESS;
498}
499
500#endif /* IN_RING3 */
501#endif /* !VBOX_WITHOUT_TESTING_FEATURES */
502
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