summaryrefslogtreecommitdiff
path: root/src/VBox/Devices/Bus/DevPciGenericEcam.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Bus/DevPciGenericEcam.cpp')
-rw-r--r--src/VBox/Devices/Bus/DevPciGenericEcam.cpp457
1 files changed, 457 insertions, 0 deletions
diff --git a/src/VBox/Devices/Bus/DevPciGenericEcam.cpp b/src/VBox/Devices/Bus/DevPciGenericEcam.cpp
new file mode 100644
index 00000000000..49ef9d54fba
--- /dev/null
+++ b/src/VBox/Devices/Bus/DevPciGenericEcam.cpp
@@ -0,0 +1,457 @@
+/* $Id$ */
+/** @file
+ * DevPciGeneric - Generic host to PCIe bridge emulation.
+ */
+
+/*
+ * Copyright (C) 2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_PCI
+#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
+#include <VBox/vmm/pdmpcidev.h>
+
+#include <VBox/AssertGuest.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 *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Saved state version of the generic ECAM PCI bus device. */
+#define VBOX_PCIGENECAM_SAVED_STATE_VERSION 1
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+static DECLCALLBACK(void) pciGenEcamSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTagSrc)
+{
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PDEVPCIBUSCC pBusCC = PDMINS_2_DATA_CC(pDevIns, PDEVPCIBUSCC);
+ uint8_t uDevFn = pPciDev->uDevFn;
+
+ LogFlowFunc(("invoked by %p/%d: iIrq=%d iLevel=%d uTagSrc=%#x\n", pDevIns, pDevIns->iInstance, iIrq, iLevel, 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));
+ MsiNotify(pDevIns, pBusCC->CTX_SUFF(pPciHlp), pPciDev, iIrq, iLevel, uTagSrc);
+ return;
+ }
+
+ if (MsixIsEnabled(pPciDev))
+ {
+ LogFlowFunc(("PCI Dev %p : MSI-X\n", pPciDev));
+ MsixNotify(pDevIns, pBusCC->CTX_SUFF(pPciHlp), pPciDev, iIrq, iLevel, uTagSrc);
+ return;
+ }
+
+ PDEVPCIBUS pBus = &pPciRoot->PciBus;
+ 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 */
+ }
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWWRITE,
+ * Emulates writes to configuration space.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) pciHostR3MmioPioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
+{
+ Log2Func(("%RGp LB %d\n", off, cb));
+ RT_NOREF(pvUser);
+
+ AssertReturn(off < _64K, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= 4, VERR_INVALID_PARAMETER);
+
+ /* Get the value. */
+ uint32_t u32;
+ switch (cb)
+ {
+ case 1:
+ u32 = *(uint8_t const *)pv;
+ break;
+ case 2:
+ u32 = *(uint16_t const *)pv;
+ break;
+ case 4:
+ u32 = *(uint32_t const *)pv;
+ break;
+ default:
+ ASSERT_GUEST_MSG_FAILED(("cb=%u off=%RGp\n", cb, off)); /** @todo how the heck should this work? Split it, right? */
+ u32 = 0;
+ break;
+ }
+
+ return PDMDevHlpIoPortWrite(pDevIns, (RTIOPORT)off, u32, cb);
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWWRITE,
+ * Emulates reads from configuration space.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) pciHostR3MmioPioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
+{
+ LogFlowFunc(("%RGp LB %u\n", off, cb));
+ RT_NOREF(pvUser);
+
+ AssertReturn(off < _64K, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= 4, VERR_INVALID_PARAMETER);
+
+ /* Perform configuration space read */
+ uint32_t u32Value = 0;
+ VBOXSTRICTRC rcStrict = PDMDevHlpIoPortRead(pDevIns, (RTIOPORT)off, &u32Value, cb);
+
+ if (RT_SUCCESS(rcStrict))
+ {
+ switch (cb)
+ {
+ case 1:
+ *(uint8_t *)pv = (uint8_t)u32Value;
+ break;
+ case 2:
+ *(uint16_t *)pv = (uint16_t)u32Value;
+ break;
+ case 4:
+ *(uint32_t *)pv = u32Value;
+ break;
+ default:
+ ASSERT_GUEST_MSG_FAILED(("cb=%u off=%RGp\n", cb, off)); /** @todo how the heck should this work? Split it, right? */
+ break;
+ }
+ }
+
+ return rcStrict;
+}
+
+
+#ifdef IN_RING3
+
+/* -=-=-=-=-=- PCI Config Space -=-=-=-=-=- */
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) pciGenEcamR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ RT_NOREF1(iInstance);
+ Assert(iInstance == 0);
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+ PDEVPCIBUSCC pBusCC = PDMINS_2_DATA_CC(pDevIns, PDEVPCIBUSCC);
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ PDEVPCIBUS pBus = &pPciRoot->PciBus;
+
+ /*
+ * Validate and read configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MmioEcamBase|MmioEcamLength|MmioPioBase|MmioPioSize", "");
+
+ int rc = pHlp->pfnCFGMQueryU64Def(pCfg, "MmioEcamBase", &pPciRoot->u64PciConfigMMioAddress, 0);
+ AssertRCReturn(rc, PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"McfgBase\"")));
+
+ rc = pHlp->pfnCFGMQueryU64Def(pCfg, "MmioEcamLength", &pPciRoot->u64PciConfigMMioLength, 0);
+ AssertRCReturn(rc, PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"McfgLength\"")));
+
+ rc = pHlp->pfnCFGMQueryU64Def(pCfg, "MmioPioBase", &pPciRoot->GCPhysMmioPioEmuBase, 0);
+ AssertRCReturn(rc, PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"MmioPioBase\"")));
+
+ rc = pHlp->pfnCFGMQueryU64Def(pCfg, "MmioPioSize", &pPciRoot->GCPhysMmioPioEmuSize, 0);
+ AssertRCReturn(rc, PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"MmioPioSize\"")));
+
+ Log(("PCI: fUseIoApic=%RTbool McfgBase=%#RX64 McfgLength=%#RX64 fR0Enabled=%RTbool fRCEnabled=%RTbool\n", pPciRoot->fUseIoApic,
+ pPciRoot->u64PciConfigMMioAddress, pPciRoot->u64PciConfigMMioLength, pDevIns->fR0Enabled, pDevIns->fRCEnabled));
+
+ /*
+ * Init data.
+ */
+ /* And fill values */
+ pBusCC->pDevInsR3 = pDevIns;
+ pPciRoot->hIoPortAddress = NIL_IOMIOPORTHANDLE;
+ pPciRoot->hIoPortData = NIL_IOMIOPORTHANDLE;
+ pPciRoot->hIoPortMagic = NIL_IOMIOPORTHANDLE;
+ pPciRoot->hMmioMcfg = NIL_IOMMMIOHANDLE;
+ pPciRoot->hMmioPioEmu = NIL_IOMMMIOHANDLE;
+ pPciRoot->PciBus.enmType = DEVPCIBUSTYPE_GENERIC_ECAM;
+ pPciRoot->PciBus.fPureBridge = false;
+ pPciRoot->PciBus.papBridgesR3 = (PPDMPCIDEV *)PDMDevHlpMMHeapAllocZ(pDevIns, sizeof(PPDMPCIDEV) * RT_ELEMENTS(pPciRoot->PciBus.apDevices));
+ AssertLogRelReturn(pPciRoot->PciBus.papBridgesR3, VERR_NO_MEMORY);
+
+ /*
+ * Disable default device locking.
+ */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Register bus
+ */
+ PDMPCIBUSREGCC PciBusReg;
+ PciBusReg.u32Version = PDM_PCIBUSREGCC_VERSION;
+ PciBusReg.pfnRegisterR3 = devpciR3CommonRegisterDevice;
+ PciBusReg.pfnRegisterMsiR3 = NULL;
+ PciBusReg.pfnIORegionRegisterR3 = devpciR3CommonIORegionRegister;
+ PciBusReg.pfnInterceptConfigAccesses = devpciR3CommonInterceptConfigAccesses;
+ PciBusReg.pfnConfigRead = devpciR3CommonConfigRead;
+ PciBusReg.pfnConfigWrite = devpciR3CommonConfigWrite;
+ PciBusReg.pfnSetIrqR3 = pciGenEcamSetIrq;
+ PciBusReg.u32EndVersion = PDM_PCIBUSREGCC_VERSION;
+ rc = PDMDevHlpPCIBusRegister(pDevIns, &PciBusReg, &pBusCC->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 (pBusCC->pPciHlpR3->u32Version != PDM_PCIHLPR3_VERSION)
+ return PDMDevHlpVMSetError(pDevIns, VERR_VERSION_MISMATCH, RT_SRC_POS,
+ N_("PCI helper version mismatch; got %#x expected %#x"),
+ pBusCC->pPciHlpR3->u32Version, PDM_PCIHLPR3_VERSION);
+
+ /*
+ * Fill in PCI configs and add them to the bus.
+ */
+#if 0
+ /* Host bridge device */
+ PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
+ AssertPtr(pPciDev);
+ PDMPciDevSetVendorId( pPciDev, 0x8086); /** @todo Intel */
+ PDMPciDevSetDeviceId( pPciDev, 0x29e0); /** @todo Desktop */
+ PDMPciDevSetRevisionId(pPciDev, 0x01); /* rev. 01 */
+ PDMPciDevSetClassBase( pPciDev, 0x06); /* bridge */
+ PDMPciDevSetClassSub( pPciDev, 0x00); /* Host/PCI bridge */
+ PDMPciDevSetClassProg( pPciDev, 0x00); /* Host/PCI bridge */
+ PDMPciDevSetHeaderType(pPciDev, 0x00); /* bridge */
+ PDMPciDevSetWord(pPciDev, VBOX_PCI_SEC_STATUS, 0x0280); /* secondary status */
+
+ rc = PDMDevHlpPCIRegisterEx(pDevIns, pPciDev, 0 /*fFlags*/, 0 /*uPciDevNo*/, 0 /*uPciFunNo*/, "Host");
+ AssertLogRelRCReturn(rc, rc);
+#endif
+
+ /*
+ * MMIO handlers.
+ */
+ if (pPciRoot->u64PciConfigMMioAddress != 0)
+ {
+ rc = PDMDevHlpMmioCreateAndMap(pDevIns, pPciRoot->u64PciConfigMMioAddress, pPciRoot->u64PciConfigMMioLength,
+ devpciCommonMcfgMmioWrite, devpciCommonMcfgMmioRead,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ "ECAM window", &pPciRoot->hMmioMcfg);
+ AssertMsgRCReturn(rc, ("rc=%Rrc %#RX64/%#RX64\n", rc, pPciRoot->u64PciConfigMMioAddress, pPciRoot->u64PciConfigMMioLength), rc);
+ }
+
+ if (pPciRoot->GCPhysMmioPioEmuBase != 0)
+ {
+ rc = PDMDevHlpMmioCreateAndMap(pDevIns, pPciRoot->GCPhysMmioPioEmuBase, pPciRoot->GCPhysMmioPioEmuSize,
+ pciHostR3MmioPioWrite, pciHostR3MmioPioRead,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ "PIO range", &pPciRoot->hMmioPioEmu);
+ AssertMsgRCReturn(rc, ("rc=%Rrc %#RGp/%#RGp\n", rc, pPciRoot->GCPhysMmioPioEmuBase, pPciRoot->GCPhysMmioPioEmuSize), rc);
+ }
+
+ /*
+ * Saved state and info handlers.
+ */
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, VBOX_PCIGENECAM_SAVED_STATE_VERSION,
+ sizeof(*pBus) + 16*128, "pgm",
+ NULL, NULL, NULL,
+ NULL, devpciR3CommonSaveExec, NULL,
+ NULL, devpciR3CommonLoadExec, NULL);
+ AssertRCReturn(rc, 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);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) pciGenEcamR3Destruct(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;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) pciGenEcamR3Reset(PPDMDEVINS pDevIns)
+{
+ /* Reset everything under the root bridge. */
+ devpciR3CommonResetBridge(pDevIns);
+}
+
+#else /* !IN_RING3 */
+
+/**
+ * @interface_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+DECLCALLBACK(int) pciGenEcamRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVPCIROOT pPciRoot = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
+ PDEVPCIBUSCC pBusCC = PDMINS_2_DATA_CC(pDevIns, PDEVPCIBUSCC);
+
+ /* Mirror the ring-3 device lock disabling: */
+ int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ /* Set up the RZ PCI bus callbacks: */
+ PDMPCIBUSREGCC PciBusReg;
+ PciBusReg.u32Version = PDM_PCIBUSREGCC_VERSION;
+ PciBusReg.iBus = pPciRoot->PciBus.iBus;
+ PciBusReg.pfnSetIrq = pciGenEcamSetIrq;
+ PciBusReg.u32EndVersion = PDM_PCIBUSREGCC_VERSION;
+ rc = PDMDevHlpPCIBusSetUpContext(pDevIns, &PciBusReg, &pBusCC->CTX_SUFF(pPciHlp));
+ AssertRCReturn(rc, rc);
+
+ /* Set up MMIO callbacks: */
+ if (pPciRoot->hMmioMcfg != NIL_IOMMMIOHANDLE)
+ {
+ rc = PDMDevHlpMmioSetUpContext(pDevIns, pPciRoot->hMmioMcfg, devpciCommonMcfgMmioWrite, devpciCommonMcfgMmioRead, NULL /*pvUser*/);
+ AssertLogRelRCReturn(rc, rc);
+ }
+
+ return rc;
+}
+
+#endif /* !IN_RING3 */
+
+/**
+ * The PCI bus device registration structure.
+ */
+const PDMDEVREG g_DevicePciGenericEcam =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "pci-generic-ecam",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_BUS_PCI,
+ /* .cMaxInstances = */ 1,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(DEVPCIROOT),
+ /* .cbInstanceCC = */ sizeof(CTX_SUFF(DEVPCIBUS)),
+ /* .cbInstanceRC = */ sizeof(DEVPCIBUSRC),
+ /* .cMaxPciDevices = */ 1,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "Generic PCI host bridge (working with pci-host-ecam-generic driver)",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ pciGenEcamR3Construct,
+ /* .pfnDestruct = */ pciGenEcamR3Destruct,
+ /* .pfnRelocate = */ NULL,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ pciGenEcamR3Reset,
+ /* .pfnSuspend = */ NULL,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ NULL,
+ /* .pfnDetach = */ NULL,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ NULL,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ pciGenEcamRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ pciGenEcamRZConstruct,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};