VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMDriver.cpp@ 93609

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

VMM/PDMQueue: Rewrote the queue code to not use the hyper heap and be a bit safer. Added a testcase (driverless). bugref:10093

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 86.5 KB
Line 
1/* $Id: PDMDriver.cpp 93609 2022-02-05 19:03:08Z vboxsync $ */
2/** @file
3 * PDM - Pluggable Device and Driver Manager, Driver parts.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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_PDM_DRIVER
23#include "PDMInternal.h"
24#include <VBox/vmm/pdm.h>
25#include <VBox/vmm/mm.h>
26#include <VBox/vmm/cfgm.h>
27#include <VBox/vmm/hm.h>
28#include <VBox/vmm/vmm.h>
29#include <VBox/sup.h>
30#include <VBox/vmm/vmcc.h>
31
32#include <VBox/version.h>
33#include <VBox/err.h>
34
35#include <VBox/log.h>
36#include <iprt/assert.h>
37#include <iprt/asm.h>
38#include <iprt/ctype.h>
39#include <iprt/mem.h>
40#include <iprt/thread.h>
41#include <iprt/path.h>
42#include <iprt/string.h>
43
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48/**
49 * Internal callback structure pointer.
50 *
51 * The main purpose is to define the extra data we associate
52 * with PDMDRVREGCB so we can find the VM instance and so on.
53 */
54typedef struct PDMDRVREGCBINT
55{
56 /** The callback structure. */
57 PDMDRVREGCB Core;
58 /** A bit of padding. */
59 uint32_t u32[4];
60 /** VM Handle. */
61 PVM pVM;
62 /** Pointer to the configuration node the registrations should be
63 * associated with. Can be NULL. */
64 PCFGMNODE pCfgNode;
65} PDMDRVREGCBINT, *PPDMDRVREGCBINT;
66typedef const PDMDRVREGCBINT *PCPDMDRVREGCBINT;
67
68
69/*********************************************************************************************************************************
70* Internal Functions *
71*********************************************************************************************************************************/
72static DECLCALLBACK(int) pdmR3DrvRegister(PCPDMDRVREGCB pCallbacks, PCPDMDRVREG pReg);
73static int pdmR3DrvLoad(PVM pVM, PPDMDRVREGCBINT pRegCB, const char *pszFilename, const char *pszName);
74
75
76/**
77 * Register drivers in a statically linked environment.
78 *
79 * @returns VBox status code.
80 * @param pVM The cross context VM structure.
81 * @param pfnCallback Driver registration callback
82 */
83VMMR3DECL(int) PDMR3DrvStaticRegistration(PVM pVM, FNPDMVBOXDRIVERSREGISTER pfnCallback)
84{
85 /*
86 * The registration callbacks.
87 */
88 PDMDRVREGCBINT RegCB;
89 RegCB.Core.u32Version = PDM_DRVREG_CB_VERSION;
90 RegCB.Core.pfnRegister = pdmR3DrvRegister;
91 RegCB.pVM = pVM;
92 RegCB.pCfgNode = NULL;
93
94 int rc = pfnCallback(&RegCB.Core, VBOX_VERSION);
95 if (RT_FAILURE(rc))
96 AssertMsgFailed(("VBoxDriversRegister failed with rc=%Rrc\n", rc));
97
98 return rc;
99}
100
101
102/**
103 * This function will initialize the drivers for this VM instance.
104 *
105 * First of all this mean loading the builtin drivers and letting them
106 * register themselves. Beyond that any additional driver modules are
107 * loaded and called for registration.
108 *
109 * @returns VBox status code.
110 * @param pVM The cross context VM structure.
111 */
112int pdmR3DrvInit(PVM pVM)
113{
114 LogFlow(("pdmR3DrvInit:\n"));
115
116 AssertRelease(!(RT_UOFFSETOF(PDMDRVINS, achInstanceData) & 15));
117 PPDMDRVINS pDrvInsAssert; NOREF(pDrvInsAssert);
118 AssertCompile(sizeof(pDrvInsAssert->Internal.s) <= sizeof(pDrvInsAssert->Internal.padding));
119 AssertRelease(sizeof(pDrvInsAssert->Internal.s) <= sizeof(pDrvInsAssert->Internal.padding));
120
121 /*
122 * The registration callbacks.
123 */
124 PDMDRVREGCBINT RegCB;
125 RegCB.Core.u32Version = PDM_DRVREG_CB_VERSION;
126 RegCB.Core.pfnRegister = pdmR3DrvRegister;
127 RegCB.pVM = pVM;
128 RegCB.pCfgNode = NULL;
129
130 /*
131 * Load the builtin module
132 */
133 PCFGMNODE pDriversNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "PDM/Drivers");
134 bool fLoadBuiltin;
135 int rc = CFGMR3QueryBool(pDriversNode, "LoadBuiltin", &fLoadBuiltin);
136 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
137 fLoadBuiltin = true;
138 else if (RT_FAILURE(rc))
139 {
140 AssertMsgFailed(("Configuration error: Querying boolean \"LoadBuiltin\" failed with %Rrc\n", rc));
141 return rc;
142 }
143 if (fLoadBuiltin)
144 {
145 /* make filename */
146 char *pszFilename = pdmR3FileR3("VBoxDD", true /*fShared*/);
147 if (!pszFilename)
148 return VERR_NO_TMP_MEMORY;
149 rc = pdmR3DrvLoad(pVM, &RegCB, pszFilename, "VBoxDD");
150 RTMemTmpFree(pszFilename);
151 if (RT_FAILURE(rc))
152 return rc;
153 }
154
155 /*
156 * Load additional driver modules.
157 */
158 for (PCFGMNODE pCur = CFGMR3GetFirstChild(pDriversNode); pCur; pCur = CFGMR3GetNextChild(pCur))
159 {
160 /*
161 * Get the name and path.
162 */
163 char szName[PDMMOD_NAME_LEN];
164 rc = CFGMR3GetName(pCur, &szName[0], sizeof(szName));
165 if (rc == VERR_CFGM_NOT_ENOUGH_SPACE)
166 {
167 AssertMsgFailed(("configuration error: The module name is too long, cchName=%zu.\n", CFGMR3GetNameLen(pCur)));
168 return VERR_PDM_MODULE_NAME_TOO_LONG;
169 }
170 else if (RT_FAILURE(rc))
171 {
172 AssertMsgFailed(("CFGMR3GetName -> %Rrc.\n", rc));
173 return rc;
174 }
175
176 /* the path is optional, if no path the module name + path is used. */
177 char szFilename[RTPATH_MAX];
178 rc = CFGMR3QueryString(pCur, "Path", &szFilename[0], sizeof(szFilename));
179 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
180 strcpy(szFilename, szName);
181 else if (RT_FAILURE(rc))
182 {
183 AssertMsgFailed(("configuration error: Failure to query the module path, rc=%Rrc.\n", rc));
184 return rc;
185 }
186
187 /* prepend path? */
188 if (!RTPathHavePath(szFilename))
189 {
190 char *psz = pdmR3FileR3(szFilename, false /*fShared*/);
191 if (!psz)
192 return VERR_NO_TMP_MEMORY;
193 size_t cch = strlen(psz) + 1;
194 if (cch > sizeof(szFilename))
195 {
196 RTMemTmpFree(psz);
197 AssertMsgFailed(("Filename too long! cch=%d '%s'\n", cch, psz));
198 return VERR_FILENAME_TOO_LONG;
199 }
200 memcpy(szFilename, psz, cch);
201 RTMemTmpFree(psz);
202 }
203
204 /*
205 * Load the module and register it's drivers.
206 */
207 RegCB.pCfgNode = pCur;
208 rc = pdmR3DrvLoad(pVM, &RegCB, szFilename, szName);
209 if (RT_FAILURE(rc))
210 return rc;
211 }
212
213 LogFlow(("pdmR3DrvInit: returns VINF_SUCCESS\n"));
214 return VINF_SUCCESS;
215}
216
217
218/**
219 * Loads one driver module and call the registration entry point.
220 *
221 * @returns VBox status code.
222 * @param pVM The cross context VM structure.
223 * @param pRegCB The registration callback stuff.
224 * @param pszFilename Module filename.
225 * @param pszName Module name.
226 */
227static int pdmR3DrvLoad(PVM pVM, PPDMDRVREGCBINT pRegCB, const char *pszFilename, const char *pszName)
228{
229 /*
230 * Load it.
231 */
232 int rc = pdmR3LoadR3U(pVM->pUVM, pszFilename, pszName);
233 if (RT_SUCCESS(rc))
234 {
235 /*
236 * Get the registration export and call it.
237 */
238 FNPDMVBOXDRIVERSREGISTER *pfnVBoxDriversRegister;
239 rc = PDMR3LdrGetSymbolR3(pVM, pszName, "VBoxDriversRegister", (void **)&pfnVBoxDriversRegister);
240 if (RT_SUCCESS(rc))
241 {
242 Log(("PDM: Calling VBoxDriversRegister (%p) of %s (%s)\n", pfnVBoxDriversRegister, pszName, pszFilename));
243 rc = pfnVBoxDriversRegister(&pRegCB->Core, VBOX_VERSION);
244 if (RT_SUCCESS(rc))
245 Log(("PDM: Successfully loaded driver module %s (%s).\n", pszName, pszFilename));
246 else
247 AssertMsgFailed(("VBoxDriversRegister failed with rc=%Rrc\n", rc));
248 }
249 else
250 {
251 AssertMsgFailed(("Failed to locate 'VBoxDriversRegister' in %s (%s) rc=%Rrc\n", pszName, pszFilename, rc));
252 if (rc == VERR_SYMBOL_NOT_FOUND)
253 rc = VERR_PDM_NO_REGISTRATION_EXPORT;
254 }
255 }
256 else
257 AssertMsgFailed(("Failed to load %s (%s) rc=%Rrc!\n", pszName, pszFilename, rc));
258 return rc;
259}
260
261
262/** @interface_method_impl{PDMDRVREGCB,pfnRegister} */
263static DECLCALLBACK(int) pdmR3DrvRegister(PCPDMDRVREGCB pCallbacks, PCPDMDRVREG pReg)
264{
265 /*
266 * Validate the registration structure.
267 */
268 AssertPtrReturn(pReg, VERR_INVALID_POINTER);
269 AssertMsgReturn(pReg->u32Version == PDM_DRVREG_VERSION,
270 ("%#x\n", pReg->u32Version),
271 VERR_PDM_UNKNOWN_DRVREG_VERSION);
272 AssertReturn(pReg->szName[0], VERR_PDM_INVALID_DRIVER_REGISTRATION);
273 AssertMsgReturn(RTStrEnd(pReg->szName, sizeof(pReg->szName)),
274 ("%.*s\n", sizeof(pReg->szName), pReg->szName),
275 VERR_PDM_INVALID_DRIVER_REGISTRATION);
276 AssertMsgReturn(pdmR3IsValidName(pReg->szName), ("%.*s\n", sizeof(pReg->szName), pReg->szName),
277 VERR_PDM_INVALID_DRIVER_REGISTRATION);
278 AssertMsgReturn( !(pReg->fFlags & PDM_DRVREG_FLAGS_R0)
279 || ( pReg->szR0Mod[0]
280 && RTStrEnd(pReg->szR0Mod, sizeof(pReg->szR0Mod))),
281 ("%s: %.*s\n", pReg->szName, sizeof(pReg->szR0Mod), pReg->szR0Mod),
282 VERR_PDM_INVALID_DRIVER_REGISTRATION);
283 AssertMsgReturn( !(pReg->fFlags & PDM_DRVREG_FLAGS_RC)
284 || ( pReg->szRCMod[0]
285 && RTStrEnd(pReg->szRCMod, sizeof(pReg->szRCMod))),
286 ("%s: %.*s\n", pReg->szName, sizeof(pReg->szRCMod), pReg->szRCMod),
287 VERR_PDM_INVALID_DRIVER_REGISTRATION);
288 AssertMsgReturn(RT_VALID_PTR(pReg->pszDescription),
289 ("%s: %p\n", pReg->szName, pReg->pszDescription),
290 VERR_PDM_INVALID_DRIVER_REGISTRATION);
291 AssertMsgReturn(!(pReg->fFlags & ~(PDM_DRVREG_FLAGS_HOST_BITS_MASK | PDM_DRVREG_FLAGS_R0 | PDM_DRVREG_FLAGS_RC)),
292 ("%s: %#x\n", pReg->szName, pReg->fFlags),
293 VERR_PDM_INVALID_DRIVER_REGISTRATION);
294 AssertMsgReturn((pReg->fFlags & PDM_DRVREG_FLAGS_HOST_BITS_MASK) == PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
295 ("%s: %#x\n", pReg->szName, pReg->fFlags),
296 VERR_PDM_INVALID_DRIVER_HOST_BITS);
297 AssertMsgReturn(pReg->cMaxInstances > 0,
298 ("%s: %#x\n", pReg->szName, pReg->cMaxInstances),
299 VERR_PDM_INVALID_DRIVER_REGISTRATION);
300 AssertMsgReturn(pReg->cbInstance <= _1M,
301 ("%s: %#x\n", pReg->szName, pReg->cbInstance),
302 VERR_PDM_INVALID_DRIVER_REGISTRATION);
303 AssertMsgReturn(RT_VALID_PTR(pReg->pfnConstruct),
304 ("%s: %p\n", pReg->szName, pReg->pfnConstruct),
305 VERR_PDM_INVALID_DRIVER_REGISTRATION);
306 AssertMsgReturn(RT_VALID_PTR(pReg->pfnRelocate) || !(pReg->fFlags & PDM_DRVREG_FLAGS_RC),
307 ("%s: %#x\n", pReg->szName, pReg->cbInstance),
308 VERR_PDM_INVALID_DRIVER_REGISTRATION);
309 AssertMsgReturn(pReg->pfnSoftReset == NULL,
310 ("%s: %p\n", pReg->szName, pReg->pfnSoftReset),
311 VERR_PDM_INVALID_DRIVER_REGISTRATION);
312 AssertMsgReturn(pReg->u32VersionEnd == PDM_DRVREG_VERSION,
313 ("%s: %#x\n", pReg->szName, pReg->u32VersionEnd),
314 VERR_PDM_INVALID_DRIVER_REGISTRATION);
315
316 /*
317 * Check for duplicate and find FIFO entry at the same time.
318 */
319 PCPDMDRVREGCBINT pRegCB = (PCPDMDRVREGCBINT)pCallbacks;
320 PPDMDRV pDrvPrev = NULL;
321 PPDMDRV pDrv = pRegCB->pVM->pdm.s.pDrvs;
322 for (; pDrv; pDrvPrev = pDrv, pDrv = pDrv->pNext)
323 {
324 if (!strcmp(pDrv->pReg->szName, pReg->szName))
325 {
326 AssertMsgFailed(("Driver '%s' already exists\n", pReg->szName));
327 return VERR_PDM_DRIVER_NAME_CLASH;
328 }
329 }
330
331 /*
332 * Allocate new driver structure and insert it into the list.
333 */
334 int rc;
335 pDrv = (PPDMDRV)MMR3HeapAlloc(pRegCB->pVM, MM_TAG_PDM_DRIVER, sizeof(*pDrv));
336 if (pDrv)
337 {
338 pDrv->pNext = NULL;
339 pDrv->cInstances = 0;
340 pDrv->iNextInstance = 0;
341 pDrv->pReg = pReg;
342 rc = CFGMR3QueryStringAllocDef( pRegCB->pCfgNode, "RCSearchPath", &pDrv->pszRCSearchPath, NULL);
343 if (RT_SUCCESS(rc))
344 rc = CFGMR3QueryStringAllocDef(pRegCB->pCfgNode, "R0SearchPath", &pDrv->pszR0SearchPath, NULL);
345 if (RT_SUCCESS(rc))
346 {
347 if (pDrvPrev)
348 pDrvPrev->pNext = pDrv;
349 else
350 pRegCB->pVM->pdm.s.pDrvs = pDrv;
351 Log(("PDM: Registered driver '%s'\n", pReg->szName));
352 return VINF_SUCCESS;
353 }
354 MMR3HeapFree(pDrv);
355 }
356 else
357 rc = VERR_NO_MEMORY;
358 return rc;
359}
360
361
362/**
363 * Lookups a driver structure by name.
364 * @internal
365 */
366PPDMDRV pdmR3DrvLookup(PVM pVM, const char *pszName)
367{
368 for (PPDMDRV pDrv = pVM->pdm.s.pDrvs; pDrv; pDrv = pDrv->pNext)
369 if (!strcmp(pDrv->pReg->szName, pszName))
370 return pDrv;
371 return NULL;
372}
373
374
375/**
376 * Transforms the driver chain as it's being instantiated.
377 *
378 * Worker for pdmR3DrvInstantiate.
379 *
380 * @returns VBox status code.
381 * @param pVM The cross context VM structure.
382 * @param pDrvAbove The driver above, NULL if top.
383 * @param pLun The LUN.
384 * @param ppNode The AttachedDriver node, replaced if any
385 * morphing took place.
386 */
387static int pdmR3DrvMaybeTransformChain(PVM pVM, PPDMDRVINS pDrvAbove, PPDMLUN pLun, PCFGMNODE *ppNode)
388{
389 /*
390 * The typical state of affairs is that there are no injections.
391 */
392 PCFGMNODE pCurTrans = CFGMR3GetFirstChild(CFGMR3GetChild(CFGMR3GetRoot(pVM), "PDM/DriverTransformations"));
393 if (!pCurTrans)
394 return VINF_SUCCESS;
395
396 /*
397 * Gather the attributes used in the matching process.
398 */
399 const char *pszDevice = pLun->pDevIns
400 ? pLun->pDevIns->Internal.s.pDevR3->pReg->szName
401 : pLun->pUsbIns->Internal.s.pUsbDev->pReg->szName;
402 char szLun[32];
403 RTStrPrintf(szLun, sizeof(szLun), "%u", pLun->iLun);
404 const char *pszAbove = pDrvAbove ? pDrvAbove->Internal.s.pDrv->pReg->szName : "<top>";
405 char *pszThisDrv;
406 int rc = CFGMR3QueryStringAlloc(*ppNode, "Driver", &pszThisDrv);
407 AssertMsgRCReturn(rc, ("Query for string value of \"Driver\" -> %Rrc\n", rc),
408 rc == VERR_CFGM_VALUE_NOT_FOUND ? VERR_PDM_CFG_MISSING_DRIVER_NAME : rc);
409
410 uint64_t uInjectTransformationAbove = 0;
411 if (pDrvAbove)
412 {
413 rc = CFGMR3QueryIntegerDef(CFGMR3GetParent(*ppNode), "InjectTransformationPtr", &uInjectTransformationAbove, 0);
414 AssertLogRelRCReturn(rc, rc);
415 }
416
417
418 /*
419 * Enumerate possible driver chain transformations.
420 */
421 unsigned cTransformations = 0;
422 for (; pCurTrans != NULL; pCurTrans = CFGMR3GetNextChild(pCurTrans))
423 {
424 char szCurTransNm[256];
425 rc = CFGMR3GetName(pCurTrans, szCurTransNm, sizeof(szCurTransNm));
426 AssertLogRelRCReturn(rc, rc);
427
428 /** @cfgm{/PDM/DriverTransformations/&lt;name&gt;/Device,string,*}
429 * One or more simple wildcard patters separated by '|' for matching
430 * the devices this transformation rule applies to. */
431 char *pszMultiPat;
432 rc = CFGMR3QueryStringAllocDef(pCurTrans, "Device", &pszMultiPat, "*");
433 AssertLogRelRCReturn(rc, rc);
434 bool fMatch = RTStrSimplePatternMultiMatch(pszMultiPat, RTSTR_MAX, pszDevice, RTSTR_MAX, NULL);
435 MMR3HeapFree(pszMultiPat);
436 if (!fMatch)
437 continue;
438
439 /** @cfgm{/PDM/DriverTransformations/&lt;name&gt;/LUN,string,*}
440 * One or more simple wildcard patters separated by '|' for matching
441 * the LUNs this transformation rule applies to. */
442 rc = CFGMR3QueryStringAllocDef(pCurTrans, "LUN", &pszMultiPat, "*");
443 AssertLogRelRCReturn(rc, rc);
444 fMatch = RTStrSimplePatternMultiMatch(pszMultiPat, RTSTR_MAX, szLun, RTSTR_MAX, NULL);
445 MMR3HeapFree(pszMultiPat);
446 if (!fMatch)
447 continue;
448
449 /** @cfgm{/PDM/DriverTransformations/&lt;name&gt;/BelowDriver,string,*}
450 * One or more simple wildcard patters separated by '|' for matching the
451 * drivers the transformation should be applied below. This means, that
452 * when the drivers matched here attached another driver below them, the
453 * transformation will be applied. To represent the device, '&lt;top&gt;'
454 * is used. */
455 rc = CFGMR3QueryStringAllocDef(pCurTrans, "BelowDriver", &pszMultiPat, "*");
456 AssertLogRelRCReturn(rc, rc);
457 fMatch = RTStrSimplePatternMultiMatch(pszMultiPat, RTSTR_MAX, pszAbove, RTSTR_MAX, NULL);
458 MMR3HeapFree(pszMultiPat);
459 if (!fMatch)
460 continue;
461
462 /** @cfgm{/PDM/DriverTransformations/&lt;name&gt;/AboveDriver,string,*}
463 * One or more simple wildcard patters separated by '|' for matching the
464 * drivers the transformation should be applie above or at (depending on
465 * the action). The value being matched against here is the driver that
466 * is in the process of being attached, so for mergeconfig actions this is
467 * usually what you need to match on. */
468 rc = CFGMR3QueryStringAlloc(pCurTrans, "AboveDriver", &pszMultiPat);
469 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
470 rc = VINF_SUCCESS;
471 else
472 {
473 AssertLogRelRCReturn(rc, rc);
474 fMatch = RTStrSimplePatternMultiMatch(pszMultiPat, RTSTR_MAX, pszThisDrv, RTSTR_MAX, NULL);
475 MMR3HeapFree(pszMultiPat);
476 if (!fMatch)
477 continue;
478 if (uInjectTransformationAbove == (uintptr_t)pCurTrans)
479 continue;
480 }
481
482 /*
483 * We've got a match! Now, what are we supposed to do?
484 */
485 /** @cfgm{/PDM/DriverTransformations/&lt;name&gt;/Action,string,inject}
486 * The action that the transformation takes. Possible values are:
487 * - inject
488 * - mergeconfig: This merges and the content of the 'Config' key under the
489 * transformation into the driver's own 'Config' key, replacing any
490 * duplicates.
491 * - remove
492 * - removetree
493 * - replace
494 * - replacetree
495 */
496 char szAction[16];
497 rc = CFGMR3QueryStringDef(pCurTrans, "Action", szAction, sizeof(szAction), "inject");
498 AssertLogRelRCReturn(rc, rc);
499 AssertLogRelMsgReturn( !strcmp(szAction, "inject")
500 || !strcmp(szAction, "mergeconfig")
501 || !strcmp(szAction, "remove")
502 || !strcmp(szAction, "removetree")
503 || !strcmp(szAction, "replace")
504 || !strcmp(szAction, "replacetree")
505 ,
506 ("Action='%s', valid values are 'inject', 'mergeconfig', 'replace', 'replacetree', 'remove', 'removetree'.\n", szAction),
507 VERR_PDM_MISCONFIGURED_DRV_TRANSFORMATION);
508 LogRel(("PDMDriver: Applying '%s' to '%s'::[%s]...'%s': %s\n", szCurTransNm, pszDevice, szLun, pszThisDrv, szAction));
509 CFGMR3Dump(*ppNode);
510 CFGMR3Dump(pCurTrans);
511
512 /* Get the attached driver to inject. */
513 PCFGMNODE pTransAttDrv = NULL;
514 if (!strcmp(szAction, "inject") || !strcmp(szAction, "replace") || !strcmp(szAction, "replacetree"))
515 {
516 pTransAttDrv = CFGMR3GetChild(pCurTrans, "AttachedDriver");
517 AssertLogRelMsgReturn(pTransAttDrv,
518 ("An %s transformation requires an AttachedDriver child node!\n", szAction),
519 VERR_PDM_MISCONFIGURED_DRV_TRANSFORMATION);
520 }
521
522
523 /*
524 * Remove the node.
525 */
526 if (!strcmp(szAction, "remove") || !strcmp(szAction, "removetree"))
527 {
528 PCFGMNODE pBelowThis = CFGMR3GetChild(*ppNode, "AttachedDriver");
529 if (!pBelowThis || !strcmp(szAction, "removetree"))
530 {
531 CFGMR3RemoveNode(*ppNode);
532 *ppNode = NULL;
533 }
534 else
535 {
536 PCFGMNODE pBelowThisCopy;
537 rc = CFGMR3DuplicateSubTree(pBelowThis, &pBelowThisCopy);
538 AssertLogRelRCReturn(rc, rc);
539
540 rc = CFGMR3ReplaceSubTree(*ppNode, pBelowThisCopy);
541 AssertLogRelRCReturnStmt(rc, CFGMR3RemoveNode(pBelowThis), rc);
542 }
543 }
544 /*
545 * Replace the driver about to be instantiated.
546 */
547 else if (!strcmp(szAction, "replace") || !strcmp(szAction, "replacetree"))
548 {
549 PCFGMNODE pTransCopy;
550 rc = CFGMR3DuplicateSubTree(pTransAttDrv, &pTransCopy);
551 AssertLogRelRCReturn(rc, rc);
552
553 PCFGMNODE pBelowThis = CFGMR3GetChild(*ppNode, "AttachedDriver");
554 if (!pBelowThis || !strcmp(szAction, "replacetree"))
555 rc = VINF_SUCCESS;
556 else
557 {
558 PCFGMNODE pBelowThisCopy;
559 rc = CFGMR3DuplicateSubTree(pBelowThis, &pBelowThisCopy);
560 if (RT_SUCCESS(rc))
561 {
562 rc = CFGMR3InsertSubTree(pTransCopy, "AttachedDriver", pBelowThisCopy, NULL);
563 AssertLogRelRC(rc);
564 if (RT_FAILURE(rc))
565 CFGMR3RemoveNode(pBelowThisCopy);
566 }
567 }
568 if (RT_SUCCESS(rc))
569 rc = CFGMR3ReplaceSubTree(*ppNode, pTransCopy);
570 if (RT_FAILURE(rc))
571 CFGMR3RemoveNode(pTransCopy);
572 }
573 /*
574 * Inject a driver before the driver about to be instantiated.
575 */
576 else if (!strcmp(szAction, "inject"))
577 {
578 PCFGMNODE pTransCopy;
579 rc = CFGMR3DuplicateSubTree(pTransAttDrv, &pTransCopy);
580 AssertLogRelRCReturn(rc, rc);
581
582 PCFGMNODE pThisCopy;
583 rc = CFGMR3DuplicateSubTree(*ppNode, &pThisCopy);
584 if (RT_SUCCESS(rc))
585 {
586 rc = CFGMR3InsertSubTree(pTransCopy, "AttachedDriver", pThisCopy, NULL);
587 if (RT_SUCCESS(rc))
588 {
589 rc = CFGMR3InsertInteger(pTransCopy, "InjectTransformationPtr", (uintptr_t)pCurTrans);
590 AssertLogRelRC(rc);
591 rc = CFGMR3InsertString(pTransCopy, "InjectTransformationNm", szCurTransNm);
592 AssertLogRelRC(rc);
593 if (RT_SUCCESS(rc))
594 rc = CFGMR3ReplaceSubTree(*ppNode, pTransCopy);
595 }
596 else
597 {
598 AssertLogRelRC(rc);
599 CFGMR3RemoveNode(pThisCopy);
600 }
601 }
602 if (RT_FAILURE(rc))
603 CFGMR3RemoveNode(pTransCopy);
604 }
605 /*
606 * Merge the Config node of the transformation with the one of the
607 * current driver.
608 */
609 else if (!strcmp(szAction, "mergeconfig"))
610 {
611 PCFGMNODE pTransConfig = CFGMR3GetChild(pCurTrans, "Config");
612 AssertLogRelReturn(pTransConfig, VERR_PDM_MISCONFIGURED_DRV_TRANSFORMATION);
613
614 PCFGMNODE pDrvConfig = CFGMR3GetChild(*ppNode, "Config");
615 if (*ppNode)
616 CFGMR3InsertNode(*ppNode, "Config", &pDrvConfig);
617 AssertLogRelReturn(pDrvConfig, VERR_PDM_CANNOT_TRANSFORM_REMOVED_DRIVER);
618
619 rc = CFGMR3CopyTree(pDrvConfig, pTransConfig, CFGM_COPY_FLAGS_REPLACE_VALUES | CFGM_COPY_FLAGS_MERGE_KEYS);
620 AssertLogRelRCReturn(rc, rc);
621 }
622 else
623 AssertFailed();
624
625 cTransformations++;
626 if (*ppNode)
627 CFGMR3Dump(*ppNode);
628 else
629 LogRel(("PDMDriver: The transformation removed the driver.\n"));
630 }
631
632 /*
633 * Note what happened in the release log.
634 */
635 if (cTransformations > 0)
636 LogRel(("PDMDriver: Transformations done. Applied %u driver transformations.\n", cTransformations));
637
638 return rc;
639}
640
641
642/**
643 * Instantiate a driver.
644 *
645 * @returns VBox status code, including informational statuses.
646 *
647 * @param pVM The cross context VM structure.
648 * @param pNode The CFGM node for the driver.
649 * @param pBaseInterface The base interface.
650 * @param pDrvAbove The driver above it. NULL if it's the top-most
651 * driver.
652 * @param pLun The LUN the driver is being attached to. NULL
653 * if we're instantiating a driver chain before
654 * attaching it - untested.
655 * @param ppBaseInterface Where to return the pointer to the base
656 * interface of the newly created driver.
657 *
658 * @remarks Recursive calls to this function is normal as the drivers will
659 * attach to anything below them during the pfnContruct call.
660 *
661 * @todo Need to extend this interface a bit so that the driver
662 * transformation feature can attach drivers to unconfigured LUNs and
663 * at the end of chains.
664 */
665int pdmR3DrvInstantiate(PVM pVM, PCFGMNODE pNode, PPDMIBASE pBaseInterface, PPDMDRVINS pDrvAbove,
666 PPDMLUN pLun, PPDMIBASE *ppBaseInterface)
667{
668 Assert(!pDrvAbove || !pDrvAbove->Internal.s.pDown);
669 Assert(!pDrvAbove || !pDrvAbove->pDownBase);
670
671 Assert(pBaseInterface->pfnQueryInterface(pBaseInterface, PDMIBASE_IID) == pBaseInterface);
672
673 /*
674 * Do driver chain injections
675 */
676 int rc = pdmR3DrvMaybeTransformChain(pVM, pDrvAbove, pLun, &pNode);
677 if (RT_FAILURE(rc))
678 return rc;
679 if (!pNode)
680 return VERR_PDM_NO_ATTACHED_DRIVER;
681
682 /*
683 * Find the driver.
684 */
685 char *pszName;
686 rc = CFGMR3QueryStringAlloc(pNode, "Driver", &pszName);
687 if (RT_SUCCESS(rc))
688 {
689 PPDMDRV pDrv = pdmR3DrvLookup(pVM, pszName);
690 if ( pDrv
691 && pDrv->cInstances < pDrv->pReg->cMaxInstances)
692 {
693 /* config node */
694 PCFGMNODE pConfigNode = CFGMR3GetChild(pNode, "Config");
695 if (!pConfigNode)
696 rc = CFGMR3InsertNode(pNode, "Config", &pConfigNode);
697 if (RT_SUCCESS(rc))
698 {
699 CFGMR3SetRestrictedRoot(pConfigNode);
700
701 /*
702 * Allocate the driver instance.
703 */
704 size_t cb = RT_UOFFSETOF_DYN(PDMDRVINS, achInstanceData[pDrv->pReg->cbInstance]);
705 cb = RT_ALIGN_Z(cb, 16);
706 bool const fHyperHeap = !!(pDrv->pReg->fFlags & (PDM_DRVREG_FLAGS_R0 | PDM_DRVREG_FLAGS_RC));
707 PPDMDRVINS pNew;
708 if (fHyperHeap)
709 rc = MMHyperAlloc(pVM, cb, 64, MM_TAG_PDM_DRIVER, (void **)&pNew);
710 else
711 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_DRIVER, cb, (void **)&pNew);
712 if (RT_SUCCESS(rc))
713 {
714 /*
715 * Initialize the instance structure (declaration order).
716 */
717 pNew->u32Version = PDM_DRVINS_VERSION;
718 pNew->iInstance = pDrv->iNextInstance;
719 pNew->Internal.s.pUp = pDrvAbove ? pDrvAbove : NULL;
720 //pNew->Internal.s.pDown = NULL;
721 pNew->Internal.s.pLun = pLun;
722 pNew->Internal.s.pDrv = pDrv;
723 pNew->Internal.s.pVMR3 = pVM;
724 pNew->Internal.s.pVMR0 = pDrv->pReg->fFlags & PDM_DRVREG_FLAGS_R0 ? pVM->pVMR0ForCall : NIL_RTR0PTR;
725 pNew->Internal.s.pVMRC = pDrv->pReg->fFlags & PDM_DRVREG_FLAGS_RC ? pVM->pVMRC : NIL_RTRCPTR;
726 //pNew->Internal.s.fDetaching = false;
727 pNew->Internal.s.fVMSuspended = true; /** @todo should be 'false', if driver is attached at runtime. */
728 //pNew->Internal.s.fVMReset = false;
729 pNew->Internal.s.fHyperHeap = fHyperHeap;
730 //pNew->Internal.s.pfnAsyncNotify = NULL;
731 pNew->Internal.s.pCfgHandle = pNode;
732 pNew->pReg = pDrv->pReg;
733 pNew->pCfg = pConfigNode;
734 pNew->pUpBase = pBaseInterface;
735 Assert(!pDrvAbove || pBaseInterface == &pDrvAbove->IBase);
736 //pNew->pDownBase = NULL;
737 //pNew->IBase.pfnQueryInterface = NULL;
738 //pNew->fTracing = 0;
739 pNew->idTracing = ++pVM->pdm.s.idTracingOther;
740 pNew->pHlpR3 = &g_pdmR3DrvHlp;
741 pNew->pvInstanceDataR3 = &pNew->achInstanceData[0];
742 if (pDrv->pReg->fFlags & PDM_DRVREG_FLAGS_R0)
743 {
744 pNew->pvInstanceDataR0 = MMHyperR3ToR0(pVM, &pNew->achInstanceData[0]);
745 rc = PDMR3LdrGetSymbolR0(pVM, NULL, "g_pdmR0DrvHlp", &pNew->pHlpR0);
746 AssertReleaseRCReturn(rc, rc);
747 }
748#ifdef VBOX_WITH_RAW_MODE_KEEP
749 if ( (pDrv->pReg->fFlags & PDM_DRVREG_FLAGS_RC)
750 && VM_IS_RAW_MODE_ENABLED(pVM))
751 {
752 pNew->pvInstanceDataR0 = MMHyperR3ToRC(pVM, &pNew->achInstanceData[0]);
753 rc = PDMR3LdrGetSymbolRC(pVM, NULL, "g_pdmRCDrvHlp", &pNew->pHlpRC);
754 AssertReleaseRCReturn(rc, rc);
755 }
756#endif
757
758 pDrv->iNextInstance++;
759 pDrv->cInstances++;
760
761 /*
762 * Link with it with the driver above / LUN.
763 */
764 if (pDrvAbove)
765 {
766 pDrvAbove->pDownBase = &pNew->IBase;
767 pDrvAbove->Internal.s.pDown = pNew;
768 }
769 else if (pLun)
770 pLun->pTop = pNew;
771 if (pLun)
772 pLun->pBottom = pNew;
773
774 /*
775 * Invoke the constructor.
776 */
777 rc = pDrv->pReg->pfnConstruct(pNew, pNew->pCfg, 0 /*fFlags*/);
778 if (RT_SUCCESS(rc))
779 {
780 AssertPtr(pNew->IBase.pfnQueryInterface);
781 Assert(pNew->IBase.pfnQueryInterface(&pNew->IBase, PDMIBASE_IID) == &pNew->IBase);
782
783 /* Success! */
784 *ppBaseInterface = &pNew->IBase;
785 if (pLun)
786 Log(("PDM: Attached driver %p:'%s'/%d to LUN#%d on device '%s'/%d, pDrvAbove=%p:'%s'/%d\n",
787 pNew, pDrv->pReg->szName, pNew->iInstance,
788 pLun->iLun,
789 pLun->pDevIns ? pLun->pDevIns->pReg->szName : pLun->pUsbIns->pReg->szName,
790 pLun->pDevIns ? pLun->pDevIns->iInstance : pLun->pUsbIns->iInstance,
791 pDrvAbove, pDrvAbove ? pDrvAbove->pReg->szName : "", pDrvAbove ? pDrvAbove->iInstance : UINT32_MAX));
792 else
793 Log(("PDM: Attached driver %p:'%s'/%d, pDrvAbove=%p:'%s'/%d\n",
794 pNew, pDrv->pReg->szName, pNew->iInstance,
795 pDrvAbove, pDrvAbove ? pDrvAbove->pReg->szName : "", pDrvAbove ? pDrvAbove->iInstance : UINT32_MAX));
796 }
797 else
798 {
799 pdmR3DrvDestroyChain(pNew, PDM_TACH_FLAGS_NO_CALLBACKS);
800 if (rc == VERR_VERSION_MISMATCH)
801 rc = VERR_PDM_DRIVER_VERSION_MISMATCH;
802 }
803 }
804 else
805 AssertMsgFailed(("Failed to allocate %d bytes for instantiating driver '%s'! rc=%Rrc\n", cb, pszName, rc));
806 }
807 else
808 AssertMsgFailed(("Failed to create Config node! rc=%Rrc\n", rc));
809 }
810 else if (pDrv)
811 {
812 AssertMsgFailed(("Too many instances of driver '%s', max is %u\n", pszName, pDrv->pReg->cMaxInstances));
813 rc = VERR_PDM_TOO_MANY_DRIVER_INSTANCES;
814 }
815 else
816 {
817 AssertMsgFailed(("Driver '%s' wasn't found!\n", pszName));
818 rc = VERR_PDM_DRIVER_NOT_FOUND;
819 }
820 MMR3HeapFree(pszName);
821 }
822 else
823 {
824 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
825 rc = VERR_PDM_CFG_MISSING_DRIVER_NAME;
826 else
827 AssertMsgFailed(("Query for string value of \"Driver\" -> %Rrc\n", rc));
828 }
829 return rc;
830}
831
832
833/**
834 * Detaches a driver from whatever it's attached to.
835 * This will of course lead to the destruction of the driver and all drivers below it in the chain.
836 *
837 * @returns VINF_SUCCESS
838 * @param pDrvIns The driver instance to detach.
839 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
840 */
841int pdmR3DrvDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
842{
843 PDMDRV_ASSERT_DRVINS(pDrvIns);
844 LogFlow(("pdmR3DrvDetach: pDrvIns=%p '%s'/%d\n", pDrvIns, pDrvIns->pReg->szName, pDrvIns->iInstance));
845 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
846
847 /*
848 * Check that we're not doing this recursively, that could have unwanted sideeffects!
849 */
850 if (pDrvIns->Internal.s.fDetaching)
851 {
852 AssertMsgFailed(("Recursive detach! '%s'/%d\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
853 return VINF_SUCCESS;
854 }
855
856 /*
857 * Check that we actually can detach this instance.
858 * The requirement is that the driver/device above has a detach method.
859 */
860 if ( pDrvIns->Internal.s.pUp
861 ? !pDrvIns->Internal.s.pUp->pReg->pfnDetach
862 : pDrvIns->Internal.s.pLun->pDevIns
863 ? !pDrvIns->Internal.s.pLun->pDevIns->pReg->pfnDetach
864 : !pDrvIns->Internal.s.pLun->pUsbIns->pReg->pfnDriverDetach
865 )
866 {
867 AssertMsgFailed(("Cannot detach driver instance because the driver/device above doesn't support it!\n"));
868 return VERR_PDM_DRIVER_DETACH_NOT_POSSIBLE;
869 }
870
871 /*
872 * Join paths with pdmR3DrvDestroyChain.
873 */
874 pdmR3DrvDestroyChain(pDrvIns, fFlags);
875 return VINF_SUCCESS;
876}
877
878
879/**
880 * Destroys a driver chain starting with the specified driver.
881 *
882 * This is used when unplugging a device at run time.
883 *
884 * @param pDrvIns Pointer to the driver instance to start with.
885 * @param fFlags PDM_TACH_FLAGS_NOT_HOT_PLUG, PDM_TACH_FLAGS_NO_CALLBACKS
886 * or 0.
887 */
888void pdmR3DrvDestroyChain(PPDMDRVINS pDrvIns, uint32_t fFlags)
889{
890 PVM pVM = pDrvIns->Internal.s.pVMR3;
891 VM_ASSERT_EMT(pVM);
892
893 /*
894 * Detach the bottommost driver until we've detached pDrvIns.
895 */
896 pDrvIns->Internal.s.fDetaching = true;
897 PPDMDRVINS pCur;
898 do
899 {
900 /* find the driver to detach. */
901 pCur = pDrvIns;
902 while (pCur->Internal.s.pDown)
903 pCur = pCur->Internal.s.pDown;
904 LogFlow(("pdmR3DrvDestroyChain: pCur=%p '%s'/%d\n", pCur, pCur->pReg->szName, pCur->iInstance));
905
906 /*
907 * Unlink it and notify parent.
908 */
909 pCur->Internal.s.fDetaching = true;
910
911 PPDMLUN pLun = pCur->Internal.s.pLun;
912 Assert(pLun->pBottom == pCur);
913 pLun->pBottom = pCur->Internal.s.pUp;
914
915 if (pCur->Internal.s.pUp)
916 {
917 /* driver parent */
918 PPDMDRVINS pParent = pCur->Internal.s.pUp;
919 pCur->Internal.s.pUp = NULL;
920 pParent->Internal.s.pDown = NULL;
921
922 if (!(fFlags & PDM_TACH_FLAGS_NO_CALLBACKS) && pParent->pReg->pfnDetach)
923 pParent->pReg->pfnDetach(pParent, fFlags);
924
925 pParent->pDownBase = NULL;
926 }
927 else
928 {
929 /* device parent */
930 Assert(pLun->pTop == pCur);
931 pLun->pTop = NULL;
932 if (!(fFlags & PDM_TACH_FLAGS_NO_CALLBACKS))
933 {
934 if (pLun->pDevIns)
935 {
936 if (pLun->pDevIns->pReg->pfnDetach)
937 {
938 PDMCritSectEnter(pVM, pLun->pDevIns->pCritSectRoR3, VERR_IGNORED);
939 pLun->pDevIns->pReg->pfnDetach(pLun->pDevIns, pLun->iLun, fFlags);
940 PDMCritSectLeave(pVM, pLun->pDevIns->pCritSectRoR3);
941 }
942 }
943 else
944 {
945 if (pLun->pUsbIns->pReg->pfnDriverDetach)
946 {
947 /** @todo USB device locking? */
948 pLun->pUsbIns->pReg->pfnDriverDetach(pLun->pUsbIns, pLun->iLun, fFlags);
949 }
950 }
951 }
952 }
953
954 /*
955 * Call destructor.
956 */
957 pCur->pUpBase = NULL;
958 if (pCur->pReg->pfnDestruct)
959 pCur->pReg->pfnDestruct(pCur);
960 pCur->Internal.s.pDrv->cInstances--;
961
962 /*
963 * Free all resources allocated by the driver.
964 */
965 /* Queues. */
966 int rc = PDMR3QueueDestroyDriver(pVM, pCur);
967 AssertRC(rc);
968
969 /* Timers. */
970 rc = TMR3TimerDestroyDriver(pVM, pCur);
971 AssertRC(rc);
972
973 /* SSM data units. */
974 rc = SSMR3DeregisterDriver(pVM, pCur, NULL, 0);
975 AssertRC(rc);
976
977 /* PDM threads. */
978 rc = pdmR3ThreadDestroyDriver(pVM, pCur);
979 AssertRC(rc);
980
981 /* Info handlers. */
982 rc = DBGFR3InfoDeregisterDriver(pVM, pCur, NULL);
983 AssertRC(rc);
984
985 /* PDM critsects. */
986 rc = pdmR3CritSectBothDeleteDriver(pVM, pCur);
987 AssertRC(rc);
988
989 /* Block caches. */
990 PDMR3BlkCacheReleaseDriver(pVM, pCur);
991
992#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
993 /* Completion templates.*/
994 pdmR3AsyncCompletionTemplateDestroyDriver(pVM, pCur);
995#endif
996
997 /* Finally, the driver it self. */
998 bool fHyperHeap = pCur->Internal.s.fHyperHeap;
999 ASMMemFill32(pCur, RT_UOFFSETOF_DYN(PDMDRVINS, achInstanceData[pCur->pReg->cbInstance]), 0xdeadd0d0);
1000 if (fHyperHeap)
1001 MMHyperFree(pVM, pCur);
1002 else
1003 MMR3HeapFree(pCur);
1004
1005 } while (pCur != pDrvIns);
1006}
1007
1008
1009
1010
1011/** @name Driver Helpers
1012 * @{
1013 */
1014
1015/** @interface_method_impl{PDMDRVHLPR3,pfnAttach} */
1016static DECLCALLBACK(int) pdmR3DrvHlp_Attach(PPDMDRVINS pDrvIns, uint32_t fFlags, PPDMIBASE *ppBaseInterface)
1017{
1018 PDMDRV_ASSERT_DRVINS(pDrvIns);
1019 PVM pVM = pDrvIns->Internal.s.pVMR3;
1020 VM_ASSERT_EMT(pVM);
1021 LogFlow(("pdmR3DrvHlp_Attach: caller='%s'/%d: fFlags=%#x\n", pDrvIns->pReg->szName, pDrvIns->iInstance, fFlags));
1022 Assert(!(fFlags & ~(PDM_TACH_FLAGS_NOT_HOT_PLUG)));
1023 RT_NOREF_PV(fFlags);
1024
1025 /*
1026 * Check that there isn't anything attached already.
1027 */
1028 int rc;
1029 if (!pDrvIns->Internal.s.pDown)
1030 {
1031 Assert(pDrvIns->Internal.s.pLun->pBottom == pDrvIns);
1032
1033 /*
1034 * Get the attached driver configuration.
1035 */
1036 PCFGMNODE pNode = CFGMR3GetChild(pDrvIns->Internal.s.pCfgHandle, "AttachedDriver");
1037 if (pNode)
1038 rc = pdmR3DrvInstantiate(pVM, pNode, &pDrvIns->IBase, pDrvIns, pDrvIns->Internal.s.pLun, ppBaseInterface);
1039 else
1040 rc = VERR_PDM_NO_ATTACHED_DRIVER;
1041 }
1042 else
1043 {
1044 AssertMsgFailed(("Already got a driver attached. The driver should keep track of such things!\n"));
1045 rc = VERR_PDM_DRIVER_ALREADY_ATTACHED;
1046 }
1047
1048 LogFlow(("pdmR3DrvHlp_Attach: caller='%s'/%d: return %Rrc\n",
1049 pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1050 return rc;
1051}
1052
1053
1054/** @interface_method_impl{PDMDRVHLPR3,pfnDetach} */
1055static DECLCALLBACK(int) pdmR3DrvHlp_Detach(PPDMDRVINS pDrvIns, uint32_t fFlags)
1056{
1057 PDMDRV_ASSERT_DRVINS(pDrvIns);
1058 LogFlow(("pdmR3DrvHlp_Detach: caller='%s'/%d: fFlags=%#x\n",
1059 pDrvIns->pReg->szName, pDrvIns->iInstance, fFlags));
1060 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1061
1062 /*
1063 * Anything attached?
1064 */
1065 int rc;
1066 if (pDrvIns->Internal.s.pDown)
1067 rc = pdmR3DrvDetach(pDrvIns->Internal.s.pDown, fFlags);
1068 else
1069 {
1070 AssertMsgFailed(("Nothing attached!\n"));
1071 rc = VERR_PDM_NO_DRIVER_ATTACHED;
1072 }
1073
1074 LogFlow(("pdmR3DrvHlp_Detach: caller='%s'/%d: returns %Rrc\n",
1075 pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1076 return rc;
1077}
1078
1079
1080/** @interface_method_impl{PDMDRVHLPR3,pfnDetachSelf} */
1081static DECLCALLBACK(int) pdmR3DrvHlp_DetachSelf(PPDMDRVINS pDrvIns, uint32_t fFlags)
1082{
1083 PDMDRV_ASSERT_DRVINS(pDrvIns);
1084 LogFlow(("pdmR3DrvHlp_DetachSelf: caller='%s'/%d: fFlags=%#x\n",
1085 pDrvIns->pReg->szName, pDrvIns->iInstance, fFlags));
1086 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1087
1088 int rc = pdmR3DrvDetach(pDrvIns, fFlags);
1089
1090 LogFlow(("pdmR3DrvHlp_Detach: returns %Rrc\n", rc)); /* pDrvIns is freed by now. */
1091 return rc;
1092}
1093
1094
1095/** @interface_method_impl{PDMDRVHLPR3,pfnMountPrepare} */
1096static DECLCALLBACK(int) pdmR3DrvHlp_MountPrepare(PPDMDRVINS pDrvIns, const char *pszFilename, const char *pszCoreDriver)
1097{
1098 PDMDRV_ASSERT_DRVINS(pDrvIns);
1099 LogFlow(("pdmR3DrvHlp_MountPrepare: caller='%s'/%d: pszFilename=%p:{%s} pszCoreDriver=%p:{%s}\n",
1100 pDrvIns->pReg->szName, pDrvIns->iInstance, pszFilename, pszFilename, pszCoreDriver, pszCoreDriver));
1101 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1102
1103 /*
1104 * Do the caller have anything attached below itself?
1105 */
1106 if (pDrvIns->Internal.s.pDown)
1107 {
1108 AssertMsgFailed(("Cannot prepare a mount when something's attached to you!\n"));
1109 return VERR_PDM_DRIVER_ALREADY_ATTACHED;
1110 }
1111
1112 /*
1113 * We're asked to prepare, so we'll start off by nuking the
1114 * attached configuration tree.
1115 */
1116 PCFGMNODE pNode = CFGMR3GetChild(pDrvIns->Internal.s.pCfgHandle, "AttachedDriver");
1117 if (pNode)
1118 CFGMR3RemoveNode(pNode);
1119
1120 /*
1121 * If there is no core driver, we'll have to probe for it.
1122 */
1123 if (!pszCoreDriver)
1124 {
1125 /** @todo implement image probing. */
1126 AssertReleaseMsgFailed(("Not implemented!\n"));
1127 return VERR_NOT_IMPLEMENTED;
1128 }
1129
1130 /*
1131 * Construct the basic attached driver configuration.
1132 */
1133 int rc = CFGMR3InsertNode(pDrvIns->Internal.s.pCfgHandle, "AttachedDriver", &pNode);
1134 if (RT_SUCCESS(rc))
1135 {
1136 rc = CFGMR3InsertString(pNode, "Driver", pszCoreDriver);
1137 if (RT_SUCCESS(rc))
1138 {
1139 PCFGMNODE pCfg;
1140 rc = CFGMR3InsertNode(pNode, "Config", &pCfg);
1141 if (RT_SUCCESS(rc))
1142 {
1143 rc = CFGMR3InsertString(pCfg, "Path", pszFilename);
1144 if (RT_SUCCESS(rc))
1145 {
1146 LogFlow(("pdmR3DrvHlp_MountPrepare: caller='%s'/%d: returns %Rrc (Driver=%s)\n",
1147 pDrvIns->pReg->szName, pDrvIns->iInstance, rc, pszCoreDriver));
1148 return rc;
1149 }
1150 else
1151 AssertMsgFailed(("Path string insert failed, rc=%Rrc\n", rc));
1152 }
1153 else
1154 AssertMsgFailed(("Config node failed, rc=%Rrc\n", rc));
1155 }
1156 else
1157 AssertMsgFailed(("Driver string insert failed, rc=%Rrc\n", rc));
1158 CFGMR3RemoveNode(pNode);
1159 }
1160 else
1161 AssertMsgFailed(("AttachedDriver node insert failed, rc=%Rrc\n", rc));
1162
1163 LogFlow(("pdmR3DrvHlp_MountPrepare: caller='%s'/%d: returns %Rrc\n",
1164 pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1165 return rc;
1166}
1167
1168
1169/** @interface_method_impl{PDMDRVHLPR3,pfnAssertEMT} */
1170static DECLCALLBACK(bool) pdmR3DrvHlp_AssertEMT(PPDMDRVINS pDrvIns, const char *pszFile, unsigned iLine, const char *pszFunction)
1171{
1172 PDMDRV_ASSERT_DRVINS(pDrvIns);
1173 if (VM_IS_EMT(pDrvIns->Internal.s.pVMR3))
1174 return true;
1175
1176 char szMsg[100];
1177 RTStrPrintf(szMsg, sizeof(szMsg), "AssertEMT '%s'/%d\n", pDrvIns->pReg->szName, pDrvIns->iInstance);
1178 RTAssertMsg1Weak(szMsg, iLine, pszFile, pszFunction);
1179 AssertBreakpoint();
1180 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1181 return false;
1182}
1183
1184
1185/** @interface_method_impl{PDMDRVHLPR3,pfnAssertOther} */
1186static DECLCALLBACK(bool) pdmR3DrvHlp_AssertOther(PPDMDRVINS pDrvIns, const char *pszFile, unsigned iLine, const char *pszFunction)
1187{
1188 PDMDRV_ASSERT_DRVINS(pDrvIns);
1189 if (!VM_IS_EMT(pDrvIns->Internal.s.pVMR3))
1190 return true;
1191
1192 char szMsg[100];
1193 RTStrPrintf(szMsg, sizeof(szMsg), "AssertOther '%s'/%d\n", pDrvIns->pReg->szName, pDrvIns->iInstance);
1194 RTAssertMsg1Weak(szMsg, iLine, pszFile, pszFunction);
1195 AssertBreakpoint();
1196 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1197 return false;
1198}
1199
1200
1201/** @interface_method_impl{PDMDRVHLPR3,pfnVMSetErrorV} */
1202static DECLCALLBACK(int) pdmR3DrvHlp_VMSetErrorV(PPDMDRVINS pDrvIns, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
1203{
1204 PDMDRV_ASSERT_DRVINS(pDrvIns);
1205 int rc2 = VMSetErrorV(pDrvIns->Internal.s.pVMR3, rc, RT_SRC_POS_ARGS, pszFormat, va); Assert(rc2 == rc); NOREF(rc2);
1206 return rc;
1207}
1208
1209
1210/** @interface_method_impl{PDMDRVHLPR3,pfnVMSetRuntimeErrorV} */
1211static DECLCALLBACK(int) pdmR3DrvHlp_VMSetRuntimeErrorV(PPDMDRVINS pDrvIns, uint32_t fFlags, const char *pszErrorId, const char *pszFormat, va_list va)
1212{
1213 PDMDRV_ASSERT_DRVINS(pDrvIns);
1214 int rc = VMSetRuntimeErrorV(pDrvIns->Internal.s.pVMR3, fFlags, pszErrorId, pszFormat, va);
1215 return rc;
1216}
1217
1218
1219/** @interface_method_impl{PDMDRVHLPR3,pfnVMState} */
1220static DECLCALLBACK(VMSTATE) pdmR3DrvHlp_VMState(PPDMDRVINS pDrvIns)
1221{
1222 PDMDRV_ASSERT_DRVINS(pDrvIns);
1223
1224 VMSTATE enmVMState = VMR3GetState(pDrvIns->Internal.s.pVMR3);
1225
1226 LogFlow(("pdmR3DrvHlp_VMState: caller='%s'/%d: returns %d (%s)\n", pDrvIns->pReg->szName, pDrvIns->iInstance,
1227 enmVMState, VMR3GetStateName(enmVMState)));
1228 return enmVMState;
1229}
1230
1231
1232/** @interface_method_impl{PDMDRVHLPR3,pfnVMTeleportedAndNotFullyResumedYet} */
1233static DECLCALLBACK(bool) pdmR3DrvHlp_VMTeleportedAndNotFullyResumedYet(PPDMDRVINS pDrvIns)
1234{
1235 PDMDRV_ASSERT_DRVINS(pDrvIns);
1236
1237 bool fRc = VMR3TeleportedAndNotFullyResumedYet(pDrvIns->Internal.s.pVMR3);
1238
1239 LogFlow(("pdmR3DrvHlp_VMState: caller='%s'/%d: returns %RTbool)\n", pDrvIns->pReg->szName, pDrvIns->iInstance,
1240 fRc));
1241 return fRc;
1242}
1243
1244
1245/** @interface_method_impl{PDMDRVHLPR3,pfnGetSupDrvSession} */
1246static DECLCALLBACK(PSUPDRVSESSION) pdmR3DrvHlp_GetSupDrvSession(PPDMDRVINS pDrvIns)
1247{
1248 PDMDRV_ASSERT_DRVINS(pDrvIns);
1249
1250 PSUPDRVSESSION pSession = pDrvIns->Internal.s.pVMR3->pSession;
1251 LogFlow(("pdmR3DrvHlp_GetSupDrvSession: caller='%s'/%d: returns %p)\n", pDrvIns->pReg->szName, pDrvIns->iInstance,
1252 pSession));
1253 return pSession;
1254}
1255
1256
1257/** @interface_method_impl{PDMDRVHLPR3,pfnQueueCreate} */
1258static DECLCALLBACK(int) pdmR3DrvHlp_QueueCreate(PPDMDRVINS pDrvIns, uint32_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
1259 PFNPDMQUEUEDRV pfnCallback, const char *pszName, PDMQUEUEHANDLE *phQueue)
1260{
1261 PDMDRV_ASSERT_DRVINS(pDrvIns);
1262 LogFlow(("pdmR3DrvHlp_PDMQueueCreate: caller='%s'/%d: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%p:{%s} phQueue=%p\n",
1263 pDrvIns->pReg->szName, pDrvIns->iInstance, cbItem, cItems, cMilliesInterval, pfnCallback, pszName, pszName, phQueue));
1264 PVM pVM = pDrvIns->Internal.s.pVMR3;
1265 VM_ASSERT_EMT(pVM);
1266
1267 if (pDrvIns->iInstance > 0)
1268 {
1269 pszName = MMR3HeapAPrintf(pVM, MM_TAG_PDM_DRIVER_DESC, "%s_%u", pszName, pDrvIns->iInstance);
1270 AssertLogRelReturn(pszName, VERR_NO_MEMORY);
1271 }
1272
1273 int rc = PDMR3QueueCreateDriver(pVM, pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName, phQueue);
1274
1275 LogFlow(("pdmR3DrvHlp_PDMQueueCreate: caller='%s'/%d: returns %Rrc *phQueue=%p\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc, *phQueue));
1276 return rc;
1277}
1278
1279
1280/** @interface_method_impl{PDMDRVHLPR3,pfnQueueAlloc} */
1281static DECLCALLBACK(PPDMQUEUEITEMCORE) pdmR3DrvHlp_QueueAlloc(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue)
1282{
1283 return PDMQueueAlloc(pDrvIns->Internal.s.pVMR3, hQueue, pDrvIns);
1284}
1285
1286
1287/** @interface_method_impl{PDMDRVHLPR3,pfnQueueInsert} */
1288static DECLCALLBACK(int) pdmR3DrvHlp_QueueInsert(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
1289{
1290 return PDMQueueInsert(pDrvIns->Internal.s.pVMR3, hQueue, pDrvIns, pItem);
1291}
1292
1293
1294/** @interface_method_impl{PDMDRVHLPR3,pfnQueueFlushIfNecessary} */
1295static DECLCALLBACK(bool) pdmR3DrvHlp_QueueFlushIfNecessary(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue)
1296{
1297 return PDMQueueFlushIfNecessary(pDrvIns->Internal.s.pVMR3, hQueue, pDrvIns) == VINF_SUCCESS;
1298}
1299
1300
1301/** @interface_method_impl{PDMDRVHLPR3,pfnTMGetVirtualFreq} */
1302static DECLCALLBACK(uint64_t) pdmR3DrvHlp_TMGetVirtualFreq(PPDMDRVINS pDrvIns)
1303{
1304 PDMDRV_ASSERT_DRVINS(pDrvIns);
1305
1306 return TMVirtualGetFreq(pDrvIns->Internal.s.pVMR3);
1307}
1308
1309
1310/** @interface_method_impl{PDMDRVHLPR3,pfnTMGetVirtualTime} */
1311static DECLCALLBACK(uint64_t) pdmR3DrvHlp_TMGetVirtualTime(PPDMDRVINS pDrvIns)
1312{
1313 PDMDRV_ASSERT_DRVINS(pDrvIns);
1314
1315 return TMVirtualGet(pDrvIns->Internal.s.pVMR3);
1316}
1317
1318
1319/** @interface_method_impl{PDMDRVHLPR3,pfnTimerCreate} */
1320static DECLCALLBACK(int) pdmR3DrvHlp_TimerCreate(PPDMDRVINS pDrvIns, TMCLOCK enmClock, PFNTMTIMERDRV pfnCallback, void *pvUser,
1321 uint32_t fFlags, const char *pszDesc, PTMTIMERHANDLE phTimer)
1322{
1323 PDMDRV_ASSERT_DRVINS(pDrvIns);
1324 LogFlow(("pdmR3DrvHlp_TimerCreate: caller='%s'/%d: enmClock=%d pfnCallback=%p pvUser=%p fFlags=%#x pszDesc=%p:{%s} phTimer=%p\n",
1325 pDrvIns->pReg->szName, pDrvIns->iInstance, enmClock, pfnCallback, pvUser, fFlags, pszDesc, pszDesc, phTimer));
1326
1327 /* Mangle the timer name if there are more than once instance of this driver. */
1328 char szName[32];
1329 AssertReturn(strlen(pszDesc) < sizeof(szName) - 3, VERR_INVALID_NAME);
1330 if (pDrvIns->iInstance > 0)
1331 {
1332 RTStrPrintf(szName, sizeof(szName), "%s[%u]", pszDesc, pDrvIns->iInstance);
1333 pszDesc = szName;
1334 }
1335
1336 /* Clear the ring-0 flag if the driver isn't configured for ring-0. */
1337 if (fFlags & TMTIMER_FLAGS_RING0)
1338 {
1339 AssertReturn(!(fFlags & TMTIMER_FLAGS_NO_RING0), VERR_INVALID_FLAGS);
1340 Assert(pDrvIns->Internal.s.pDrv->pReg->fFlags & PDM_DRVREG_FLAGS_R0);
1341 /* if (!(pDrvIns->Internal.s.fIntFlags & PDMDRVINSINT_FLAGS_R0_ENABLED)) */ /** @todo PDMDRVINSINT_FLAGS_R0_ENABLED? */
1342 fFlags = (fFlags & ~TMTIMER_FLAGS_RING0) | TMTIMER_FLAGS_NO_RING0;
1343 }
1344 else
1345 fFlags |= TMTIMER_FLAGS_NO_RING0;
1346
1347 int rc = TMR3TimerCreateDriver(pDrvIns->Internal.s.pVMR3, pDrvIns, enmClock, pfnCallback, pvUser, fFlags, pszDesc, phTimer);
1348
1349 LogFlow(("pdmR3DrvHlp_TMTimerCreate: caller='%s'/%d: returns %Rrc *phTimer=%p\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc, *phTimer));
1350 return rc;
1351}
1352
1353
1354/** @interface_method_impl{PDMDRVHLPR3,pfnTimerSetMillies} */
1355static DECLCALLBACK(int) pdmR3DrvHlp_TimerSetMillies(PPDMDRVINS pDrvIns, TMTIMERHANDLE hTimer, uint64_t cMilliesToNext)
1356{
1357 PDMDRV_ASSERT_DRVINS(pDrvIns);
1358 return TMTimerSetMillies(pDrvIns->Internal.s.pVMR3, hTimer, cMilliesToNext);
1359}
1360
1361
1362/** @interface_method_impl{PDMDRVHLPR3,pfnSSMRegister} */
1363static DECLCALLBACK(int) pdmR3DrvHlp_SSMRegister(PPDMDRVINS pDrvIns, uint32_t uVersion, size_t cbGuess,
1364 PFNSSMDRVLIVEPREP pfnLivePrep, PFNSSMDRVLIVEEXEC pfnLiveExec, PFNSSMDRVLIVEVOTE pfnLiveVote,
1365 PFNSSMDRVSAVEPREP pfnSavePrep, PFNSSMDRVSAVEEXEC pfnSaveExec, PFNSSMDRVSAVEDONE pfnSaveDone,
1366 PFNSSMDRVLOADPREP pfnLoadPrep, PFNSSMDRVLOADEXEC pfnLoadExec, PFNSSMDRVLOADDONE pfnLoadDone)
1367{
1368 PDMDRV_ASSERT_DRVINS(pDrvIns);
1369 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1370 LogFlow(("pdmR3DrvHlp_SSMRegister: caller='%s'/%d: uVersion=%#x cbGuess=%#x \n"
1371 " pfnLivePrep=%p pfnLiveExec=%p pfnLiveVote=%p pfnSavePrep=%p pfnSaveExec=%p pfnSaveDone=%p pszLoadPrep=%p pfnLoadExec=%p pfnLoaddone=%p\n",
1372 pDrvIns->pReg->szName, pDrvIns->iInstance, uVersion, cbGuess,
1373 pfnLivePrep, pfnLiveExec, pfnLiveVote,
1374 pfnSavePrep, pfnSaveExec, pfnSaveDone, pfnLoadPrep, pfnLoadExec, pfnLoadDone));
1375
1376 int rc = SSMR3RegisterDriver(pDrvIns->Internal.s.pVMR3, pDrvIns, pDrvIns->pReg->szName, pDrvIns->iInstance,
1377 uVersion, cbGuess,
1378 pfnLivePrep, pfnLiveExec, pfnLiveVote,
1379 pfnSavePrep, pfnSaveExec, pfnSaveDone,
1380 pfnLoadPrep, pfnLoadExec, pfnLoadDone);
1381
1382 LogFlow(("pdmR3DrvHlp_SSMRegister: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1383 return rc;
1384}
1385
1386
1387/** @interface_method_impl{PDMDRVHLPR3,pfnSSMDeregister} */
1388static DECLCALLBACK(int) pdmR3DrvHlp_SSMDeregister(PPDMDRVINS pDrvIns, const char *pszName, uint32_t uInstance)
1389{
1390 PDMDRV_ASSERT_DRVINS(pDrvIns);
1391 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1392 LogFlow(("pdmR3DrvHlp_SSMDeregister: caller='%s'/%d: pszName=%p:{%s} uInstance=%#x\n",
1393 pDrvIns->pReg->szName, pDrvIns->iInstance, pszName, pszName, uInstance));
1394
1395 int rc = SSMR3DeregisterDriver(pDrvIns->Internal.s.pVMR3, pDrvIns, pszName, uInstance);
1396
1397 LogFlow(("pdmR3DrvHlp_SSMDeregister: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1398 return rc;
1399}
1400
1401
1402/** @interface_method_impl{PDMDRVHLPR3,pfnMMHeapFree} */
1403static DECLCALLBACK(void) pdmR3DrvHlp_MMHeapFree(PPDMDRVINS pDrvIns, void *pv)
1404{
1405 PDMDRV_ASSERT_DRVINS(pDrvIns); RT_NOREF(pDrvIns);
1406 LogFlow(("pdmR3DrvHlp_MMHeapFree: caller='%s'/%d: pv=%p\n",
1407 pDrvIns->pReg->szName, pDrvIns->iInstance, pv));
1408
1409 MMR3HeapFree(pv);
1410
1411 LogFlow(("pdmR3DrvHlp_MMHeapFree: caller='%s'/%d: returns\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1412}
1413
1414
1415/** @interface_method_impl{PDMDRVHLPR3,pfnDBGFInfoRegister} */
1416static DECLCALLBACK(int) pdmR3DrvHlp_DBGFInfoRegister(PPDMDRVINS pDrvIns, const char *pszName, const char *pszDesc, PFNDBGFHANDLERDRV pfnHandler)
1417{
1418 PDMDRV_ASSERT_DRVINS(pDrvIns);
1419 LogFlow(("pdmR3DrvHlp_DBGFInfoRegister: caller='%s'/%d: pszName=%p:{%s} pszDesc=%p:{%s} pfnHandler=%p\n",
1420 pDrvIns->pReg->szName, pDrvIns->iInstance, pszName, pszName, pszDesc, pszDesc, pfnHandler));
1421
1422 int rc = DBGFR3InfoRegisterDriver(pDrvIns->Internal.s.pVMR3, pszName, pszDesc, pfnHandler, pDrvIns);
1423
1424 LogFlow(("pdmR3DrvHlp_DBGFInfoRegister: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1425 return rc;
1426}
1427
1428
1429/** @interface_method_impl{PDMDRVHLPR3,pfnDBGFInfoRegisterArgv} */
1430static DECLCALLBACK(int) pdmR3DrvHlp_DBGFInfoRegisterArgv(PPDMDRVINS pDrvIns, const char *pszName, const char *pszDesc, PFNDBGFINFOARGVDRV pfnHandler)
1431{
1432 PDMDRV_ASSERT_DRVINS(pDrvIns);
1433 LogFlow(("pdmR3DrvHlp_DBGFInfoRegisterArgv: caller='%s'/%d: pszName=%p:{%s} pszDesc=%p:{%s} pfnHandler=%p\n",
1434 pDrvIns->pReg->szName, pDrvIns->iInstance, pszName, pszName, pszDesc, pszDesc, pfnHandler));
1435
1436 int rc = DBGFR3InfoRegisterDriverArgv(pDrvIns->Internal.s.pVMR3, pszName, pszDesc, pfnHandler, pDrvIns);
1437
1438 LogFlow(("pdmR3DrvHlp_DBGFInfoRegisterArgv: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1439 return rc;
1440}
1441
1442
1443/** @interface_method_impl{PDMDRVHLPR3,pfnDBGFInfoDeregister} */
1444static DECLCALLBACK(int) pdmR3DrvHlp_DBGFInfoDeregister(PPDMDRVINS pDrvIns, const char *pszName)
1445{
1446 PDMDRV_ASSERT_DRVINS(pDrvIns);
1447 LogFlow(("pdmR3DrvHlp_DBGFInfoDeregister: caller='%s'/%d: pszName=%p:{%s}\n",
1448 pDrvIns->pReg->szName, pDrvIns->iInstance, pszName, pszName));
1449
1450 int rc = DBGFR3InfoDeregisterDriver(pDrvIns->Internal.s.pVMR3, pDrvIns, pszName);
1451
1452 LogFlow(("pdmR3DrvHlp_DBGFInfoDeregister: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1453
1454 return rc;
1455}
1456
1457
1458/** @interface_method_impl{PDMDRVHLPR3,pfnSTAMRegister} */
1459static DECLCALLBACK(void) pdmR3DrvHlp_STAMRegister(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType, const char *pszName,
1460 STAMUNIT enmUnit, const char *pszDesc)
1461{
1462 PDMDRV_ASSERT_DRVINS(pDrvIns);
1463 PVM pVM = pDrvIns->Internal.s.pVMR3;
1464 VM_ASSERT_EMT(pVM);
1465
1466#ifdef VBOX_WITH_STATISTICS /** @todo rework this to always be compiled in */
1467 if (*pszName == '/')
1468 STAM_REG(pDrvIns->Internal.s.pVMR3, pvSample, enmType, pszName, enmUnit, pszDesc);
1469 else
1470 STAMR3RegisterF(pVM, pvSample, enmType, STAMVISIBILITY_ALWAYS, enmUnit, pszDesc,
1471 "/Drivers/%s-%u/%s", pDrvIns->pReg->szName, pDrvIns->iInstance, pszName);
1472#else
1473 RT_NOREF(pDrvIns, pvSample, enmType, pszName, enmUnit, pszDesc, pVM);
1474#endif
1475}
1476
1477
1478/** @interface_method_impl{PDMDRVHLPR3,pfnSTAMRegisterV} */
1479static DECLCALLBACK(void) pdmR3DrvHlp_STAMRegisterV(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility,
1480 STAMUNIT enmUnit, const char *pszDesc, const char *pszName, va_list args)
1481{
1482 PDMDRV_ASSERT_DRVINS(pDrvIns);
1483 PVM pVM = pDrvIns->Internal.s.pVMR3;
1484 VM_ASSERT_EMT(pVM);
1485
1486 int rc;
1487 if (*pszName == '/')
1488 rc = STAMR3RegisterV(pVM, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
1489 else
1490 {
1491 /* We need to format it to check whether it starts with a
1492 slash or not (will rework this later). */
1493 char szFormatted[2048];
1494 ssize_t cchBase = RTStrPrintf2(szFormatted, sizeof(szFormatted) - 1024, "/Drivers/%s-%u/",
1495 pDrvIns->pReg->szName, pDrvIns->iInstance);
1496 AssertReturnVoid(cchBase > 0);
1497
1498 ssize_t cch2 = RTStrPrintf2V(&szFormatted[cchBase], sizeof(szFormatted) - cchBase, pszName, args);
1499 AssertReturnVoid(cch2 > 0);
1500
1501 rc = STAMR3Register(pVM, pvSample, enmType, enmVisibility,
1502 &szFormatted[szFormatted[cchBase] == '/' ? cchBase : 0], enmUnit, pszDesc);
1503 }
1504 AssertRC(rc);
1505}
1506
1507
1508/** @interface_method_impl{PDMDRVHLPR3,pfnSTAMRegisterF} */
1509static DECLCALLBACK(void) pdmR3DrvHlp_STAMRegisterF(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility,
1510 STAMUNIT enmUnit, const char *pszDesc, const char *pszName, ...)
1511{
1512 va_list va;
1513 va_start(va, pszName);
1514 pdmR3DrvHlp_STAMRegisterV(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, va);
1515 va_end(va);
1516}
1517
1518
1519/** @interface_method_impl{PDMDRVHLPR3,pfnSTAMDeregister} */
1520static DECLCALLBACK(int) pdmR3DrvHlp_STAMDeregister(PPDMDRVINS pDrvIns, void *pvSample)
1521{
1522 PDMDRV_ASSERT_DRVINS(pDrvIns);
1523 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1524
1525 return STAMR3DeregisterByAddr(pDrvIns->Internal.s.pVMR3->pUVM, pvSample);
1526}
1527
1528
1529/** @interface_method_impl{PDMDRVHLPR3,pfnSTAMDeregisterByPrefix} */
1530static DECLCALLBACK(int) pdmR3DrvHlp_STAMDeregisterByPrefix(PPDMDRVINS pDrvIns, const char *pszPrefix)
1531{
1532 PDMDRV_ASSERT_DRVINS(pDrvIns);
1533
1534 if (*pszPrefix == '/')
1535 return STAMR3DeregisterByPrefix(pDrvIns->Internal.s.pVMR3->pUVM, pszPrefix);
1536
1537 char szTmp[2048];
1538 ssize_t cch = RTStrPrintf2(szTmp, sizeof(szTmp), "/Drivers/%s-%u/%s", pDrvIns->pReg->szName, pDrvIns->iInstance, pszPrefix);
1539 AssertReturn(cch > 0, VERR_BUFFER_OVERFLOW);
1540 return STAMR3DeregisterByPrefix(pDrvIns->Internal.s.pVMR3->pUVM, szTmp);
1541}
1542
1543
1544/** @interface_method_impl{PDMDRVHLPR3,pfnSUPCallVMMR0Ex} */
1545static DECLCALLBACK(int) pdmR3DrvHlp_SUPCallVMMR0Ex(PPDMDRVINS pDrvIns, unsigned uOperation, void *pvArg, unsigned cbArg)
1546{
1547 PDMDRV_ASSERT_DRVINS(pDrvIns);
1548 LogFlow(("pdmR3DrvHlp_SSMCallVMMR0Ex: caller='%s'/%d: uOperation=%u pvArg=%p cbArg=%d\n",
1549 pDrvIns->pReg->szName, pDrvIns->iInstance, uOperation, pvArg, cbArg));
1550 RT_NOREF_PV(cbArg);
1551
1552 int rc;
1553 if ( uOperation >= VMMR0_DO_SRV_START
1554 && uOperation < VMMR0_DO_SRV_END)
1555 rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pDrvIns->Internal.s.pVMR3), NIL_VMCPUID, uOperation, 0, (PSUPVMMR0REQHDR)pvArg);
1556 else
1557 {
1558 AssertMsgFailed(("Invalid uOperation=%u\n", uOperation));
1559 rc = VERR_INVALID_PARAMETER;
1560 }
1561
1562 LogFlow(("pdmR3DrvHlp_SUPCallVMMR0Ex: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1563 return rc;
1564}
1565
1566
1567/** @interface_method_impl{PDMDRVHLPR3,pfnUSBRegisterHub} */
1568static DECLCALLBACK(int) pdmR3DrvHlp_USBRegisterHub(PPDMDRVINS pDrvIns, uint32_t fVersions, uint32_t cPorts, PCPDMUSBHUBREG pUsbHubReg, PPCPDMUSBHUBHLP ppUsbHubHlp)
1569{
1570 PDMDRV_ASSERT_DRVINS(pDrvIns);
1571 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1572 LogFlow(("pdmR3DrvHlp_USBRegisterHub: caller='%s'/%d: fVersions=%#x cPorts=%#x pUsbHubReg=%p ppUsbHubHlp=%p\n",
1573 pDrvIns->pReg->szName, pDrvIns->iInstance, fVersions, cPorts, pUsbHubReg, ppUsbHubHlp));
1574
1575#ifdef VBOX_WITH_USB
1576 int rc = pdmR3UsbRegisterHub(pDrvIns->Internal.s.pVMR3, pDrvIns, fVersions, cPorts, pUsbHubReg, ppUsbHubHlp);
1577#else
1578 int rc = VERR_NOT_SUPPORTED;
1579#endif
1580
1581 LogFlow(("pdmR3DrvHlp_USBRegisterHub: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1582 return rc;
1583}
1584
1585
1586/** @interface_method_impl{PDMDRVHLPR3,pfnSetAsyncNotification} */
1587static DECLCALLBACK(int) pdmR3DrvHlp_SetAsyncNotification(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
1588{
1589 PDMDRV_ASSERT_DRVINS(pDrvIns);
1590 VM_ASSERT_EMT0(pDrvIns->Internal.s.pVMR3);
1591 LogFlow(("pdmR3DrvHlp_SetAsyncNotification: caller='%s'/%d: pfnAsyncNotify=%p\n", pDrvIns->pReg->szName, pDrvIns->iInstance, pfnAsyncNotify));
1592
1593 int rc = VINF_SUCCESS;
1594 AssertStmt(pfnAsyncNotify, rc = VERR_INVALID_PARAMETER);
1595 AssertStmt(!pDrvIns->Internal.s.pfnAsyncNotify, rc = VERR_WRONG_ORDER);
1596 AssertStmt(pDrvIns->Internal.s.fVMSuspended || pDrvIns->Internal.s.fVMReset, rc = VERR_WRONG_ORDER);
1597 VMSTATE enmVMState = VMR3GetState(pDrvIns->Internal.s.pVMR3);
1598 AssertStmt( enmVMState == VMSTATE_SUSPENDING
1599 || enmVMState == VMSTATE_SUSPENDING_EXT_LS
1600 || enmVMState == VMSTATE_SUSPENDING_LS
1601 || enmVMState == VMSTATE_RESETTING
1602 || enmVMState == VMSTATE_RESETTING_LS
1603 || enmVMState == VMSTATE_POWERING_OFF
1604 || enmVMState == VMSTATE_POWERING_OFF_LS,
1605 rc = VERR_INVALID_STATE);
1606
1607 if (RT_SUCCESS(rc))
1608 pDrvIns->Internal.s.pfnAsyncNotify = pfnAsyncNotify;
1609
1610 LogFlow(("pdmR3DrvHlp_SetAsyncNotification: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc));
1611 return rc;
1612}
1613
1614
1615/** @interface_method_impl{PDMDRVHLPR3,pfnAsyncNotificationCompleted} */
1616static DECLCALLBACK(void) pdmR3DrvHlp_AsyncNotificationCompleted(PPDMDRVINS pDrvIns)
1617{
1618 PDMDRV_ASSERT_DRVINS(pDrvIns);
1619 PVM pVM = pDrvIns->Internal.s.pVMR3;
1620
1621 VMSTATE enmVMState = VMR3GetState(pVM);
1622 if ( enmVMState == VMSTATE_SUSPENDING
1623 || enmVMState == VMSTATE_SUSPENDING_EXT_LS
1624 || enmVMState == VMSTATE_SUSPENDING_LS
1625 || enmVMState == VMSTATE_RESETTING
1626 || enmVMState == VMSTATE_RESETTING_LS
1627 || enmVMState == VMSTATE_POWERING_OFF
1628 || enmVMState == VMSTATE_POWERING_OFF_LS)
1629 {
1630 LogFlow(("pdmR3DrvHlp_AsyncNotificationCompleted: caller='%s'/%d:\n", pDrvIns->pReg->szName, pDrvIns->iInstance));
1631 VMR3AsyncPdmNotificationWakeupU(pVM->pUVM);
1632 }
1633 else
1634 LogFlow(("pdmR3DrvHlp_AsyncNotificationCompleted: caller='%s'/%d: enmVMState=%d\n", pDrvIns->pReg->szName, pDrvIns->iInstance, enmVMState));
1635}
1636
1637
1638/** @interface_method_impl{PDMDRVHLPR3,pfnThreadCreate} */
1639static DECLCALLBACK(int) pdmR3DrvHlp_ThreadCreate(PPDMDRVINS pDrvIns, PPPDMTHREAD ppThread, void *pvUser, PFNPDMTHREADDRV pfnThread,
1640 PFNPDMTHREADWAKEUPDRV pfnWakeup, size_t cbStack, RTTHREADTYPE enmType, const char *pszName)
1641{
1642 PDMDRV_ASSERT_DRVINS(pDrvIns);
1643 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1644 LogFlow(("pdmR3DrvHlp_ThreadCreate: caller='%s'/%d: ppThread=%p pvUser=%p pfnThread=%p pfnWakeup=%p cbStack=%#zx enmType=%d pszName=%p:{%s}\n",
1645 pDrvIns->pReg->szName, pDrvIns->iInstance, ppThread, pvUser, pfnThread, pfnWakeup, cbStack, enmType, pszName, pszName));
1646
1647 int rc = pdmR3ThreadCreateDriver(pDrvIns->Internal.s.pVMR3, pDrvIns, ppThread, pvUser, pfnThread, pfnWakeup, cbStack, enmType, pszName);
1648
1649 LogFlow(("pdmR3DrvHlp_ThreadCreate: caller='%s'/%d: returns %Rrc *ppThread=%RTthrd\n", pDrvIns->pReg->szName, pDrvIns->iInstance,
1650 rc, *ppThread));
1651 return rc;
1652}
1653
1654
1655/** @interface_method_impl{PDMDRVHLPR3,pfnAsyncCompletionTemplateCreate} */
1656static DECLCALLBACK(int) pdmR3DrvHlp_AsyncCompletionTemplateCreate(PPDMDRVINS pDrvIns, PPPDMASYNCCOMPLETIONTEMPLATE ppTemplate,
1657 PFNPDMASYNCCOMPLETEDRV pfnCompleted, void *pvTemplateUser,
1658 const char *pszDesc)
1659{
1660 PDMDRV_ASSERT_DRVINS(pDrvIns);
1661 LogFlow(("pdmR3DrvHlp_AsyncCompletionTemplateCreate: caller='%s'/%d: ppTemplate=%p pfnCompleted=%p pszDesc=%p:{%s}\n",
1662 pDrvIns->pReg->szName, pDrvIns->iInstance, ppTemplate, pfnCompleted, pszDesc, pszDesc));
1663
1664 int rc = pdmR3AsyncCompletionTemplateCreateDriver(pDrvIns->Internal.s.pVMR3, pDrvIns, ppTemplate, pfnCompleted, pvTemplateUser, pszDesc);
1665
1666 LogFlow(("pdmR3DrvHlp_AsyncCompletionTemplateCreate: caller='%s'/%d: returns %Rrc *ppThread=%p\n", pDrvIns->pReg->szName,
1667 pDrvIns->iInstance, rc, *ppTemplate));
1668 return rc;
1669}
1670
1671
1672/** @interface_method_impl{PDMDRVHLPR3,pfnNetShaperAttach} */
1673static DECLCALLBACK(int) pdmR3DrvHlp_NetShaperAttach(PPDMDRVINS pDrvIns, const char *pszBwGroup, PPDMNSFILTER pFilter)
1674{
1675#ifdef VBOX_WITH_NETSHAPER
1676 PDMDRV_ASSERT_DRVINS(pDrvIns);
1677 LogFlow(("pdmR3DrvHlp_NetShaperAttach: caller='%s'/%d: pFilter=%p pszBwGroup=%p:{%s}\n",
1678 pDrvIns->pReg->szName, pDrvIns->iInstance, pFilter, pszBwGroup, pszBwGroup));
1679
1680 int rc = PDMR3NsAttach(pDrvIns->Internal.s.pVMR3->pUVM, pDrvIns, pszBwGroup, pFilter);
1681
1682 LogFlow(("pdmR3DrvHlp_NetShaperAttach: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName,
1683 pDrvIns->iInstance, rc));
1684 return rc;
1685#else
1686 RT_NOREF(pDrvIns, pszBwGroup, pFilter);
1687 return VERR_NOT_IMPLEMENTED;
1688#endif
1689}
1690
1691
1692/** @interface_method_impl{PDMDRVHLPR3,pfnNetShaperDetach} */
1693static DECLCALLBACK(int) pdmR3DrvHlp_NetShaperDetach(PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter)
1694{
1695#ifdef VBOX_WITH_NETSHAPER
1696 PDMDRV_ASSERT_DRVINS(pDrvIns);
1697 LogFlow(("pdmR3DrvHlp_NetShaperDetach: caller='%s'/%d: pFilter=%p\n",
1698 pDrvIns->pReg->szName, pDrvIns->iInstance, pFilter));
1699
1700 int rc = PDMR3NsDetach(pDrvIns->Internal.s.pVMR3->pUVM, pDrvIns, pFilter);
1701
1702 LogFlow(("pdmR3DrvHlp_NetShaperDetach: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName,
1703 pDrvIns->iInstance, rc));
1704 return rc;
1705#else
1706 RT_NOREF(pDrvIns, pFilter);
1707 return VERR_NOT_IMPLEMENTED;
1708#endif
1709}
1710
1711
1712/** @interface_method_impl{PDMDRVHLPR3,pfnNetShaperAllocateBandwidth} */
1713static DECLCALLBACK(bool) pdmR3DrvHlp_NetShaperAllocateBandwidth(PPDMDRVINS pDrvIns, PPDMNSFILTER pFilter, size_t cbTransfer)
1714{
1715#ifdef VBOX_WITH_NETSHAPER
1716 PDMDRV_ASSERT_DRVINS(pDrvIns);
1717 LogFlow(("pdmR3DrvHlp_NetShaperDetach: caller='%s'/%d: pFilter=%p cbTransfer=%#zx\n",
1718 pDrvIns->pReg->szName, pDrvIns->iInstance, pFilter, cbTransfer));
1719
1720 bool const fRc = PDMNetShaperAllocateBandwidth(pDrvIns->Internal.s.pVMR3, pFilter, cbTransfer);
1721
1722 LogFlow(("pdmR3DrvHlp_NetShaperDetach: caller='%s'/%d: returns %RTbool\n", pDrvIns->pReg->szName, pDrvIns->iInstance, fRc));
1723 return fRc;
1724#else
1725 RT_NOREF(pDrvIns, pFilter, cbTransfer);
1726 return true;
1727#endif
1728}
1729
1730
1731/** @interface_method_impl{PDMDRVHLPR3,pfnLdrGetRCInterfaceSymbols} */
1732static DECLCALLBACK(int) pdmR3DrvHlp_LdrGetRCInterfaceSymbols(PPDMDRVINS pDrvIns, void *pvInterface, size_t cbInterface,
1733 const char *pszSymPrefix, const char *pszSymList)
1734{
1735 PDMDRV_ASSERT_DRVINS(pDrvIns);
1736 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1737 LogFlow(("pdmR3DrvHlp_LdrGetRCInterfaceSymbols: caller='%s'/%d: pvInterface=%p cbInterface=%zu pszSymPrefix=%p:{%s} pszSymList=%p:{%s}\n",
1738 pDrvIns->pReg->szName, pDrvIns->iInstance, pvInterface, cbInterface, pszSymPrefix, pszSymPrefix, pszSymList, pszSymList));
1739
1740 int rc;
1741 if ( strncmp(pszSymPrefix, "drv", 3) == 0
1742 && RTStrIStr(pszSymPrefix + 3, pDrvIns->pReg->szName) != NULL)
1743 {
1744 if (pDrvIns->pReg->fFlags & PDM_DRVREG_FLAGS_RC)
1745 rc = PDMR3LdrGetInterfaceSymbols(pDrvIns->Internal.s.pVMR3,
1746 pvInterface, cbInterface,
1747 pDrvIns->pReg->szRCMod, pDrvIns->Internal.s.pDrv->pszRCSearchPath,
1748 pszSymPrefix, pszSymList,
1749 false /*fRing0OrRC*/);
1750 else
1751 {
1752 AssertMsgFailed(("Not a raw-mode enabled driver\n"));
1753 rc = VERR_PERMISSION_DENIED;
1754 }
1755 }
1756 else
1757 {
1758 AssertMsgFailed(("Invalid prefix '%s' for '%s'; must start with 'drv' and contain the driver name!\n",
1759 pszSymPrefix, pDrvIns->pReg->szName));
1760 rc = VERR_INVALID_NAME;
1761 }
1762
1763 LogFlow(("pdmR3DrvHlp_LdrGetRCInterfaceSymbols: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName,
1764 pDrvIns->iInstance, rc));
1765 return rc;
1766}
1767
1768
1769/** @interface_method_impl{PDMDRVHLPR3,pfnLdrGetR0InterfaceSymbols} */
1770static DECLCALLBACK(int) pdmR3DrvHlp_LdrGetR0InterfaceSymbols(PPDMDRVINS pDrvIns, void *pvInterface, size_t cbInterface,
1771 const char *pszSymPrefix, const char *pszSymList)
1772{
1773 PDMDRV_ASSERT_DRVINS(pDrvIns);
1774 VM_ASSERT_EMT(pDrvIns->Internal.s.pVMR3);
1775 LogFlow(("pdmR3DrvHlp_LdrGetR0InterfaceSymbols: caller='%s'/%d: pvInterface=%p cbInterface=%zu pszSymPrefix=%p:{%s} pszSymList=%p:{%s}\n",
1776 pDrvIns->pReg->szName, pDrvIns->iInstance, pvInterface, cbInterface, pszSymPrefix, pszSymPrefix, pszSymList, pszSymList));
1777
1778 int rc;
1779 if ( strncmp(pszSymPrefix, "drv", 3) == 0
1780 && RTStrIStr(pszSymPrefix + 3, pDrvIns->pReg->szName) != NULL)
1781 {
1782 if (pDrvIns->pReg->fFlags & PDM_DRVREG_FLAGS_R0)
1783 rc = PDMR3LdrGetInterfaceSymbols(pDrvIns->Internal.s.pVMR3,
1784 pvInterface, cbInterface,
1785 pDrvIns->pReg->szR0Mod, pDrvIns->Internal.s.pDrv->pszR0SearchPath,
1786 pszSymPrefix, pszSymList,
1787 true /*fRing0OrRC*/);
1788 else
1789 {
1790 AssertMsgFailed(("Not a ring-0 enabled driver\n"));
1791 rc = VERR_PERMISSION_DENIED;
1792 }
1793 }
1794 else
1795 {
1796 AssertMsgFailed(("Invalid prefix '%s' for '%s'; must start with 'drv' and contain the driver name!\n",
1797 pszSymPrefix, pDrvIns->pReg->szName));
1798 rc = VERR_INVALID_NAME;
1799 }
1800
1801 LogFlow(("pdmR3DrvHlp_LdrGetR0InterfaceSymbols: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName,
1802 pDrvIns->iInstance, rc));
1803 return rc;
1804}
1805
1806
1807/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectInit} */
1808static DECLCALLBACK(int) pdmR3DrvHlp_CritSectInit(PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect,
1809 RT_SRC_POS_DECL, const char *pszName)
1810{
1811 PDMDRV_ASSERT_DRVINS(pDrvIns);
1812 PVM pVM = pDrvIns->Internal.s.pVMR3;
1813 VM_ASSERT_EMT(pVM);
1814 LogFlow(("pdmR3DrvHlp_CritSectInit: caller='%s'/%d: pCritSect=%p pszName=%s\n",
1815 pDrvIns->pReg->szName, pDrvIns->iInstance, pCritSect, pszName));
1816
1817 int rc = pdmR3CritSectInitDriver(pVM, pDrvIns, pCritSect, RT_SRC_POS_ARGS, "%s_%u", pszName, pDrvIns->iInstance);
1818
1819 LogFlow(("pdmR3DrvHlp_CritSectInit: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName,
1820 pDrvIns->iInstance, rc));
1821 return rc;
1822}
1823
1824/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectYield} */
1825static DECLCALLBACK(bool) pdmR3DrvHlp_CritSectYield(PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect)
1826{
1827 PDMDRV_ASSERT_DRVINS(pDrvIns);
1828 RT_NOREF(pDrvIns);
1829 return PDMR3CritSectYield(pDrvIns->Internal.s.pVMR3, pCritSect);
1830}
1831
1832
1833/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectEnter} */
1834static DECLCALLBACK(int) pdmR3DrvHlp_CritSectEnter(PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect, int rcBusy)
1835{
1836 PDMDRV_ASSERT_DRVINS(pDrvIns);
1837 return PDMCritSectEnter(pDrvIns->Internal.s.pVMR3, pCritSect, rcBusy);
1838}
1839
1840
1841/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectEnterDebug} */
1842static DECLCALLBACK(int) pdmR3DrvHlp_CritSectEnterDebug(PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect, int rcBusy,
1843 RTHCUINTPTR uId, RT_SRC_POS_DECL)
1844{
1845 PDMDRV_ASSERT_DRVINS(pDrvIns);
1846 return PDMCritSectEnterDebug(pDrvIns->Internal.s.pVMR3, pCritSect, rcBusy, uId, RT_SRC_POS_ARGS);
1847}
1848
1849
1850/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectTryEnter} */
1851static DECLCALLBACK(int) pdmR3DrvHlp_CritSectTryEnter(PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect)
1852{
1853 PDMDRV_ASSERT_DRVINS(pDrvIns);
1854 return PDMCritSectTryEnter(pDrvIns->Internal.s.pVMR3, pCritSect);
1855}
1856
1857
1858/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectTryEnterDebug} */
1859static DECLCALLBACK(int) pdmR3DrvHlp_CritSectTryEnterDebug(PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect,
1860 RTHCUINTPTR uId, RT_SRC_POS_DECL)
1861{
1862 PDMDRV_ASSERT_DRVINS(pDrvIns);
1863 return PDMCritSectTryEnterDebug(pDrvIns->Internal.s.pVMR3, pCritSect, uId, RT_SRC_POS_ARGS);
1864}
1865
1866
1867/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectLeave} */
1868static DECLCALLBACK(int) pdmR3DrvHlp_CritSectLeave(PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect)
1869{
1870 PDMDRV_ASSERT_DRVINS(pDrvIns);
1871 return PDMCritSectLeave(pDrvIns->Internal.s.pVMR3, pCritSect);
1872}
1873
1874
1875/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectIsOwner} */
1876static DECLCALLBACK(bool) pdmR3DrvHlp_CritSectIsOwner(PPDMDRVINS pDrvIns, PCPDMCRITSECT pCritSect)
1877{
1878 PDMDRV_ASSERT_DRVINS(pDrvIns);
1879 return PDMCritSectIsOwner(pDrvIns->Internal.s.pVMR3, pCritSect);
1880}
1881
1882
1883/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectIsInitialized} */
1884static DECLCALLBACK(bool) pdmR3DrvHlp_CritSectIsInitialized(PPDMDRVINS pDrvIns, PCPDMCRITSECT pCritSect)
1885{
1886 PDMDRV_ASSERT_DRVINS(pDrvIns);
1887 RT_NOREF(pDrvIns);
1888 return PDMCritSectIsInitialized(pCritSect);
1889}
1890
1891
1892/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectHasWaiters} */
1893static DECLCALLBACK(bool) pdmR3DrvHlp_CritSectHasWaiters(PPDMDRVINS pDrvIns, PCPDMCRITSECT pCritSect)
1894{
1895 PDMDRV_ASSERT_DRVINS(pDrvIns);
1896 return PDMCritSectHasWaiters(pDrvIns->Internal.s.pVMR3, pCritSect);
1897}
1898
1899
1900/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectGetRecursion} */
1901static DECLCALLBACK(uint32_t) pdmR3DrvHlp_CritSectGetRecursion(PPDMDRVINS pDrvIns, PCPDMCRITSECT pCritSect)
1902{
1903 PDMDRV_ASSERT_DRVINS(pDrvIns);
1904 RT_NOREF(pDrvIns);
1905 return PDMCritSectGetRecursion(pCritSect);
1906}
1907
1908
1909/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectScheduleExitEvent} */
1910static DECLCALLBACK(int) pdmR3DrvHlp_CritSectScheduleExitEvent(PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect,
1911 SUPSEMEVENT hEventToSignal)
1912{
1913 PDMDRV_ASSERT_DRVINS(pDrvIns);
1914 RT_NOREF(pDrvIns);
1915 return PDMHCCritSectScheduleExitEvent(pCritSect, hEventToSignal);
1916}
1917
1918
1919/** @interface_method_impl{PDMDRVHLPR3,pfnCritSectDelete} */
1920static DECLCALLBACK(int) pdmR3DrvHlp_CritSectDelete(PPDMDRVINS pDrvIns, PPDMCRITSECT pCritSect)
1921{
1922 PDMDRV_ASSERT_DRVINS(pDrvIns);
1923 return PDMR3CritSectDelete(pDrvIns->Internal.s.pVMR3, pCritSect);
1924}
1925
1926
1927/** @interface_method_impl{PDMDRVHLPR3,pfnCallR0} */
1928static DECLCALLBACK(int) pdmR3DrvHlp_CallR0(PPDMDRVINS pDrvIns, uint32_t uOperation, uint64_t u64Arg)
1929{
1930 PDMDRV_ASSERT_DRVINS(pDrvIns);
1931 PVM pVM = pDrvIns->Internal.s.pVMR3;
1932 LogFlow(("pdmR3DrvHlp_CallR0: caller='%s'/%d: uOperation=%#x u64Arg=%#RX64\n",
1933 pDrvIns->pReg->szName, pDrvIns->iInstance, uOperation, u64Arg));
1934
1935 /*
1936 * Lazy resolve the ring-0 entry point.
1937 */
1938 int rc = VINF_SUCCESS;
1939 PFNPDMDRVREQHANDLERR0 pfnReqHandlerR0 = pDrvIns->Internal.s.pfnReqHandlerR0;
1940 if (RT_UNLIKELY(pfnReqHandlerR0 == NIL_RTR0PTR))
1941 {
1942 if (pDrvIns->pReg->fFlags & PDM_DRVREG_FLAGS_R0)
1943 {
1944 char szSymbol[ sizeof("drvR0") + sizeof(pDrvIns->pReg->szName) + sizeof("ReqHandler")];
1945 strcat(strcat(strcpy(szSymbol, "drvR0"), pDrvIns->pReg->szName), "ReqHandler");
1946 szSymbol[sizeof("drvR0") - 1] = RT_C_TO_UPPER(szSymbol[sizeof("drvR0") - 1]);
1947
1948 rc = PDMR3LdrGetSymbolR0Lazy(pVM, pDrvIns->pReg->szR0Mod, pDrvIns->Internal.s.pDrv->pszR0SearchPath, szSymbol,
1949 &pfnReqHandlerR0);
1950 if (RT_SUCCESS(rc))
1951 pDrvIns->Internal.s.pfnReqHandlerR0 = pfnReqHandlerR0;
1952 else
1953 pfnReqHandlerR0 = NIL_RTR0PTR;
1954 }
1955 else
1956 rc = VERR_ACCESS_DENIED;
1957 }
1958 if (RT_LIKELY(pfnReqHandlerR0 != NIL_RTR0PTR))
1959 {
1960 /*
1961 * Make the ring-0 call.
1962 */
1963 PDMDRIVERCALLREQHANDLERREQ Req;
1964 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1965 Req.Hdr.cbReq = sizeof(Req);
1966 Req.pDrvInsR0 = PDMDRVINS_2_R0PTR(pDrvIns);
1967 Req.uOperation = uOperation;
1968 Req.u32Alignment = 0;
1969 Req.u64Arg = u64Arg;
1970 rc = SUPR3CallVMMR0Ex(VMCC_GET_VMR0_FOR_CALL(pVM), NIL_VMCPUID, VMMR0_DO_PDM_DRIVER_CALL_REQ_HANDLER, 0, &Req.Hdr);
1971 }
1972
1973 LogFlow(("pdmR3DrvHlp_CallR0: caller='%s'/%d: returns %Rrc\n", pDrvIns->pReg->szName,
1974 pDrvIns->iInstance, rc));
1975 return rc;
1976}
1977
1978
1979/** @interface_method_impl{PDMDRVHLPR3,pfnBlkCacheRetain} */
1980static DECLCALLBACK(int) pdmR3DrvHlp_BlkCacheRetain(PPDMDRVINS pDrvIns, PPPDMBLKCACHE ppBlkCache,
1981 PFNPDMBLKCACHEXFERCOMPLETEDRV pfnXferComplete,
1982 PFNPDMBLKCACHEXFERENQUEUEDRV pfnXferEnqueue,
1983 PFNPDMBLKCACHEXFERENQUEUEDISCARDDRV pfnXferEnqueueDiscard,
1984 const char *pcszId)
1985{
1986 PDMDRV_ASSERT_DRVINS(pDrvIns);
1987 return PDMR3BlkCacheRetainDriver(pDrvIns->Internal.s.pVMR3, pDrvIns, ppBlkCache,
1988 pfnXferComplete, pfnXferEnqueue, pfnXferEnqueueDiscard, pcszId);
1989}
1990
1991
1992
1993/** @interface_method_impl{PDMDRVHLPR3,pfnVMGetSuspendReason} */
1994static DECLCALLBACK(VMSUSPENDREASON) pdmR3DrvHlp_VMGetSuspendReason(PPDMDRVINS pDrvIns)
1995{
1996 PDMDRV_ASSERT_DRVINS(pDrvIns);
1997 PVM pVM = pDrvIns->Internal.s.pVMR3;
1998 VM_ASSERT_EMT(pVM);
1999 VMSUSPENDREASON enmReason = VMR3GetSuspendReason(pVM->pUVM);
2000 LogFlow(("pdmR3DrvHlp_VMGetSuspendReason: caller='%s'/%d: returns %d\n",
2001 pDrvIns->pReg->szName, pDrvIns->iInstance, enmReason));
2002 return enmReason;
2003}
2004
2005
2006/** @interface_method_impl{PDMDRVHLPR3,pfnVMGetResumeReason} */
2007static DECLCALLBACK(VMRESUMEREASON) pdmR3DrvHlp_VMGetResumeReason(PPDMDRVINS pDrvIns)
2008{
2009 PDMDRV_ASSERT_DRVINS(pDrvIns);
2010 PVM pVM = pDrvIns->Internal.s.pVMR3;
2011 VM_ASSERT_EMT(pVM);
2012 VMRESUMEREASON enmReason = VMR3GetResumeReason(pVM->pUVM);
2013 LogFlow(("pdmR3DrvHlp_VMGetResumeReason: caller='%s'/%d: returns %d\n",
2014 pDrvIns->pReg->szName, pDrvIns->iInstance, enmReason));
2015 return enmReason;
2016}
2017
2018
2019/** @interface_method_impl{PDMDRVHLPR3,pfnQueryGenericUserObject} */
2020static DECLCALLBACK(void *) pdmR3DrvHlp_QueryGenericUserObject(PPDMDRVINS pDrvIns, PCRTUUID pUuid)
2021{
2022 PDMDRV_ASSERT_DRVINS(pDrvIns);
2023 LogFlow(("pdmR3DrvHlp_QueryGenericUserObject: caller='%s'/%d: pUuid=%p:%RTuuid\n",
2024 pDrvIns->pReg->szName, pDrvIns->iInstance, pUuid, pUuid));
2025
2026 void *pvRet;
2027 PUVM pUVM = pDrvIns->Internal.s.pVMR3->pUVM;
2028 if (pUVM->pVmm2UserMethods->pfnQueryGenericObject)
2029 pvRet = pUVM->pVmm2UserMethods->pfnQueryGenericObject(pUVM->pVmm2UserMethods, pUVM, pUuid);
2030 else
2031 pvRet = NULL;
2032
2033 LogRel(("pdmR3DrvHlp_QueryGenericUserObject: caller='%s'/%d: returns %#p for %RTuuid\n",
2034 pDrvIns->pReg->szName, pDrvIns->iInstance, pvRet, pUuid));
2035 return pvRet;
2036}
2037
2038
2039/**
2040 * The driver helper structure.
2041 */
2042const PDMDRVHLPR3 g_pdmR3DrvHlp =
2043{
2044 PDM_DRVHLPR3_VERSION,
2045 pdmR3DrvHlp_Attach,
2046 pdmR3DrvHlp_Detach,
2047 pdmR3DrvHlp_DetachSelf,
2048 pdmR3DrvHlp_MountPrepare,
2049 pdmR3DrvHlp_AssertEMT,
2050 pdmR3DrvHlp_AssertOther,
2051 pdmR3DrvHlp_VMSetErrorV,
2052 pdmR3DrvHlp_VMSetRuntimeErrorV,
2053 pdmR3DrvHlp_VMState,
2054 pdmR3DrvHlp_VMTeleportedAndNotFullyResumedYet,
2055 pdmR3DrvHlp_GetSupDrvSession,
2056 pdmR3DrvHlp_QueueCreate,
2057 pdmR3DrvHlp_QueueAlloc,
2058 pdmR3DrvHlp_QueueInsert,
2059 pdmR3DrvHlp_QueueFlushIfNecessary,
2060 pdmR3DrvHlp_TMGetVirtualFreq,
2061 pdmR3DrvHlp_TMGetVirtualTime,
2062 pdmR3DrvHlp_TimerCreate,
2063 pdmR3DrvHlp_SSMRegister,
2064 pdmR3DrvHlp_SSMDeregister,
2065 SSMR3PutStruct,
2066 SSMR3PutStructEx,
2067 SSMR3PutBool,
2068 SSMR3PutU8,
2069 SSMR3PutS8,
2070 SSMR3PutU16,
2071 SSMR3PutS16,
2072 SSMR3PutU32,
2073 SSMR3PutS32,
2074 SSMR3PutU64,
2075 SSMR3PutS64,
2076 SSMR3PutU128,
2077 SSMR3PutS128,
2078 SSMR3PutUInt,
2079 SSMR3PutSInt,
2080 SSMR3PutGCUInt,
2081 SSMR3PutGCUIntReg,
2082 SSMR3PutGCPhys32,
2083 SSMR3PutGCPhys64,
2084 SSMR3PutGCPhys,
2085 SSMR3PutGCPtr,
2086 SSMR3PutGCUIntPtr,
2087 SSMR3PutRCPtr,
2088 SSMR3PutIOPort,
2089 SSMR3PutSel,
2090 SSMR3PutMem,
2091 SSMR3PutStrZ,
2092 SSMR3GetStruct,
2093 SSMR3GetStructEx,
2094 SSMR3GetBool,
2095 SSMR3GetBoolV,
2096 SSMR3GetU8,
2097 SSMR3GetU8V,
2098 SSMR3GetS8,
2099 SSMR3GetS8V,
2100 SSMR3GetU16,
2101 SSMR3GetU16V,
2102 SSMR3GetS16,
2103 SSMR3GetS16V,
2104 SSMR3GetU32,
2105 SSMR3GetU32V,
2106 SSMR3GetS32,
2107 SSMR3GetS32V,
2108 SSMR3GetU64,
2109 SSMR3GetU64V,
2110 SSMR3GetS64,
2111 SSMR3GetS64V,
2112 SSMR3GetU128,
2113 SSMR3GetU128V,
2114 SSMR3GetS128,
2115 SSMR3GetS128V,
2116 SSMR3GetGCPhys32,
2117 SSMR3GetGCPhys32V,
2118 SSMR3GetGCPhys64,
2119 SSMR3GetGCPhys64V,
2120 SSMR3GetGCPhys,
2121 SSMR3GetGCPhysV,
2122 SSMR3GetUInt,
2123 SSMR3GetSInt,
2124 SSMR3GetGCUInt,
2125 SSMR3GetGCUIntReg,
2126 SSMR3GetGCPtr,
2127 SSMR3GetGCUIntPtr,
2128 SSMR3GetRCPtr,
2129 SSMR3GetIOPort,
2130 SSMR3GetSel,
2131 SSMR3GetMem,
2132 SSMR3GetStrZ,
2133 SSMR3GetStrZEx,
2134 SSMR3Skip,
2135 SSMR3SkipToEndOfUnit,
2136 SSMR3SetLoadError,
2137 SSMR3SetLoadErrorV,
2138 SSMR3SetCfgError,
2139 SSMR3SetCfgErrorV,
2140 SSMR3HandleGetStatus,
2141 SSMR3HandleGetAfter,
2142 SSMR3HandleIsLiveSave,
2143 SSMR3HandleMaxDowntime,
2144 SSMR3HandleHostBits,
2145 SSMR3HandleRevision,
2146 SSMR3HandleVersion,
2147 SSMR3HandleHostOSAndArch,
2148 CFGMR3Exists,
2149 CFGMR3QueryType,
2150 CFGMR3QuerySize,
2151 CFGMR3QueryInteger,
2152 CFGMR3QueryIntegerDef,
2153 CFGMR3QueryString,
2154 CFGMR3QueryStringDef,
2155 CFGMR3QueryPassword,
2156 CFGMR3QueryPasswordDef,
2157 CFGMR3QueryBytes,
2158 CFGMR3QueryU64,
2159 CFGMR3QueryU64Def,
2160 CFGMR3QueryS64,
2161 CFGMR3QueryS64Def,
2162 CFGMR3QueryU32,
2163 CFGMR3QueryU32Def,
2164 CFGMR3QueryS32,
2165 CFGMR3QueryS32Def,
2166 CFGMR3QueryU16,
2167 CFGMR3QueryU16Def,
2168 CFGMR3QueryS16,
2169 CFGMR3QueryS16Def,
2170 CFGMR3QueryU8,
2171 CFGMR3QueryU8Def,
2172 CFGMR3QueryS8,
2173 CFGMR3QueryS8Def,
2174 CFGMR3QueryBool,
2175 CFGMR3QueryBoolDef,
2176 CFGMR3QueryPort,
2177 CFGMR3QueryPortDef,
2178 CFGMR3QueryUInt,
2179 CFGMR3QueryUIntDef,
2180 CFGMR3QuerySInt,
2181 CFGMR3QuerySIntDef,
2182 CFGMR3QueryPtr,
2183 CFGMR3QueryPtrDef,
2184 CFGMR3QueryGCPtr,
2185 CFGMR3QueryGCPtrDef,
2186 CFGMR3QueryGCPtrU,
2187 CFGMR3QueryGCPtrUDef,
2188 CFGMR3QueryGCPtrS,
2189 CFGMR3QueryGCPtrSDef,
2190 CFGMR3QueryStringAlloc,
2191 CFGMR3QueryStringAllocDef,
2192 CFGMR3GetParent,
2193 CFGMR3GetChild,
2194 CFGMR3GetChildF,
2195 CFGMR3GetChildFV,
2196 CFGMR3GetFirstChild,
2197 CFGMR3GetNextChild,
2198 CFGMR3GetName,
2199 CFGMR3GetNameLen,
2200 CFGMR3AreChildrenValid,
2201 CFGMR3GetFirstValue,
2202 CFGMR3GetNextValue,
2203 CFGMR3GetValueName,
2204 CFGMR3GetValueNameLen,
2205 CFGMR3GetValueType,
2206 CFGMR3AreValuesValid,
2207 CFGMR3ValidateConfig,
2208 pdmR3DrvHlp_MMHeapFree,
2209 pdmR3DrvHlp_DBGFInfoRegister,
2210 pdmR3DrvHlp_DBGFInfoRegisterArgv,
2211 pdmR3DrvHlp_DBGFInfoDeregister,
2212 pdmR3DrvHlp_STAMRegister,
2213 pdmR3DrvHlp_STAMRegisterF,
2214 pdmR3DrvHlp_STAMRegisterV,
2215 pdmR3DrvHlp_STAMDeregister,
2216 pdmR3DrvHlp_SUPCallVMMR0Ex,
2217 pdmR3DrvHlp_USBRegisterHub,
2218 pdmR3DrvHlp_SetAsyncNotification,
2219 pdmR3DrvHlp_AsyncNotificationCompleted,
2220 pdmR3DrvHlp_ThreadCreate,
2221 PDMR3ThreadDestroy,
2222 PDMR3ThreadIAmSuspending,
2223 PDMR3ThreadIAmRunning,
2224 PDMR3ThreadSleep,
2225 PDMR3ThreadSuspend,
2226 PDMR3ThreadResume,
2227 pdmR3DrvHlp_AsyncCompletionTemplateCreate,
2228 PDMR3AsyncCompletionTemplateDestroy,
2229 PDMR3AsyncCompletionEpCreateForFile,
2230 PDMR3AsyncCompletionEpClose,
2231 PDMR3AsyncCompletionEpGetSize,
2232 PDMR3AsyncCompletionEpSetSize,
2233 PDMR3AsyncCompletionEpSetBwMgr,
2234 PDMR3AsyncCompletionEpFlush,
2235 PDMR3AsyncCompletionEpRead,
2236 PDMR3AsyncCompletionEpWrite,
2237 pdmR3DrvHlp_NetShaperAttach,
2238 pdmR3DrvHlp_NetShaperDetach,
2239 pdmR3DrvHlp_NetShaperAllocateBandwidth,
2240 pdmR3DrvHlp_LdrGetRCInterfaceSymbols,
2241 pdmR3DrvHlp_LdrGetR0InterfaceSymbols,
2242 pdmR3DrvHlp_CritSectInit,
2243 pdmR3DrvHlp_CritSectYield,
2244 pdmR3DrvHlp_CritSectEnter,
2245 pdmR3DrvHlp_CritSectEnterDebug,
2246 pdmR3DrvHlp_CritSectTryEnter,
2247 pdmR3DrvHlp_CritSectTryEnterDebug,
2248 pdmR3DrvHlp_CritSectLeave,
2249 pdmR3DrvHlp_CritSectIsOwner,
2250 pdmR3DrvHlp_CritSectIsInitialized,
2251 pdmR3DrvHlp_CritSectHasWaiters,
2252 pdmR3DrvHlp_CritSectGetRecursion,
2253 pdmR3DrvHlp_CritSectScheduleExitEvent,
2254 pdmR3DrvHlp_CritSectDelete,
2255 pdmR3DrvHlp_CallR0,
2256 pdmR3DrvHlp_BlkCacheRetain,
2257 PDMR3BlkCacheRelease,
2258 PDMR3BlkCacheClear,
2259 PDMR3BlkCacheSuspend,
2260 PDMR3BlkCacheResume,
2261 PDMR3BlkCacheIoXferComplete,
2262 PDMR3BlkCacheRead,
2263 PDMR3BlkCacheWrite,
2264 PDMR3BlkCacheFlush,
2265 PDMR3BlkCacheDiscard,
2266 pdmR3DrvHlp_VMGetSuspendReason,
2267 pdmR3DrvHlp_VMGetResumeReason,
2268 pdmR3DrvHlp_TimerSetMillies,
2269 pdmR3DrvHlp_STAMDeregisterByPrefix,
2270 pdmR3DrvHlp_QueryGenericUserObject,
2271 NULL,
2272 NULL,
2273 NULL,
2274 NULL,
2275 NULL,
2276 NULL,
2277 NULL,
2278 NULL,
2279 NULL,
2280 PDM_DRVHLPR3_VERSION /* u32TheEnd */
2281};
2282
2283/** @} */
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