From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Devices/Serial/DevOxPcie958.cpp | 710 +++++++++++++++++++++++++++++++ 1 file changed, 710 insertions(+) create mode 100644 src/VBox/Devices/Serial/DevOxPcie958.cpp (limited to 'src/VBox/Devices/Serial/DevOxPcie958.cpp') 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ + -- cgit v1.2.3