PDM.cpp revision 96dce0123cc032388e78766d08f9ee5a66b80fac
* @section sec_pdm_dev The Pluggable Devices * Devices register themselves when the module containing them is loaded. PDM * will call the entry point 'VBoxDevicesRegister' when loading a device module. * The device module will then use the supplied callback table to check the VMM * version and to register its devices. Each device have an unique (for the * configured VM) name. The name is not only used in PDM but also in CFGM (to * organize device and device instance settings) and by anyone who wants to talk * to a specific device instance. * When all device modules have been successfully loaded PDM will instantiate * those devices which are configured for the VM. Note that a device may have * more than one instance, see network adaptors for instance. When * instantiating a device PDM provides device instance memory and a callback * table (aka Device Helpers / DevHlp) with the VM APIs which the device * instance is trusted with. * Some devices are trusted devices, most are not. The trusted devices are an * integrated part of the VM and can obtain the VM handle from their device * instance handles, thus enabling them to call any VM api. Untrusted devices * can only use the callbacks provided during device instantiation. * The main purpose in having DevHlps rather than just giving all the devices * the VM handle and let them call the internal VM APIs directly, is both to * create a binary interface that can be supported accross releases and to * create a barrier between devices and the VM. (The trusted / untrusted bit * hasn't turned out to be of much use btw., but it's easy to maintain so there * isn't any point in removing it.) * A device can provide a ring-0 and/or a raw-mode context extension to improve * the VM performance by handling exits and traps (respectively) without * requiring context switches (to ring-3). Callbacks for MMIO and I/O ports can * needs to be registered specifically for the additional contexts for this to * make sense. Also, the device has to be trusted to be loaded into R0/RC * because of the extra privilege it entails. Note that raw-mode code and data * will be subject to relocation. * @section sec_pdm_special_devs Special Devices * Several kinds of devices interacts with the VMM and/or other device and PDM * will work like a mediator for these. The typical pattern is that the device * calls a special registration device helper with a set of callbacks, PDM * responds by copying this and providing a pointer to a set helper callbacks * for that particular kind of device. Unlike interfaces where the callback * table pointer is used a 'this' pointer, these arrangements will use the * device instance pointer (PPDMDEVINS) as a kind of 'this' pointer. * For an example of this kind of setup, see the PIC. The PIC registers itself * by calling PDMDEVHLPR3::pfnPICRegister. PDM saves the device instance, * copies the callback tables (PDMPICREG), resolving the ring-0 and raw-mode * addresses in the process, and hands back the pointer to a set of helper * methods (PDMPICHLPR3). The PCI device then queries the ring-0 and raw-mode * helpers using PDMPICHLPR3::pfnGetR0Helpers and PDMPICHLPR3::pfnGetRCHelpers. * The PCI device repeates ths pfnGetRCHelpers call in it's relocation method * since the address changes when RC is relocated. * @section sec_pdm_usbdev The Pluggable USB Devices * USB devices are handled a little bit differently than other devices. The * general concepts wrt. pluggability are mostly the same, but the details * varies. The registration entry point is 'VBoxUsbRegister', the device * instance is PDMUSBINS and the callbacks helpers are different. Also, USB * device are restricted to ring-3 and cannot have any ring-0 or raw-mode * extensions (at least not yet). * The way USB devices work differs greatly from other devices though since they * aren't attaches directly to the PCI/ISA/whatever system buses but via a * USB host control (OHCI, UHCI or EHCI). USB devices handles USB requests * (URBs) and does not register I/O ports, MMIO ranges or PCI bus * @section sec_pdm_drv The Pluggable Drivers * The VM devices are often accessing host hardware or OS facilities. For most * devices these facilities can be abstracted in one or more levels. These * abstractions are called drivers. * For instance take a DVD/CD drive. This can be connected to a SCSI * controller, an ATA controller or a SATA controller. The basics of the DVD/CD * drive implementation remains the same - eject, insert, read, seek, and such. * (For the scsi case, you might wanna speak SCSI directly to, but that can of * course be fixed - see SCSI passthru.) So, it * makes much sense to have a generic CD/DVD driver which implements this. * Then the media 'inserted' into the DVD/CD drive can be a ISO image, or it can * be read from a real CD or DVD drive (there are probably other custom formats * someone could desire to read or construct too). So, it would make sense to * have abstracted interfaces for dealing with this in a generic way so the * cdrom unit doesn't have to implement it all. Thus we have created the * So, for this example the IDE controller #1 (i.e. secondary) will have * the DVD/CD Driver attached to it's LUN #0 (master). When a media is mounted * the DVD/CD Driver will have a ISO, HostDVD or RAW (media) Driver attached. * It is possible to configure many levels of drivers inserting filters, loggers, * or whatever you desire into the chain. We're using this for network sniffing * The drivers are loaded in a similar manner to that of the device, namely by * iterating a keyspace in CFGM, load the modules listed there and call * 'VBoxDriversRegister' with a callback table. * @section sec_pdm_ifs Interfaces * The pluggable drivers and devices exposes one standard interface (callback * table) which is used to construct, destruct, attach, detach,( ++,) and query * other interfaces. A device will query the interfaces required for it's * operation during init and hot-plug. PDM may query some interfaces during * An interface here means a function table contained within the device or * driver instance data. Its method are invoked with the function table pointer * as the first argument and they will calculate the address of the device or * driver instance data from it. (This is one of the aspects which *might* have * been better done in C++.) * @see grp_pdm_interfaces * @section sec_pdm_utils Utilities * As mentioned earlier, PDM is the location of any usful constrcts that doesn't * quite fit into IPRT. The next subsections will discuss these. * One thing these APIs all have in common is that resources will be associated * with a device / driver and automatically freed after it has been destroyed if * the destructor didn't do this. * @subsection sec_pdm_async_completion Async I/O * The PDM Async I/O API provides a somewhat platform agnostic interface for * asynchronous I/O. For reasons of performance and complexcity this does not * build upon any IPRT API. * @see grp_pdm_async_completion * @subsection sec_pdm_async_task Async Task - not implemented * @todo implement and describe * @see grp_pdm_async_task * @subsection sec_pdm_critsect Critical Section * The PDM Critical Section API is currently building on the IPRT API with the * same name. It adds the posibility to use critical sections in ring-0 and * raw-mode as well as in ring-3. There are certain restrictions on the RC and * R0 usage though since we're not able to wait on it, nor wake up anyone that * is waiting on it. These restrictions origins with the use of a ring-3 event * semaphore. In a later incarnation we plan to replace the ring-3 event * semaphore with a ring-0 one, thus enabling us to wake up waiters while * exectuing in ring-0 and making the hardware assisted execution mode more * efficient. (Raw-mode won't benefit much from this, naturally.) * @subsection sec_pdm_queue Queue * The PDM Queue API is for queuing one or more tasks for later consumption in * ring-3 by EMT, and optinally forcing a delayed or ASAP return to ring-3. The * queues can also be run on a timer basis as an alternative to the ASAP thing. * The queue will be flushed at forced action time. * A queue can also be used by another thread (a I/O worker for instance) to * send work / events over to the EMT. * @subsection sec_pdm_task Task - not implemented yet * The PDM Task API is for flagging a task for execution at a later point when * we're back in ring-3, optionally forcing the ring-3 return to happen ASAP. * As you can see the concept is similar to queues only simpler. * A task can also be scheduled by another thread (a I/O worker for instance) as * a mean of getting something done in EMT. * @subsection sec_pdm_thread Thread * The PDM Thread API is there to help devices and drivers manage their threads * correctly wrt. power on, suspend, resume, power off and destruction. * The general usage pattern for threads in the employ of devices and drivers is * that they shuffle data or requests while the VM is running and stop doing * this when the VM is paused or powered down. Rogue threads running while the * VM is paused can cause the state to change during saving or have other * unwanted side effects. The PDM Threads API ensures that this won't happen. /******************************************************************************* *******************************************************************************/ /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** The PDM saved state version. */ /******************************************************************************* *******************************************************************************/ * Initializes the PDM part of the UVM. * This doesn't really do much right now but has to be here for the sake * @returns VBox status code. * @param pUVM Pointer to the user mode VM structure. * @returns VBox status code. * @param pVM The VM to operate on. * Assert alignment and sizes. * Initialize sub compontents. * Register the saved state data unit. LogFlow((
"PDM: Successfully initialized\n"));
* Cleanup and return failure. * Applies relocations to data and code managed by this * component. This function will be called at init and * whenever the VMM need to relocate it self inside the GC. * @param offDelta Relocation delta relative to old location. * @remark The loader subcomponent is relocated by PDMR3LdrRelocate() very * early in the relocation phase. * The registered I/O APIC. * The register PCI Buses. LogFlow((
"PDMR3Relocate: Relocating device '%s'/%d\n",
LogFlow((
"PDMR3Relocate: Relocating driver '%s'/%u attached to '%s'/%d/%u\n",
* Worker for pdmR3Term that terminates a LUN chain. * @param pVM Pointer to the shared VM structure. * @param pLun The head of the chain. * @param pszDevice The name of the device (for logging). * @param iInstance The device instance number (for logging). * Destroy them one at a time from the bottom up. LogFlow((
"pdmR3DevTerm: Destroying - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
//PDMR3QueueDestroyDriver(pVM, pDrvIns); //pdmR3ThreadDestroyDriver(pVM, pDrvIns); * Termination means cleaning up and freeing all resources, * the VM it self is at this point powered off or suspended. * @returns VBox status code. * @param pVM The VM to operate on. * Iterate the device instances and attach drivers, doing * relevant destruction processing. * N.B. There is no need to mess around freeing memory allocated * from any MM heap since MM will do that in its Term function. LogFlow((
"pdmR3DevTerm: Destroying - device '%s'/%d\n",
//TMR3TimerDestroyUsb(pVM, pUsbIns); //SSMR3DeregisterUsb(pVM, pUsbIns, NULL, 0); /* then the 'normal' ones. */ LogFlow((
"pdmR3DevTerm: Destroying - device '%s'/%d\n",
//SSMR3DeregisterDriver(pVM, pDevIns, NULL, 0); //pdmR3ThreadDestroyDevice(pVM, pDevIns); //PDMR3QueueDestroyDevice(pVM, pDevIns); * Free async completion managers. /* The MiscCritSect is deleted by PDMR3CritSectTerm. */ * Terminates the PDM part of the UVM. * This will unload any modules left behind. * @param pUVM Pointer to the user mode VM structure. * In the normal cause of events we will now call pdmR3LdrTermU for * the second time. In the case of init failure however, this might * the first time, which is why we do it. * Bits that are saved in pass 0 and in the final pass. * @param pVM The VM handle. * @param pSSM The saved state handle. * Save the list of device instances so we can check that they're all still * there when we load the state and that nothing new has been added. * @returns VBox status code. * @param pVM The VM handle. * @param pSSM The saved state handle. * Execute state save operation. * @returns VBox status code. * @param pVM The VM handle. * @param pSSM The saved state handle. * Save interrupt and DMA states. * Prepare state load operation. * This will dispatch pending operations and clear the FFs governed by PDM and its devices. * @returns VBox status code. * @param pVM The VM handle. * @param pSSM The SSM handle. * In case there is work pending that will raise an interrupt, * start a DMA transfer, or release a lock. (unlikely) * Execute state load operation. * @returns VBox status code. * @param pSSM SSM operation handle. * @param uVersion Data layout version. * @param uPass The data pass. * Load the interrupt and DMA states. * Load the list of devices and verify that they are all there. /* Get the sequence number / terminator. */ /* Get the name and instance number. */ * Check that no additional devices were configured. * Worker for PDMR3PowerOn that deals with one driver. * @param pDrvIns The driver instance. * @param pszDeviceName The parent device name. * @param iDevInstance The parent device instance number. * @param iLun The parent LUN number. LogFlow((
"PDMR3PowerOn: Notifying - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
LogRel((
"PDMR3PowerOn: driver '%s'/%d on LUN#%d of device '%s'/%d -> %Rrc\n",
* Worker for PDMR3PowerOn that deals with one USB device instance. * @returns VBox status code. * @param pUsbIns The USB device instance. * Worker for PDMR3PowerOn that deals with one device instance. * @returns VBox status code. * @param pDevIns The device instance. * This function will notify all the devices and their * attached drivers about the VM now being powered on. * Iterate thru the device instances and USB device instances, * processing the drivers associated with those. * On failure, clean up via PDMR3Suspend. LogFlow((
"PDMR3PowerOn: returns %Rrc\n",
rc));
* Worker for PDMR3Reset that deals with one driver. * @param pDrvIns The driver instance. * @param pcAsync The asynchronous reset notification counter. * @param pszDeviceName The parent device name. * @param iDevInstance The parent device instance number. * @param iLun The parent LUN number. LogFlow((
"PDMR3Reset: Notifying - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
LogFlow((
"PDMR3Reset: Async notification started - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
LogFlow((
"PDMR3Reset: Async notification completed - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
* Worker for PDMR3Reset that deals with one USB device instance. * @param pUsbIns The USB device instance. * @param pcAsync The asynchronous reset notification counter. * Worker for PDMR3Reset that deals with one device instance. * @param pDevIns The device instance. * @param pcAsync The asynchronous reset notification counter. * Used by PDMR3Reset and CPU hot plugging. * @param pVCpu The virtual CPU handle. * This function will notify all the devices and their attached drivers about * the VM now being reset. * Clear all the reset flags. * The outer loop repeats until there are no more async requests. * Iterate thru the device instances and USB device instances, * processing the drivers associated with those. /** @todo This is utterly nuts and completely unsafe... will get back to it in a * Clear all pending interrupts and DMA operations. LogFlow((
"PDMR3Reset: returns void\n"));
* Worker for PDMR3Suspend that deals with one driver. * @param pDrvIns The driver instance. * @param pcAsync The asynchronous suspend notification counter. * @param pszDeviceName The parent device name. * @param iDevInstance The parent device instance number. * @param iLun The parent LUN number. LogFlow((
"PDMR3Suspend: Notifying - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
LogFlow((
"PDMR3Suspend: Async notification started - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
LogFlow((
"PDMR3Suspend: Async notification completed - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
* Worker for PDMR3Suspend that deals with one USB device instance. * @param pUsbIns The USB device instance. * @param pcAsync The asynchronous suspend notification counter. * Worker for PDMR3Suspend that deals with one device instance. * @param pDevIns The device instance. * @param pcAsync The asynchronous suspend notification counter. * This function will notify all the devices and their attached drivers about * the VM now being suspended. * @param pVM The VM Handle. * The outer loop repeats until there are no more async requests. * Note! We depend on the suspended indicators to be in the desired state * and we do not reset them before starting because this allows * PDMR3PowerOn and PDMR3Resume to use PDMR3Suspend for cleaning up * Iterate thru the device instances and USB device instances, * processing the drivers associated with those. * The attached drivers are normally processed first. Some devices * (like DevAHCI) though needs to be notified before the drivers so * that it doesn't kick off any new requests after the drivers stopped * taking any. (DrvVD changes to read-only in this particular case.) /** @todo This is utterly nuts and completely unsafe... will get back to it in a LogFlow((
"PDMR3Suspend: returns void\n"));
* Worker for PDMR3Resume that deals with one driver. * @param pDrvIns The driver instance. * @param pszDeviceName The parent device name. * @param iDevInstance The parent device instance number. * @param iLun The parent LUN number. LogFlow((
"PDMR3Resume: Notifying - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
LogRel((
"PDMR3Resume: driver '%s'/%d on LUN#%d of device '%s'/%d -> %Rrc\n",
* Worker for PDMR3Resume that deals with one USB device instance. * @returns VBox status code. * @param pUsbIns The USB device instance. * Worker for PDMR3Resume that deals with one device instance. * @returns VBox status code. * @param pDevIns The device instance. * This function will notify all the devices and their * attached drivers about the VM now being resumed. * Iterate thru the device instances and USB device instances, * processing the drivers associated with those. * On failure, clean up via PDMR3Suspend. LogFlow((
"PDMR3Resume: returns %Rrc\n",
rc));
* Worker for PDMR3PowerOff that deals with one driver. * @param pDrvIns The driver instance. * @param pcAsync The asynchronous power off notification counter. * @param pszDeviceName The parent device name. * @param iDevInstance The parent device instance number. * @param iLun The parent LUN number. LogFlow((
"PDMR3PowerOff: Notifying - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
LogFlow((
"PDMR3PowerOff: Async notification started - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
LogFlow((
"PDMR3PowerOff: Async notification completed - driver '%s'/%d on LUN#%d of device '%s'/%d\n",
* Worker for PDMR3PowerOff that deals with one USB device instance. * @param pUsbIns The USB device instance. * @param pcAsync The asynchronous power off notification counter. * Worker for PDMR3PowerOff that deals with one device instance. * @param pDevIns The device instance. * @param pcAsync The asynchronous power off notification counter. * This function will notify all the devices and their * attached drivers about the VM being powered off. * The outer loop repeats until there are no more async requests. * Iterate thru the device instances and USB device instances, * processing the drivers associated with those. * The attached drivers are normally processed first. Some devices * (like DevAHCI) though needs to be notified before the drivers so * that it doesn't kick off any new requests after the drivers stopped * taking any. (DrvVD changes to read-only in this particular case.) /** @todo This is utterly nuts and completely unsafe... will get back to it in a LogFlow((
"PDMR3PowerOff: returns void\n"));
* Queries the base interace of a device instance. * The caller can use this to query other interfaces the device implements * and use them to talk to the device. * @returns VBox status code. * @param pszDevice Device name. * @param iInstance Device instance. * @param ppBase Where to store the pointer to the base device interface on success. * @remark We're not doing any locking ATM, so don't try call this at times when the * device chain is known to be updated. * Iterate registered devices looking for the device. * Iterate device instances. LogFlow((
"PDMR3DeviceQuery: return VINF_SUCCESS and *ppBase=%p\n", *
ppBase));
LogFlow((
"PDMR3DeviceQuery: returns VERR_PDM_DEVICE_INSTANCE_NO_IBASE\n"));
LogFlow((
"PDMR3DeviceQuery: returns VERR_PDM_DEVICE_INSTANCE_NOT_FOUND\n"));
LogFlow((
"PDMR3QueryDevice: returns VERR_PDM_DEVICE_NOT_FOUND\n"));
* Queries the base interface of a device LUN. * This differs from PDMR3QueryLun by that it returns the interface on the * device and not the top level driver. * @returns VBox status code. * @param pszDevice Device name. * @param iInstance Device instance. * @param iLun The Logical Unit to obtain the interface of. * @param ppBase Where to store the base interface pointer. * @remark We're not doing any locking ATM, so don't try call this at times when the * device chain is known to be updated. LogFlow((
"PDMR3QueryLun: pszDevice=%p:{%s} iInstance=%u iLun=%u ppBase=%p\n",
LogFlow((
"PDMR3QueryDeviceLun: return VINF_SUCCESS and *ppBase=%p\n", *
ppBase));
LogFlow((
"PDMR3QueryDeviceLun: returns %Rrc\n",
rc));
* Query the interface of the top level driver on a LUN. * @returns VBox status code. * @param pszDevice Device name. * @param iInstance Device instance. * @param iLun The Logical Unit to obtain the interface of. * @param ppBase Where to store the base interface pointer. * @remark We're not doing any locking ATM, so don't try call this at times when the * device chain is known to be updated. LogFlow((
"PDMR3QueryLun: pszDevice=%p:{%s} iInstance=%u iLun=%u ppBase=%p\n",
LogFlow((
"PDMR3QueryLun: returns %Rrc\n",
rc));
* Executes pending DMA transfers. /* Note! Not really SMP safe; restrict it to VCPU 0. */ * Service a VMMCALLRING3_PDM_LOCK call. * @returns VBox status code. * @param pVM The VM handle. * Registers the VMM device heap * @returns VBox status code. * @param GCPhys The physical address. * @param pvHeap Ring-3 pointer. * @param cbSize Size of the heap. * Unregisters the VMM device heap * @returns VBox status code. * @param GCPhys The physical address. Log((
"PDMR3UnregisterVMMDevHeap %RGp\n",
GCPhys));
* Allocates memory from the VMM device heap * @returns VBox status code. * @param cbSize Allocation size. * @param pv Ring-3 pointer. (out) /** @todo not a real heap as there's currently only one user. */ * Frees memory from the VMM device heap * @returns VBox status code. * @param pv Ring-3 pointer. Log((
"PDMR3VMMDevHeapFree %RHv\n",
pv));
/** @todo not a real heap as there's currently only one user. */ * Release the PDM lock if owned by the current VCPU * @param pVM The VM to operate on.