summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Serial/DevOxPcie958.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Serial/DevOxPcie958.cpp')
-rw-r--r--src/VBox/Devices/Serial/DevOxPcie958.cpp710
1 files changed, 710 insertions, 0 deletions
diff --git a/src/VBox/Devices/Serial/DevOxPcie958.cpp b/src/VBox/Devices/Serial/DevOxPcie958.cpp
new file mode 100644
index 00000000..e9cccd0b
--- /dev/null
+++ b/src/VBox/Devices/Serial/DevOxPcie958.cpp
@@ -0,0 +1,710 @@
+/* $Id: DevOxPcie958.cpp $ */
+/** @file
+ * DevOxPcie958 - Oxford Semiconductor OXPCIe958 PCI Express bridge to octal serial port emulation
+ */
+
+/*
+ * Copyright (C) 2018-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
+ */
+
+/** @page pg_dev_oxpcie958 OXPCIe958 - Oxford Semiconductor OXPCIe958 PCI Express bridge to octal serial port emulation.
+ * @todo Write something
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_SERIAL
+#include <VBox/pci.h>
+#include <VBox/msi.h>
+#include <VBox/vmm/pdm.h>
+#include <VBox/vmm/pdmpci.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/list.h>
+#include <iprt/asm.h>
+
+#include "VBoxDD.h"
+#include "UartCore.h"
+
+
+/** @name PCI device related constants.
+ * @{ */
+/** The PCI device ID. */
+#define OX958_PCI_DEVICE_ID 0xc308
+/** The PCI vendor ID. */
+#define OX958_PCI_VENDOR_ID 0x1415
+/** Where the MSI capability starts. */
+#define OX958_PCI_MSI_CAP_OFS 0x80
+/** Where the MSI-X capability starts. */
+#define OX958_PCI_MSIX_CAP_OFS (OX958_PCI_MSI_CAP_OFS + VBOX_MSI_CAP_SIZE_64)
+/** The BAR for the MSI-X related functionality. */
+#define OX958_PCI_MSIX_BAR 1
+/** @} */
+
+/** Maximum number of UARTs supported by the device. */
+#define OX958_UARTS_MAX 16
+
+/** Offset op the class code and revision ID register. */
+#define OX958_REG_CC_REV_ID 0x00
+/** Offset fof the UART count register. */
+#define OX958_REG_UART_CNT 0x04
+/** Offset of the global UART IRQ status register. */
+#define OX958_REG_UART_IRQ_STS 0x08
+/** Offset of the global UART IRQ enable register. */
+#define OX958_REG_UART_IRQ_ENABLE 0x0c
+/** Offset of the global UART IRQ disable register. */
+#define OX958_REG_UART_IRQ_DISABLE 0x10
+/** Offset of the global UART wake IRQ enable register. */
+#define OX958_REG_UART_WAKE_IRQ_ENABLE 0x14
+/** Offset of the global UART wake IRQ disable register. */
+#define OX958_REG_UART_WAKE_IRQ_DISABLE 0x18
+/** Offset of the region in MMIO space where the UARTs actually start. */
+#define OX958_REG_UART_REGION_OFFSET 0x1000
+/** Register region size for each UART. */
+#define OX958_REG_UART_REGION_SIZE 0x200
+/** Offset where the DMA channels registers start for each UART. */
+#define OX958_REG_UART_DMA_REGION_OFFSET 0x100
+
+
+/**
+ * Shared OXPCIe958 UART core.
+ */
+typedef struct OX958UART
+{
+ /** The UART core. */
+ UARTCORE UartCore;
+ /** DMA address configured. */
+ RTGCPHYS GCPhysDmaAddr;
+ /** The DMA transfer length configured. */
+ uint32_t cbDmaXfer;
+ /** The DMA status registers. */
+ uint32_t u32RegDmaSts;
+} OX958UART;
+/** Pointer to a shared OXPCIe958 UART core. */
+typedef OX958UART *POX958UART;
+
+/**
+ * Ring-3 OXPCIe958 UART core.
+ */
+typedef struct OX958UARTR3
+{
+ /** The ring-3 UART core. */
+ UARTCORER3 UartCore;
+} OX958UARTR3;
+/** Pointer to a ring-3 OXPCIe958 UART core. */
+typedef OX958UARTR3 *POX958UARTR3;
+
+/**
+ * Ring-0 OXPCIe958 UART core.
+ */
+typedef struct OX958UARTR0
+{
+ /** The ring-0 UART core. */
+ UARTCORER0 UartCore;
+} OX958UARTR0;
+/** Pointer to a ring-0 OXPCIe958 UART core. */
+typedef OX958UARTR0 *POX958UARTR0;
+
+
+/**
+ * Raw-mode OXPCIe958 UART core.
+ */
+typedef struct OX958UARTRC
+{
+ /** The raw-mode UART core. */
+ UARTCORERC UartCore;
+} OX958UARTRC;
+/** Pointer to a raw-mode OXPCIe958 UART core. */
+typedef OX958UARTRC *POX958UARTRC;
+
+/** Current context OXPCIe958 UART core. */
+typedef CTX_SUFF(OX958UART) OX958UARTCC;
+/** Pointer to a current context OXPCIe958 UART core. */
+typedef CTX_SUFF(POX958UART) POX958UARTCC;
+
+
+/**
+ * Shared OXPCIe958 device instance data.
+ */
+typedef struct DEVOX958
+{
+ /** UART global IRQ status. */
+ volatile uint32_t u32RegIrqStsGlob;
+ /** UART global IRQ enable mask. */
+ volatile uint32_t u32RegIrqEnGlob;
+ /** UART wake IRQ enable mask. */
+ volatile uint32_t u32RegIrqEnWake;
+ /** Number of UARTs configured. */
+ uint32_t cUarts;
+ /** Handle to the MMIO region (PCI region \#0). */
+ IOMMMIOHANDLE hMmio;
+ /** The UARTs. */
+ OX958UART aUarts[OX958_UARTS_MAX];
+} DEVOX958;
+/** Pointer to shared OXPCIe958 device instance data. */
+typedef DEVOX958 *PDEVOX958;
+
+/**
+ * Ring-3 OXPCIe958 device instance data.
+ */
+typedef struct DEVOX958R3
+{
+ /** The UARTs. */
+ OX958UARTR3 aUarts[OX958_UARTS_MAX];
+} DEVOX958R3;
+/** Pointer to ring-3 OXPCIe958 device instance data. */
+typedef DEVOX958R3 *PDEVOX958R3;
+
+/**
+ * Ring-0 OXPCIe958 device instance data.
+ */
+typedef struct DEVOX958R0
+{
+ /** The UARTs. */
+ OX958UARTR0 aUarts[OX958_UARTS_MAX];
+} DEVOX958R0;
+/** Pointer to ring-0 OXPCIe958 device instance data. */
+typedef DEVOX958R0 *PDEVOX958R0;
+
+/**
+ * Raw-mode OXPCIe958 device instance data.
+ */
+typedef struct DEVOX958RC
+{
+ /** The UARTs. */
+ OX958UARTRC aUarts[OX958_UARTS_MAX];
+} DEVOX958RC;
+/** Pointer to raw-mode OXPCIe958 device instance data. */
+typedef DEVOX958RC *PDEVOX958RC;
+
+/** Current context OXPCIe958 device instance data. */
+typedef CTX_SUFF(DEVOX958) DEVOX958CC;
+/** Pointer to current context OXPCIe958 device instance data. */
+typedef CTX_SUFF(PDEVOX958) PDEVOX958CC;
+
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+
+/**
+ * Update IRQ status of the device.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared OXPCIe958 device instance data.
+ */
+static void ox958IrqUpdate(PPDMDEVINS pDevIns, PDEVOX958 pThis)
+{
+ uint32_t u32IrqSts = ASMAtomicReadU32(&pThis->u32RegIrqStsGlob);
+ uint32_t u32IrqEn = ASMAtomicReadU32(&pThis->u32RegIrqEnGlob);
+
+ if (u32IrqSts & u32IrqEn)
+ PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_HIGH);
+ else
+ PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
+}
+
+
+/**
+ * Performs a register read from the given UART.
+ *
+ * @returns Strict VBox status code.
+ * @param pDevIns The device instance.
+ * @param pThis The shared OXPCIe958 device instance data.
+ * @param pUart The UART accessed, shared bits.
+ * @param pUartCC The UART accessed, current context bits.
+ * @param offUartReg Offset of the register being read.
+ * @param pv Where to store the read data.
+ * @param cb Number of bytes to read.
+ */
+static VBOXSTRICTRC ox958UartRegRead(PPDMDEVINS pDevIns, PDEVOX958 pThis, POX958UART pUart, POX958UARTCC pUartCC,
+ uint32_t offUartReg, void *pv, unsigned cb)
+{
+ VBOXSTRICTRC rc;
+ RT_NOREF(pThis);
+
+ if (offUartReg >= OX958_REG_UART_DMA_REGION_OFFSET)
+ {
+ /* Access to the DMA registers. */
+ rc = VINF_SUCCESS;
+ }
+ else /* Access UART registers. */
+ rc = uartRegRead(pDevIns, &pUart->UartCore, &pUartCC->UartCore, offUartReg, (uint32_t *)pv, cb);
+
+ return rc;
+}
+
+
+/**
+ * Performs a register write to the given UART.
+ *
+ * @returns Strict VBox status code.
+ * @param pDevIns The device instance.
+ * @param pThis The shared OXPCIe958 device instance data.
+ * @param pUart The UART accessed, shared bits.
+ * @param pUartCC The UART accessed, current context bits.
+ * @param offUartReg Offset of the register being written.
+ * @param pv The data to write.
+ * @param cb Number of bytes to write.
+ */
+static VBOXSTRICTRC ox958UartRegWrite(PPDMDEVINS pDevIns, PDEVOX958 pThis, POX958UART pUart, POX958UARTCC pUartCC,
+ uint32_t offUartReg, const void *pv, unsigned cb)
+{
+ VBOXSTRICTRC rc;
+ RT_NOREF(pThis);
+
+ if (offUartReg >= OX958_REG_UART_DMA_REGION_OFFSET)
+ {
+ /* Access to the DMA registers. */
+ rc = VINF_SUCCESS;
+ }
+ else /* Access UART registers. */
+ rc = uartRegWrite(pDevIns, &pUart->UartCore, &pUartCC->UartCore, offUartReg, *(const uint32_t *)pv, cb);
+
+ return rc;
+}
+
+
+/**
+ * UART core IRQ request callback.
+ *
+ * @param pDevIns The device instance.
+ * @param pUart The UART requesting an IRQ update.
+ * @param iLUN The UART index.
+ * @param iLvl IRQ level requested.
+ */
+static DECLCALLBACK(void) ox958IrqReq(PPDMDEVINS pDevIns, PUARTCORE pUart, unsigned iLUN, int iLvl)
+{
+ RT_NOREF(pUart);
+ PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
+
+ if (iLvl)
+ ASMAtomicOrU32(&pThis->u32RegIrqStsGlob, RT_BIT_32(iLUN));
+ else
+ ASMAtomicAndU32(&pThis->u32RegIrqStsGlob, ~RT_BIT_32(iLUN));
+ ox958IrqUpdate(pDevIns, pThis);
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWREAD}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) ox958MmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
+{
+ PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
+ PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ RT_NOREF(pvUser);
+
+ if (off < OX958_REG_UART_REGION_OFFSET)
+ {
+ uint32_t *pu32 = (uint32_t *)pv;
+ Assert(cb == 4);
+
+ switch ((uint32_t)off)
+ {
+ case OX958_REG_CC_REV_ID:
+ *pu32 = 0x00070002;
+ break;
+ case OX958_REG_UART_CNT:
+ *pu32 = pThis->cUarts;
+ break;
+ case OX958_REG_UART_IRQ_STS:
+ *pu32 = ASMAtomicReadU32(&pThis->u32RegIrqStsGlob);
+ break;
+ case OX958_REG_UART_IRQ_ENABLE:
+ *pu32 = ASMAtomicReadU32(&pThis->u32RegIrqEnGlob);
+ break;
+ case OX958_REG_UART_IRQ_DISABLE:
+ *pu32 = ~ASMAtomicReadU32(&pThis->u32RegIrqEnGlob);
+ break;
+ case OX958_REG_UART_WAKE_IRQ_ENABLE:
+ *pu32 = ASMAtomicReadU32(&pThis->u32RegIrqEnWake);
+ break;
+ case OX958_REG_UART_WAKE_IRQ_DISABLE:
+ *pu32 = ~ASMAtomicReadU32(&pThis->u32RegIrqEnWake);
+ break;
+ default:
+ rc = VINF_IOM_MMIO_UNUSED_00;
+ }
+ }
+ else
+ {
+ /* Figure out the UART accessed from the offset. */
+ off -= OX958_REG_UART_REGION_OFFSET;
+ uint32_t iUart = (uint32_t)off / OX958_REG_UART_REGION_SIZE;
+ uint32_t offUartReg = (uint32_t)off % OX958_REG_UART_REGION_SIZE;
+ if (iUart < RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts)))
+ {
+ POX958UART pUart = &pThis->aUarts[iUart];
+ POX958UARTCC pUartCC = &pThisCC->aUarts[iUart];
+ rc = ox958UartRegRead(pDevIns, pThis, pUart, pUartCC, offUartReg, pv, cb);
+ if (rc == VINF_IOM_R3_IOPORT_READ)
+ rc = VINF_IOM_R3_MMIO_READ;
+ }
+ else
+ rc = VINF_IOM_MMIO_UNUSED_00;
+ }
+
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWWRITE}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) ox958MmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
+{
+ PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
+ PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ RT_NOREF1(pvUser);
+
+ if (off < OX958_REG_UART_REGION_OFFSET)
+ {
+ const uint32_t u32 = *(const uint32_t *)pv;
+ Assert(cb == 4);
+
+ switch ((uint32_t)off)
+ {
+ case OX958_REG_UART_IRQ_ENABLE:
+ ASMAtomicOrU32(&pThis->u32RegIrqEnGlob, u32);
+ ox958IrqUpdate(pDevIns, pThis);
+ break;
+ case OX958_REG_UART_IRQ_DISABLE:
+ ASMAtomicAndU32(&pThis->u32RegIrqEnGlob, ~u32);
+ ox958IrqUpdate(pDevIns, pThis);
+ break;
+ case OX958_REG_UART_WAKE_IRQ_ENABLE:
+ ASMAtomicOrU32(&pThis->u32RegIrqEnWake, u32);
+ break;
+ case OX958_REG_UART_WAKE_IRQ_DISABLE:
+ ASMAtomicAndU32(&pThis->u32RegIrqEnWake, ~u32);
+ break;
+ case OX958_REG_UART_IRQ_STS: /* Readonly */
+ case OX958_REG_CC_REV_ID: /* Readonly */
+ case OX958_REG_UART_CNT: /* Readonly */
+ default:
+ break;
+ }
+ }
+ else
+ {
+ /* Figure out the UART accessed from the offset. */
+ off -= OX958_REG_UART_REGION_OFFSET;
+ uint32_t iUart = (uint32_t)off / OX958_REG_UART_REGION_SIZE;
+ uint32_t offUartReg = (uint32_t)off % OX958_REG_UART_REGION_SIZE;
+ if (iUart < RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts)))
+ {
+ POX958UART pUart = &pThis->aUarts[iUart];
+ POX958UARTCC pUartCC = &pThisCC->aUarts[iUart];
+ rc = ox958UartRegWrite(pDevIns, pThis, pUart, pUartCC, offUartReg, pv, cb);
+ if (rc == VINF_IOM_R3_IOPORT_WRITE)
+ rc = VINF_IOM_R3_MMIO_WRITE;
+ }
+ }
+
+ return rc;
+}
+
+
+#ifdef IN_RING3
+
+/** @interface_method_impl{PDMDEVREG,pfnDetach} */
+static DECLCALLBACK(void) ox958R3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
+ PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
+ AssertReturnVoid(iLUN >= pThis->cUarts);
+
+ RT_NOREF(fFlags);
+
+ return uartR3Detach(pDevIns, &pThis->aUarts[iLUN].UartCore, &pThisCC->aUarts[iLUN].UartCore);
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnAttach} */
+static DECLCALLBACK(int) ox958R3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
+ PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
+
+ RT_NOREF(fFlags);
+
+ if (iLUN >= RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts)))
+ return VERR_PDM_LUN_NOT_FOUND;
+
+ return uartR3Attach(pDevIns, &pThis->aUarts[iLUN].UartCore, &pThisCC->aUarts[iLUN].UartCore, iLUN);
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnReset} */
+static DECLCALLBACK(void) ox958R3Reset(PPDMDEVINS pDevIns)
+{
+ PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
+ PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
+
+ pThis->u32RegIrqStsGlob = 0x00;
+ pThis->u32RegIrqEnGlob = 0x00;
+ pThis->u32RegIrqEnWake = 0x00;
+
+ uint32_t const cUarts = RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts));
+ for (uint32_t i = 0; i < cUarts; i++)
+ uartR3Reset(pDevIns, &pThis->aUarts[i].UartCore, &pThisCC->aUarts[i].UartCore);
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnDestruct} */
+static DECLCALLBACK(int) ox958R3Destruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+ PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
+
+ uint32_t const cUarts = RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts));
+ for (uint32_t i = 0; i < cUarts; i++)
+ uartR3Destruct(pDevIns, &pThis->aUarts[i].UartCore);
+
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnConstruct} */
+static DECLCALLBACK(int) ox958R3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ RT_NOREF(iInstance);
+ PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
+ PDEVOX958R3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ bool fMsiXSupported = false;
+ int rc;
+
+ /*
+ * Init instance data.
+ */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Validate and read configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MsiXSupported|UartCount", "");
+
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "MsiXSupported", &fMsiXSupported, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("OXPCIe958 configuration error: failed to read \"MsiXSupported\" as boolean"));
+
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "UartCount", &pThis->cUarts, OX958_UARTS_MAX);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("OXPCIe958 configuration error: failed to read \"UartCount\" as unsigned 32bit integer"));
+
+ if (!pThis->cUarts || pThis->cUarts > OX958_UARTS_MAX)
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("OXPCIe958 configuration error: \"UartCount\" has invalid value %u (must be in range [1 .. %u]"),
+ pThis->cUarts, OX958_UARTS_MAX);
+
+ /*
+ * Fill PCI config space.
+ */
+ PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
+ PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
+
+ PDMPciDevSetVendorId(pPciDev, OX958_PCI_VENDOR_ID);
+ PDMPciDevSetDeviceId(pPciDev, OX958_PCI_DEVICE_ID);
+ PDMPciDevSetCommand(pPciDev, 0x0000);
+# ifdef VBOX_WITH_MSI_DEVICES
+ PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
+ PDMPciDevSetCapabilityList(pPciDev, OX958_PCI_MSI_CAP_OFS);
+# else
+ PDMPciDevSetCapabilityList(pPciDev, 0x70);
+# endif
+ PDMPciDevSetRevisionId(pPciDev, 0x00);
+ PDMPciDevSetClassBase(pPciDev, 0x07); /* Communication controller. */
+ PDMPciDevSetClassSub(pPciDev, 0x00); /* Serial controller. */
+ PDMPciDevSetClassProg(pPciDev, 0x02); /* 16550. */
+
+ PDMPciDevSetRevisionId(pPciDev, 0x00);
+ PDMPciDevSetSubSystemVendorId(pPciDev, OX958_PCI_VENDOR_ID);
+ PDMPciDevSetSubSystemId(pPciDev, OX958_PCI_DEVICE_ID);
+
+ PDMPciDevSetInterruptLine(pPciDev, 0x00);
+ PDMPciDevSetInterruptPin(pPciDev, 0x01);
+ /** @todo More Capabilities. */
+
+ /*
+ * Register PCI device and I/O region.
+ */
+ rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
+ if (RT_FAILURE(rc))
+ return rc;
+
+# ifdef VBOX_WITH_MSI_DEVICES
+ PDMMSIREG MsiReg;
+ RT_ZERO(MsiReg);
+ MsiReg.cMsiVectors = 1;
+ MsiReg.iMsiCapOffset = OX958_PCI_MSI_CAP_OFS;
+ MsiReg.iMsiNextOffset = OX958_PCI_MSIX_CAP_OFS;
+ MsiReg.fMsi64bit = true;
+ if (fMsiXSupported)
+ {
+ MsiReg.cMsixVectors = VBOX_MSIX_MAX_ENTRIES;
+ MsiReg.iMsixCapOffset = OX958_PCI_MSIX_CAP_OFS;
+ MsiReg.iMsixNextOffset = 0x00;
+ MsiReg.iMsixBar = OX958_PCI_MSIX_BAR;
+ }
+ rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
+ if (RT_FAILURE(rc))
+ {
+ PDMPciDevSetCapabilityList(pPciDev, 0x0);
+ /* That's OK, we can work without MSI */
+ }
+# endif
+
+ rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 0 /*iPciRegion*/, _16K, PCI_ADDRESS_SPACE_MEM,
+ ox958MmioWrite, ox958MmioRead, NULL /*pvUser*/,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ "OxPCIe958", &pThis->hMmio);
+ AssertRCReturn(rc, rc);
+
+
+ /*
+ * Initialize the UARTs.
+ */
+ for (uint32_t i = 0; i < pThis->cUarts; i++)
+ {
+ POX958UART pUart = &pThis->aUarts[i];
+ POX958UARTCC pUartCC = &pThisCC->aUarts[i];
+ rc = uartR3Init(pDevIns, &pUart->UartCore, &pUartCC->UartCore, UARTTYPE_16550A, i, 0, ox958IrqReq);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("OXPCIe958 configuration error: failed to initialize UART %u"), i);
+ }
+
+ ox958R3Reset(pDevIns);
+ return VINF_SUCCESS;
+}
+
+#else /* !IN_RING3 */
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) ox958RZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
+ PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
+
+ int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, ox958MmioWrite, ox958MmioRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+
+ uint32_t const cUarts = RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts));
+ for (uint32_t i = 0; i < cUarts; i++)
+ {
+ POX958UARTCC pUartCC = &pThisCC->aUarts[i];
+ rc = uartRZInit(&pUartCC->UartCore, ox958IrqReq);
+ AssertRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_RING3 */
+
+
+const PDMDEVREG g_DeviceOxPcie958 =
+{
+ /* .u32version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "oxpcie958uart",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_SERIAL,
+ /* .cMaxInstances = */ ~0U,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(DEVOX958),
+ /* .cbInstanceCC = */ sizeof(DEVOX958CC),
+ /* .cbInstanceRC = */ sizeof(DEVOX958RC),
+ /* .cMaxPciDevices = */ 1,
+ /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
+ /* .pszDescription = */ "OXPCIe958 based UART controller.\n",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ ox958R3Construct,
+ /* .pfnDestruct = */ ox958R3Destruct,
+ /* .pfnRelocate = */ NULL,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ ox958R3Reset,
+ /* .pfnSuspend = */ NULL,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ ox958R3Attach,
+ /* .pfnDetach = */ ox958R3Detach,
+ /* .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 = */ ox958RZConstruct,
+ /* .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 = */ ox958RZConstruct,
+ /* .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
+};
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
+