summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMR3/PDMDevice.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/VMM/VMMR3/PDMDevice.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/VMM/VMMR3/PDMDevice.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/PDMDevice.cpp1291
1 files changed, 1291 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/PDMDevice.cpp b/src/VBox/VMM/VMMR3/PDMDevice.cpp
new file mode 100644
index 00000000..155d4294
--- /dev/null
+++ b/src/VBox/VMM/VMMR3/PDMDevice.cpp
@@ -0,0 +1,1291 @@
+/* $Id: PDMDevice.cpp $ */
+/** @file
+ * PDM - Pluggable Device and Driver Manager, Device parts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_PDM_DEVICE
+#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
+#include "PDMInternal.h"
+#include <VBox/vmm/pdm.h>
+#include <VBox/vmm/apic.h>
+#include <VBox/vmm/cfgm.h>
+#include <VBox/vmm/dbgf.h>
+#include <VBox/vmm/hm.h>
+#include <VBox/vmm/mm.h>
+#include <VBox/vmm/iom.h>
+#include <VBox/vmm/pgm.h>
+#include <VBox/vmm/vm.h>
+#include <VBox/vmm/uvm.h>
+#include <VBox/vmm/vmm.h>
+
+#include <VBox/version.h>
+#include <VBox/log.h>
+#include <VBox/msi.h>
+#include <VBox/err.h>
+#include <iprt/alloc.h>
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Internal callback structure pointer.
+ * The main purpose is to define the extra data we associate
+ * with PDMDEVREGCB so we can find the VM instance and so on.
+ */
+typedef struct PDMDEVREGCBINT
+{
+ /** The callback structure. */
+ PDMDEVREGCB Core;
+ /** A bit of padding. */
+ uint32_t u32[4];
+ /** VM Handle. */
+ PVM pVM;
+ /** Pointer to the configuration node the registrations should be
+ * associated with. Can be NULL. */
+ PCFGMNODE pCfgNode;
+} PDMDEVREGCBINT;
+/** Pointer to a PDMDEVREGCBINT structure. */
+typedef PDMDEVREGCBINT *PPDMDEVREGCBINT;
+/** Pointer to a const PDMDEVREGCBINT structure. */
+typedef const PDMDEVREGCBINT *PCPDMDEVREGCBINT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) pdmR3DevReg_Register(PPDMDEVREGCB pCallbacks, PCPDMDEVREG pReg);
+static int pdmR3DevLoadModules(PVM pVM);
+static int pdmR3DevLoad(PVM pVM, PPDMDEVREGCBINT pRegCB, const char *pszFilename, const char *pszName);
+
+
+
+
+/**
+ * This function will initialize the devices for this VM instance.
+ *
+ *
+ * First of all this mean loading the builtin device and letting them
+ * register themselves. Beyond that any additional device modules are
+ * loaded and called for registration.
+ *
+ * Then the device configuration is enumerated, the instantiation order
+ * is determined, and finally they are instantiated.
+ *
+ * After all devices have been successfully instantiated the primary
+ * PCI Bus device is called to emulate the PCI BIOS, i.e. making the
+ * resource assignments. If there is no PCI device, this step is of course
+ * skipped.
+ *
+ * Finally the init completion routines of the instantiated devices
+ * are called.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ */
+int pdmR3DevInit(PVM pVM)
+{
+ LogFlow(("pdmR3DevInit:\n"));
+
+ AssertRelease(!(RT_UOFFSETOF(PDMDEVINS, achInstanceData) & 15));
+ AssertRelease(sizeof(pVM->pdm.s.pDevInstances->Internal.s) <= sizeof(pVM->pdm.s.pDevInstances->Internal.padding));
+
+ /*
+ * Load device modules.
+ */
+ int rc = pdmR3DevLoadModules(pVM);
+ if (RT_FAILURE(rc))
+ return rc;
+
+#ifdef VBOX_WITH_USB
+ /* ditto for USB Devices. */
+ rc = pdmR3UsbLoadModules(pVM);
+ if (RT_FAILURE(rc))
+ return rc;
+#endif
+
+ /*
+ * Get the RC & R0 devhlps and create the devhlp R3 task queue.
+ */
+ rc = PDMR3QueueCreateInternal(pVM, sizeof(PDMDEVHLPTASK), pVM->cCpus * 8, 0, pdmR3DevHlpQueueConsumer, true, "DevHlp",
+ &pVM->pdm.s.hDevHlpQueue);
+ AssertRCReturn(rc, rc);
+
+ /*
+ *
+ * Enumerate the device instance configurations
+ * and come up with a instantiation order.
+ *
+ */
+ /* Switch to /Devices, which contains the device instantiations. */
+ PCFGMNODE pDevicesNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "Devices");
+
+ /*
+ * Count the device instances.
+ */
+ PCFGMNODE pCur;
+ PCFGMNODE pInstanceNode;
+ unsigned cDevs = 0;
+ for (pCur = CFGMR3GetFirstChild(pDevicesNode); pCur; pCur = CFGMR3GetNextChild(pCur))
+ for (pInstanceNode = CFGMR3GetFirstChild(pCur); pInstanceNode; pInstanceNode = CFGMR3GetNextChild(pInstanceNode))
+ cDevs++;
+ if (!cDevs)
+ {
+ Log(("PDM: No devices were configured!\n"));
+ return VINF_SUCCESS;
+ }
+ Log2(("PDM: cDevs=%u\n", cDevs));
+
+ /*
+ * Collect info on each device instance.
+ */
+ struct DEVORDER
+ {
+ /** Configuration node. */
+ PCFGMNODE pNode;
+ /** Pointer to device. */
+ PPDMDEV pDev;
+ /** Init order. */
+ uint32_t u32Order;
+ /** VBox instance number. */
+ uint32_t iInstance;
+ } *paDevs = (struct DEVORDER *)alloca(sizeof(paDevs[0]) * (cDevs + 1)); /* (One extra for swapping) */
+ Assert(paDevs);
+ unsigned i = 0;
+ for (pCur = CFGMR3GetFirstChild(pDevicesNode); pCur; pCur = CFGMR3GetNextChild(pCur))
+ {
+ /* Get the device name. */
+ char szName[sizeof(paDevs[0].pDev->pReg->szName)];
+ rc = CFGMR3GetName(pCur, szName, sizeof(szName));
+ AssertMsgRCReturn(rc, ("Configuration error: device name is too long (or something)! rc=%Rrc\n", rc), rc);
+
+ /* Find the device. */
+ PPDMDEV pDev = pdmR3DevLookup(pVM, szName);
+ AssertLogRelMsgReturn(pDev, ("Configuration error: device '%s' not found!\n", szName), VERR_PDM_DEVICE_NOT_FOUND);
+
+ /* Configured priority or use default based on device class? */
+ uint32_t u32Order;
+ rc = CFGMR3QueryU32(pCur, "Priority", &u32Order);
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ {
+ uint32_t u32 = pDev->pReg->fClass;
+ for (u32Order = 1; !(u32 & u32Order); u32Order <<= 1)
+ /* nop */;
+ }
+ else
+ AssertMsgRCReturn(rc, ("Configuration error: reading \"Priority\" for the '%s' device failed rc=%Rrc!\n", szName, rc), rc);
+
+ /* Enumerate the device instances. */
+ uint32_t const iStart = i;
+ for (pInstanceNode = CFGMR3GetFirstChild(pCur); pInstanceNode; pInstanceNode = CFGMR3GetNextChild(pInstanceNode))
+ {
+ paDevs[i].pNode = pInstanceNode;
+ paDevs[i].pDev = pDev;
+ paDevs[i].u32Order = u32Order;
+
+ /* Get the instance number. */
+ char szInstance[32];
+ rc = CFGMR3GetName(pInstanceNode, szInstance, sizeof(szInstance));
+ AssertMsgRCReturn(rc, ("Configuration error: instance name is too long (or something)! rc=%Rrc\n", rc), rc);
+ char *pszNext = NULL;
+ rc = RTStrToUInt32Ex(szInstance, &pszNext, 0, &paDevs[i].iInstance);
+ AssertMsgRCReturn(rc, ("Configuration error: RTStrToInt32Ex failed on the instance name '%s'! rc=%Rrc\n", szInstance, rc), rc);
+ AssertMsgReturn(!*pszNext, ("Configuration error: the instance name '%s' isn't all digits. (%s)\n", szInstance, pszNext), VERR_INVALID_PARAMETER);
+
+ /* next instance */
+ i++;
+ }
+
+ /* check the number of instances */
+ if (i - iStart > pDev->pReg->cMaxInstances)
+ AssertLogRelMsgFailedReturn(("Configuration error: Too many instances of %s was configured: %u, max %u\n",
+ szName, i - iStart, pDev->pReg->cMaxInstances),
+ VERR_PDM_TOO_MANY_DEVICE_INSTANCES);
+ } /* devices */
+ Assert(i == cDevs);
+
+ /*
+ * Sort (bubble) the device array ascending on u32Order and instance number
+ * for a device.
+ */
+ unsigned c = cDevs - 1;
+ while (c)
+ {
+ unsigned j = 0;
+ for (i = 0; i < c; i++)
+ if ( paDevs[i].u32Order > paDevs[i + 1].u32Order
+ || ( paDevs[i].u32Order == paDevs[i + 1].u32Order
+ && paDevs[i].iInstance > paDevs[i + 1].iInstance
+ && paDevs[i].pDev == paDevs[i + 1].pDev) )
+ {
+ paDevs[cDevs] = paDevs[i + 1];
+ paDevs[i + 1] = paDevs[i];
+ paDevs[i] = paDevs[cDevs];
+ j = i;
+ }
+ c = j;
+ }
+
+
+ /*
+ *
+ * Instantiate the devices.
+ *
+ */
+ for (i = 0; i < cDevs; i++)
+ {
+ PDMDEVREGR3 const * const pReg = paDevs[i].pDev->pReg;
+
+ /*
+ * Gather a bit of config.
+ */
+ /* trusted */
+ bool fTrusted;
+ rc = CFGMR3QueryBool(paDevs[i].pNode, "Trusted", &fTrusted);
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ fTrusted = false;
+ else if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("configuration error: failed to query boolean \"Trusted\", rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /* RZEnabled, R0Enabled, RCEnabled*/
+ bool fR0Enabled = false;
+ bool fRCEnabled = false;
+ if ( (pReg->fFlags & (PDM_DEVREG_FLAGS_R0 | PDM_DEVREG_FLAGS_RC))
+#ifdef VBOX_WITH_PGM_NEM_MODE
+ && !PGMR3IsNemModeEnabled(pVM) /* No ring-0 in simplified memory mode. */
+#endif
+ && !SUPR3IsDriverless())
+ {
+ if (pReg->fFlags & PDM_DEVREG_FLAGS_R0)
+ {
+ if (pReg->fFlags & PDM_DEVREG_FLAGS_REQUIRE_R0)
+ fR0Enabled = true;
+ else
+ {
+ rc = CFGMR3QueryBoolDef(paDevs[i].pNode, "R0Enabled", &fR0Enabled,
+ !(pReg->fFlags & PDM_DEVREG_FLAGS_OPT_IN_R0));
+ AssertLogRelRCReturn(rc, rc);
+ }
+ }
+
+ if (pReg->fFlags & PDM_DEVREG_FLAGS_RC)
+ {
+ if (pReg->fFlags & PDM_DEVREG_FLAGS_REQUIRE_RC)
+ fRCEnabled = true;
+ else
+ {
+ rc = CFGMR3QueryBoolDef(paDevs[i].pNode, "RCEnabled", &fRCEnabled,
+ !(pReg->fFlags & PDM_DEVREG_FLAGS_OPT_IN_RC));
+ AssertLogRelRCReturn(rc, rc);
+ }
+ fRCEnabled = false;
+ }
+ }
+
+#ifdef VBOX_WITH_DBGF_TRACING
+ DBGFTRACEREVTSRC hDbgfTraceEvtSrc = NIL_DBGFTRACEREVTSRC;
+ bool fTracingEnabled = false;
+ bool fGCPhysRwAll = false;
+ rc = CFGMR3QueryBoolDef(paDevs[i].pNode, "TracingEnabled", &fTracingEnabled,
+ false);
+ AssertLogRelRCReturn(rc, rc);
+ if (fTracingEnabled)
+ {
+ rc = CFGMR3QueryBoolDef(paDevs[i].pNode, "TraceAllGstMemRw", &fGCPhysRwAll,
+ false);
+ AssertLogRelRCReturn(rc, rc);
+
+ /* Traced devices need to be trusted for now. */
+ if (fTrusted)
+ {
+ rc = DBGFR3TracerRegisterEvtSrc(pVM, pReg->szName, &hDbgfTraceEvtSrc);
+ AssertLogRelRCReturn(rc, rc);
+ }
+ else
+ AssertMsgFailedReturn(("configuration error: Device tracing needs a trusted device\n"), VERR_INCOMPATIBLE_CONFIG);
+ }
+#endif
+
+ /* config node */
+ PCFGMNODE pConfigNode = CFGMR3GetChild(paDevs[i].pNode, "Config");
+ if (!pConfigNode)
+ {
+ rc = CFGMR3InsertNode(paDevs[i].pNode, "Config", &pConfigNode);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("Failed to create Config node! rc=%Rrc\n", rc));
+ return rc;
+ }
+ }
+ CFGMR3SetRestrictedRoot(pConfigNode);
+
+ /*
+ * Allocate the device instance and critical section.
+ */
+ AssertLogRelReturn(paDevs[i].pDev->cInstances < pReg->cMaxInstances,
+ VERR_PDM_TOO_MANY_DEVICE_INSTANCES);
+ PPDMDEVINS pDevIns;
+ PPDMCRITSECT pCritSect;
+ if (fR0Enabled || fRCEnabled)
+ {
+ AssertLogRel(fR0Enabled /* not possible to just enabled raw-mode atm. */);
+
+ rc = PDMR3LdrLoadR0(pVM->pUVM, pReg->pszR0Mod, paDevs[i].pDev->pszR0SearchPath);
+ if (RT_FAILURE(rc))
+ return VMR3SetError(pVM->pUVM, rc, RT_SRC_POS, "Failed to load ring-0 module '%s' for device '%s'",
+ pReg->pszR0Mod, pReg->szName);
+
+ PDMDEVICECREATEREQ Req;
+ Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ Req.Hdr.cbReq = sizeof(Req);
+ Req.pDevInsR3 = NULL;
+ /** @todo Add tracer id in request so R0 can set up DEVINSR0 properly. */
+ Req.fFlags = pReg->fFlags;
+ Req.fClass = pReg->fClass;
+ Req.cMaxInstances = pReg->cMaxInstances;
+ Req.uSharedVersion = pReg->uSharedVersion;
+ Req.cbInstanceShared = pReg->cbInstanceShared;
+ Req.cbInstanceR3 = pReg->cbInstanceCC;
+ Req.cbInstanceRC = pReg->cbInstanceRC;
+ Req.cMaxPciDevices = pReg->cMaxPciDevices;
+ Req.cMaxMsixVectors = pReg->cMaxMsixVectors;
+ Req.iInstance = paDevs[i].iInstance;
+ Req.fRCEnabled = fRCEnabled;
+ Req.afReserved[0] = false;
+ Req.afReserved[1] = false;
+ Req.afReserved[2] = false;
+#ifdef VBOX_WITH_DBGF_TRACING
+ Req.hDbgfTracerEvtSrc = hDbgfTraceEvtSrc;
+#else
+ Req.hDbgfTracerEvtSrc = NIL_DBGFTRACEREVTSRC;
+#endif
+ rc = RTStrCopy(Req.szDevName, sizeof(Req.szDevName), pReg->szName);
+ AssertLogRelRCReturn(rc, rc);
+ rc = RTStrCopy(Req.szModName, sizeof(Req.szModName), pReg->pszR0Mod);
+ AssertLogRelRCReturn(rc, rc);
+
+ rc = VMMR3CallR0Emt(pVM, pVM->apCpusR3[0], VMMR0_DO_PDM_DEVICE_CREATE, 0, &Req.Hdr);
+ AssertLogRelMsgRCReturn(rc, ("VMMR0_DO_PDM_DEVICE_CREATE for %s failed: %Rrc\n", pReg->szName, rc), rc);
+
+ pDevIns = Req.pDevInsR3;
+ pCritSect = pDevIns->pCritSectRoR3;
+
+ Assert(pDevIns->Internal.s.fIntFlags & PDMDEVINSINT_FLAGS_R0_ENABLED);
+ AssertLogRelReturn(pDevIns->Internal.s.idxR0Device < PDM_MAX_RING0_DEVICE_INSTANCES, VERR_PDM_DEV_IPE_1);
+ AssertLogRelReturn(pVM->pdm.s.apDevRing0Instances[pDevIns->Internal.s.idxR0Device] == pDevIns, VERR_PDM_DEV_IPE_1);
+ }
+ else
+ {
+ /* The code in this else branch works by the same rules as the PDMR0Device.cpp
+ code, except there is only the ring-3 components of the device instance.
+ Changes here may need to be reflected in PDMR0DEvice.cpp and vice versa! */
+ uint32_t cb = RT_UOFFSETOF_DYN(PDMDEVINS, achInstanceData[pReg->cbInstanceCC]);
+ cb = RT_ALIGN_32(cb, 64);
+ uint32_t const offShared = cb;
+ cb += RT_ALIGN_32(pReg->cbInstanceShared, 64);
+ uint32_t const cbCritSect = RT_ALIGN_32(sizeof(*pCritSect), 64);
+ cb += cbCritSect;
+ uint32_t const cbMsixState = RT_ALIGN_32(pReg->cMaxMsixVectors * 16 + (pReg->cMaxMsixVectors + 7) / 8, _4K);
+ uint32_t const cbPciDev = RT_ALIGN_32(RT_UOFFSETOF_DYN(PDMPCIDEV, abMsixState[cbMsixState]), 64);
+ uint32_t const cPciDevs = RT_MIN(pReg->cMaxPciDevices, 1024);
+ uint32_t const cbPciDevs = cbPciDev * cPciDevs;
+ cb += cbPciDevs;
+ AssertLogRelMsgReturn(cb <= PDM_MAX_DEVICE_INSTANCE_SIZE_R3,
+ ("Device %s total instance size is to big: %u, max %u\n",
+ pReg->szName, cb, PDM_MAX_DEVICE_INSTANCE_SIZE_R3),
+ VERR_ALLOCATION_TOO_BIG);
+
+#if 0 /* Several devices demands cacheline aligned data, if not page aligned. Real problem in NEM mode. */
+ rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_DEVICE, cb, (void **)&pDevIns);
+ AssertLogRelMsgRCReturn(rc, ("Failed to allocate %zu bytes of instance data for device '%s'. rc=%Rrc\n",
+ cb, pReg->szName, rc), rc);
+#else
+ pDevIns = (PPDMDEVINS)RTMemPageAllocZ(cb);
+ AssertLogRelMsgReturn(pDevIns, ("Failed to allocate %zu bytes of instance data for device '%s'\n", cb, pReg->szName),
+ VERR_NO_PAGE_MEMORY);
+#endif
+
+ /* Initialize it: */
+ pDevIns->u32Version = PDM_DEVINSR3_VERSION;
+ pDevIns->iInstance = paDevs[i].iInstance;
+ pDevIns->cbRing3 = cb;
+ //pDevIns->fR0Enabled = false;
+ //pDevIns->fRCEnabled = false;
+ pDevIns->pvInstanceDataR3 = (uint8_t *)pDevIns + offShared;
+ pDevIns->pvInstanceDataForR3 = &pDevIns->achInstanceData[0];
+ pCritSect = (PPDMCRITSECT)((uint8_t *)pDevIns + offShared + RT_ALIGN_32(pReg->cbInstanceShared, 64));
+ pDevIns->pCritSectRoR3 = pCritSect;
+ pDevIns->cbPciDev = cbPciDev;
+ pDevIns->cPciDevs = cPciDevs;
+ for (uint32_t iPciDev = 0; iPciDev < cPciDevs; iPciDev++)
+ {
+ PPDMPCIDEV pPciDev = (PPDMPCIDEV)((uint8_t *)pDevIns->pCritSectRoR3 + cbCritSect + cbPciDev * iPciDev);
+ if (iPciDev < RT_ELEMENTS(pDevIns->apPciDevs))
+ pDevIns->apPciDevs[iPciDev] = pPciDev;
+ pPciDev->cbConfig = _4K;
+ pPciDev->cbMsixState = cbMsixState;
+ pPciDev->idxSubDev = (uint16_t)iPciDev;
+ pPciDev->Int.s.idxSubDev = (uint16_t)iPciDev;
+ pPciDev->u32Magic = PDMPCIDEV_MAGIC;
+ }
+ }
+
+ pDevIns->pHlpR3 = fTrusted ? &g_pdmR3DevHlpTrusted : &g_pdmR3DevHlpUnTrusted;
+ pDevIns->pReg = pReg;
+ pDevIns->pCfg = pConfigNode;
+ //pDevIns->IBase.pfnQueryInterface = NULL;
+ //pDevIns->fTracing = 0;
+ pDevIns->idTracing = ++pVM->pdm.s.idTracingDev;
+
+ //pDevIns->Internal.s.pNextR3 = NULL;
+ //pDevIns->Internal.s.pPerDeviceNextR3 = NULL;
+ pDevIns->Internal.s.pDevR3 = paDevs[i].pDev;
+ //pDevIns->Internal.s.pLunsR3 = NULL;
+ //pDevIns->Internal.s.pfnAsyncNotify = NULL;
+ pDevIns->Internal.s.pCfgHandle = paDevs[i].pNode;
+ pDevIns->Internal.s.pVMR3 = pVM;
+#ifdef VBOX_WITH_DBGF_TRACING
+ pDevIns->Internal.s.hDbgfTraceEvtSrc = hDbgfTraceEvtSrc;
+#else
+ pDevIns->Internal.s.hDbgfTraceEvtSrc = NIL_DBGFTRACEREVTSRC;
+#endif
+ //pDevIns->Internal.s.pHeadPciDevR3 = NULL;
+ pDevIns->Internal.s.fIntFlags |= PDMDEVINSINT_FLAGS_SUSPENDED;
+ //pDevIns->Internal.s.uLastIrqTag = 0;
+
+ rc = pdmR3CritSectInitDeviceAuto(pVM, pDevIns, pCritSect, RT_SRC_POS,
+ "%s#%uAuto", pDevIns->pReg->szName, pDevIns->iInstance);
+ AssertLogRelRCReturn(rc, rc);
+
+ /*
+ * Link it into all the lists.
+ */
+ /* The global instance FIFO. */
+ PPDMDEVINS pPrev1 = pVM->pdm.s.pDevInstances;
+ if (!pPrev1)
+ pVM->pdm.s.pDevInstances = pDevIns;
+ else
+ {
+ while (pPrev1->Internal.s.pNextR3)
+ pPrev1 = pPrev1->Internal.s.pNextR3;
+ pPrev1->Internal.s.pNextR3 = pDevIns;
+ }
+
+ /* The per device instance FIFO. */
+ PPDMDEVINS pPrev2 = paDevs[i].pDev->pInstances;
+ if (!pPrev2)
+ paDevs[i].pDev->pInstances = pDevIns;
+ else
+ {
+ while (pPrev2->Internal.s.pPerDeviceNextR3)
+ pPrev2 = pPrev2->Internal.s.pPerDeviceNextR3;
+ pPrev2->Internal.s.pPerDeviceNextR3 = pDevIns;
+ }
+
+#ifdef VBOX_WITH_DBGF_TRACING
+ /*
+ * Allocate memory for the MMIO/IO port registration tracking if DBGF tracing is enabled.
+ */
+ if (hDbgfTraceEvtSrc != NIL_DBGFTRACEREVTSRC)
+ {
+ pDevIns->Internal.s.paDbgfTraceTrack = (PPDMDEVINSDBGFTRACK)RTMemAllocZ(PDM_MAX_DEVICE_DBGF_TRACING_TRACK);
+ if (!pDevIns->Internal.s.paDbgfTraceTrack)
+ {
+ LogRel(("PDM: Failed to construct '%s'/%d! %Rra\n", pDevIns->pReg->szName, pDevIns->iInstance, VERR_NO_MEMORY));
+ if (VMR3GetErrorCount(pVM->pUVM) == 0)
+ VMSetError(pVM, rc, RT_SRC_POS, "Failed to construct device '%s' instance #%u",
+ pDevIns->pReg->szName, pDevIns->iInstance);
+ paDevs[i].pDev->cInstances--;
+ return VERR_NO_MEMORY;
+ }
+
+ pDevIns->Internal.s.idxDbgfTraceTrackNext = 0;
+ pDevIns->Internal.s.cDbgfTraceTrackMax = PDM_MAX_DEVICE_DBGF_TRACING_TRACK / sizeof(PDMDEVINSDBGFTRACK);
+ pDevIns->pHlpR3 = &g_pdmR3DevHlpTracing;
+ }
+#endif
+
+ /*
+ * Call the constructor.
+ */
+ paDevs[i].pDev->cInstances++;
+ Log(("PDM: Constructing device '%s' instance %d...\n", pDevIns->pReg->szName, pDevIns->iInstance));
+ rc = pDevIns->pReg->pfnConstruct(pDevIns, pDevIns->iInstance, pDevIns->pCfg);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("PDM: Failed to construct '%s'/%d! %Rra\n", pDevIns->pReg->szName, pDevIns->iInstance, rc));
+ if (VMR3GetErrorCount(pVM->pUVM) == 0)
+ VMSetError(pVM, rc, RT_SRC_POS, "Failed to construct device '%s' instance #%u",
+ pDevIns->pReg->szName, pDevIns->iInstance);
+ /* Because we're damn lazy, the destructor will be called even if
+ the constructor fails. So, no unlinking. */
+ paDevs[i].pDev->cInstances--;
+ return rc == VERR_VERSION_MISMATCH ? VERR_PDM_DEVICE_VERSION_MISMATCH : rc;
+ }
+
+ /*
+ * Call the ring-0 constructor if applicable.
+ */
+ if (fR0Enabled)
+ {
+ PDMDEVICEGENCALLREQ Req;
+ RT_ZERO(Req.Params);
+ Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ Req.Hdr.cbReq = sizeof(Req);
+ Req.enmCall = PDMDEVICEGENCALL_CONSTRUCT;
+ Req.idxR0Device = pDevIns->Internal.s.idxR0Device;
+ Req.pDevInsR3 = pDevIns;
+ rc = VMMR3CallR0Emt(pVM, pVM->apCpusR3[0], VMMR0_DO_PDM_DEVICE_GEN_CALL, 0, &Req.Hdr);
+ pDevIns->Internal.s.fIntFlags |= PDMDEVINSINT_FLAGS_R0_CONTRUCT;
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("PDM: Failed to construct (ring-0) '%s'/%d! %Rra\n", pDevIns->pReg->szName, pDevIns->iInstance, rc));
+ if (VMR3GetErrorCount(pVM->pUVM) == 0)
+ VMSetError(pVM, rc, RT_SRC_POS, "The ring-0 constructor of device '%s' instance #%u failed",
+ pDevIns->pReg->szName, pDevIns->iInstance);
+ paDevs[i].pDev->cInstances--;
+ return rc == VERR_VERSION_MISMATCH ? VERR_PDM_DEVICE_VERSION_MISMATCH : rc;
+ }
+ }
+
+ } /* for device instances */
+
+#ifdef VBOX_WITH_USB
+ /* ditto for USB Devices. */
+ rc = pdmR3UsbInstantiateDevices(pVM);
+ if (RT_FAILURE(rc))
+ return rc;
+#endif
+
+ LogFlow(("pdmR3DevInit: returns %Rrc\n", VINF_SUCCESS));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Performs the init complete callback after ring-0 and raw-mode has been
+ * initialized.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ */
+int pdmR3DevInitComplete(PVM pVM)
+{
+ int rc;
+
+ /*
+ * Iterate thru the device instances and work the callback.
+ */
+ for (PPDMDEVINS pDevIns = pVM->pdm.s.pDevInstances; pDevIns; pDevIns = pDevIns->Internal.s.pNextR3)
+ {
+ if (pDevIns->pReg->pfnInitComplete)
+ {
+ PDMCritSectEnter(pVM, pDevIns->pCritSectRoR3, VERR_IGNORED);
+ rc = pDevIns->pReg->pfnInitComplete(pDevIns);
+ PDMCritSectLeave(pVM, pDevIns->pCritSectRoR3);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("InitComplete on device '%s'/%d failed with rc=%Rrc\n",
+ pDevIns->pReg->szName, pDevIns->iInstance, rc));
+ return rc;
+ }
+ }
+ }
+
+#ifdef VBOX_WITH_USB
+ rc = pdmR3UsbVMInitComplete(pVM);
+ if (RT_FAILURE(rc))
+ {
+ Log(("pdmR3DevInit: returns %Rrc\n", rc));
+ return rc;
+ }
+#endif
+
+ LogFlow(("pdmR3DevInit: returns %Rrc\n", VINF_SUCCESS));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Lookups a device structure by name.
+ * @internal
+ */
+PPDMDEV pdmR3DevLookup(PVM pVM, const char *pszName)
+{
+ size_t cchName = strlen(pszName);
+ for (PPDMDEV pDev = pVM->pdm.s.pDevs; pDev; pDev = pDev->pNext)
+ if ( pDev->cchName == cchName
+ && !strcmp(pDev->pReg->szName, pszName))
+ return pDev;
+ return NULL;
+}
+
+
+/**
+ * Loads the device modules.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ */
+static int pdmR3DevLoadModules(PVM pVM)
+{
+ /*
+ * Initialize the callback structure.
+ */
+ PDMDEVREGCBINT RegCB;
+ RegCB.Core.u32Version = PDM_DEVREG_CB_VERSION;
+ RegCB.Core.pfnRegister = pdmR3DevReg_Register;
+ RegCB.pVM = pVM;
+ RegCB.pCfgNode = NULL;
+
+ /*
+ * Register the internal VMM APIC device.
+ */
+ int rc = pdmR3DevReg_Register(&RegCB.Core, &g_DeviceAPIC);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Load the builtin module.
+ */
+ PCFGMNODE pDevicesNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "PDM/Devices");
+ bool fLoadBuiltin;
+ rc = CFGMR3QueryBool(pDevicesNode, "LoadBuiltin", &fLoadBuiltin);
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
+ fLoadBuiltin = true;
+ else if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("Configuration error: Querying boolean \"LoadBuiltin\" failed with %Rrc\n", rc));
+ return rc;
+ }
+ if (fLoadBuiltin)
+ {
+ /* make filename */
+ char *pszFilename = pdmR3FileR3("VBoxDD", true /*fShared*/);
+ if (!pszFilename)
+ return VERR_NO_TMP_MEMORY;
+ rc = pdmR3DevLoad(pVM, &RegCB, pszFilename, "VBoxDD");
+ RTMemTmpFree(pszFilename);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* make filename */
+ pszFilename = pdmR3FileR3("VBoxDD2", true /*fShared*/);
+ if (!pszFilename)
+ return VERR_NO_TMP_MEMORY;
+ rc = pdmR3DevLoad(pVM, &RegCB, pszFilename, "VBoxDD2");
+ RTMemTmpFree(pszFilename);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Load additional device modules.
+ */
+ PCFGMNODE pCur;
+ for (pCur = CFGMR3GetFirstChild(pDevicesNode); pCur; pCur = CFGMR3GetNextChild(pCur))
+ {
+ /*
+ * Get the name and path.
+ */
+ char szName[PDMMOD_NAME_LEN];
+ rc = CFGMR3GetName(pCur, &szName[0], sizeof(szName));
+ if (rc == VERR_CFGM_NOT_ENOUGH_SPACE)
+ {
+ AssertMsgFailed(("configuration error: The module name is too long, cchName=%zu.\n", CFGMR3GetNameLen(pCur)));
+ return VERR_PDM_MODULE_NAME_TOO_LONG;
+ }
+ else if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("CFGMR3GetName -> %Rrc.\n", rc));
+ return rc;
+ }
+
+ /* the path is optional, if no path the module name + path is used. */
+ char szFilename[RTPATH_MAX];
+ rc = CFGMR3QueryString(pCur, "Path", &szFilename[0], sizeof(szFilename));
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ strcpy(szFilename, szName);
+ else if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("configuration error: Failure to query the module path, rc=%Rrc.\n", rc));
+ return rc;
+ }
+
+ /* prepend path? */
+ if (!RTPathHavePath(szFilename))
+ {
+ char *psz = pdmR3FileR3(szFilename, false /*fShared*/);
+ if (!psz)
+ return VERR_NO_TMP_MEMORY;
+ size_t cch = strlen(psz) + 1;
+ if (cch > sizeof(szFilename))
+ {
+ RTMemTmpFree(psz);
+ AssertMsgFailed(("Filename too long! cch=%d '%s'\n", cch, psz));
+ return VERR_FILENAME_TOO_LONG;
+ }
+ memcpy(szFilename, psz, cch);
+ RTMemTmpFree(psz);
+ }
+
+ /*
+ * Load the module and register it's devices.
+ */
+ RegCB.pCfgNode = pCur;
+ rc = pdmR3DevLoad(pVM, &RegCB, szFilename, szName);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Loads one device module and call the registration entry point.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pRegCB The registration callback stuff.
+ * @param pszFilename Module filename.
+ * @param pszName Module name.
+ */
+static int pdmR3DevLoad(PVM pVM, PPDMDEVREGCBINT pRegCB, const char *pszFilename, const char *pszName)
+{
+ /*
+ * Load it.
+ */
+ int rc = pdmR3LoadR3U(pVM->pUVM, pszFilename, pszName);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the registration export and call it.
+ */
+ FNPDMVBOXDEVICESREGISTER *pfnVBoxDevicesRegister;
+ rc = PDMR3LdrGetSymbolR3(pVM, pszName, "VBoxDevicesRegister", (void **)&pfnVBoxDevicesRegister);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("PDM: Calling VBoxDevicesRegister (%p) of %s (%s)\n", pfnVBoxDevicesRegister, pszName, pszFilename));
+ rc = pfnVBoxDevicesRegister(&pRegCB->Core, VBOX_VERSION);
+ if (RT_SUCCESS(rc))
+ Log(("PDM: Successfully loaded device module %s (%s).\n", pszName, pszFilename));
+ else
+ {
+ VMR3SetError(pVM->pUVM, rc, RT_SRC_POS, "VBoxDevicesRegister failed with rc=%Rrc for module %s (%s)",
+ rc, pszName, pszFilename);
+ AssertMsgFailed(("VBoxDevicesRegister failed with rc=%Rrc for module %s (%s)\n", rc, pszName, pszFilename));
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("Failed to locate 'VBoxDevicesRegister' in %s (%s) rc=%Rrc\n", pszName, pszFilename, rc));
+ if (rc == VERR_SYMBOL_NOT_FOUND)
+ rc = VERR_PDM_NO_REGISTRATION_EXPORT;
+ VMR3SetError(pVM->pUVM, rc, RT_SRC_POS, "Failed to locate 'VBoxDevicesRegister' in %s (%s) rc=%Rrc",
+ pszName, pszFilename, rc);
+ }
+ }
+ else
+ AssertMsgFailed(("Failed to load %s %s!\n", pszFilename, pszName));
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREGCB,pfnRegister}
+ */
+static DECLCALLBACK(int) pdmR3DevReg_Register(PPDMDEVREGCB pCallbacks, PCPDMDEVREG pReg)
+{
+ /*
+ * Validate the registration structure.
+ */
+ Assert(pReg);
+ AssertMsgReturn(pReg->u32Version == PDM_DEVREG_VERSION,
+ ("Unknown struct version %#x!\n", pReg->u32Version),
+ VERR_PDM_UNKNOWN_DEVREG_VERSION);
+
+ AssertMsgReturn( pReg->szName[0]
+ && strlen(pReg->szName) < sizeof(pReg->szName)
+ && pdmR3IsValidName(pReg->szName),
+ ("Invalid name '%.*s'\n", sizeof(pReg->szName), pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertMsgReturn( !(pReg->fFlags & PDM_DEVREG_FLAGS_RC)
+ || ( pReg->pszRCMod[0]
+ && strlen(pReg->pszRCMod) < RT_SIZEOFMEMB(PDMDEVICECREATEREQ, szModName)),
+ ("Invalid GC module name '%s' - (Device %s)\n", pReg->pszRCMod, pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertMsgReturn( !(pReg->fFlags & PDM_DEVREG_FLAGS_R0)
+ || ( pReg->pszR0Mod[0]
+ && strlen(pReg->pszR0Mod) < RT_SIZEOFMEMB(PDMDEVICECREATEREQ, szModName)),
+ ("Invalid R0 module name '%s' - (Device %s)\n", pReg->pszR0Mod, pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertMsgReturn((pReg->fFlags & PDM_DEVREG_FLAGS_HOST_BITS_MASK) == PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT,
+ ("Invalid host bits flags! fFlags=%#x (Device %s)\n", pReg->fFlags, pReg->szName),
+ VERR_PDM_INVALID_DEVICE_HOST_BITS);
+ AssertMsgReturn((pReg->fFlags & PDM_DEVREG_FLAGS_GUEST_BITS_MASK),
+ ("Invalid guest bits flags! fFlags=%#x (Device %s)\n", pReg->fFlags, pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertMsgReturn(pReg->fClass,
+ ("No class! (Device %s)\n", pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertMsgReturn(pReg->cMaxInstances > 0,
+ ("Max instances %u! (Device %s)\n", pReg->cMaxInstances, pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ uint32_t const cbMaxInstance = pReg->fFlags & (PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0)
+ ? PDM_MAX_DEVICE_INSTANCE_SIZE : PDM_MAX_DEVICE_INSTANCE_SIZE_R3;
+ AssertMsgReturn(pReg->cbInstanceShared <= cbMaxInstance,
+ ("Instance size %u bytes! (Max %u; Device %s)\n", pReg->cbInstanceShared, cbMaxInstance, pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertMsgReturn(pReg->cbInstanceCC <= cbMaxInstance,
+ ("Instance size %d bytes! (Max %u; Device %s)\n", pReg->cbInstanceCC, cbMaxInstance, pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertMsgReturn(pReg->pfnConstruct,
+ ("No constructor! (Device %s)\n", pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertLogRelMsgReturn((pReg->fFlags & PDM_DEVREG_FLAGS_GUEST_BITS_MASK) == PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT,
+ ("PDM: Rejected device '%s' because it didn't match the guest bits.\n", pReg->szName),
+ VERR_PDM_INVALID_DEVICE_GUEST_BITS);
+ AssertLogRelMsg(pReg->u32VersionEnd == PDM_DEVREG_VERSION,
+ ("u32VersionEnd=%#x, expected %#x. (szName=%s)\n",
+ pReg->u32VersionEnd, PDM_DEVREG_VERSION, pReg->szName));
+ AssertLogRelMsgReturn(pReg->cMaxPciDevices <= 8, ("%#x (szName=%s)\n", pReg->cMaxPciDevices, pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertLogRelMsgReturn(pReg->cMaxMsixVectors <= VBOX_MSIX_MAX_ENTRIES,
+ ("%#x (szName=%s)\n", pReg->cMaxMsixVectors, pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+ AssertLogRelMsgReturn(pReg->fFlags & PDM_DEVREG_FLAGS_NEW_STYLE /* the flag is required now */,
+ ("PDM_DEVREG_FLAGS_NEW_STYLE not set for szName=%s!\n", pReg->szName),
+ VERR_PDM_INVALID_DEVICE_REGISTRATION);
+
+ /*
+ * Check for duplicate and find FIFO entry at the same time.
+ */
+ PCPDMDEVREGCBINT pRegCB = (PCPDMDEVREGCBINT)pCallbacks;
+ PPDMDEV pDevPrev = NULL;
+ PPDMDEV pDev = pRegCB->pVM->pdm.s.pDevs;
+ for (; pDev; pDevPrev = pDev, pDev = pDev->pNext)
+ AssertMsgReturn(strcmp(pDev->pReg->szName, pReg->szName),
+ ("Device '%s' already exists\n", pReg->szName),
+ VERR_PDM_DEVICE_NAME_CLASH);
+
+ /*
+ * Allocate new device structure, initialize and insert it into the list.
+ */
+ int rc;
+ pDev = (PPDMDEV)MMR3HeapAlloc(pRegCB->pVM, MM_TAG_PDM_DEVICE, sizeof(*pDev));
+ if (pDev)
+ {
+ pDev->pNext = NULL;
+ pDev->cInstances = 0;
+ pDev->pInstances = NULL;
+ pDev->pReg = pReg;
+ pDev->cchName = (uint32_t)strlen(pReg->szName);
+ rc = CFGMR3QueryStringAllocDef( pRegCB->pCfgNode, "RCSearchPath", &pDev->pszRCSearchPath, NULL);
+ if (RT_SUCCESS(rc))
+ rc = CFGMR3QueryStringAllocDef(pRegCB->pCfgNode, "R0SearchPath", &pDev->pszR0SearchPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (pDevPrev)
+ pDevPrev->pNext = pDev;
+ else
+ pRegCB->pVM->pdm.s.pDevs = pDev;
+ Log(("PDM: Registered device '%s'\n", pReg->szName));
+ return VINF_SUCCESS;
+ }
+
+ MMR3HeapFree(pDev);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Locates a LUN.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pszDevice Device name.
+ * @param iInstance Device instance.
+ * @param iLun The Logical Unit to obtain the interface of.
+ * @param ppLun Where to store the pointer to the LUN if found.
+ * @thread Try only do this in EMT...
+ */
+int pdmR3DevFindLun(PVM pVM, const char *pszDevice, unsigned iInstance, unsigned iLun, PPPDMLUN ppLun)
+{
+ /*
+ * Iterate registered devices looking for the device.
+ */
+ size_t cchDevice = strlen(pszDevice);
+ for (PPDMDEV pDev = pVM->pdm.s.pDevs; pDev; pDev = pDev->pNext)
+ {
+ if ( pDev->cchName == cchDevice
+ && !memcmp(pDev->pReg->szName, pszDevice, cchDevice))
+ {
+ /*
+ * Iterate device instances.
+ */
+ for (PPDMDEVINS pDevIns = pDev->pInstances; pDevIns; pDevIns = pDevIns->Internal.s.pPerDeviceNextR3)
+ {
+ if (pDevIns->iInstance == iInstance)
+ {
+ /*
+ * Iterate luns.
+ */
+ for (PPDMLUN pLun = pDevIns->Internal.s.pLunsR3; pLun; pLun = pLun->pNext)
+ {
+ if (pLun->iLun == iLun)
+ {
+ *ppLun = pLun;
+ return VINF_SUCCESS;
+ }
+ }
+ return VERR_PDM_LUN_NOT_FOUND;
+ }
+ }
+ return VERR_PDM_DEVICE_INSTANCE_NOT_FOUND;
+ }
+ }
+ return VERR_PDM_DEVICE_NOT_FOUND;
+}
+
+
+/**
+ * Attaches a preconfigured driver to an existing device instance.
+ *
+ * This is used to change drivers and suchlike at runtime.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pszDevice Device name.
+ * @param iInstance Device instance.
+ * @param iLun The Logical Unit to obtain the interface of.
+ * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ * @param ppBase Where to store the base interface pointer. Optional.
+ * @thread EMT
+ */
+VMMR3DECL(int) PDMR3DeviceAttach(PUVM pUVM, const char *pszDevice, unsigned iInstance, unsigned iLun, uint32_t fFlags, PPPDMIBASE ppBase)
+{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ PVM pVM = pUVM->pVM;
+ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_EMT(pVM);
+ LogFlow(("PDMR3DeviceAttach: pszDevice=%p:{%s} iInstance=%d iLun=%d fFlags=%#x ppBase=%p\n",
+ pszDevice, pszDevice, iInstance, iLun, fFlags, ppBase));
+
+ /*
+ * Find the LUN in question.
+ */
+ PPDMLUN pLun;
+ int rc = pdmR3DevFindLun(pVM, pszDevice, iInstance, iLun, &pLun);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Can we attach anything at runtime?
+ */
+ PPDMDEVINS pDevIns = pLun->pDevIns;
+ if (pDevIns->pReg->pfnAttach)
+ {
+ if (!pLun->pTop)
+ {
+ PDMCritSectEnter(pVM, pDevIns->pCritSectRoR3, VERR_IGNORED);
+ rc = pDevIns->pReg->pfnAttach(pDevIns, iLun, fFlags);
+ PDMCritSectLeave(pVM, pDevIns->pCritSectRoR3);
+ }
+ else
+ rc = VERR_PDM_DRIVER_ALREADY_ATTACHED;
+ }
+ else
+ rc = VERR_PDM_DEVICE_NO_RT_ATTACH;
+
+ if (ppBase)
+ *ppBase = pLun->pTop ? &pLun->pTop->IBase : NULL;
+ }
+ else if (ppBase)
+ *ppBase = NULL;
+
+ if (ppBase)
+ LogFlow(("PDMR3DeviceAttach: returns %Rrc *ppBase=%p\n", rc, *ppBase));
+ else
+ LogFlow(("PDMR3DeviceAttach: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Detaches a driver chain from an existing device instance.
+ *
+ * This is used to change drivers and suchlike at runtime.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pszDevice Device name.
+ * @param iInstance Device instance.
+ * @param iLun The Logical Unit to obtain the interface of.
+ * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ * @thread EMT
+ */
+VMMR3DECL(int) PDMR3DeviceDetach(PUVM pUVM, const char *pszDevice, unsigned iInstance, unsigned iLun, uint32_t fFlags)
+{
+ return PDMR3DriverDetach(pUVM, pszDevice, iInstance, iLun, NULL, 0, fFlags);
+}
+
+
+/**
+ * References the critical section associated with a device for the use by a
+ * timer or similar created by the device.
+ *
+ * @returns Pointer to the critical section.
+ * @param pVM The cross context VM structure.
+ * @param pDevIns The device instance in question.
+ *
+ * @internal
+ */
+VMMR3_INT_DECL(PPDMCRITSECT) PDMR3DevGetCritSect(PVM pVM, PPDMDEVINS pDevIns)
+{
+ VM_ASSERT_EMT(pVM); RT_NOREF_PV(pVM);
+ VM_ASSERT_STATE(pVM, VMSTATE_CREATING);
+ AssertPtr(pDevIns);
+
+ PPDMCRITSECT pCritSect = pDevIns->pCritSectRoR3;
+ AssertPtr(pCritSect);
+ pCritSect->s.fUsedByTimerOrSimilar = true;
+
+ return pCritSect;
+}
+
+
+/**
+ * Attaches a preconfigured driver to an existing device or driver instance.
+ *
+ * This is used to change drivers and suchlike at runtime. The driver or device
+ * at the end of the chain will be told to attach to whatever is configured
+ * below it.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pszDevice Device name.
+ * @param iInstance Device instance.
+ * @param iLun The Logical Unit to obtain the interface of.
+ * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ * @param ppBase Where to store the base interface pointer. Optional.
+ *
+ * @thread EMT
+ */
+VMMR3DECL(int) PDMR3DriverAttach(PUVM pUVM, const char *pszDevice, unsigned iInstance, unsigned iLun, uint32_t fFlags, PPPDMIBASE ppBase)
+{
+ LogFlow(("PDMR3DriverAttach: pszDevice=%p:{%s} iInstance=%d iLun=%d fFlags=%#x ppBase=%p\n",
+ pszDevice, pszDevice, iInstance, iLun, fFlags, ppBase));
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ PVM pVM = pUVM->pVM;
+ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_EMT(pVM);
+
+ if (ppBase)
+ *ppBase = NULL;
+
+ /*
+ * Find the LUN in question.
+ */
+ PPDMLUN pLun;
+ int rc = pdmR3DevFindLun(pVM, pszDevice, iInstance, iLun, &pLun);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Anything attached to the LUN?
+ */
+ PPDMDRVINS pDrvIns = pLun->pTop;
+ if (!pDrvIns)
+ {
+ /* No, ask the device to attach to the new stuff. */
+ PPDMDEVINS pDevIns = pLun->pDevIns;
+ if (pDevIns->pReg->pfnAttach)
+ {
+ PDMCritSectEnter(pVM, pDevIns->pCritSectRoR3, VERR_IGNORED);
+ rc = pDevIns->pReg->pfnAttach(pDevIns, iLun, fFlags);
+ if (RT_SUCCESS(rc) && ppBase)
+ *ppBase = pLun->pTop ? &pLun->pTop->IBase : NULL;
+ PDMCritSectLeave(pVM, pDevIns->pCritSectRoR3);
+ }
+ else
+ rc = VERR_PDM_DEVICE_NO_RT_ATTACH;
+ }
+ else
+ {
+ /* Yes, find the bottom most driver and ask it to attach to the new stuff. */
+ while (pDrvIns->Internal.s.pDown)
+ pDrvIns = pDrvIns->Internal.s.pDown;
+ if (pDrvIns->pReg->pfnAttach)
+ {
+ rc = pDrvIns->pReg->pfnAttach(pDrvIns, fFlags);
+ if (RT_SUCCESS(rc) && ppBase)
+ *ppBase = pDrvIns->Internal.s.pDown
+ ? &pDrvIns->Internal.s.pDown->IBase
+ : NULL;
+ }
+ else
+ rc = VERR_PDM_DRIVER_NO_RT_ATTACH;
+ }
+ }
+
+ if (ppBase)
+ LogFlow(("PDMR3DriverAttach: returns %Rrc *ppBase=%p\n", rc, *ppBase));
+ else
+ LogFlow(("PDMR3DriverAttach: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Detaches the specified driver instance.
+ *
+ * This is used to replumb drivers at runtime for simulating hot plugging and
+ * media changes.
+ *
+ * This is a superset of PDMR3DeviceDetach. It allows detaching drivers from
+ * any driver or device by specifying the driver to start detaching at. The
+ * only prerequisite is that the driver or device above implements the
+ * pfnDetach callback (PDMDRVREG / PDMDEVREG).
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pszDevice Device name.
+ * @param iDevIns Device instance.
+ * @param iLun The Logical Unit in which to look for the driver.
+ * @param pszDriver The name of the driver which to detach. If NULL
+ * then the entire driver chain is detatched.
+ * @param iOccurrence The occurrence of that driver in the chain. This is
+ * usually 0.
+ * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ * @thread EMT
+ */
+VMMR3DECL(int) PDMR3DriverDetach(PUVM pUVM, const char *pszDevice, unsigned iDevIns, unsigned iLun,
+ const char *pszDriver, unsigned iOccurrence, uint32_t fFlags)
+{
+ LogFlow(("PDMR3DriverDetach: pszDevice=%p:{%s} iDevIns=%u iLun=%u pszDriver=%p:{%s} iOccurrence=%u fFlags=%#x\n",
+ pszDevice, pszDevice, iDevIns, iLun, pszDriver, pszDriver, iOccurrence, fFlags));
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ PVM pVM = pUVM->pVM;
+ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_EMT(pVM);
+ AssertPtr(pszDevice);
+ AssertPtrNull(pszDriver);
+ Assert(iOccurrence == 0 || pszDriver);
+ Assert(!(fFlags & ~(PDM_TACH_FLAGS_NOT_HOT_PLUG)));
+
+ /*
+ * Find the LUN in question.
+ */
+ PPDMLUN pLun;
+ int rc = pdmR3DevFindLun(pVM, pszDevice, iDevIns, iLun, &pLun);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the driver.
+ */
+ PPDMDRVINS pDrvIns = pLun->pTop;
+ if (pDrvIns)
+ {
+ if (pszDriver)
+ {
+ while (pDrvIns)
+ {
+ if (!strcmp(pDrvIns->pReg->szName, pszDriver))
+ {
+ if (iOccurrence == 0)
+ break;
+ iOccurrence--;
+ }
+ pDrvIns = pDrvIns->Internal.s.pDown;
+ }
+ }
+ if (pDrvIns)
+ rc = pdmR3DrvDetach(pDrvIns, fFlags);
+ else
+ rc = VERR_PDM_DRIVER_INSTANCE_NOT_FOUND;
+ }
+ else
+ rc = VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN;
+ }
+
+ LogFlow(("PDMR3DriverDetach: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Runtime detach and reattach of a new driver chain or sub chain.
+ *
+ * This is intended to be called on a non-EMT thread, this will instantiate the
+ * new driver (sub-)chain, and then the EMTs will do the actual replumbing. The
+ * destruction of the old driver chain will be taken care of on the calling
+ * thread.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pszDevice Device name.
+ * @param iDevIns Device instance.
+ * @param iLun The Logical Unit in which to look for the driver.
+ * @param pszDriver The name of the driver which to detach and replace.
+ * If NULL then the entire driver chain is to be
+ * reattached.
+ * @param iOccurrence The occurrence of that driver in the chain. This is
+ * usually 0.
+ * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ * @param pCfg The configuration of the new driver chain that is
+ * going to be attached. The subtree starts with the
+ * node containing a Driver key, a Config subtree and
+ * optionally an AttachedDriver subtree.
+ * If this parameter is NULL, then this call will work
+ * like at a non-pause version of PDMR3DriverDetach.
+ * @param ppBase Where to store the base interface pointer to the new
+ * driver. Optional.
+ *
+ * @thread Any thread. The EMTs will be involved at some point though.
+ */
+VMMR3DECL(int) PDMR3DriverReattach(PUVM pUVM, const char *pszDevice, unsigned iDevIns, unsigned iLun,
+ const char *pszDriver, unsigned iOccurrence, uint32_t fFlags,
+ PCFGMNODE pCfg, PPPDMIBASE ppBase)
+{
+ NOREF(pUVM); NOREF(pszDevice); NOREF(iDevIns); NOREF(iLun); NOREF(pszDriver); NOREF(iOccurrence);
+ NOREF(fFlags); NOREF(pCfg); NOREF(ppBase);
+ return VERR_NOT_IMPLEMENTED;
+}
+