VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use