VirtualBox

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

Last change on this file since 76681 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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