VirtualBox

source: vbox/trunk/src/VBox/Devices/testcase/tstDevice.cpp@ 83062

Last change on this file since 83062 was 83062, checked in by vboxsync, 5 years ago

Devices/testcase/tstDevice: Implement JSON based configuration loader for the device testbench

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.7 KB
Line 
1/* $Id: tstDevice.cpp 83062 2020-02-12 17:45:51Z vboxsync $ */
2/** @file
3 * tstDevice - Test framework for PDM devices/drivers
4 */
5
6/*
7 * Copyright (C) 2017-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo */
23#include <VBox/types.h>
24#include <VBox/sup.h>
25#include <VBox/version.h>
26#include <iprt/assert.h>
27#include <iprt/ctype.h>
28#include <iprt/getopt.h>
29#include <iprt/initterm.h>
30#include <iprt/ldr.h>
31#include <iprt/log.h>
32#include <iprt/list.h>
33#include <iprt/mem.h>
34#include <iprt/once.h>
35#include <iprt/path.h>
36#include <iprt/string.h>
37#include <iprt/stream.h>
38#include <iprt/trace.h>
39
40#include "tstDeviceInternal.h"
41#include "tstDeviceCfg.h"
42
43
44/*********************************************************************************************************************************
45* Defined Constants And Macros *
46*********************************************************************************************************************************/
47
48
49/*********************************************************************************************************************************
50* Structures and Typedefs *
51*********************************************************************************************************************************/
52
53
54/**
55 * Testcase plugin descriptor.
56 */
57typedef struct TSTDEVPLUGIN
58{
59 /** Node for the plugin list. */
60 RTLISTNODE NdPlugins;
61 /** Copy of the filename. */
62 char *pszFilename;
63 /** Loader handle. */
64 RTLDRMOD hMod;
65 /** Number of references to this plugin. */
66 volatile uint32_t cRefs;
67} TSTDEVPLUGIN;
68/** Pointer to a device plugin descriptor. */
69typedef TSTDEVPLUGIN *PTSTDEVPLUGIN;
70/** Pointer to a const plugin descriptor. */
71typedef const TSTDEVPLUGIN *PCTSTDEVPLUGIN;
72
73
74/**
75 * Testcase descriptor.
76 */
77typedef struct TSTDEVTESTCASE
78{
79 /** Node for the list of registered testcases. */
80 RTLISTNODE NdTestcases;
81 /** Pointer to the plugin the testcase belongs to. */
82 PCTSTDEVPLUGIN pPlugin;
83 /** Pointer to the testcase descriptor. */
84 PCTSTDEVTESTCASEREG pTestcaseReg;
85} TSTDEVTESTCASE;
86/** Pointer to a testcase descriptor. */
87typedef TSTDEVTESTCASE *PTSTDEVTESTCASE;
88/** Pointer to a constant testcase descriptor. */
89typedef const TSTDEVTESTCASE *PCTSTDEVTESTCASE;
90
91
92/**
93 * PDM R0/RC module trampoline descriptor.
94 */
95#pragma pack(1)
96typedef struct TSTDEVPDMMODTRAMPOLINE
97{
98 /** Jump instruction. */
99 uint8_t abJmp[6];
100 /** Address to jump to. */
101 uintptr_t AddrTarget;
102 /** Padding to get a 16byte aligned structure. */
103 uint8_t abPadding[HC_ARCH_BITS == 64 ? 2 : 5];
104} TSTDEVPDMMODTRAMPOLINE;
105#pragma pack()
106AssertCompileSize(TSTDEVPDMMODTRAMPOLINE, 16);
107/** Pointer to a trampoline descriptor. */
108typedef TSTDEVPDMMODTRAMPOLINE *PTSTDEVPDMMODTRAMPOLINE;
109
110/**
111 * PDM module descriptor.
112 */
113typedef struct TSTDEVPDMMOD
114{
115 /** Node for the module list. */
116 RTLISTNODE NdPdmMods;
117 /** Type of module (R3/R0/RC). */
118 TSTDEVPDMMODTYPE enmType;
119 /** Copy of the filename. */
120 char *pszFilename;
121 /** Loader handle. */
122 RTLDRMOD hLdrMod;
123 /** Number of references to this plugin. */
124 volatile uint32_t cRefs;
125 /** R0/RC Module type dependent data. */
126 struct
127 {
128 /** The exectuable image bits. */
129 void *pvBits;
130 /** Size of the memory buffer. */
131 size_t cbBits;
132 /** Pointer to the executable memory containing the trampoline code. */
133 uint8_t *pbTrampoline;
134 /** Number of trampoline entries supported. */
135 uint32_t cTrampolinesMax;
136 /** Number of trampoline entries used. */
137 uint32_t cTrampolines;
138 /** Pointer to the next unused trampoline entry. */
139 PTSTDEVPDMMODTRAMPOLINE pTrampolineNext;
140 } R0Rc;
141} TSTDEVPDMMOD;
142/** Pointer to a PDM module descriptor. */
143typedef TSTDEVPDMMOD *PTSTDEVPDMMOD;
144/** Pointer to a const PDM module descriptor. */
145typedef const TSTDEVPDMMOD *PCTSTDEVPDMMOD;
146
147/**
148 * PDM device descriptor.
149 */
150typedef struct TSTDEVPDMDEV
151{
152 /** Node for the known device list. */
153 RTLISTNODE NdPdmDevs;
154 /** Pointer to the PDM module containing the device. */
155 PCTSTDEVPDMMOD pPdmMod;
156 /** Device registration structure. */
157 const PDMDEVREG *pReg;
158} TSTDEVPDMDEV;
159/** Pointer to a PDM device descriptor .*/
160typedef TSTDEVPDMDEV *PTSTDEVPDMDEV;
161/** Pointer to a constant PDM device descriptor .*/
162typedef const TSTDEVPDMDEV *PCTSTDEVPDMDEV;
163
164
165/**
166 * Internal callback structure pointer.
167 * The main purpose is to define the extra data we associate
168 * with PDMDEVREGCB so we can find the plugin the device is associated with etc.
169 */
170typedef struct TSTDEVPDMDEVREGCBINT
171{
172 /** The callback structure. */
173 PDMDEVREGCB Core;
174 /** A bit of padding. */
175 uint32_t u32[4];
176 /** Pointer to plugin. */
177 PTSTDEVPDMMOD pMod;
178} TSTDEVPDMDEVREGCBINT;
179/** Pointer to a PDMDEVREGCBINT structure. */
180typedef TSTDEVPDMDEVREGCBINT *PTSTDEVPDMDEVREGCBINT;
181/** Pointer to a const PDMDEVREGCBINT structure. */
182typedef const TSTDEVPDMDEVREGCBINT *PCTSTDEVPDMDEVREGCBINT;
183
184
185typedef struct TSTDEVPDMR0IMPORTS
186{
187 /** The symbol name. */
188 const char *pszSymbol;
189 /** The pointer. */
190 PFNRT pfn;
191} TSTDEVPDMR0IMPORTS;
192typedef const TSTDEVPDMR0IMPORTS *PCTSTDEVPDMR0IMPORTS;
193
194
195/*********************************************************************************************************************************
196* Global Variables *
197*********************************************************************************************************************************/
198/** List of registered testcase plugins. */
199RTLISTANCHOR g_LstPlugins;
200/** List of registered testcases. */
201RTLISTANCHOR g_LstTestcases;
202/** List of registered PDM modules. */
203RTLISTANCHOR g_LstPdmMods;
204/** List of registered PDM devices. */
205RTLISTANCHOR g_LstPdmDevs;
206
207/**
208 * PDM R0 imports we implement.
209 */
210static const TSTDEVPDMR0IMPORTS g_aPdmR0Imports[] =
211{
212#if 0
213 {"IOMMMIOMapMMIO2Page", (PFNRT)IOMMMIOMapMMIO2Page},
214 {"IOMMMIOResetRegion", (PFNRT)IOMMMIOResetRegion},
215 {"IntNetR0IfSend", (PFNRT)/*IntNetR0IfSend*/NULL},
216 {"IntNetR0IfSetPromiscuousMode", (PFNRT)/*IntNetR0IfSetPromiscuousMode*/NULL},
217 {"PDMCritSectEnterDebug", (PFNRT)PDMCritSectEnterDebug},
218 {"PDMCritSectIsOwner", (PFNRT)PDMCritSectIsOwner},
219 {"PDMCritSectLeave", (PFNRT)PDMCritSectLeave},
220 {"PDMCritSectTryEnterDebug", (PFNRT)PDMCritSectTryEnterDebug},
221 {"PDMHCCritSectScheduleExitEvent", (PFNRT)PDMHCCritSectScheduleExitEvent},
222 {"PDMNsAllocateBandwidth", (PFNRT)PDMNsAllocateBandwidth},
223 {"PDMQueueAlloc", (PFNRT)PDMQueueAlloc},
224 {"PDMQueueInsert", (PFNRT)PDMQueueInsert},
225 {"PGMHandlerPhysicalPageTempOff", (PFNRT)PGMHandlerPhysicalPageTempOff},
226 {"PGMShwMakePageWritable", (PFNRT)PGMShwMakePageWritable},
227 {"RTAssertMsg1Weak", (PFNRT)RTAssertMsg1Weak},
228 {"RTAssertMsg2Weak", (PFNRT)RTAssertMsg2Weak},
229 {"RTAssertShouldPanic", (PFNRT)RTAssertShouldPanic},
230 {"RTLogDefaultInstanceEx", (PFNRT)RTLogDefaultInstanceEx},
231 {"RTLogLoggerEx", (PFNRT)RTLogLoggerEx},
232 {"RTLogRelGetDefaultInstanceEx", (PFNRT)RTLogRelGetDefaultInstanceEx},
233 {"RTOnceSlow", (PFNRT)RTOnceSlow},
234 {"RTR0AssertPanicSystem", (PFNRT)0x10101010},
235 {"RTThreadSleep", (PFNRT)RTThreadSleep},
236 {"RTTimeMilliTS", (PFNRT)RTTimeMilliTS},
237 {"RTTimeNanoTS", (PFNRT)RTTimeNanoTS},
238 {"RTTraceBufAddMsgF", (PFNRT)RTTraceBufAddMsgF},
239 {"SUPSemEventSignal", (PFNRT)SUPSemEventSignal},
240 {"TMTimerGet", (PFNRT)TMTimerGet},
241 {"TMTimerGetFreq", (PFNRT)TMTimerGetFreq},
242 {"TMTimerIsActive", (PFNRT)TMTimerIsActive},
243 {"TMTimerIsLockOwner", (PFNRT)TMTimerIsLockOwner},
244 {"TMTimerLock", (PFNRT)TMTimerLock},
245 {"TMTimerSet", (PFNRT)TMTimerSet},
246 {"TMTimerSetFrequencyHint", (PFNRT)TMTimerSetFrequencyHint},
247 {"TMTimerSetMicro", (PFNRT)TMTimerSetMicro},
248 {"TMTimerSetMillies", (PFNRT)TMTimerSetMillies},
249 {"TMTimerSetNano", (PFNRT)TMTimerSetNano},
250 {"TMTimerStop", (PFNRT)TMTimerStop},
251 {"TMTimerUnlock", (PFNRT)TMTimerUnlock},
252 {"nocrt_memcmp", (PFNRT)memcmp},
253 {"nocrt_memcpy", (PFNRT)memcpy},
254 {"nocrt_memmove", (PFNRT)memmove},
255 {"nocrt_memset", (PFNRT)memset},
256 {"nocrt_strlen", (PFNRT)strlen},
257#else
258 { NULL, NULL }
259#endif
260};
261
262
263/*********************************************************************************************************************************
264* Internal Functions *
265*********************************************************************************************************************************/
266
267#if 0
268/**
269 * Parses the options given to the testcase.
270 *
271 * @returns Process status code.
272 * @param cArgs Number of arguments given.
273 * @param paszArgs Pointer to the argument vector.
274 */
275static RTEXITCODE tstDevParseOptions(int cArgs, char *paszArgs[])
276{
277 static RTGETOPTDEF const s_aOptions[] =
278 {
279 };
280
281
282 int ch;
283 RTGETOPTUNION Value;
284 RTGETOPTSTATE GetState;
285 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
286 while ((ch = RTGetOpt(&GetState, &Value)))
287 {
288 switch (ch)
289 {
290 default:
291 return RTGetOptPrintError(ch, &Value);
292 }
293 }
294}
295#endif
296
297
298/**
299 * Checks whether the given testcase name is already existing.
300 *
301 * @returns Pointer to existing testcase, NULL if not found.
302 * @param pszFilename The filename to check.
303 */
304static PCTSTDEVTESTCASE tstDevTestcaseFind(const char *pszName)
305{
306 PCTSTDEVTESTCASE pIt;
307 RTListForEach(&g_LstTestcases, pIt, TSTDEVTESTCASE, NdTestcases)
308 {
309 if (!RTStrCmp(pIt->pTestcaseReg->szName, pszName))
310 return pIt;
311 }
312
313 return NULL;
314}
315
316
317/**
318 * @interface_method_impl{TSTDEVPLUGINREGISTER,pfnRegisterTestcase}
319 */
320static DECLCALLBACK(int) tstDevRegisterTestcase(void *pvUser, PCTSTDEVTESTCASEREG pTestcaseReg)
321{
322 int rc = VINF_SUCCESS;
323 PTSTDEVPLUGIN pPlugin = (PTSTDEVPLUGIN)pvUser;
324
325 /* Try to find a testcase with the name first. */
326 if (!tstDevTestcaseFind(pTestcaseReg->szName))
327 {
328 PTSTDEVTESTCASE pTestcase = (PTSTDEVTESTCASE)RTMemAllocZ(sizeof(TSTDEVPLUGIN));
329 if (RT_LIKELY(pTestcase))
330 {
331 pTestcase->pPlugin = pPlugin;
332 pPlugin->cRefs++;
333 pTestcase->pTestcaseReg = pTestcaseReg;
334 RTListAppend(&g_LstTestcases, &pTestcase->NdTestcases);
335 return VINF_SUCCESS;
336 }
337 else
338 rc = VERR_NO_MEMORY;
339 }
340 else
341 rc = VERR_ALREADY_EXISTS;
342
343 return rc;
344}
345
346
347/**
348 * Checks whether the given plugin filename was already loaded.
349 *
350 * @returns Pointer to already loaded plugin, NULL if not found.
351 * @param pszFilename The filename to check.
352 */
353static PCTSTDEVPLUGIN tstDevPluginFind(const char *pszFilename)
354{
355 PCTSTDEVPLUGIN pIt;
356 RTListForEach(&g_LstPlugins, pIt, TSTDEVPLUGIN, NdPlugins)
357 {
358 if (!RTStrCmp(pIt->pszFilename, pszFilename))
359 return pIt;
360 }
361
362 return NULL;
363}
364
365
366/**
367 * Tries to loads the given plugin.
368 *
369 * @returns VBox status code.
370 * @param pszFilename The filename to load.
371 */
372static int tstDevLoadPlugin(const char *pszFilename)
373{
374 int rc = VINF_SUCCESS;
375
376 /* Check whether the plugin is loaded first. */
377 if (!tstDevPluginFind(pszFilename))
378 {
379 PTSTDEVPLUGIN pPlugin = (PTSTDEVPLUGIN)RTMemAllocZ(sizeof(TSTDEVPLUGIN));
380 if (RT_LIKELY(pPlugin))
381 {
382 pPlugin->pszFilename = RTStrDup(pszFilename);
383 pPlugin->cRefs = 1;
384 rc = RTLdrLoad(pszFilename, &pPlugin->hMod);
385 if (RT_SUCCESS(rc))
386 {
387 TSTDEVPLUGINREGISTER TestcaseRegister;
388 PFNTSTDEVPLUGINLOAD pfnPluginLoad = NULL;
389
390 TestcaseRegister.pfnRegisterTestcase = tstDevRegisterTestcase;
391
392 rc = RTLdrGetSymbol(pPlugin->hMod, TSTDEV_PLUGIN_LOAD_NAME, (void**)&pfnPluginLoad);
393 if (RT_FAILURE(rc) || !pfnPluginLoad)
394 {
395 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnPluginLoad=%#p\n",
396 TSTDEV_PLUGIN_LOAD_NAME, pszFilename, rc, pfnPluginLoad));
397 if (RT_SUCCESS(rc))
398 rc = VERR_SYMBOL_NOT_FOUND;
399 }
400
401 if (RT_SUCCESS(rc))
402 {
403 /* Get the function table. */
404 rc = pfnPluginLoad(pPlugin, &TestcaseRegister);
405 }
406 else
407 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszFilename, rc));
408
409 /* Create a plugin entry on success. */
410 if (RT_SUCCESS(rc))
411 {
412 RTListAppend(&g_LstPlugins, &pPlugin->NdPlugins);
413 return VINF_SUCCESS;
414 }
415 else
416 RTLdrClose(pPlugin->hMod);
417 }
418
419 RTMemFree(pPlugin);
420 }
421 else
422 rc = VERR_NO_MEMORY;
423 }
424
425 return rc;
426}
427
428
429/**
430 * Checks whether the given testcase name is already existing.
431 *
432 * @returns Pointer to already loaded plugin, NULL if not found.
433 * @param pszFilename The filename to check.
434 */
435static PCTSTDEVPDMDEV tstDevPdmDeviceFind(const char *pszName)
436{
437 PCTSTDEVPDMDEV pIt;
438 RTListForEach(&g_LstPdmDevs, pIt, TSTDEVPDMDEV, NdPdmDevs)
439 {
440 if (!RTStrCmp(pIt->pReg->szName, pszName))
441 return pIt;
442 }
443
444 return NULL;
445}
446
447
448/**
449 * Checks that a PDMDRVREG::szName, PDMDEVREG::szName or PDMUSBREG::szName
450 * field contains only a limited set of ASCII characters.
451 *
452 * @returns true / false.
453 * @param pszName The name to validate.
454 */
455bool tstDevPdmR3IsValidName(const char *pszName)
456{
457 char ch;
458 while ( (ch = *pszName) != '\0'
459 && ( RT_C_IS_ALNUM(ch)
460 || ch == '-'
461 || ch == ' ' /** @todo disallow this! */
462 || ch == '_') )
463 pszName++;
464 return ch == '\0';
465}
466
467
468/**
469 * @interface_method_impl{PDMDEVREGCB,pfnRegister}
470 */
471static DECLCALLBACK(int) tstDevPdmR3DevReg_Register(PPDMDEVREGCB pCallbacks, PCPDMDEVREG pReg)
472{
473 /*
474 * Validate the registration structure (mostly copy and paste from PDMDevice.cpp).
475 */
476 Assert(pReg);
477 AssertMsgReturn(pReg->u32Version == PDM_DEVREG_VERSION,
478 ("Unknown struct version %#x!\n", pReg->u32Version),
479 VERR_PDM_UNKNOWN_DEVREG_VERSION);
480
481 AssertMsgReturn( pReg->szName[0]
482 && strlen(pReg->szName) < sizeof(pReg->szName)
483 && tstDevPdmR3IsValidName(pReg->szName),
484 ("Invalid name '%.*s'\n", sizeof(pReg->szName), pReg->szName),
485 VERR_PDM_INVALID_DEVICE_REGISTRATION);
486 AssertMsgReturn((pReg->fFlags & PDM_DEVREG_FLAGS_HOST_BITS_MASK) == PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT,
487 ("Invalid host bits flags! fFlags=%#x (Device %s)\n", pReg->fFlags, pReg->szName),
488 VERR_PDM_INVALID_DEVICE_HOST_BITS);
489 AssertMsgReturn((pReg->fFlags & PDM_DEVREG_FLAGS_GUEST_BITS_MASK),
490 ("Invalid guest bits flags! fFlags=%#x (Device %s)\n", pReg->fFlags, pReg->szName),
491 VERR_PDM_INVALID_DEVICE_REGISTRATION);
492 AssertMsgReturn(pReg->fClass,
493 ("No class! (Device %s)\n", pReg->szName),
494 VERR_PDM_INVALID_DEVICE_REGISTRATION);
495 AssertMsgReturn(pReg->cMaxInstances > 0,
496 ("Max instances %u! (Device %s)\n", pReg->cMaxInstances, pReg->szName),
497 VERR_PDM_INVALID_DEVICE_REGISTRATION);
498 AssertMsgReturn(pReg->cbInstanceCC <= (uint32_t)(pReg->fFlags & (PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0) ? 96 * _1K : _1M),
499 ("Instance size %d bytes! (Device %s)\n", pReg->cbInstanceCC, pReg->szName),
500 VERR_PDM_INVALID_DEVICE_REGISTRATION);
501 AssertMsgReturn(pReg->pfnConstruct,
502 ("No constructor! (Device %s)\n", pReg->szName),
503 VERR_PDM_INVALID_DEVICE_REGISTRATION);
504 AssertLogRelMsgReturn((pReg->fFlags & PDM_DEVREG_FLAGS_GUEST_BITS_MASK) == PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT,
505 ("PDM: Rejected device '%s' because it didn't match the guest bits.\n", pReg->szName),
506 VERR_PDM_INVALID_DEVICE_GUEST_BITS);
507 AssertLogRelMsg(pReg->u32VersionEnd == PDM_DEVREG_VERSION,
508 ("u32VersionEnd=%#x, expected %#x. (szName=%s)\n",
509 pReg->u32VersionEnd, PDM_DEVREG_VERSION, pReg->szName));
510
511 /*
512 * Check for duplicates.
513 */
514 int rc = VINF_SUCCESS;
515 PCTSTDEVPDMDEVREGCBINT pRegCB = (PCTSTDEVPDMDEVREGCBINT)pCallbacks;
516 if (!tstDevPdmDeviceFind(pReg->szName))
517 {
518 PTSTDEVPDMDEV pPdmDev = (PTSTDEVPDMDEV)RTMemAllocZ(sizeof(TSTDEVPDMDEV));
519 if (RT_LIKELY(pPdmDev))
520 {
521 pPdmDev->pPdmMod = pRegCB->pMod;
522 pRegCB->pMod->cRefs++;
523 pPdmDev->pReg = pReg;
524 RTListAppend(&g_LstPdmDevs, &pPdmDev->NdPdmDevs);
525 return VINF_SUCCESS;
526 }
527 else
528 rc = VERR_NO_MEMORY;
529 }
530 else
531 rc = VERR_PDM_DEVICE_NAME_CLASH;
532
533 return rc;
534}
535
536
537/**
538 * Checks whether the given PDM module filename was already loaded.
539 *
540 * @returns Pointer to already loaded plugin, NULL if not found.
541 * @param pszFilename The filename to check.
542 */
543static PCTSTDEVPDMMOD tstDevPdmModFind(const char *pszFilename)
544{
545 PCTSTDEVPDMMOD pIt;
546 RTListForEach(&g_LstPdmMods, pIt, TSTDEVPDMMOD, NdPdmMods)
547 {
548 if (!RTStrCmp(pIt->pszFilename, pszFilename))
549 return pIt;
550 }
551
552 return NULL;
553}
554
555
556/**
557 * Resolve an external symbol during RTLdrGetBits().
558 *
559 * @returns iprt status code.
560 * @param hLdrMod The loader module handle.
561 * @param pszModule Module name.
562 * @param pszSymbol Symbol name, NULL if uSymbol should be used.
563 * @param uSymbol Symbol ordinal, ~0 if pszSymbol should be used.
564 * @param pValue Where to store the symbol value (address).
565 * @param pvUser User argument.
566 */
567static DECLCALLBACK(int) tstDevPdmLoadR0RcModGetImport(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol,
568 unsigned uSymbol, RTUINTPTR *pValue, void *pvUser)
569{
570 RT_NOREF(hLdrMod, uSymbol, pszModule);
571 PTSTDEVPDMMOD pMod = (PTSTDEVPDMMOD)pvUser;
572
573 /* Resolve the import. */
574 PCTSTDEVPDMR0IMPORTS pImpDesc = NULL;
575 bool fFound = false;
576 for (uint32_t i = 0; i < RT_ELEMENTS(g_aPdmR0Imports); i++)
577 {
578 pImpDesc = &g_aPdmR0Imports[i];
579 if (!strcmp(pszSymbol, pImpDesc->pszSymbol))
580 {
581 fFound = true;
582 break;
583 }
584 }
585
586 int rc = VERR_SYMBOL_NOT_FOUND;
587 if (fFound)
588 {
589 /* Check whether the symbol has a trampoline already. */
590 PTSTDEVPDMMODTRAMPOLINE pTrampoline = (PTSTDEVPDMMODTRAMPOLINE)pMod->R0Rc.pbTrampoline;
591 for (uint32_t i = 0; i < pMod->R0Rc.cTrampolines; i++)
592 {
593 if (pTrampoline->AddrTarget == (uintptr_t)pImpDesc->pfn)
594 break;
595 pTrampoline++;
596 }
597
598 /* Create new trampoline if not found. */
599 if (pTrampoline->AddrTarget != (uintptr_t)pImpDesc->pfn)
600 {
601 if (pMod->R0Rc.cTrampolines < pMod->R0Rc.cTrampolinesMax)
602 {
603 pTrampoline = pMod->R0Rc.pTrampolineNext;
604 pMod->R0Rc.pTrampolineNext++;
605 pMod->R0Rc.cTrampolines++;
606 pTrampoline->abJmp[0] = 0xff; /* jmp */
607 pTrampoline->abJmp[1] = 0x25; /* rip */
608 pTrampoline->abJmp[2] = 0x00; /* offset */
609 pTrampoline->abJmp[3] = 0x00;
610 pTrampoline->abJmp[4] = 0x00;
611 pTrampoline->abJmp[5] = 0x00;
612 pTrampoline->AddrTarget = (uintptr_t)pImpDesc->pfn;
613 rc = VINF_SUCCESS;
614 }
615 else
616 {
617 rc = VERR_SYMBOL_NOT_FOUND;
618 AssertFailed();
619 }
620 }
621 else
622 rc = VINF_SUCCESS;
623
624 if (RT_SUCCESS(rc))
625 *pValue = (RTUINTPTR)pTrampoline;
626 }
627 else
628 AssertFailed();
629
630 return rc;
631}
632
633
634/**
635 * Loads a new R0 modules given by the filename.
636 *
637 * @returns VBox status code.
638 * @param pMod Pointer to module structure.
639 */
640static int tstDevPdmLoadR0RcMod(PTSTDEVPDMMOD pMod)
641{
642 int rc = VINF_SUCCESS;
643 const char *pszFile = RTPathFilename(pMod->pszFilename);
644
645 /* Check whether the plugin is loaded first. */
646 if (!tstDevPdmModFind(pszFile))
647 {
648 /*
649 * R0 modules need special treatment as these are relocatable images
650 * which are supposed to run in ring 0.
651 */
652 rc = RTLdrOpen(pMod->pszFilename, 0, RTLDRARCH_HOST, &pMod->hLdrMod);
653 if (RT_SUCCESS(rc))
654 {
655 size_t cb = RTLdrSize(pMod->hLdrMod) + 1024 * sizeof(TSTDEVPDMMODTRAMPOLINE);
656
657 /* Allocate bits. */
658 uint32_t fFlags = RTMEMALLOCEX_FLAGS_EXEC;
659#ifdef RT_OS_LINUX
660 /*
661 * amd64 ELF binaries support only a 2GB code segment everything must be in
662 * (X86_64_PC32 relocation) so we have to use a trampoline to the final destination
663 * which is kept close to the imported module.
664 */
665 fFlags |= RTMEMALLOCEX_FLAGS_32BIT_REACH;
666#endif
667 rc = RTMemAllocEx(cb, 0, fFlags, (void **)&pMod->R0Rc.pbTrampoline);
668 pMod->R0Rc.cbBits = cb;
669 if (RT_SUCCESS(rc))
670 {
671 pMod->R0Rc.pvBits = pMod->R0Rc.pbTrampoline + 1024 * sizeof(TSTDEVPDMMODTRAMPOLINE);
672 pMod->R0Rc.cTrampolinesMax = 1024;
673 pMod->R0Rc.cTrampolines = 0;
674 pMod->R0Rc.pTrampolineNext = (PTSTDEVPDMMODTRAMPOLINE)pMod->R0Rc.pbTrampoline;
675 /* Get the bits. */
676 rc = RTLdrGetBits(pMod->hLdrMod, pMod->R0Rc.pvBits, (uintptr_t)pMod->R0Rc.pvBits,
677 tstDevPdmLoadR0RcModGetImport, pMod);
678 if (RT_FAILURE(rc))
679 RTMemFreeEx(pMod->R0Rc.pbTrampoline, pMod->R0Rc.cbBits);
680 }
681
682 if (RT_FAILURE(rc))
683 RTLdrClose(pMod->hLdrMod);
684 }
685 }
686
687 return rc;
688}
689
690
691/**
692 * Loads the given
693 */
694static int tstDevPdmLoadR3Mod(PTSTDEVPDMMOD pMod)
695{
696 int rc = RTLdrLoad(pMod->pszFilename, &pMod->hLdrMod);
697 if (RT_SUCCESS(rc))
698 {
699 FNPDMVBOXDEVICESREGISTER *pfnVBoxDevicesRegister;
700 rc = RTLdrGetSymbol(pMod->hLdrMod, "VBoxDevicesRegister", (void**)&pfnVBoxDevicesRegister);
701 if (RT_FAILURE(rc) || !pfnVBoxDevicesRegister)
702 {
703 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnPluginLoad=%#p\n",
704 "VBoxDevicesRegister", pMod->pszFilename, rc, pfnVBoxDevicesRegister));
705 if (RT_SUCCESS(rc))
706 rc = VERR_SYMBOL_NOT_FOUND;
707 }
708
709 if (RT_SUCCESS(rc))
710 {
711 TSTDEVPDMDEVREGCBINT RegCB;
712 RegCB.Core.u32Version = PDM_DEVREG_CB_VERSION;
713 RegCB.Core.pfnRegister = tstDevPdmR3DevReg_Register;
714 RegCB.pMod = pMod;
715 rc = pfnVBoxDevicesRegister(&RegCB.Core, VBOX_VERSION);
716 }
717 else
718 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pMod->pszFilename, rc));
719
720 if (RT_FAILURE(rc))
721 RTLdrClose(pMod->hLdrMod);
722 }
723
724 return rc;
725}
726
727
728/**
729 * Tries to loads the given PDM module.
730 *
731 * @returns VBox status code.
732 * @param pszFilename The filename to load.
733 * @param enmModType The module type.
734 */
735static int tstDevPdmLoadMod(const char *pszFilename, TSTDEVPDMMODTYPE enmModType)
736{
737 int rc = VINF_SUCCESS;
738
739 /* Check whether the plugin is loaded first. */
740 if (!tstDevPdmModFind(pszFilename))
741 {
742 PTSTDEVPDMMOD pMod = (PTSTDEVPDMMOD)RTMemAllocZ(sizeof(TSTDEVPDMMOD));
743 if (RT_LIKELY(pMod))
744 {
745 pMod->pszFilename = RTStrDup(pszFilename);
746 pMod->cRefs = 1;
747 pMod->enmType = enmModType;
748
749 if (enmModType == TSTDEVPDMMODTYPE_R3)
750 rc = tstDevPdmLoadR3Mod(pMod);
751 else if (enmModType == TSTDEVPDMMODTYPE_RC || enmModType == TSTDEVPDMMODTYPE_R0)
752 rc = tstDevPdmLoadR0RcMod(pMod);
753
754 if (RT_SUCCESS(rc))
755 RTListAppend(&g_LstPdmMods, &pMod->NdPdmMods);
756 else
757 RTMemFree(pMod);
758 }
759 else
760 rc = VERR_NO_MEMORY;
761 }
762
763 return rc;
764}
765
766
767/**
768 * Tries to resolve the given symbol from the module given.
769 *
770 * @returns VBox status code.
771 * @param pThis The device under test instance.
772 * @param pszMod The module name.
773 * @param enmModType The module type if the module needs to be loaded.
774 * @param pszSymbol The symbol to resolve.
775 * @param ppfn Where to store the value on success.
776 */
777DECLHIDDEN(int) tstDevPdmLdrGetSymbol(PTSTDEVDUTINT pThis, const char *pszMod, TSTDEVPDMMODTYPE enmModType,
778 const char *pszSymbol, PFNRT *ppfn)
779{
780 RT_NOREF(pThis);
781
782 int rc = VINF_SUCCESS;
783 PCTSTDEVPDMMOD pMod = tstDevPdmModFind(pszMod);
784 if (!pMod)
785 {
786 /* Try to load the module. */
787 rc = tstDevPdmLoadMod(pszMod, enmModType);
788 if (RT_SUCCESS(rc))
789 {
790 pMod = tstDevPdmModFind(pszMod);
791 AssertPtr(pMod);
792 }
793 }
794
795 if (RT_SUCCESS(rc))
796 {
797 if (pMod->enmType == TSTDEVPDMMODTYPE_R0 || pMod->enmType == TSTDEVPDMMODTYPE_RC)
798 rc = RTLdrGetSymbolEx(pMod->hLdrMod, pMod->R0Rc.pvBits, (uintptr_t)pMod->R0Rc.pvBits,
799 UINT32_MAX, pszSymbol, (PRTLDRADDR)ppfn);
800 else
801 rc = RTLdrGetSymbol(pMod->hLdrMod, pszSymbol, (void **)ppfn);
802 }
803
804 return rc;
805}
806
807
808/**
809 * Create a new PDM device with default config.
810 *
811 * @returns VBox status code.
812 * @param pszName Name of the device to create.
813 * @param fR0Enabled Flag whether R0 support should be enabled for this device.
814 * @param fRCEnabled Flag whether RC support should be enabled for this device.
815 * @param pDut The device under test structure the created PDM device instance is exercised under.
816 */
817static int tstDevPdmDevCreate(const char *pszName, bool fR0Enabled, bool fRCEnabled, PTSTDEVDUTINT pDut)
818{
819 int rc = VINF_SUCCESS;
820 PCTSTDEVPDMDEV pPdmDev = tstDevPdmDeviceFind(pszName);
821 if (RT_LIKELY(pPdmDev))
822 {
823 PPDMCRITSECT pCritSect;
824 /* Figure out how much we need. */
825 uint32_t cb = RT_UOFFSETOF_DYN(PDMDEVINS, achInstanceData[pPdmDev->pReg->cbInstanceCC]);
826 cb = RT_ALIGN_32(cb, 64);
827 uint32_t const offShared = cb;
828 cb += RT_ALIGN_32(pPdmDev->pReg->cbInstanceShared, 64);
829 uint32_t const cbCritSect = RT_ALIGN_32(sizeof(*pCritSect), 64);
830 cb += cbCritSect;
831 uint32_t const cbMsixState = RT_ALIGN_32(pPdmDev->pReg->cMaxMsixVectors * 16 + (pPdmDev->pReg->cMaxMsixVectors + 7) / 8, _4K);
832 uint32_t const cbPciDev = RT_ALIGN_32(RT_UOFFSETOF_DYN(PDMPCIDEV, abMsixState[cbMsixState]), 64);
833 uint32_t const cPciDevs = RT_MIN(pPdmDev->pReg->cMaxPciDevices, 1024);
834 uint32_t const cbPciDevs = cbPciDev * cPciDevs;
835 cb += cbPciDevs;
836
837 PPDMDEVINS pDevIns = (PPDMDEVINS)RTMemAllocZ(cb);
838 pDevIns->u32Version = PDM_DEVINS_VERSION;
839 pDevIns->iInstance = 0;
840 pDevIns->pReg = pPdmDev->pReg;
841 pDevIns->pvInstanceDataR3 = &pDevIns->achInstanceData[0];
842 pDevIns->pHlpR3 = &g_tstDevPdmDevHlpR3;
843 pDevIns->pCfg = &pDut->Cfg;
844 pDevIns->Internal.s.pDut = pDut;
845 pDevIns->cbRing3 = cb;
846 pDevIns->fR0Enabled = fR0Enabled;
847 pDevIns->fRCEnabled = fRCEnabled;
848 pDevIns->pvInstanceDataR3 = (uint8_t *)pDevIns + offShared;
849 pDevIns->pvInstanceDataForR3 = &pDevIns->achInstanceData[0];
850 pCritSect = (PPDMCRITSECT)((uint8_t *)pDevIns + offShared + RT_ALIGN_32(pPdmDev->pReg->cbInstanceShared, 64));
851 pDevIns->pCritSectRoR3 = pCritSect;
852 pDevIns->cbPciDev = cbPciDev;
853 pDevIns->cPciDevs = cPciDevs;
854 for (uint32_t iPciDev = 0; iPciDev < cPciDevs; iPciDev++)
855 {
856 PPDMPCIDEV pPciDev = (PPDMPCIDEV)((uint8_t *)pDevIns->pCritSectRoR3 + cbCritSect + cbPciDev * iPciDev);
857 if (iPciDev < RT_ELEMENTS(pDevIns->apPciDevs))
858 pDevIns->apPciDevs[iPciDev] = pPciDev;
859 pPciDev->cbConfig = _4K;
860 pPciDev->cbMsixState = cbMsixState;
861 pPciDev->idxSubDev = (uint16_t)iPciDev;
862 pPciDev->u32Magic = PDMPCIDEV_MAGIC;
863 }
864
865 rc = pPdmDev->pReg->pfnConstruct(pDevIns, 0, pDevIns->pCfg);
866 if (RT_SUCCESS(rc))
867 pDut->pDevIns = pDevIns;
868 else
869 {
870 rc = pPdmDev->pReg->pfnDestruct(pDevIns);
871 RTMemFree(pDevIns);
872 }
873 }
874 else
875 rc = VERR_NOT_FOUND;
876
877 return rc;
878}
879
880
881/**
882 * Run a given test config.
883 *
884 * @returns VBox status code.
885 * @param pDevTstCfg The test config to run.
886 */
887static int tstDevTestsRun(PCTSTDEVCFG pDevTstCfg)
888{
889 int rc = VINF_SUCCESS;
890
891 for (uint32_t i = 0; i < pDevTstCfg->cTests; i++)
892 {
893 PCTSTDEVTEST pTest = &pDevTstCfg->aTests[i];
894
895 TSTDEVDUTINT Dut;
896 Dut.pTest = pTest;
897 Dut.enmCtx = TSTDEVDUTCTX_R3;
898 Dut.pVm = NULL;
899 Dut.SupSession.pDut = &Dut;
900 Dut.Cfg.pDut = &Dut;
901 RTListInit(&Dut.LstIoPorts);
902 RTListInit(&Dut.LstTimers);
903 RTListInit(&Dut.LstMmHeap);
904 RTListInit(&Dut.LstPdmThreads);
905 RTListInit(&Dut.LstSsmHandlers);
906 RTListInit(&Dut.SupSession.LstSupSem);
907
908 rc = RTCritSectRwInit(&Dut.CritSectLists);
909 AssertRC(rc);
910
911 rc = RTCritSectInitEx(&Dut.CritSectNop.s.CritSect, RTCRITSECT_FLAGS_NOP, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "DutNop");
912 AssertRC(rc);
913
914 rc = tstDevPdmDevCreate(pDevTstCfg->pszDevName, pTest->fR0Enabled, pTest->fRCEnabled, &Dut);
915 if (RT_SUCCESS(rc))
916 {
917 /** @todo Next */
918 }
919 }
920
921 return rc;
922}
923
924
925int main(int argc, char *argv[])
926{
927 /*
928 * Init the runtime and parse the arguments.
929 */
930 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
931 int rc = RTR3InitExe(argc, &argv, 0);
932 if (RT_SUCCESS(rc))
933 {
934 RTListInit(&g_LstPlugins);
935 RTListInit(&g_LstTestcases);
936 RTListInit(&g_LstPdmMods);
937 RTListInit(&g_LstPdmDevs);
938
939 PCTSTDEVCFG pDevTstCfg = NULL;
940 rc = tstDevCfgLoad(argv[1], NULL, &pDevTstCfg);
941 if (RT_SUCCESS(rc))
942 {
943 if (pDevTstCfg->pszTstDevMod)
944 rc = tstDevLoadPlugin(pDevTstCfg->pszTstDevMod);
945 if (RT_SUCCESS(rc))
946 {
947 rc = tstDevPdmLoadMod(pDevTstCfg->pszPdmR3Mod, TSTDEVPDMMODTYPE_R3);
948 if ( RT_SUCCESS(rc)
949 && pDevTstCfg->pszPdmR0Mod)
950 rc = tstDevPdmLoadMod(pDevTstCfg->pszPdmR0Mod, TSTDEVPDMMODTYPE_R0);
951 if ( RT_SUCCESS(rc)
952 && pDevTstCfg->pszPdmRCMod)
953 rc = tstDevPdmLoadMod(pDevTstCfg->pszPdmRCMod, TSTDEVPDMMODTYPE_RC);
954
955 if (RT_SUCCESS(rc))
956 rc = tstDevTestsRun(pDevTstCfg);
957 else
958 rcExit = RTEXITCODE_FAILURE;
959 }
960 else
961 rcExit = RTEXITCODE_FAILURE;
962 }
963
964 tstDevCfgDestroy(pDevTstCfg);
965 }
966 else
967 rcExit = RTEXITCODE_FAILURE;
968
969 return rcExit;
970}
971
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