VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMDevice.cpp@ 80309

Last change on this file since 80309 was 80281, checked in by vboxsync, 5 years ago

VMM,++: Refactoring code to use VMMC & VMMCPUCC. bugref:9217

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 40.5 KB
Line 
1/* $Id: PDMDevice.cpp 80281 2019-08-15 07:29:37Z vboxsync $ */
2/** @file
3 * PDM - Pluggable Device and Driver Manager, Device parts.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define VBOX_BUGREF_9217_PART_I
23#define LOG_GROUP LOG_GROUP_PDM_DEVICE
24#include "PDMInternal.h"
25#include <VBox/vmm/pdm.h>
26#include <VBox/vmm/mm.h>
27#include <VBox/vmm/pgm.h>
28#include <VBox/vmm/iom.h>
29#include <VBox/vmm/hm.h>
30#include <VBox/vmm/cfgm.h>
31#include <VBox/vmm/apic.h>
32#ifdef VBOX_WITH_REM
33# include <VBox/vmm/rem.h>
34#endif
35#include <VBox/vmm/dbgf.h>
36#include <VBox/vmm/vm.h>
37#include <VBox/vmm/uvm.h>
38#include <VBox/vmm/vmm.h>
39
40#include <VBox/version.h>
41#include <VBox/log.h>
42#include <VBox/err.h>
43#include <iprt/alloc.h>
44#include <iprt/alloca.h>
45#include <iprt/asm.h>
46#include <iprt/assert.h>
47#include <iprt/path.h>
48#include <iprt/semaphore.h>
49#include <iprt/string.h>
50#include <iprt/thread.h>
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56/**
57 * Internal callback structure pointer.
58 * The main purpose is to define the extra data we associate
59 * with PDMDEVREGCB so we can find the VM instance and so on.
60 */
61typedef struct PDMDEVREGCBINT
62{
63 /** The callback structure. */
64 PDMDEVREGCB Core;
65 /** A bit of padding. */
66 uint32_t u32[4];
67 /** VM Handle. */
68 PVM pVM;
69 /** Pointer to the configuration node the registrations should be
70 * associated with. Can be NULL. */
71 PCFGMNODE pCfgNode;
72} PDMDEVREGCBINT;
73/** Pointer to a PDMDEVREGCBINT structure. */
74typedef PDMDEVREGCBINT *PPDMDEVREGCBINT;
75/** Pointer to a const PDMDEVREGCBINT structure. */
76typedef const PDMDEVREGCBINT *PCPDMDEVREGCBINT;
77
78
79/*********************************************************************************************************************************
80* Internal Functions *
81*********************************************************************************************************************************/
82static DECLCALLBACK(int) pdmR3DevReg_Register(PPDMDEVREGCB pCallbacks, PCPDMDEVREG pReg);
83static int pdmR3DevLoadModules(PVM pVM);
84static int pdmR3DevLoad(PVM pVM, PPDMDEVREGCBINT pRegCB, const char *pszFilename, const char *pszName);
85
86
87
88
89/**
90 * This function will initialize the devices for this VM instance.
91 *
92 *
93 * First of all this mean loading the builtin device and letting them
94 * register themselves. Beyond that any additional device modules are
95 * loaded and called for registration.
96 *
97 * Then the device configuration is enumerated, the instantiation order
98 * is determined, and finally they are instantiated.
99 *
100 * After all devices have been successfully instantiated the primary
101 * PCI Bus device is called to emulate the PCI BIOS, i.e. making the
102 * resource assignments. If there is no PCI device, this step is of course
103 * skipped.
104 *
105 * Finally the init completion routines of the instantiated devices
106 * are called.
107 *
108 * @returns VBox status code.
109 * @param pVM The cross context VM structure.
110 */
111int pdmR3DevInit(PVM pVM)
112{
113 LogFlow(("pdmR3DevInit:\n"));
114
115 AssertRelease(!(RT_UOFFSETOF(PDMDEVINS, achInstanceData) & 15));
116 AssertRelease(sizeof(pVM->pdm.s.pDevInstances->Internal.s) <= sizeof(pVM->pdm.s.pDevInstances->Internal.padding));
117
118 /*
119 * Load device modules.
120 */
121 int rc = pdmR3DevLoadModules(pVM);
122 if (RT_FAILURE(rc))
123 return rc;
124
125#ifdef VBOX_WITH_USB
126 /* ditto for USB Devices. */
127 rc = pdmR3UsbLoadModules(pVM);
128 if (RT_FAILURE(rc))
129 return rc;
130#endif
131
132 /*
133 * Get the RC & R0 devhlps and create the devhlp R3 task queue.
134 */
135 PCPDMDEVHLPRC pHlpRC = NIL_RTRCPTR;
136 if (VM_IS_RAW_MODE_ENABLED(pVM))
137 {
138 rc = PDMR3LdrGetSymbolRC(pVM, NULL, "g_pdmRCDevHlp", &pHlpRC);
139 AssertReleaseRCReturn(rc, rc);
140 }
141
142 PCPDMDEVHLPR0 pHlpR0;
143 rc = PDMR3LdrGetSymbolR0(pVM, NULL, "g_pdmR0DevHlp", &pHlpR0);
144 AssertReleaseRCReturn(rc, rc);
145
146 rc = PDMR3QueueCreateInternal(pVM, sizeof(PDMDEVHLPTASK), 8, 0, pdmR3DevHlpQueueConsumer, true, "DevHlp",
147 &pVM->pdm.s.pDevHlpQueueR3);
148 AssertRCReturn(rc, rc);
149 pVM->pdm.s.pDevHlpQueueR0 = PDMQueueR0Ptr(pVM->pdm.s.pDevHlpQueueR3);
150 pVM->pdm.s.pDevHlpQueueRC = PDMQueueRCPtr(pVM->pdm.s.pDevHlpQueueR3);
151
152
153 /*
154 *
155 * Enumerate the device instance configurations
156 * and come up with a instantiation order.
157 *
158 */
159 /* Switch to /Devices, which contains the device instantiations. */
160 PCFGMNODE pDevicesNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "Devices");
161
162 /*
163 * Count the device instances.
164 */
165 PCFGMNODE pCur;
166 PCFGMNODE pInstanceNode;
167 unsigned cDevs = 0;
168 for (pCur = CFGMR3GetFirstChild(pDevicesNode); pCur; pCur = CFGMR3GetNextChild(pCur))
169 for (pInstanceNode = CFGMR3GetFirstChild(pCur); pInstanceNode; pInstanceNode = CFGMR3GetNextChild(pInstanceNode))
170 cDevs++;
171 if (!cDevs)
172 {
173 Log(("PDM: No devices were configured!\n"));
174 return VINF_SUCCESS;
175 }
176 Log2(("PDM: cDevs=%u\n", cDevs));
177
178 /*
179 * Collect info on each device instance.
180 */
181 struct DEVORDER
182 {
183 /** Configuration node. */
184 PCFGMNODE pNode;
185 /** Pointer to device. */
186 PPDMDEV pDev;
187 /** Init order. */
188 uint32_t u32Order;
189 /** VBox instance number. */
190 uint32_t iInstance;
191 } *paDevs = (struct DEVORDER *)alloca(sizeof(paDevs[0]) * (cDevs + 1)); /* (One extra for swapping) */
192 Assert(paDevs);
193 unsigned i = 0;
194 for (pCur = CFGMR3GetFirstChild(pDevicesNode); pCur; pCur = CFGMR3GetNextChild(pCur))
195 {
196 /* Get the device name. */
197 char szName[sizeof(paDevs[0].pDev->pReg->szName)];
198 rc = CFGMR3GetName(pCur, szName, sizeof(szName));
199 AssertMsgRCReturn(rc, ("Configuration error: device name is too long (or something)! rc=%Rrc\n", rc), rc);
200
201 /* Find the device. */
202 PPDMDEV pDev = pdmR3DevLookup(pVM, szName);
203 AssertLogRelMsgReturn(pDev, ("Configuration error: device '%s' not found!\n", szName), VERR_PDM_DEVICE_NOT_FOUND);
204
205 /* Configured priority or use default based on device class? */
206 uint32_t u32Order;
207 rc = CFGMR3QueryU32(pCur, "Priority", &u32Order);
208 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
209 {
210 uint32_t u32 = pDev->pReg->fClass;
211 for (u32Order = 1; !(u32 & u32Order); u32Order <<= 1)
212 /* nop */;
213 }
214 else
215 AssertMsgRCReturn(rc, ("Configuration error: reading \"Priority\" for the '%s' device failed rc=%Rrc!\n", szName, rc), rc);
216
217 /* Enumerate the device instances. */
218 uint32_t const iStart = i;
219 for (pInstanceNode = CFGMR3GetFirstChild(pCur); pInstanceNode; pInstanceNode = CFGMR3GetNextChild(pInstanceNode))
220 {
221 paDevs[i].pNode = pInstanceNode;
222 paDevs[i].pDev = pDev;
223 paDevs[i].u32Order = u32Order;
224
225 /* Get the instance number. */
226 char szInstance[32];
227 rc = CFGMR3GetName(pInstanceNode, szInstance, sizeof(szInstance));
228 AssertMsgRCReturn(rc, ("Configuration error: instance name is too long (or something)! rc=%Rrc\n", rc), rc);
229 char *pszNext = NULL;
230 rc = RTStrToUInt32Ex(szInstance, &pszNext, 0, &paDevs[i].iInstance);
231 AssertMsgRCReturn(rc, ("Configuration error: RTStrToInt32Ex failed on the instance name '%s'! rc=%Rrc\n", szInstance, rc), rc);
232 AssertMsgReturn(!*pszNext, ("Configuration error: the instance name '%s' isn't all digits. (%s)\n", szInstance, pszNext), VERR_INVALID_PARAMETER);
233
234 /* next instance */
235 i++;
236 }
237
238 /* check the number of instances */
239 if (i - iStart > pDev->pReg->cMaxInstances)
240 AssertLogRelMsgFailedReturn(("Configuration error: Too many instances of %s was configured: %u, max %u\n",
241 szName, i - iStart, pDev->pReg->cMaxInstances),
242 VERR_PDM_TOO_MANY_DEVICE_INSTANCES);
243 } /* devices */
244 Assert(i == cDevs);
245
246 /*
247 * Sort (bubble) the device array ascending on u32Order and instance number
248 * for a device.
249 */
250 unsigned c = cDevs - 1;
251 while (c)
252 {
253 unsigned j = 0;
254 for (i = 0; i < c; i++)
255 if ( paDevs[i].u32Order > paDevs[i + 1].u32Order
256 || ( paDevs[i].u32Order == paDevs[i + 1].u32Order
257 && paDevs[i].iInstance > paDevs[i + 1].iInstance
258 && paDevs[i].pDev == paDevs[i + 1].pDev) )
259 {
260 paDevs[cDevs] = paDevs[i + 1];
261 paDevs[i + 1] = paDevs[i];
262 paDevs[i] = paDevs[cDevs];
263 j = i;
264 }
265 c = j;
266 }
267
268
269 /*
270 *
271 * Instantiate the devices.
272 *
273 */
274 for (i = 0; i < cDevs; i++)
275 {
276 /*
277 * Gather a bit of config.
278 */
279 /* trusted */
280 bool fTrusted;
281 rc = CFGMR3QueryBool(paDevs[i].pNode, "Trusted", &fTrusted);
282 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
283 fTrusted = false;
284 else if (RT_FAILURE(rc))
285 {
286 AssertMsgFailed(("configuration error: failed to query boolean \"Trusted\", rc=%Rrc\n", rc));
287 return rc;
288 }
289 /* config node */
290 PCFGMNODE pConfigNode = CFGMR3GetChild(paDevs[i].pNode, "Config");
291 if (!pConfigNode)
292 {
293 rc = CFGMR3InsertNode(paDevs[i].pNode, "Config", &pConfigNode);
294 if (RT_FAILURE(rc))
295 {
296 AssertMsgFailed(("Failed to create Config node! rc=%Rrc\n", rc));
297 return rc;
298 }
299 }
300 CFGMR3SetRestrictedRoot(pConfigNode);
301
302 /*
303 * Allocate the device instance and critical section.
304 */
305 AssertReturn(paDevs[i].pDev->cInstances < paDevs[i].pDev->pReg->cMaxInstances, VERR_PDM_TOO_MANY_DEVICE_INSTANCES);
306 size_t cb = RT_UOFFSETOF_DYN(PDMDEVINS, achInstanceData[paDevs[i].pDev->pReg->cbInstance]);
307 cb = RT_ALIGN_Z(cb, 16);
308 PPDMDEVINS pDevIns;
309 if (paDevs[i].pDev->pReg->fFlags & (PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0))
310 rc = MMR3HyperAllocOnceNoRel(pVM, cb, 0, MM_TAG_PDM_DEVICE, (void **)&pDevIns);
311 else
312 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_DEVICE, cb, (void **)&pDevIns);
313 AssertLogRelMsgRCReturn(rc,
314 ("Failed to allocate %d bytes of instance data for device '%s'. rc=%Rrc\n",
315 cb, paDevs[i].pDev->pReg->szName, rc),
316 rc);
317 PPDMCRITSECT pCritSect;
318 if (paDevs[i].pDev->pReg->fFlags & (PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0))
319 rc = MMHyperAlloc(pVM, sizeof(*pCritSect), 0, MM_TAG_PDM_DEVICE, (void **)&pCritSect);
320 else
321 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_DEVICE, sizeof(*pCritSect), (void **)&pCritSect);
322 AssertLogRelMsgRCReturn(rc, ("Failed to allocate a critical section for the device (%Rrc)\n", rc), rc);
323
324 /*
325 * Initialize it.
326 */
327 pDevIns->u32Version = PDM_DEVINS_VERSION;
328 pDevIns->iInstance = paDevs[i].iInstance;
329 //pDevIns->Internal.s.pNextR3 = NULL;
330 //pDevIns->Internal.s.pPerDeviceNextR3 = NULL;
331 pDevIns->Internal.s.pDevR3 = paDevs[i].pDev;
332 pDevIns->Internal.s.pVMR3 = pVM;
333#ifdef VBOX_BUGREF_9217
334 pDevIns->Internal.s.pVMR0 = pVM->pVMR0ForCall;
335#else
336 pDevIns->Internal.s.pVMR0 = pVM->pVMR0;
337#endif
338 pDevIns->Internal.s.pVMRC = pVM->pVMRC;
339 //pDevIns->Internal.s.pLunsR3 = NULL;
340 pDevIns->Internal.s.pCfgHandle = paDevs[i].pNode;
341 //pDevIns->Internal.s.pHeadPciDevR3 = NULL;
342 //pDevIns->Internal.s.pHeadPciDevR0 = 0;
343 //pDevIns->Internal.s.pHeadPciDevRC = 0;
344 pDevIns->Internal.s.fIntFlags = PDMDEVINSINT_FLAGS_SUSPENDED;
345 //pDevIns->Internal.s.uLastIrqTag = 0;
346 pDevIns->pHlpR3 = fTrusted ? &g_pdmR3DevHlpTrusted : &g_pdmR3DevHlpUnTrusted;
347 pDevIns->pHlpRC = pHlpRC;
348 pDevIns->pHlpR0 = pHlpR0;
349 pDevIns->pReg = paDevs[i].pDev->pReg;
350 pDevIns->pCfg = pConfigNode;
351 //pDevIns->IBase.pfnQueryInterface = NULL;
352 //pDevIns->fTracing = 0;
353 pDevIns->idTracing = ++pVM->pdm.s.idTracingDev;
354 pDevIns->pvInstanceDataR3 = &pDevIns->achInstanceData[0];
355 pDevIns->pvInstanceDataRC = pDevIns->pReg->fFlags & PDM_DEVREG_FLAGS_RC
356 ? MMHyperR3ToRC(pVM, pDevIns->pvInstanceDataR3) : NIL_RTRCPTR;
357 pDevIns->pvInstanceDataR0 = pDevIns->pReg->fFlags & PDM_DEVREG_FLAGS_R0
358 ? MMHyperR3ToR0(pVM, pDevIns->pvInstanceDataR3) : NIL_RTR0PTR;
359
360 pDevIns->pCritSectRoR3 = pCritSect;
361 pDevIns->pCritSectRoRC = pDevIns->pReg->fFlags & PDM_DEVREG_FLAGS_RC
362 ? MMHyperR3ToRC(pVM, pCritSect) : NIL_RTRCPTR;
363 pDevIns->pCritSectRoR0 = pDevIns->pReg->fFlags & PDM_DEVREG_FLAGS_R0
364 ? MMHyperR3ToR0(pVM, pCritSect) : NIL_RTR0PTR;
365
366 rc = pdmR3CritSectInitDeviceAuto(pVM, pDevIns, pCritSect, RT_SRC_POS,
367 "%s#%uAuto", pDevIns->pReg->szName, pDevIns->iInstance);
368 AssertLogRelRCReturn(rc, rc);
369
370 /*
371 * Link it into all the lists.
372 */
373 /* The global instance FIFO. */
374 PPDMDEVINS pPrev1 = pVM->pdm.s.pDevInstances;
375 if (!pPrev1)
376 pVM->pdm.s.pDevInstances = pDevIns;
377 else
378 {
379 while (pPrev1->Internal.s.pNextR3)
380 pPrev1 = pPrev1->Internal.s.pNextR3;
381 pPrev1->Internal.s.pNextR3 = pDevIns;
382 }
383
384 /* The per device instance FIFO. */
385 PPDMDEVINS pPrev2 = paDevs[i].pDev->pInstances;
386 if (!pPrev2)
387 paDevs[i].pDev->pInstances = pDevIns;
388 else
389 {
390 while (pPrev2->Internal.s.pPerDeviceNextR3)
391 pPrev2 = pPrev2->Internal.s.pPerDeviceNextR3;
392 pPrev2->Internal.s.pPerDeviceNextR3 = pDevIns;
393 }
394
395 /*
396 * Call the constructor.
397 */
398 paDevs[i].pDev->cInstances++;
399 Log(("PDM: Constructing device '%s' instance %d...\n", pDevIns->pReg->szName, pDevIns->iInstance));
400 rc = pDevIns->pReg->pfnConstruct(pDevIns, pDevIns->iInstance, pDevIns->pCfg);
401 if (RT_FAILURE(rc))
402 {
403 LogRel(("PDM: Failed to construct '%s'/%d! %Rra\n", pDevIns->pReg->szName, pDevIns->iInstance, rc));
404 paDevs[i].pDev->cInstances--;
405 /* Because we're damn lazy, the destructor will be called even if
406 the constructor fails. So, no unlinking. */
407 return rc == VERR_VERSION_MISMATCH ? VERR_PDM_DEVICE_VERSION_MISMATCH : rc;
408 }
409 } /* for device instances */
410
411#ifdef VBOX_WITH_USB
412 /* ditto for USB Devices. */
413 rc = pdmR3UsbInstantiateDevices(pVM);
414 if (RT_FAILURE(rc))
415 return rc;
416#endif
417
418 LogFlow(("pdmR3DevInit: returns %Rrc\n", VINF_SUCCESS));
419 return VINF_SUCCESS;
420}
421
422
423/**
424 * Performs the init complete callback after ring-0 and raw-mode has been
425 * initialized.
426 *
427 * @returns VBox status code.
428 * @param pVM The cross context VM structure.
429 */
430int pdmR3DevInitComplete(PVM pVM)
431{
432 int rc;
433
434 /*
435 * Iterate thru the device instances and work the callback.
436 */
437 for (PPDMDEVINS pDevIns = pVM->pdm.s.pDevInstances; pDevIns; pDevIns = pDevIns->Internal.s.pNextR3)
438 {
439 if (pDevIns->pReg->pfnInitComplete)
440 {
441 PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
442 rc = pDevIns->pReg->pfnInitComplete(pDevIns);
443 PDMCritSectLeave(pDevIns->pCritSectRoR3);
444 if (RT_FAILURE(rc))
445 {
446 AssertMsgFailed(("InitComplete on device '%s'/%d failed with rc=%Rrc\n",
447 pDevIns->pReg->szName, pDevIns->iInstance, rc));
448 return rc;
449 }
450 }
451 }
452
453#ifdef VBOX_WITH_USB
454 rc = pdmR3UsbVMInitComplete(pVM);
455 if (RT_FAILURE(rc))
456 {
457 Log(("pdmR3DevInit: returns %Rrc\n", rc));
458 return rc;
459 }
460#endif
461
462 LogFlow(("pdmR3DevInit: returns %Rrc\n", VINF_SUCCESS));
463 return VINF_SUCCESS;
464}
465
466
467/**
468 * Lookups a device structure by name.
469 * @internal
470 */
471PPDMDEV pdmR3DevLookup(PVM pVM, const char *pszName)
472{
473 size_t cchName = strlen(pszName);
474 for (PPDMDEV pDev = pVM->pdm.s.pDevs; pDev; pDev = pDev->pNext)
475 if ( pDev->cchName == cchName
476 && !strcmp(pDev->pReg->szName, pszName))
477 return pDev;
478 return NULL;
479}
480
481
482/**
483 * Loads the device modules.
484 *
485 * @returns VBox status code.
486 * @param pVM The cross context VM structure.
487 */
488static int pdmR3DevLoadModules(PVM pVM)
489{
490 /*
491 * Initialize the callback structure.
492 */
493 PDMDEVREGCBINT RegCB;
494 RegCB.Core.u32Version = PDM_DEVREG_CB_VERSION;
495 RegCB.Core.pfnRegister = pdmR3DevReg_Register;
496 RegCB.pVM = pVM;
497 RegCB.pCfgNode = NULL;
498
499 /*
500 * Load the internal VMM APIC device.
501 */
502 int rc = APICR3RegisterDevice(&RegCB.Core);
503 AssertRCReturn(rc, rc);
504
505 /*
506 * Load the builtin module.
507 */
508 PCFGMNODE pDevicesNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "PDM/Devices");
509 bool fLoadBuiltin;
510 rc = CFGMR3QueryBool(pDevicesNode, "LoadBuiltin", &fLoadBuiltin);
511 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
512 fLoadBuiltin = true;
513 else if (RT_FAILURE(rc))
514 {
515 AssertMsgFailed(("Configuration error: Querying boolean \"LoadBuiltin\" failed with %Rrc\n", rc));
516 return rc;
517 }
518 if (fLoadBuiltin)
519 {
520 /* make filename */
521 char *pszFilename = pdmR3FileR3("VBoxDD", true /*fShared*/);
522 if (!pszFilename)
523 return VERR_NO_TMP_MEMORY;
524 rc = pdmR3DevLoad(pVM, &RegCB, pszFilename, "VBoxDD");
525 RTMemTmpFree(pszFilename);
526 if (RT_FAILURE(rc))
527 return rc;
528
529 /* make filename */
530 pszFilename = pdmR3FileR3("VBoxDD2", true /*fShared*/);
531 if (!pszFilename)
532 return VERR_NO_TMP_MEMORY;
533 rc = pdmR3DevLoad(pVM, &RegCB, pszFilename, "VBoxDD2");
534 RTMemTmpFree(pszFilename);
535 if (RT_FAILURE(rc))
536 return rc;
537 }
538
539 /*
540 * Load additional device modules.
541 */
542 PCFGMNODE pCur;
543 for (pCur = CFGMR3GetFirstChild(pDevicesNode); pCur; pCur = CFGMR3GetNextChild(pCur))
544 {
545 /*
546 * Get the name and path.
547 */
548 char szName[PDMMOD_NAME_LEN];
549 rc = CFGMR3GetName(pCur, &szName[0], sizeof(szName));
550 if (rc == VERR_CFGM_NOT_ENOUGH_SPACE)
551 {
552 AssertMsgFailed(("configuration error: The module name is too long, cchName=%zu.\n", CFGMR3GetNameLen(pCur)));
553 return VERR_PDM_MODULE_NAME_TOO_LONG;
554 }
555 else if (RT_FAILURE(rc))
556 {
557 AssertMsgFailed(("CFGMR3GetName -> %Rrc.\n", rc));
558 return rc;
559 }
560
561 /* the path is optional, if no path the module name + path is used. */
562 char szFilename[RTPATH_MAX];
563 rc = CFGMR3QueryString(pCur, "Path", &szFilename[0], sizeof(szFilename));
564 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
565 strcpy(szFilename, szName);
566 else if (RT_FAILURE(rc))
567 {
568 AssertMsgFailed(("configuration error: Failure to query the module path, rc=%Rrc.\n", rc));
569 return rc;
570 }
571
572 /* prepend path? */
573 if (!RTPathHavePath(szFilename))
574 {
575 char *psz = pdmR3FileR3(szFilename, false /*fShared*/);
576 if (!psz)
577 return VERR_NO_TMP_MEMORY;
578 size_t cch = strlen(psz) + 1;
579 if (cch > sizeof(szFilename))
580 {
581 RTMemTmpFree(psz);
582 AssertMsgFailed(("Filename too long! cch=%d '%s'\n", cch, psz));
583 return VERR_FILENAME_TOO_LONG;
584 }
585 memcpy(szFilename, psz, cch);
586 RTMemTmpFree(psz);
587 }
588
589 /*
590 * Load the module and register it's devices.
591 */
592 RegCB.pCfgNode = pCur;
593 rc = pdmR3DevLoad(pVM, &RegCB, szFilename, szName);
594 if (RT_FAILURE(rc))
595 return rc;
596 }
597
598 return VINF_SUCCESS;
599}
600
601
602/**
603 * Loads one device module and call the registration entry point.
604 *
605 * @returns VBox status code.
606 * @param pVM The cross context VM structure.
607 * @param pRegCB The registration callback stuff.
608 * @param pszFilename Module filename.
609 * @param pszName Module name.
610 */
611static int pdmR3DevLoad(PVM pVM, PPDMDEVREGCBINT pRegCB, const char *pszFilename, const char *pszName)
612{
613 /*
614 * Load it.
615 */
616 int rc = pdmR3LoadR3U(pVM->pUVM, pszFilename, pszName);
617 if (RT_SUCCESS(rc))
618 {
619 /*
620 * Get the registration export and call it.
621 */
622 FNPDMVBOXDEVICESREGISTER *pfnVBoxDevicesRegister;
623 rc = PDMR3LdrGetSymbolR3(pVM, pszName, "VBoxDevicesRegister", (void **)&pfnVBoxDevicesRegister);
624 if (RT_SUCCESS(rc))
625 {
626 Log(("PDM: Calling VBoxDevicesRegister (%p) of %s (%s)\n", pfnVBoxDevicesRegister, pszName, pszFilename));
627 rc = pfnVBoxDevicesRegister(&pRegCB->Core, VBOX_VERSION);
628 if (RT_SUCCESS(rc))
629 Log(("PDM: Successfully loaded device module %s (%s).\n", pszName, pszFilename));
630 else
631 AssertMsgFailed(("VBoxDevicesRegister failed with rc=%Rrc for module %s (%s)\n", rc, pszName, pszFilename));
632 }
633 else
634 {
635 AssertMsgFailed(("Failed to locate 'VBoxDevicesRegister' in %s (%s) rc=%Rrc\n", pszName, pszFilename, rc));
636 if (rc == VERR_SYMBOL_NOT_FOUND)
637 rc = VERR_PDM_NO_REGISTRATION_EXPORT;
638 }
639 }
640 else
641 AssertMsgFailed(("Failed to load %s %s!\n", pszFilename, pszName));
642 return rc;
643}
644
645
646/**
647 * @interface_method_impl{PDMDEVREGCB,pfnRegister}
648 */
649static DECLCALLBACK(int) pdmR3DevReg_Register(PPDMDEVREGCB pCallbacks, PCPDMDEVREG pReg)
650{
651 /*
652 * Validate the registration structure.
653 */
654 Assert(pReg);
655 AssertMsgReturn(pReg->u32Version == PDM_DEVREG_VERSION,
656 ("Unknown struct version %#x!\n", pReg->u32Version),
657 VERR_PDM_UNKNOWN_DEVREG_VERSION);
658
659 AssertMsgReturn( pReg->szName[0]
660 && strlen(pReg->szName) < sizeof(pReg->szName)
661 && pdmR3IsValidName(pReg->szName),
662 ("Invalid name '%.*s'\n", sizeof(pReg->szName), pReg->szName),
663 VERR_PDM_INVALID_DEVICE_REGISTRATION);
664 AssertMsgReturn( !(pReg->fFlags & PDM_DEVREG_FLAGS_RC)
665 || ( pReg->szRCMod[0]
666 && strlen(pReg->szRCMod) < sizeof(pReg->szRCMod)),
667 ("Invalid GC module name '%s' - (Device %s)\n", pReg->szRCMod, pReg->szName),
668 VERR_PDM_INVALID_DEVICE_REGISTRATION);
669 AssertMsgReturn( !(pReg->fFlags & PDM_DEVREG_FLAGS_R0)
670 || ( pReg->szR0Mod[0]
671 && strlen(pReg->szR0Mod) < sizeof(pReg->szR0Mod)),
672 ("Invalid R0 module name '%s' - (Device %s)\n", pReg->szR0Mod, pReg->szName),
673 VERR_PDM_INVALID_DEVICE_REGISTRATION);
674 AssertMsgReturn((pReg->fFlags & PDM_DEVREG_FLAGS_HOST_BITS_MASK) == PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT,
675 ("Invalid host bits flags! fFlags=%#x (Device %s)\n", pReg->fFlags, pReg->szName),
676 VERR_PDM_INVALID_DEVICE_HOST_BITS);
677 AssertMsgReturn((pReg->fFlags & PDM_DEVREG_FLAGS_GUEST_BITS_MASK),
678 ("Invalid guest bits flags! fFlags=%#x (Device %s)\n", pReg->fFlags, pReg->szName),
679 VERR_PDM_INVALID_DEVICE_REGISTRATION);
680 AssertMsgReturn(pReg->fClass,
681 ("No class! (Device %s)\n", pReg->szName),
682 VERR_PDM_INVALID_DEVICE_REGISTRATION);
683 AssertMsgReturn(pReg->cMaxInstances > 0,
684 ("Max instances %u! (Device %s)\n", pReg->cMaxInstances, pReg->szName),
685 VERR_PDM_INVALID_DEVICE_REGISTRATION);
686 AssertMsgReturn(pReg->cbInstance <= (uint32_t)(pReg->fFlags & (PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0) ? 96 * _1K : _1M),
687 ("Instance size %d bytes! (Device %s)\n", pReg->cbInstance, pReg->szName),
688 VERR_PDM_INVALID_DEVICE_REGISTRATION);
689 AssertMsgReturn(pReg->pfnConstruct,
690 ("No constructor! (Device %s)\n", pReg->szName),
691 VERR_PDM_INVALID_DEVICE_REGISTRATION);
692 AssertLogRelMsgReturn((pReg->fFlags & PDM_DEVREG_FLAGS_GUEST_BITS_MASK) == PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT,
693 ("PDM: Rejected device '%s' because it didn't match the guest bits.\n", pReg->szName),
694 VERR_PDM_INVALID_DEVICE_GUEST_BITS);
695 AssertLogRelMsg(pReg->u32VersionEnd == PDM_DEVREG_VERSION,
696 ("u32VersionEnd=%#x, expected %#x. (szName=%s)\n",
697 pReg->u32VersionEnd, PDM_DEVREG_VERSION, pReg->szName));
698
699 /*
700 * Check for duplicate and find FIFO entry at the same time.
701 */
702 PCPDMDEVREGCBINT pRegCB = (PCPDMDEVREGCBINT)pCallbacks;
703 PPDMDEV pDevPrev = NULL;
704 PPDMDEV pDev = pRegCB->pVM->pdm.s.pDevs;
705 for (; pDev; pDevPrev = pDev, pDev = pDev->pNext)
706 AssertMsgReturn(strcmp(pDev->pReg->szName, pReg->szName),
707 ("Device '%s' already exists\n", pReg->szName),
708 VERR_PDM_DEVICE_NAME_CLASH);
709
710 /*
711 * Allocate new device structure, initialize and insert it into the list.
712 */
713 int rc;
714 pDev = (PPDMDEV)MMR3HeapAlloc(pRegCB->pVM, MM_TAG_PDM_DEVICE, sizeof(*pDev));
715 if (pDev)
716 {
717 pDev->pNext = NULL;
718 pDev->cInstances = 0;
719 pDev->pInstances = NULL;
720 pDev->pReg = pReg;
721 pDev->cchName = (uint32_t)strlen(pReg->szName);
722 rc = CFGMR3QueryStringAllocDef( pRegCB->pCfgNode, "RCSearchPath", &pDev->pszRCSearchPath, NULL);
723 if (RT_SUCCESS(rc))
724 rc = CFGMR3QueryStringAllocDef(pRegCB->pCfgNode, "R0SearchPath", &pDev->pszR0SearchPath, NULL);
725 if (RT_SUCCESS(rc))
726 {
727 if (pDevPrev)
728 pDevPrev->pNext = pDev;
729 else
730 pRegCB->pVM->pdm.s.pDevs = pDev;
731 Log(("PDM: Registered device '%s'\n", pReg->szName));
732 return VINF_SUCCESS;
733 }
734
735 MMR3HeapFree(pDev);
736 }
737 else
738 rc = VERR_NO_MEMORY;
739 return rc;
740}
741
742
743/**
744 * Locates a LUN.
745 *
746 * @returns VBox status code.
747 * @param pVM The cross context VM structure.
748 * @param pszDevice Device name.
749 * @param iInstance Device instance.
750 * @param iLun The Logical Unit to obtain the interface of.
751 * @param ppLun Where to store the pointer to the LUN if found.
752 * @thread Try only do this in EMT...
753 */
754int pdmR3DevFindLun(PVM pVM, const char *pszDevice, unsigned iInstance, unsigned iLun, PPPDMLUN ppLun)
755{
756 /*
757 * Iterate registered devices looking for the device.
758 */
759 size_t cchDevice = strlen(pszDevice);
760 for (PPDMDEV pDev = pVM->pdm.s.pDevs; pDev; pDev = pDev->pNext)
761 {
762 if ( pDev->cchName == cchDevice
763 && !memcmp(pDev->pReg->szName, pszDevice, cchDevice))
764 {
765 /*
766 * Iterate device instances.
767 */
768 for (PPDMDEVINS pDevIns = pDev->pInstances; pDevIns; pDevIns = pDevIns->Internal.s.pPerDeviceNextR3)
769 {
770 if (pDevIns->iInstance == iInstance)
771 {
772 /*
773 * Iterate luns.
774 */
775 for (PPDMLUN pLun = pDevIns->Internal.s.pLunsR3; pLun; pLun = pLun->pNext)
776 {
777 if (pLun->iLun == iLun)
778 {
779 *ppLun = pLun;
780 return VINF_SUCCESS;
781 }
782 }
783 return VERR_PDM_LUN_NOT_FOUND;
784 }
785 }
786 return VERR_PDM_DEVICE_INSTANCE_NOT_FOUND;
787 }
788 }
789 return VERR_PDM_DEVICE_NOT_FOUND;
790}
791
792
793/**
794 * Attaches a preconfigured driver to an existing device instance.
795 *
796 * This is used to change drivers and suchlike at runtime.
797 *
798 * @returns VBox status code.
799 * @param pUVM The user mode VM handle.
800 * @param pszDevice Device name.
801 * @param iInstance Device instance.
802 * @param iLun The Logical Unit to obtain the interface of.
803 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
804 * @param ppBase Where to store the base interface pointer. Optional.
805 * @thread EMT
806 */
807VMMR3DECL(int) PDMR3DeviceAttach(PUVM pUVM, const char *pszDevice, unsigned iInstance, unsigned iLun, uint32_t fFlags, PPPDMIBASE ppBase)
808{
809 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
810 PVM pVM = pUVM->pVM;
811 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
812 VM_ASSERT_EMT(pVM);
813 LogFlow(("PDMR3DeviceAttach: pszDevice=%p:{%s} iInstance=%d iLun=%d fFlags=%#x ppBase=%p\n",
814 pszDevice, pszDevice, iInstance, iLun, fFlags, ppBase));
815
816 /*
817 * Find the LUN in question.
818 */
819 PPDMLUN pLun;
820 int rc = pdmR3DevFindLun(pVM, pszDevice, iInstance, iLun, &pLun);
821 if (RT_SUCCESS(rc))
822 {
823 /*
824 * Can we attach anything at runtime?
825 */
826 PPDMDEVINS pDevIns = pLun->pDevIns;
827 if (pDevIns->pReg->pfnAttach)
828 {
829 if (!pLun->pTop)
830 {
831 PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
832 rc = pDevIns->pReg->pfnAttach(pDevIns, iLun, fFlags);
833 PDMCritSectLeave(pDevIns->pCritSectRoR3);
834 }
835 else
836 rc = VERR_PDM_DRIVER_ALREADY_ATTACHED;
837 }
838 else
839 rc = VERR_PDM_DEVICE_NO_RT_ATTACH;
840
841 if (ppBase)
842 *ppBase = pLun->pTop ? &pLun->pTop->IBase : NULL;
843 }
844 else if (ppBase)
845 *ppBase = NULL;
846
847 if (ppBase)
848 LogFlow(("PDMR3DeviceAttach: returns %Rrc *ppBase=%p\n", rc, *ppBase));
849 else
850 LogFlow(("PDMR3DeviceAttach: returns %Rrc\n", rc));
851 return rc;
852}
853
854
855/**
856 * Detaches a driver chain from an existing device instance.
857 *
858 * This is used to change drivers and suchlike at runtime.
859 *
860 * @returns VBox status code.
861 * @param pUVM The user mode VM handle.
862 * @param pszDevice Device name.
863 * @param iInstance Device instance.
864 * @param iLun The Logical Unit to obtain the interface of.
865 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
866 * @thread EMT
867 */
868VMMR3DECL(int) PDMR3DeviceDetach(PUVM pUVM, const char *pszDevice, unsigned iInstance, unsigned iLun, uint32_t fFlags)
869{
870 return PDMR3DriverDetach(pUVM, pszDevice, iInstance, iLun, NULL, 0, fFlags);
871}
872
873
874/**
875 * References the critical section associated with a device for the use by a
876 * timer or similar created by the device.
877 *
878 * @returns Pointer to the critical section.
879 * @param pVM The cross context VM structure.
880 * @param pDevIns The device instance in question.
881 *
882 * @internal
883 */
884VMMR3_INT_DECL(PPDMCRITSECT) PDMR3DevGetCritSect(PVM pVM, PPDMDEVINS pDevIns)
885{
886 VM_ASSERT_EMT(pVM); RT_NOREF_PV(pVM);
887 VM_ASSERT_STATE(pVM, VMSTATE_CREATING);
888 AssertPtr(pDevIns);
889
890 PPDMCRITSECT pCritSect = pDevIns->pCritSectRoR3;
891 AssertPtr(pCritSect);
892 pCritSect->s.fUsedByTimerOrSimilar = true;
893
894 return pCritSect;
895}
896
897
898/**
899 * Attaches a preconfigured driver to an existing device or driver instance.
900 *
901 * This is used to change drivers and suchlike at runtime. The driver or device
902 * at the end of the chain will be told to attach to whatever is configured
903 * below it.
904 *
905 * @returns VBox status code.
906 * @param pUVM The user mode VM handle.
907 * @param pszDevice Device name.
908 * @param iInstance Device instance.
909 * @param iLun The Logical Unit to obtain the interface of.
910 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
911 * @param ppBase Where to store the base interface pointer. Optional.
912 *
913 * @thread EMT
914 */
915VMMR3DECL(int) PDMR3DriverAttach(PUVM pUVM, const char *pszDevice, unsigned iInstance, unsigned iLun, uint32_t fFlags, PPPDMIBASE ppBase)
916{
917 LogFlow(("PDMR3DriverAttach: pszDevice=%p:{%s} iInstance=%d iLun=%d fFlags=%#x ppBase=%p\n",
918 pszDevice, pszDevice, iInstance, iLun, fFlags, ppBase));
919 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
920 PVM pVM = pUVM->pVM;
921 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
922 VM_ASSERT_EMT(pVM);
923
924 if (ppBase)
925 *ppBase = NULL;
926
927 /*
928 * Find the LUN in question.
929 */
930 PPDMLUN pLun;
931 int rc = pdmR3DevFindLun(pVM, pszDevice, iInstance, iLun, &pLun);
932 if (RT_SUCCESS(rc))
933 {
934 /*
935 * Anything attached to the LUN?
936 */
937 PPDMDRVINS pDrvIns = pLun->pTop;
938 if (!pDrvIns)
939 {
940 /* No, ask the device to attach to the new stuff. */
941 PPDMDEVINS pDevIns = pLun->pDevIns;
942 if (pDevIns->pReg->pfnAttach)
943 {
944 PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
945 rc = pDevIns->pReg->pfnAttach(pDevIns, iLun, fFlags);
946 if (RT_SUCCESS(rc) && ppBase)
947 *ppBase = pLun->pTop ? &pLun->pTop->IBase : NULL;
948 PDMCritSectLeave(pDevIns->pCritSectRoR3);
949 }
950 else
951 rc = VERR_PDM_DEVICE_NO_RT_ATTACH;
952 }
953 else
954 {
955 /* Yes, find the bottom most driver and ask it to attach to the new stuff. */
956 while (pDrvIns->Internal.s.pDown)
957 pDrvIns = pDrvIns->Internal.s.pDown;
958 if (pDrvIns->pReg->pfnAttach)
959 {
960 rc = pDrvIns->pReg->pfnAttach(pDrvIns, fFlags);
961 if (RT_SUCCESS(rc) && ppBase)
962 *ppBase = pDrvIns->Internal.s.pDown
963 ? &pDrvIns->Internal.s.pDown->IBase
964 : NULL;
965 }
966 else
967 rc = VERR_PDM_DRIVER_NO_RT_ATTACH;
968 }
969 }
970
971 if (ppBase)
972 LogFlow(("PDMR3DriverAttach: returns %Rrc *ppBase=%p\n", rc, *ppBase));
973 else
974 LogFlow(("PDMR3DriverAttach: returns %Rrc\n", rc));
975 return rc;
976}
977
978
979/**
980 * Detaches the specified driver instance.
981 *
982 * This is used to replumb drivers at runtime for simulating hot plugging and
983 * media changes.
984 *
985 * This is a superset of PDMR3DeviceDetach. It allows detaching drivers from
986 * any driver or device by specifying the driver to start detaching at. The
987 * only prerequisite is that the driver or device above implements the
988 * pfnDetach callback (PDMDRVREG / PDMDEVREG).
989 *
990 * @returns VBox status code.
991 * @param pUVM The user mode VM handle.
992 * @param pszDevice Device name.
993 * @param iDevIns Device instance.
994 * @param iLun The Logical Unit in which to look for the driver.
995 * @param pszDriver The name of the driver which to detach. If NULL
996 * then the entire driver chain is detatched.
997 * @param iOccurrence The occurrence of that driver in the chain. This is
998 * usually 0.
999 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1000 * @thread EMT
1001 */
1002VMMR3DECL(int) PDMR3DriverDetach(PUVM pUVM, const char *pszDevice, unsigned iDevIns, unsigned iLun,
1003 const char *pszDriver, unsigned iOccurrence, uint32_t fFlags)
1004{
1005 LogFlow(("PDMR3DriverDetach: pszDevice=%p:{%s} iDevIns=%u iLun=%u pszDriver=%p:{%s} iOccurrence=%u fFlags=%#x\n",
1006 pszDevice, pszDevice, iDevIns, iLun, pszDriver, pszDriver, iOccurrence, fFlags));
1007 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1008 PVM pVM = pUVM->pVM;
1009 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
1010 VM_ASSERT_EMT(pVM);
1011 AssertPtr(pszDevice);
1012 AssertPtrNull(pszDriver);
1013 Assert(iOccurrence == 0 || pszDriver);
1014 Assert(!(fFlags & ~(PDM_TACH_FLAGS_NOT_HOT_PLUG)));
1015
1016 /*
1017 * Find the LUN in question.
1018 */
1019 PPDMLUN pLun;
1020 int rc = pdmR3DevFindLun(pVM, pszDevice, iDevIns, iLun, &pLun);
1021 if (RT_SUCCESS(rc))
1022 {
1023 /*
1024 * Locate the driver.
1025 */
1026 PPDMDRVINS pDrvIns = pLun->pTop;
1027 if (pDrvIns)
1028 {
1029 if (pszDriver)
1030 {
1031 while (pDrvIns)
1032 {
1033 if (!strcmp(pDrvIns->pReg->szName, pszDriver))
1034 {
1035 if (iOccurrence == 0)
1036 break;
1037 iOccurrence--;
1038 }
1039 pDrvIns = pDrvIns->Internal.s.pDown;
1040 }
1041 }
1042 if (pDrvIns)
1043 rc = pdmR3DrvDetach(pDrvIns, fFlags);
1044 else
1045 rc = VERR_PDM_DRIVER_INSTANCE_NOT_FOUND;
1046 }
1047 else
1048 rc = VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN;
1049 }
1050
1051 LogFlow(("PDMR3DriverDetach: returns %Rrc\n", rc));
1052 return rc;
1053}
1054
1055
1056/**
1057 * Runtime detach and reattach of a new driver chain or sub chain.
1058 *
1059 * This is intended to be called on a non-EMT thread, this will instantiate the
1060 * new driver (sub-)chain, and then the EMTs will do the actual replumbing. The
1061 * destruction of the old driver chain will be taken care of on the calling
1062 * thread.
1063 *
1064 * @returns VBox status code.
1065 * @param pUVM The user mode VM handle.
1066 * @param pszDevice Device name.
1067 * @param iDevIns Device instance.
1068 * @param iLun The Logical Unit in which to look for the driver.
1069 * @param pszDriver The name of the driver which to detach and replace.
1070 * If NULL then the entire driver chain is to be
1071 * reattached.
1072 * @param iOccurrence The occurrence of that driver in the chain. This is
1073 * usually 0.
1074 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1075 * @param pCfg The configuration of the new driver chain that is
1076 * going to be attached. The subtree starts with the
1077 * node containing a Driver key, a Config subtree and
1078 * optionally an AttachedDriver subtree.
1079 * If this parameter is NULL, then this call will work
1080 * like at a non-pause version of PDMR3DriverDetach.
1081 * @param ppBase Where to store the base interface pointer to the new
1082 * driver. Optional.
1083 *
1084 * @thread Any thread. The EMTs will be involved at some point though.
1085 */
1086VMMR3DECL(int) PDMR3DriverReattach(PUVM pUVM, const char *pszDevice, unsigned iDevIns, unsigned iLun,
1087 const char *pszDriver, unsigned iOccurrence, uint32_t fFlags,
1088 PCFGMNODE pCfg, PPPDMIBASE ppBase)
1089{
1090 NOREF(pUVM); NOREF(pszDevice); NOREF(iDevIns); NOREF(iLun); NOREF(pszDriver); NOREF(iOccurrence);
1091 NOREF(fFlags); NOREF(pCfg); NOREF(ppBase);
1092 return VERR_NOT_IMPLEMENTED;
1093}
1094
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