VirtualBox

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

Last change on this file since 93110 was 92124, checked in by vboxsync, 3 years ago

Devices/testcase/tstDevice: Fuzz ring-0 device handlers as well, bugref:9006

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