VirtualBox

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

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

Devices/tstDevice: Testbench updates, work in progress

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