summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Bus/DevPciMerge1.cpp.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Devices/Bus/DevPciMerge1.cpp.h240
1 files changed, 240 insertions, 0 deletions
diff --git a/src/VBox/Devices/Bus/DevPciMerge1.cpp.h b/src/VBox/Devices/Bus/DevPciMerge1.cpp.h
new file mode 100644
index 00000000..ace0ab5c
--- /dev/null
+++ b/src/VBox/Devices/Bus/DevPciMerge1.cpp.h
@@ -0,0 +1,240 @@
+/* $Id: DevPciMerge1.cpp.h $ */
+/** @file
+ * DevPci - Early attempt at common code for DevPci and DevPciIch9.
+ *
+ * @note Don't add more, add code to DevPciIch9.cpp instead.
+ * @note We'll keep this file like this for a little while longer
+ * because of 5.1.
+ */
+
+/*
+ * Copyright (C) 2004-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * 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.
+ */
+
+
+/**
+ * Search for a completely unused device entry (all 8 functions are unused).
+ *
+ * @returns VBox status code.
+ * @param pBus The bus to register with.
+ * @remarks Caller enters the PDM critical section.
+ */
+static uint8_t pciR3MergedFindUnusedDeviceNo(PDEVPCIBUS pBus)
+{
+ for (uint8_t uPciDevNo = pBus->iDevSearch >> VBOX_PCI_DEVFN_DEV_SHIFT; uPciDevNo < VBOX_PCI_MAX_DEVICES; uPciDevNo++)
+ if ( !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 0)]
+ && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 1)]
+ && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 2)]
+ && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 3)]
+ && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 4)]
+ && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 5)]
+ && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 6)]
+ && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 7)])
+ return uPciDevNo;
+ return UINT8_MAX;
+}
+
+
+
+/**
+ * Registers the device with the specified PCI bus.
+ *
+ * This is shared between the pci bus and pci bridge code.
+ *
+ * @returns VBox status code.
+ * @param pBus The bus to register with.
+ * @param pPciDev The PCI device structure.
+ * @param fFlags Reserved for future use, PDMPCIDEVREG_F_MBZ.
+ * @param uPciDevNo PDMPCIDEVREG_DEV_NO_FIRST_UNUSED, or a specific
+ * device number (0-31).
+ * @param uPciFunNo PDMPCIDEVREG_FUN_NO_FIRST_UNUSED, or a specific
+ * function number (0-7).
+ * @param pszName Device name (static but not unique).
+ * @param pfnConfigRead The default config read method.
+ * @param pfnConfigWrite The default config read method.
+ *
+ * @remarks Caller enters the PDM critical section.
+ */
+static int pciR3MergedRegisterDeviceOnBus(PDEVPCIBUS pBus, PPDMPCIDEV pPciDev, uint32_t fFlags,
+ uint8_t uPciDevNo, uint8_t uPciFunNo, const char *pszName,
+ PFNPCICONFIGREAD pfnConfigRead, PFNPCICONFIGWRITE pfnConfigWrite)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pPciDev, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~PDMPCIDEVREG_F_VALID_MASK), VERR_INVALID_FLAGS);
+ AssertReturn(uPciDevNo < VBOX_PCI_MAX_DEVICES || uPciDevNo == PDMPCIDEVREG_DEV_NO_FIRST_UNUSED, VERR_INVALID_PARAMETER);
+ AssertReturn(uPciFunNo < VBOX_PCI_MAX_FUNCTIONS || uPciFunNo == PDMPCIDEVREG_FUN_NO_FIRST_UNUSED, VERR_INVALID_PARAMETER);
+
+ /*
+ * Assign device & function numbers.
+ */
+
+ /* Work the optional assignment flag. */
+ if (fFlags & PDMPCIDEVREG_F_NOT_MANDATORY_NO)
+ {
+ AssertLogRelMsgReturn(uPciDevNo < VBOX_PCI_MAX_DEVICES && uPciFunNo < VBOX_PCI_MAX_FUNCTIONS,
+ ("PDMPCIDEVREG_F_NOT_MANDATORY_NO not implemented for #Dev=%#x / #Fun=%#x\n", uPciDevNo, uPciFunNo),
+ VERR_NOT_IMPLEMENTED);
+ if (pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)])
+ {
+ uPciDevNo = PDMPCIDEVREG_DEV_NO_FIRST_UNUSED;
+ uPciFunNo = PDMPCIDEVREG_FUN_NO_FIRST_UNUSED;
+ }
+ }
+
+ if (uPciDevNo == PDMPCIDEVREG_DEV_NO_FIRST_UNUSED)
+ {
+ /* Just find the next unused device number and we're good. */
+ uPciDevNo = pciR3MergedFindUnusedDeviceNo(pBus);
+ AssertLogRelMsgReturn(uPciDevNo < VBOX_PCI_MAX_DEVICES, ("Couldn't find a free spot!\n"), VERR_PDM_TOO_PCI_MANY_DEVICES);
+ if (uPciFunNo == PDMPCIDEVREG_FUN_NO_FIRST_UNUSED)
+ uPciFunNo = 0;
+ }
+ else
+ {
+ /*
+ * Direct assignment of device number can be more complicated.
+ */
+ PPDMPCIDEV pClash;
+ if (uPciFunNo != PDMPCIDEVREG_FUN_NO_FIRST_UNUSED)
+ {
+ /* In the case of a specified function, we only relocate an existing
+ device if it belongs to a different device instance. Reasoning is
+ that the device should figure out it's own function assignments.
+ Note! We could make this more flexible by relocating functions assigned
+ via PDMPCIDEVREG_FUN_NO_FIRST_UNUSED, but it can wait till it's needed. */
+ pClash = pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)];
+ if (!pClash)
+ { /* likely */ }
+ else if (pClash->Int.s.pDevInsR3 == pPciDev->Int.s.pDevInsR3)
+ AssertLogRelMsgFailedReturn(("PCI Configuration conflict at %u.%u: %s vs %s (same pDevIns)!\n",
+ uPciDevNo, uPciFunNo, pClash->pszNameR3, pszName),
+ VERR_PDM_TOO_PCI_MANY_DEVICES);
+ else if (!pClash->Int.s.fReassignableDevNo)
+ AssertLogRelMsgFailedReturn(("PCI Configuration conflict at %u.%u: %s vs %s (different pDevIns)!\n",
+ uPciDevNo, uPciFunNo, pClash->pszNameR3, pszName),
+ VERR_PDM_TOO_PCI_MANY_DEVICES);
+ }
+ else
+ {
+ /* First unused function slot. Again, we only relocate the whole
+ thing if all the device instance differs, because we otherwise
+ reason that a device should manage its own functions correctly. */
+ unsigned cSameDevInses = 0;
+ for (uPciFunNo = 0, pClash = NULL; uPciFunNo < VBOX_PCI_MAX_FUNCTIONS; uPciFunNo++)
+ {
+ pClash = pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)];
+ if (!pClash)
+ break;
+ cSameDevInses += pClash->Int.s.pDevInsR3 == pPciDev->Int.s.pDevInsR3;
+ }
+ if (!pClash)
+ Assert(uPciFunNo < VBOX_PCI_MAX_FUNCTIONS);
+ else
+ AssertLogRelMsgReturn(cSameDevInses == 0,
+ ("PCI Configuration conflict at %u.* appending %s (%u of %u pDevIns matches)!\n",
+ uPciDevNo, pszName, cSameDevInses, VBOX_PCI_MAX_FUNCTIONS),
+ VERR_PDM_TOO_PCI_MANY_DEVICES);
+ }
+ if (pClash)
+ {
+ /*
+ * Try relocate the existing device.
+ */
+ /* Check that all functions can be moved. */
+ for (uint8_t uMoveFun = 0; uMoveFun < VBOX_PCI_MAX_FUNCTIONS; uMoveFun++)
+ {
+ PPDMPCIDEV pMovePciDev = pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uMoveFun)];
+ AssertLogRelMsgReturn(!pMovePciDev || pMovePciDev->Int.s.fReassignableDevNo,
+ ("PCI Configuration conflict at %u.%u: %s vs %s\n",
+ uPciDevNo, uMoveFun, pMovePciDev->pszNameR3, pszName),
+ VERR_PDM_TOO_PCI_MANY_DEVICES);
+ }
+
+ /* Find a free device number to move it to. */
+ uint8_t uMoveToDevNo = pciR3MergedFindUnusedDeviceNo(pBus);
+ Assert(uMoveToDevNo != uPciFunNo);
+ AssertLogRelMsgReturn(uMoveToDevNo < VBOX_PCI_MAX_DEVICES,
+ ("No space to relocate device at %u so '%s' can be placed there instead!\n", uPciFunNo, pszName),
+ VERR_PDM_TOO_PCI_MANY_DEVICES);
+
+ /* Execute the move. */
+ for (uint8_t uMoveFun = 0; uMoveFun < VBOX_PCI_MAX_FUNCTIONS; uMoveFun++)
+ {
+ PPDMPCIDEV pMovePciDev = pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uMoveFun)];
+ if (pMovePciDev)
+ {
+ Log(("PCI: Relocating '%s' from %u.%u to %u.%u.\n", pMovePciDev->pszNameR3, uPciDevNo, uMoveFun, uMoveToDevNo, uMoveFun));
+ pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uMoveFun)] = NULL;
+ pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uMoveToDevNo, uMoveFun)] = pMovePciDev;
+ pMovePciDev->uDevFn = VBOX_PCI_DEVFN_MAKE(uMoveToDevNo, uMoveFun);
+ }
+ }
+ }
+ }
+
+ /*
+ * Now, initialize the rest of the PCI device structure.
+ */
+ Assert(!pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)]);
+ pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)] = pPciDev;
+
+ pPciDev->uDevFn = VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo);
+ pPciDev->Int.s.pBusR3 = pBus;
+ pPciDev->Int.s.pBusR0 = MMHyperR3ToR0(PDMDevHlpGetVM(pBus->CTX_SUFF(pDevIns)), pBus);
+ pPciDev->Int.s.pBusRC = MMHyperR3ToRC(PDMDevHlpGetVM(pBus->CTX_SUFF(pDevIns)), pBus);
+ pPciDev->Int.s.pfnConfigRead = pfnConfigRead;
+ pPciDev->Int.s.pfnConfigWrite = pfnConfigWrite;
+
+ /* Remember and mark bridges. */
+ if (fFlags & PDMPCIDEVREG_F_PCI_BRIDGE)
+ {
+ AssertLogRelMsgReturn(pBus->cBridges < RT_ELEMENTS(pBus->apDevices),
+ ("Number of bridges exceeds the number of possible devices on the bus\n"),
+ VERR_INTERNAL_ERROR_3);
+ pBus->papBridgesR3[pBus->cBridges++] = pPciDev;
+ pciDevSetPci2PciBridge(pPciDev);
+ }
+
+ Log(("PCI: Registered device %d function %d (%#x) '%s'.\n",
+ uPciDevNo, uPciFunNo, UINT32_C(0x80000000) | (pPciDev->uDevFn << 8), pszName));
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMPCIBUSREG,pfnRegisterR3}
+ */
+static DECLCALLBACK(int) pciR3MergedRegister(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t fFlags,
+ uint8_t uPciDevNo, uint8_t uPciFunNo, const char *pszName)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ AssertCompileMemberOffset(DEVPCIROOT, PciBus, 0);
+ return pciR3MergedRegisterDeviceOnBus(pBus, pPciDev, fFlags, uPciDevNo, uPciFunNo, pszName,
+ devpciR3CommonDefaultConfigRead, devpciR3CommonDefaultConfigWrite);
+}
+
+
+/**
+ * @interface_method_impl{PDMPCIBUSREG,pfnRegisterR3}
+ */
+static DECLCALLBACK(int) pcibridgeR3MergedRegisterDevice(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t fFlags,
+ uint8_t uPciDevNo, uint8_t uPciFunNo, const char *pszName)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ return pciR3MergedRegisterDeviceOnBus(pBus, pPciDev, fFlags, uPciDevNo, uPciFunNo, pszName,
+ devpciR3CommonDefaultConfigRead, devpciR3CommonDefaultConfigWrite);
+}
+