summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Bus
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Devices/Bus
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/Bus')
-rw-r--r--src/VBox/Devices/Bus/DevPCI.cpp1780
-rw-r--r--src/VBox/Devices/Bus/DevPciIch9.cpp3684
-rw-r--r--src/VBox/Devices/Bus/DevPciInternal.h240
-rw-r--r--src/VBox/Devices/Bus/DevPciMerge1.cpp.h240
-rw-r--r--src/VBox/Devices/Bus/Makefile.kup0
-rw-r--r--src/VBox/Devices/Bus/MsiCommon.cpp336
-rw-r--r--src/VBox/Devices/Bus/MsiCommon.h41
-rw-r--r--src/VBox/Devices/Bus/MsixCommon.cpp357
-rw-r--r--src/VBox/Devices/Bus/PciInline.h105
-rw-r--r--src/VBox/Devices/Bus/SrvPciRawR0.cpp1031
10 files changed, 7814 insertions, 0 deletions
diff --git a/src/VBox/Devices/Bus/DevPCI.cpp b/src/VBox/Devices/Bus/DevPCI.cpp
new file mode 100644
index 00000000..db50d475
--- /dev/null
+++ b/src/VBox/Devices/Bus/DevPCI.cpp
@@ -0,0 +1,1780 @@
+/* $Id: DevPCI.cpp $ */
+/** @file
+ * DevPCI - PCI BUS Device.
+ *
+ * @remarks New code shall be added to DevPciIch9.cpp as that will become
+ * the common PCI bus code soon. Don't fix code in both DevPCI.cpp
+ * and DevPciIch9.cpp when it's possible to just make the latter
+ * version common. Common code uses the 'devpci' prefix, is
+ * prototyped in DevPciInternal.h, and is defined in DevPciIch9.cpp.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ * --------------------------------------------------------------------
+ *
+ * This code is based on:
+ *
+ * QEMU PCI bus manager
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_PCI
+#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
+#include <VBox/vmm/pdmpcidev.h>
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/mm.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include "PciInline.h"
+#include "VBoxDD.h"
+#include "DevPciInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Saved state version of the PCI bus device. */
+#define VBOX_PCI_SAVED_STATE_VERSION VBOX_PCI_SAVED_STATE_VERSION_REGION_SIZES
+/** Adds I/O region types and sizes for dealing changes in resource regions. */
+#define VBOX_PCI_SAVED_STATE_VERSION_REGION_SIZES 4
+/** Before region sizes, the first named one.
+ * Looking at the code though, we support even older version. */
+#define VBOX_PCI_SAVED_STATE_VERSION_IRQ_STATES 3
+/** Notes whether we use the I/O APIC. */
+#define VBOX_PCI_SAVED_STATE_VERSION_USE_IO_APIC 2
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+
+PDMBOTHCBDECL(void) pciSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTag);
+PDMBOTHCBDECL(void) pcibridgeSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTag);
+PDMBOTHCBDECL(int) pciIOPortAddressWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
+PDMBOTHCBDECL(int) pciIOPortAddressRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
+PDMBOTHCBDECL(int) pciIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
+PDMBOTHCBDECL(int) pciIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
+
+#ifdef IN_RING3
+DECLINLINE(PPDMPCIDEV) pciR3FindBridge(PDEVPCIBUS pBus, uint8_t iBus);
+#endif
+
+RT_C_DECLS_END
+
+#define DEBUG_PCI
+
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x01 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x02 /* Enable response in Memory space */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+
+static int pci_data_write(PDEVPCIROOT pGlobals, uint32_t addr, uint32_t val, int len)
+{
+ uint8_t iBus, iDevice;
+ uint32_t config_addr;
+
+ LogFunc(("addr=%08x val=%08x len=%d\n", pGlobals->uConfigReg, val, len));
+
+ if (!(pGlobals->uConfigReg & (1 << 31))) {
+ return VINF_SUCCESS;
+ }
+ if ((pGlobals->uConfigReg & 0x3) != 0) {
+ return VINF_SUCCESS;
+ }
+ iBus = (pGlobals->uConfigReg >> 16) & 0xff;
+ iDevice = (pGlobals->uConfigReg >> 8) & 0xff;
+ config_addr = (pGlobals->uConfigReg & 0xfc) | (addr & 3);
+ RT_UNTRUSTED_VALIDATED_FENCE(); /* paranoia */
+ if (iBus != 0)
+ {
+ if (pGlobals->PciBus.cBridges)
+ {
+#ifdef IN_RING3 /** @todo do lookup in R0/RC too! */
+ PPDMPCIDEV pBridgeDevice = pciR3FindBridge(&pGlobals->PciBus, iBus);
+ if (pBridgeDevice)
+ {
+ AssertPtr(pBridgeDevice->Int.s.pfnBridgeConfigWrite);
+ pBridgeDevice->Int.s.pfnBridgeConfigWrite(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), iBus, iDevice, config_addr, val, len);
+ }
+#else
+ RT_NOREF2(val, len);
+ return VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ }
+ }
+ else
+ {
+ R3PTRTYPE(PDMPCIDEV *) pci_dev = pGlobals->PciBus.apDevices[iDevice];
+ if (pci_dev)
+ {
+#ifdef IN_RING3
+ LogFunc(("%s: addr=%02x val=%08x len=%d\n", pci_dev->pszNameR3, config_addr, val, len));
+ return VBOXSTRICTRC_TODO(pci_dev->Int.s.pfnConfigWrite(pci_dev->Int.s.CTX_SUFF(pDevIns), pci_dev, config_addr, val, len));
+#else
+ return VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+static int pci_data_read(PDEVPCIROOT pGlobals, uint32_t addr, int len, uint32_t *pu32)
+{
+ uint8_t iBus, iDevice;
+ uint32_t config_addr;
+
+ *pu32 = 0xffffffff;
+
+ if (!(pGlobals->uConfigReg & (1 << 31)))
+ return VINF_SUCCESS;
+ if ((pGlobals->uConfigReg & 0x3) != 0)
+ return VINF_SUCCESS;
+ iBus = (pGlobals->uConfigReg >> 16) & 0xff;
+ iDevice = (pGlobals->uConfigReg >> 8) & 0xff;
+ config_addr = (pGlobals->uConfigReg & 0xfc) | (addr & 3);
+ RT_UNTRUSTED_VALIDATED_FENCE();
+ if (iBus != 0)
+ {
+ if (pGlobals->PciBus.cBridges)
+ {
+#ifdef IN_RING3 /** @todo do lookup in R0/RC too! */
+ PPDMPCIDEV pBridgeDevice = pciR3FindBridge(&pGlobals->PciBus, iBus);
+ if (pBridgeDevice)
+ {
+ AssertPtr(pBridgeDevice->Int.s.pfnBridgeConfigRead);
+ *pu32 = pBridgeDevice->Int.s.pfnBridgeConfigRead(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), iBus, iDevice, config_addr, len);
+ }
+#else
+ NOREF(len);
+ return VINF_IOM_R3_IOPORT_READ;
+#endif
+ }
+ }
+ else
+ {
+ R3PTRTYPE(PDMPCIDEV *) pci_dev = pGlobals->PciBus.apDevices[iDevice];
+ if (pci_dev)
+ {
+#ifdef IN_RING3
+ *pu32 = pci_dev->Int.s.pfnConfigRead(pci_dev->Int.s.CTX_SUFF(pDevIns), pci_dev, config_addr, len);
+ LogFunc(("%s: addr=%02x val=%08x len=%d\n", pci_dev->pszNameR3, config_addr, *pu32, len));
+#else
+ NOREF(len);
+ return VINF_IOM_R3_IOPORT_READ;
+#endif
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+
+/* return the global irq number corresponding to a given device irq
+ pin. We could also use the bus number to have a more precise
+ mapping.
+ This is the implementation note described in the PCI spec chapter 2.2.6 */
+static inline int pci_slot_get_pirq(uint8_t uDevFn, int irq_num)
+{
+ int slot_addend;
+ slot_addend = (uDevFn >> 3) - 1;
+ return (irq_num + slot_addend) & 3;
+}
+
+static inline int pci_slot_get_apic_pirq(uint8_t uDevFn, int irq_num)
+{
+ return (irq_num + (uDevFn >> 3)) & 7;
+}
+
+static inline int get_pci_irq_apic_level(PDEVPCIROOT pGlobals, int irq_num)
+{
+ return (pGlobals->auPciApicIrqLevels[irq_num] != 0);
+}
+
+static void apic_set_irq(PDEVPCIBUS pBus, uint8_t uDevFn, PDMPCIDEV *pPciDev, int irq_num1, int iLevel, int iAcpiIrq, uint32_t uTagSrc)
+{
+ /* This is only allowed to be called with a pointer to the host bus. */
+ AssertMsg(pBus->iBus == 0, ("iBus=%u\n", pBus->iBus));
+
+ if (iAcpiIrq == -1) {
+ int apic_irq, apic_level;
+ PDEVPCIROOT pGlobals = DEVPCIBUS_2_DEVPCIROOT(pBus);
+ int irq_num = pci_slot_get_apic_pirq(uDevFn, irq_num1);
+
+ if ((iLevel & PDM_IRQ_LEVEL_HIGH) == PDM_IRQ_LEVEL_HIGH)
+ ASMAtomicIncU32(&pGlobals->auPciApicIrqLevels[irq_num]);
+ else if ((iLevel & PDM_IRQ_LEVEL_HIGH) == PDM_IRQ_LEVEL_LOW)
+ ASMAtomicDecU32(&pGlobals->auPciApicIrqLevels[irq_num]);
+
+ apic_irq = irq_num + 0x10;
+ apic_level = get_pci_irq_apic_level(pGlobals, irq_num);
+ Log3Func(("%s: irq_num1=%d level=%d apic_irq=%d apic_level=%d irq_num1=%d\n",
+ R3STRING(pPciDev->pszNameR3), irq_num1, iLevel, apic_irq, apic_level, irq_num));
+ pBus->CTX_SUFF(pPciHlp)->pfnIoApicSetIrq(pBus->CTX_SUFF(pDevIns), apic_irq, apic_level, uTagSrc);
+
+ if ((iLevel & PDM_IRQ_LEVEL_FLIP_FLOP) == PDM_IRQ_LEVEL_FLIP_FLOP) {
+ ASMAtomicDecU32(&pGlobals->auPciApicIrqLevels[irq_num]);
+ pPciDev->Int.s.uIrqPinState = PDM_IRQ_LEVEL_LOW;
+ apic_level = get_pci_irq_apic_level(pGlobals, irq_num);
+ Log3Func(("%s: irq_num1=%d level=%d apic_irq=%d apic_level=%d irq_num1=%d (flop)\n",
+ R3STRING(pPciDev->pszNameR3), irq_num1, iLevel, apic_irq, apic_level, irq_num));
+ pBus->CTX_SUFF(pPciHlp)->pfnIoApicSetIrq(pBus->CTX_SUFF(pDevIns), apic_irq, apic_level, uTagSrc);
+ }
+ } else {
+ Log3Func(("%s: irq_num1=%d level=%d iAcpiIrq=%d\n",
+ R3STRING(pPciDev->pszNameR3), irq_num1, iLevel, iAcpiIrq));
+ pBus->CTX_SUFF(pPciHlp)->pfnIoApicSetIrq(pBus->CTX_SUFF(pDevIns), iAcpiIrq, iLevel, uTagSrc);
+ }
+}
+
+DECLINLINE(int) get_pci_irq_level(PDEVPCIROOT pGlobals, int irq_num)
+{
+ return (pGlobals->Piix3.auPciLegacyIrqLevels[irq_num] != 0);
+}
+
+/**
+ * Set the IRQ for a PCI device on the host bus - shared by host bus and bridge.
+ *
+ * @param pGlobals Device instance of the host PCI Bus.
+ * @param uDevFn The device number on the host bus which will raise the IRQ
+ * @param pPciDev The PCI device structure which raised the interrupt.
+ * @param iIrq IRQ number to set.
+ * @param iLevel IRQ level.
+ * @param uTagSrc The IRQ tag and source ID (for tracing).
+ * @remark uDevFn and pPciDev->uDevFn are not the same if the device is behind
+ * a bridge. In that case uDevFn will be the slot of the bridge which
+ * is needed to calculate the PIRQ value.
+ */
+static void pciSetIrqInternal(PDEVPCIROOT pGlobals, uint8_t uDevFn, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTagSrc)
+{
+ PDEVPCIBUS pBus = &pGlobals->PciBus;
+ uint8_t *pbCfg = pGlobals->Piix3.PIIX3State.dev.abConfig;
+ const bool fIsAcpiDevice = pPciDev->abConfig[2] == 0x13 && pPciDev->abConfig[3] == 0x71;
+ /* If the two configuration space bytes at 0xde, 0xad are set to 0xbe, 0xef, a back door
+ * is opened to route PCI interrupts directly to the I/O APIC and bypass the PIC.
+ * See the \_SB_.PCI0._PRT method in vbox.dsl.
+ */
+ const bool fIsApicEnabled = pGlobals->fUseIoApic && pbCfg[0xde] == 0xbe && pbCfg[0xad] == 0xef;
+ int pic_irq, pic_level;
+
+ /* Check if the state changed. */
+ if (pPciDev->Int.s.uIrqPinState != iLevel)
+ {
+ pPciDev->Int.s.uIrqPinState = (iLevel & PDM_IRQ_LEVEL_HIGH);
+
+ /* Send interrupt to I/O APIC only. */
+ if (fIsApicEnabled)
+ {
+ if (fIsAcpiDevice)
+ /*
+ * ACPI needs special treatment since SCI is hardwired and
+ * should not be affected by PCI IRQ routing tables at the
+ * same time SCI IRQ is shared in PCI sense hence this
+ * kludge (i.e. we fetch the hardwired value from ACPIs
+ * PCI device configuration space).
+ */
+ apic_set_irq(pBus, uDevFn, pPciDev, -1, iLevel, pPciDev->abConfig[PCI_INTERRUPT_LINE], uTagSrc);
+ else
+ apic_set_irq(pBus, uDevFn, pPciDev, iIrq, iLevel, -1, uTagSrc);
+ return;
+ }
+
+ if (fIsAcpiDevice)
+ {
+ /* As per above treat ACPI in a special way */
+ pic_irq = pPciDev->abConfig[PCI_INTERRUPT_LINE];
+ pGlobals->Piix3.iAcpiIrq = pic_irq;
+ pGlobals->Piix3.iAcpiIrqLevel = iLevel & PDM_IRQ_LEVEL_HIGH;
+ }
+ else
+ {
+ int irq_num;
+ irq_num = pci_slot_get_pirq(uDevFn, iIrq);
+
+ if (pPciDev->Int.s.uIrqPinState == PDM_IRQ_LEVEL_HIGH)
+ ASMAtomicIncU32(&pGlobals->Piix3.auPciLegacyIrqLevels[irq_num]);
+ else if (pPciDev->Int.s.uIrqPinState == PDM_IRQ_LEVEL_LOW)
+ ASMAtomicDecU32(&pGlobals->Piix3.auPciLegacyIrqLevels[irq_num]);
+
+ /* now we change the pic irq level according to the piix irq mappings */
+ pic_irq = pbCfg[0x60 + irq_num];
+ if (pic_irq >= 16)
+ {
+ if ((iLevel & PDM_IRQ_LEVEL_FLIP_FLOP) == PDM_IRQ_LEVEL_FLIP_FLOP)
+ {
+ ASMAtomicDecU32(&pGlobals->Piix3.auPciLegacyIrqLevels[irq_num]);
+ pPciDev->Int.s.uIrqPinState = PDM_IRQ_LEVEL_LOW;
+ }
+
+ return;
+ }
+ }
+
+ /* the pic level is the logical OR of all the PCI irqs mapped to it */
+ pic_level = 0;
+ if (pic_irq == pbCfg[0x60])
+ pic_level |= get_pci_irq_level(pGlobals, 0);
+ if (pic_irq == pbCfg[0x61])
+ pic_level |= get_pci_irq_level(pGlobals, 1);
+ if (pic_irq == pbCfg[0x62])
+ pic_level |= get_pci_irq_level(pGlobals, 2);
+ if (pic_irq == pbCfg[0x63])
+ pic_level |= get_pci_irq_level(pGlobals, 3);
+ if (pic_irq == pGlobals->Piix3.iAcpiIrq)
+ pic_level |= pGlobals->Piix3.iAcpiIrqLevel;
+
+ Log3Func(("%s: iLevel=%d iIrq=%d pic_irq=%d pic_level=%d uTagSrc=%#x\n",
+ R3STRING(pPciDev->pszNameR3), iLevel, iIrq, pic_irq, pic_level, uTagSrc));
+ pBus->CTX_SUFF(pPciHlp)->pfnIsaSetIrq(pBus->CTX_SUFF(pDevIns), pic_irq, pic_level, uTagSrc);
+
+ /** @todo optimize pci irq flip-flop some rainy day. */
+ if ((iLevel & PDM_IRQ_LEVEL_FLIP_FLOP) == PDM_IRQ_LEVEL_FLIP_FLOP)
+ pciSetIrqInternal(pGlobals, uDevFn, pPciDev, iIrq, PDM_IRQ_LEVEL_LOW, uTagSrc);
+ }
+}
+
+
+/**
+ * @interface_method_impl{PDMPCIBUSREG,pfnSetIrqR3}
+ */
+PDMBOTHCBDECL(void) pciSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTagSrc)
+{
+ pciSetIrqInternal(PDMINS_2_DATA(pDevIns, PDEVPCIROOT), pPciDev->uDevFn, pPciDev, iIrq, iLevel, uTagSrc);
+}
+
+#ifdef IN_RING3
+
+/**
+ * Finds a bridge on the bus which contains the destination bus.
+ *
+ * @return Pointer to the device instance data of the bus or
+ * NULL if no bridge was found.
+ * @param pBus Pointer to the bus to search on.
+ * @param iBus Destination bus number.
+ */
+DECLINLINE(PPDMPCIDEV) pciR3FindBridge(PDEVPCIBUS pBus, uint8_t iBus)
+{
+ /* Search for a fitting bridge. */
+ for (uint32_t iBridge = 0; iBridge < pBus->cBridges; iBridge++)
+ {
+ /*
+ * Examine secondary and subordinate bus number.
+ * If the target bus is in the range we pass the request on to the bridge.
+ */
+ PPDMPCIDEV pBridgeTemp = pBus->papBridgesR3[iBridge];
+ AssertMsg(pBridgeTemp && pciDevIsPci2PciBridge(pBridgeTemp),
+ ("Device is not a PCI bridge but on the list of PCI bridges\n"));
+
+ if ( iBus >= pBridgeTemp->abConfig[VBOX_PCI_SECONDARY_BUS]
+ && iBus <= pBridgeTemp->abConfig[VBOX_PCI_SUBORDINATE_BUS])
+ return pBridgeTemp;
+ }
+
+ /* Nothing found. */
+ return NULL;
+}
+
+static void pciR3Piix3Reset(PIIX3ISABRIDGE *d)
+{
+ uint8_t *pci_conf = d->dev.abConfig;
+
+ pci_conf[0x04] = 0x07; /* master, memory and I/O */
+ pci_conf[0x05] = 0x00;
+ pci_conf[0x06] = 0x00;
+ pci_conf[0x07] = 0x02; /* PCI_status_devsel_medium */
+ pci_conf[0x4c] = 0x4d;
+ pci_conf[0x4e] = 0x03;
+ pci_conf[0x4f] = 0x00;
+ pci_conf[0x60] = 0x80;
+ pci_conf[0x69] = 0x02;
+ pci_conf[0x70] = 0x80;
+ pci_conf[0x76] = 0x0c;
+ pci_conf[0x77] = 0x0c;
+ pci_conf[0x78] = 0x02;
+ pci_conf[0x79] = 0x00;
+ pci_conf[0x80] = 0x00;
+ pci_conf[0x82] = 0x02; /* Get rid of the Linux guest "Enabling Passive Release" PCI quirk warning. */
+ pci_conf[0xa0] = 0x08;
+ pci_conf[0xa2] = 0x00;
+ pci_conf[0xa3] = 0x00;
+ pci_conf[0xa4] = 0x00;
+ pci_conf[0xa5] = 0x00;
+ pci_conf[0xa6] = 0x00;
+ pci_conf[0xa7] = 0x00;
+ pci_conf[0xa8] = 0x0f;
+ pci_conf[0xaa] = 0x00;
+ pci_conf[0xab] = 0x00;
+ pci_conf[0xac] = 0x00;
+ pci_conf[0xae] = 0x00;
+}
+
+/* host irqs corresponding to PCI irqs A-D */
+static const uint8_t pci_irqs[4] = { 11, 10, 9, 11 }; /* bird: added const */
+
+static void pci_bios_init_device(PDEVPCIROOT pGlobals, PDEVPCIBUS pBus, PPDMPCIDEV pPciDev, uint8_t cBridgeDepth, uint8_t *paBridgePositions)
+{
+ uint32_t *paddr;
+ int pin, pic_irq;
+ uint16_t devclass, vendor_id, device_id;
+
+ devclass = devpciR3GetWord(pPciDev, PCI_CLASS_DEVICE);
+ vendor_id = devpciR3GetWord(pPciDev, PCI_VENDOR_ID);
+ device_id = devpciR3GetWord(pPciDev, PCI_DEVICE_ID);
+
+ /* Check if device is present. */
+ if (vendor_id != 0xffff)
+ {
+ switch(devclass)
+ {
+ case 0x0101:
+ if ( (vendor_id == 0x8086)
+ && (device_id == 0x7010 || device_id == 0x7111 || device_id == 0x269e))
+ {
+ /* PIIX3, PIIX4 or ICH6 IDE */
+ devpciR3SetWord(pPciDev, 0x40, 0x8000); /* enable IDE0 */
+ devpciR3SetWord(pPciDev, 0x42, 0x8000); /* enable IDE1 */
+ goto default_map;
+ }
+ else
+ {
+ /* IDE: we map it as in ISA mode */
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, 0, 0x1f0);
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, 1, 0x3f4);
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, 2, 0x170);
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, 3, 0x374);
+ devpciR3SetWord(pPciDev, PCI_COMMAND,
+ devpciR3GetWord(pPciDev, PCI_COMMAND)
+ | PCI_COMMAND_IOACCESS);
+ }
+ break;
+ case 0x0300:
+ {
+ if (vendor_id != 0x80ee)
+ goto default_map;
+ /* VGA: map frame buffer to default Bochs VBE address */
+ int iRegion = devpciR3GetWord(pPciDev, VBOX_PCI_SUBSYSTEM_VENDOR_ID) == 0x15ad ? 1 : 0;
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, iRegion, 0xe0000000);
+ /*
+ * Legacy VGA I/O ports are implicitly decoded by a VGA class device. But
+ * only the framebuffer (i.e., a memory region) is explicitly registered via
+ * devpciR3BiosInitSetRegionAddress, so don't forget to enable I/O decoding.
+ */
+ devpciR3SetWord(pPciDev, PCI_COMMAND,
+ devpciR3GetWord(pPciDev, PCI_COMMAND)
+ | PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS);
+ break;
+ }
+ case 0x0800:
+ /* PIC */
+ vendor_id = devpciR3GetWord(pPciDev, PCI_VENDOR_ID);
+ device_id = devpciR3GetWord(pPciDev, PCI_DEVICE_ID);
+ if (vendor_id == 0x1014)
+ {
+ /* IBM */
+ if (device_id == 0x0046 || device_id == 0xFFFF)
+ {
+ /* MPIC & MPIC2 */
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, 0, 0x80800000 + 0x00040000);
+ devpciR3SetWord(pPciDev, PCI_COMMAND,
+ devpciR3GetWord(pPciDev, PCI_COMMAND)
+ | PCI_COMMAND_MEMACCESS);
+ }
+ }
+ break;
+ case 0xff00:
+ if ( (vendor_id == 0x0106b)
+ && (device_id == 0x0017 || device_id == 0x0022))
+ {
+ /* macio bridge */
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, 0, 0x80800000);
+ devpciR3SetWord(pPciDev, PCI_COMMAND,
+ devpciR3GetWord(pPciDev, PCI_COMMAND)
+ | PCI_COMMAND_MEMACCESS);
+ }
+ break;
+ case 0x0604:
+ {
+ /* Init PCI-to-PCI bridge. */
+ devpciR3SetByte(pPciDev, VBOX_PCI_PRIMARY_BUS, pBus->iBus);
+
+ AssertMsg(pGlobals->uPciBiosBus < 255, ("Too many bridges on the bus\n"));
+ pGlobals->uPciBiosBus++;
+ devpciR3SetByte(pPciDev, VBOX_PCI_SECONDARY_BUS, pGlobals->uPciBiosBus);
+ devpciR3SetByte(pPciDev, VBOX_PCI_SUBORDINATE_BUS, 0xff); /* Temporary until we know how many other bridges are behind this one. */
+
+ /* Add position of this bridge into the array. */
+ paBridgePositions[cBridgeDepth+1] = (pPciDev->uDevFn >> 3);
+
+ /*
+ * The I/O range for the bridge must be aligned to a 4KB boundary.
+ * This does not change anything really as the access to the device is not going
+ * through the bridge but we want to be compliant to the spec.
+ */
+ if ((pGlobals->uPciBiosIo % _4K) != 0)
+ pGlobals->uPciBiosIo = RT_ALIGN_32(pGlobals->uPciBiosIo, _4K);
+ LogFunc(("Aligned I/O start address. New address %#x\n", pGlobals->uPciBiosIo));
+ devpciR3SetByte(pPciDev, VBOX_PCI_IO_BASE, (pGlobals->uPciBiosIo >> 8) & 0xf0);
+
+ /* The MMIO range for the bridge must be aligned to a 1MB boundary. */
+ if ((pGlobals->uPciBiosMmio % _1M) != 0)
+ pGlobals->uPciBiosMmio = RT_ALIGN_32(pGlobals->uPciBiosMmio, _1M);
+ LogFunc(("Aligned MMIO start address. New address %#x\n", pGlobals->uPciBiosMmio));
+ devpciR3SetWord(pPciDev, VBOX_PCI_MEMORY_BASE, (pGlobals->uPciBiosMmio >> 16) & UINT32_C(0xffff0));
+
+ /* Save values to compare later to. */
+ uint32_t u32IoAddressBase = pGlobals->uPciBiosIo;
+ uint32_t u32MMIOAddressBase = pGlobals->uPciBiosMmio;
+
+ /* Init devices behind the bridge and possibly other bridges as well. */
+ PDEVPCIBUS pChildBus = PDMINS_2_DATA(pPciDev->Int.s.CTX_SUFF(pDevIns), PDEVPCIBUS);
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pChildBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pChildPciDev = pChildBus->apDevices[uDevFn];
+ if (pChildPciDev)
+ pci_bios_init_device(pGlobals, pChildBus, pChildPciDev, cBridgeDepth + 1, paBridgePositions);
+ }
+
+ /* The number of bridges behind the this one is now available. */
+ devpciR3SetByte(pPciDev, VBOX_PCI_SUBORDINATE_BUS, pGlobals->uPciBiosBus);
+
+ /*
+ * Set I/O limit register. If there is no device with I/O space behind the bridge
+ * we set a lower value than in the base register.
+ * The result with a real bridge is that no I/O transactions are passed to the secondary
+ * interface. Again this doesn't really matter here but we want to be compliant to the spec.
+ */
+ if ((u32IoAddressBase != pGlobals->uPciBiosIo) && ((pGlobals->uPciBiosIo % _4K) != 0))
+ {
+ /* The upper boundary must be one byte less than a 4KB boundary. */
+ pGlobals->uPciBiosIo = RT_ALIGN_32(pGlobals->uPciBiosIo, _4K);
+ }
+ devpciR3SetByte(pPciDev, VBOX_PCI_IO_LIMIT, ((pGlobals->uPciBiosIo >> 8) & 0xf0) - 1);
+
+ /* Same with the MMIO limit register but with 1MB boundary here. */
+ if ((u32MMIOAddressBase != pGlobals->uPciBiosMmio) && ((pGlobals->uPciBiosMmio % _1M) != 0))
+ {
+ /* The upper boundary must be one byte less than a 1MB boundary. */
+ pGlobals->uPciBiosMmio = RT_ALIGN_32(pGlobals->uPciBiosMmio, _1M);
+ }
+ devpciR3SetWord(pPciDev, VBOX_PCI_MEMORY_LIMIT, ((pGlobals->uPciBiosMmio >> 16) & UINT32_C(0xfff0)) - 1);
+
+ /*
+ * Set the prefetch base and limit registers. We currently have no device with a prefetchable region
+ * which may be behind a bridge. That's why it is unconditionally disabled here atm by writing a higher value into
+ * the base register than in the limit register.
+ */
+ devpciR3SetWord(pPciDev, VBOX_PCI_PREF_MEMORY_BASE, 0xfff0);
+ devpciR3SetWord(pPciDev, VBOX_PCI_PREF_MEMORY_LIMIT, 0x0);
+ devpciR3SetDWord(pPciDev, VBOX_PCI_PREF_BASE_UPPER32, 0x00);
+ devpciR3SetDWord(pPciDev, VBOX_PCI_PREF_LIMIT_UPPER32, 0x00);
+ break;
+ }
+ default:
+ default_map:
+ {
+ /* default memory mappings */
+ bool fActiveMemRegion = false;
+ bool fActiveIORegion = false;
+ /*
+ * PCI_NUM_REGIONS is 7 because of the rom region but there are only 6 base address register defined by the PCI spec.
+ * Leaving only PCI_NUM_REGIONS would cause reading another and enabling a memory region which does not exist.
+ */
+ for (unsigned i = 0; i < (PCI_NUM_REGIONS-1); i++)
+ {
+ uint32_t u32Size;
+ uint8_t u8RessourceType;
+ uint32_t u32Address = 0x10 + i * 4;
+
+ /* Calculate size. */
+ u8RessourceType = devpciR3GetByte(pPciDev, u32Address);
+ devpciR3SetDWord(pPciDev, u32Address, UINT32_C(0xffffffff));
+ u32Size = devpciR3GetDWord(pPciDev, u32Address);
+ bool fIsPio = ((u8RessourceType & PCI_COMMAND_IOACCESS) == PCI_COMMAND_IOACCESS);
+ /* Clear resource information depending on resource type. */
+ if (fIsPio) /* I/O */
+ u32Size &= ~(0x01);
+ else /* MMIO */
+ u32Size &= ~(0x0f);
+
+ /*
+ * Invert all bits and add 1 to get size of the region.
+ * (From PCI implementation note)
+ */
+ if (fIsPio && (u32Size & UINT32_C(0xffff0000)) == 0)
+ u32Size = (~(u32Size | UINT32_C(0xffff0000))) + 1;
+ else
+ u32Size = (~u32Size) + 1;
+
+ Log2Func(("Size of region %u for device %d on bus %d is %u\n", i, pPciDev->uDevFn, pBus->iBus, u32Size));
+
+ if (u32Size)
+ {
+ if (fIsPio)
+ paddr = &pGlobals->uPciBiosIo;
+ else
+ paddr = &pGlobals->uPciBiosMmio;
+ uint32_t uNew = *paddr;
+ uNew = (uNew + u32Size - 1) & ~(u32Size - 1);
+ if (fIsPio)
+ uNew &= UINT32_C(0xffff);
+ /* Unconditionally exclude I/O-APIC/HPET/ROM. Pessimistic, but better than causing a mess. */
+ if (!uNew || (uNew <= UINT32_C(0xffffffff) && uNew + u32Size - 1 >= UINT32_C(0xfec00000)))
+ {
+ LogRel(("PCI: no space left for BAR%u of device %u/%u/%u (vendor=%#06x device=%#06x)\n",
+ i, pBus->iBus, pPciDev->uDevFn >> 3, pPciDev->uDevFn & 7, vendor_id, device_id)); /** @todo make this a VM start failure later. */
+ /* Undo the mapping mess caused by the size probing. */
+ devpciR3SetDWord(pPciDev, u32Address, UINT32_C(0));
+ }
+ else
+ {
+ LogFunc(("Start address of %s region %u is %#x\n", (fIsPio ? "I/O" : "MMIO"), i, uNew));
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, i, uNew);
+ if (fIsPio)
+ fActiveIORegion = true;
+ else
+ fActiveMemRegion = true;
+ *paddr = uNew + u32Size;
+ Log2Func(("New address is %#x\n", *paddr));
+ }
+ }
+ }
+
+ /* Update the command word appropriately. */
+ devpciR3SetWord(pPciDev, PCI_COMMAND,
+ devpciR3GetWord(pPciDev, PCI_COMMAND)
+ | (fActiveMemRegion ? PCI_COMMAND_MEMACCESS : 0)
+ | (fActiveIORegion ? PCI_COMMAND_IOACCESS : 0));
+
+ break;
+ }
+ }
+
+ /* map the interrupt */
+ pin = devpciR3GetByte(pPciDev, PCI_INTERRUPT_PIN);
+ if (pin != 0)
+ {
+ uint8_t uBridgeDevFn = pPciDev->uDevFn;
+ pin--;
+
+ /* We need to go up to the host bus to see which irq this device will assert there. */
+ while (cBridgeDepth != 0)
+ {
+ /* Get the pin the device would assert on the bridge. */
+ pin = ((uBridgeDevFn >> 3) + pin) & 3;
+ uBridgeDevFn = paBridgePositions[cBridgeDepth];
+ cBridgeDepth--;
+ }
+
+ pin = pci_slot_get_pirq(pPciDev->uDevFn, pin);
+ pic_irq = pci_irqs[pin];
+ devpciR3SetByte(pPciDev, PCI_INTERRUPT_LINE, pic_irq);
+ }
+ }
+}
+
+/**
+ * Worker for Fake PCI BIOS config, triggered by magic port access by BIOS.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns i440FX device instance.
+ */
+static int pciR3FakePCIBIOS(PPDMDEVINS pDevIns)
+{
+ uint8_t elcr[2] = {0, 0};
+ PDEVPCIROOT pGlobals = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PVM pVM = PDMDevHlpGetVM(pDevIns); Assert(pVM);
+ PVMCPU pVCpu = PDMDevHlpGetVMCPU(pDevIns); Assert(pVM);
+ uint32_t const cbBelow4GB = MMR3PhysGetRamSizeBelow4GB(pVM);
+ uint64_t const cbAbove4GB = MMR3PhysGetRamSizeAbove4GB(pVM);
+ RT_NOREF(cbBelow4GB, cbAbove4GB);
+
+ LogRel(("PCI: Setting up resources and interrupts\n"));
+
+ /*
+ * Set the start addresses.
+ */
+ pGlobals->uPciBiosBus = 0;
+ pGlobals->uPciBiosIo = 0xd000;
+ pGlobals->uPciBiosMmio = UINT32_C(0xf0000000);
+
+ /*
+ * Activate IRQ mappings.
+ */
+ PPDMPCIDEV pPIIX3 = &pGlobals->Piix3.PIIX3State.dev;
+ for (unsigned i = 0; i < 4; i++)
+ {
+ uint8_t irq = pci_irqs[i];
+ /* Set to trigger level. */
+ elcr[irq >> 3] |= (1 << (irq & 7));
+ /* Activate irq remapping in PIIX3. */
+ devpciR3SetByte(pPIIX3, 0x60 + i, irq);
+ }
+
+ /* Tell to the PIC. */
+ VBOXSTRICTRC rcStrict = IOMIOPortWrite(pVM, pVCpu, 0x4d0, elcr[0], sizeof(uint8_t));
+ if (rcStrict == VINF_SUCCESS)
+ rcStrict = IOMIOPortWrite(pVM, pVCpu, 0x4d1, elcr[1], sizeof(uint8_t));
+ if (rcStrict != VINF_SUCCESS)
+ {
+ AssertMsgFailed(("Writing to PIC failed! rcStrict=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
+ return RT_SUCCESS(rcStrict) ? VERR_INTERNAL_ERROR : VBOXSTRICTRC_VAL(rcStrict);
+ }
+
+ /*
+ * Init the devices.
+ */
+ PDEVPCIBUS pBus = &pGlobals->PciBus;
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pPciDev = pBus->apDevices[uDevFn];
+ uint8_t aBridgePositions[256];
+
+ if (pPciDev)
+ {
+ memset(aBridgePositions, 0, sizeof(aBridgePositions));
+ Log2(("PCI: Initializing device %d (%#x)\n",
+ uDevFn, 0x80000000 | (uDevFn << 8)));
+ pci_bios_init_device(pGlobals, pBus, pPciDev, 0, aBridgePositions);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+#endif /* IN_RING3 */
+
+
+/* -=-=-=-=-=- I/O ports -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT, PCI address}
+ */
+PDMBOTHCBDECL(int) pciIOPortAddressWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ LogFunc(("Port=%#x u32=%#x cb=%d\n", Port, u32, cb));
+ RT_NOREF2(Port, pvUser);
+ if (cb == 4)
+ {
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_WRITE);
+ pThis->uConfigReg = u32 & ~3; /* Bits 0-1 are reserved and we silently clear them */
+ PCI_UNLOCK(pDevIns);
+ }
+ /* else: 440FX does "pass through to the bus" for other writes, what ever that means.
+ * Linux probes for cmd640 using byte writes/reads during ide init. We'll just ignore it. */
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN, PCI address}
+ */
+PDMBOTHCBDECL(int) pciIOPortAddressRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ RT_NOREF2(Port, pvUser);
+ if (cb == 4)
+ {
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_READ);
+ *pu32 = pThis->uConfigReg;
+ PCI_UNLOCK(pDevIns);
+ LogFunc(("Port=%#x cb=%d -> %#x\n", Port, cb, *pu32));
+ return VINF_SUCCESS;
+ }
+ /* else: 440FX does "pass through to the bus" for other writes, what ever that means.
+ * Linux probes for cmd640 using byte writes/reads during ide init. We'll just ignore it. */
+ LogFunc(("Port=%#x cb=%d VERR_IOM_IOPORT_UNUSED\n", Port, cb));
+ return VERR_IOM_IOPORT_UNUSED;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT, PCI data}
+ */
+PDMBOTHCBDECL(int) pciIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ LogFunc(("Port=%#x u32=%#x cb=%d\n", Port, u32, cb));
+ NOREF(pvUser);
+ int rc = VINF_SUCCESS;
+ if (!(Port % cb))
+ {
+ PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_WRITE);
+ rc = pci_data_write(PDMINS_2_DATA(pDevIns, PDEVPCIROOT), Port, u32, cb);
+ PCI_UNLOCK(pDevIns);
+ }
+ else
+ AssertMsgFailed(("Write to port %#x u32=%#x cb=%d\n", Port, u32, cb));
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN, PCI data}
+ */
+PDMBOTHCBDECL(int) pciIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ NOREF(pvUser);
+ if (!(Port % cb))
+ {
+ PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_READ);
+ int rc = pci_data_read(PDMINS_2_DATA(pDevIns, PDEVPCIROOT), Port, cb, pu32);
+ PCI_UNLOCK(pDevIns);
+ LogFunc(("Port=%#x cb=%#x -> %#x (%Rrc)\n", Port, cb, *pu32, rc));
+ return rc;
+ }
+ AssertMsgFailed(("Read from port %#x cb=%d\n", Port, cb));
+ return VERR_IOM_IOPORT_UNUSED;
+}
+
+#ifdef IN_RING3
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT, PCI data}
+ */
+DECLCALLBACK(int) pciR3IOPortMagicPCIWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ RT_NOREF2(pvUser, Port);
+ LogFunc(("Port=%#x u32=%#x cb=%d\n", Port, u32, cb));
+ if (cb == 4)
+ {
+ if (u32 == UINT32_C(19200509)) // Richard Adams
+ {
+ int rc = pciR3FakePCIBIOS(pDevIns);
+ AssertRC(rc);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN, PCI data}
+ */
+DECLCALLBACK(int) pciR3IOPortMagicPCIRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ RT_NOREF5(pDevIns, pvUser, Port, pu32, cb);
+ LogFunc(("Port=%#x cb=%d VERR_IOM_IOPORT_UNUSED\n", Port, cb));
+ return VERR_IOM_IOPORT_UNUSED;
+}
+
+
+/*
+ * Include code we share with the other PCI bus implementation.
+ *
+ * Note! No #ifdefs, use instant data booleans/flags/whatever. Goal is to
+ * completely merge these files! File #1 contains code we write, where
+ * as a possible file #2 contains external code if there's any left.
+ */
+# include "DevPciMerge1.cpp.h"
+
+
+/* -=-=-=-=-=- Saved state -=-=-=-=-=- */
+
+/**
+ * Common worker for pciR3SaveExec and pcibridgeR3SaveExec.
+ *
+ * @returns VBox status code.
+ * @param pBus The bus to save.
+ * @param pSSM The saved state handle.
+ */
+static int pciR3CommonSaveExec(PDEVPCIBUS pBus, PSSMHANDLE pSSM)
+{
+ /*
+ * Iterate thru all the devices.
+ */
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pDev = pBus->apDevices[uDevFn];
+ if (pDev)
+ {
+ SSMR3PutU32(pSSM, uDevFn);
+ SSMR3PutMem(pSSM, pDev->abConfig, sizeof(pDev->abConfig));
+
+ SSMR3PutS32(pSSM, pDev->Int.s.uIrqPinState);
+
+ /* Save the type an size of all the regions. */
+ for (uint32_t iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
+ {
+ SSMR3PutU8(pSSM, pDev->Int.s.aIORegions[iRegion].type);
+ SSMR3PutU64(pSSM, pDev->Int.s.aIORegions[iRegion].size);
+ }
+ }
+ }
+ return SSMR3PutU32(pSSM, UINT32_MAX); /* terminator */
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) pciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+
+ /*
+ * Bus state data.
+ */
+ SSMR3PutU32(pSSM, pThis->uConfigReg);
+ SSMR3PutBool(pSSM, pThis->fUseIoApic);
+
+ /*
+ * Save IRQ states.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->Piix3.auPciLegacyIrqLevels); i++)
+ SSMR3PutU32(pSSM, pThis->Piix3.auPciLegacyIrqLevels[i]);
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->auPciApicIrqLevels); i++)
+ SSMR3PutU32(pSSM, pThis->auPciApicIrqLevels[i]);
+
+ SSMR3PutU32(pSSM, pThis->Piix3.iAcpiIrqLevel);
+ SSMR3PutS32(pSSM, pThis->Piix3.iAcpiIrq);
+
+ SSMR3PutU32(pSSM, UINT32_MAX); /* separator */
+
+ /*
+ * Join paths with pcibridgeR3SaveExec.
+ */
+ return pciR3CommonSaveExec(&pThis->PciBus, pSSM);
+}
+
+
+/**
+ * Common worker for pciR3LoadExec and pcibridgeR3LoadExec.
+ *
+ * @returns VBox status code.
+ * @param pBus The bus which data is being loaded.
+ * @param pSSM The saved state handle.
+ * @param uVersion The data version.
+ * @param uPass The pass.
+ */
+static DECLCALLBACK(int) pciR3CommonLoadExec(PDEVPCIBUS pBus, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ uint32_t u32;
+ int rc;
+
+ Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
+
+ /*
+ * Iterate thru all the devices and write 0 to the COMMAND register so
+ * that all the memory is unmapped before we start restoring the saved
+ * mapping locations.
+ *
+ * The register value is restored afterwards so we can do proper
+ * LogRels in devpciR3CommonRestoreConfig.
+ */
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pDev = pBus->apDevices[uDevFn];
+ if (pDev)
+ {
+ uint16_t u16 = PCIDevGetCommand(pDev);
+ pDev->Int.s.pfnConfigWrite(pDev->Int.s.CTX_SUFF(pDevIns), pDev, VBOX_PCI_COMMAND, 0, 2);
+ PCIDevSetCommand(pDev, u16);
+ Assert(PCIDevGetCommand(pDev) == u16);
+ }
+ }
+
+ /*
+ * Iterate all the devices.
+ */
+ for (uint32_t uDevFn = 0;; uDevFn++)
+ {
+ /* index / terminator */
+ rc = SSMR3GetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (u32 == UINT32_MAX)
+ break;
+ if ( u32 >= RT_ELEMENTS(pBus->apDevices)
+ || u32 < uDevFn)
+ {
+ AssertMsgFailed(("u32=%#x uDevFn=%#x\n", u32, uDevFn));
+ return rc;
+ }
+
+ /* skip forward to the device checking that no new devices are present. */
+ for (; uDevFn < u32; uDevFn++)
+ {
+ if (pBus->apDevices[uDevFn])
+ {
+ LogRel(("PCI: New device in slot %#x, %s (vendor=%#06x device=%#06x)\n", uDevFn, pBus->apDevices[uDevFn]->pszNameR3,
+ PCIDevGetVendorId(pBus->apDevices[uDevFn]), PCIDevGetDeviceId(pBus->apDevices[uDevFn])));
+ if (SSMR3HandleGetAfter(pSSM) != SSMAFTER_DEBUG_IT)
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("New device in slot %#x, %s (vendor=%#06x device=%#06x)"),
+ uDevFn, pBus->apDevices[uDevFn]->pszNameR3, PCIDevGetVendorId(pBus->apDevices[uDevFn]), PCIDevGetDeviceId(pBus->apDevices[uDevFn]));
+ }
+ }
+
+ /* get the data */
+ PDMPCIDEV DevTmp;
+ RT_ZERO(DevTmp);
+ DevTmp.Int.s.uIrqPinState = ~0; /* Invalid value in case we have an older saved state to force a state change in pciSetIrq. */
+ SSMR3GetMem(pSSM, DevTmp.abConfig, sizeof(DevTmp.abConfig));
+ if (uVersion < VBOX_PCI_SAVED_STATE_VERSION_IRQ_STATES)
+ {
+ int32_t i32Temp;
+ /* Irq value not needed anymore. */
+ rc = SSMR3GetS32(pSSM, &i32Temp);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ else
+ {
+ rc = SSMR3GetS32(pSSM, &DevTmp.Int.s.uIrqPinState);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /* Load the region types and sizes. */
+ if (uVersion >= VBOX_PCI_SAVED_STATE_VERSION_REGION_SIZES)
+ {
+ for (uint32_t iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
+ {
+ SSMR3GetU8(pSSM, &DevTmp.Int.s.aIORegions[iRegion].type);
+ rc = SSMR3GetU64(pSSM, &DevTmp.Int.s.aIORegions[iRegion].size);
+ AssertLogRelRCReturn(rc, rc);
+ }
+ }
+
+ /* check that it's still around. */
+ PPDMPCIDEV pDev = pBus->apDevices[uDevFn];
+ if (!pDev)
+ {
+ LogRel(("PCI: Device in slot %#x has been removed! vendor=%#06x device=%#06x\n", uDevFn,
+ PCIDevGetVendorId(&DevTmp), PCIDevGetDeviceId(&DevTmp)));
+ if (SSMR3HandleGetAfter(pSSM) != SSMAFTER_DEBUG_IT)
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Device in slot %#x has been removed! vendor=%#06x device=%#06x"),
+ uDevFn, PCIDevGetVendorId(&DevTmp), PCIDevGetDeviceId(&DevTmp));
+ continue;
+ }
+
+ /* match the vendor id assuming that this will never be changed. */
+ if ( DevTmp.abConfig[0] != pDev->abConfig[0]
+ || DevTmp.abConfig[1] != pDev->abConfig[1])
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS,
+ N_("Device in slot %#x (%s) vendor id mismatch! saved=%.4Rhxs current=%.4Rhxs"),
+ uDevFn, pDev->pszNameR3, DevTmp.abConfig, pDev->abConfig);
+
+ /* commit the loaded device config. */
+ rc = devpciR3CommonRestoreRegions(pSSM, pDev, DevTmp.Int.s.aIORegions,
+ uVersion >= VBOX_PCI_SAVED_STATE_VERSION_REGION_SIZES);
+ if (RT_FAILURE(rc))
+ break;
+ devpciR3CommonRestoreConfig(pDev, &DevTmp.abConfig[0]);
+
+ pDev->Int.s.uIrqPinState = DevTmp.Int.s.uIrqPinState;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) pciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PDEVPCIBUS pBus = &pThis->PciBus;
+ uint32_t u32;
+ int rc;
+
+ /*
+ * Check the version.
+ */
+ if (uVersion > VBOX_PCI_SAVED_STATE_VERSION)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+ Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
+
+ /*
+ * Bus state data.
+ */
+ SSMR3GetU32(pSSM, &pThis->uConfigReg);
+ if (uVersion >= VBOX_PCI_SAVED_STATE_VERSION_USE_IO_APIC)
+ SSMR3GetBool(pSSM, &pThis->fUseIoApic);
+
+ /* Load IRQ states. */
+ if (uVersion >= VBOX_PCI_SAVED_STATE_VERSION_IRQ_STATES)
+ {
+ for (uint8_t i = 0; i < RT_ELEMENTS(pThis->Piix3.auPciLegacyIrqLevels); i++)
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->Piix3.auPciLegacyIrqLevels[i]);
+ for (uint8_t i = 0; i < RT_ELEMENTS(pThis->auPciApicIrqLevels); i++)
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->auPciApicIrqLevels[i]);
+
+ SSMR3GetU32(pSSM, &pThis->Piix3.iAcpiIrqLevel);
+ SSMR3GetS32(pSSM, &pThis->Piix3.iAcpiIrq);
+ }
+
+ /* separator */
+ rc = SSMR3GetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (u32 != UINT32_MAX)
+ AssertMsgFailedReturn(("u32=%#x\n", u32), rc);
+
+ /*
+ * The devices.
+ */
+ return pciR3CommonLoadExec(pBus, pSSM, uVersion, uPass);
+}
+
+
+/* -=-=-=-=-=- PCI Bus Interface Methods (PDMPCIBUSREG) -=-=-=-=-=- */
+
+
+/* -=-=-=-=-=- Debug Info Handlers -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) pciR3IrqRouteInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PDEVPCIROOT pGlobals = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PPDMPCIDEV pPIIX3 = &pGlobals->Piix3.PIIX3State.dev;
+ NOREF(pszArgs);
+
+ uint16_t router = pPIIX3->uDevFn;
+ pHlp->pfnPrintf(pHlp, "PCI interrupt router at: %02X:%02X:%X\n",
+ router >> 8, (router >> 3) & 0x1f, router & 0x7);
+
+ for (int i = 0; i < 4; ++i)
+ {
+ uint8_t irq_map = devpciR3GetByte(pPIIX3, 0x60 + i);
+ if (irq_map & 0x80)
+ pHlp->pfnPrintf(pHlp, "PIRQ%c disabled\n", 'A' + i);
+ else
+ pHlp->pfnPrintf(pHlp, "PIRQ%c -> IRQ%d\n", 'A' + i, irq_map & 0xf);
+ }
+}
+
+
+/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) pciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ RT_NOREF1(iInstance);
+ Assert(iInstance == 0);
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+ /*
+ * Validate and read configuration.
+ */
+ if (!CFGMR3AreValuesValid(pCfg, "IOAPIC\0" "GCEnabled\0" "R0Enabled\0"))
+ return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
+
+ /* query whether we got an IOAPIC */
+ bool fUseIoApic;
+ int rc = CFGMR3QueryBoolDef(pCfg, "IOAPIC", &fUseIoApic, false);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"IOAPIC\""));
+
+ /* check if RC code is enabled. */
+ bool fGCEnabled;
+ rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"GCEnabled\""));
+
+ /* check if R0 code is enabled. */
+ bool fR0Enabled;
+ rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"R0Enabled\""));
+ Log(("PCI: fUseIoApic=%RTbool fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fUseIoApic, fGCEnabled, fR0Enabled));
+
+ /*
+ * Init data and register the PCI bus.
+ */
+ PDEVPCIROOT pGlobals = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ pGlobals->uPciBiosIo = 0xc000;
+ pGlobals->uPciBiosMmio = 0xf0000000;
+ memset((void *)&pGlobals->Piix3.auPciLegacyIrqLevels, 0, sizeof(pGlobals->Piix3.auPciLegacyIrqLevels));
+ pGlobals->fUseIoApic = fUseIoApic;
+ memset((void *)&pGlobals->auPciApicIrqLevels, 0, sizeof(pGlobals->auPciApicIrqLevels));
+
+ pGlobals->pDevInsR3 = pDevIns;
+ pGlobals->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pGlobals->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+
+ pGlobals->PciBus.fTypePiix3 = true;
+ pGlobals->PciBus.fTypeIch9 = false;
+ pGlobals->PciBus.fPureBridge = false;
+ pGlobals->PciBus.pDevInsR3 = pDevIns;
+ pGlobals->PciBus.pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pGlobals->PciBus.pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pGlobals->PciBus.papBridgesR3 = (PPDMPCIDEV *)PDMDevHlpMMHeapAllocZ(pDevIns,
+ sizeof(PPDMPCIDEV)
+ * RT_ELEMENTS(pGlobals->PciBus.apDevices));
+ AssertLogRelReturn(pGlobals->PciBus.papBridgesR3, VERR_NO_MEMORY);
+
+
+ PDMPCIBUSREG PciBusReg;
+ PDEVPCIBUS pBus = &pGlobals->PciBus;
+ PciBusReg.u32Version = PDM_PCIBUSREG_VERSION;
+ PciBusReg.pfnRegisterR3 = pciR3MergedRegister;
+ PciBusReg.pfnRegisterMsiR3 = NULL;
+ PciBusReg.pfnIORegionRegisterR3 = devpciR3CommonIORegionRegister;
+ PciBusReg.pfnSetConfigCallbacksR3 = devpciR3CommonSetConfigCallbacks;
+ PciBusReg.pfnSetIrqR3 = pciSetIrq;
+ PciBusReg.pszSetIrqRC = fGCEnabled ? "pciSetIrq" : NULL;
+ PciBusReg.pszSetIrqR0 = fR0Enabled ? "pciSetIrq" : NULL;
+ rc = PDMDevHlpPCIBusRegister(pDevIns, &PciBusReg, &pBus->pPciHlpR3, &pBus->iBus);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Failed to register ourselves as a PCI Bus"));
+ Assert(pBus->iBus == 0);
+ if (pBus->pPciHlpR3->u32Version != PDM_PCIHLPR3_VERSION)
+ return PDMDevHlpVMSetError(pDevIns, VERR_VERSION_MISMATCH, RT_SRC_POS,
+ N_("PCI helper version mismatch; got %#x expected %#x"),
+ pBus->pPciHlpR3->u32Version, PDM_PCIHLPR3_VERSION);
+
+ pBus->pPciHlpRC = pBus->pPciHlpR3->pfnGetRCHelpers(pDevIns);
+ pBus->pPciHlpR0 = pBus->pPciHlpR3->pfnGetR0Helpers(pDevIns);
+
+ /* Disable default device locking. */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Fill in PCI configs and add them to the bus.
+ */
+ /* i440FX */
+ PCIDevSetVendorId( &pBus->PciDev, 0x8086); /* Intel */
+ PCIDevSetDeviceId( &pBus->PciDev, 0x1237);
+ PCIDevSetRevisionId(&pBus->PciDev, 0x02);
+ PCIDevSetClassSub( &pBus->PciDev, 0x00); /* host2pci */
+ PCIDevSetClassBase( &pBus->PciDev, 0x06); /* PCI_bridge */
+ PCIDevSetHeaderType(&pBus->PciDev, 0x00);
+ rc = PDMDevHlpPCIRegisterEx(pDevIns, &pBus->PciDev, PDMPCIDEVREG_CFG_PRIMARY, 0 /*fFlags*/,
+ 0 /*uPciDevNo*/, 0 /*uPciFunNo*/, "i440FX");
+ AssertLogRelRCReturn(rc, rc);
+
+ /* PIIX3 */
+ PCIDevSetVendorId( &pGlobals->Piix3.PIIX3State.dev, 0x8086); /* Intel */
+ PCIDevSetDeviceId( &pGlobals->Piix3.PIIX3State.dev, 0x7000); /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */
+ PCIDevSetClassSub( &pGlobals->Piix3.PIIX3State.dev, 0x01); /* PCI_ISA */
+ PCIDevSetClassBase( &pGlobals->Piix3.PIIX3State.dev, 0x06); /* PCI_bridge */
+ PCIDevSetHeaderType(&pGlobals->Piix3.PIIX3State.dev, 0x80); /* PCI_multifunction, generic */
+ rc = PDMDevHlpPCIRegisterEx(pDevIns, &pGlobals->Piix3.PIIX3State.dev, PDMPCIDEVREG_CFG_NEXT, 0 /*fFlags*/,
+ 1 /*uPciDevNo*/, 0 /*uPciFunNo*/, "PIIX3");
+ AssertLogRelRCReturn(rc, rc);
+ pciR3Piix3Reset(&pGlobals->Piix3.PIIX3State);
+
+ pBus->iDevSearch = 16;
+
+ /*
+ * Register I/O ports and save state.
+ */
+ rc = PDMDevHlpIOPortRegister(pDevIns, 0x0cf8, 1, NULL, pciIOPortAddressWrite, pciIOPortAddressRead, NULL, NULL, "i440FX (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIOPortRegister(pDevIns, 0x0cfc, 4, NULL, pciIOPortDataWrite, pciIOPortDataRead, NULL, NULL, "i440FX (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ if (fGCEnabled)
+ {
+ rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x0cf8, 1, NIL_RTGCPTR, "pciIOPortAddressWrite", "pciIOPortAddressRead", NULL, NULL, "i440FX (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x0cfc, 4, NIL_RTGCPTR, "pciIOPortDataWrite", "pciIOPortDataRead", NULL, NULL, "i440FX (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ if (fR0Enabled)
+ {
+ rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x0cf8, 1, NIL_RTR0PTR, "pciIOPortAddressWrite", "pciIOPortAddressRead", NULL, NULL, "i440FX (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x0cfc, 4, NIL_RTR0PTR, "pciIOPortDataWrite", "pciIOPortDataRead", NULL, NULL, "i440FX (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ rc = PDMDevHlpIOPortRegister(pDevIns, 0x0410, 1, NULL, pciR3IOPortMagicPCIWrite, pciR3IOPortMagicPCIRead, NULL, NULL, "i440FX (Fake PCI BIOS trigger)")
+;
+ if (RT_FAILURE(rc))
+ return rc;
+
+
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, VBOX_PCI_SAVED_STATE_VERSION, sizeof(*pBus) + 16*128, "pgm",
+ NULL, NULL, NULL,
+ NULL, pciR3SaveExec, NULL,
+ NULL, pciR3LoadExec, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ PDMDevHlpDBGFInfoRegister(pDevIns, "pci",
+ "Display PCI bus status. Recognizes 'basic' or 'verbose' as arguments, defaults to 'basic'.",
+ devpciR3InfoPci);
+ PDMDevHlpDBGFInfoRegister(pDevIns, "pciirq", "Display PCI IRQ state. (no arguments)", devpciR3InfoPciIrq);
+ PDMDevHlpDBGFInfoRegister(pDevIns, "irqroute", "Display PCI IRQ routing. (no arguments)", pciR3IrqRouteInfo);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) pciR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDEVPCIROOT pGlobals = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ if (pGlobals->PciBus.papBridgesR3)
+ {
+ PDMDevHlpMMHeapFree(pDevIns, pGlobals->PciBus.papBridgesR3);
+ pGlobals->PciBus.papBridgesR3 = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) pciR3Reset(PPDMDEVINS pDevIns)
+{
+ PDEVPCIROOT pGlobals = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PDEVPCIBUS pBus = &pGlobals->PciBus;
+
+ /* PCI-specific reset for each device. */
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ if (pBus->apDevices[uDevFn])
+ devpciR3ResetDevice(pBus->apDevices[uDevFn]);
+ }
+
+ pciR3Piix3Reset(&pGlobals->Piix3.PIIX3State);
+}
+
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DevicePCI =
+{
+ /* u32Version */
+ PDM_DEVREG_VERSION,
+ /* szName */
+ "pci",
+ /* szRCMod */
+ "VBoxDDRC.rc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "i440FX PCI bridge and PIIX3 ISA bridge.",
+ /* fFlags */
+ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
+ /* fClass */
+ PDM_DEVREG_CLASS_BUS_PCI | PDM_DEVREG_CLASS_BUS_ISA,
+ /* cMaxInstances */
+ 1,
+ /* cbInstance */
+ sizeof(DEVPCIROOT),
+ /* pfnConstruct */
+ pciR3Construct,
+ /* pfnDestruct */
+ pciR3Destruct,
+ /* pfnRelocate */
+ devpciR3RootRelocate,
+ /* pfnMemSetup */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ pciR3Reset,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnQueryInterface */
+ NULL,
+ /* pfnInitComplete */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DEVREG_VERSION
+
+};
+#endif /* IN_RING3 */
+
+
+
+/* -=-=-=-=-=- The PCI bridge specific bits -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMPCIBUSREG,pfnSetIrqR3}
+ */
+PDMBOTHCBDECL(void) pcibridgeSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTagSrc)
+{
+ /*
+ * The PCI-to-PCI bridge specification defines how the interrupt pins
+ * are routed from the secondary to the primary bus (see chapter 9).
+ * iIrq gives the interrupt pin the pci device asserted.
+ * We change iIrq here according to the spec and call the SetIrq function
+ * of our parent passing the device which asserted the interrupt instead of the device of the bridge.
+ */
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ PPDMPCIDEV pPciDevBus = pPciDev;
+ int iIrqPinBridge = iIrq;
+ uint8_t uDevFnBridge = 0;
+
+ /* Walk the chain until we reach the host bus. */
+ do
+ {
+ uDevFnBridge = pBus->PciDev.uDevFn;
+ iIrqPinBridge = ((pPciDevBus->uDevFn >> 3) + iIrqPinBridge) & 3;
+
+ /* Get the parent. */
+ pBus = pBus->PciDev.Int.s.CTX_SUFF(pBus);
+ pPciDevBus = &pBus->PciDev;
+ } while (pBus->iBus != 0);
+
+ AssertMsg(pBus->iBus == 0, ("This is not the host pci bus iBus=%d\n", pBus->iBus));
+ pciSetIrqInternal(DEVPCIBUS_2_DEVPCIROOT(pBus), uDevFnBridge, pPciDev, iIrqPinBridge, iLevel, uTagSrc);
+}
+
+#ifdef IN_RING3
+
+/**
+ * @callback_method_impl{FNPCIBRIDGECONFIGWRITE}
+ */
+static DECLCALLBACK(void) pcibridgeR3ConfigWrite(PPDMDEVINSR3 pDevIns, uint8_t iBus, uint8_t iDevice, uint32_t u32Address, uint32_t u32Value, unsigned cb)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+
+ LogFlowFunc(("pDevIns=%p iBus=%d iDevice=%d u32Address=%u u32Value=%u cb=%d\n", pDevIns, iBus, iDevice, u32Address, u32Value, cb));
+
+ /* If the current bus is not the target bus search for the bus which contains the device. */
+ if (iBus != pBus->PciDev.abConfig[VBOX_PCI_SECONDARY_BUS])
+ {
+ PPDMPCIDEV pBridgeDevice = pciR3FindBridge(pBus, iBus);
+ if (pBridgeDevice)
+ {
+ AssertPtr(pBridgeDevice->Int.s.pfnBridgeConfigWrite);
+ pBridgeDevice->Int.s.pfnBridgeConfigWrite(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), iBus, iDevice, u32Address, u32Value, cb);
+ }
+ }
+ else
+ {
+ /* This is the target bus, pass the write to the device. */
+ PPDMPCIDEV pPciDev = pBus->apDevices[iDevice];
+ if (pPciDev)
+ {
+ LogFunc(("%s: addr=%02x val=%08x len=%d\n", pPciDev->pszNameR3, u32Address, u32Value, cb));
+ /** @todo return rc */
+ pPciDev->Int.s.pfnConfigWrite(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev, u32Address, u32Value, cb);
+ }
+ }
+}
+
+
+/**
+ * @callback_method_impl{FNPCIBRIDGECONFIGREAD}
+ */
+static DECLCALLBACK(uint32_t) pcibridgeR3ConfigRead(PPDMDEVINSR3 pDevIns, uint8_t iBus, uint8_t iDevice, uint32_t u32Address, unsigned cb)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ uint32_t u32Value = 0xffffffff; /* Return value in case there is no device. */
+
+ LogFlowFunc(("pDevIns=%p iBus=%d iDevice=%d u32Address=%u cb=%d\n", pDevIns, iBus, iDevice, u32Address, cb));
+
+ /* If the current bus is not the target bus search for the bus which contains the device. */
+ if (iBus != pBus->PciDev.abConfig[VBOX_PCI_SECONDARY_BUS])
+ {
+ PPDMPCIDEV pBridgeDevice = pciR3FindBridge(pBus, iBus);
+ if (pBridgeDevice)
+ {
+ AssertPtr( pBridgeDevice->Int.s.pfnBridgeConfigRead);
+ u32Value = pBridgeDevice->Int.s.pfnBridgeConfigRead(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), iBus, iDevice, u32Address, cb);
+ }
+ }
+ else
+ {
+ /* This is the target bus, pass the read to the device. */
+ PPDMPCIDEV pPciDev = pBus->apDevices[iDevice];
+ if (pPciDev)
+ {
+ u32Value = pPciDev->Int.s.pfnConfigRead(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev, u32Address, cb);
+ LogFunc(("%s: u32Address=%02x u32Value=%08x cb=%d\n", pPciDev->pszNameR3, u32Address, u32Value, cb));
+ }
+ }
+
+ return u32Value;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) pcibridgeR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDEVPCIBUS pThis = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ return pciR3CommonSaveExec(pThis, pSSM);
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) pcibridgeR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDEVPCIBUS pThis = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ if (uVersion > VBOX_PCI_SAVED_STATE_VERSION)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+ return pciR3CommonLoadExec(pThis, pSSM, uVersion, uPass);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) pcibridgeR3Reset(PPDMDEVINS pDevIns)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+
+ /* Reset config space to default values. */
+ pBus->PciDev.abConfig[VBOX_PCI_PRIMARY_BUS] = 0;
+ pBus->PciDev.abConfig[VBOX_PCI_SECONDARY_BUS] = 0;
+ pBus->PciDev.abConfig[VBOX_PCI_SUBORDINATE_BUS] = 0;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) pcibridgeR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ RT_NOREF(iInstance);
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+ /*
+ * Validate and read configuration.
+ */
+ if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0" "R0Enabled\0"))
+ return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
+
+ /* check if RC code is enabled. */
+ bool fGCEnabled;
+ int rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"GCEnabled\""));
+
+ /* check if R0 code is enabled. */
+ bool fR0Enabled;
+ rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"R0Enabled\""));
+ Log(("PCI: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
+
+ /*
+ * Init data and register the PCI bus.
+ */
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ pBus->fTypePiix3 = true;
+ pBus->fTypeIch9 = false;
+ pBus->fPureBridge = true;
+ pBus->pDevInsR3 = pDevIns;
+ pBus->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pBus->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pBus->papBridgesR3 = (PPDMPCIDEV *)PDMDevHlpMMHeapAllocZ(pDevIns, sizeof(PPDMPCIDEV) * RT_ELEMENTS(pBus->apDevices));
+ AssertLogRelReturn(pBus->papBridgesR3, VERR_NO_MEMORY);
+
+ PDMPCIBUSREG PciBusReg;
+ PciBusReg.u32Version = PDM_PCIBUSREG_VERSION;
+ PciBusReg.pfnRegisterR3 = pcibridgeR3MergedRegisterDevice;
+ PciBusReg.pfnRegisterMsiR3 = NULL;
+ PciBusReg.pfnIORegionRegisterR3 = devpciR3CommonIORegionRegister;
+ PciBusReg.pfnSetConfigCallbacksR3 = devpciR3CommonSetConfigCallbacks;
+ PciBusReg.pfnSetIrqR3 = pcibridgeSetIrq;
+ PciBusReg.pszSetIrqRC = fGCEnabled ? "pcibridgeSetIrq" : NULL;
+ PciBusReg.pszSetIrqR0 = fR0Enabled ? "pcibridgeSetIrq" : NULL;
+ rc = PDMDevHlpPCIBusRegister(pDevIns, &PciBusReg, &pBus->pPciHlpR3, &pBus->iBus);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Failed to register ourselves as a PCI Bus"));
+ Assert(pBus->iBus == (uint32_t)iInstance + 1); /* Can be removed when adding support for multiple bridge implementations. */
+ if (pBus->pPciHlpR3->u32Version != PDM_PCIHLPR3_VERSION)
+ return PDMDevHlpVMSetError(pDevIns, VERR_VERSION_MISMATCH, RT_SRC_POS,
+ N_("PCI helper version mismatch; got %#x expected %#x"),
+ pBus->pPciHlpR3->u32Version, PDM_PCIHLPR3_VERSION);
+
+ pBus->pPciHlpRC = pBus->pPciHlpR3->pfnGetRCHelpers(pDevIns);
+ pBus->pPciHlpR0 = pBus->pPciHlpR3->pfnGetR0Helpers(pDevIns);
+
+ /*
+ * Fill in PCI configs and add them to the bus.
+ */
+ PCIDevSetVendorId( &pBus->PciDev, 0x8086); /* Intel */
+ PCIDevSetDeviceId( &pBus->PciDev, 0x2448); /* 82801 Mobile PCI bridge. */
+ PCIDevSetRevisionId(&pBus->PciDev, 0xf2);
+ PCIDevSetClassSub( &pBus->PciDev, 0x04); /* pci2pci */
+ PCIDevSetClassBase( &pBus->PciDev, 0x06); /* PCI_bridge */
+ PCIDevSetClassProg( &pBus->PciDev, 0x01); /* Supports subtractive decoding. */
+ PCIDevSetHeaderType(&pBus->PciDev, 0x01); /* Single function device which adheres to the PCI-to-PCI bridge spec. */
+ PCIDevSetCommand( &pBus->PciDev, 0x0000);
+ PCIDevSetStatus( &pBus->PciDev, 0x0020); /* 66MHz Capable. */
+ PCIDevSetInterruptLine(&pBus->PciDev, 0x00); /* This device does not assert interrupts. */
+
+ /*
+ * This device does not generate interrupts. Interrupt delivery from
+ * devices attached to the bus is unaffected.
+ */
+ PCIDevSetInterruptPin(&pBus->PciDev, 0x00);
+
+ /*
+ * Register this PCI bridge. The called function will take care on which bus we will get registered.
+ */
+ rc = PDMDevHlpPCIRegisterEx(pDevIns, &pBus->PciDev, PDMPCIDEVREG_CFG_PRIMARY, PDMPCIDEVREG_F_PCI_BRIDGE,
+ PDMPCIDEVREG_DEV_NO_FIRST_UNUSED, PDMPCIDEVREG_FUN_NO_FIRST_UNUSED, "pcibridge");
+ if (RT_FAILURE(rc))
+ return rc;
+ pBus->PciDev.Int.s.pfnBridgeConfigRead = pcibridgeR3ConfigRead;
+ pBus->PciDev.Int.s.pfnBridgeConfigWrite = pcibridgeR3ConfigWrite;
+
+ pBus->iDevSearch = 0;
+
+ /*
+ * Register SSM handlers. We use the same saved state version as for the host bridge
+ * to make changes easier.
+ */
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, VBOX_PCI_SAVED_STATE_VERSION, sizeof(*pBus) + 16*128, "pgm",
+ NULL, NULL, NULL,
+ NULL, pcibridgeR3SaveExec, NULL,
+ NULL, pcibridgeR3LoadExec, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) pcibridgeR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ if (pBus->papBridgesR3)
+ {
+ PDMDevHlpMMHeapFree(pDevIns, pBus->papBridgesR3);
+ pBus->papBridgesR3 = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * The device registration structure
+ * for the PCI-to-PCI bridge.
+ */
+const PDMDEVREG g_DevicePCIBridge =
+{
+ /* u32Version */
+ PDM_DEVREG_VERSION,
+ /* szName */
+ "pcibridge",
+ /* szRCMod */
+ "VBoxDDRC.rc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "82801 Mobile PCI to PCI bridge",
+ /* fFlags */
+ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
+ /* fClass */
+ PDM_DEVREG_CLASS_BUS_PCI,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DEVPCIBUS),
+ /* pfnConstruct */
+ pcibridgeR3Construct,
+ /* pfnDestruct */
+ pcibridgeR3Destruct,
+ /* pfnRelocate */
+ devpciR3BusRelocate,
+ /* pfnMemSetup */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ pcibridgeR3Reset,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnQueryInterface */
+ NULL,
+ /* pfnInitComplete */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DEVREG_VERSION
+};
+
+#endif /* IN_RING3 */
+
diff --git a/src/VBox/Devices/Bus/DevPciIch9.cpp b/src/VBox/Devices/Bus/DevPciIch9.cpp
new file mode 100644
index 00000000..835238df
--- /dev/null
+++ b/src/VBox/Devices/Bus/DevPciIch9.cpp
@@ -0,0 +1,3684 @@
+/* $Id: DevPciIch9.cpp $ */
+/** @file
+ * DevPCI - ICH9 southbridge PCI bus emulation device.
+ *
+ * @remarks We'll be slowly promoting the code in this file to common PCI bus
+ * code. Function without 'static' and using 'devpci' as prefix is
+ * also used by DevPCI.cpp and have a prototype in DevPciInternal.h.
+ *
+ * For the time being the DevPciMerge1.cpp.h file will remain separate,
+ * due to 5.1. We can merge it into this one later in the dev cycle.
+ *
+ * DO NOT use the PDMPciDev* or PCIDev* family of functions in this
+ * file except in the two callbacks for config space access (and the
+ * functions which are used exclusively by that code) and the two
+ * device constructors when setting up the config space for the
+ * bridges. Everything else need extremely careful review. Using
+ * them elsewhere (especially in the init code) causes weird failures
+ * with PCI passthrough, as it would only update the array of
+ * (emulated) config space, but not talk to the actual device (needs
+ * invoking the respective callback).
+ */
+
+/*
+ * Copyright (C) 2010-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_PCI
+#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
+#include <VBox/vmm/pdmpcidev.h>
+
+#include <VBox/msi.h>
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/mm.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#ifdef IN_RING3
+# include <iprt/mem.h>
+# include <iprt/uuid.h>
+#endif
+
+#include "PciInline.h"
+#include "VBoxDD.h"
+#include "MsiCommon.h"
+#include "DevPciInternal.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * PCI configuration space address.
+ */
+typedef struct
+{
+ uint8_t iBus;
+ uint8_t iDeviceFunc;
+ uint16_t iRegister;
+} PciAddress;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Saved state version of the ICH9 PCI bus device. */
+#define VBOX_ICH9PCI_SAVED_STATE_VERSION VBOX_ICH9PCI_SAVED_STATE_VERSION_REGION_SIZES
+/** Adds I/O region types and sizes for dealing changes in resource regions. */
+#define VBOX_ICH9PCI_SAVED_STATE_VERSION_REGION_SIZES 3
+/** This appears to be the first state we need to care about. */
+#define VBOX_ICH9PCI_SAVED_STATE_VERSION_MSI 2
+/** This is apparently not supported or has a grossly incomplete state, juding
+ * from hints in the code. */
+#define VBOX_ICH9PCI_SAVED_STATE_VERSION_NOMSI 1
+
+/** Invalid PCI region mapping address. */
+#define INVALID_PCI_ADDRESS UINT32_MAX
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+/* Prototypes */
+static void ich9pciSetIrqInternal(PDEVPCIROOT pPciRoot, uint8_t uDevFn, PPDMPCIDEV pPciDev,
+ int iIrq, int iLevel, uint32_t uTagSrc);
+#ifdef IN_RING3
+static int ich9pciFakePCIBIOS(PPDMDEVINS pDevIns);
+DECLINLINE(PPDMPCIDEV) ich9pciFindBridge(PDEVPCIBUS pBus, uint8_t uBus);
+static void ich9pciBiosInitAllDevicesOnBus(PDEVPCIROOT pPciRoot, PDEVPCIBUS pBus);
+static bool ich9pciBiosInitAllDevicesPrefetchableOnBus(PDEVPCIROOT pPciRoot, PDEVPCIBUS pBus, bool fUse64Bit, bool fDryrun);
+#endif
+
+
+// See 7.2.2. PCI Express Enhanced Configuration Mechanism for details of address
+// mapping, we take n=6 approach
+DECLINLINE(void) ich9pciPhysToPciAddr(PDEVPCIROOT pPciRoot, RTGCPHYS GCPhysAddr, PciAddress* pPciAddr)
+{
+ NOREF(pPciRoot);
+ pPciAddr->iBus = (GCPhysAddr >> 20) & ((1<<6) - 1);
+ pPciAddr->iDeviceFunc = (GCPhysAddr >> 12) & ((1<<(5+3)) - 1); // 5 bits - device, 3 bits - function
+ pPciAddr->iRegister = (GCPhysAddr >> 0) & ((1<<(6+4+2)) - 1); // 6 bits - register, 4 bits - extended register, 2 bits -Byte Enable
+ RT_UNTRUSTED_VALIDATED_FENCE(); /* paranoia */
+}
+
+DECLINLINE(void) ich9pciStateToPciAddr(PDEVPCIROOT pPciRoot, RTGCPHYS addr, PciAddress* pPciAddr)
+{
+ pPciAddr->iBus = (pPciRoot->uConfigReg >> 16) & 0xff;
+ pPciAddr->iDeviceFunc = (pPciRoot->uConfigReg >> 8) & 0xff;
+ pPciAddr->iRegister = (pPciRoot->uConfigReg & 0xfc) | (addr & 3);
+ RT_UNTRUSTED_VALIDATED_FENCE(); /* paranoia */
+}
+
+PDMBOTHCBDECL(void) ich9pciSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTagSrc)
+{
+ LogFlowFunc(("invoked by %p/%d: iIrq=%d iLevel=%d uTagSrc=%#x\n", pDevIns, pDevIns->iInstance, iIrq, iLevel, uTagSrc));
+ ich9pciSetIrqInternal(PDMINS_2_DATA(pDevIns, PDEVPCIROOT), pPciDev->uDevFn, pPciDev, iIrq, iLevel, uTagSrc);
+}
+
+PDMBOTHCBDECL(void) ich9pcibridgeSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTagSrc)
+{
+ /*
+ * The PCI-to-PCI bridge specification defines how the interrupt pins
+ * are routed from the secondary to the primary bus (see chapter 9).
+ * iIrq gives the interrupt pin the pci device asserted.
+ * We change iIrq here according to the spec and call the SetIrq function
+ * of our parent passing the device which asserted the interrupt instead of the device of the bridge.
+ */
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ PPDMPCIDEV pPciDevBus = pPciDev;
+ int iIrqPinBridge = iIrq;
+ uint8_t uDevFnBridge = 0;
+
+ /* Walk the chain until we reach the host bus. */
+ do
+ {
+ uDevFnBridge = pBus->PciDev.uDevFn;
+ iIrqPinBridge = ((pPciDevBus->uDevFn >> 3) + iIrqPinBridge) & 3;
+
+ /* Get the parent. */
+ pBus = pBus->PciDev.Int.s.CTX_SUFF(pBus);
+ pPciDevBus = &pBus->PciDev;
+ } while (pBus->iBus != 0);
+
+ AssertMsgReturnVoid(pBus->iBus == 0, ("This is not the host pci bus iBus=%d\n", pBus->iBus));
+
+ /*
+ * For MSI/MSI-X enabled devices the iIrq doesn't denote the pin but rather a vector which is completely
+ * orthogonal to the pin based approach. The vector is not subject to the pin based routing with PCI bridges.
+ */
+ int iIrqPinVector = iIrqPinBridge;
+ if ( MsiIsEnabled(pPciDev)
+ || MsixIsEnabled(pPciDev))
+ iIrqPinVector = iIrq;
+ ich9pciSetIrqInternal(DEVPCIBUS_2_DEVPCIROOT(pBus), uDevFnBridge, pPciDev, iIrqPinVector, iLevel, uTagSrc);
+}
+
+
+#ifdef IN_RING3
+
+/**
+ * Port I/O Handler for Fake PCI BIOS trigger OUT operations at 0410h
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns ICH9 device instance.
+ * @param pvUser User argument - ignored.
+ * @param uPort Port number used for the OUT operation.
+ * @param u32 The value to output.
+ * @param cb The value size in bytes.
+ */
+DECLCALLBACK(int) ich9pciR3IOPortMagicPCIWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb)
+{
+ RT_NOREF2(pvUser, uPort);
+ LogFlowFunc(("Port=%#x u32=%#x cb=%d\n", uPort, u32, cb));
+ if (cb == 4)
+ {
+ if (u32 == UINT32_C(19200509)) // Richard Adams
+ {
+ int rc = ich9pciFakePCIBIOS(pDevIns);
+ AssertRC(rc);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Port I/O Handler for Fake PCI BIOS trigger IN operations at 0410h
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns ICH9 device instance.
+ * @param pvUser User argument - ignored.
+ * @param uPort Port number used for the IN operation.
+ * @param pu32 Where to store the result.
+ * @param cb Number of bytes read.
+ */
+DECLCALLBACK(int) ich9pciR3IOPortMagicPCIRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb)
+{
+ RT_NOREF5(pDevIns, pvUser, uPort, pu32, cb);
+ LogFunc(("Port=%#x cb=%d VERR_IOM_IOPORT_UNUSED\n", uPort, cb));
+ return VERR_IOM_IOPORT_UNUSED;
+}
+
+#endif /* IN_RING3 */
+
+
+/**
+ * Port I/O Handler for PCI address OUT operations.
+ *
+ * Emulates writes to Configuration Address Port at 0CF8h for
+ * Configuration Mechanism #1.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns ICH9 device instance.
+ * @param pvUser User argument - ignored.
+ * @param uPort Port number used for the OUT operation.
+ * @param u32 The value to output.
+ * @param cb The value size in bytes.
+ */
+PDMBOTHCBDECL(int) ich9pciIOPortAddressWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb)
+{
+ LogFlowFunc(("Port=%#x u32=%#x cb=%d\n", uPort, u32, cb));
+ RT_NOREF2(uPort, pvUser);
+ if (cb == 4)
+ {
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+
+ /*
+ * bits [1:0] are hard-wired, read-only and must return zeroes
+ * when read.
+ */
+ u32 &= ~3;
+
+ PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_WRITE);
+ pThis->uConfigReg = u32;
+ PCI_UNLOCK(pDevIns);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Port I/O Handler for PCI address IN operations.
+ *
+ * Emulates reads from Configuration Address Port at 0CF8h for
+ * Configuration Mechanism #1.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns ICH9 device instance.
+ * @param pvUser User argument - ignored.
+ * @param uPort Port number used for the IN operation.
+ * @param pu32 Where to store the result.
+ * @param cb Number of bytes read.
+ */
+PDMBOTHCBDECL(int) ich9pciIOPortAddressRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb)
+{
+ RT_NOREF2(uPort, pvUser);
+ if (cb == 4)
+ {
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+
+ PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_READ);
+ *pu32 = pThis->uConfigReg;
+ PCI_UNLOCK(pDevIns);
+
+ LogFlowFunc(("Port=%#x cb=%d -> %#x\n", uPort, cb, *pu32));
+ return VINF_SUCCESS;
+ }
+
+ LogFunc(("Port=%#x cb=%d VERR_IOM_IOPORT_UNUSED\n", uPort, cb));
+ return VERR_IOM_IOPORT_UNUSED;
+}
+
+
+/*
+ * Perform configuration space write.
+ */
+static int ich9pciConfigWrite(PDEVPCIROOT pPciRoot, PciAddress* pAddr,
+ uint32_t val, int cb, int rcReschedule)
+{
+ int rc = VINF_SUCCESS;
+#ifdef IN_RING3
+ NOREF(rcReschedule);
+#else
+ RT_NOREF2(val, cb);
+#endif
+
+ if (pAddr->iBus != 0) /* forward to subordinate bus */
+ {
+ if (pPciRoot->PciBus.cBridges)
+ {
+#ifdef IN_RING3 /** @todo do lookup in R0/RC too! r=klaus don't think that it can work, since the config space access callback only works in R3 */
+ PPDMPCIDEV pBridgeDevice = ich9pciFindBridge(&pPciRoot->PciBus, pAddr->iBus);
+ if (pBridgeDevice)
+ {
+ AssertPtr(pBridgeDevice->Int.s.pfnBridgeConfigWrite);
+ pBridgeDevice->Int.s.pfnBridgeConfigWrite(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), pAddr->iBus, pAddr->iDeviceFunc,
+ pAddr->iRegister, val, cb);
+ }
+#else
+ rc = rcReschedule;
+#endif
+ }
+ }
+ else /* forward to directly connected device */
+ {
+ R3PTRTYPE(PDMPCIDEV *) pPciDev = pPciRoot->PciBus.apDevices[pAddr->iDeviceFunc];
+ if (pPciDev)
+ {
+#ifdef IN_RING3
+ rc = VBOXSTRICTRC_TODO(pPciDev->Int.s.pfnConfigWrite(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev,
+ pAddr->iRegister, val, cb));
+#else
+ rc = rcReschedule;
+#endif
+ }
+ }
+
+ Log2Func(("%02x:%02x.%d reg %x(%d) %x %Rrc\n",
+ pAddr->iBus, pAddr->iDeviceFunc >> 3, pAddr->iDeviceFunc & 0x7, pAddr->iRegister, cb, val, rc));
+ return rc;
+}
+
+
+/**
+ * Port I/O Handler for PCI data OUT operations.
+ *
+ * Emulates writes to Configuration Data Port at 0CFCh for
+ * Configuration Mechanism #1.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns ICH9 device instance.
+ * @param pvUser User argument - ignored.
+ * @param uPort Port number used for the OUT operation.
+ * @param u32 The value to output.
+ * @param cb The value size in bytes.
+ */
+PDMBOTHCBDECL(int) ich9pciIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb)
+{
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ LogFlowFunc(("Port=%#x u32=%#x cb=%d (config=%#10x)\n", uPort, u32, cb, pThis->uConfigReg));
+ NOREF(pvUser);
+ int rc = VINF_SUCCESS;
+ if (!(uPort % cb))
+ {
+ PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_WRITE);
+
+ do
+ {
+ /* Configuration space mapping enabled? */
+ if (!(pThis->uConfigReg & (1 << 31)))
+ break;
+
+ /* Decode target device from Configuration Address Port */
+ PciAddress aPciAddr;
+ ich9pciStateToPciAddr(pThis, uPort, &aPciAddr);
+
+ /* Perform configuration space write */
+ rc = ich9pciConfigWrite(pThis, &aPciAddr, u32, cb, VINF_IOM_R3_IOPORT_WRITE);
+ } while (0);
+
+ PCI_UNLOCK(pDevIns);
+ }
+ else
+ AssertMsgFailed(("Unaligned write to port %#x u32=%#x cb=%d\n", uPort, u32, cb));
+ return rc;
+}
+
+
+/*
+ * Perform configuration space read.
+ */
+static int ich9pciConfigRead(PDEVPCIROOT pPciRoot, PciAddress* pPciAddr, int cb,
+ uint32_t *pu32, int rcReschedule)
+{
+ int rc = VINF_SUCCESS;
+#ifdef IN_RING3
+ NOREF(rcReschedule);
+#else
+ NOREF(cb);
+#endif
+
+ if (pPciAddr->iBus != 0) /* forward to subordinate bus */
+ {
+ if (pPciRoot->PciBus.cBridges)
+ {
+#ifdef IN_RING3 /** @todo do lookup in R0/RC too! r=klaus don't think that it can work, since the config space access callback only works in R3 */
+ PPDMPCIDEV pBridgeDevice = ich9pciFindBridge(&pPciRoot->PciBus, pPciAddr->iBus);
+ if (pBridgeDevice)
+ {
+ AssertPtr(pBridgeDevice->Int.s.pfnBridgeConfigRead);
+ *pu32 = pBridgeDevice->Int.s.pfnBridgeConfigRead(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), pPciAddr->iBus,
+ pPciAddr->iDeviceFunc, pPciAddr->iRegister, cb);
+ }
+ else
+ *pu32 = 0xffffffff;
+#else
+ rc = rcReschedule;
+#endif
+ }
+ else
+ *pu32 = 0xffffffff;
+ }
+ else /* forward to directly connected device */
+ {
+ R3PTRTYPE(PDMPCIDEV *) pPciDev = pPciRoot->PciBus.apDevices[pPciAddr->iDeviceFunc];
+ if (pPciDev)
+ {
+#ifdef IN_RING3
+ *pu32 = pPciDev->Int.s.pfnConfigRead(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev, pPciAddr->iRegister, cb);
+#else
+ rc = rcReschedule;
+#endif
+ }
+ else
+ *pu32 = 0xffffffff;
+ }
+
+ Log3Func(("%02x:%02x.%d reg %x(%d) gave %x %Rrc\n",
+ pPciAddr->iBus, pPciAddr->iDeviceFunc >> 3, pPciAddr->iDeviceFunc & 0x7, pPciAddr->iRegister, cb, *pu32, rc));
+ return rc;
+}
+
+
+/**
+ * Port I/O Handler for PCI data IN operations.
+ *
+ * Emulates reads from Configuration Data Port at 0CFCh for
+ * Configuration Mechanism #1.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns ICH9 device instance.
+ * @param pvUser User argument - ignored.
+ * @param uPort Port number used for the IN operation.
+ * @param pu32 Where to store the result.
+ * @param cb Number of bytes read.
+ */
+PDMBOTHCBDECL(int) ich9pciIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb)
+{
+ NOREF(pvUser);
+ int rc = VINF_SUCCESS;
+ if (!(uPort % cb))
+ {
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ *pu32 = 0xffffffff;
+
+ PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_READ);
+
+ do
+ {
+ /* Configuration space mapping enabled? */
+ if (!(pThis->uConfigReg & (1 << 31)))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Decode target device and configuration space register */
+ PciAddress aPciAddr;
+ ich9pciStateToPciAddr(pThis, uPort, &aPciAddr);
+
+ /* Perform configuration space read */
+ rc = ich9pciConfigRead(pThis, &aPciAddr, cb, pu32, VINF_IOM_R3_IOPORT_READ);
+ } while (0);
+
+ PCI_UNLOCK(pDevIns);
+
+ LogFlowFunc(("Port=%#x cb=%#x (config=%#10x) -> %#x (%Rrc)\n", uPort, cb, *pu32, pThis->uConfigReg, rc));
+ return rc;
+ }
+ AssertMsgFailed(("Unaligned read from port %#x cb=%d\n", uPort, cb));
+ return VERR_IOM_IOPORT_UNUSED;
+}
+
+
+/* Compute mapping of PCI slot and IRQ number to APIC interrupt line */
+DECLINLINE(int) ich9pciSlot2ApicIrq(uint8_t uSlot, int irq_num)
+{
+ return (irq_num + uSlot) & 7;
+}
+
+#ifdef IN_RING3
+
+/* return the global irq number corresponding to a given device irq
+ pin. We could also use the bus number to have a more precise
+ mapping. This is the implementation note described in the PCI spec chapter 2.2.6 */
+DECLINLINE(int) ich9pciSlotGetPirq(uint8_t uBus, uint8_t uDevFn, uint8_t uIrqNum)
+{
+ NOREF(uBus);
+ int iSlotAddend = (uDevFn >> 3) - 1;
+ return (uIrqNum + iSlotAddend) & 3;
+}
+
+/* irqs corresponding to PCI irqs A-D, must match pci_irq_list in pcibios.inc */
+/** @todo r=klaus inconsistent! ich9 doesn't implement PIRQ yet, so both needs to be addressed and tested thoroughly. */
+static const uint8_t aPciIrqs[4] = { 11, 10, 9, 5 };
+
+#endif /* IN_RING3 */
+
+/* Add one more level up request on APIC input line */
+DECLINLINE(void) ich9pciApicLevelUp(PDEVPCIROOT pPciRoot, int irq_num)
+{
+ ASMAtomicIncU32(&pPciRoot->auPciApicIrqLevels[irq_num]);
+}
+
+/* Remove one level up request on APIC input line */
+DECLINLINE(void) ich9pciApicLevelDown(PDEVPCIROOT pPciRoot, int irq_num)
+{
+ ASMAtomicDecU32(&pPciRoot->auPciApicIrqLevels[irq_num]);
+}
+
+static void ich9pciApicSetIrq(PDEVPCIBUS pBus, uint8_t uDevFn, PDMPCIDEV *pPciDev, int irq_num1, int iLevel,
+ uint32_t uTagSrc, int iForcedIrq)
+{
+ /* This is only allowed to be called with a pointer to the root bus. */
+ AssertMsg(pBus->iBus == 0, ("iBus=%u\n", pBus->iBus));
+
+ if (iForcedIrq == -1)
+ {
+ int apic_irq, apic_level;
+ PDEVPCIROOT pPciRoot = DEVPCIBUS_2_DEVPCIROOT(pBus);
+ int irq_num = ich9pciSlot2ApicIrq(uDevFn >> 3, irq_num1);
+
+ if ((iLevel & PDM_IRQ_LEVEL_HIGH) == PDM_IRQ_LEVEL_HIGH)
+ ich9pciApicLevelUp(pPciRoot, irq_num);
+ else if ((iLevel & PDM_IRQ_LEVEL_HIGH) == PDM_IRQ_LEVEL_LOW)
+ ich9pciApicLevelDown(pPciRoot, irq_num);
+
+ apic_irq = irq_num + 0x10;
+ apic_level = pPciRoot->auPciApicIrqLevels[irq_num] != 0;
+ Log3Func(("%s: irq_num1=%d level=%d apic_irq=%d apic_level=%d irq_num1=%d uTagSrc=%#x\n",
+ R3STRING(pPciDev->pszNameR3), irq_num1, iLevel, apic_irq, apic_level, irq_num, uTagSrc));
+ pBus->CTX_SUFF(pPciHlp)->pfnIoApicSetIrq(pBus->CTX_SUFF(pDevIns), apic_irq, apic_level, uTagSrc);
+
+ if ((iLevel & PDM_IRQ_LEVEL_FLIP_FLOP) == PDM_IRQ_LEVEL_FLIP_FLOP)
+ {
+ /*
+ * we raised it few lines above, as PDM_IRQ_LEVEL_FLIP_FLOP has
+ * PDM_IRQ_LEVEL_HIGH bit set
+ */
+ ich9pciApicLevelDown(pPciRoot, irq_num);
+ pPciDev->Int.s.uIrqPinState = PDM_IRQ_LEVEL_LOW;
+ apic_level = pPciRoot->auPciApicIrqLevels[irq_num] != 0;
+ Log3Func(("%s: irq_num1=%d level=%d apic_irq=%d apic_level=%d irq_num1=%d uTagSrc=%#x (flop)\n",
+ R3STRING(pPciDev->pszNameR3), irq_num1, iLevel, apic_irq, apic_level, irq_num, uTagSrc));
+ pBus->CTX_SUFF(pPciHlp)->pfnIoApicSetIrq(pBus->CTX_SUFF(pDevIns), apic_irq, apic_level, uTagSrc);
+ }
+ } else {
+ Log3Func(("(forced) %s: irq_num1=%d level=%d acpi_irq=%d uTagSrc=%#x\n",
+ R3STRING(pPciDev->pszNameR3), irq_num1, iLevel, iForcedIrq, uTagSrc));
+ pBus->CTX_SUFF(pPciHlp)->pfnIoApicSetIrq(pBus->CTX_SUFF(pDevIns), iForcedIrq, iLevel, uTagSrc);
+ }
+}
+
+static void ich9pciSetIrqInternal(PDEVPCIROOT pPciRoot, uint8_t uDevFn, PPDMPCIDEV pPciDev,
+ int iIrq, int iLevel, uint32_t uTagSrc)
+{
+ /* If MSI or MSI-X is enabled, PCI INTx# signals are disabled regardless of the PCI command
+ * register interrupt bit state.
+ * PCI 3.0 (section 6.8) forbids MSI and MSI-X to be enabled at the same time and makes
+ * that undefined behavior. We check for MSI first, then MSI-X.
+ */
+ if (MsiIsEnabled(pPciDev))
+ {
+ Assert(!MsixIsEnabled(pPciDev)); /* Not allowed -- see note above. */
+ LogFlowFunc(("PCI Dev %p : MSI\n", pPciDev));
+ PPDMDEVINS pDevIns = pPciRoot->PciBus.CTX_SUFF(pDevIns);
+ MsiNotify(pDevIns, pPciRoot->PciBus.CTX_SUFF(pPciHlp), pPciDev, iIrq, iLevel, uTagSrc);
+ return;
+ }
+
+ if (MsixIsEnabled(pPciDev))
+ {
+ LogFlowFunc(("PCI Dev %p : MSI-X\n", pPciDev));
+ PPDMDEVINS pDevIns = pPciRoot->PciBus.CTX_SUFF(pDevIns);
+ MsixNotify(pDevIns, pPciRoot->PciBus.CTX_SUFF(pPciHlp), pPciDev, iIrq, iLevel, uTagSrc);
+ return;
+ }
+
+ PDEVPCIBUS pBus = &pPciRoot->PciBus;
+ /* safe, only needs to go to the config space array */
+ const bool fIsAcpiDevice = PDMPciDevGetDeviceId(pPciDev) == 0x7113;
+
+ LogFlowFunc(("PCI Dev %p : IRQ\n", pPciDev));
+ /* Check if the state changed. */
+ if (pPciDev->Int.s.uIrqPinState != iLevel)
+ {
+ pPciDev->Int.s.uIrqPinState = (iLevel & PDM_IRQ_LEVEL_HIGH);
+
+ /** @todo r=klaus: implement PIRQ handling (if APIC isn't active). Needed for legacy OSes which don't use the APIC stuff. */
+
+ /* Send interrupt to I/O APIC only now. */
+ if (fIsAcpiDevice)
+ /*
+ * ACPI needs special treatment since SCI is hardwired and
+ * should not be affected by PCI IRQ routing tables at the
+ * same time SCI IRQ is shared in PCI sense hence this
+ * kludge (i.e. we fetch the hardwired value from ACPIs
+ * PCI device configuration space).
+ */
+ /* safe, only needs to go to the config space array */
+ ich9pciApicSetIrq(pBus, uDevFn, pPciDev, -1, iLevel, uTagSrc, PDMPciDevGetInterruptLine(pPciDev));
+ else
+ ich9pciApicSetIrq(pBus, uDevFn, pPciDev, iIrq, iLevel, uTagSrc, -1);
+ }
+}
+
+
+/**
+ * Memory mapped I/O Handler for write operations.
+ *
+ * Emulates writes to configuration space.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pvUser User argument.
+ * @param GCPhysAddr Physical address (in GC) where the read starts.
+ * @param pv Where to fetch the result.
+ * @param cb Number of bytes to write.
+ * @remarks Caller enters the device critical section.
+ */
+PDMBOTHCBDECL(int) ich9pciMcfgMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
+{
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ uint32_t u32 = 0;
+ NOREF(pvUser);
+
+ Log2Func(("%RGp(%d) \n", GCPhysAddr, cb));
+
+ PCI_LOCK(pDevIns, VINF_IOM_R3_MMIO_WRITE);
+
+ /* Decode target device and configuration space register */
+ PciAddress aDest;
+ ich9pciPhysToPciAddr(pPciRoot, GCPhysAddr, &aDest);
+
+ switch (cb)
+ {
+ case 1:
+ u32 = *(uint8_t*)pv;
+ break;
+ case 2:
+ u32 = *(uint16_t*)pv;
+ break;
+ case 4:
+ u32 = *(uint32_t*)pv;
+ break;
+ default:
+ Assert(false);
+ break;
+ }
+
+ /* Perform configuration space write */
+ int rc = ich9pciConfigWrite(pPciRoot, &aDest, u32, cb, VINF_IOM_R3_MMIO_WRITE);
+ PCI_UNLOCK(pDevIns);
+
+ return rc;
+}
+
+
+/**
+ * Memory mapped I/O Handler for read operations.
+ *
+ * Emulates reads from configuration space.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pvUser User argument.
+ * @param GCPhysAddr Physical address (in GC) where the read starts.
+ * @param pv Where to store the result.
+ * @param cb Number of bytes read.
+ * @remarks Caller enters the device critical section.
+ */
+PDMBOTHCBDECL(int) ich9pciMcfgMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
+{
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ uint32_t rv;
+ NOREF(pvUser);
+
+ LogFlowFunc(("%RGp(%d) \n", GCPhysAddr, cb));
+
+ PCI_LOCK(pDevIns, VINF_IOM_R3_MMIO_READ);
+
+ /* Decode target device and configuration space register */
+ PciAddress aDest;
+ ich9pciPhysToPciAddr(pPciRoot, GCPhysAddr, &aDest);
+
+ /* Perform configuration space read */
+ int rc = ich9pciConfigRead(pPciRoot, &aDest, cb, &rv, VINF_IOM_R3_MMIO_READ);
+
+ if (RT_SUCCESS(rc))
+ {
+ switch (cb)
+ {
+ case 1:
+ *(uint8_t*)pv = (uint8_t)rv;
+ break;
+ case 2:
+ *(uint16_t*)pv = (uint16_t)rv;
+ break;
+ case 4:
+ *(uint32_t*)pv = (uint32_t)rv;
+ break;
+ default:
+ Assert(false);
+ break;
+ }
+ }
+ PCI_UNLOCK(pDevIns);
+
+ return rc;
+}
+
+#ifdef IN_RING3
+
+/*
+ * Include code we share with the other PCI bus implementation.
+ *
+ * Note! No #ifdefs, use instant data booleans/flags/whatever. Goal is to
+ * completely merge these files! File #1 contains code we write, where
+ * as a possible file #2 contains external code if there's any left.
+ */
+# include "DevPciMerge1.cpp.h"
+
+
+DECLINLINE(PPDMPCIDEV) ich9pciFindBridge(PDEVPCIBUS pBus, uint8_t uBus)
+{
+ /* Search for a fitting bridge. */
+ for (uint32_t iBridge = 0; iBridge < pBus->cBridges; iBridge++)
+ {
+ /*
+ * Examine secondary and subordinate bus number.
+ * If the target bus is in the range we pass the request on to the bridge.
+ */
+ PPDMPCIDEV pBridge = pBus->papBridgesR3[iBridge];
+ AssertMsg(pBridge && pciDevIsPci2PciBridge(pBridge),
+ ("Device is not a PCI bridge but on the list of PCI bridges\n"));
+ /* safe, only needs to go to the config space array */
+ uint32_t uSecondary = PDMPciDevGetByte(pBridge, VBOX_PCI_SECONDARY_BUS);
+ /* safe, only needs to go to the config space array */
+ uint32_t uSubordinate = PDMPciDevGetByte(pBridge, VBOX_PCI_SUBORDINATE_BUS);
+ Log3Func(("bus %p, bridge %d: %d in %d..%d\n", pBus, iBridge, uBus, uSecondary, uSubordinate));
+ if (uBus >= uSecondary && uBus <= uSubordinate)
+ return pBridge;
+ }
+
+ /* Nothing found. */
+ return NULL;
+}
+
+uint32_t devpciR3GetCfg(PPDMPCIDEV pPciDev, int32_t iRegister, int cb)
+{
+ return pPciDev->Int.s.pfnConfigRead(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev, iRegister, cb);
+}
+
+DECLINLINE(uint32_t) devpciGetRegionReg(int iRegion)
+{
+ return iRegion == VBOX_PCI_ROM_SLOT
+ ? VBOX_PCI_ROM_ADDRESS : (VBOX_PCI_BASE_ADDRESS_0 + iRegion * 4);
+}
+
+void devpciR3SetCfg(PPDMPCIDEV pPciDev, int32_t iRegister, uint32_t u32, int cb)
+{
+ pPciDev->Int.s.pfnConfigWrite(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev, iRegister, u32, cb);
+}
+
+
+/* -=-=-=-=-=- PCI Bus Interface Methods (PDMPCIBUSREG) -=-=-=-=-=- */
+
+
+static DECLCALLBACK(int) ich9pciRegisterMsi(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, PPDMMSIREG pMsiReg)
+{
+ NOREF(pDevIns);
+ int rc;
+
+ rc = MsiR3Init(pPciDev, pMsiReg);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = MsixR3Init(pPciDev->Int.s.CTX_SUFF(pBus)->CTX_SUFF(pPciHlp), pPciDev, pMsiReg);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMPCIBUSREG,pfnIORegionRegisterR3}
+ */
+DECLCALLBACK(int) devpciR3CommonIORegionRegister(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iRegion, RTGCPHYS cbRegion,
+ PCIADDRESSSPACE enmType, PFNPCIIOREGIONMAP pfnCallback)
+{
+ NOREF(pDevIns);
+
+ /*
+ * Validate.
+ */
+ AssertMsgReturn( enmType == (PCI_ADDRESS_SPACE_MEM | PCI_ADDRESS_SPACE_BAR32)
+ || enmType == (PCI_ADDRESS_SPACE_MEM_PREFETCH | PCI_ADDRESS_SPACE_BAR32)
+ || enmType == (PCI_ADDRESS_SPACE_MEM | PCI_ADDRESS_SPACE_BAR64)
+ || enmType == (PCI_ADDRESS_SPACE_MEM_PREFETCH | PCI_ADDRESS_SPACE_BAR64)
+ || enmType == PCI_ADDRESS_SPACE_IO
+ ,
+ ("Invalid enmType=%#x? Or was this a bitmask after all...\n", enmType),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn((unsigned)iRegion < VBOX_PCI_NUM_REGIONS,
+ ("Invalid iRegion=%d VBOX_PCI_NUM_REGIONS=%d\n", iRegion, VBOX_PCI_NUM_REGIONS),
+ VERR_INVALID_PARAMETER);
+ int iLastSet = ASMBitLastSetU64(cbRegion);
+ AssertMsgReturn( iLastSet != 0
+ && RT_BIT_64(iLastSet - 1) == cbRegion,
+ ("Invalid cbRegion=%RGp iLastSet=%#x (not a power of 2 or 0)\n", cbRegion, iLastSet),
+ VERR_INVALID_PARAMETER);
+
+ LogFunc(("%s region %d size %RGp type %x\n", pPciDev->pszNameR3, iRegion, cbRegion, enmType));
+
+ /* Make sure that we haven't marked this region as continuation of 64-bit region. */
+ Assert(pPciDev->Int.s.aIORegions[iRegion].type != 0xff);
+
+ /*
+ * Register the I/O region.
+ */
+ PPCIIOREGION pRegion = &pPciDev->Int.s.aIORegions[iRegion];
+ pRegion->addr = INVALID_PCI_ADDRESS;
+ pRegion->size = cbRegion;
+ pRegion->type = enmType;
+ pRegion->map_func = pfnCallback;
+
+ if ((enmType & PCI_ADDRESS_SPACE_BAR64) != 0)
+ {
+ /* VBOX_PCI_BASE_ADDRESS_5 and VBOX_PCI_ROM_ADDRESS are excluded. */
+ AssertMsgReturn(iRegion < VBOX_PCI_NUM_REGIONS - 2,
+ ("Region %d cannot be 64-bit\n", iRegion),
+ VERR_INVALID_PARAMETER);
+ /* Mark next region as continuation of this one. */
+ pPciDev->Int.s.aIORegions[iRegion + 1].type = 0xff;
+ }
+
+ /* Set type in the PCI config space. */
+ AssertCompile(PCI_ADDRESS_SPACE_MEM == 0);
+ AssertCompile(PCI_ADDRESS_SPACE_IO == 1);
+ AssertCompile(PCI_ADDRESS_SPACE_BAR64 == RT_BIT_32(2));
+ AssertCompile(PCI_ADDRESS_SPACE_MEM_PREFETCH == RT_BIT_32(3));
+ uint32_t u32Value = (uint32_t)enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64 | PCI_ADDRESS_SPACE_MEM_PREFETCH);
+ /* safe, only needs to go to the config space array */
+ PDMPciDevSetDWord(pPciDev, devpciGetRegionReg(iRegion), u32Value);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMPCIBUSREG,pfnSetConfigCallbacksR3}
+ */
+DECLCALLBACK(void) devpciR3CommonSetConfigCallbacks(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev,
+ PFNPCICONFIGREAD pfnRead, PPFNPCICONFIGREAD ppfnReadOld,
+ PFNPCICONFIGWRITE pfnWrite, PPFNPCICONFIGWRITE ppfnWriteOld)
+{
+ NOREF(pDevIns);
+
+ if (ppfnReadOld)
+ *ppfnReadOld = pPciDev->Int.s.pfnConfigRead;
+ pPciDev->Int.s.pfnConfigRead = pfnRead;
+
+ if (ppfnWriteOld)
+ *ppfnWriteOld = pPciDev->Int.s.pfnConfigWrite;
+ pPciDev->Int.s.pfnConfigWrite = pfnWrite;
+}
+
+
+static int ich9pciR3CommonSaveExec(PDEVPCIBUS pBus, PSSMHANDLE pSSM)
+{
+ /*
+ * Iterate thru all the devices.
+ */
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pDev = pBus->apDevices[uDevFn];
+ if (pDev)
+ {
+ /* Device position */
+ SSMR3PutU32(pSSM, uDevFn);
+ /* PCI config registers */
+ SSMR3PutMem(pSSM, pDev->abConfig, sizeof(pDev->abConfig));
+
+ /* Device flags */
+ int rc = SSMR3PutU32(pSSM, pDev->Int.s.fFlags);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* IRQ pin state */
+ rc = SSMR3PutS32(pSSM, pDev->Int.s.uIrqPinState);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* MSI info */
+ rc = SSMR3PutU8(pSSM, pDev->Int.s.u8MsiCapOffset);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = SSMR3PutU8(pSSM, pDev->Int.s.u8MsiCapSize);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* MSI-X info */
+ rc = SSMR3PutU8(pSSM, pDev->Int.s.u8MsixCapOffset);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = SSMR3PutU8(pSSM, pDev->Int.s.u8MsixCapSize);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Save MSI-X page state */
+ if (pDev->Int.s.u8MsixCapOffset != 0)
+ {
+ Assert(pDev->Int.s.pMsixPageR3 != NULL);
+ SSMR3PutMem(pSSM, pDev->Int.s.pMsixPageR3, 0x1000);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /* Save the type an size of all the regions. */
+ for (uint32_t iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
+ {
+ SSMR3PutU8(pSSM, pDev->Int.s.aIORegions[iRegion].type);
+ rc = SSMR3PutU64(pSSM, pDev->Int.s.aIORegions[iRegion].size);
+ AssertRCReturn(rc, rc);
+ }
+ }
+ }
+ return SSMR3PutU32(pSSM, UINT32_MAX); /* terminator */
+}
+
+static DECLCALLBACK(int) ich9pciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+
+ /*
+ * Bus state data.
+ */
+ SSMR3PutU32(pSSM, pThis->uConfigReg);
+
+ /*
+ * Save IRQ states.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->auPciApicIrqLevels); i++)
+ SSMR3PutU32(pSSM, pThis->auPciApicIrqLevels[i]);
+
+ SSMR3PutU32(pSSM, UINT32_MAX); /* separator */
+
+ return ich9pciR3CommonSaveExec(&pThis->PciBus, pSSM);
+}
+
+
+static DECLCALLBACK(int) ich9pcibridgeR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDEVPCIBUS pThis = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ return ich9pciR3CommonSaveExec(pThis, pSSM);
+}
+
+
+static DECLCALLBACK(void) ich9pcibridgeConfigWrite(PPDMDEVINSR3 pDevIns, uint8_t uBus, uint8_t uDevice, uint32_t u32Address, uint32_t u32Value, unsigned cb)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+
+ LogFlowFunc(("pDevIns=%p uBus=%d uDevice=%d u32Address=%u u32Value=%u cb=%d\n", pDevIns, uBus, uDevice, u32Address, u32Value, cb));
+
+ /* If the current bus is not the target bus search for the bus which contains the device. */
+ /* safe, only needs to go to the config space array */
+ if (uBus != PDMPciDevGetByte(&pBus->PciDev, VBOX_PCI_SECONDARY_BUS))
+ {
+ PPDMPCIDEV pBridgeDevice = ich9pciFindBridge(pBus, uBus);
+ if (pBridgeDevice)
+ {
+ AssertPtr(pBridgeDevice->Int.s.pfnBridgeConfigWrite);
+ pBridgeDevice->Int.s.pfnBridgeConfigWrite(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), uBus, uDevice,
+ u32Address, u32Value, cb);
+ }
+ }
+ else
+ {
+ /* This is the target bus, pass the write to the device. */
+ PPDMPCIDEV pPciDev = pBus->apDevices[uDevice];
+ if (pPciDev)
+ {
+ LogFunc(("%s: addr=%02x val=%08x len=%d\n", pPciDev->pszNameR3, u32Address, u32Value, cb));
+ /** @todo return rc */
+ pPciDev->Int.s.pfnConfigWrite(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev, u32Address, u32Value, cb);
+ }
+ }
+}
+
+static DECLCALLBACK(uint32_t) ich9pcibridgeConfigRead(PPDMDEVINSR3 pDevIns, uint8_t uBus, uint8_t uDevice, uint32_t u32Address, unsigned cb)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ uint32_t u32Value;
+
+ LogFlowFunc(("pDevIns=%p uBus=%d uDevice=%d u32Address=%u cb=%d\n", pDevIns, uBus, uDevice, u32Address, cb));
+
+ /* If the current bus is not the target bus search for the bus which contains the device. */
+ /* safe, only needs to go to the config space array */
+ if (uBus != PDMPciDevGetByte(&pBus->PciDev, VBOX_PCI_SECONDARY_BUS))
+ {
+ PPDMPCIDEV pBridgeDevice = ich9pciFindBridge(pBus, uBus);
+ if (pBridgeDevice)
+ {
+ AssertPtr( pBridgeDevice->Int.s.pfnBridgeConfigRead);
+ u32Value = pBridgeDevice->Int.s.pfnBridgeConfigRead(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), uBus, uDevice,
+ u32Address, cb);
+ }
+ else
+ u32Value = 0xffffffff;
+ }
+ else
+ {
+ /* This is the target bus, pass the read to the device. */
+ PPDMPCIDEV pPciDev = pBus->apDevices[uDevice];
+ if (pPciDev)
+ {
+ u32Value = pPciDev->Int.s.pfnConfigRead(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev, u32Address, cb);
+ LogFunc(("%s: u32Address=%02x u32Value=%08x cb=%d\n", pPciDev->pszNameR3, u32Address, u32Value, cb));
+ }
+ else
+ u32Value = 0xffffffff;
+ }
+
+ return u32Value;
+}
+
+
+
+/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
+
+
+/**
+ * Common routine for restoring the config registers of a PCI device.
+ *
+ * @param pDev The PCI device.
+ * @param pbSrcConfig The configuration register values to be loaded.
+ */
+void devpciR3CommonRestoreConfig(PPDMPCIDEV pDev, uint8_t const *pbSrcConfig)
+{
+ /*
+ * This table defines the fields for normal devices and bridge devices, and
+ * the order in which they need to be restored.
+ */
+ static const struct PciField
+ {
+ uint8_t off;
+ uint8_t cb;
+ uint8_t fWritable;
+ uint8_t fBridge;
+ const char *pszName;
+ } s_aFields[] =
+ {
+ /* off,cb,fW,fB, pszName */
+ { 0x00, 2, 0, 3, "VENDOR_ID" },
+ { 0x02, 2, 0, 3, "DEVICE_ID" },
+ { 0x06, 2, 1, 3, "STATUS" },
+ { 0x08, 1, 0, 3, "REVISION_ID" },
+ { 0x09, 1, 0, 3, "CLASS_PROG" },
+ { 0x0a, 1, 0, 3, "CLASS_SUB" },
+ { 0x0b, 1, 0, 3, "CLASS_BASE" },
+ { 0x0c, 1, 1, 3, "CACHE_LINE_SIZE" },
+ { 0x0d, 1, 1, 3, "LATENCY_TIMER" },
+ { 0x0e, 1, 0, 3, "HEADER_TYPE" },
+ { 0x0f, 1, 1, 3, "BIST" },
+ { 0x10, 4, 1, 3, "BASE_ADDRESS_0" },
+ { 0x14, 4, 1, 3, "BASE_ADDRESS_1" },
+ { 0x18, 4, 1, 1, "BASE_ADDRESS_2" },
+ { 0x18, 1, 1, 2, "PRIMARY_BUS" },
+ { 0x19, 1, 1, 2, "SECONDARY_BUS" },
+ { 0x1a, 1, 1, 2, "SUBORDINATE_BUS" },
+ { 0x1b, 1, 1, 2, "SEC_LATENCY_TIMER" },
+ { 0x1c, 4, 1, 1, "BASE_ADDRESS_3" },
+ { 0x1c, 1, 1, 2, "IO_BASE" },
+ { 0x1d, 1, 1, 2, "IO_LIMIT" },
+ { 0x1e, 2, 1, 2, "SEC_STATUS" },
+ { 0x20, 4, 1, 1, "BASE_ADDRESS_4" },
+ { 0x20, 2, 1, 2, "MEMORY_BASE" },
+ { 0x22, 2, 1, 2, "MEMORY_LIMIT" },
+ { 0x24, 4, 1, 1, "BASE_ADDRESS_5" },
+ { 0x24, 2, 1, 2, "PREF_MEMORY_BASE" },
+ { 0x26, 2, 1, 2, "PREF_MEMORY_LIMIT" },
+ { 0x28, 4, 0, 1, "CARDBUS_CIS" },
+ { 0x28, 4, 1, 2, "PREF_BASE_UPPER32" },
+ { 0x2c, 2, 0, 1, "SUBSYSTEM_VENDOR_ID" },
+ { 0x2c, 4, 1, 2, "PREF_LIMIT_UPPER32" },
+ { 0x2e, 2, 0, 1, "SUBSYSTEM_ID" },
+ { 0x30, 4, 1, 1, "ROM_ADDRESS" },
+ { 0x30, 2, 1, 2, "IO_BASE_UPPER16" },
+ { 0x32, 2, 1, 2, "IO_LIMIT_UPPER16" },
+ { 0x34, 4, 0, 3, "CAPABILITY_LIST" },
+ { 0x38, 4, 1, 1, "RESERVED_38" },
+ { 0x38, 4, 1, 2, "ROM_ADDRESS_BR" },
+ { 0x3c, 1, 1, 3, "INTERRUPT_LINE" },
+ { 0x3d, 1, 0, 3, "INTERRUPT_PIN" },
+ { 0x3e, 1, 0, 1, "MIN_GNT" },
+ { 0x3e, 2, 1, 2, "BRIDGE_CONTROL" },
+ { 0x3f, 1, 0, 1, "MAX_LAT" },
+ /* The COMMAND register must come last as it requires the *ADDRESS*
+ registers to be restored before we pretent to change it from 0 to
+ whatever value the guest assigned it. */
+ { 0x04, 2, 1, 3, "COMMAND" },
+ };
+
+#ifdef RT_STRICT
+ /* Check that we've got full register coverage. */
+ uint32_t bmDevice[0x40 / 32];
+ uint32_t bmBridge[0x40 / 32];
+ RT_ZERO(bmDevice);
+ RT_ZERO(bmBridge);
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aFields); i++)
+ {
+ uint8_t off = s_aFields[i].off;
+ uint8_t cb = s_aFields[i].cb;
+ uint8_t f = s_aFields[i].fBridge;
+ while (cb-- > 0)
+ {
+ if (f & 1) AssertMsg(!ASMBitTest(bmDevice, off), ("%#x\n", off));
+ if (f & 2) AssertMsg(!ASMBitTest(bmBridge, off), ("%#x\n", off));
+ if (f & 1) ASMBitSet(bmDevice, off);
+ if (f & 2) ASMBitSet(bmBridge, off);
+ off++;
+ }
+ }
+ for (uint32_t off = 0; off < 0x40; off++)
+ {
+ AssertMsg(ASMBitTest(bmDevice, off), ("%#x\n", off));
+ AssertMsg(ASMBitTest(bmBridge, off), ("%#x\n", off));
+ }
+#endif
+
+ /*
+ * Loop thru the fields covering the 64 bytes of standard registers.
+ */
+ uint8_t const fBridge = pciDevIsPci2PciBridge(pDev) ? 2 : 1;
+ Assert(!pciDevIsPassthrough(pDev));
+ uint8_t *pbDstConfig = &pDev->abConfig[0];
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aFields); i++)
+ if (s_aFields[i].fBridge & fBridge)
+ {
+ uint8_t const off = s_aFields[i].off;
+ uint8_t const cb = s_aFields[i].cb;
+ uint32_t u32Src;
+ uint32_t u32Dst;
+ switch (cb)
+ {
+ case 1:
+ u32Src = pbSrcConfig[off];
+ u32Dst = pbDstConfig[off];
+ break;
+ case 2:
+ u32Src = *(uint16_t const *)&pbSrcConfig[off];
+ u32Dst = *(uint16_t const *)&pbDstConfig[off];
+ break;
+ case 4:
+ u32Src = *(uint32_t const *)&pbSrcConfig[off];
+ u32Dst = *(uint32_t const *)&pbDstConfig[off];
+ break;
+ default:
+ AssertFailed();
+ continue;
+ }
+
+ if ( u32Src != u32Dst
+ || off == VBOX_PCI_COMMAND)
+ {
+ if (u32Src != u32Dst)
+ {
+ if (!s_aFields[i].fWritable)
+ LogRel(("PCI: %8s/%u: %2u-bit field %s: %x -> %x - !READ ONLY!\n",
+ pDev->pszNameR3, pDev->Int.s.CTX_SUFF(pDevIns)->iInstance, cb*8, s_aFields[i].pszName, u32Dst, u32Src));
+ else
+ LogRel(("PCI: %8s/%u: %2u-bit field %s: %x -> %x\n",
+ pDev->pszNameR3, pDev->Int.s.CTX_SUFF(pDevIns)->iInstance, cb*8, s_aFields[i].pszName, u32Dst, u32Src));
+ }
+ if (off == VBOX_PCI_COMMAND)
+ /* safe, only needs to go to the config space array */
+ PDMPciDevSetCommand(pDev, 0); /* For remapping, see pciR3CommonLoadExec/ich9pciR3CommonLoadExec. */
+ pDev->Int.s.pfnConfigWrite(pDev->Int.s.CTX_SUFF(pDevIns), pDev, off, u32Src, cb);
+ }
+ }
+
+ /*
+ * The device dependent registers.
+ *
+ * We will not use ConfigWrite here as we have no clue about the size
+ * of the registers, so the device is responsible for correctly
+ * restoring functionality governed by these registers.
+ */
+ for (uint32_t off = 0x40; off < sizeof(pDev->abConfig); off++)
+ if (pbDstConfig[off] != pbSrcConfig[off])
+ {
+ LogRel(("PCI: %8s/%u: register %02x: %02x -> %02x\n",
+ pDev->pszNameR3, pDev->Int.s.CTX_SUFF(pDevIns)->iInstance, off, pbDstConfig[off], pbSrcConfig[off])); /** @todo make this Log() later. */
+ pbDstConfig[off] = pbSrcConfig[off];
+ }
+}
+
+
+/**
+ * @callback_method_impl{FNPCIIOREGIONOLDSETTER}
+ */
+static DECLCALLBACK(int) devpciR3CommonRestoreOldSetRegion(PPDMPCIDEV pPciDev, uint32_t iRegion,
+ RTGCPHYS cbRegion, PCIADDRESSSPACE enmType)
+{
+ AssertLogRelReturn(iRegion < RT_ELEMENTS(pPciDev->Int.s.aIORegions), VERR_INVALID_PARAMETER);
+ pPciDev->Int.s.aIORegions[iRegion].type = enmType;
+ pPciDev->Int.s.aIORegions[iRegion].size = cbRegion;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks for and deals with changes in resource sizes and types.
+ *
+ * @returns VBox status code.
+ * @param pSSM The Saved state handle.
+ * @param pPciDev The PCI device in question.
+ * @param paIoRegions I/O regions with the size and type fields from
+ * the saved state.
+ * @param fNewState Set if this is a new state with I/O region sizes
+ * and types, clear if old one.
+ */
+int devpciR3CommonRestoreRegions(PSSMHANDLE pSSM, PPDMPCIDEV pPciDev, PPCIIOREGION paIoRegions, bool fNewState)
+{
+ int rc;
+ if (fNewState)
+ {
+ for (uint32_t iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
+ {
+ if ( pPciDev->Int.s.aIORegions[iRegion].type != paIoRegions[iRegion].type
+ || pPciDev->Int.s.aIORegions[iRegion].size != paIoRegions[iRegion].size)
+ {
+ AssertLogRelMsgFailed(("PCI: %8s/%u: region #%u size/type load change: %#RGp/%#x -> %#RGp/%#x\n",
+ pPciDev->pszNameR3, pPciDev->Int.s.CTX_SUFF(pDevIns)->iInstance, iRegion,
+ pPciDev->Int.s.aIORegions[iRegion].size, pPciDev->Int.s.aIORegions[iRegion].type,
+ paIoRegions[iRegion].size, paIoRegions[iRegion].type));
+ if (pPciDev->pfnRegionLoadChangeHookR3)
+ {
+ rc = pPciDev->pfnRegionLoadChangeHookR3(pPciDev->Int.s.pDevInsR3, pPciDev, iRegion, paIoRegions[iRegion].size,
+ (PCIADDRESSSPACE)paIoRegions[iRegion].type, NULL /*pfnOldSetter*/);
+ if (RT_FAILURE(rc))
+ return SSMR3SetLoadError(pSSM, rc, RT_SRC_POS,
+ N_("Device %s/%u failed to respond to region #%u size/type changing from %#RGp/%#x to %#RGp/%#x: %Rrc"),
+ pPciDev->pszNameR3, pPciDev->Int.s.CTX_SUFF(pDevIns)->iInstance, iRegion,
+ pPciDev->Int.s.aIORegions[iRegion].size, pPciDev->Int.s.aIORegions[iRegion].type,
+ paIoRegions[iRegion].size, paIoRegions[iRegion].type, rc);
+ }
+ pPciDev->Int.s.aIORegions[iRegion].type = paIoRegions[iRegion].type;
+ pPciDev->Int.s.aIORegions[iRegion].size = paIoRegions[iRegion].size;
+ }
+ }
+ }
+ /* Old saved state without sizes and types. Do a special hook call to give
+ devices with changes a chance to adjust resources back to old values. */
+ else if (pPciDev->pfnRegionLoadChangeHookR3)
+ {
+ rc = pPciDev->pfnRegionLoadChangeHookR3(pPciDev->Int.s.pDevInsR3, pPciDev, UINT32_MAX, RTGCPHYS_MAX, (PCIADDRESSSPACE)-1,
+ devpciR3CommonRestoreOldSetRegion);
+ if (RT_FAILURE(rc))
+ return SSMR3SetLoadError(pSSM, rc, RT_SRC_POS, N_("Device %s/%u failed to resize its resources: %Rrc"),
+ pPciDev->pszNameR3, pPciDev->Int.s.CTX_SUFF(pDevIns)->iInstance, rc);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Common worker for ich9pciR3LoadExec and ich9pcibridgeR3LoadExec.
+ *
+ * @returns VBox status code.
+ * @param pBus The bus which data is being loaded.
+ * @param pSSM The saved state handle.
+ * @param uVersion The data version.
+ * @param uPass The pass.
+ */
+static DECLCALLBACK(int) ich9pciR3CommonLoadExec(PDEVPCIBUS pBus, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ uint32_t u32;
+ int rc;
+
+ Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
+ if ( uVersion < VBOX_ICH9PCI_SAVED_STATE_VERSION_MSI
+ || uVersion > VBOX_ICH9PCI_SAVED_STATE_VERSION)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ /*
+ * Iterate thru all the devices and write 0 to the COMMAND register so
+ * that all the memory is unmapped before we start restoring the saved
+ * mapping locations.
+ *
+ * The register value is restored afterwards so we can do proper
+ * LogRels in devpciR3CommonRestoreConfig.
+ */
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pDev = pBus->apDevices[uDevFn];
+ if (pDev)
+ {
+ /* safe, only needs to go to the config space array */
+ uint16_t u16 = PDMPciDevGetCommand(pDev);
+ pDev->Int.s.pfnConfigWrite(pDev->Int.s.CTX_SUFF(pDevIns), pDev, VBOX_PCI_COMMAND, 0, 2);
+ /* safe, only needs to go to the config space array */
+ PDMPciDevSetCommand(pDev, u16);
+ /* safe, only needs to go to the config space array */
+ Assert(PDMPciDevGetCommand(pDev) == u16);
+ }
+ }
+
+ void *pvMsixPage = RTMemTmpAllocZ(0x1000);
+ AssertReturn(pvMsixPage, VERR_NO_TMP_MEMORY);
+
+ /*
+ * Iterate all the devices.
+ */
+ for (uint32_t uDevFn = 0;; uDevFn++)
+ {
+ /* index / terminator */
+ rc = SSMR3GetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ break;
+ if (u32 == (uint32_t)~0)
+ break;
+ AssertMsgBreak(u32 < RT_ELEMENTS(pBus->apDevices) && u32 >= uDevFn, ("u32=%#x uDevFn=%#x\n", u32, uDevFn));
+
+ /* skip forward to the device checking that no new devices are present. */
+ PPDMPCIDEV pDev;
+ for (; uDevFn < u32; uDevFn++)
+ {
+ pDev = pBus->apDevices[uDevFn];
+ if (pDev)
+ {
+ /* safe, only needs to go to the config space array */
+ LogRel(("PCI: New device in slot %#x, %s (vendor=%#06x device=%#06x)\n", uDevFn, pDev->pszNameR3,
+ PDMPciDevGetVendorId(pDev), PDMPciDevGetDeviceId(pDev)));
+ if (SSMR3HandleGetAfter(pSSM) != SSMAFTER_DEBUG_IT)
+ {
+ /* safe, only needs to go to the config space array */
+ rc = SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("New device in slot %#x, %s (vendor=%#06x device=%#06x)"),
+ uDevFn, pDev->pszNameR3, PDMPciDevGetVendorId(pDev), PDMPciDevGetDeviceId(pDev));
+ break;
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ break;
+
+ /* get the data */
+ PDMPCIDEV DevTmp;
+ RT_ZERO(DevTmp);
+ DevTmp.Int.s.fFlags = 0;
+ DevTmp.Int.s.u8MsiCapOffset = 0;
+ DevTmp.Int.s.u8MsiCapSize = 0;
+ DevTmp.Int.s.u8MsixCapOffset = 0;
+ DevTmp.Int.s.u8MsixCapSize = 0;
+ DevTmp.Int.s.uIrqPinState = ~0; /* Invalid value in case we have an older saved state to force a state change in pciSetIrq. */
+ SSMR3GetMem(pSSM, DevTmp.abConfig, sizeof(DevTmp.abConfig));
+
+ SSMR3GetU32(pSSM, &DevTmp.Int.s.fFlags);
+ SSMR3GetS32(pSSM, &DevTmp.Int.s.uIrqPinState);
+ SSMR3GetU8(pSSM, &DevTmp.Int.s.u8MsiCapOffset);
+ SSMR3GetU8(pSSM, &DevTmp.Int.s.u8MsiCapSize);
+ SSMR3GetU8(pSSM, &DevTmp.Int.s.u8MsixCapOffset);
+ rc = SSMR3GetU8(pSSM, &DevTmp.Int.s.u8MsixCapSize);
+ if (RT_FAILURE(rc))
+ break;
+
+ /* Load MSI-X page state */
+ if (DevTmp.Int.s.u8MsixCapOffset != 0)
+ {
+ Assert(pvMsixPage != NULL);
+ rc = SSMR3GetMem(pSSM, pvMsixPage, 0x1000);
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ /* Load the region types and sizes. */
+ if (uVersion >= VBOX_ICH9PCI_SAVED_STATE_VERSION_REGION_SIZES)
+ {
+ for (uint32_t iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
+ {
+ SSMR3GetU8(pSSM, &DevTmp.Int.s.aIORegions[iRegion].type);
+ rc = SSMR3GetU64(pSSM, &DevTmp.Int.s.aIORegions[iRegion].size);
+ AssertLogRelRCReturn(rc, rc);
+ }
+ }
+
+ /*
+ * Check that it's still around.
+ */
+ pDev = pBus->apDevices[uDevFn];
+ if (!pDev)
+ {
+ /* safe, only needs to go to the config space array */
+ LogRel(("PCI: Device in slot %#x has been removed! vendor=%#06x device=%#06x\n", uDevFn,
+ PDMPciDevGetVendorId(&DevTmp), PDMPciDevGetDeviceId(&DevTmp)));
+ if (SSMR3HandleGetAfter(pSSM) != SSMAFTER_DEBUG_IT)
+ {
+ /* safe, only needs to go to the config space array */
+ rc = SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Device in slot %#x has been removed! vendor=%#06x device=%#06x"),
+ uDevFn, PDMPciDevGetVendorId(&DevTmp), PDMPciDevGetDeviceId(&DevTmp));
+ break;
+ }
+ continue;
+ }
+
+ /* match the vendor id assuming that this will never be changed. */
+ /* safe, only needs to go to the config space array */
+ if (PDMPciDevGetVendorId(&DevTmp) != PDMPciDevGetVendorId(pDev))
+ {
+ /* safe, only needs to go to the config space array */
+ rc = SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Device in slot %#x (%s) vendor id mismatch! saved=%.4Rhxs current=%.4Rhxs"),
+ uDevFn, pDev->pszNameR3, PDMPciDevGetVendorId(&DevTmp), PDMPciDevGetVendorId(pDev));
+ break;
+ }
+
+ /* commit the loaded device config. */
+ rc = devpciR3CommonRestoreRegions(pSSM, pDev, DevTmp.Int.s.aIORegions,
+ uVersion >= VBOX_ICH9PCI_SAVED_STATE_VERSION_REGION_SIZES);
+ if (RT_FAILURE(rc))
+ break;
+ Assert(!pciDevIsPassthrough(pDev));
+ devpciR3CommonRestoreConfig(pDev, &DevTmp.abConfig[0]);
+
+ pDev->Int.s.uIrqPinState = DevTmp.Int.s.uIrqPinState;
+ pDev->Int.s.u8MsiCapOffset = DevTmp.Int.s.u8MsiCapOffset;
+ pDev->Int.s.u8MsiCapSize = DevTmp.Int.s.u8MsiCapSize;
+ pDev->Int.s.u8MsixCapOffset = DevTmp.Int.s.u8MsixCapOffset;
+ pDev->Int.s.u8MsixCapSize = DevTmp.Int.s.u8MsixCapSize;
+ if (DevTmp.Int.s.u8MsixCapSize != 0)
+ {
+ Assert(pDev->Int.s.pMsixPageR3 != NULL);
+ Assert(pDev->Int.s.cbMsixRegion != 0);
+ memcpy(pDev->Int.s.pMsixPageR3, pvMsixPage, pDev->Int.s.cbMsixRegion);
+ }
+ }
+
+ RTMemTmpFree(pvMsixPage);
+
+ return rc;
+}
+
+static DECLCALLBACK(int) ich9pciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PDEVPCIBUS pBus = &pThis->PciBus;
+ uint32_t u32;
+ int rc;
+
+ /* We ignore this version as there's no saved state with it anyway */
+ if (uVersion <= VBOX_ICH9PCI_SAVED_STATE_VERSION_NOMSI)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+ if (uVersion > VBOX_ICH9PCI_SAVED_STATE_VERSION)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ /*
+ * Bus state data.
+ */
+ SSMR3GetU32(pSSM, &pThis->uConfigReg);
+
+ /*
+ * Load IRQ states.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->auPciApicIrqLevels); i++)
+ SSMR3GetU32(pSSM, (uint32_t*)&pThis->auPciApicIrqLevels[i]);
+
+ /* separator */
+ rc = SSMR3GetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (u32 != (uint32_t)~0)
+ AssertMsgFailedReturn(("u32=%#x\n", u32), rc);
+
+ return ich9pciR3CommonLoadExec(pBus, pSSM, uVersion, uPass);
+}
+
+static DECLCALLBACK(int) ich9pcibridgeR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDEVPCIBUS pThis = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ return ich9pciR3CommonLoadExec(pThis, pSSM, uVersion, uPass);
+}
+
+
+
+/* -=-=-=-=-=- Fake PCI BIOS Init -=-=-=-=-=- */
+
+
+void devpciR3BiosInitSetRegionAddress(PDEVPCIBUS pBus, PPDMPCIDEV pPciDev, int iRegion, uint64_t addr)
+{
+ NOREF(pBus);
+ uint32_t uReg = devpciGetRegionReg(iRegion);
+
+ /* Read memory type first. */
+ uint8_t uResourceType = devpciR3GetByte(pPciDev, uReg);
+ bool f64Bit = (uResourceType & ((uint8_t)(PCI_ADDRESS_SPACE_BAR64 | PCI_ADDRESS_SPACE_IO)))
+ == PCI_ADDRESS_SPACE_BAR64;
+
+ Log(("Set region address: %02x:%02x.%d region %d address=%RX64%s\n",
+ pBus->iBus, pPciDev->uDevFn >> 3, pPciDev->uDevFn & 7, iRegion, addr, f64Bit ? " (64-bit)" : ""));
+
+ /* Write address of the device. */
+ devpciR3SetDWord(pPciDev, uReg, (uint32_t)addr);
+ if (f64Bit)
+ devpciR3SetDWord(pPciDev, uReg + 4, (uint32_t)(addr >> 32));
+}
+
+
+static void ich9pciBiosInitBridge(PDEVPCIROOT pPciRoot, PDEVPCIBUS pBus)
+{
+ PPDMPCIDEV pBridge = &pBus->PciDev;
+ Log(("BIOS init bridge: %02x:%02x.%d\n", pBus->iBus, pBridge->uDevFn >> 3, pBridge->uDevFn & 7));
+
+ /*
+ * The I/O range for the bridge must be aligned to a 4KB boundary.
+ * This does not change anything really as the access to the device is not going
+ * through the bridge but we want to be compliant to the spec.
+ */
+ if ((pPciRoot->uPciBiosIo % _4K) != 0)
+ {
+ pPciRoot->uPciBiosIo = RT_ALIGN_32(pPciRoot->uPciBiosIo, _4K);
+ LogFunc(("Aligned I/O start address. New address %#x\n", pPciRoot->uPciBiosIo));
+ }
+ devpciR3SetByte(pBridge, VBOX_PCI_IO_BASE, (pPciRoot->uPciBiosIo >> 8) & 0xf0);
+
+ /* The MMIO range for the bridge must be aligned to a 1MB boundary. */
+ if ((pPciRoot->uPciBiosMmio % _1M) != 0)
+ {
+ pPciRoot->uPciBiosMmio = RT_ALIGN_32(pPciRoot->uPciBiosMmio, _1M);
+ LogFunc(("Aligned MMIO start address. New address %#x\n", pPciRoot->uPciBiosMmio));
+ }
+ devpciR3SetWord(pBridge, VBOX_PCI_MEMORY_BASE, (pPciRoot->uPciBiosMmio >> 16) & UINT32_C(0xffff0));
+
+ /* Save values to compare later to. */
+ uint32_t u32IoAddressBase = pPciRoot->uPciBiosIo;
+ uint32_t u32MMIOAddressBase = pPciRoot->uPciBiosMmio;
+
+ /* Init all devices behind the bridge (recursing to further buses). */
+ ich9pciBiosInitAllDevicesOnBus(pPciRoot, pBus);
+
+ /*
+ * Set I/O limit register. If there is no device with I/O space behind the
+ * bridge we set a lower value than in the base register.
+ */
+ if (u32IoAddressBase != pPciRoot->uPciBiosIo)
+ {
+ /* Need again alignment to a 4KB boundary. */
+ pPciRoot->uPciBiosIo = RT_ALIGN_32(pPciRoot->uPciBiosIo, _4K);
+ devpciR3SetByte(pBridge, VBOX_PCI_IO_LIMIT, ((pPciRoot->uPciBiosIo - 1) >> 8) & 0xf0);
+ }
+ else
+ {
+ devpciR3SetByte(pBridge, VBOX_PCI_IO_BASE, 0xf0);
+ devpciR3SetByte(pBridge, VBOX_PCI_IO_LIMIT, 0x00);
+ }
+
+ /* Same with the MMIO limit register but with 1MB boundary here. */
+ if (u32MMIOAddressBase != pPciRoot->uPciBiosMmio)
+ {
+ pPciRoot->uPciBiosMmio = RT_ALIGN_32(pPciRoot->uPciBiosMmio, _1M);
+ devpciR3SetWord(pBridge, VBOX_PCI_MEMORY_LIMIT, ((pPciRoot->uPciBiosMmio - 1) >> 16) & UINT32_C(0xfff0));
+ }
+ else
+ {
+ devpciR3SetWord(pBridge, VBOX_PCI_MEMORY_BASE, 0xfff0);
+ devpciR3SetWord(pBridge, VBOX_PCI_MEMORY_LIMIT, 0x0000);
+ }
+
+ /*
+ * Set the prefetch base and limit registers. We currently have no device with a prefetchable region
+ * which may be behind a bridge. That's why it is unconditionally disabled here atm by writing a higher value into
+ * the base register than in the limit register.
+ */
+ devpciR3SetWord(pBridge, VBOX_PCI_PREF_MEMORY_BASE, 0xfff0);
+ devpciR3SetWord(pBridge, VBOX_PCI_PREF_MEMORY_LIMIT, 0x0000);
+ devpciR3SetDWord(pBridge, VBOX_PCI_PREF_BASE_UPPER32, 0x00000000);
+ devpciR3SetDWord(pBridge, VBOX_PCI_PREF_LIMIT_UPPER32, 0x00000000);
+}
+
+static int ich9pciBiosInitDeviceGetRegions(PPDMPCIDEV pPciDev)
+{
+ uint8_t uHeaderType = devpciR3GetByte(pPciDev, VBOX_PCI_HEADER_TYPE) & 0x7f;
+ if (uHeaderType == 0x00)
+ /* Ignore ROM region here, which is included in VBOX_PCI_NUM_REGIONS. */
+ return VBOX_PCI_NUM_REGIONS - 1;
+ else if (uHeaderType == 0x01)
+ /* PCI bridges have 2 BARs. */
+ return 2;
+ else
+ {
+ AssertMsgFailed(("invalid header type %#x\n", uHeaderType));
+ return 0;
+ }
+}
+
+static void ich9pciBiosInitDeviceBARs(PDEVPCIROOT pPciRoot, PDEVPCIBUS pBus, PPDMPCIDEV pPciDev)
+{
+ int cRegions = ich9pciBiosInitDeviceGetRegions(pPciDev);
+ bool fSuppressMem = false;
+ bool fActiveMemRegion = false;
+ bool fActiveIORegion = false;
+ for (int iRegion = 0; iRegion < cRegions; iRegion++)
+ {
+ uint32_t u32Address = devpciGetRegionReg(iRegion);
+
+ /* Calculate size - we write all 1s into the BAR, and then evaluate which bits
+ are cleared. */
+ uint8_t u8ResourceType = devpciR3GetByte(pPciDev, u32Address);
+
+ bool fPrefetch = (u8ResourceType & ((uint8_t)(PCI_ADDRESS_SPACE_MEM_PREFETCH | PCI_ADDRESS_SPACE_IO)))
+ == PCI_ADDRESS_SPACE_MEM_PREFETCH;
+ bool f64Bit = (u8ResourceType & ((uint8_t)(PCI_ADDRESS_SPACE_BAR64 | PCI_ADDRESS_SPACE_IO)))
+ == PCI_ADDRESS_SPACE_BAR64;
+ bool fIsPio = ((u8ResourceType & PCI_ADDRESS_SPACE_IO) == PCI_ADDRESS_SPACE_IO);
+ uint64_t cbRegSize64 = 0;
+
+ /* Hack: initialize prefetchable BARs for devices on the root bus
+ * early, but for all other prefetchable BARs do it after the
+ * non-prefetchable BARs are initialized on all buses. */
+ if (fPrefetch && pBus->iBus != 0)
+ {
+ fSuppressMem = true;
+ if (f64Bit)
+ iRegion++; /* skip next region */
+ continue;
+ }
+
+ if (f64Bit)
+ {
+ devpciR3SetDWord(pPciDev, u32Address, UINT32_C(0xffffffff));
+ devpciR3SetDWord(pPciDev, u32Address+4, UINT32_C(0xffffffff));
+ cbRegSize64 = RT_MAKE_U64(devpciR3GetDWord(pPciDev, u32Address),
+ devpciR3GetDWord(pPciDev, u32Address+4));
+ cbRegSize64 &= ~UINT64_C(0x0f);
+ cbRegSize64 = (~cbRegSize64) + 1;
+
+ /* No 64-bit PIO regions possible. */
+#ifndef DEBUG_bird /* EFI triggers this for DevAHCI. */
+ AssertMsg((u8ResourceType & PCI_ADDRESS_SPACE_IO) == 0, ("type=%#x rgn=%d\n", u8ResourceType, iRegion));
+#endif
+ }
+ else
+ {
+ uint32_t cbRegSize32;
+ devpciR3SetDWord(pPciDev, u32Address, UINT32_C(0xffffffff));
+ cbRegSize32 = devpciR3GetDWord(pPciDev, u32Address);
+
+ /* Clear resource information depending on resource type. */
+ if (fIsPio) /* PIO */
+ cbRegSize32 &= ~UINT32_C(0x01);
+ else /* MMIO */
+ cbRegSize32 &= ~UINT32_C(0x0f);
+
+ /*
+ * Invert all bits and add 1 to get size of the region.
+ * (From PCI implementation note)
+ */
+ if (fIsPio && (cbRegSize32 & UINT32_C(0xffff0000)) == 0)
+ cbRegSize32 = (~(cbRegSize32 | UINT32_C(0xffff0000))) + 1;
+ else
+ cbRegSize32 = (~cbRegSize32) + 1;
+
+ cbRegSize64 = cbRegSize32;
+ }
+ Log2Func(("Size of region %u for device %d on bus %d is %lld\n", iRegion, pPciDev->uDevFn, pBus->iBus, cbRegSize64));
+
+ if (cbRegSize64)
+ {
+ /* Try 32-bit base first. */
+ uint32_t* paddr = fIsPio ? &pPciRoot->uPciBiosIo : &pPciRoot->uPciBiosMmio;
+ uint64_t uNew = *paddr;
+ /* Align starting address to region size. */
+ uNew = (uNew + cbRegSize64 - 1) & ~(cbRegSize64 - 1);
+ if (fIsPio)
+ uNew &= UINT32_C(0xffff);
+ /* Unconditionally exclude I/O-APIC/HPET/ROM. Pessimistic, but better than causing a mess. */
+ if ( !uNew
+ || (uNew <= UINT32_C(0xffffffff) && uNew + cbRegSize64 - 1 >= UINT32_C(0xfec00000))
+ || uNew >= _4G)
+ {
+ /* Only prefetchable regions can be placed above 4GB, as the
+ * address decoder for non-prefetchable addresses in bridges
+ * is limited to 32 bits. */
+ if (f64Bit && fPrefetch)
+ {
+ /* Map a 64-bit region above 4GB. */
+ Assert(!fIsPio);
+ uNew = pPciRoot->uPciBiosMmio64;
+ /* Align starting address to region size. */
+ uNew = (uNew + cbRegSize64 - 1) & ~(cbRegSize64 - 1);
+ LogFunc(("Start address of 64-bit MMIO region %u/%u is %#llx\n", iRegion, iRegion + 1, uNew));
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, iRegion, uNew);
+ fActiveMemRegion = true;
+ pPciRoot->uPciBiosMmio64 = uNew + cbRegSize64;
+ Log2Func(("New 64-bit address is %#llx\n", pPciRoot->uPciBiosMmio64));
+ }
+ else
+ {
+ uint16_t uVendor = devpciR3GetWord(pPciDev, VBOX_PCI_VENDOR_ID);
+ uint16_t uDevice = devpciR3GetWord(pPciDev, VBOX_PCI_DEVICE_ID);
+ LogRel(("PCI: no space left for BAR%u of device %u/%u/%u (vendor=%#06x device=%#06x)\n",
+ iRegion, pBus->iBus, pPciDev->uDevFn >> 3, pPciDev->uDevFn & 7, uVendor, uDevice)); /** @todo make this a VM start failure later. */
+ /* Undo the mapping mess caused by the size probing. */
+ devpciR3SetDWord(pPciDev, u32Address, UINT32_C(0));
+ }
+ }
+ else
+ {
+ LogFunc(("Start address of %s region %u is %#x\n", (fIsPio ? "I/O" : "MMIO"), iRegion, uNew));
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, iRegion, uNew);
+ if (fIsPio)
+ fActiveIORegion = true;
+ else
+ fActiveMemRegion = true;
+ *paddr = uNew + cbRegSize64;
+ Log2Func(("New 32-bit address is %#x\n", *paddr));
+ }
+
+ if (f64Bit)
+ iRegion++; /* skip next region */
+ }
+ }
+
+ /* Update the command word appropriately. */
+ uint16_t uCmd = devpciR3GetWord(pPciDev, VBOX_PCI_COMMAND);
+ if (fActiveMemRegion && !fSuppressMem)
+ uCmd |= VBOX_PCI_COMMAND_MEMORY; /* Enable MMIO access. */
+ if (fActiveIORegion)
+ uCmd |= VBOX_PCI_COMMAND_IO; /* Enable I/O space access. */
+ devpciR3SetWord(pPciDev, VBOX_PCI_COMMAND, uCmd);
+}
+
+static bool ich9pciBiosInitDevicePrefetchableBARs(PDEVPCIROOT pPciRoot, PDEVPCIBUS pBus, PPDMPCIDEV pPciDev, bool fUse64Bit, bool fDryrun)
+{
+ int cRegions = ich9pciBiosInitDeviceGetRegions(pPciDev);
+ bool fActiveMemRegion = false;
+ for (int iRegion = 0; iRegion < cRegions; iRegion++)
+ {
+ uint32_t u32Address = devpciGetRegionReg(iRegion);
+ uint8_t u8ResourceType = devpciR3GetByte(pPciDev, u32Address);
+ bool fPrefetch = (u8ResourceType & ((uint8_t)(PCI_ADDRESS_SPACE_MEM_PREFETCH | PCI_ADDRESS_SPACE_IO)))
+ == PCI_ADDRESS_SPACE_MEM_PREFETCH;
+ bool f64Bit = (u8ResourceType & ((uint8_t)(PCI_ADDRESS_SPACE_BAR64 | PCI_ADDRESS_SPACE_IO)))
+ == PCI_ADDRESS_SPACE_BAR64;
+ uint64_t cbRegSize64 = 0;
+
+ /* Everything besides prefetchable regions has been set up already. */
+ if (!fPrefetch)
+ continue;
+
+ if (f64Bit)
+ {
+ devpciR3SetDWord(pPciDev, u32Address, UINT32_C(0xffffffff));
+ devpciR3SetDWord(pPciDev, u32Address+4, UINT32_C(0xffffffff));
+ cbRegSize64 = RT_MAKE_U64(devpciR3GetDWord(pPciDev, u32Address),
+ devpciR3GetDWord(pPciDev, u32Address+4));
+ cbRegSize64 &= ~UINT64_C(0x0f);
+ cbRegSize64 = (~cbRegSize64) + 1;
+ }
+ else
+ {
+ uint32_t cbRegSize32;
+ devpciR3SetDWord(pPciDev, u32Address, UINT32_C(0xffffffff));
+ cbRegSize32 = devpciR3GetDWord(pPciDev, u32Address);
+ cbRegSize32 &= ~UINT32_C(0x0f);
+ cbRegSize32 = (~cbRegSize32) + 1;
+
+ cbRegSize64 = cbRegSize32;
+ }
+ Log2Func(("Size of region %u for device %d on bus %d is %lld\n", iRegion, pPciDev->uDevFn, pBus->iBus, cbRegSize64));
+
+ if (cbRegSize64)
+ {
+ uint64_t uNew;
+ if (!fUse64Bit)
+ {
+ uNew = pPciRoot->uPciBiosMmio;
+ /* Align starting address to region size. */
+ uNew = (uNew + cbRegSize64 - 1) & ~(cbRegSize64 - 1);
+ /* Unconditionally exclude I/O-APIC/HPET/ROM. Pessimistic, but better than causing a mess. Okay for BIOS. */
+ if ( !uNew
+ || (uNew <= UINT32_C(0xffffffff) && uNew + cbRegSize64 - 1 >= UINT32_C(0xfec00000))
+ || uNew >= _4G)
+ {
+ Log2Func(("region #%u: Rejecting address range: %#x LB %#RX64\n", iRegion, uNew, cbRegSize64));
+ Assert(fDryrun);
+ return true;
+ }
+ if (!fDryrun)
+ {
+ LogFunc(("Start address of MMIO region %u is %#x\n", iRegion, uNew));
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, iRegion, uNew);
+ fActiveMemRegion = true;
+ }
+ pPciRoot->uPciBiosMmio = uNew + cbRegSize64;
+ }
+ else
+ {
+ /* Can't handle 32-bit BARs when forcing 64-bit allocs. */
+ if (!f64Bit)
+ {
+ Assert(fDryrun);
+ return true;
+ }
+ uNew = pPciRoot->uPciBiosMmio64;
+ /* Align starting address to region size. */
+ uNew = (uNew + cbRegSize64 - 1) & ~(cbRegSize64 - 1);
+ pPciRoot->uPciBiosMmio64 = uNew + cbRegSize64;
+ if (!fDryrun)
+ {
+ LogFunc(("Start address of 64-bit MMIO region %u/%u is %#llx\n", iRegion, iRegion + 1, uNew));
+ devpciR3BiosInitSetRegionAddress(pBus, pPciDev, iRegion, uNew);
+ fActiveMemRegion = true;
+ }
+ }
+
+ if (f64Bit)
+ iRegion++; /* skip next region */
+ }
+ }
+
+ if (!fDryrun)
+ {
+ /* Update the command word appropriately. */
+ uint16_t uCmd = devpciR3GetWord(pPciDev, VBOX_PCI_COMMAND);
+ if (fActiveMemRegion)
+ uCmd |= VBOX_PCI_COMMAND_MEMORY; /* Enable MMIO access. */
+ devpciR3SetWord(pPciDev, VBOX_PCI_COMMAND, uCmd);
+ }
+ else
+ Assert(!fActiveMemRegion);
+
+ return false;
+}
+
+static bool ich9pciBiosInitBridgePrefetchable(PDEVPCIROOT pPciRoot, PDEVPCIBUS pBus, bool fUse64Bit, bool fDryrun)
+{
+ PPDMPCIDEV pBridge = &pBus->PciDev;
+ Log(("BIOS init bridge (prefetch): %02x:%02x.%d use64bit=%d dryrun=%d\n", pBus->iBus, pBridge->uDevFn >> 3, pBridge->uDevFn & 7, fUse64Bit, fDryrun));
+
+ pPciRoot->uPciBiosMmio = RT_ALIGN_32(pPciRoot->uPciBiosMmio, _1M);
+ pPciRoot->uPciBiosMmio64 = RT_ALIGN_64(pPciRoot->uPciBiosMmio64, _1M);
+
+ /* Save values to compare later to. */
+ uint32_t u32MMIOAddressBase = pPciRoot->uPciBiosMmio;
+ uint64_t u64MMIOAddressBase = pPciRoot->uPciBiosMmio64;
+
+ /* Init all devices behind the bridge (recursing to further buses). */
+ bool fRes = ich9pciBiosInitAllDevicesPrefetchableOnBus(pPciRoot, pBus, fUse64Bit, fDryrun);
+ if (fDryrun)
+ return fRes;
+ Assert(!fRes);
+
+ /* Set prefetchable MMIO limit register with 1MB boundary. */
+ uint64_t uBase, uLimit;
+ if (fUse64Bit)
+ {
+ if (u64MMIOAddressBase == pPciRoot->uPciBiosMmio64)
+ return false;
+ uBase = u64MMIOAddressBase;
+ uLimit = RT_ALIGN_64(pPciRoot->uPciBiosMmio64, _1M) - 1;
+ }
+ else
+ {
+ if (u32MMIOAddressBase == pPciRoot->uPciBiosMmio)
+ return false;
+ uBase = u32MMIOAddressBase;
+ uLimit = RT_ALIGN_32(pPciRoot->uPciBiosMmio, _1M) - 1;
+ }
+ devpciR3SetDWord(pBridge, VBOX_PCI_PREF_BASE_UPPER32, uBase >> 32);
+ devpciR3SetWord(pBridge, VBOX_PCI_PREF_MEMORY_BASE, (uint32_t)(uBase >> 16) & UINT32_C(0xfff0));
+ devpciR3SetDWord(pBridge, VBOX_PCI_PREF_LIMIT_UPPER32, uLimit >> 32);
+ devpciR3SetWord(pBridge, VBOX_PCI_PREF_MEMORY_LIMIT, (uint32_t)(uLimit >> 16) & UINT32_C(0xfff0));
+
+ return false;
+}
+
+static bool ich9pciBiosInitAllDevicesPrefetchableOnBus(PDEVPCIROOT pPciRoot, PDEVPCIBUS pBus, bool fUse64Bit, bool fDryrun)
+{
+ /* First pass: assign resources to all devices. */
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pPciDev = pBus->apDevices[uDevFn];
+
+ /* check if device is present */
+ if (!pPciDev)
+ continue;
+
+ Log(("BIOS init device (prefetch): %02x:%02x.%d\n", pBus->iBus, uDevFn >> 3, uDevFn & 7));
+
+ /* prefetchable memory mappings */
+ bool fRes = ich9pciBiosInitDevicePrefetchableBARs(pPciRoot, pBus, pPciDev, fUse64Bit, fDryrun);
+ if (fRes)
+ {
+ Assert(fDryrun);
+ return fRes;
+ }
+ }
+
+ /* Second pass: handle bridges recursively. */
+ for (uint32_t iBridge = 0; iBridge < pBus->cBridges; iBridge++)
+ {
+ PPDMPCIDEV pBridge = pBus->papBridgesR3[iBridge];
+ AssertMsg(pBridge && pciDevIsPci2PciBridge(pBridge),
+ ("Device is not a PCI bridge but on the list of PCI bridges\n"));
+ PDEVPCIBUS pChildBus = PDMINS_2_DATA(pBridge->Int.s.CTX_SUFF(pDevIns), PDEVPCIBUS);
+
+ bool fRes = ich9pciBiosInitBridgePrefetchable(pPciRoot, pChildBus, fUse64Bit, fDryrun);
+ if (fRes)
+ {
+ Assert(fDryrun);
+ return fRes;
+ }
+ }
+ return false;
+}
+
+static void ich9pciBiosInitAllDevicesOnBus(PDEVPCIROOT pPciRoot, PDEVPCIBUS pBus)
+{
+ /* First pass: assign resources to all devices and map the interrupt. */
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pPciDev = pBus->apDevices[uDevFn];
+
+ /* check if device is present */
+ if (!pPciDev)
+ continue;
+
+ Log(("BIOS init device: %02x:%02x.%d\n", pBus->iBus, uDevFn >> 3, uDevFn & 7));
+
+ /* default memory mappings */
+ ich9pciBiosInitDeviceBARs(pPciRoot, pBus, pPciDev);
+ uint16_t uDevClass = devpciR3GetWord(pPciDev, VBOX_PCI_CLASS_DEVICE);
+ switch (uDevClass)
+ {
+ case 0x0101:
+ /* IDE controller */
+ devpciR3SetWord(pPciDev, 0x40, 0x8000); /* enable IDE0 */
+ devpciR3SetWord(pPciDev, 0x42, 0x8000); /* enable IDE1 */
+ break;
+ case 0x0300:
+ {
+ /* VGA controller */
+
+ /* NB: Default Bochs VGA LFB address is 0xE0000000. Old guest
+ * software may break if the framebuffer isn't mapped there.
+ */
+
+ /*
+ * Legacy VGA I/O ports are implicitly decoded by a VGA class device. But
+ * only the framebuffer (i.e., a memory region) is explicitly registered via
+ * ich9pciSetRegionAddress, so don't forget to enable I/O decoding.
+ */
+ uint16_t uCmd = devpciR3GetWord(pPciDev, VBOX_PCI_COMMAND);
+ devpciR3SetWord(pPciDev, VBOX_PCI_COMMAND, uCmd | VBOX_PCI_COMMAND_IO);
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* map the interrupt */
+ uint8_t iPin = devpciR3GetByte(pPciDev, VBOX_PCI_INTERRUPT_PIN);
+ if (iPin != 0)
+ {
+ iPin--;
+
+ /* We need to go up to the host bus to see which irq pin this
+ * device will use there. See logic in ich9pcibridgeSetIrq().
+ */
+ for (PDEVPCIBUS pParent = pBus; pParent->iBus != 0; pParent = pParent->PciDev.Int.s.pBusR3)
+ {
+ /* Get the pin the device would assert on the bridge. */
+ iPin = ((pParent->PciDev.uDevFn >> 3) + iPin) & 3;
+ }
+
+ int iIrq = aPciIrqs[ich9pciSlotGetPirq(pBus->iBus, uDevFn, iPin)];
+ Log(("Using pin %d and IRQ %d for device %02x:%02x.%d\n",
+ iPin, iIrq, pBus->iBus, uDevFn>>3, uDevFn&7));
+ devpciR3SetByte(pPciDev, VBOX_PCI_INTERRUPT_LINE, iIrq);
+ }
+ }
+
+ /* Second pass: handle bridges recursively. */
+ for (uint32_t iBridge = 0; iBridge < pBus->cBridges; iBridge++)
+ {
+ PPDMPCIDEV pBridge = pBus->papBridgesR3[iBridge];
+ AssertMsg(pBridge && pciDevIsPci2PciBridge(pBridge),
+ ("Device is not a PCI bridge but on the list of PCI bridges\n"));
+ PDEVPCIBUS pChildBus = PDMINS_2_DATA(pBridge->Int.s.CTX_SUFF(pDevIns), PDEVPCIBUS);
+
+ ich9pciBiosInitBridge(pPciRoot, pChildBus);
+ }
+
+ /* Third pass (only for bus 0): set up prefetchable BARs recursively. */
+ if (pBus->iBus == 0)
+ {
+ for (uint32_t iBridge = 0; iBridge < pBus->cBridges; iBridge++)
+ {
+ PPDMPCIDEV pBridge = pBus->papBridgesR3[iBridge];
+ AssertMsg(pBridge && pciDevIsPci2PciBridge(pBridge),
+ ("Device is not a PCI bridge but on the list of PCI bridges\n"));
+ PDEVPCIBUS pChildBus = PDMINS_2_DATA(pBridge->Int.s.CTX_SUFF(pDevIns), PDEVPCIBUS);
+
+ Log(("BIOS init prefetchable memory behind bridge: %02x:%02x.%d\n", pChildBus->iBus, pBridge->uDevFn >> 3, pBridge->uDevFn & 7));
+ /* Save values for the prefetchable dryruns. */
+ uint32_t u32MMIOAddressBase = pPciRoot->uPciBiosMmio;
+ uint64_t u64MMIOAddressBase = pPciRoot->uPciBiosMmio64;
+
+ bool fProbe = ich9pciBiosInitBridgePrefetchable(pPciRoot, pChildBus, false /* fUse64Bit */, true /* fDryrun */);
+ pPciRoot->uPciBiosMmio = u32MMIOAddressBase;
+ pPciRoot->uPciBiosMmio64 = u64MMIOAddressBase;
+ if (fProbe)
+ {
+ fProbe = ich9pciBiosInitBridgePrefetchable(pPciRoot, pChildBus, true /* fUse64Bit */, true /* fDryrun */);
+ pPciRoot->uPciBiosMmio = u32MMIOAddressBase;
+ pPciRoot->uPciBiosMmio64 = u64MMIOAddressBase;
+ if (fProbe)
+ LogRel(("PCI: unresolvable prefetchable memory behind bridge %02x:%02x.%d\n", pChildBus->iBus, pBridge->uDevFn >> 3, pBridge->uDevFn & 7));
+ else
+ ich9pciBiosInitBridgePrefetchable(pPciRoot, pChildBus, true /* fUse64Bit */, false /* fDryrun */);
+ }
+ else
+ ich9pciBiosInitBridgePrefetchable(pPciRoot, pChildBus, false /* fUse64Bit */, false /* fDryrun */);
+ }
+ }
+}
+
+/**
+ * Initializes bridges registers used for routing.
+ *
+ * We ASSUME PDM bus assignments are the same as the PCI bus assignments and
+ * will complain if we find any conflicts. This because it is just soo much
+ * simpler to have the two numbers match one another by default.
+ *
+ * @returns Max subordinate bus number.
+ * @param pPciRoot Global device instance data used to generate unique bus numbers.
+ * @param pBus The PCI bus to initialize.
+ * @param pbmUsed Pointer to a 32-bit bitmap tracking which device
+ * (ranges) has been used.
+ * @param uBusPrimary The primary bus number the bus is connected to.
+ */
+static uint8_t ich9pciBiosInitBridgeTopology(PDEVPCIROOT pPciRoot, PDEVPCIBUS pBus, uint32_t *pbmUsed, uint8_t uBusPrimary)
+{
+ PPDMPCIDEV pBridgeDev = &pBus->PciDev;
+
+ /* Check if the PDM bus assignment makes sense. */
+ AssertLogRelMsg(!(*pbmUsed & RT_BIT_32(pBus->iBus)),
+ ("PCIBIOS: Bad PCI bridge config! Conflict for bus %#x. Make sure to instantiate bridges for a sub-trees in sequence!\n",
+ pBus->iBus));
+ *pbmUsed |= RT_BIT_32(pBus->iBus);
+
+ /* Set only if we are not on the root bus, it has no primary bus attached. */
+ if (pBus->iBus != 0)
+ {
+ devpciR3SetByte(pBridgeDev, VBOX_PCI_PRIMARY_BUS, uBusPrimary);
+ devpciR3SetByte(pBridgeDev, VBOX_PCI_SECONDARY_BUS, pBus->iBus);
+ /* Since the subordinate bus value can only be finalized once we
+ * finished recursing through everything behind the bridge, the only
+ * solution is temporarily configuring the subordinate to the maximum
+ * possible value. This makes sure that the config space accesses work
+ * (for our own sloppy emulation it apparently doesn't matter, but
+ * this is vital for real PCI bridges/devices in passthrough mode). */
+ devpciR3SetByte(pBridgeDev, VBOX_PCI_SUBORDINATE_BUS, 0xff);
+ }
+
+ uint8_t uMaxSubNum = pBus->iBus;
+ for (uint32_t iBridge = 0; iBridge < pBus->cBridges; iBridge++)
+ {
+ PPDMPCIDEV pBridge = pBus->papBridgesR3[iBridge];
+ AssertMsg(pBridge && pciDevIsPci2PciBridge(pBridge),
+ ("Device is not a PCI bridge but on the list of PCI bridges\n"));
+ PDEVPCIBUS pChildBus = PDMINS_2_DATA(pBridge->Int.s.CTX_SUFF(pDevIns), PDEVPCIBUS);
+ uint8_t uMaxChildSubBus = ich9pciBiosInitBridgeTopology(pPciRoot, pChildBus, pbmUsed, pBus->iBus);
+ uMaxSubNum = RT_MAX(uMaxSubNum, uMaxChildSubBus);
+ }
+
+ if (pBus->iBus != 0)
+ devpciR3SetByte(pBridgeDev, VBOX_PCI_SUBORDINATE_BUS, uMaxSubNum);
+ for (uint32_t i = pBus->iBus; i <= uMaxSubNum; i++)
+ *pbmUsed |= RT_BIT_32(i);
+
+ /* Make sure that transactions are able to get through the bridge. Not
+ * strictly speaking necessary this early (before any device is set up),
+ * but on the other hand it can't hurt either. */
+ if (pBus->iBus != 0)
+ devpciR3SetWord(pBridgeDev, VBOX_PCI_COMMAND,
+ VBOX_PCI_COMMAND_IO
+ | VBOX_PCI_COMMAND_MEMORY
+ | VBOX_PCI_COMMAND_MASTER);
+
+ /* safe, only needs to go to the config space array */
+ Log2Func(("for bus %p: primary=%d secondary=%d subordinate=%d\n",
+ pBus,
+ PDMPciDevGetByte(pBridgeDev, VBOX_PCI_PRIMARY_BUS),
+ PDMPciDevGetByte(pBridgeDev, VBOX_PCI_SECONDARY_BUS),
+ PDMPciDevGetByte(pBridgeDev, VBOX_PCI_SUBORDINATE_BUS)
+ ));
+
+ return uMaxSubNum;
+}
+
+
+/**
+ * Worker for Fake PCI BIOS config
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns ICH9 device instance.
+ */
+static int ich9pciFakePCIBIOS(PPDMDEVINS pDevIns)
+{
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PVM pVM = PDMDevHlpGetVM(pDevIns);
+ uint32_t const cbBelow4GB = MMR3PhysGetRamSizeBelow4GB(pVM);
+ uint64_t const cbAbove4GB = MMR3PhysGetRamSizeAbove4GB(pVM);
+
+ LogRel(("PCI: setting up topology, resources and interrupts\n"));
+
+ /** @todo r=klaus this needs to do the same elcr magic as DevPCI.cpp, as the BIOS can't be trusted to do the right thing. Of course it's more difficult than with the old code, as there are bridges to be handled. The interrupt routing needs to be taken into account. Also I highly suspect that the chipset has 8 interrupt lines which we might be able to use for handling things on the root bus better (by treating them as devices on the mainboard). */
+
+ /*
+ * Set the start addresses.
+ */
+ pPciRoot->uPciBiosBus = 0;
+ pPciRoot->uPciBiosIo = 0xd000;
+ pPciRoot->uPciBiosMmio = cbBelow4GB;
+ pPciRoot->uPciBiosMmio64 = cbAbove4GB + _4G;
+
+ /* NB: Assume that if PCI controller MMIO range is enabled, it is below the beginning of the memory hole. */
+ if (pPciRoot->u64PciConfigMMioAddress)
+ {
+ AssertRelease(pPciRoot->u64PciConfigMMioAddress >= cbBelow4GB);
+ pPciRoot->uPciBiosMmio = pPciRoot->u64PciConfigMMioAddress + pPciRoot->u64PciConfigMMioLength;
+ }
+ Log(("cbBelow4GB: %#RX32, uPciBiosMmio: %#RX64, cbAbove4GB: %#RX64, uPciBiosMmio64=%#RX64\n",
+ cbBelow4GB, pPciRoot->uPciBiosMmio, cbAbove4GB, pPciRoot->uPciBiosMmio64));
+
+ /*
+ * Assign bridge topology, for further routing to work.
+ */
+ PDEVPCIBUS pBus = &pPciRoot->PciBus;
+ AssertLogRel(pBus->iBus == 0);
+ uint32_t bmUsed = 0;
+ ich9pciBiosInitBridgeTopology(pPciRoot, pBus, &bmUsed, 0);
+
+ /*
+ * Init all devices on bus 0 (recursing to further buses).
+ */
+ ich9pciBiosInitAllDevicesOnBus(pPciRoot, pBus);
+
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=- PCI Config Space -=-=-=-=-=- */
+
+
+/**
+ * @callback_method_impl{PFNPCICONFIGREAD, Default config space read callback.}
+ */
+DECLCALLBACK(uint32_t) devpciR3CommonDefaultConfigRead(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t uAddress, unsigned cb)
+{
+ NOREF(pDevIns);
+
+ uint32_t uValue;
+ if (uAddress + cb <= 256)
+ {
+ switch (cb)
+ {
+ case 1:
+ /* safe, only needs to go to the config space array */
+ uValue = PDMPciDevGetByte(pPciDev, uAddress);
+ break;
+ case 2:
+ /* safe, only needs to go to the config space array */
+ uValue = PDMPciDevGetWord(pPciDev, uAddress);
+ break;
+ case 4:
+ /* safe, only needs to go to the config space array */
+ uValue = PDMPciDevGetDWord(pPciDev, uAddress);
+ break;
+ default:
+ AssertFailed();
+ uValue = 0;
+ break;
+ }
+
+#ifdef LOG_ENABLED
+ if ( pciDevIsMsiCapable(pPciDev)
+ && uAddress - (uint32_t)pPciDev->Int.s.u8MsiCapOffset < (uint32_t)pPciDev->Int.s.u8MsiCapSize )
+ Log2Func(("MSI CAP: %#x LB %u -> %#x\n", uAddress - (uint32_t)pPciDev->Int.s.u8MsiCapOffset, cb, uValue));
+ else if ( pciDevIsMsixCapable(pPciDev)
+ && uAddress - (uint32_t)pPciDev->Int.s.u8MsixCapOffset < (uint32_t)pPciDev->Int.s.u8MsixCapSize)
+ Log2Func(("MSI-X CAP: %#x LB %u -> %#x\n", uAddress - (uint32_t)pPciDev->Int.s.u8MsiCapOffset, cb, uValue));
+#endif
+ }
+ else
+ {
+ if (uAddress + cb < _4K)
+ LogRel(("PCI: %8s/%u: Read from extended register %d fallen back to generic code\n",
+ pPciDev->pszNameR3, pPciDev->Int.s.CTX_SUFF(pDevIns)->iInstance, uAddress));
+ else
+ AssertFailed();
+ uValue = 0;
+ }
+ return uValue;
+}
+
+
+/**
+ * Worker for devpciR3ResetDevice and devpciR3UpdateMappings that unmaps a region.
+ *
+ * @returns VBox status code.
+ * @param pDev The PCI device.
+ * @param iRegion The region to unmap.
+ */
+static int devpciR3UnmapRegion(PPDMPCIDEV pDev, int iRegion)
+{
+ PCIIORegion *pRegion = &pDev->Int.s.aIORegions[iRegion];
+ AssertReturn(pRegion->size != 0, VINF_SUCCESS);
+
+ int rc;
+ if (pRegion->addr == INVALID_PCI_ADDRESS)
+ rc = VINF_SUCCESS;
+ else
+ {
+ if (pRegion->type & PCI_ADDRESS_SPACE_IO)
+ {
+ /* Port IO */
+ rc = PDMDevHlpIOPortDeregister(pDev->Int.s.pDevInsR3, pRegion->addr, pRegion->size);
+ AssertRC(rc);
+ }
+ else
+ {
+ PDEVPCIBUS pBus = pDev->Int.s.CTX_SUFF(pBus);
+ RTGCPHYS GCPhysBase = pRegion->addr;
+ if (pBus->pPciHlpR3->pfnIsMMIOExBase(pBus->pDevInsR3, pDev->Int.s.pDevInsR3, GCPhysBase))
+ {
+ /* unmap it. */
+ rc = pRegion->map_func(pDev->Int.s.pDevInsR3, pDev, iRegion,
+ NIL_RTGCPHYS, pRegion->size, (PCIADDRESSSPACE)(pRegion->type));
+ AssertRC(rc);
+ rc = PDMDevHlpMMIOExUnmap(pDev->Int.s.pDevInsR3, pDev, iRegion, GCPhysBase);
+ }
+ else
+ rc = PDMDevHlpMMIODeregister(pDev->Int.s.pDevInsR3, GCPhysBase, pRegion->size);
+ AssertRC(rc);
+ }
+ pRegion->addr = INVALID_PCI_ADDRESS;
+ }
+ return rc;
+}
+
+
+/**
+ * Worker for devpciR3CommonDefaultConfigWrite that updates BAR and ROM mappings.
+ *
+ * @returns VINF_SUCCESS of DBGFSTOP result.
+ * @param pPciDev The PCI device to update the mappings for.
+ * @param fP2PBridge Whether this is a PCI to PCI bridge or not.
+ */
+static VBOXSTRICTRC devpciR3UpdateMappings(PPDMPCIDEV pPciDev, bool fP2PBridge)
+{
+ /* safe, only needs to go to the config space array */
+ uint16_t const u16Cmd = PDMPciDevGetWord(pPciDev, VBOX_PCI_COMMAND);
+ Log4(("devpciR3UpdateMappings: dev %u/%u (%s): u16Cmd=%#x\n",
+ pPciDev->uDevFn >> VBOX_PCI_DEVFN_DEV_SHIFT, pPciDev->uDevFn & VBOX_PCI_DEVFN_FUN_MASK, pPciDev->pszNameR3, u16Cmd));
+ for (unsigned iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
+ {
+ /* Skip over BAR2..BAR5 for bridges, as they have a different meaning there. */
+ if (fP2PBridge && iRegion >= 2 && iRegion <= 5)
+ continue;
+ PCIIORegion *pRegion = &pPciDev->Int.s.aIORegions[iRegion];
+ uint64_t const cbRegion = pRegion->size;
+ if (cbRegion != 0)
+ {
+ uint32_t const offCfgReg = devpciGetRegionReg(iRegion);
+ bool const f64Bit = (pRegion->type & ((uint8_t)(PCI_ADDRESS_SPACE_BAR64 | PCI_ADDRESS_SPACE_IO)))
+ == PCI_ADDRESS_SPACE_BAR64;
+ uint64_t uNew = INVALID_PCI_ADDRESS;
+
+ /*
+ * Port I/O region. Check if mapped and within 1..65535 range.
+ */
+ if (pRegion->type & PCI_ADDRESS_SPACE_IO)
+ {
+ if (u16Cmd & VBOX_PCI_COMMAND_IO)
+ {
+ /* safe, only needs to go to the config space array */
+ uint32_t uIoBase = PDMPciDevGetDWord(pPciDev, offCfgReg);
+ uIoBase &= ~(uint32_t)(cbRegion - 1);
+
+ uint64_t uLast = cbRegion - 1 + uIoBase;
+ if ( uLast < _64K
+ && uIoBase < uLast
+ && uIoBase > 0)
+ uNew = uIoBase;
+ else
+ Log4(("devpciR3UpdateMappings: dev %u/%u (%s): region #%u: Disregarding invalid I/O port range: %#RX32..%#RX64\n",
+ pPciDev->uDevFn >> VBOX_PCI_DEVFN_DEV_SHIFT, pPciDev->uDevFn & VBOX_PCI_DEVFN_FUN_MASK,
+ pPciDev->pszNameR3, iRegion, uIoBase, uLast));
+ }
+ }
+ /*
+ * MMIO or ROM. Check ROM enable bit and range.
+ *
+ * Note! We exclude the I/O-APIC/HPET/ROM area at the end of the first 4GB to
+ * prevent the (fake) PCI BIOS and others from making a mess. Pure paranoia.
+ * Additionally addresses with the top 32 bits all set are excluded, to
+ * catch silly OSes which probe 64-bit BARs without disabling the
+ * corresponding transactions.
+ *
+ * Update: The pure paranoia above broke NT 3.51, so it was changed to only
+ * exclude the 64KB BIOS mapping at the top. NT 3.51 excludes the
+ * top 256KB, btw.
+ */
+ /** @todo Query upper boundrary from CPUM and PGMPhysRom instead of making
+ * incorrect assumptions. */
+ else if (u16Cmd & VBOX_PCI_COMMAND_MEMORY)
+ {
+ /* safe, only needs to go to the config space array */
+ uint64_t uMemBase = PDMPciDevGetDWord(pPciDev, offCfgReg);
+ if (f64Bit)
+ {
+ Assert(iRegion < VBOX_PCI_ROM_SLOT);
+ /* safe, only needs to go to the config space array */
+ uMemBase |= (uint64_t)PDMPciDevGetDWord(pPciDev, offCfgReg + 4) << 32;
+ }
+ if ( iRegion != PCI_ROM_SLOT
+ || (uMemBase & RT_BIT_32(0))) /* ROM enable bit. */
+ {
+ uMemBase &= ~(cbRegion - 1);
+
+ uint64_t uLast = uMemBase + cbRegion - 1;
+ if ( uMemBase < uLast
+ && uMemBase > 0)
+ {
+ if ( ( uMemBase > UINT32_C(0xffffffff)
+ || uLast < UINT32_C(0xffff0000) ) /* UINT32_C(0xfec00000) - breaks NT3.51! */
+ && uMemBase < UINT64_C(0xffffffff00000000) )
+ uNew = uMemBase;
+ else
+ Log(("devpciR3UpdateMappings: dev %u/%u (%s): region #%u: Rejecting address range: %#RX64..%#RX64!\n",
+ pPciDev->uDevFn >> VBOX_PCI_DEVFN_DEV_SHIFT, pPciDev->uDevFn & VBOX_PCI_DEVFN_FUN_MASK,
+ pPciDev->pszNameR3, iRegion, uMemBase, uLast));
+ }
+ else
+ Log2(("devpciR3UpdateMappings: dev %u/%u (%s): region #%u: Disregarding invalid address range: %#RX64..%#RX64\n",
+ pPciDev->uDevFn >> VBOX_PCI_DEVFN_DEV_SHIFT, pPciDev->uDevFn & VBOX_PCI_DEVFN_FUN_MASK,
+ pPciDev->pszNameR3, iRegion, uMemBase, uLast));
+ }
+ }
+
+ /*
+ * Do real unmapping and/or mapping if the address change.
+ */
+ Log4(("devpciR3UpdateMappings: dev %u/%u (%s): iRegion=%u addr=%#RX64 uNew=%#RX64\n",
+ pPciDev->uDevFn >> VBOX_PCI_DEVFN_DEV_SHIFT, pPciDev->uDevFn & VBOX_PCI_DEVFN_FUN_MASK, pPciDev->pszNameR3,
+ iRegion, pRegion->addr, uNew));
+ if (uNew != pRegion->addr)
+ {
+ LogRel2(("PCI: config dev %u/%u (%s) BAR%i: %#RX64 -> %#RX64 (LB %RX64 (%RU64))\n",
+ pPciDev->uDevFn >> VBOX_PCI_DEVFN_DEV_SHIFT, pPciDev->uDevFn & VBOX_PCI_DEVFN_FUN_MASK,
+ pPciDev->pszNameR3, iRegion, pRegion->addr, uNew, cbRegion, cbRegion));
+
+ devpciR3UnmapRegion(pPciDev, iRegion);
+ pRegion->addr = uNew;
+ if (uNew != INVALID_PCI_ADDRESS)
+ {
+ int rc = pRegion->map_func(pPciDev->Int.s.pDevInsR3, pPciDev, iRegion, uNew, cbRegion,
+ (PCIADDRESSSPACE)(pRegion->type));
+ AssertRC(rc);
+ }
+ }
+
+ if (f64Bit)
+ iRegion++;
+ }
+ /* else: size == 0: unused region */
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for devpciR3CommonDefaultConfigWrite that write a byte to a BAR.
+ *
+ * @param pPciDev The PCI device.
+ * @param iRegion The region.
+ * @param off The BAR offset.
+ * @param bVal The byte to write.
+ */
+DECLINLINE(void) devpciR3WriteBarByte(PPDMPCIDEV pPciDev, uint32_t iRegion, uint32_t off, uint8_t bVal)
+{
+ PCIIORegion *pRegion = &pPciDev->Int.s.aIORegions[iRegion];
+ Log3Func(("region=%d off=%d val=%#x size=%#llx\n", iRegion, off, bVal, pRegion->size));
+ Assert(off <= 3);
+
+ /* Check if we're writing to upper part of 64-bit BAR. */
+ if (pRegion->type == 0xff)
+ {
+ AssertLogRelReturnVoid(iRegion > 0 && iRegion < VBOX_PCI_ROM_SLOT);
+ pRegion--;
+ iRegion--;
+ off += 4;
+ Assert(pRegion->type & PCI_ADDRESS_SPACE_BAR64);
+ }
+
+ /* Ignore zero sized regions (they don't exist). */
+ if (pRegion->size != 0)
+ {
+ uint32_t uAddr = devpciGetRegionReg(iRegion) + off;
+ Assert((pRegion->size & (pRegion->size - 1)) == 0); /* Region size must be power of two. */
+ uint8_t bMask = ( (pRegion->size - 1) >> (off * 8) ) & 0xff;
+ if (off == 0)
+ bMask |= (pRegion->type & PCI_ADDRESS_SPACE_IO)
+ ? (1 << 2) - 1 /* 2 lowest bits for IO region */ :
+ (1 << 4) - 1 /* 4 lowest bits for memory region, also ROM enable bit for ROM region */;
+
+ /* safe, only needs to go to the config space array */
+ uint8_t bOld = PDMPciDevGetByte(pPciDev, uAddr) & bMask;
+ bVal = (bOld & bMask) | (bVal & ~bMask);
+
+ Log3Func(("%x changed to %x\n", bOld, bVal));
+
+ /* safe, only needs to go to the config space array */
+ PDMPciDevSetByte(pPciDev, uAddr, bVal);
+ }
+}
+
+
+/**
+ * Checks if the given configuration byte is writable.
+ *
+ * @returns true if writable, false if not
+ * @param uAddress The config space byte byte.
+ * @param bHeaderType The device header byte.
+ */
+DECLINLINE(bool) devpciR3IsConfigByteWritable(uint32_t uAddress, uint8_t bHeaderType)
+{
+ switch (bHeaderType)
+ {
+ case 0x00: /* normal device */
+ case 0x80: /* multi-function device */
+ switch (uAddress)
+ {
+ /* Read-only registers. */
+ case VBOX_PCI_VENDOR_ID:
+ case VBOX_PCI_VENDOR_ID+1:
+ case VBOX_PCI_DEVICE_ID:
+ case VBOX_PCI_DEVICE_ID+1:
+ case VBOX_PCI_REVISION_ID:
+ case VBOX_PCI_CLASS_PROG:
+ case VBOX_PCI_CLASS_SUB:
+ case VBOX_PCI_CLASS_BASE:
+ case VBOX_PCI_HEADER_TYPE:
+ case VBOX_PCI_SUBSYSTEM_VENDOR_ID:
+ case VBOX_PCI_SUBSYSTEM_VENDOR_ID+1:
+ case VBOX_PCI_SUBSYSTEM_ID:
+ case VBOX_PCI_SUBSYSTEM_ID+1:
+ case VBOX_PCI_ROM_ADDRESS:
+ case VBOX_PCI_ROM_ADDRESS+1:
+ case VBOX_PCI_ROM_ADDRESS+2:
+ case VBOX_PCI_ROM_ADDRESS+3:
+ case VBOX_PCI_CAPABILITY_LIST:
+ case VBOX_PCI_INTERRUPT_PIN:
+ return false;
+ /* Other registers can be written. */
+ default:
+ return true;
+ }
+ break;
+ case 0x01: /* PCI-PCI bridge */
+ switch (uAddress)
+ {
+ /* Read-only registers. */
+ case VBOX_PCI_VENDOR_ID:
+ case VBOX_PCI_VENDOR_ID+1:
+ case VBOX_PCI_DEVICE_ID:
+ case VBOX_PCI_DEVICE_ID+1:
+ case VBOX_PCI_REVISION_ID:
+ case VBOX_PCI_CLASS_PROG:
+ case VBOX_PCI_CLASS_SUB:
+ case VBOX_PCI_CLASS_BASE:
+ case VBOX_PCI_HEADER_TYPE:
+ case VBOX_PCI_ROM_ADDRESS_BR:
+ case VBOX_PCI_ROM_ADDRESS_BR+1:
+ case VBOX_PCI_ROM_ADDRESS_BR+2:
+ case VBOX_PCI_ROM_ADDRESS_BR+3:
+ case VBOX_PCI_INTERRUPT_PIN:
+ return false;
+ /* Other registers can be written. */
+ default:
+ return true;
+ }
+ break;
+ default:
+ AssertMsgFailed(("Unknown header type %#x\n", bHeaderType));
+ return false;
+ }
+}
+
+
+/**
+ * @callback_method_impl{PFNPCICONFIGWRITE,
+ * Default config space write callback.}
+ *
+ * See paragraph 7.5 of PCI Express specification (p. 349) for
+ * definition of registers and their writability policy.
+ */
+DECLCALLBACK(VBOXSTRICTRC) devpciR3CommonDefaultConfigWrite(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev,
+ uint32_t uAddress, uint32_t u32Value, unsigned cb)
+{
+ NOREF(pDevIns);
+ Assert(cb <= 4);
+ VBOXSTRICTRC rcRet = VINF_SUCCESS;
+
+ if (uAddress + cb <= 256)
+ {
+ /*
+ * MSI and MSI-X capabilites needs to be handled separately.
+ */
+ if ( pciDevIsMsiCapable(pPciDev)
+ && uAddress - (uint32_t)pPciDev->Int.s.u8MsiCapOffset < (uint32_t)pPciDev->Int.s.u8MsiCapSize)
+ MsiR3PciConfigWrite(pPciDev->Int.s.CTX_SUFF(pBus)->CTX_SUFF(pDevIns),
+ pPciDev->Int.s.CTX_SUFF(pBus)->CTX_SUFF(pPciHlp),
+ pPciDev, uAddress, u32Value, cb);
+ else if ( pciDevIsMsixCapable(pPciDev)
+ && uAddress - (uint32_t)pPciDev->Int.s.u8MsixCapOffset < (uint32_t)pPciDev->Int.s.u8MsixCapSize)
+ MsixR3PciConfigWrite(pPciDev->Int.s.CTX_SUFF(pBus)->CTX_SUFF(pDevIns),
+ pPciDev->Int.s.CTX_SUFF(pBus)->CTX_SUFF(pPciHlp),
+ pPciDev, uAddress, u32Value, cb);
+ else
+ {
+ /*
+ * Handle the writes byte-by-byte to catch all possible cases.
+ *
+ * Note! Real hardware may not necessarily handle non-dword writes like
+ * we do here and even produce erratic behavior. We don't (yet)
+ * try emulate that.
+ */
+ uint8_t const bHeaderType = devpciR3GetByte(pPciDev, VBOX_PCI_HEADER_TYPE);
+ bool const fP2PBridge = bHeaderType == 0x01; /* PCI-PCI bridge */
+ bool fUpdateMappings = false;
+ while (cb-- > 0)
+ {
+ bool fWritable = devpciR3IsConfigByteWritable(uAddress, bHeaderType);
+ uint8_t bVal = (uint8_t)u32Value;
+ bool fRom = false;
+ switch (uAddress)
+ {
+ case VBOX_PCI_COMMAND: /* Command register, bits 0-7. */
+ if (fWritable)
+ {
+ /* safe, only needs to go to the config space array */
+ PDMPciDevSetByte(pPciDev, uAddress, bVal);
+ fUpdateMappings = true;
+ }
+ break;
+
+ case VBOX_PCI_COMMAND+1: /* Command register, bits 8-15. */
+ if (fWritable)
+ {
+ /* don't change reserved bits (11-15) */
+ bVal &= ~UINT8_C(0xf8);
+ /* safe, only needs to go to the config space array */
+ PDMPciDevSetByte(pPciDev, uAddress, bVal);
+ fUpdateMappings = true;
+ }
+ break;
+
+ case VBOX_PCI_STATUS: /* Status register, bits 0-7. */
+ /* don't change read-only bits => actually all lower bits are read-only */
+ bVal &= ~UINT8_C(0xff);
+ /* status register, low part: clear bits by writing a '1' to the corresponding bit */
+ pPciDev->abConfig[uAddress] &= ~bVal;
+ break;
+
+ case VBOX_PCI_STATUS+1: /* Status register, bits 8-15. */
+ /* don't change read-only bits */
+ bVal &= ~UINT8_C(0x06);
+ /* status register, high part: clear bits by writing a '1' to the corresponding bit */
+ pPciDev->abConfig[uAddress] &= ~bVal;
+ break;
+
+ case VBOX_PCI_ROM_ADDRESS: case VBOX_PCI_ROM_ADDRESS +1: case VBOX_PCI_ROM_ADDRESS +2: case VBOX_PCI_ROM_ADDRESS +3:
+ fRom = true;
+ RT_FALL_THRU();
+ case VBOX_PCI_BASE_ADDRESS_0: case VBOX_PCI_BASE_ADDRESS_0+1: case VBOX_PCI_BASE_ADDRESS_0+2: case VBOX_PCI_BASE_ADDRESS_0+3:
+ case VBOX_PCI_BASE_ADDRESS_1: case VBOX_PCI_BASE_ADDRESS_1+1: case VBOX_PCI_BASE_ADDRESS_1+2: case VBOX_PCI_BASE_ADDRESS_1+3:
+ case VBOX_PCI_BASE_ADDRESS_2: case VBOX_PCI_BASE_ADDRESS_2+1: case VBOX_PCI_BASE_ADDRESS_2+2: case VBOX_PCI_BASE_ADDRESS_2+3:
+ case VBOX_PCI_BASE_ADDRESS_3: case VBOX_PCI_BASE_ADDRESS_3+1: case VBOX_PCI_BASE_ADDRESS_3+2: case VBOX_PCI_BASE_ADDRESS_3+3:
+ case VBOX_PCI_BASE_ADDRESS_4: case VBOX_PCI_BASE_ADDRESS_4+1: case VBOX_PCI_BASE_ADDRESS_4+2: case VBOX_PCI_BASE_ADDRESS_4+3:
+ case VBOX_PCI_BASE_ADDRESS_5: case VBOX_PCI_BASE_ADDRESS_5+1: case VBOX_PCI_BASE_ADDRESS_5+2: case VBOX_PCI_BASE_ADDRESS_5+3:
+ /* We check that, as same PCI register numbers as BARs may mean different registers for bridges */
+ if (!fP2PBridge)
+ {
+ uint32_t iRegion = fRom ? VBOX_PCI_ROM_SLOT : (uAddress - VBOX_PCI_BASE_ADDRESS_0) >> 2;
+ devpciR3WriteBarByte(pPciDev, iRegion, uAddress & 0x3, bVal);
+ fUpdateMappings = true;
+ break;
+ }
+ else if (uAddress < VBOX_PCI_BASE_ADDRESS_2 || uAddress > VBOX_PCI_BASE_ADDRESS_5+3)
+ {
+ /* PCI bridges have only BAR0, BAR1 and ROM */
+ uint32_t iRegion = fRom ? VBOX_PCI_ROM_SLOT : (uAddress - VBOX_PCI_BASE_ADDRESS_0) >> 2;
+ devpciR3WriteBarByte(pPciDev, iRegion, uAddress & 0x3, bVal);
+ fUpdateMappings = true;
+ break;
+ }
+ else if ( uAddress == VBOX_PCI_IO_BASE
+ || uAddress == VBOX_PCI_IO_LIMIT
+ || uAddress == VBOX_PCI_MEMORY_BASE
+ || uAddress == VBOX_PCI_MEMORY_LIMIT
+ || uAddress == VBOX_PCI_PREF_MEMORY_BASE
+ || uAddress == VBOX_PCI_PREF_MEMORY_LIMIT)
+ {
+ /* All bridge address decoders have the low 4 bits
+ * as readonly, and all but the prefetchable ones
+ * have the low 4 bits as 0 (the prefetchable have
+ * it as 1 to show the 64-bit decoder support. */
+ bVal &= 0xf0;
+ if ( uAddress == VBOX_PCI_PREF_MEMORY_BASE
+ || uAddress == VBOX_PCI_PREF_MEMORY_LIMIT)
+ bVal |= 0x01;
+ }
+ /* (bridge config space which isn't a BAR) */
+ RT_FALL_THRU();
+ default:
+ if (fWritable)
+ /* safe, only needs to go to the config space array */
+ PDMPciDevSetByte(pPciDev, uAddress, bVal);
+ break;
+ }
+ uAddress++;
+ u32Value >>= 8;
+ }
+
+ /*
+ * Update the region mappings if anything changed related to them (command, BARs, ROM).
+ */
+ if (fUpdateMappings)
+ rcRet = devpciR3UpdateMappings(pPciDev, fP2PBridge);
+ }
+ }
+ else if (uAddress + cb <= _4K)
+ LogRel(("PCI: %8s/%u: Write to extended register %d fallen back to generic code\n",
+ pPciDev->pszNameR3, pPciDev->Int.s.CTX_SUFF(pDevIns)->iInstance, uAddress));
+ else
+ AssertMsgFailed(("Write after end of PCI config space\n"));
+
+ return rcRet;
+}
+
+
+/* -=-=-=-=-=- Debug Info Handlers -=-=-=-=-=- */
+
+/**
+ * Indents an info line.
+ * @param pHlp The info helper.
+ * @param iIndentLvl The desired indentation level.
+ */
+static void devpciR3InfoIndent(PCDBGFINFOHLP pHlp, unsigned iIndentLvl)
+{
+ for (unsigned i = 0; i < iIndentLvl; i++)
+ pHlp->pfnPrintf(pHlp, " ");
+}
+
+static const char *devpciR3InInfoPciBusClassName(uint8_t iBaseClass)
+{
+ static const char *s_szBaseClass[] =
+ {
+ /* 00h */ "unknown",
+ /* 01h */ "mass storage controller",
+ /* 02h */ "network controller",
+ /* 03h */ "display controller",
+ /* 04h */ "multimedia controller",
+ /* 05h */ "memory controller",
+ /* 06h */ "bridge device",
+ /* 07h */ "simple communication controllers",
+ /* 08h */ "base system peripherals",
+ /* 09h */ "input devices",
+ /* 0Ah */ "docking stations",
+ /* 0Bh */ "processors",
+ /* 0Ch */ "serial bus controllers",
+ /* 0Dh */ "wireless controller",
+ /* 0Eh */ "intelligent I/O controllers",
+ /* 0Fh */ "satellite communication controllers",
+ /* 10h */ "encryption/decryption controllers",
+ /* 11h */ "data acquisition and signal processing controllers"
+ };
+ if (iBaseClass < RT_ELEMENTS(s_szBaseClass))
+ return s_szBaseClass[iBaseClass];
+ if (iBaseClass < 0xFF)
+ return "reserved";
+ return "device does not fit in any defined classes";
+}
+
+
+/**
+ * Recursive worker for devpciR3InfoPci.
+ *
+ * @param pBus The bus to show info for.
+ * @param pHlp The info helpers.
+ * @param iIndentLvl The indentation level.
+ * @param fRegisters Whether to show device registers or not.
+ */
+static void devpciR3InfoPciBus(PDEVPCIBUS pBus, PCDBGFINFOHLP pHlp, unsigned iIndentLvl, bool fRegisters)
+{
+ /* This has to use the callbacks for accuracy reasons. Otherwise it can get
+ * confusing in the passthrough case or when the callbacks for some device
+ * are doing something non-trivial (like implementing an indirect
+ * passthrough approach), because then the abConfig array is an imprecise
+ * cache needed for efficiency (so that certain reads can be done from
+ * R0/RC), but far from authoritative or what the guest would see. */
+
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pPciDev = pBus->apDevices[uDevFn];
+ if (pPciDev != NULL)
+ {
+ devpciR3InfoIndent(pHlp, iIndentLvl);
+
+ /*
+ * For passthrough devices MSI/MSI-X mostly reflects the way interrupts delivered to the guest,
+ * as host driver handles real devices interrupts.
+ */
+ pHlp->pfnPrintf(pHlp, "%02x:%02x.%d %s%s: %04x-%04x %s%s%s",
+ pBus->iBus, (uDevFn >> 3) & 0xff, uDevFn & 0x7,
+ pPciDev->pszNameR3,
+ pciDevIsPassthrough(pPciDev) ? " (PASSTHROUGH)" : "",
+ devpciR3GetWord(pPciDev, VBOX_PCI_VENDOR_ID), devpciR3GetWord(pPciDev, VBOX_PCI_DEVICE_ID),
+ pBus->fTypeIch9 ? "ICH9" : pBus->fTypePiix3 ? "PIIX3" : "?type?",
+ pciDevIsMsiCapable(pPciDev) ? " MSI" : "",
+ pciDevIsMsixCapable(pPciDev) ? " MSI-X" : ""
+ );
+ if (devpciR3GetByte(pPciDev, VBOX_PCI_INTERRUPT_PIN) != 0)
+ {
+ pHlp->pfnPrintf(pHlp, " IRQ%d", devpciR3GetByte(pPciDev, VBOX_PCI_INTERRUPT_LINE));
+ pHlp->pfnPrintf(pHlp, " (INTA#->IRQ%d)", 0x10 + ich9pciSlot2ApicIrq(uDevFn >> 3, 0));
+ }
+ pHlp->pfnPrintf(pHlp, "\n");
+ devpciR3InfoIndent(pHlp, iIndentLvl + 2);
+ uint8_t uClassBase = devpciR3GetByte(pPciDev, VBOX_PCI_CLASS_BASE);
+ uint8_t uClassSub = devpciR3GetByte(pPciDev, VBOX_PCI_CLASS_SUB);
+ pHlp->pfnPrintf(pHlp, "Class base/sub: %02x%02x (%s)\n",
+ uClassBase, uClassSub, devpciR3InInfoPciBusClassName(uClassBase));
+
+ if (pciDevIsMsiCapable(pPciDev) || pciDevIsMsixCapable(pPciDev))
+ {
+ devpciR3InfoIndent(pHlp, iIndentLvl + 2);
+
+ if (pciDevIsMsiCapable(pPciDev))
+ pHlp->pfnPrintf(pHlp, "MSI: %s ", MsiIsEnabled(pPciDev) ? "on" : "off");
+
+ if (pciDevIsMsixCapable(pPciDev))
+ pHlp->pfnPrintf(pHlp, "MSI-X: %s ", MsixIsEnabled(pPciDev) ? "on" : "off");
+
+ pHlp->pfnPrintf(pHlp, "\n");
+ }
+
+ for (unsigned iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
+ {
+ PCIIORegion const *pRegion = &pPciDev->Int.s.aIORegions[iRegion];
+ uint64_t const cbRegion = pRegion->size;
+
+ if (cbRegion == 0)
+ continue;
+
+ uint32_t uAddr = devpciR3GetDWord(pPciDev, devpciGetRegionReg(iRegion));
+ const char * pszDesc;
+ char szDescBuf[128];
+
+ bool f64Bit = (pRegion->type & ((uint8_t)(PCI_ADDRESS_SPACE_BAR64 | PCI_ADDRESS_SPACE_IO)))
+ == PCI_ADDRESS_SPACE_BAR64;
+ if (pRegion->type & PCI_ADDRESS_SPACE_IO)
+ {
+ pszDesc = "IO";
+ uAddr &= ~0x3;
+ }
+ else
+ {
+ RTStrPrintf(szDescBuf, sizeof(szDescBuf), "MMIO%s%s",
+ f64Bit ? "64" : "32",
+ pRegion->type & PCI_ADDRESS_SPACE_MEM_PREFETCH ? " PREFETCH" : "");
+ pszDesc = szDescBuf;
+ uAddr &= ~0xf;
+ }
+
+ devpciR3InfoIndent(pHlp, iIndentLvl + 2);
+ pHlp->pfnPrintf(pHlp, "%s region #%u: ", pszDesc, iRegion);
+ if (f64Bit)
+ {
+ uint32_t u32High = devpciR3GetDWord(pPciDev, devpciGetRegionReg(iRegion+1));
+ uint64_t u64Addr = RT_MAKE_U64(uAddr, u32High);
+ pHlp->pfnPrintf(pHlp, "%RX64..%RX64\n", u64Addr, u64Addr + cbRegion - 1);
+ iRegion++;
+ }
+ else
+ pHlp->pfnPrintf(pHlp, "%x..%x\n", uAddr, uAddr + (uint32_t)cbRegion - 1);
+ }
+
+ devpciR3InfoIndent(pHlp, iIndentLvl + 2);
+ uint16_t iCmd = devpciR3GetWord(pPciDev, VBOX_PCI_COMMAND);
+ uint16_t iStatus = devpciR3GetWord(pPciDev, VBOX_PCI_STATUS);
+ pHlp->pfnPrintf(pHlp, "Command: %04x, Status: %04x\n", iCmd, iStatus);
+ devpciR3InfoIndent(pHlp, iIndentLvl + 2);
+ pHlp->pfnPrintf(pHlp, "Bus master: %s\n", iCmd & VBOX_PCI_COMMAND_MASTER ? "Yes" : "No");
+ if (iCmd != PDMPciDevGetCommand(pPciDev))
+ {
+ devpciR3InfoIndent(pHlp, iIndentLvl + 2);
+ pHlp->pfnPrintf(pHlp, "CACHE INCONSISTENCY: Command: %04x\n", PDMPciDevGetCommand(pPciDev));
+ }
+
+ if (fRegisters)
+ {
+ devpciR3InfoIndent(pHlp, iIndentLvl + 2);
+ pHlp->pfnPrintf(pHlp, "PCI registers:\n");
+ for (unsigned iReg = 0; iReg < 0x100; )
+ {
+ unsigned iPerLine = 0x10;
+ Assert(0x100 % iPerLine == 0);
+ devpciR3InfoIndent(pHlp, iIndentLvl + 3);
+
+ while (iPerLine-- > 0)
+ pHlp->pfnPrintf(pHlp, "%02x ", devpciR3GetByte(pPciDev, iReg++));
+ pHlp->pfnPrintf(pHlp, "\n");
+ }
+ }
+ }
+ }
+
+ if (pBus->cBridges > 0)
+ {
+ devpciR3InfoIndent(pHlp, iIndentLvl);
+ pHlp->pfnPrintf(pHlp, "Registered %d bridges, subordinate buses info follows\n", pBus->cBridges);
+ for (uint32_t iBridge = 0; iBridge < pBus->cBridges; iBridge++)
+ {
+ PDEVPCIBUS pBusSub = PDMINS_2_DATA(pBus->papBridgesR3[iBridge]->Int.s.CTX_SUFF(pDevIns), PDEVPCIBUS);
+ uint8_t uPrimary = devpciR3GetByte(&pBusSub->PciDev, VBOX_PCI_PRIMARY_BUS);
+ uint8_t uSecondary = devpciR3GetByte(&pBusSub->PciDev, VBOX_PCI_SECONDARY_BUS);
+ uint8_t uSubordinate = devpciR3GetByte(&pBusSub->PciDev, VBOX_PCI_SUBORDINATE_BUS);
+ devpciR3InfoIndent(pHlp, iIndentLvl);
+ pHlp->pfnPrintf(pHlp, "%02x:%02x.%d: bridge topology: primary=%d secondary=%d subordinate=%d\n",
+ uPrimary, pBusSub->PciDev.uDevFn >> 3, pBusSub->PciDev.uDevFn & 7,
+ uPrimary, uSecondary, uSubordinate);
+ if ( uPrimary != PDMPciDevGetByte(&pBusSub->PciDev, VBOX_PCI_PRIMARY_BUS)
+ || uSecondary != PDMPciDevGetByte(&pBusSub->PciDev, VBOX_PCI_SECONDARY_BUS)
+ || uSubordinate != PDMPciDevGetByte(&pBusSub->PciDev, VBOX_PCI_SUBORDINATE_BUS))
+ {
+ devpciR3InfoIndent(pHlp, iIndentLvl);
+ pHlp->pfnPrintf(pHlp, "CACHE INCONSISTENCY: primary=%d secondary=%d subordinate=%d\n",
+ PDMPciDevGetByte(&pBusSub->PciDev, VBOX_PCI_PRIMARY_BUS),
+ PDMPciDevGetByte(&pBusSub->PciDev, VBOX_PCI_SECONDARY_BUS),
+ PDMPciDevGetByte(&pBusSub->PciDev, VBOX_PCI_SUBORDINATE_BUS));
+ }
+ devpciR3InfoIndent(pHlp, iIndentLvl);
+ pHlp->pfnPrintf(pHlp, "behind bridge: ");
+ uint8_t uIoBase = devpciR3GetByte(&pBusSub->PciDev, VBOX_PCI_IO_BASE);
+ uint8_t uIoLimit = devpciR3GetByte(&pBusSub->PciDev, VBOX_PCI_IO_LIMIT);
+ pHlp->pfnPrintf(pHlp, "I/O %#06x..%#06x",
+ (uIoBase & 0xf0) << 8,
+ (uIoLimit & 0xf0) << 8 | 0xfff);
+ if (uIoBase > uIoLimit)
+ pHlp->pfnPrintf(pHlp, " (IGNORED)");
+ pHlp->pfnPrintf(pHlp, "\n");
+ devpciR3InfoIndent(pHlp, iIndentLvl);
+ pHlp->pfnPrintf(pHlp, "behind bridge: ");
+ uint32_t uMemoryBase = devpciR3GetWord(&pBusSub->PciDev, VBOX_PCI_MEMORY_BASE);
+ uint32_t uMemoryLimit = devpciR3GetWord(&pBusSub->PciDev, VBOX_PCI_MEMORY_LIMIT);
+ pHlp->pfnPrintf(pHlp, "memory %#010x..%#010x",
+ (uMemoryBase & 0xfff0) << 16,
+ (uMemoryLimit & 0xfff0) << 16 | 0xfffff);
+ if (uMemoryBase > uMemoryLimit)
+ pHlp->pfnPrintf(pHlp, " (IGNORED)");
+ pHlp->pfnPrintf(pHlp, "\n");
+ devpciR3InfoIndent(pHlp, iIndentLvl);
+ pHlp->pfnPrintf(pHlp, "behind bridge: ");
+ uint32_t uPrefMemoryRegBase = devpciR3GetWord(&pBusSub->PciDev, VBOX_PCI_PREF_MEMORY_BASE);
+ uint32_t uPrefMemoryRegLimit = devpciR3GetWord(&pBusSub->PciDev, VBOX_PCI_PREF_MEMORY_LIMIT);
+ uint64_t uPrefMemoryBase = (uPrefMemoryRegBase & 0xfff0) << 16;
+ uint64_t uPrefMemoryLimit = (uPrefMemoryRegLimit & 0xfff0) << 16 | 0xfffff;
+ if ( (uPrefMemoryRegBase & 0xf) == 1
+ && (uPrefMemoryRegLimit & 0xf) == 1)
+ {
+ uPrefMemoryBase |= (uint64_t)devpciR3GetDWord(&pBusSub->PciDev, VBOX_PCI_PREF_BASE_UPPER32) << 32;
+ uPrefMemoryLimit |= (uint64_t)devpciR3GetDWord(&pBusSub->PciDev, VBOX_PCI_PREF_LIMIT_UPPER32) << 32;
+ pHlp->pfnPrintf(pHlp, "64-bit ");
+ }
+ else
+ pHlp->pfnPrintf(pHlp, "32-bit ");
+ pHlp->pfnPrintf(pHlp, "prefetch memory %#018llx..%#018llx", uPrefMemoryBase, uPrefMemoryLimit);
+ if (uPrefMemoryBase > uPrefMemoryLimit)
+ pHlp->pfnPrintf(pHlp, " (IGNORED)");
+ pHlp->pfnPrintf(pHlp, "\n");
+ devpciR3InfoPciBus(pBusSub, pHlp, iIndentLvl + 1, fRegisters);
+ }
+ }
+}
+
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV, 'pci'}
+ */
+DECLCALLBACK(void) devpciR3InfoPci(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PDEVPCIBUS pBus = DEVINS_2_DEVPCIBUS(pDevIns);
+
+ if (pszArgs == NULL || !*pszArgs || !strcmp(pszArgs, "basic"))
+ devpciR3InfoPciBus(pBus, pHlp, 0 /*iIndentLvl*/, false /*fRegisters*/);
+ else if (!strcmp(pszArgs, "verbose"))
+ devpciR3InfoPciBus(pBus, pHlp, 0 /*iIndentLvl*/, true /*fRegisters*/);
+ else
+ pHlp->pfnPrintf(pHlp, "Invalid argument. Recognized arguments are 'basic', 'verbose'.\n");
+}
+
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV, 'pciirq'}
+ */
+DECLCALLBACK(void) devpciR3InfoPciIrq(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ NOREF(pszArgs);
+
+ pHlp->pfnPrintf(pHlp, "PCI I/O APIC IRQ levels:\n");
+ for (int i = 0; i < DEVPCI_APIC_IRQ_PINS; ++i)
+ pHlp->pfnPrintf(pHlp, " IRQ%02d: %u\n", 0x10 + i, pPciRoot->auPciApicIrqLevels[i]);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) ich9pciConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ RT_NOREF1(iInstance);
+ Assert(iInstance == 0);
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+ /*
+ * Validate and read configuration.
+ */
+ if (!CFGMR3AreValuesValid(pCfg,
+ "IOAPIC\0"
+ "GCEnabled\0"
+ "R0Enabled\0"
+ "McfgBase\0"
+ "McfgLength\0"
+ ))
+ return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
+
+ /* query whether we got an IOAPIC */
+ bool fUseIoApic;
+ int rc = CFGMR3QueryBoolDef(pCfg, "IOAPIC", &fUseIoApic, false);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"IOAPIC\""));
+
+ /* check if RC code is enabled. */
+ bool fGCEnabled;
+ rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"GCEnabled\""));
+ /* check if R0 code is enabled. */
+ bool fR0Enabled;
+ rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"R0Enabled\""));
+
+ Log(("PCI: fUseIoApic=%RTbool fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fUseIoApic, fGCEnabled, fR0Enabled));
+
+ /*
+ * Init data.
+ */
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PDEVPCIBUS pBus = &pPciRoot->PciBus;
+ /* Zero out everything */
+ memset(pPciRoot, 0, sizeof(*pPciRoot));
+ /* And fill values */
+ if (!fUseIoApic)
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Must use IO-APIC with ICH9 chipset"));
+ rc = CFGMR3QueryU64Def(pCfg, "McfgBase", &pPciRoot->u64PciConfigMMioAddress, 0);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to read \"McfgBase\""));
+ rc = CFGMR3QueryU64Def(pCfg, "McfgLength", &pPciRoot->u64PciConfigMMioLength, 0);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to read \"McfgLength\""));
+
+ pPciRoot->fUseIoApic = fUseIoApic;
+ pPciRoot->pDevInsR3 = pDevIns;
+ pPciRoot->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pPciRoot->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+
+ pPciRoot->PciBus.fTypePiix3 = false;
+ pPciRoot->PciBus.fTypeIch9 = true;
+ pPciRoot->PciBus.fPureBridge = false;
+ pPciRoot->PciBus.pDevInsR3 = pDevIns;
+ pPciRoot->PciBus.pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pPciRoot->PciBus.pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pPciRoot->PciBus.papBridgesR3 = (PPDMPCIDEV *)PDMDevHlpMMHeapAllocZ(pDevIns, sizeof(PPDMPCIDEV) * RT_ELEMENTS(pPciRoot->PciBus.apDevices));
+ AssertLogRelReturn(pPciRoot->PciBus.papBridgesR3, VERR_NO_MEMORY);
+
+ /*
+ * Register bus
+ */
+ PDMPCIBUSREG PciBusReg;
+ PciBusReg.u32Version = PDM_PCIBUSREG_VERSION;
+ PciBusReg.pfnRegisterR3 = pciR3MergedRegister;
+ PciBusReg.pfnRegisterMsiR3 = ich9pciRegisterMsi;
+ PciBusReg.pfnIORegionRegisterR3 = devpciR3CommonIORegionRegister;
+ PciBusReg.pfnSetConfigCallbacksR3 = devpciR3CommonSetConfigCallbacks;
+ PciBusReg.pfnSetIrqR3 = ich9pciSetIrq;
+ PciBusReg.pszSetIrqRC = fGCEnabled ? "ich9pciSetIrq" : NULL;
+ PciBusReg.pszSetIrqR0 = fR0Enabled ? "ich9pciSetIrq" : NULL;
+ rc = PDMDevHlpPCIBusRegister(pDevIns, &PciBusReg, &pBus->pPciHlpR3, &pBus->iBus);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Failed to register ourselves as a PCI Bus"));
+ Assert(pBus->iBus == 0);
+ if (pBus->pPciHlpR3->u32Version != PDM_PCIHLPR3_VERSION)
+ return PDMDevHlpVMSetError(pDevIns, VERR_VERSION_MISMATCH, RT_SRC_POS,
+ N_("PCI helper version mismatch; got %#x expected %#x"),
+ pBus->pPciHlpR3->u32Version, PDM_PCIHLPR3_VERSION);
+
+ pBus->pPciHlpRC = pBus->pPciHlpR3->pfnGetRCHelpers(pDevIns);
+ pBus->pPciHlpR0 = pBus->pPciHlpR3->pfnGetR0Helpers(pDevIns);
+
+ /*
+ * Fill in PCI configs and add them to the bus.
+ */
+ /** @todo Disabled for now because this causes error messages with Linux guests.
+ * The guest loads the x38_edac device which tries to map a memory region
+ * using an address given at place 0x48 - 0x4f in the PCI config space.
+ * This fails. because we don't register such a region.
+ */
+#if 0
+ /* Host bridge device */
+ PDMPciDevSetVendorId( &pBus->PciDev, 0x8086); /* Intel */
+ PDMPciDevSetDeviceId( &pBus->PciDev, 0x29e0); /* Desktop */
+ PDMPciDevSetRevisionId(&pBus->PciDev, 0x01); /* rev. 01 */
+ PDMPciDevSetClassBase( &pBus->PciDev, 0x06); /* bridge */
+ PDMPciDevSetClassSub( &pBus->PciDev, 0x00); /* Host/PCI bridge */
+ PDMPciDevSetClassProg( &pBus->PciDev, 0x00); /* Host/PCI bridge */
+ PDMPciDevSetHeaderType(&pBus->PciDev, 0x00); /* bridge */
+ PDMPciDevSetWord(&pBus->PciDev, VBOX_PCI_SEC_STATUS, 0x0280); /* secondary status */
+
+ pBus->PciDev.pDevIns = pDevIns;
+ /* We register Host<->PCI controller on the bus */
+ ich9pciRegisterInternal(pBus, 0, &pBus->PciDev, "dram");
+#endif
+
+ /*
+ * Register I/O ports and save state.
+ */
+ rc = PDMDevHlpIOPortRegister(pDevIns, 0x0cf8, 1, NULL, ich9pciIOPortAddressWrite, ich9pciIOPortAddressRead, NULL, NULL, "ICH9 (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIOPortRegister(pDevIns, 0x0cfc, 4, NULL, ich9pciIOPortDataWrite, ich9pciIOPortDataRead, NULL, NULL, "ICH9 (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ if (fGCEnabled)
+ {
+ rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x0cf8, 1, NIL_RTGCPTR, "ich9pciIOPortAddressWrite", "ich9pciIOPortAddressRead", NULL, NULL, "ICH9 (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x0cfc, 4, NIL_RTGCPTR, "ich9pciIOPortDataWrite", "ich9pciIOPortDataRead", NULL, NULL, "ICH9 (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ if (fR0Enabled)
+ {
+ rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x0cf8, 1, NIL_RTR0PTR, "ich9pciIOPortAddressWrite", "ich9pciIOPortAddressRead", NULL, NULL, "ICH9 (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x0cfc, 4, NIL_RTR0PTR, "ich9pciIOPortDataWrite", "ich9pciIOPortDataRead", NULL, NULL, "ICH9 (PCI)");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ rc = PDMDevHlpIOPortRegister(pDevIns, 0x0410, 1, NULL, ich9pciR3IOPortMagicPCIWrite, ich9pciR3IOPortMagicPCIRead, NULL, NULL, "ICH9 (Fake PCI BIOS trigger)");
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pPciRoot->u64PciConfigMMioAddress != 0)
+ {
+ rc = PDMDevHlpMMIORegister(pDevIns, pPciRoot->u64PciConfigMMioAddress, pPciRoot->u64PciConfigMMioLength, NULL /*pvUser*/,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ ich9pciMcfgMMIOWrite, ich9pciMcfgMMIORead, "MCFG ranges");
+ AssertMsgRCReturn(rc, ("rc=%Rrc %#llx/%#llx\n", rc, pPciRoot->u64PciConfigMMioAddress, pPciRoot->u64PciConfigMMioLength), rc);
+
+ if (fGCEnabled)
+ {
+ rc = PDMDevHlpMMIORegisterRC(pDevIns, pPciRoot->u64PciConfigMMioAddress, pPciRoot->u64PciConfigMMioLength,
+ NIL_RTRCPTR /*pvUser*/, "ich9pciMcfgMMIOWrite", "ich9pciMcfgMMIORead");
+ AssertRCReturn(rc, rc);
+ }
+
+
+ if (fR0Enabled)
+ {
+ rc = PDMDevHlpMMIORegisterR0(pDevIns, pPciRoot->u64PciConfigMMioAddress, pPciRoot->u64PciConfigMMioLength,
+ NIL_RTR0PTR /*pvUser*/, "ich9pciMcfgMMIOWrite", "ich9pciMcfgMMIORead");
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, VBOX_ICH9PCI_SAVED_STATE_VERSION,
+ sizeof(*pBus) + 16*128, "pgm",
+ NULL, NULL, NULL,
+ NULL, ich9pciR3SaveExec, NULL,
+ NULL, ich9pciR3LoadExec, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+
+ /** @todo other chipset devices shall be registered too */
+
+ PDMDevHlpDBGFInfoRegister(pDevIns, "pci",
+ "Display PCI bus status. Recognizes 'basic' or 'verbose' as arguments, defaults to 'basic'.",
+ devpciR3InfoPci);
+ PDMDevHlpDBGFInfoRegister(pDevIns, "pciirq", "Display PCI IRQ state. (no arguments)", devpciR3InfoPciIrq);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) ich9pciDestruct(PPDMDEVINS pDevIns)
+{
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ if (pPciRoot->PciBus.papBridgesR3)
+ {
+ PDMDevHlpMMHeapFree(pDevIns, pPciRoot->PciBus.papBridgesR3);
+ pPciRoot->PciBus.papBridgesR3 = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+
+void devpciR3ResetDevice(PPDMPCIDEV pDev)
+{
+ /* Clear regions */
+ for (int iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
+ {
+ PCIIORegion* pRegion = &pDev->Int.s.aIORegions[iRegion];
+ if (pRegion->size == 0)
+ continue;
+ bool const f64Bit = (pRegion->type & ((uint8_t)(PCI_ADDRESS_SPACE_BAR64 | PCI_ADDRESS_SPACE_IO)))
+ == PCI_ADDRESS_SPACE_BAR64;
+
+ devpciR3UnmapRegion(pDev, iRegion);
+
+ if (f64Bit)
+ iRegion++;
+ }
+
+ if (pciDevIsPassthrough(pDev))
+ {
+ // no reset handler - we can do what we need in PDM reset handler
+ /// @todo is it correct?
+ }
+ else
+ {
+ devpciR3SetWord(pDev, VBOX_PCI_COMMAND,
+ devpciR3GetWord(pDev, VBOX_PCI_COMMAND)
+ & ~(VBOX_PCI_COMMAND_IO | VBOX_PCI_COMMAND_MEMORY |
+ VBOX_PCI_COMMAND_MASTER | VBOX_PCI_COMMAND_SPECIAL |
+ VBOX_PCI_COMMAND_PARITY | VBOX_PCI_COMMAND_SERR |
+ VBOX_PCI_COMMAND_FAST_BACK | VBOX_PCI_COMMAND_INTX_DISABLE));
+
+ /* Bridge device reset handlers processed later */
+ if (!pciDevIsPci2PciBridge(pDev))
+ {
+ devpciR3SetByte(pDev, VBOX_PCI_CACHE_LINE_SIZE, 0x0);
+ devpciR3SetByte(pDev, VBOX_PCI_INTERRUPT_LINE, 0x0);
+ }
+
+ /* Reset MSI message control. */
+ if (pciDevIsMsiCapable(pDev))
+ {
+ devpciR3SetWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_CONTROL,
+ devpciR3GetWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_CONTROL) & 0xff8e);
+ }
+
+ /* Reset MSI-X message control. */
+ if (pciDevIsMsixCapable(pDev))
+ {
+ devpciR3SetWord(pDev, pDev->Int.s.u8MsixCapOffset + VBOX_MSIX_CAP_MESSAGE_CONTROL,
+ devpciR3GetWord(pDev, pDev->Int.s.u8MsixCapOffset + VBOX_MSIX_CAP_MESSAGE_CONTROL) & 0x3fff);
+ }
+ }
+}
+
+/**
+ * Returns the PCI express encoding for the given PCI Express Device/Port type string.
+ *
+ * @returns PCI express encoding.
+ * @param pszExpressPortType The string identifier for the port/device type.
+ */
+static uint8_t ich9pcibridgeR3GetExpressPortTypeFromString(const char *pszExpressPortType)
+{
+ if (!RTStrCmp(pszExpressPortType, "EndPtDev"))
+ return VBOX_PCI_EXP_TYPE_ENDPOINT;
+ else if (!RTStrCmp(pszExpressPortType, "LegEndPtDev"))
+ return VBOX_PCI_EXP_TYPE_LEG_END;
+ else if (!RTStrCmp(pszExpressPortType, "RootCmplxRootPort"))
+ return VBOX_PCI_EXP_TYPE_ROOT_PORT;
+ else if (!RTStrCmp(pszExpressPortType, "ExpressSwUpstream"))
+ return VBOX_PCI_EXP_TYPE_UPSTREAM;
+ else if (!RTStrCmp(pszExpressPortType, "ExpressSwDownstream"))
+ return VBOX_PCI_EXP_TYPE_DOWNSTREAM;
+ else if (!RTStrCmp(pszExpressPortType, "Express2PciBridge"))
+ return VBOX_PCI_EXP_TYPE_PCI_BRIDGE;
+ else if (!RTStrCmp(pszExpressPortType, "Pci2ExpressBridge"))
+ return VBOX_PCI_EXP_TYPE_PCIE_BRIDGE;
+ else if (!RTStrCmp(pszExpressPortType, "RootCmplxIntEp"))
+ return VBOX_PCI_EXP_TYPE_ROOT_INT_EP;
+ else if (!RTStrCmp(pszExpressPortType, "RootCmplxEc"))
+ return VBOX_PCI_EXP_TYPE_ROOT_EC;
+
+ AssertLogRelMsgFailedReturn(("Unknown express port type specified"), VBOX_PCI_EXP_TYPE_ROOT_INT_EP);
+}
+
+/**
+ * Recursive worker for ich9pciReset.
+ *
+ * @param pDevIns ICH9 bridge (root or PCI-to-PCI) instance.
+ */
+static void ich9pciResetBridge(PPDMDEVINS pDevIns)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+
+ /* PCI-specific reset for each device. */
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ if (pBus->apDevices[uDevFn])
+ devpciR3ResetDevice(pBus->apDevices[uDevFn]);
+ }
+
+ for (uint32_t iBridge = 0; iBridge < pBus->cBridges; iBridge++)
+ {
+ if (pBus->papBridgesR3[iBridge])
+ ich9pciResetBridge(pBus->papBridgesR3[iBridge]->Int.s.CTX_SUFF(pDevIns));
+ }
+
+ /* Reset topology config for non-root bridge. Last thing to do, otherwise
+ * the secondary and subordinate are instantly unreachable. */
+ if (pBus->iBus != 0)
+ {
+ devpciR3SetByte(&pBus->PciDev, VBOX_PCI_PRIMARY_BUS, 0);
+ devpciR3SetByte(&pBus->PciDev, VBOX_PCI_SECONDARY_BUS, 0);
+ devpciR3SetByte(&pBus->PciDev, VBOX_PCI_SUBORDINATE_BUS, 0);
+ /* Not resetting the address decoders of the bridge to 0, since the
+ * PCI-to-PCI Bridge spec says that there is no default value. */
+ }
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) ich9pciReset(PPDMDEVINS pDevIns)
+{
+ /* Reset everything under the root bridge. */
+ ich9pciResetBridge(pDevIns);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnRelocate}
+ */
+DECLCALLBACK(void) devpciR3BusRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+
+ pBus->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pBus->pPciHlpRC = pBus->pPciHlpR3->pfnGetRCHelpers(pDevIns);
+
+ /* Relocate RC pointers for the attached pci devices. */
+ for (uint32_t uDevFn = 0; uDevFn < RT_ELEMENTS(pBus->apDevices); uDevFn++)
+ {
+ PPDMPCIDEV pDev = pBus->apDevices[uDevFn];
+ if (pDev)
+ {
+ pDev->Int.s.pBusRC += offDelta;
+ if (pDev->Int.s.pMsixPageRC)
+ pDev->Int.s.pMsixPageRC += offDelta;
+ }
+ }
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnRelocate}
+ */
+DECLCALLBACK(void) devpciR3RootRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ pPciRoot->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+
+ AssertCompileMemberOffset(DEVPCIROOT, PciBus, 0);
+ devpciR3BusRelocate(pDevIns, offDelta);
+}
+
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) ich9pcibridgeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
+ /* Special access to the PDMPCIDEV structure of a ich9pcibridge instance. */
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIICH9BRIDGEPDMPCIDEV, &pBus->PciDev);
+ return NULL;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) ich9pcibridgeConstruct(PPDMDEVINS pDevIns,
+ int iInstance,
+ PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+ /*
+ * Validate and read configuration.
+ */
+ if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0" "R0Enabled\0" "ExpressEnabled\0" "ExpressPortType\0"))
+ return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
+
+ /* check if RC code is enabled. */
+ bool fGCEnabled;
+ int rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"GCEnabled\""));
+
+ /* check if R0 code is enabled. */
+ bool fR0Enabled;
+ rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"R0Enabled\""));
+ Log(("PCI: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
+
+ /* check if we're supposed to implement a PCIe bridge. */
+ bool fExpress;
+ rc = CFGMR3QueryBoolDef(pCfg, "ExpressEnabled", &fExpress, false);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to query boolean value \"ExpressEnabled\""));
+
+ char *pszExpressPortType;
+ rc = CFGMR3QueryStringAllocDef(pCfg, "ExpressPortType",
+ &pszExpressPortType, "RootCmplxIntEp");
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("LsiLogic configuration error: failed to read \"ExpressPortType\" as string"));
+
+ uint8_t uExpressPortType = ich9pcibridgeR3GetExpressPortTypeFromString(pszExpressPortType);
+ MMR3HeapFree(pszExpressPortType);
+
+ pDevIns->IBase.pfnQueryInterface = ich9pcibridgeQueryInterface;
+
+ /*
+ * Init data and register the PCI bus.
+ */
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ pBus->fTypePiix3 = false;
+ pBus->fTypeIch9 = true;
+ pBus->fPureBridge = true;
+ pBus->pDevInsR3 = pDevIns;
+ pBus->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pBus->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ /** @todo r=klaus figure out how to extend this to allow PCIe config space
+ * extension, which increases the config space from 256 bytes to 4K. */
+ pBus->papBridgesR3 = (PPDMPCIDEV *)PDMDevHlpMMHeapAllocZ(pDevIns, sizeof(PPDMPCIDEV) * RT_ELEMENTS(pBus->apDevices));
+ AssertLogRelReturn(pBus->papBridgesR3, VERR_NO_MEMORY);
+
+ PDMPCIBUSREG PciBusReg;
+ PciBusReg.u32Version = PDM_PCIBUSREG_VERSION;
+ PciBusReg.pfnRegisterR3 = pcibridgeR3MergedRegisterDevice;
+ PciBusReg.pfnRegisterMsiR3 = ich9pciRegisterMsi;
+ PciBusReg.pfnIORegionRegisterR3 = devpciR3CommonIORegionRegister;
+ PciBusReg.pfnSetConfigCallbacksR3 = devpciR3CommonSetConfigCallbacks;
+ PciBusReg.pfnSetIrqR3 = ich9pcibridgeSetIrq;
+ PciBusReg.pszSetIrqRC = fGCEnabled ? "ich9pcibridgeSetIrq" : NULL;
+ PciBusReg.pszSetIrqR0 = fR0Enabled ? "ich9pcibridgeSetIrq" : NULL;
+ rc = PDMDevHlpPCIBusRegister(pDevIns, &PciBusReg, &pBus->pPciHlpR3, &pBus->iBus);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Failed to register ourselves as a PCI Bus"));
+ Assert(pBus->iBus == (uint32_t)iInstance + 1); /* Can be removed when adding support for multiple bridge implementations. */
+ if (pBus->pPciHlpR3->u32Version != PDM_PCIHLPR3_VERSION)
+ return PDMDevHlpVMSetError(pDevIns, VERR_VERSION_MISMATCH, RT_SRC_POS,
+ N_("PCI helper version mismatch; got %#x expected %#x"),
+ pBus->pPciHlpR3->u32Version, PDM_PCIHLPR3_VERSION);
+
+ pBus->pPciHlpRC = pBus->pPciHlpR3->pfnGetRCHelpers(pDevIns);
+ pBus->pPciHlpR0 = pBus->pPciHlpR3->pfnGetR0Helpers(pDevIns);
+ LogRel(("PCI: Registered bridge instance #%u as PDM bus no %u.\n", iInstance, pBus->iBus));
+
+
+ /* Disable default device locking. */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Fill in PCI configs and add them to the bus.
+ */
+ PDMPciDevSetVendorId( &pBus->PciDev, 0x8086); /* Intel */
+ if (fExpress)
+ {
+ PDMPciDevSetDeviceId(&pBus->PciDev, 0x29e1); /* 82X38/X48 Express Host-Primary PCI Express Bridge. */
+ PDMPciDevSetRevisionId(&pBus->PciDev, 0x01);
+ }
+ else
+ {
+ PDMPciDevSetDeviceId(&pBus->PciDev, 0x2448); /* 82801 Mobile PCI bridge. */
+ PDMPciDevSetRevisionId(&pBus->PciDev, 0xf2);
+ }
+ PDMPciDevSetClassSub( &pBus->PciDev, 0x04); /* pci2pci */
+ PDMPciDevSetClassBase( &pBus->PciDev, 0x06); /* PCI_bridge */
+ if (fExpress)
+ PDMPciDevSetClassProg(&pBus->PciDev, 0x00); /* Normal decoding. */
+ else
+ PDMPciDevSetClassProg(&pBus->PciDev, 0x01); /* Supports subtractive decoding. */
+ PDMPciDevSetHeaderType(&pBus->PciDev, 0x01); /* Single function device which adheres to the PCI-to-PCI bridge spec. */
+ if (fExpress)
+ {
+ PDMPciDevSetCommand(&pBus->PciDev, VBOX_PCI_COMMAND_SERR);
+ PDMPciDevSetStatus(&pBus->PciDev, VBOX_PCI_STATUS_CAP_LIST); /* Has capabilities. */
+ PDMPciDevSetByte(&pBus->PciDev, VBOX_PCI_CACHE_LINE_SIZE, 8); /* 32 bytes */
+ /* PCI Express */
+ PDMPciDevSetByte(&pBus->PciDev, 0xa0 + 0, VBOX_PCI_CAP_ID_EXP); /* PCI_Express */
+ PDMPciDevSetByte(&pBus->PciDev, 0xa0 + 1, 0); /* next */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 2,
+ /* version */ 0x2
+ | (uExpressPortType << 4));
+ PDMPciDevSetDWord(&pBus->PciDev, 0xa0 + 4, VBOX_PCI_EXP_DEVCAP_RBE); /* Device capabilities. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 8, 0x0000); /* Device control. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 10, 0x0000); /* Device status. */
+ PDMPciDevSetDWord(&pBus->PciDev, 0xa0 + 12,
+ /* Max Link Speed */ 2
+ | /* Maximum Link Width */ (16 << 4)
+ | /* Active State Power Management (ASPM) Sopport */ (0 << 10)
+ | VBOX_PCI_EXP_LNKCAP_LBNC
+ | /* Port Number */ ((2 + iInstance) << 24)); /* Link capabilities. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 16, VBOX_PCI_EXP_LNKCTL_CLOCK); /* Link control. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 18,
+ /* Current Link Speed */ 2
+ | /* Negotiated Link Width */ (16 << 4)
+ | VBOX_PCI_EXP_LNKSTA_SL_CLK); /* Link status. */
+ PDMPciDevSetDWord(&pBus->PciDev, 0xa0 + 20,
+ /* Slot Power Limit Value */ (75 << 7)
+ | /* Physical Slot Number */ (0 << 19)); /* Slot capabilities. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 24, 0x0000); /* Slot control. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 26, 0x0000); /* Slot status. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 28, 0x0000); /* Root control. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 30, 0x0000); /* Root capabilities. */
+ PDMPciDevSetDWord(&pBus->PciDev, 0xa0 + 32, 0x00000000); /* Root status. */
+ PDMPciDevSetDWord(&pBus->PciDev, 0xa0 + 36, 0x00000000); /* Device capabilities 2. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 40, 0x0000); /* Device control 2. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 42, 0x0000); /* Device status 2. */
+ PDMPciDevSetDWord(&pBus->PciDev, 0xa0 + 44,
+ /* Supported Link Speeds Vector */ (2 << 1)); /* Link capabilities 2. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 48,
+ /* Target Link Speed */ 2); /* Link control 2. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 50, 0x0000); /* Link status 2. */
+ PDMPciDevSetDWord(&pBus->PciDev, 0xa0 + 52, 0x00000000); /* Slot capabilities 2. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 56, 0x0000); /* Slot control 2. */
+ PDMPciDevSetWord(&pBus->PciDev, 0xa0 + 58, 0x0000); /* Slot status 2. */
+ PDMPciDevSetCapabilityList(&pBus->PciDev, 0xa0);
+ }
+ else
+ {
+ PDMPciDevSetCommand(&pBus->PciDev, 0x00);
+ PDMPciDevSetStatus(&pBus->PciDev, 0x20); /* 66MHz Capable. */
+ }
+ PDMPciDevSetInterruptLine(&pBus->PciDev, 0x00); /* This device does not assert interrupts. */
+
+ /*
+ * This device does not generate interrupts. Interrupt delivery from
+ * devices attached to the bus is unaffected.
+ */
+ PDMPciDevSetInterruptPin (&pBus->PciDev, 0x00);
+
+ if (fExpress)
+ {
+ /** @todo r=klaus set up the PCIe config space beyond the old 256 byte
+ * limit, containing additional capability descriptors. */
+ }
+
+ /*
+ * Register this PCI bridge. The called function will take care on which bus we will get registered.
+ */
+ rc = PDMDevHlpPCIRegisterEx(pDevIns, &pBus->PciDev, PDMPCIDEVREG_CFG_PRIMARY, PDMPCIDEVREG_F_PCI_BRIDGE,
+ PDMPCIDEVREG_DEV_NO_FIRST_UNUSED, PDMPCIDEVREG_FUN_NO_FIRST_UNUSED, "ich9pcibridge");
+ if (RT_FAILURE(rc))
+ return rc;
+ pBus->PciDev.Int.s.pfnBridgeConfigRead = ich9pcibridgeConfigRead;
+ pBus->PciDev.Int.s.pfnBridgeConfigWrite = ich9pcibridgeConfigWrite;
+
+ /*
+ * Register SSM handlers. We use the same saved state version as for the host bridge
+ * to make changes easier.
+ */
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, VBOX_ICH9PCI_SAVED_STATE_VERSION,
+ sizeof(*pBus) + 16*128,
+ "pgm" /* before */,
+ NULL, NULL, NULL,
+ NULL, ich9pcibridgeR3SaveExec, NULL,
+ NULL, ich9pcibridgeR3LoadExec, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) ich9pcibridgeDestruct(PPDMDEVINS pDevIns)
+{
+ PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
+ if (pBus->papBridgesR3)
+ {
+ PDMDevHlpMMHeapFree(pDevIns, pBus->papBridgesR3);
+ pBus->papBridgesR3 = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * The PCI bus device registration structure.
+ */
+const PDMDEVREG g_DevicePciIch9 =
+{
+ /* u32Version */
+ PDM_DEVREG_VERSION,
+ /* szName */
+ "ich9pci",
+ /* szRCMod */
+ "VBoxDDRC.rc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "ICH9 PCI bridge",
+ /* fFlags */
+ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
+ /* fClass */
+ PDM_DEVREG_CLASS_BUS_PCI | PDM_DEVREG_CLASS_BUS_ISA,
+ /* cMaxInstances */
+ 1,
+ /* cbInstance */
+ sizeof(DEVPCIROOT),
+ /* pfnConstruct */
+ ich9pciConstruct,
+ /* pfnDestruct */
+ ich9pciDestruct,
+ /* pfnRelocate */
+ devpciR3RootRelocate,
+ /* pfnMemSetup */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ ich9pciReset,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnQueryInterface */
+ NULL,
+ /* pfnInitComplete */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DEVREG_VERSION
+};
+
+/**
+ * The device registration structure
+ * for the PCI-to-PCI bridge.
+ */
+const PDMDEVREG g_DevicePciIch9Bridge =
+{
+ /* u32Version */
+ PDM_DEVREG_VERSION,
+ /* szName */
+ "ich9pcibridge",
+ /* szRCMod */
+ "VBoxDDRC.rc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "ICH9 PCI to PCI bridge",
+ /* fFlags */
+ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
+ /* fClass */
+ PDM_DEVREG_CLASS_BUS_PCI,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DEVPCIBUS),
+ /* pfnConstruct */
+ ich9pcibridgeConstruct,
+ /* pfnDestruct */
+ ich9pcibridgeDestruct,
+ /* pfnRelocate */
+ devpciR3BusRelocate,
+ /* pfnMemSetup */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL, /* Must be NULL, to make sure only bus driver handles reset */
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnQueryInterface */
+ NULL,
+ /* pfnInitComplete */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DEVREG_VERSION
+};
+
+#endif /* IN_RING3 */
+
diff --git a/src/VBox/Devices/Bus/DevPciInternal.h b/src/VBox/Devices/Bus/DevPciInternal.h
new file mode 100644
index 00000000..7a863b46
--- /dev/null
+++ b/src/VBox/Devices/Bus/DevPciInternal.h
@@ -0,0 +1,240 @@
+/* $Id: DevPciInternal.h $ */
+/** @file
+ * DevPCI - Common Internal Header.
+ */
+
+/*
+ * Copyright (C) 2010-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Bus_DevPciInternal_h
+#define VBOX_INCLUDED_SRC_Bus_DevPciInternal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifndef PDMPCIDEV_INCLUDE_PRIVATE
+# define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
+#endif
+#include <VBox/vmm/pdmdev.h>
+
+
+/**
+ * PCI bus instance (common to both).
+ */
+typedef struct DEVPCIBUS
+{
+ /** Bus number. */
+ uint32_t iBus;
+ /** Number of bridges attached to the bus. */
+ uint32_t cBridges;
+ /** Start device number - always zero (only for DevPCI source compat). */
+ uint32_t iDevSearch;
+ /** Set if PIIX3 type. */
+ uint32_t fTypePiix3 : 1;
+ /** Set if ICH9 type. */
+ uint32_t fTypeIch9: 1;
+ /** Set if this is a pure bridge, i.e. not part of DEVPCIGLOBALS struct. */
+ uint32_t fPureBridge : 1;
+ /** Reserved for future config flags. */
+ uint32_t uReservedConfigFlags : 29;
+
+ /** R3 pointer to the device instance. */
+ PPDMDEVINSR3 pDevInsR3;
+ /** Pointer to the PCI R3 helpers. */
+ PCPDMPCIHLPR3 pPciHlpR3;
+
+ /** R0 pointer to the device instance. */
+ PPDMDEVINSR0 pDevInsR0;
+ /** Pointer to the PCI R0 helpers. */
+ PCPDMPCIHLPR0 pPciHlpR0;
+
+ /** RC pointer to the device instance. */
+ PPDMDEVINSRC pDevInsRC;
+ /** Pointer to the PCI RC helpers. */
+ PCPDMPCIHLPRC pPciHlpRC;
+
+ /** Array of bridges attached to the bus. */
+ R3PTRTYPE(PPDMPCIDEV *) papBridgesR3;
+#if HC_ARCH_BITS == 32
+ uint32_t au32Alignment1[5]; /**< Cache line align apDevices. */
+#endif
+ /** Array of PCI devices. We assume 32 slots, each with 8 functions. */
+ R3PTRTYPE(PPDMPCIDEV) apDevices[256];
+
+ /** The PCI device for the PCI bridge. */
+ PDMPCIDEV PciDev;
+} DEVPCIBUS;
+/** Pointer to a PCI bus instance. */
+typedef DEVPCIBUS *PDEVPCIBUS;
+
+
+/** @def DEVPCI_APIC_IRQ_PINS
+ * Number of pins for interrupts if the APIC is used.
+ */
+#define DEVPCI_APIC_IRQ_PINS 8
+/** @def DEVPCI_LEGACY_IRQ_PINS
+ * Number of pins for interrupts (PIRQ#0...PIRQ#3).
+ * @remarks Labling this "legacy" might be a bit off...
+ */
+#define DEVPCI_LEGACY_IRQ_PINS 4
+
+/**
+ * PIIX3 ISA bridge state.
+ */
+typedef struct PIIX3ISABRIDGE
+{
+ /** The PCI device of the bridge. */
+ PDMPCIDEV dev;
+} PIIX3ISABRIDGE;
+
+
+/**
+ * PCI Globals - This is the host-to-pci bridge and the root bus.
+ *
+ * @note Only used by the root bus, not the bridges.
+ */
+typedef struct DEVPCIROOT
+{
+ /** PCI bus which is attached to the host-to-PCI bridge.
+ * @note This must come first so we can share more code with the bridges! */
+ DEVPCIBUS PciBus;
+
+ /** R3 pointer to the device instance. */
+ PPDMDEVINSR3 pDevInsR3;
+ /** R0 pointer to the device instance. */
+ PPDMDEVINSR0 pDevInsR0;
+ /** RC pointer to the device instance. */
+ PPDMDEVINSRC pDevInsRC;
+
+ /** I/O APIC usage flag (always true of ICH9, see constructor). */
+ bool fUseIoApic;
+ /** Reserved for future config flags. */
+ bool afFutureFlags[3];
+ /** Physical address of PCI config space MMIO region. */
+ uint64_t u64PciConfigMMioAddress;
+ /** Length of PCI config space MMIO region. */
+ uint64_t u64PciConfigMMioLength;
+
+ /** I/O APIC irq levels */
+ volatile uint32_t auPciApicIrqLevels[DEVPCI_APIC_IRQ_PINS];
+ /** Value latched in Configuration Address Port (0CF8h) */
+ uint32_t uConfigReg;
+ /** Alignment padding. */
+ uint32_t u32Alignment1;
+ /** Members only used by the PIIX3 code variant. */
+ struct
+ {
+ /** ACPI IRQ level */
+ uint32_t iAcpiIrqLevel;
+ /** ACPI PIC IRQ */
+ int32_t iAcpiIrq;
+ /** Irq levels for the four PCI Irqs.
+ * These count how many devices asserted the IRQ line. If greater 0 an IRQ
+ * is sent to the guest. If it drops to 0 the IRQ is deasserted.
+ * @remarks Labling this "legacy" might be a bit off...
+ */
+ volatile uint32_t auPciLegacyIrqLevels[DEVPCI_LEGACY_IRQ_PINS];
+ /** ISA bridge state. */
+ PIIX3ISABRIDGE PIIX3State;
+ } Piix3;
+
+#if 1 /* Will be moved into the BIOS "soon". */
+ /** Current bus number - obsolete (still used by DevPCI, but merge will fix that). */
+ uint8_t uPciBiosBus;
+ uint8_t abAlignment2[7];
+ /** The next I/O port address which the PCI BIOS will use. */
+ uint32_t uPciBiosIo;
+ /** The next MMIO address which the PCI BIOS will use. */
+ uint32_t uPciBiosMmio;
+ /** The next 64-bit MMIO address which the PCI BIOS will use. */
+ uint64_t uPciBiosMmio64;
+#endif
+
+} DEVPCIROOT;
+/** Pointer to PCI device globals. */
+typedef DEVPCIROOT *PDEVPCIROOT;
+
+
+/** Converts a PCI bus device instance pointer to a DEVPCIBUS pointer. */
+#define DEVINS_2_DEVPCIBUS(pDevIns) (&PDMINS_2_DATA(pDevIns, PDEVPCIROOT)->PciBus)
+/** Converts a pointer to a PCI bus instance to a DEVPCIROOT pointer. */
+#define DEVPCIBUS_2_DEVPCIROOT(pPciBus) RT_FROM_MEMBER(pPciBus, DEVPCIROOT, PciBus)
+
+/** @def PCI_LOCK
+ * Acquires the PDM lock. This is a NOP if locking is disabled. */
+/** @def PCI_UNLOCK
+ * Releases the PDM lock. This is a NOP if locking is disabled. */
+#define PCI_LOCK(pDevIns, rc) \
+ do { \
+ int rc2 = DEVINS_2_DEVPCIBUS(pDevIns)->CTX_SUFF(pPciHlp)->pfnLock((pDevIns), rc); \
+ if (rc2 != VINF_SUCCESS) \
+ return rc2; \
+ } while (0)
+#define PCI_UNLOCK(pDevIns) \
+ DEVINS_2_DEVPCIBUS(pDevIns)->CTX_SUFF(pPciHlp)->pfnUnlock(pDevIns)
+
+
+#ifdef IN_RING3
+
+DECLCALLBACK(void) devpciR3RootRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta);
+DECLCALLBACK(void) devpciR3BusRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta);
+DECLCALLBACK(void) devpciR3InfoPci(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs);
+DECLCALLBACK(void) devpciR3InfoPciIrq(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs);
+DECLCALLBACK(int) devpciR3CommonIORegionRegister(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iRegion, RTGCPHYS cbRegion,
+ PCIADDRESSSPACE enmType, PFNPCIIOREGIONMAP pfnCallback);
+DECLCALLBACK(void) devpciR3CommonSetConfigCallbacks(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev,
+ PFNPCICONFIGREAD pfnRead, PPFNPCICONFIGREAD ppfnReadOld,
+ PFNPCICONFIGWRITE pfnWrite, PPFNPCICONFIGWRITE ppfnWriteOld);
+DECLCALLBACK(uint32_t) devpciR3CommonDefaultConfigRead(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t uAddress, unsigned cb);
+DECLCALLBACK(VBOXSTRICTRC) devpciR3CommonDefaultConfigWrite(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev,
+ uint32_t uAddress, uint32_t u32Value, unsigned cb);
+void devpciR3CommonRestoreConfig(PPDMPCIDEV pDev, uint8_t const *pbSrcConfig);
+int devpciR3CommonRestoreRegions(PSSMHANDLE pSSM, PPDMPCIDEV pPciDev, PPCIIOREGION paIoRegions, bool fNewState);
+void devpciR3ResetDevice(PPDMPCIDEV pDev);
+void devpciR3BiosInitSetRegionAddress(PDEVPCIBUS pBus, PPDMPCIDEV pPciDev, int iRegion, uint64_t addr);
+uint32_t devpciR3GetCfg(PPDMPCIDEV pPciDev, int32_t iRegister, int cb);
+void devpciR3SetCfg(PPDMPCIDEV pPciDev, int32_t iRegister, uint32_t u32, int cb);
+
+DECLINLINE(uint8_t) devpciR3GetByte(PPDMPCIDEV pPciDev, int32_t iRegister)
+{
+ return (uint8_t)devpciR3GetCfg(pPciDev, iRegister, 1);
+}
+
+DECLINLINE(uint16_t) devpciR3GetWord(PPDMPCIDEV pPciDev, int32_t iRegister)
+{
+ return (uint16_t)devpciR3GetCfg(pPciDev, iRegister, 2);
+}
+
+DECLINLINE(uint32_t) devpciR3GetDWord(PPDMPCIDEV pPciDev, int32_t iRegister)
+{
+ return (uint32_t)devpciR3GetCfg(pPciDev, iRegister, 4);
+}
+
+DECLINLINE(void) devpciR3SetByte(PPDMPCIDEV pPciDev, int32_t iRegister, uint8_t u8)
+{
+ devpciR3SetCfg(pPciDev, iRegister, u8, 1);
+}
+
+DECLINLINE(void) devpciR3SetWord(PPDMPCIDEV pPciDev, int32_t iRegister, uint16_t u16)
+{
+ devpciR3SetCfg(pPciDev, iRegister, u16, 2);
+}
+
+DECLINLINE(void) devpciR3SetDWord(PPDMPCIDEV pPciDev, int32_t iRegister, uint32_t u32)
+{
+ devpciR3SetCfg(pPciDev, iRegister, u32, 4);
+}
+
+#endif /* IN_RING3 */
+
+#endif /* !VBOX_INCLUDED_SRC_Bus_DevPciInternal_h */
+
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);
+}
+
diff --git a/src/VBox/Devices/Bus/Makefile.kup b/src/VBox/Devices/Bus/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Bus/Makefile.kup
diff --git a/src/VBox/Devices/Bus/MsiCommon.cpp b/src/VBox/Devices/Bus/MsiCommon.cpp
new file mode 100644
index 00000000..4ed91c58
--- /dev/null
+++ b/src/VBox/Devices/Bus/MsiCommon.cpp
@@ -0,0 +1,336 @@
+/* $Id: MsiCommon.cpp $ */
+/** @file
+ * MSI support routines
+ *
+ * @todo Straighten up this file!!
+ */
+
+/*
+ * Copyright (C) 2010-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.
+ */
+
+#define LOG_GROUP LOG_GROUP_DEV_PCI
+#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
+#include <VBox/pci.h>
+#include <VBox/msi.h>
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/log.h>
+
+#include "MsiCommon.h"
+#include "PciInline.h"
+
+
+DECLINLINE(uint16_t) msiGetMessageControl(PPDMPCIDEV pDev)
+{
+ uint32_t idxMessageControl = pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_CONTROL;
+#ifdef IN_RING3
+ if (pciDevIsPassthrough(pDev))
+ return pDev->Int.s.pfnConfigRead(pDev->Int.s.CTX_SUFF(pDevIns), pDev, idxMessageControl, 2);
+#endif
+ return PCIDevGetWord(pDev, idxMessageControl);
+}
+
+DECLINLINE(bool) msiIs64Bit(PPDMPCIDEV pDev)
+{
+ return pciDevIsMsi64Capable(pDev);
+}
+
+/** @todo r=klaus This design assumes that the config space cache is always
+ * up to date, which is a wrong assumption for the "emulate passthrough" case
+ * where only the callbacks give the correct data. */
+DECLINLINE(uint32_t *) msiGetMaskBits(PPDMPCIDEV pDev)
+{
+ uint8_t iOff = msiIs64Bit(pDev) ? VBOX_MSI_CAP_MASK_BITS_64 : VBOX_MSI_CAP_MASK_BITS_32;
+ /* devices may have no masked/pending support */
+ if (iOff >= pDev->Int.s.u8MsiCapSize)
+ return NULL;
+ iOff += pDev->Int.s.u8MsiCapOffset;
+ return (uint32_t*)(pDev->abConfig + iOff);
+}
+
+/** @todo r=klaus This design assumes that the config space cache is always
+ * up to date, which is a wrong assumption for the "emulate passthrough" case
+ * where only the callbacks give the correct data. */
+DECLINLINE(uint32_t*) msiGetPendingBits(PPDMPCIDEV pDev)
+{
+ uint8_t iOff = msiIs64Bit(pDev) ? VBOX_MSI_CAP_PENDING_BITS_64 : VBOX_MSI_CAP_PENDING_BITS_32;
+ /* devices may have no masked/pending support */
+ if (iOff >= pDev->Int.s.u8MsiCapSize)
+ return NULL;
+ iOff += pDev->Int.s.u8MsiCapOffset;
+ return (uint32_t*)(pDev->abConfig + iOff);
+}
+
+DECLINLINE(bool) msiIsEnabled(PPDMPCIDEV pDev)
+{
+ return (msiGetMessageControl(pDev) & VBOX_PCI_MSI_FLAGS_ENABLE) != 0;
+}
+
+DECLINLINE(uint8_t) msiGetMme(PPDMPCIDEV pDev)
+{
+ return (msiGetMessageControl(pDev) & VBOX_PCI_MSI_FLAGS_QSIZE) >> 4;
+}
+
+DECLINLINE(RTGCPHYS) msiGetMsiAddress(PPDMPCIDEV pDev)
+{
+ if (msiIs64Bit(pDev))
+ {
+ uint32_t lo = PCIDevGetDWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_ADDRESS_LO);
+ uint32_t hi = PCIDevGetDWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_ADDRESS_HI);
+ return RT_MAKE_U64(lo, hi);
+ }
+ return PCIDevGetDWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_ADDRESS_32);
+}
+
+DECLINLINE(uint32_t) msiGetMsiData(PPDMPCIDEV pDev, int32_t iVector)
+{
+ int16_t iOff = msiIs64Bit(pDev) ? VBOX_MSI_CAP_MESSAGE_DATA_64 : VBOX_MSI_CAP_MESSAGE_DATA_32;
+ uint16_t lo = PCIDevGetWord(pDev, pDev->Int.s.u8MsiCapOffset + iOff);
+
+ // vector encoding into lower bits of message data
+ uint8_t bits = msiGetMme(pDev);
+ uint16_t uMask = ((1 << bits) - 1);
+ lo &= ~uMask;
+ lo |= iVector & uMask;
+
+ return RT_MAKE_U32(lo, 0);
+}
+
+#ifdef IN_RING3
+
+DECLINLINE(bool) msiR3BitJustCleared(uint32_t uOldValue, uint32_t uNewValue, uint32_t uMask)
+{
+ return !!(uOldValue & uMask) && !(uNewValue & uMask);
+}
+
+DECLINLINE(bool) msiR3BitJustSet(uint32_t uOldValue, uint32_t uNewValue, uint32_t uMask)
+{
+ return !(uOldValue & uMask) && !!(uNewValue & uMask);
+}
+
+/**
+ * PCI config space accessors for MSI registers.
+ */
+void MsiR3PciConfigWrite(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev,
+ uint32_t u32Address, uint32_t val, unsigned len)
+{
+ int32_t iOff = u32Address - pDev->Int.s.u8MsiCapOffset;
+ Assert(iOff >= 0 && (pciDevIsMsiCapable(pDev) && iOff < pDev->Int.s.u8MsiCapSize));
+
+ Log2(("MsiR3PciConfigWrite: %d <- %x (%d)\n", iOff, val, len));
+
+ uint32_t uAddr = u32Address;
+ bool f64Bit = msiIs64Bit(pDev);
+
+ for (uint32_t i = 0; i < len; i++)
+ {
+ uint32_t reg = i + iOff;
+ uint8_t u8Val = (uint8_t)val;
+ switch (reg)
+ {
+ case 0: /* Capability ID, ro */
+ case 1: /* Next pointer, ro */
+ break;
+ case VBOX_MSI_CAP_MESSAGE_CONTROL:
+ /* don't change read-only bits: 1-3,7 */
+ u8Val &= UINT8_C(~0x8e);
+ pDev->abConfig[uAddr] = u8Val | (pDev->abConfig[uAddr] & UINT8_C(0x8e));
+ break;
+ case VBOX_MSI_CAP_MESSAGE_CONTROL + 1:
+ /* don't change read-only bit 8, and reserved 9-15 */
+ break;
+ default:
+ if (pDev->abConfig[uAddr] != u8Val)
+ {
+ int32_t maskUpdated = -1;
+
+ /* If we're enabling masked vector, and have pending messages
+ for this vector, we have to send this message now */
+ if ( !f64Bit
+ && (reg >= VBOX_MSI_CAP_MASK_BITS_32)
+ && (reg < VBOX_MSI_CAP_MASK_BITS_32 + 4)
+ )
+ {
+ maskUpdated = reg - VBOX_MSI_CAP_MASK_BITS_32;
+ }
+ if ( f64Bit
+ && (reg >= VBOX_MSI_CAP_MASK_BITS_64)
+ && (reg < VBOX_MSI_CAP_MASK_BITS_64 + 4)
+ )
+ {
+ maskUpdated = reg - VBOX_MSI_CAP_MASK_BITS_64;
+ }
+
+ if (maskUpdated != -1 && msiIsEnabled(pDev))
+ {
+ uint32_t* puPending = msiGetPendingBits(pDev);
+ for (int iBitNum = 0; iBitNum < 8; iBitNum++)
+ {
+ int32_t iBit = 1 << iBitNum;
+ uint32_t uVector = maskUpdated*8 + iBitNum;
+
+ if (msiR3BitJustCleared(pDev->abConfig[uAddr], u8Val, iBit))
+ {
+ Log(("msi: mask updated bit %d@%x (%d)\n", iBitNum, uAddr, maskUpdated));
+
+ /* To ensure that we're no longer masked */
+ pDev->abConfig[uAddr] &= ~iBit;
+ if ((*puPending & (1 << uVector)) != 0)
+ {
+ Log(("msi: notify earlier masked pending vector: %d\n", uVector));
+ MsiNotify(pDevIns, pPciHlp, pDev, uVector, PDM_IRQ_LEVEL_HIGH, 0 /*uTagSrc*/);
+ }
+ }
+ if (msiR3BitJustSet(pDev->abConfig[uAddr], u8Val, iBit))
+ {
+ Log(("msi: mask vector: %d\n", uVector));
+ }
+ }
+ }
+
+ pDev->abConfig[uAddr] = u8Val;
+ }
+ }
+ uAddr++;
+ val >>= 8;
+ }
+}
+
+/**
+ * Initializes MSI support for the given PCI device.
+ */
+int MsiR3Init(PPDMPCIDEV pDev, PPDMMSIREG pMsiReg)
+{
+ if (pMsiReg->cMsiVectors == 0)
+ return VINF_SUCCESS;
+
+ /* XXX: done in pcirawAnalyzePciCaps() */
+ if (pciDevIsPassthrough(pDev))
+ return VINF_SUCCESS;
+
+ uint16_t cVectors = pMsiReg->cMsiVectors;
+ uint8_t iCapOffset = pMsiReg->iMsiCapOffset;
+ uint8_t iNextOffset = pMsiReg->iMsiNextOffset;
+ bool f64bit = pMsiReg->fMsi64bit;
+ bool fNoMasking = pMsiReg->fMsiNoMasking;
+ uint16_t iFlags = 0;
+
+ Assert(iCapOffset != 0 && iCapOffset < 0xff && iNextOffset < 0xff);
+
+ if (!fNoMasking)
+ {
+ int iMmc;
+
+ /* Compute multiple-message capable bitfield */
+ for (iMmc = 0; iMmc < 6; iMmc++)
+ {
+ if ((1 << iMmc) >= cVectors)
+ break;
+ }
+
+ if ((cVectors > VBOX_MSI_MAX_ENTRIES) || (1 << iMmc) < cVectors)
+ return VERR_TOO_MUCH_DATA;
+
+ /* We support per-vector masking */
+ iFlags |= VBOX_PCI_MSI_FLAGS_MASKBIT;
+ /* How many vectors we're capable of */
+ iFlags |= iMmc;
+ }
+
+ if (f64bit)
+ iFlags |= VBOX_PCI_MSI_FLAGS_64BIT;
+
+ pDev->Int.s.u8MsiCapOffset = iCapOffset;
+ pDev->Int.s.u8MsiCapSize = f64bit ? VBOX_MSI_CAP_SIZE_64 : VBOX_MSI_CAP_SIZE_32;
+
+ PCIDevSetByte(pDev, iCapOffset + 0, VBOX_PCI_CAP_ID_MSI);
+ PCIDevSetByte(pDev, iCapOffset + 1, iNextOffset); /* next */
+ PCIDevSetWord(pDev, iCapOffset + VBOX_MSI_CAP_MESSAGE_CONTROL, iFlags);
+
+ if (!fNoMasking)
+ {
+ *msiGetMaskBits(pDev) = 0;
+ *msiGetPendingBits(pDev) = 0;
+ }
+
+ pciDevSetMsiCapable(pDev);
+ if (f64bit)
+ pciDevSetMsi64Capable(pDev);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* IN_RING3 */
+
+
+/**
+ * Checks if MSI is enabled for the given PCI device.
+ *
+ * (Must use MSINotify() for notifications when true.)
+ */
+bool MsiIsEnabled(PPDMPCIDEV pDev)
+{
+ return pciDevIsMsiCapable(pDev) && msiIsEnabled(pDev);
+}
+
+/**
+ * Device notification (aka interrupt).
+ */
+void MsiNotify(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, int iVector, int iLevel, uint32_t uTagSrc)
+{
+ AssertMsg(msiIsEnabled(pDev), ("Must be enabled to use that"));
+
+ uint32_t uMask;
+ uint32_t *puPending = msiGetPendingBits(pDev);
+ if (puPending)
+ {
+ uint32_t *puMask = msiGetMaskBits(pDev);
+ AssertPtr(puMask);
+ uMask = *puMask;
+ LogFlow(("MsiNotify: %d pending=%x mask=%x\n", iVector, *puPending, uMask));
+ }
+ else
+ {
+ uMask = 0;
+ LogFlow(("MsiNotify: %d\n", iVector));
+ }
+
+ /* We only trigger MSI on level up */
+ if ((iLevel & PDM_IRQ_LEVEL_HIGH) == 0)
+ {
+ /** @todo maybe clear pending interrupts on level down? */
+#if 0
+ if (puPending)
+ {
+ *puPending &= ~(1<<iVector);
+ LogFlow(("msi: clear pending %d, now %x\n", iVector, *puPending));
+ }
+#endif
+ return;
+ }
+
+ if ((uMask & (1<<iVector)) != 0)
+ {
+ *puPending |= (1<<iVector);
+ LogFlow(("msi: %d is masked, mark pending, now %x\n", iVector, *puPending));
+ return;
+ }
+
+ RTGCPHYS GCAddr = msiGetMsiAddress(pDev);
+ uint32_t u32Value = msiGetMsiData(pDev, iVector);
+
+ if (puPending)
+ *puPending &= ~(1<<iVector);
+
+ Assert(pPciHlp->pfnIoApicSendMsi != NULL);
+ pPciHlp->pfnIoApicSendMsi(pDevIns, GCAddr, u32Value, uTagSrc);
+}
+
diff --git a/src/VBox/Devices/Bus/MsiCommon.h b/src/VBox/Devices/Bus/MsiCommon.h
new file mode 100644
index 00000000..142e98f2
--- /dev/null
+++ b/src/VBox/Devices/Bus/MsiCommon.h
@@ -0,0 +1,41 @@
+/* $Id: MsiCommon.h $ */
+/** @file
+ * Header for MSI/MSI-X support routines.
+ */
+
+/*
+ * Copyright (C) 2010-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Bus_MsiCommon_h
+#define VBOX_INCLUDED_SRC_Bus_MsiCommon_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+typedef CTX_SUFF(PCPDMPCIHLP) PCPDMPCIHLP;
+
+#ifdef IN_RING3
+int MsiR3Init(PPDMPCIDEV pDev, PPDMMSIREG pMsiReg);
+void MsiR3PciConfigWrite(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, uint32_t u32Address, uint32_t val, unsigned len);
+#endif
+bool MsiIsEnabled(PPDMPCIDEV pDev);
+void MsiNotify(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, int iVector, int iLevel, uint32_t uTagSrc);
+
+#ifdef IN_RING3
+int MsixR3Init(PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, PPDMMSIREG pMsiReg);
+void MsixR3PciConfigWrite(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, uint32_t u32Address, uint32_t val, unsigned len);
+#endif
+bool MsixIsEnabled(PPDMPCIDEV pDev);
+void MsixNotify(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, int iVector, int iLevel, uint32_t uTagSrc);
+
+#endif /* !VBOX_INCLUDED_SRC_Bus_MsiCommon_h */
+
diff --git a/src/VBox/Devices/Bus/MsixCommon.cpp b/src/VBox/Devices/Bus/MsixCommon.cpp
new file mode 100644
index 00000000..051dc7e8
--- /dev/null
+++ b/src/VBox/Devices/Bus/MsixCommon.cpp
@@ -0,0 +1,357 @@
+/* $Id: MsixCommon.cpp $ */
+/** @file
+ * MSI-X support routines
+ */
+
+/*
+ * Copyright (C) 2010-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.
+ */
+
+
+#define LOG_GROUP LOG_GROUP_DEV_PCI
+#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
+#include <VBox/pci.h>
+#include <VBox/msi.h>
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/log.h>
+#include <VBox/vmm/mm.h>
+
+#include <iprt/assert.h>
+
+#include "MsiCommon.h"
+#include "PciInline.h"
+
+typedef struct
+{
+ uint32_t u32MsgAddressLo;
+ uint32_t u32MsgAddressHi;
+ uint32_t u32MsgData;
+ uint32_t u32VectorControl;
+} MsixTableRecord;
+AssertCompileSize(MsixTableRecord, VBOX_MSIX_ENTRY_SIZE);
+
+
+/** @todo use accessors so that raw PCI devices work correctly with MSI-X. */
+DECLINLINE(uint16_t) msixGetMessageControl(PPDMPCIDEV pDev)
+{
+ return PCIDevGetWord(pDev, pDev->Int.s.u8MsixCapOffset + VBOX_MSIX_CAP_MESSAGE_CONTROL);
+}
+
+DECLINLINE(bool) msixIsEnabled(PPDMPCIDEV pDev)
+{
+ return (msixGetMessageControl(pDev) & VBOX_PCI_MSIX_FLAGS_ENABLE) != 0;
+}
+
+DECLINLINE(bool) msixIsMasked(PPDMPCIDEV pDev)
+{
+ return (msixGetMessageControl(pDev) & VBOX_PCI_MSIX_FLAGS_FUNCMASK) != 0;
+}
+
+#ifdef IN_RING3
+DECLINLINE(uint16_t) msixTableSize(PPDMPCIDEV pDev)
+{
+ return (msixGetMessageControl(pDev) & 0x3ff) + 1;
+}
+#endif
+
+DECLINLINE(uint8_t *) msixGetPageOffset(PPDMPCIDEV pDev, uint32_t off)
+{
+ return (uint8_t *)pDev->Int.s.CTX_SUFF(pMsixPage) + off;
+}
+
+DECLINLINE(MsixTableRecord *) msixGetVectorRecord(PPDMPCIDEV pDev, uint32_t iVector)
+{
+ return (MsixTableRecord *)msixGetPageOffset(pDev, iVector * VBOX_MSIX_ENTRY_SIZE);
+}
+
+DECLINLINE(RTGCPHYS) msixGetMsiAddress(PPDMPCIDEV pDev, uint32_t iVector)
+{
+ MsixTableRecord *pRec = msixGetVectorRecord(pDev, iVector);
+ return RT_MAKE_U64(pRec->u32MsgAddressLo & ~UINT32_C(0x3), pRec->u32MsgAddressHi);
+}
+
+DECLINLINE(uint32_t) msixGetMsiData(PPDMPCIDEV pDev, uint32_t iVector)
+{
+ return msixGetVectorRecord(pDev, iVector)->u32MsgData;
+}
+
+DECLINLINE(uint32_t) msixIsVectorMasked(PPDMPCIDEV pDev, uint32_t iVector)
+{
+ return (msixGetVectorRecord(pDev, iVector)->u32VectorControl & 0x1) != 0;
+}
+
+DECLINLINE(uint8_t *) msixPendingByte(PPDMPCIDEV pDev, uint32_t iVector)
+{
+ return msixGetPageOffset(pDev, pDev->Int.s.offMsixPba + iVector / 8);
+}
+
+DECLINLINE(void) msixSetPending(PPDMPCIDEV pDev, uint32_t iVector)
+{
+ *msixPendingByte(pDev, iVector) |= (1 << (iVector & 0x7));
+}
+
+DECLINLINE(void) msixClearPending(PPDMPCIDEV pDev, uint32_t iVector)
+{
+ *msixPendingByte(pDev, iVector) &= ~(1 << (iVector & 0x7));
+}
+
+#ifdef IN_RING3
+
+DECLINLINE(bool) msixR3IsPending(PPDMPCIDEV pDev, uint32_t iVector)
+{
+ return (*msixPendingByte(pDev, iVector) & (1 << (iVector & 0x7))) != 0;
+}
+
+static void msixR3CheckPendingVector(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, uint32_t iVector)
+{
+ if (msixR3IsPending(pDev, iVector) && !msixIsVectorMasked(pDev, iVector))
+ MsixNotify(pDevIns, pPciHlp, pDev, iVector, 1 /* iLevel */, 0 /*uTagSrc*/);
+}
+
+
+PDMBOTHCBDECL(int) msixR3MMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
+{
+ LogFlowFunc(("\n"));
+
+ uint32_t off = (uint32_t)(GCPhysAddr & 0xffff);
+ PPDMPCIDEV pPciDev = (PPDMPCIDEV)pvUser;
+
+ /// @todo qword accesses?
+ RT_NOREF(pDevIns);
+ AssertMsgReturn(cb == 4,
+ ("MSI-X must be accessed with 4-byte reads"),
+ VERR_INTERNAL_ERROR);
+ AssertMsgReturn(off + cb <= pPciDev->Int.s.cbMsixRegion,
+ ("Out of bounds access for the MSI-X region\n"),
+ VINF_IOM_MMIO_UNUSED_FF);
+
+ *(uint32_t *)pv = *(uint32_t *)msixGetPageOffset(pPciDev, off);
+ return VINF_SUCCESS;
+}
+
+PDMBOTHCBDECL(int) msixR3MMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
+{
+ LogFlowFunc(("\n"));
+
+ PPDMPCIDEV pPciDev = (PPDMPCIDEV)pvUser;
+ uint32_t off = (uint32_t)(GCPhysAddr & 0xffff);
+
+ /// @todo qword accesses?
+ AssertMsgReturn(cb == 4,
+ ("MSI-X must be accessed with 4-byte reads"),
+ VERR_INTERNAL_ERROR);
+ AssertMsgReturn(off + cb <= pPciDev->Int.s.offMsixPba,
+ ("Trying to write to PBA\n"), VINF_SUCCESS);
+
+ *(uint32_t *)msixGetPageOffset(pPciDev, off) = *(uint32_t *)pv;
+
+ msixR3CheckPendingVector(pDevIns, (PCPDMPCIHLP)pPciDev->Int.s.pPciBusPtrR3, pPciDev, off / VBOX_MSIX_ENTRY_SIZE);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNPCIIOREGIONMAP}
+ */
+static DECLCALLBACK(int) msixR3Map(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
+ RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
+{
+ Assert(enmType == PCI_ADDRESS_SPACE_MEM);
+ NOREF(iRegion); NOREF(enmType);
+
+ int rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, pPciDev,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ msixR3MMIOWrite, msixR3MMIORead, "MSI-X tables");
+
+ if (RT_FAILURE(rc))
+ return rc;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initalizes MSI-X support for the given PCI device.
+ */
+int MsixR3Init(PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, PPDMMSIREG pMsiReg)
+{
+ if (pMsiReg->cMsixVectors == 0)
+ return VINF_SUCCESS;
+
+ /* We cannot init MSI-X on raw devices yet. */
+ Assert(!pciDevIsPassthrough(pDev));
+
+ uint16_t cVectors = pMsiReg->cMsixVectors;
+ uint8_t iCapOffset = pMsiReg->iMsixCapOffset;
+ uint8_t iNextOffset = pMsiReg->iMsixNextOffset;
+ uint8_t iBar = pMsiReg->iMsixBar;
+
+ AssertMsgReturn(cVectors <= VBOX_MSIX_MAX_ENTRIES,
+ ("Too many MSI-X vectors: %d\n", cVectors),
+ VERR_TOO_MUCH_DATA);
+ AssertMsgReturn(iBar <= 5,
+ ("Using wrong BAR for MSI-X: %d\n", iBar),
+ VERR_INVALID_PARAMETER);
+
+ Assert(iCapOffset != 0 && iCapOffset < 0xff && iNextOffset < 0xff);
+
+ int rc = VINF_SUCCESS;
+ uint16_t cbPba = cVectors / 8;
+ if (cVectors % 8)
+ cbPba++;
+ uint16_t cbMsixRegion = RT_ALIGN_T(cVectors * sizeof(MsixTableRecord) + cbPba, _4K, uint16_t);
+
+ /* If device is passthrough, BAR is registered using common mechanism. */
+ if (!pciDevIsPassthrough(pDev))
+ {
+ rc = PDMDevHlpPCIIORegionRegister(pDev->Int.s.CTX_SUFF(pDevIns), iBar, cbMsixRegion, PCI_ADDRESS_SPACE_MEM, msixR3Map);
+ if (RT_FAILURE (rc))
+ return rc;
+ }
+
+ uint16_t offTable = 0;
+ uint16_t offPBA = cVectors * sizeof(MsixTableRecord);
+
+ pDev->Int.s.u8MsixCapOffset = iCapOffset;
+ pDev->Int.s.u8MsixCapSize = VBOX_MSIX_CAP_SIZE;
+ pDev->Int.s.cbMsixRegion = cbMsixRegion;
+ pDev->Int.s.offMsixPba = offPBA;
+ PVM pVM = PDMDevHlpGetVM(pDev->Int.s.CTX_SUFF(pDevIns));
+
+ pDev->Int.s.pMsixPageR3 = NULL;
+
+ rc = MMHyperAlloc(pVM, cbMsixRegion, 1, MM_TAG_PDM_DEVICE_USER, (void **)&pDev->Int.s.pMsixPageR3);
+ if (RT_FAILURE(rc) || (pDev->Int.s.pMsixPageR3 == NULL))
+ return VERR_NO_VM_MEMORY;
+ RT_BZERO(pDev->Int.s.pMsixPageR3, cbMsixRegion);
+ pDev->Int.s.pMsixPageR0 = MMHyperR3ToR0(pVM, pDev->Int.s.pMsixPageR3);
+ pDev->Int.s.pMsixPageRC = MMHyperR3ToRC(pVM, pDev->Int.s.pMsixPageR3);
+
+ /* R3 PCI helper */
+ pDev->Int.s.pPciBusPtrR3 = pPciHlp;
+
+ PCIDevSetByte(pDev, iCapOffset + 0, VBOX_PCI_CAP_ID_MSIX);
+ PCIDevSetByte(pDev, iCapOffset + 1, iNextOffset); /* next */
+ PCIDevSetWord(pDev, iCapOffset + VBOX_MSIX_CAP_MESSAGE_CONTROL, cVectors - 1);
+
+ PCIDevSetDWord(pDev, iCapOffset + VBOX_MSIX_TABLE_BIROFFSET, offTable | iBar);
+ PCIDevSetDWord(pDev, iCapOffset + VBOX_MSIX_PBA_BIROFFSET, offPBA | iBar);
+
+ pciDevSetMsixCapable(pDev);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Checks if MSI-X is enabled for the tiven PCI device.
+ *
+ * (Must use MSIXNotify() for notifications when true.)
+ */
+bool MsixIsEnabled(PPDMPCIDEV pDev)
+{
+ return pciDevIsMsixCapable(pDev) && msixIsEnabled(pDev);
+}
+
+/**
+ * Device notification (aka interrupt).
+ */
+void MsixNotify(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, int iVector, int iLevel, uint32_t uTagSrc)
+{
+ AssertMsg(msixIsEnabled(pDev), ("Must be enabled to use that"));
+
+ Assert(pPciHlp->pfnIoApicSendMsi != NULL);
+
+ /* We only trigger MSI-X on level up */
+ if ((iLevel & PDM_IRQ_LEVEL_HIGH) == 0)
+ {
+ return;
+ }
+
+ // if this vector is somehow disabled
+ if (msixIsMasked(pDev) || msixIsVectorMasked(pDev, iVector))
+ {
+ // mark pending bit
+ msixSetPending(pDev, iVector);
+ return;
+ }
+
+ // clear pending bit
+ msixClearPending(pDev, iVector);
+
+ RTGCPHYS GCAddr = msixGetMsiAddress(pDev, iVector);
+ uint32_t u32Value = msixGetMsiData(pDev, iVector);
+
+ pPciHlp->pfnIoApicSendMsi(pDevIns, GCAddr, u32Value, uTagSrc);
+}
+
+#ifdef IN_RING3
+
+DECLINLINE(bool) msixR3BitJustCleared(uint32_t uOldValue, uint32_t uNewValue, uint32_t uMask)
+{
+ return !!(uOldValue & uMask) && !(uNewValue & uMask);
+}
+
+
+static void msixR3CheckPendingVectors(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev)
+{
+ for (uint32_t i = 0; i < msixTableSize(pDev); i++)
+ msixR3CheckPendingVector(pDevIns, pPciHlp, pDev, i);
+}
+
+/**
+ * PCI config space accessors for MSI-X.
+ */
+void MsixR3PciConfigWrite(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, uint32_t u32Address, uint32_t val, unsigned len)
+{
+ int32_t iOff = u32Address - pDev->Int.s.u8MsixCapOffset;
+ Assert(iOff >= 0 && (pciDevIsMsixCapable(pDev) && iOff < pDev->Int.s.u8MsixCapSize));
+
+ Log2(("MsixR3PciConfigWrite: %d <- %x (%d)\n", iOff, val, len));
+
+ uint32_t uAddr = u32Address;
+ uint8_t u8NewVal;
+ bool fJustEnabled = false;
+
+ for (uint32_t i = 0; i < len; i++)
+ {
+ uint32_t reg = i + iOff;
+ uint8_t u8Val = (uint8_t)val;
+ switch (reg)
+ {
+ case 0: /* Capability ID, ro */
+ case 1: /* Next pointer, ro */
+ break;
+ case VBOX_MSIX_CAP_MESSAGE_CONTROL:
+ /* don't change read-only bits: 0-7 */
+ break;
+ case VBOX_MSIX_CAP_MESSAGE_CONTROL + 1:
+ {
+ /* don't change read-only bits 8-13 */
+ u8NewVal = (u8Val & UINT8_C(~0x3f)) | (pDev->abConfig[uAddr] & UINT8_C(0x3f));
+ /* If just enabled globally - check pending vectors */
+ fJustEnabled |= msixR3BitJustCleared(pDev->abConfig[uAddr], u8NewVal, VBOX_PCI_MSIX_FLAGS_ENABLE >> 8);
+ fJustEnabled |= msixR3BitJustCleared(pDev->abConfig[uAddr], u8NewVal, VBOX_PCI_MSIX_FLAGS_FUNCMASK >> 8);
+ pDev->abConfig[uAddr] = u8NewVal;
+ break;
+ }
+ default:
+ /* other fields read-only too */
+ break;
+ }
+ uAddr++;
+ val >>= 8;
+ }
+
+ if (fJustEnabled)
+ msixR3CheckPendingVectors(pDevIns, pPciHlp, pDev);
+}
+
+#endif /* IN_RING3 */
diff --git a/src/VBox/Devices/Bus/PciInline.h b/src/VBox/Devices/Bus/PciInline.h
new file mode 100644
index 00000000..bf5a3821
--- /dev/null
+++ b/src/VBox/Devices/Bus/PciInline.h
@@ -0,0 +1,105 @@
+/* $Id: PciInline.h $ */
+/** @file
+ * PCI - The PCI Controller And Devices, inline device helpers.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Bus_PciInline_h
+#define VBOX_INCLUDED_SRC_Bus_PciInline_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+DECLINLINE(void) pciDevSetPci2PciBridge(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags |= PCIDEV_FLAG_PCI_TO_PCI_BRIDGE;
+}
+
+DECLINLINE(bool) pciDevIsPci2PciBridge(PPDMPCIDEV pDev)
+{
+ return (pDev->Int.s.fFlags & PCIDEV_FLAG_PCI_TO_PCI_BRIDGE) != 0;
+}
+
+DECLINLINE(void) pciDevSetPciExpress(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags |= PCIDEV_FLAG_PCI_EXPRESS_DEVICE;
+}
+
+DECLINLINE(bool) pciDevIsPciExpress(PPDMPCIDEV pDev)
+{
+ return (pDev->Int.s.fFlags & PCIDEV_FLAG_PCI_EXPRESS_DEVICE) != 0;
+}
+
+DECLINLINE(void) pciDevSetMsiCapable(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags |= PCIDEV_FLAG_MSI_CAPABLE;
+}
+
+DECLINLINE(void) pciDevClearMsiCapable(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags &= ~PCIDEV_FLAG_MSI_CAPABLE;
+}
+
+DECLINLINE(bool) pciDevIsMsiCapable(PPDMPCIDEV pDev)
+{
+ return (pDev->Int.s.fFlags & PCIDEV_FLAG_MSI_CAPABLE) != 0;
+}
+
+DECLINLINE(void) pciDevSetMsi64Capable(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags |= PCIDEV_FLAG_MSI64_CAPABLE;
+}
+
+DECLINLINE(void) pciDevClearMsi64Capable(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags &= ~PCIDEV_FLAG_MSI64_CAPABLE;
+}
+
+DECLINLINE(bool) pciDevIsMsi64Capable(PPDMPCIDEV pDev)
+{
+ return (pDev->Int.s.fFlags & PCIDEV_FLAG_MSI64_CAPABLE) != 0;
+}
+
+DECLINLINE(void) pciDevSetMsixCapable(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags |= PCIDEV_FLAG_MSIX_CAPABLE;
+}
+
+DECLINLINE(void) pciDevClearMsixCapable(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags &= ~PCIDEV_FLAG_MSIX_CAPABLE;
+}
+
+DECLINLINE(bool) pciDevIsMsixCapable(PPDMPCIDEV pDev)
+{
+ return (pDev->Int.s.fFlags & PCIDEV_FLAG_MSIX_CAPABLE) != 0;
+}
+
+DECLINLINE(void) pciDevSetPassthrough(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags |= PCIDEV_FLAG_PASSTHROUGH;
+}
+
+DECLINLINE(void) pciDevClearPassthrough(PPDMPCIDEV pDev)
+{
+ pDev->Int.s.fFlags &= ~PCIDEV_FLAG_PASSTHROUGH;
+}
+
+DECLINLINE(bool) pciDevIsPassthrough(PPDMPCIDEV pDev)
+{
+ return (pDev->Int.s.fFlags & PCIDEV_FLAG_PASSTHROUGH) != 0;
+}
+
+#endif /* !VBOX_INCLUDED_SRC_Bus_PciInline_h */
+
diff --git a/src/VBox/Devices/Bus/SrvPciRawR0.cpp b/src/VBox/Devices/Bus/SrvPciRawR0.cpp
new file mode 100644
index 00000000..3d248e86
--- /dev/null
+++ b/src/VBox/Devices/Bus/SrvPciRawR0.cpp
@@ -0,0 +1,1031 @@
+/* $Id: SrvPciRawR0.cpp $ */
+/** @file
+ * PCI passthrough - The ring 0 service.
+ */
+
+/*
+ * Copyright (C) 2011-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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_PCI_RAW
+#include <VBox/log.h>
+#include <VBox/sup.h>
+#include <VBox/rawpci.h>
+#include <VBox/vmm/pdmpci.h>
+#include <VBox/vmm/pdm.h>
+#include <VBox/vmm/gvm.h>
+#include <VBox/vmm/gvmm.h>
+#include <VBox/vmm/vm.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/handletable.h>
+#include <iprt/mp.h>
+#include <iprt/mem.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct PCIRAWSRVSTATE
+{
+ /** Structure lock. */
+ RTSPINLOCK hSpinlock;
+
+ /** Handle table for devices. */
+ RTHANDLETABLE hHtDevs;
+
+} PCIRAWSRVSTATE;
+typedef PCIRAWSRVSTATE *PPCIRAWSRVSTATE;
+
+typedef struct PCIRAWDEV
+{
+ /* Port pointer. */
+ PRAWPCIDEVPORT pPort;
+
+ /* Handle used by everybody else. */
+ PCIRAWDEVHANDLE hHandle;
+
+ /** The session this device is associated with. */
+ PSUPDRVSESSION pSession;
+
+ /** Structure lock. */
+ RTSPINLOCK hSpinlock;
+
+ /** Event for IRQ updates. */
+ RTSEMEVENT hIrqEvent;
+
+ /** Current pending IRQ for the device. */
+ int32_t iPendingIrq;
+
+ /** ISR handle. */
+ PCIRAWISRHANDLE hIsr;
+
+ /* If object is being destroyed. */
+ bool fTerminate;
+
+ /** The SUPR0 object. */
+ void *pvObj;
+} PCIRAWDEV;
+typedef PCIRAWDEV *PPCIRAWDEV;
+
+static PCIRAWSRVSTATE g_State;
+
+
+/** Interrupt handler. Could be called in the interrupt context,
+ * depending on host OS implmenetation. */
+static DECLCALLBACK(bool) pcirawr0Isr(void* pContext, int32_t iHostIrq)
+{
+ PPCIRAWDEV pThis = (PPCIRAWDEV)pContext;
+
+#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS
+ uint16_t uStatus;
+ PCIRAWMEMLOC Loc;
+ int rc;
+
+ Loc.cb = 2;
+ rc = pThis->pPort->pfnPciCfgRead(pThis->pPort, VBOX_PCI_STATUS, &Loc);
+ /* Cannot read, assume non-shared. */
+ if (RT_FAILURE(rc))
+ return false;
+
+ /* Check interrupt status bit. */
+ if ((Loc.u.u16 & (1 << 3)) == 0)
+ return false;
+#endif
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ pThis->iPendingIrq = iHostIrq;
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /**
+ * @todo RTSemEventSignal() docs claims that it's platform-dependent
+ * if RTSemEventSignal() could be called from the ISR, but it seems IPRT
+ * doesn't provide primitives that guaranteed to work this way.
+ */
+ RTSemEventSignal(pThis->hIrqEvent);
+
+ return true;
+}
+
+static DECLCALLBACK(int) pcirawr0DevRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
+{
+ NOREF(pvUser);
+ NOREF(hHandleTable);
+ PPCIRAWDEV pDev = (PPCIRAWDEV)pvObj;
+ if (pDev->hHandle != 0)
+ return SUPR0ObjAddRefEx(pDev->pvObj, (PSUPDRVSESSION)pvCtx, true /* fNoBlocking */);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initializes the raw PCI ring-0 service.
+ *
+ * @returns VBox status code.
+ */
+PCIRAWR0DECL(int) PciRawR0Init(void)
+{
+ LogFlow(("PciRawR0Init:\n"));
+ int rc = VINF_SUCCESS;
+
+ rc = RTHandleTableCreateEx(&g_State.hHtDevs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
+ UINT32_C(0xfefe0000), 4096, pcirawr0DevRetainHandle, NULL);
+
+ LogFlow(("PciRawR0Init: returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Destroys raw PCI ring-0 service.
+ */
+PCIRAWR0DECL(void) PciRawR0Term(void)
+{
+ LogFlow(("PciRawR0Term:\n"));
+ RTHandleTableDestroy(g_State.hHtDevs, NULL, NULL);
+ g_State.hHtDevs = NIL_RTHANDLETABLE;
+}
+
+
+/**
+ * Per-VM R0 module init.
+ */
+PCIRAWR0DECL(int) PciRawR0InitVM(PGVM pGVM, PVM pVM)
+{
+ PRAWPCIFACTORY pFactory = NULL;
+ int rc = SUPR0ComponentQueryFactory(pGVM->pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory);
+ if (RT_SUCCESS(rc))
+ {
+ if (pFactory)
+ {
+ rc = pFactory->pfnInitVm(pFactory, pVM, &pGVM->rawpci.s);
+ pFactory->pfnRelease(pFactory);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Per-VM R0 module termination routine.
+ */
+PCIRAWR0DECL(void) PciRawR0TermVM(PGVM pGVM, PVM pVM)
+{
+ PRAWPCIFACTORY pFactory = NULL;
+ int rc = SUPR0ComponentQueryFactory(pGVM->pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory);
+ if (RT_SUCCESS(rc))
+ {
+ if (pFactory)
+ {
+ pFactory->pfnDeinitVm(pFactory, pVM, &pGVM->rawpci.s);
+ pFactory->pfnRelease(pFactory);
+ }
+ }
+}
+
+static int pcirawr0DevTerm(PPCIRAWDEV pThis, int32_t fFlags)
+{
+ ASMAtomicWriteBool(&pThis->fTerminate, true);
+
+ if (pThis->hIrqEvent)
+ RTSemEventSignal(pThis->hIrqEvent);
+
+ /* Enable that, once figure our how to make sure
+ IRQ getter thread notified and woke up. */
+#if 0
+ if (pThis->hIrqEvent)
+ {
+ RTSemEventDestroy(pThis->hIrqEvent);
+ pThis->hIrqEvent = NIL_RTSEMEVENT;
+ }
+#endif
+
+ if (pThis->hSpinlock)
+ {
+ RTSpinlockDestroy(pThis->hSpinlock);
+ pThis->hSpinlock = NIL_RTSPINLOCK;
+ }
+
+ /* Forcefully deinit. */
+ return pThis->pPort->pfnDeinit(pThis->pPort, fFlags);
+}
+
+#define GET_PORT(hDev) \
+ PPCIRAWDEV pDev = (PPCIRAWDEV)RTHandleTableLookupWithCtx(g_State.hHtDevs, hDev, pSession); \
+ if (!pDev) \
+ return VERR_INVALID_HANDLE; \
+ PRAWPCIDEVPORT pDevPort = pDev->pPort; \
+ AssertReturn(pDevPort != NULL, VERR_INVALID_PARAMETER); \
+ AssertReturn(pDevPort->u32Version == RAWPCIDEVPORT_VERSION, VERR_INVALID_PARAMETER); \
+ AssertReturn(pDevPort->u32VersionEnd == RAWPCIDEVPORT_VERSION, VERR_INVALID_PARAMETER);
+
+#define PUT_PORT() if (pDev->pvObj) SUPR0ObjRelease(pDev->pvObj, pSession)
+
+#ifdef DEBUG_nike
+
+/* Code to perform debugging without host driver. */
+typedef struct DUMMYRAWPCIINS
+{
+ /* Host PCI address of this device. */
+ uint32_t HostPciAddress;
+ /* Padding */
+ uint32_t pad0;
+
+ uint8_t aPciCfg[256];
+
+ /** Port, given to the outside world. */
+ RAWPCIDEVPORT DevPort;
+} DUMMYRAWPCIINS;
+typedef struct DUMMYRAWPCIINS *PDUMMYRAWPCIINS;
+
+#define DEVPORT_2_DUMMYRAWPCIINS(pPort) \
+ ( (PDUMMYRAWPCIINS)((uint8_t *)pPort - RT_UOFFSETOF(DUMMYRAWPCIINS, DevPort)) )
+
+static uint8_t dummyPciGetByte(PDUMMYRAWPCIINS pThis, uint32_t iRegister)
+{
+ return pThis->aPciCfg[iRegister];
+}
+
+static void dummyPciSetByte(PDUMMYRAWPCIINS pThis, uint32_t iRegister, uint8_t u8)
+{
+ pThis->aPciCfg[iRegister] = u8;
+}
+
+static uint16_t dummyPciGetWord(PDUMMYRAWPCIINS pThis, uint32_t iRegister)
+{
+ uint16_t u16Value = *(uint16_t*)&pThis->aPciCfg[iRegister];
+ return RT_H2LE_U16(u16Value);
+}
+
+static void dummyPciSetWord(PDUMMYRAWPCIINS pThis, uint32_t iRegister, uint16_t u16)
+{
+ *(uint16_t*)&pThis->aPciCfg[iRegister] = RT_H2LE_U16(u16);
+}
+
+static uint32_t dummyPciGetDWord(PDUMMYRAWPCIINS pThis, uint32_t iRegister)
+{
+ uint32_t u32Value = *(uint32_t*)&pThis->aPciCfg[iRegister];
+ return RT_H2LE_U32(u32Value);
+}
+
+static void dummyPciSetDWord(PDUMMYRAWPCIINS pThis, uint32_t iRegister, uint32_t u32)
+{
+ *(uint32_t*)&pThis->aPciCfg[iRegister] = RT_H2LE_U32(u32);
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnInit
+ */
+static DECLCALLBACK(int) dummyPciDevInit(PRAWPCIDEVPORT pPort, uint32_t fFlags)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ dummyPciSetWord(pThis, VBOX_PCI_VENDOR_ID, 0xccdd);
+ dummyPciSetWord(pThis, VBOX_PCI_DEVICE_ID, 0xeeff);
+ dummyPciSetWord(pThis, VBOX_PCI_COMMAND, PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS | PCI_COMMAND_BUSMASTER);
+ dummyPciSetByte(pThis, VBOX_PCI_INTERRUPT_PIN, 1);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnDeinit
+ */
+static DECLCALLBACK(int) dummyPciDevDeinit(PRAWPCIDEVPORT pPort, uint32_t fFlags)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnDestroy
+ */
+static DECLCALLBACK(int) dummyPciDevDestroy(PRAWPCIDEVPORT pPort)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnGetRegionInfo
+ */
+static DECLCALLBACK(int) dummyPciDevGetRegionInfo(PRAWPCIDEVPORT pPort,
+ int32_t iRegion,
+ RTHCPHYS *pRegionStart,
+ uint64_t *pu64RegionSize,
+ bool *pfPresent,
+ uint32_t *pfFlags)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ if (iRegion == 0)
+ {
+ *pfPresent = true;
+ *pRegionStart = 0xfef0;
+ *pu64RegionSize = 0x10;
+ *pfFlags = PCIRAW_ADDRESS_SPACE_IO;
+ }
+ else if (iRegion == 2)
+ {
+ *pfPresent = true;
+ *pRegionStart = 0xffff0000;
+ *pu64RegionSize = 0x1000;
+ *pfFlags = PCIRAW_ADDRESS_SPACE_BAR64 | PCIRAW_ADDRESS_SPACE_MEM;
+ }
+ else
+ *pfPresent = false;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnMapRegion
+ */
+static DECLCALLBACK(int) dummyPciDevMapRegion(PRAWPCIDEVPORT pPort,
+ int32_t iRegion,
+ RTHCPHYS HCRegionStart,
+ uint64_t u64RegionSize,
+ int32_t fFlags,
+ RTR0PTR *pRegionBase)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnUnapRegion
+ */
+static DECLCALLBACK(int) dummyPciDevUnmapRegion(PRAWPCIDEVPORT pPort,
+ int32_t iRegion,
+ RTHCPHYS HCRegionStart,
+ uint64_t u64RegionSize,
+ RTR0PTR RegionBase)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnPciCfgRead
+ */
+static DECLCALLBACK(int) dummyPciDevPciCfgRead(PRAWPCIDEVPORT pPort,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ switch (pValue->cb)
+ {
+ case 1:
+ pValue->u.u8 = dummyPciGetByte(pThis, Register);
+ break;
+ case 2:
+ pValue->u.u16 = dummyPciGetWord(pThis, Register);
+ break;
+ case 4:
+ pValue->u.u32 = dummyPciGetDWord(pThis, Register);
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnPciCfgWrite
+ */
+static DECLCALLBACK(int) dummyPciDevPciCfgWrite(PRAWPCIDEVPORT pPort,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ switch (pValue->cb)
+ {
+ case 1:
+ dummyPciSetByte(pThis, Register, pValue->u.u8);
+ break;
+ case 2:
+ dummyPciSetWord(pThis, Register, pValue->u.u16);
+ break;
+ case 4:
+ dummyPciSetDWord(pThis, Register, pValue->u.u32);
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) dummyPciDevRegisterIrqHandler(PRAWPCIDEVPORT pPort,
+ PFNRAWPCIISR pfnHandler,
+ void* pIrqContext,
+ PCIRAWISRHANDLE *phIsr)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) dummyPciDevUnregisterIrqHandler(PRAWPCIDEVPORT pPort,
+ PCIRAWISRHANDLE hIsr)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) dummyPciDevPowerStateChange(PRAWPCIDEVPORT pPort,
+ PCIRAWPOWERSTATE aState,
+ uint64_t *pu64Param)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+static PRAWPCIDEVPORT pcirawr0CreateDummyDevice(uint32_t HostDevice, uint32_t fFlags)
+{
+ PDUMMYRAWPCIINS pNew = (PDUMMYRAWPCIINS)RTMemAllocZ(sizeof(*pNew));
+ if (!pNew)
+ return NULL;
+
+ pNew->HostPciAddress = HostDevice;
+
+ pNew->DevPort.u32Version = RAWPCIDEVPORT_VERSION;
+ pNew->DevPort.pfnInit = dummyPciDevInit;
+ pNew->DevPort.pfnDeinit = dummyPciDevDeinit;
+ pNew->DevPort.pfnDestroy = dummyPciDevDestroy;
+ pNew->DevPort.pfnGetRegionInfo = dummyPciDevGetRegionInfo;
+ pNew->DevPort.pfnMapRegion = dummyPciDevMapRegion;
+ pNew->DevPort.pfnUnmapRegion = dummyPciDevUnmapRegion;
+ pNew->DevPort.pfnPciCfgRead = dummyPciDevPciCfgRead;
+ pNew->DevPort.pfnPciCfgWrite = dummyPciDevPciCfgWrite;
+ pNew->DevPort.pfnRegisterIrqHandler = dummyPciDevRegisterIrqHandler;
+ pNew->DevPort.pfnUnregisterIrqHandler = dummyPciDevUnregisterIrqHandler;
+ pNew->DevPort.pfnPowerStateChange = dummyPciDevPowerStateChange;
+
+ pNew->DevPort.u32VersionEnd = RAWPCIDEVPORT_VERSION;
+
+ return &pNew->DevPort;
+}
+
+#endif /* DEBUG_nike */
+
+static DECLCALLBACK(void) pcirawr0DevObjDestructor(void *pvObj, void *pvIns, void *pvUnused)
+{
+ PPCIRAWDEV pThis = (PPCIRAWDEV)pvIns;
+ NOREF(pvObj); NOREF(pvUnused);
+
+ /* Forcefully deinit. */
+ pcirawr0DevTerm(pThis, 0);
+
+ /* And destroy. */
+ pThis->pPort->pfnDestroy(pThis->pPort);
+
+ RTMemFree(pThis);
+}
+
+
+static int pcirawr0OpenDevice(PGVM pGVM, PVM pVM, PSUPDRVSESSION pSession,
+ uint32_t HostDevice,
+ uint32_t fFlags,
+ PCIRAWDEVHANDLE *pHandle,
+ uint32_t *pfDevFlags)
+{
+
+ int rc = GVMMR0ValidateGVMandVMandEMT(pGVM, pVM, 0 /*idCpu*/);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Query the factory we want, then use it create and connect the host device.
+ */
+ PPCIRAWDEV pNew = (PPCIRAWDEV)RTMemAllocZ(sizeof(*pNew));
+ if (!pNew)
+ return VERR_NO_MEMORY;
+
+ PRAWPCIFACTORY pFactory = NULL;
+ rc = SUPR0ComponentQueryFactory(pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory);
+ /* No host driver registered, provide some fake implementation
+ for debugging purposes. */
+ PRAWPCIDEVPORT pDevPort = NULL;
+#ifdef DEBUG_nike
+ if (rc == VERR_SUPDRV_COMPONENT_NOT_FOUND)
+ {
+ pDevPort = pcirawr0CreateDummyDevice(HostDevice, fFlags);
+ if (pDevPort)
+ {
+ pDevPort->pfnInit(pDevPort, fFlags);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+#endif
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pFactory)
+ {
+ rc = pFactory->pfnCreateAndConnect(pFactory,
+ HostDevice,
+ fFlags,
+ &pGVM->rawpci.s,
+ &pDevPort,
+ pfDevFlags);
+ pFactory->pfnRelease(pFactory);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSpinlockCreate(&pNew->hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "PciRaw");
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pNew->hIrqEvent);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ pNew->pSession = pSession;
+ pNew->pPort = pDevPort;
+ pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_RAW_PCI_DEVICE,
+ pcirawr0DevObjDestructor, pNew, NULL);
+ if (pNew->pvObj)
+ {
+
+ uint32_t hHandle = 0;
+ rc = RTHandleTableAllocWithCtx(g_State.hHtDevs, pNew, pSession, &hHandle);
+ if (RT_SUCCESS(rc))
+ {
+ pNew->hHandle = (PCIRAWDEVHANDLE)hHandle;
+ *pHandle = pNew->hHandle;
+ return rc;
+ }
+ SUPR0ObjRelease(pNew->pvObj, pSession);
+ }
+ RTSemEventDestroy(pNew->hIrqEvent);
+ }
+ RTSpinlockDestroy(pNew->hSpinlock);
+ }
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pNew);
+
+ return rc;
+}
+
+static int pcirawr0CloseDevice(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint32_t fFlags)
+{
+ GET_PORT(TargetDevice);
+ int rc;
+
+ pDevPort->pfnUnregisterIrqHandler(pDevPort, pDev->hIsr);
+ pDev->hIsr = 0;
+
+ rc = pcirawr0DevTerm(pDev, fFlags);
+
+ RTHandleTableFreeWithCtx(g_State.hHtDevs, TargetDevice, pSession);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+/* We may want to call many functions here directly, so no static */
+static int pcirawr0GetRegionInfo(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ int32_t iRegion,
+ RTHCPHYS *pRegionStart,
+ uint64_t *pu64RegionSize,
+ bool *pfPresent,
+ uint32_t *pfFlags)
+{
+ LogFlow(("pcirawr0GetRegionInfo: %d\n", iRegion));
+ GET_PORT(TargetDevice);
+
+ int rc = pDevPort->pfnGetRegionInfo(pDevPort, iRegion, pRegionStart, pu64RegionSize, pfPresent, pfFlags);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0MapRegion(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ int32_t iRegion,
+ RTHCPHYS HCRegionStart,
+ uint64_t u64RegionSize,
+ uint32_t fFlags,
+ RTR3PTR *ppvAddressR3,
+ RTR0PTR *ppvAddressR0)
+{
+ LogFlow(("pcirawr0MapRegion\n"));
+ GET_PORT(TargetDevice);
+ int rc;
+
+ rc = pDevPort->pfnMapRegion(pDevPort, iRegion, HCRegionStart, u64RegionSize, fFlags, ppvAddressR0);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(*ppvAddressR0 != NULL);
+
+ /* Do we need to do something to help with R3 mapping, if ((fFlags & PCIRAWRFLAG_ALLOW_R3MAP) != 0) */
+ }
+
+ *ppvAddressR3 = 0;
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0UnmapRegion(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ int32_t iRegion,
+ RTHCPHYS HCRegionStart,
+ uint64_t u64RegionSize,
+ RTR3PTR pvAddressR3,
+ RTR0PTR pvAddressR0)
+{
+ LogFlow(("pcirawr0UnmapRegion\n"));
+ int rc;
+ NOREF(pSession); NOREF(pvAddressR3);
+
+ GET_PORT(TargetDevice);
+
+ rc = pDevPort->pfnUnmapRegion(pDevPort, iRegion, HCRegionStart, u64RegionSize, pvAddressR0);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0PioWrite(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint16_t Port,
+ uint32_t u32,
+ unsigned cb)
+{
+ NOREF(pSession); NOREF(TargetDevice);
+ /// @todo add check that port fits into device range
+ switch (cb)
+ {
+ case 1:
+ ASMOutU8 (Port, u32);
+ break;
+ case 2:
+ ASMOutU16(Port, u32);
+ break;
+ case 4:
+ ASMOutU32(Port, u32);
+ break;
+ default:
+ AssertMsgFailed(("Unhandled port write: %d\n", cb));
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static int pcirawr0PioRead(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint16_t Port,
+ uint32_t *pu32,
+ unsigned cb)
+{
+ NOREF(pSession); NOREF(TargetDevice);
+ /// @todo add check that port fits into device range
+ switch (cb)
+ {
+ case 1:
+ *pu32 = ASMInU8 (Port);
+ break;
+ case 2:
+ *pu32 = ASMInU16(Port);
+ break;
+ case 4:
+ *pu32 = ASMInU32(Port);
+ break;
+ default:
+ AssertMsgFailed(("Unhandled port read: %d\n", cb));
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static int pcirawr0MmioRead(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ RTR0PTR Address,
+ PCIRAWMEMLOC *pValue)
+{
+ NOREF(pSession); NOREF(TargetDevice);
+ /// @todo add check that address fits into device range
+#if 1
+ switch (pValue->cb)
+ {
+ case 1:
+ pValue->u.u8 = *(uint8_t*)Address;
+ break;
+ case 2:
+ pValue->u.u16 = *(uint16_t*)Address;
+ break;
+ case 4:
+ pValue->u.u32 = *(uint32_t*)Address;
+ break;
+ case 8:
+ pValue->u.u64 = *(uint64_t*)Address;
+ break;
+ }
+#else
+ memset(&pValue->u.u64, 0, 8);
+#endif
+ return VINF_SUCCESS;
+}
+
+static int pcirawr0MmioWrite(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ RTR0PTR Address,
+ PCIRAWMEMLOC *pValue)
+{
+ NOREF(pSession); NOREF(TargetDevice);
+ /// @todo add check that address fits into device range
+#if 1
+ switch (pValue->cb)
+ {
+ case 1:
+ *(uint8_t*)Address = pValue->u.u8;
+ break;
+ case 2:
+ *(uint16_t*)Address = pValue->u.u16;
+ break;
+ case 4:
+ *(uint32_t*)Address = pValue->u.u32;
+ break;
+ case 8:
+ *(uint64_t*)Address = pValue->u.u64;
+ break;
+ }
+#endif
+ return VINF_SUCCESS;
+}
+
+static int pcirawr0PciCfgRead(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ GET_PORT(TargetDevice);
+
+ return pDevPort->pfnPciCfgRead(pDevPort, Register, pValue);
+}
+
+static int pcirawr0PciCfgWrite(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ int rc;
+
+ GET_PORT(TargetDevice);
+
+ rc = pDevPort->pfnPciCfgWrite(pDevPort, Register, pValue);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0EnableIrq(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice)
+{
+ int rc = VINF_SUCCESS;
+ GET_PORT(TargetDevice);
+
+ rc = pDevPort->pfnRegisterIrqHandler(pDevPort, pcirawr0Isr, pDev,
+ &pDev->hIsr);
+
+ PUT_PORT();
+ return rc;
+}
+
+static int pcirawr0DisableIrq(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice)
+{
+ int rc = VINF_SUCCESS;
+ GET_PORT(TargetDevice);
+
+ rc = pDevPort->pfnUnregisterIrqHandler(pDevPort, pDev->hIsr);
+ pDev->hIsr = 0;
+
+ PUT_PORT();
+ return rc;
+}
+
+static int pcirawr0GetIrq(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ int64_t iTimeout,
+ int32_t *piIrq)
+{
+ int rc = VINF_SUCCESS;
+ bool fTerminate = false;
+ int32_t iPendingIrq = 0;
+
+ LogFlow(("pcirawr0GetIrq\n"));
+
+ GET_PORT(TargetDevice);
+
+ RTSpinlockAcquire(pDev->hSpinlock);
+ iPendingIrq = pDev->iPendingIrq;
+ pDev->iPendingIrq = 0;
+ fTerminate = pDev->fTerminate;
+ RTSpinlockRelease(pDev->hSpinlock);
+
+ /* Block until new IRQs arrives */
+ if (!fTerminate)
+ {
+ if (iPendingIrq == 0)
+ {
+ rc = RTSemEventWaitNoResume(pDev->hIrqEvent, iTimeout);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo racy */
+ if (!ASMAtomicReadBool(&pDev->fTerminate))
+ {
+ RTSpinlockAcquire(pDev->hSpinlock);
+ iPendingIrq = pDev->iPendingIrq;
+ pDev->iPendingIrq = 0;
+ RTSpinlockRelease(pDev->hSpinlock);
+ }
+ else
+ rc = VERR_INTERRUPTED;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ *piIrq = iPendingIrq;
+ }
+ else
+ rc = VERR_INTERRUPTED;
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0PowerStateChange(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ PCIRAWPOWERSTATE aState,
+ uint64_t *pu64Param)
+{
+ LogFlow(("pcirawr0PowerStateChange\n"));
+ GET_PORT(TargetDevice);
+
+ int rc = pDevPort->pfnPowerStateChange(pDevPort, aState, pu64Param);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+/**
+ * Process PCI raw request
+ *
+ * @returns VBox status code.
+ */
+PCIRAWR0DECL(int) PciRawR0ProcessReq(PGVM pGVM, PVM pVM, PSUPDRVSESSION pSession, PPCIRAWSENDREQ pReq)
+{
+ LogFlow(("PciRawR0ProcessReq: %d for %x\n", pReq->iRequest, pReq->TargetDevice));
+ int rc = VINF_SUCCESS;
+
+ /* Route request to the host driver */
+ switch (pReq->iRequest)
+ {
+ case PCIRAWR0_DO_OPEN_DEVICE:
+ rc = pcirawr0OpenDevice(pGVM, pVM, pSession,
+ pReq->u.aOpenDevice.PciAddress,
+ pReq->u.aOpenDevice.fFlags,
+ &pReq->u.aOpenDevice.Device,
+ &pReq->u.aOpenDevice.fDevFlags);
+ break;
+ case PCIRAWR0_DO_CLOSE_DEVICE:
+ rc = pcirawr0CloseDevice(pSession,
+ pReq->TargetDevice,
+ pReq->u.aCloseDevice.fFlags);
+ break;
+ case PCIRAWR0_DO_GET_REGION_INFO:
+ rc = pcirawr0GetRegionInfo(pSession,
+ pReq->TargetDevice,
+ pReq->u.aGetRegionInfo.iRegion,
+ &pReq->u.aGetRegionInfo.RegionStart,
+ &pReq->u.aGetRegionInfo.u64RegionSize,
+ &pReq->u.aGetRegionInfo.fPresent,
+ &pReq->u.aGetRegionInfo.fFlags);
+ break;
+ case PCIRAWR0_DO_MAP_REGION:
+ rc = pcirawr0MapRegion(pSession,
+ pReq->TargetDevice,
+ pReq->u.aMapRegion.iRegion,
+ pReq->u.aMapRegion.StartAddress,
+ pReq->u.aMapRegion.iRegionSize,
+ pReq->u.aMapRegion.fFlags,
+ &pReq->u.aMapRegion.pvAddressR3,
+ &pReq->u.aMapRegion.pvAddressR0);
+ break;
+ case PCIRAWR0_DO_UNMAP_REGION:
+ rc = pcirawr0UnmapRegion(pSession,
+ pReq->TargetDevice,
+ pReq->u.aUnmapRegion.iRegion,
+ pReq->u.aUnmapRegion.StartAddress,
+ pReq->u.aUnmapRegion.iRegionSize,
+ pReq->u.aUnmapRegion.pvAddressR3,
+ pReq->u.aUnmapRegion.pvAddressR0);
+ break;
+ case PCIRAWR0_DO_PIO_WRITE:
+ rc = pcirawr0PioWrite(pSession,
+ pReq->TargetDevice,
+ pReq->u.aPioWrite.iPort,
+ pReq->u.aPioWrite.iValue,
+ pReq->u.aPioWrite.cb);
+ break;
+ case PCIRAWR0_DO_PIO_READ:
+ rc = pcirawr0PioRead(pSession,
+ pReq->TargetDevice,
+ pReq->u.aPioRead.iPort,
+ &pReq->u.aPioWrite.iValue,
+ pReq->u.aPioRead.cb);
+ break;
+ case PCIRAWR0_DO_MMIO_WRITE:
+ rc = pcirawr0MmioWrite(pSession,
+ pReq->TargetDevice,
+ pReq->u.aMmioWrite.Address,
+ &pReq->u.aMmioWrite.Value);
+ break;
+ case PCIRAWR0_DO_MMIO_READ:
+ rc = pcirawr0MmioRead(pSession,
+ pReq->TargetDevice,
+ pReq->u.aMmioRead.Address,
+ &pReq->u.aMmioRead.Value);
+ break;
+ case PCIRAWR0_DO_PCICFG_WRITE:
+ rc = pcirawr0PciCfgWrite(pSession,
+ pReq->TargetDevice,
+ pReq->u.aPciCfgWrite.iOffset,
+ &pReq->u.aPciCfgWrite.Value);
+ break;
+ case PCIRAWR0_DO_PCICFG_READ:
+ rc = pcirawr0PciCfgRead(pSession,
+ pReq->TargetDevice,
+ pReq->u.aPciCfgRead.iOffset,
+ &pReq->u.aPciCfgRead.Value);
+ break;
+ case PCIRAWR0_DO_ENABLE_IRQ:
+ rc = pcirawr0EnableIrq(pSession,
+ pReq->TargetDevice);
+ break;
+ case PCIRAWR0_DO_DISABLE_IRQ:
+ rc = pcirawr0DisableIrq(pSession,
+ pReq->TargetDevice);
+ break;
+ case PCIRAWR0_DO_GET_IRQ:
+ rc = pcirawr0GetIrq(pSession,
+ pReq->TargetDevice,
+ pReq->u.aGetIrq.iTimeout,
+ &pReq->u.aGetIrq.iIrq);
+ break;
+ case PCIRAWR0_DO_POWER_STATE_CHANGE:
+ rc = pcirawr0PowerStateChange(pSession,
+ pReq->TargetDevice,
+ (PCIRAWPOWERSTATE)pReq->u.aPowerStateChange.iState,
+ &pReq->u.aPowerStateChange.u64Param);
+ break;
+ default:
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ LogFlow(("PciRawR0ProcessReq: returns %Rrc\n", rc));
+ return rc;
+}
+