VirtualBox

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

Last change on this file since 69162 was 69162, checked in by vboxsync, 7 years ago

Devices/testcase: Skeleton for PDM device unit test framework, disabled by default (for backup purposes and to move more easily between hosts)

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