PDMDevice.cpp revision 049c8053622bff0467eef969798c1b831d44ad57
/* $Id$ */
/** @file
* PDM - Pluggable Device and Driver Manager, Device parts.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_PDM_DEVICE
#include "PDMInternal.h"
#include <iprt/semaphore.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. */
/** A bit of padding. */
/** VM Handle. */
/** Pointer to a PDMDEVREGCBINT structure. */
typedef PDMDEVREGCBINT *PPDMDEVREGCBINT;
/** Pointer to a const PDMDEVREGCBINT structure. */
typedef const PDMDEVREGCBINT *PCPDMDEVREGCBINT;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
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 VM Handle.
*/
{
LogFlow(("pdmR3DevInit:\n"));
AssertRelease(sizeof(pVM->pdm.s.pDevInstances->Internal.s) <= sizeof(pVM->pdm.s.pDevInstances->Internal.padding));
/*
* Load device modules.
*/
if (RT_FAILURE(rc))
return rc;
#ifdef VBOX_WITH_USB
/* ditto for USB Devices. */
if (RT_FAILURE(rc))
return rc;
#endif
/*
* Get the RC & R0 devhlps and create the devhlp R3 task queue.
*/
rc = PDMR3QueueCreateInternal(pVM, sizeof(PDMDEVHLPTASK), 8, 0, pdmR3DevHlpQueueConsumer, true, "DevHlp",
/*
*
* Enumerate the device instance configurations
* and come up with a instantiation order.
*
*/
/* Switch to /Devices, which contains the device instantiations. */
/*
* Count the device instances.
*/
unsigned cDevs = 0;
for (pInstanceNode = CFGMR3GetFirstChild(pCur); pInstanceNode; pInstanceNode = CFGMR3GetNextChild(pInstanceNode))
cDevs++;
if (!cDevs)
{
Log(("PDM: No devices were configured!\n"));
return VINF_SUCCESS;
}
/*
* Collect info on each device instance.
*/
struct DEVORDER
{
/** Configuration node. */
/** Pointer to device. */
/** Init order. */
/** VBox instance number. */
} *paDevs = (struct DEVORDER *)alloca(sizeof(paDevs[0]) * (cDevs + 1)); /* (One extra for swapping) */
unsigned i = 0;
{
/* Get the device name. */
AssertMsgRCReturn(rc, ("Configuration error: device name is too long (or something)! rc=%Rrc\n", rc), rc);
/* Find the device. */
AssertMsgReturn(pDev, ("Configuration error: device '%s' not found!\n", szName), VERR_PDM_DEVICE_NOT_FOUND);
/* Configured priority or use default based on device class? */
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
{
/* nop */;
}
else
AssertMsgRCReturn(rc, ("Configuration error: reading \"Priority\" for the '%s' device failed rc=%Rrc!\n", szName, rc), rc);
/* Enumerate the device instances. */
for (pInstanceNode = CFGMR3GetFirstChild(pCur); pInstanceNode; pInstanceNode = CFGMR3GetNextChild(pInstanceNode))
{
/* Get the instance number. */
char szInstance[32];
AssertMsgRCReturn(rc, ("Configuration error: instance name is too long (or something)! rc=%Rrc\n", rc), rc);
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 */
AssertLogRelMsgFailedReturn(("Configuration error: Too many instances of %s was configured: %u, max %u\n",
} /* devices */
/*
* Sort the device array ascending on u32Order. (bubble)
*/
unsigned c = cDevs - 1;
while (c)
{
unsigned j = 0;
for (i = 0; i < c; i++)
{
j = i;
}
c = j;
}
/*
*
* Instantiate the devices.
*
*/
for (i = 0; i < cDevs; i++)
{
/*
* Gather a bit of config.
*/
/* trusted */
bool fTrusted;
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
fTrusted = false;
else if (RT_FAILURE(rc))
{
return rc;
}
/* config node */
if (!pConfigNode)
{
if (RT_FAILURE(rc))
{
return rc;
}
}
/*
* Allocate the device instance.
*/
AssertReturn(paDevs[i].pDev->cInstances < paDevs[i].pDev->pDevReg->cMaxInstances, VERR_PDM_TOO_MANY_DEVICE_INSTANCES);
else
if (RT_FAILURE(rc))
{
AssertMsgFailed(("Failed to allocate %d bytes of instance data for device '%s'. rc=%Rrc\n",
return rc;
}
/*
* Initialize it.
*/
//pDevIns->Internal.s.pNextR3 = NULL;
//pDevIns->Internal.s.pPerDeviceNextR3 = NULL;
//pDevIns->Internal.s.pLunsR3 = NULL;
//pDevIns->Internal.s.pPciDeviceR3 = NULL;
//pDevIns->Internal.s.pPciBusR3 = NULL;
//pDevIns->Internal.s.pPciDeviceR0 = 0;
//pDevIns->Internal.s.pPciBusR0 = 0;
//pDevIns->Internal.s.pPciDeviceRC = 0;
//pDevIns->Internal.s.pPciBusRC = 0;
/*
* Link it into all the lists.
*/
/* The global instance FIFO. */
if (!pPrev1)
else
{
}
/* The per device instance FIFO. */
if (!pPrev2)
else
{
}
/*
* Call the constructor.
*/
Log(("PDM: Constructing device '%s' instance %d...\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance));
if (RT_FAILURE(rc))
{
LogRel(("PDM: Failed to construct '%s'/%d! %Rra\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance, rc));
/* because we're damn lazy right now, we'll say that the destructor will be called even if the constructor fails. */
return rc;
}
} /* for device instances */
#ifdef VBOX_WITH_USB
/* ditto for USB Devices. */
if (RT_FAILURE(rc))
return rc;
#endif
/*
*
* PCI BIOS Fake and Init Complete.
*
*/
{
if (RT_FAILURE(rc))
{
return rc;
}
}
{
{
if (RT_FAILURE(rc))
{
AssertMsgFailed(("InitComplete on device '%s'/%d failed with rc=%Rrc\n",
return rc;
}
}
}
#ifdef VBOX_WITH_USB
/* ditto for USB Devices. */
if (RT_FAILURE(rc))
return rc;
#endif
return VINF_SUCCESS;
}
/**
* Lookups a device structure by name.
* @internal
*/
{
return pDev;
return NULL;
}
/**
* Loads the device modules.
*
* @returns VBox status code.
* @param pVM Pointer to the shared VM structure.
*/
{
/*
* Initialize the callback structure.
*/
/*
* Load the builtin module
*/
bool fLoadBuiltin;
fLoadBuiltin = true;
else if (RT_FAILURE(rc))
{
return rc;
}
if (fLoadBuiltin)
{
/* make filename */
if (!pszFilename)
return VERR_NO_TMP_MEMORY;
if (RT_FAILURE(rc))
return rc;
/* make filename */
if (!pszFilename)
return VERR_NO_TMP_MEMORY;
if (RT_FAILURE(rc))
return rc;
}
/*
* Load additional device modules.
*/
{
/*
* Get the name and path.
*/
char szName[PDMMOD_NAME_LEN];
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))
{
return rc;
}
/* the path is optional, if no path the module name + path is used. */
char szFilename[RTPATH_MAX];
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
else if (RT_FAILURE(rc))
{
return rc;
}
/* prepend path? */
if (!RTPathHavePath(szFilename))
{
if (!psz)
return VERR_NO_TMP_MEMORY;
if (cch > sizeof(szFilename))
{
return VERR_FILENAME_TOO_LONG;
}
}
/*
* Load the module and register it's devices.
*/
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 VM handle.
* @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.
*/
if (RT_SUCCESS(rc))
{
/*
* Get the registration export and call it.
*/
if (RT_SUCCESS(rc))
{
Log(("PDM: Calling VBoxDevicesRegister (%p) of %s (%s)\n", pfnVBoxDevicesRegister, pszName, pszFilename));
if (RT_SUCCESS(rc))
else
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)
}
}
else
return rc;
}
/**
* Registers a device with the current VM instance.
*
* @returns VBox status code.
* @param pCallbacks Pointer to the callback table.
* @param pDevReg Pointer to the device registration record.
* This data must be permanent and readonly.
*/
{
/*
* Validate the registration structure.
*/
{
return VERR_PDM_UNKNOWN_DEVREG_VERSION;
}
if ( !pDevReg->szDeviceName[0]
{
}
{
AssertMsgFailed(("Invalid GC module name '%s' - (Device %s)\n", pDevReg->szRCMod, pDevReg->szDeviceName));
}
{
AssertMsgFailed(("Invalid R0 module name '%s' - (Device %s)\n", pDevReg->szR0Mod, pDevReg->szDeviceName));
}
{
AssertMsgFailed(("Invalid host bits flags! fFlags=%#x (Device %s)\n", pDevReg->fFlags, pDevReg->szDeviceName));
}
{
AssertMsgFailed(("Invalid guest bits flags! fFlags=%#x (Device %s)\n", pDevReg->fFlags, pDevReg->szDeviceName));
}
{
}
if (pDevReg->cMaxInstances <= 0)
{
AssertMsgFailed(("Max instances %u! (Device %s)\n", pDevReg->cMaxInstances, pDevReg->szDeviceName));
}
if (pDevReg->cbInstance > (RTUINT)(pDevReg->fFlags & (PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0) ? 96 * _1K : _1M))
{
AssertMsgFailed(("Instance size %d bytes! (Device %s)\n", pDevReg->cbInstance, pDevReg->szDeviceName));
}
if (!pDevReg->pfnConstruct)
{
}
/* Check matching guest bits last without any asserting. Enables trial and error registration. */
{
Log(("PDM: Rejected device '%s' because it didn't match the guest bits.\n", pDevReg->szDeviceName));
}
("u32VersionEnd=%#x, expected %#x. (szDeviceName=%s)\n",
/*
* Check for duplicate and find FIFO entry at the same time.
*/
{
{
return VERR_PDM_DEVICE_NAME_CLASH;
}
}
/*
* Allocate new device structure and insert it into the list.
*/
if (pDev)
{
pDev->cInstances = 0;
if (pDevPrev)
else
return VINF_SUCCESS;
}
return VERR_NO_MEMORY;
}
/**
* Allocate memory which is associated with current VM instance
* and automatically freed on it's destruction.
*
* @returns Pointer to allocated memory. The memory is *NOT* zero-ed.
* @param pCallbacks Pointer to the callback table.
* @param cb Number of bytes to allocate.
*/
{
return pv;
}
/**
* Locates a LUN.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @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.
*/
{
{
/*
* Iterate device instances.
*/
for (PPDMDEVINS pDevIns = pDev->pInstances; pDevIns; pDevIns = pDevIns->Internal.s.pPerDeviceNextR3)
{
{
/*
* Iterate luns.
*/
{
{
return VINF_SUCCESS;
}
}
return VERR_PDM_LUN_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 pVM 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(PVM pVM, const char *pszDevice, unsigned iInstance, unsigned iLun, uint32_t fFlags, PPPDMIBASE ppBase)
{
LogFlow(("PDMR3DeviceAttach: pszDevice=%p:{%s} iInstance=%d iLun=%d fFlags=%#x ppBase=%p\n",
/*
* Find the LUN in question.
*/
if (RT_SUCCESS(rc))
{
/*
* Can we attach anything at runtime?
*/
{
{
}
else
}
else
if (ppBase)
}
else if (ppBase)
if (ppBase)
else
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 pVM 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(PVM pVM, const char *pszDevice, unsigned iInstance, unsigned iLun, uint32_t fFlags)
{
}
/**
* 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 pVM 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(PVM pVM, 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",
if (ppBase)
/*
* Find the LUN in question.
*/
if (RT_SUCCESS(rc))
{
/*
* Anything attached to the LUN?
*/
if (!pDrvIns)
{
/* No, ask the device to attach to the new stuff. */
{
}
else
}
else
{
/* Yes, find the bottom most driver and ask it to attach to the new stuff. */
{
: NULL;
}
else
}
}
if (ppBase)
else
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 pVM 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 iOccurance The occurance of that driver in the chain. This is
* usually 0.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
* @thread EMT
*/
{
LogFlow(("PDMR3DriverDetach: pszDevice=%p:{%s} iDevIns=%u iLun=%u pszDriver=%p:{%s} iOccurance=%u fFlags=%#x\n",
/*
* Find the LUN in question.
*/
if (RT_SUCCESS(rc))
{
/*
* Locate the driver.
*/
if (pDrvIns)
{
if (pszDriver)
{
while (pDrvIns)
{
{
if (iOccurance == 0)
break;
iOccurance--;
}
}
}
if (pDrvIns)
else
}
else
}
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 pVM 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 iOccurance The occurance 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.
*/
{
return VERR_NOT_IMPLEMENTED;
}