Index: /trunk/src/VBox/Devices/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/Makefile.kmk	(revision 73134)
+++ /trunk/src/VBox/Devices/Makefile.kmk	(revision 73135)
@@ -129,5 +129,6 @@
  	VBOX_WITH_DMI_CHASSIS \
  	VBOX_WITH_DMI_OEMSTRINGS \
-	$(if $(VBOX_WITH_NEW_LPC_DEVICE),VBOX_WITH_NEW_LPC_DEVICE,)
+	$(if $(VBOX_WITH_NEW_LPC_DEVICE),VBOX_WITH_NEW_LPC_DEVICE,) \
+	$(if $(VBOX_WITH_NEW_SERIAL),VBOX_WITH_NEW_SERIAL,)
  VBoxDD_DEFS.win         = _WIN32_WINNT=0x0510
  ifeq ($(KBUILD_TARGET_ARCH),x86)
@@ -199,4 +200,6 @@
   VBoxDD_SOURCES += \
  	Serial/DevSerialNew.cpp \
+ 	Serial/DevOxPcie958.cpp \
+ 	Serial/UartCore.cpp \
  	Serial/DrvCharNew.cpp \
  	Serial/DrvHostSerialNew.cpp
@@ -1022,5 +1025,7 @@
    VBoxDDRC_SOURCES := \
   	$(filter-out Serial/DevSerial.cpp, $(VBoxDDRC_SOURCES)) \
-  	Serial/DevSerialNew.cpp
+  	Serial/DevSerialNew.cpp \
+  	Serial/DevOxPcie958.cpp \
+  	Serial/UartCore.cpp
   endif
 
@@ -1197,5 +1202,7 @@
   VBoxDDR0_SOURCES := \
  	$(filter-out Serial/DevSerial.cpp, $(VBoxDDR0_SOURCES)) \
- 	Serial/DevSerialNew.cpp
+ 	Serial/DevSerialNew.cpp \
+ 	Serial/DevOxPcie958.cpp \
+  	Serial/UartCore.cpp
  endif
 
Index: /trunk/src/VBox/Devices/Serial/DevOxPcie958.cpp
===================================================================
--- /trunk/src/VBox/Devices/Serial/DevOxPcie958.cpp	(revision 73135)
+++ /trunk/src/VBox/Devices/Serial/DevOxPcie958.cpp	(revision 73135)
@@ -0,0 +1,688 @@
+/* $Id$ */
+/** @file
+ * DevOxPcie958 - Oxford Semiconductor OXPCIe958 PCI Express bridge to octal serial port emulation
+ */
+
+/*
+ * Copyright (C) 2018 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.
+ */
+
+/** @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/vmm/vm.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
+
+
+/**
+ * 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 OXPCIe958 UART core. */
+typedef OX958UART *POX958UART;
+
+
+/**
+ * OXPCIe958 device instance data.
+ */
+typedef struct DEVOX958
+{
+    /** The corresponding PCI device. */
+    PDMPCIDEV                       PciDev;
+
+    /** Pointer to the device instance - R3 ptr. */
+    PPDMDEVINSR3                    pDevInsR3;
+    /** Pointer to the device instance - R0 ptr */
+    PPDMDEVINSR0                    pDevInsR0;
+    /** Pointer to the device instance - RC ptr. */
+    PPDMDEVINSRC                    pDevInsRC;
+    /** Flag whether R0 is enabled. */
+    bool                            fR0Enabled;
+    /** Flag whether RC is enabled. */
+    bool                            fRCEnabled;
+    /** Alignment. */
+    bool                            afAlignment[2];
+    /** 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;
+    /** MMIO Base address. */
+    RTGCPHYS                        GCPhysMMIO;
+    /** The UARTs. */
+    OX958UART                       aUarts[OX958_UARTS_MAX];
+
+} DEVOX958;
+/** Pointer to an OXPCIe958 device instance. */
+typedef DEVOX958 *PDEVOX958;
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+
+
+/**
+ * Update IRQ status of the device.
+ *
+ * @returns nothing.
+ * @param   pThis               The OXPCIe958 device instance.
+ */
+static void ox958IrqUpdate(PDEVOX958 pThis)
+{
+    uint32_t u32IrqSts = ASMAtomicReadU32(&pThis->u32RegIrqStsGlob);
+    uint32_t u32IrqEn  = ASMAtomicReadU32(&pThis->u32RegIrqEnGlob);
+
+    if (u32IrqSts & u32IrqEn)
+        PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, PDM_IRQ_LEVEL_HIGH);
+    else
+        PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0, PDM_IRQ_LEVEL_LOW);
+}
+
+
+/**
+ * Performs a register read from the given UART.
+ *
+ * @returns nothing.
+ * @param   pThis               The OXPCIe958 device instance.
+ * @param   pUart               The UART accessed.
+ * @param   offUartReg          Offset of the register being read.
+ * @param   pv                  Where to store the read data.
+ * @param   cb                  Number of bytes to read.
+ */
+static int ox958UartRegRead(PDEVOX958 pThis, POX958UART pUart, uint32_t offUartReg, void *pv, unsigned cb)
+{
+    int rc = VINF_SUCCESS;
+    RT_NOREF(pThis);
+
+    if (offUartReg >= OX958_REG_UART_DMA_REGION_OFFSET)
+    {
+        /* Access to the DMA registers. */
+    }
+    else /* Access UART registers. */
+        rc = uartRegRead(&pUart->UartCore, offUartReg, (uint32_t *)pv, cb);
+
+    return rc;
+}
+
+
+/**
+ * Performs a register write to the given UART.
+ *
+ * @returns nothing.
+ * @param   pThis               The OXPCIe958 device instance.
+ * @param   pUart               The UART accessed.
+ * @param   offUartReg          Offset of the register being written.
+ * @param   pv                  The data to write.
+ * @param   cb                  Number of bytes to write.
+ */
+static int ox958UartRegWrite(PDEVOX958 pThis, POX958UART pUart, uint32_t offUartReg, const void *pv, unsigned cb)
+{
+    int rc = VINF_SUCCESS;
+    RT_NOREF(pThis);
+
+    if (offUartReg >= OX958_REG_UART_DMA_REGION_OFFSET)
+    {
+        /* Access to the DMA registers. */
+    }
+    else /* Access UART registers. */
+        rc = uartRegWrite(&pUart->UartCore, offUartReg, *(const uint32_t *)pv, cb);
+
+    return rc;
+}
+
+
+/**
+ * UART core IRQ request callback.
+ *
+ * @returns nothing.
+ * @param   pDevIns     The device instance.
+ * @param   pUart       The UART requesting an IRQ update.
+ * @param   iLUN        The UART index.
+ * @param   iLvl        IRQ level requested.
+ */
+PDMBOTHCBDECL(void) ox958IrqReq(PPDMDEVINS pDevIns, PUARTCORE pUart, unsigned iLUN, int iLvl)
+{
+    RT_NOREF(pUart);
+    PDEVOX958 pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
+
+    if (iLvl)
+        ASMAtomicOrU32(&pThis->u32RegIrqStsGlob, RT_BIT_32(iLUN));
+    else
+        ASMAtomicAndU32(&pThis->u32RegIrqStsGlob, ~RT_BIT_32(iLUN));
+    ox958IrqUpdate(pThis);
+}
+
+
+/**
+ * Read a MMIO register.
+ *
+ * @returns VBox status code suitable for scheduling.
+ * @param   pDevIns     The device instance.
+ * @param   pvUser      A user argument (ignored).
+ * @param   GCPhysAddr  The physical address being written to. (This is within our MMIO memory range.)
+ * @param   pv          Where to put the data we read.
+ * @param   cb          The size of the read.
+ */
+PDMBOTHCBDECL(int) ox958MmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
+{
+    PDEVOX958 pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
+    uint32_t  offReg = (GCPhysAddr - pThis->GCPhysMMIO);
+    int       rc = VINF_SUCCESS;
+    RT_NOREF(pThis, pvUser);
+
+    if (offReg < OX958_REG_UART_REGION_OFFSET)
+    {
+        uint32_t *pu32 = (uint32_t *)pv;
+        Assert(cb == 4);
+
+        switch (offReg)
+        {
+            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. */
+        offReg -= OX958_REG_UART_REGION_OFFSET;
+        uint32_t iUart = offReg / OX958_REG_UART_REGION_SIZE;
+        uint32_t offUartReg = offReg % OX958_REG_UART_REGION_SIZE;
+        if (iUart < pThis->cUarts)
+        {
+            POX958UART pUart = &pThis->aUarts[iUart];
+            rc = ox958UartRegRead(pThis, pUart, 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;
+}
+
+
+/**
+ * Write to a MMIO register.
+ *
+ * @returns VBox status code suitable for scheduling.
+ * @param   pDevIns     The device instance.
+ * @param   pvUser      A user argument (ignored).
+ * @param   GCPhysAddr  The physical address being written to. (This is within our MMIO memory range.)
+ * @param   pv          Pointer to the data being written.
+ * @param   cb          The size of the data being written.
+ */
+PDMBOTHCBDECL(int) ox958MmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
+{
+    PDEVOX958 pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
+    uint32_t  offReg = (GCPhysAddr - pThis->GCPhysMMIO);
+    int       rc = VINF_SUCCESS;
+    RT_NOREF1(pvUser);
+
+    if (offReg < OX958_REG_UART_REGION_OFFSET)
+    {
+        const uint32_t u32 = *(const uint32_t *)pv;
+        Assert(cb == 4);
+
+        switch (offReg)
+        {
+            case OX958_REG_UART_IRQ_ENABLE:
+                ASMAtomicOrU32(&pThis->u32RegIrqEnGlob, u32);
+                ox958IrqUpdate(pThis);
+                break;
+            case OX958_REG_UART_IRQ_DISABLE:
+                ASMAtomicAndU32(&pThis->u32RegIrqEnGlob, ~u32);
+                ox958IrqUpdate(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:
+                rc = VINF_SUCCESS;
+        }
+    }
+    else
+    {
+        /* Figure out the UART accessed from the offset. */
+        offReg -= OX958_REG_UART_REGION_OFFSET;
+        uint32_t iUart = offReg / OX958_REG_UART_REGION_SIZE;
+        uint32_t offUartReg = offReg % OX958_REG_UART_REGION_SIZE;
+        if (iUart < pThis->cUarts)
+        {
+            POX958UART pUart = &pThis->aUarts[iUart];
+            rc = ox958UartRegWrite(pThis, pUart, offUartReg, pv, cb);
+            if (rc == VINF_IOM_R3_IOPORT_WRITE)
+                rc = VINF_IOM_R3_MMIO_WRITE;
+        }
+    }
+
+    return rc;
+}
+
+
+#ifdef IN_RING3
+/**
+ * @callback_method_impl{FNPCIIOREGIONMAP}
+ */
+static DECLCALLBACK(int) ox958R3Map(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
+                                    RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
+{
+    RT_NOREF(enmType);
+    PDEVOX958 pThis = (PDEVOX958)pPciDev;
+    int       rc = VINF_SUCCESS;
+
+    if (iRegion == 0)
+    {
+        Assert(enmType == PCI_ADDRESS_SPACE_MEM);
+
+        rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
+                                   IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+                                   ox958MmioWrite, ox958MmioRead, "OxPCIe958");
+        if (RT_FAILURE(rc))
+            return rc;
+
+        /* Enable (or not) RC/R0 support. */
+        if (pThis->fRCEnabled)
+        {
+            rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
+                                         "ox958MmioWrite", "ox958MmioRead");
+            if (RT_FAILURE(rc))
+                return rc;
+        }
+
+        if (pThis->fR0Enabled)
+        {
+            rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
+                                         "ox958MmioWrite", "ox958MmioRead");
+            if (RT_FAILURE(rc))
+                return rc;
+        }
+
+        pThis->GCPhysMMIO = GCPhysAddress;
+    }
+
+    return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnDetach} */
+static DECLCALLBACK(void) ox958R3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+    PDEVOX958 pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
+    AssertReturnVoid(iLUN >= pThis->cUarts);
+
+    RT_NOREF(fFlags);
+
+    return uartR3Detach(&pThis->aUarts[iLUN].UartCore);
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnAttach} */
+static DECLCALLBACK(int) ox958R3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+    PDEVOX958 pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
+
+    RT_NOREF(fFlags);
+
+    if (iLUN >= pThis->cUarts)
+        return VERR_PDM_LUN_NOT_FOUND;
+
+    return uartR3Attach(&pThis->aUarts[iLUN].UartCore, iLUN);
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnReset} */
+static DECLCALLBACK(void) ox958R3Reset(PPDMDEVINS pDevIns)
+{
+    PDEVOX958 pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
+
+    pThis->u32RegIrqStsGlob = 0x00;
+    pThis->u32RegIrqEnGlob  = 0x00;
+    pThis->u32RegIrqEnWake  = 0x00;
+
+    for (uint32_t i = 0; i < pThis->cUarts; i++)
+        uartR3Reset(&pThis->aUarts[i].UartCore);
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnRelocate} */
+static DECLCALLBACK(void) ox958R3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+    PDEVOX958 pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
+    RT_NOREF(offDelta);
+
+    pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+    for (uint32_t i = 0; i < pThis->cUarts; i++)
+        uartR3Relocate(&pThis->aUarts[i].UartCore, offDelta);
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnDestruct} */
+static DECLCALLBACK(int) ox958R3Destruct(PPDMDEVINS pDevIns)
+{
+    PDEVOX958 pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
+    PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+
+    for (uint32_t i = 0; i < pThis->cUarts; i++)
+        uartR3Destruct(&pThis->aUarts[i].UartCore);
+
+    return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{PDMDEVREG,pfnConstruct} */
+static DECLCALLBACK(int) ox958R3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+    RT_NOREF(iInstance);
+    PDEVOX958   pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
+    bool        fRCEnabled = true;
+    bool        fR0Enabled = true;
+    bool        fMsiXSupported = false;
+    int         rc;
+
+    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+    /*
+     * Validate and read configuration.
+     */
+    if (!CFGMR3AreValuesValid(pCfg, "RCEnabled\0"
+                                    "R0Enabled\0"
+                                    "MsiXSupported\0"
+                                    "UartCount\0"))
+        return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
+                                N_("OXPCIe958 configuration error: Unknown option specified"));
+
+    rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &fRCEnabled, true);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("OXPCIe958 configuration error: Failed to read \"RCEnabled\" as boolean"));
+
+    rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("OXPCIe958 configuration error: failed to read \"R0Enabled\" as boolean"));
+
+    rc = CFGMR3QueryBoolDef(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 = CFGMR3QueryU32Def(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);
+
+    /*
+     * Init instance data.
+     */
+    pThis->fR0Enabled            = fR0Enabled;
+    pThis->fRCEnabled            = fRCEnabled;
+    pThis->pDevInsR3             = pDevIns;
+    pThis->pDevInsR0             = PDMDEVINS_2_R0PTR(pDevIns);
+    pThis->pDevInsRC             = PDMDEVINS_2_RCPTR(pDevIns);
+
+    /* Fill PCI config space. */
+    PDMPciDevSetVendorId         (&pThis->PciDev, OX958_PCI_VENDOR_ID);
+    PDMPciDevSetDeviceId         (&pThis->PciDev, OX958_PCI_DEVICE_ID);
+    PDMPciDevSetCommand          (&pThis->PciDev, 0x0000);
+#ifdef VBOX_WITH_MSI_DEVICES
+    PDMPciDevSetStatus           (&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST);
+    PDMPciDevSetCapabilityList   (&pThis->PciDev, OX958_PCI_MSI_CAP_OFS);
+#else
+    PDMPciDevSetCapabilityList   (&pThis->PciDev, 0x70);
+#endif
+    PDMPciDevSetRevisionId       (&pThis->PciDev, 0x00);
+    PDMPciDevSetClassBase        (&pThis->PciDev, 0x07); /* Communication controller. */
+    PDMPciDevSetClassSub         (&pThis->PciDev, 0x00); /* Serial controller. */
+    PDMPciDevSetClassProg        (&pThis->PciDev, 0x02); /* 16550. */
+
+    PDMPciDevSetRevisionId       (&pThis->PciDev, 0x00);
+    PDMPciDevSetSubSystemVendorId(&pThis->PciDev, OX958_PCI_VENDOR_ID);
+    PDMPciDevSetSubSystemId      (&pThis->PciDev, OX958_PCI_DEVICE_ID);
+
+    PDMPciDevSetInterruptLine       (&pThis->PciDev, 0x00);
+    PDMPciDevSetInterruptPin        (&pThis->PciDev, 0x01);
+    /** @todo More Capabilities. */
+
+    rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Register PCI device and I/O region.
+     */
+    rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
+    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))
+    {
+        PCIDevSetCapabilityList(&pThis->PciDev, 0x0);
+        /* That's OK, we can work without MSI */
+    }
+#endif
+
+    rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, _16K, PCI_ADDRESS_SPACE_MEM, ox958R3Map);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    PVM pVM = PDMDevHlpGetVM(pDevIns);
+    RTR0PTR pfnSerialIrqReqR0 = NIL_RTR0PTR;
+    RTRCPTR pfnSerialIrqReqRC = NIL_RTRCPTR;
+
+    if (   fRCEnabled
+        && VM_IS_RAW_MODE_ENABLED(pVM))
+    {
+        rc = PDMR3LdrGetSymbolRC(pVM, pDevIns->pReg->szRCMod, "ox958IrqReq", &pfnSerialIrqReqRC);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    if (fR0Enabled)
+    {
+        rc = PDMR3LdrGetSymbolR0(pVM, pDevIns->pReg->szR0Mod, "ox958IrqReq", &pfnSerialIrqReqR0);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    for (uint32_t i = 0; i < pThis->cUarts; i++)
+    {
+        POX958UART pUart = &pThis->aUarts[i];
+        rc = uartR3Init(&pUart->UartCore, pDevIns, UARTTYPE_16550A, i, 0, ox958IrqReq, pfnSerialIrqReqR0, pfnSerialIrqReqRC);
+        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;
+}
+
+
+const PDMDEVREG g_DeviceOxPcie958 =
+{
+    /* u32version */
+    PDM_DEVREG_VERSION,
+    /* szName */
+    "oxpcie958uart",
+    /* szRCMod */
+    "VBoxDDRC.rc",
+    /* szR0Mod */
+    "VBoxDDR0.r0",
+    /* pszDescription */
+    "OXPCIe958 based UART controller.\n",
+    /* fFlags */
+    PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
+    /* fClass */
+    PDM_DEVREG_CLASS_SERIAL,
+    /* cMaxInstances */
+    ~0U,
+    /* cbInstance */
+    sizeof(DEVOX958),
+    /* pfnConstruct */
+    ox958R3Construct,
+    /* pfnDestruct */
+    ox958R3Destruct,
+    /* pfnRelocate */
+    ox958R3Relocate,
+    /* pfnMemSetup */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    ox958R3Reset,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    ox958R3Attach,
+    /* pfnDetach */
+    ox958R3Detach,
+    /* pfnQueryInterface */
+    NULL,
+    /* pfnInitComplete */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32VersionEnd */
+    PDM_DEVREG_VERSION
+};
+
+#endif /* IN_RING3 */
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
+
Index: /trunk/src/VBox/Devices/Serial/DevSerialNew.cpp
===================================================================
--- /trunk/src/VBox/Devices/Serial/DevSerialNew.cpp	(revision 73134)
+++ /trunk/src/VBox/Devices/Serial/DevSerialNew.cpp	(revision 73135)
@@ -25,4 +25,5 @@
 #include <VBox/vmm/pdmdev.h>
 #include <VBox/vmm/pdmserialifs.h>
+#include <VBox/vmm/vm.h>
 #include <iprt/assert.h>
 #include <iprt/uuid.h>
@@ -32,4 +33,5 @@
 
 #include "VBoxDD.h"
+#include "UartCore.h"
 
 
@@ -38,160 +40,4 @@
 *********************************************************************************************************************************/
 
-/** The RBR/DLL register index (from the base of the port range). */
-#define UART_REG_RBR_DLL_INDEX               0
-
-/** The THR/DLL register index (from the base of the port range). */
-#define UART_REG_THR_DLL_INDEX               0
-
-/** The IER/DLM register index (from the base of the port range). */
-#define UART_REG_IER_DLM_INDEX               1
-/** Enable received data available interrupt */
-# define UART_REG_IER_ERBFI                  RT_BIT(0)
-/** Enable transmitter holding register empty interrupt */
-# define UART_REG_IER_ETBEI                  RT_BIT(1)
-/** Enable receiver line status interrupt */
-# define UART_REG_IER_ELSI                   RT_BIT(2)
-/** Enable modem status interrupt. */
-# define UART_REG_IER_EDSSI                  RT_BIT(3)
-/** Mask of writeable bits. */
-# define UART_REG_IER_MASK_WR                0x0f
-
-/** The IIR register index (from the base of the port range). */
-#define UART_REG_IIR_INDEX                   2
-/** Interrupt Pending - high means no interrupt pending. */
-# define UART_REG_IIR_IP_NO_INT              RT_BIT(0)
-/** Interrupt identification mask. */
-# define UART_REG_IIR_ID_MASK                0x0e
-/** Sets the interrupt identification to the given value. */
-# define UART_REG_IIR_ID_SET(a_Val)          (((a_Val) << 1) & UART_REG_IIR_ID_MASK)
-/** Receiver Line Status interrupt. */
-#  define UART_REG_IIR_ID_RCL                0x3
-/** Received Data Available interrupt. */
-#  define UART_REG_IIR_ID_RDA                0x2
-/** Character Timeou Indicator interrupt. */
-#  define UART_REG_IIR_ID_CTI                0x6
-/** Transmitter Holding Register Empty interrupt. */
-#  define UART_REG_IIR_ID_THRE               0x1
-/** Modem Status interrupt. */
-#  define UART_REG_IIR_ID_MS                 0x0
-/** FIFOs enabled. */
-# define UART_REG_IIR_FIFOS_EN               0xc0
-/** Bits relevant for checking whether the interrupt status has changed. */
-# define UART_REG_IIR_CHANGED_MASK           0x0f
-
-/** The FCR register index (from the base of the port range). */
-#define UART_REG_FCR_INDEX                   2
-/** Enable the TX/RX FIFOs. */
-# define UART_REG_FCR_FIFO_EN                RT_BIT(0)
-/** Reset the receive FIFO. */
-# define UART_REG_FCR_RCV_FIFO_RST           RT_BIT(1)
-/** Reset the transmit FIFO. */
-# define UART_REG_FCR_XMIT_FIFO_RST          RT_BIT(2)
-/** DMA Mode Select. */
-# define UART_REG_FCR_DMA_MODE_SEL           RT_BIT(3)
-/** Receiver level interrupt trigger. */
-# define UART_REG_FCR_RCV_LVL_IRQ_MASK       0xc0
-/** Returns the receive level trigger value from the given FCR register. */
-# define UART_REG_FCR_RCV_LVL_IRQ_GET(a_Fcr) (((a_Fcr) & UART_REG_FCR_RCV_LVL_IRQ_MASK) >> 6)
-/** RCV Interrupt trigger level - 1 byte. */
-# define UART_REG_FCR_RCV_LVL_IRQ_1          0x0
-/** RCV Interrupt trigger level - 4 bytes. */
-# define UART_REG_FCR_RCV_LVL_IRQ_4          0x1
-/** RCV Interrupt trigger level - 8 bytes. */
-# define UART_REG_FCR_RCV_LVL_IRQ_8          0x2
-/** RCV Interrupt trigger level - 14 bytes. */
-# define UART_REG_FCR_RCV_LVL_IRQ_14         0x3
-/** Mask of writeable bits. */
-# define UART_REG_FCR_MASK_WR                0xcf
-/** Mask of sticky bits. */
-# define UART_REG_FCR_MASK_STICKY            0xc9
-
-/** The LCR register index (from the base of the port range). */
-#define UART_REG_LCR_INDEX                   3
-/** Word Length Select Mask. */
-# define UART_REG_LCR_WLS_MASK               0x3
-/** Returns the WLS value form the given LCR register value. */
-# define UART_REG_LCR_WLS_GET(a_Lcr)         ((a_Lcr) & UART_REG_LCR_WLS_MASK)
-/** Number of stop bits. */
-# define UART_REG_LCR_STB                    RT_BIT(2)
-/** Parity Enable. */
-# define UART_REG_LCR_PEN                    RT_BIT(3)
-/** Even Parity. */
-# define UART_REG_LCR_EPS                    RT_BIT(4)
-/** Stick parity. */
-# define UART_REG_LCR_PAR_STICK              RT_BIT(5)
-/** Set Break. */
-# define UART_REG_LCR_BRK_SET                RT_BIT(6)
-/** Divisor Latch Access Bit. */
-# define UART_REG_LCR_DLAB                   RT_BIT(7)
-
-/** The MCR register index (from the base of the port range). */
-#define UART_REG_MCR_INDEX                   4
-/** Data Terminal Ready. */
-# define UART_REG_MCR_DTR                    RT_BIT(0)
-/** Request To Send. */
-# define UART_REG_MCR_RTS                    RT_BIT(1)
-/** Out1. */
-# define UART_REG_MCR_OUT1                   RT_BIT(2)
-/** Out2. */
-# define UART_REG_MCR_OUT2                   RT_BIT(3)
-/** Loopback connection. */
-# define UART_REG_MCR_LOOP                   RT_BIT(4)
-/** Mask of writeable bits. */
-# define UART_REG_MCR_MASK_WR                0x1f
-
-/** The LSR register index (from the base of the port range). */
-#define UART_REG_LSR_INDEX                   5
-/** Data Ready. */
-# define UART_REG_LSR_DR                     RT_BIT(0)
-/** Overrun Error. */
-# define UART_REG_LSR_OE                     RT_BIT(1)
-/** Parity Error. */
-# define UART_REG_LSR_PE                     RT_BIT(2)
-/** Framing Error. */
-# define UART_REG_LSR_FE                     RT_BIT(3)
-/** Break Interrupt. */
-# define UART_REG_LSR_BI                     RT_BIT(4)
-/** Transmitter Holding Register. */
-# define UART_REG_LSR_THRE                   RT_BIT(5)
-/** Transmitter Empty. */
-# define UART_REG_LSR_TEMT                   RT_BIT(6)
-/** Error in receiver FIFO. */
-# define UART_REG_LSR_RCV_FIFO_ERR           RT_BIT(7)
-/** The bits to check in this register when checking for the RCL interrupt. */
-# define UART_REG_LSR_BITS_IIR_RCL           0x1e
-
-/** The MSR register index (from the base of the port range). */
-#define UART_REG_MSR_INDEX                   6
-/** Delta Clear to Send. */
-# define UART_REG_MSR_DCTS                   RT_BIT(0)
-/** Delta Data Set Ready. */
-# define UART_REG_MSR_DDSR                   RT_BIT(1)
-/** Trailing Edge Ring Indicator. */
-# define UART_REG_MSR_TERI                   RT_BIT(2)
-/** Delta Data Carrier Detect. */
-# define UART_REG_MSR_DDCD                   RT_BIT(3)
-/** Clear to Send. */
-# define UART_REG_MSR_CTS                    RT_BIT(4)
-/** Data Set Ready. */
-# define UART_REG_MSR_DSR                    RT_BIT(5)
-/** Ring Indicator. */
-# define UART_REG_MSR_RI                     RT_BIT(6)
-/** Data Carrier Detect. */
-# define UART_REG_MSR_DCD                    RT_BIT(7)
-/** The bits to check in this register when checking for the MS interrupt. */
-# define UART_REG_MSR_BITS_IIR_MS            0x0f
-
-/** The SCR register index (from the base of the port range). */
-#define UART_REG_SCR_INDEX                   7
-
-/** Set the specified bits in the given register. */
-#define UART_REG_SET(a_Reg, a_Set)           ((a_Reg) |= (a_Set))
-/** Clear the specified bits in the given register. */
-#define UART_REG_CLR(a_Reg, a_Clr)           ((a_Reg) &= ~(a_Clr))
-
-/** Size of a FIFO. */
-#define UART_FIFO_LENGTH                     16
-
 
 /*********************************************************************************************************************************
@@ -200,33 +46,8 @@
 
 /**
- * Serial FIFO.
- */
-typedef struct SERIALFIFO
-{
-    /** Current amount of bytes used. */
-    uint8_t                         cbUsed;
-    /** Next index to write to. */
-    uint8_t                         offWrite;
-    /** Next index to read from. */
-    uint8_t                         offRead;
-    /** The interrupt trigger level (only used for the receive FIFO). */
-    uint8_t                         cbItl;
-    /** The data in the FIFO. */
-    uint8_t                         abBuf[UART_FIFO_LENGTH];
-} SERIALFIFO;
-/** Pointer to a FIFO. */
-typedef SERIALFIFO *PSERIALFIFO;
-
-
-/**
  * Serial device.
- *
- * @implements  PDMIBASE
- * @implements  PDMISERIALPORT
  */
 typedef struct DEVSERIAL
 {
-    /** Access critical section. */
-    PDMCRITSECT                     CritSect;
     /** Pointer to the device instance - R3 Ptr. */
     PPDMDEVINSR3                    pDevInsR3;
@@ -237,20 +58,10 @@
     /** Alignment. */
     RTRCPTR                         Alignment0;
-    /** LUN\#0: The base interface. */
-    PDMIBASE                        IBase;
-    /** LUN\#0: The serial port interface. */
-    PDMISERIALPORT                  ISerialPort;
-    /** Pointer to the attached base driver. */
-    R3PTRTYPE(PPDMIBASE)            pDrvBase;
-    /** Pointer to the attached serial driver. */
-    R3PTRTYPE(PPDMISERIALCONNECTOR) pDrvSerial;
     /** Flag whether the R0 portion of this device is enabled. */
     bool                            fR0Enabled;
     /** Flag whether the RC portion of this device is enabled. */
     bool                            fRCEnabled;
-    /** Flag whether an 16550A (with FIFO) or a plain 16450 is emulated. */
-    bool                            f16550AEnabled;
-    /** Flag whether to yield on an guest LSR read. */
-    bool                            fYieldOnLSRRead;
+    /** Alignment. */
+    bool                            afAlignment1[2];
     /** The IRQ value. */
     uint8_t                         uIrq;
@@ -258,35 +69,6 @@
     RTIOPORT                        PortBase;
 
-    /** The divisor register (DLAB = 1). */
-    uint16_t                        uRegDivisor;
-    /** The Receiver Buffer Register (RBR, DLAB = 0). */
-    uint8_t                         uRegRbr;
-    /** The Transmitter Holding Register (THR, DLAB = 0). */
-    uint8_t                         uRegThr;
-    /** The Interrupt Enable Register (IER, DLAB = 0). */
-    uint8_t                         uRegIer;
-    /** The Interrupt Identification Register (IIR). */
-    uint8_t                         uRegIir;
-    /** The FIFO Control Register (FCR). */
-    uint8_t                         uRegFcr;
-    /** The Line Control Register (LCR). */
-    uint8_t                         uRegLcr;
-    /** The Modem Control Register (MCR). */
-    uint8_t                         uRegMcr;
-    /** The Line Status Register (LSR). */
-    uint8_t                         uRegLsr;
-    /** The Modem Status Register (MSR). */
-    uint8_t                         uRegMsr;
-    /** The Scratch Register (SCR). */
-    uint8_t                         uRegScr;
-
-    /** The transmit FIFO. */
-    SERIALFIFO                      FifoXmit;
-    /** The receive FIFO. */
-    SERIALFIFO                      FifoRecv;
-
-    /** Number of bytes available for reading from the layer below. */
-    volatile uint32_t               cbAvailRdr;
-
+    /** The UART core. */
+    UARTCORE                        UartCore;
 } DEVSERIAL;
 /** Pointer to the serial device state. */
@@ -299,1132 +81,46 @@
 *   Global Variables                                                                                                             *
 *********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+
+
+PDMBOTHCBDECL(void) serialIrqReq(PPDMDEVINS pDevIns, PUARTCORE pUart, unsigned iLUN, int iLvl)
+{
+    RT_NOREF(pUart, iLUN);
+    PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
+    PDMDevHlpISASetIrqNoWait(pDevIns, pThis->uIrq, iLvl);
+}
+
+
+/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+PDMBOTHCBDECL(int) serialIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb)
+{
+    PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
+    RT_NOREF_PV(pvUser);
+
+    return uartRegWrite(&pThis->UartCore, uPort - pThis->PortBase, u32, cb);
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+PDMBOTHCBDECL(int) serialIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb)
+{
+    PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
+    RT_NOREF_PV(pvUser);
+
+    return uartRegRead(&pThis->UartCore, uPort - pThis->PortBase, pu32, cb);
+}
+
+
 #ifdef IN_RING3
-/**
- * String versions of the parity enum.
- */
-static const char *s_aszParity[] =
-{
-    "INVALID",
-    "NONE",
-    "EVEN",
-    "ODD",
-    "MARK",
-    "SPACE",
-    "INVALID"
-};
-
-
-/**
- * String versions of the stop bits enum.
- */
-static const char *s_aszStopBits[] =
-{
-    "INVALID",
-    "1",
-    "1.5",
-    "2",
-    "INVALID"
-};
-#endif
-
-
-/*********************************************************************************************************************************
-*   Internal Functions                                                                                                           *
-*********************************************************************************************************************************/
-
-
-/**
- * Updates the IRQ state based on the current device state.
- *
- * @returns nothing.
- * @param   pThis               The serial port instance.
- */
-static void serialIrqUpdate(PDEVSERIAL pThis)
-{
-    LogFlowFunc(("pThis=%#p\n", pThis));
-
-    /*
-     * The interrupt uses a priority scheme, only the interrupt with the
-     * highest priority is indicated in the interrupt identification register.
-     *
-     * The priorities are as follows (high to low):
-     *      * Receiver line status
-     *      * Received data available
-     *      * Character timeout indication (only in FIFO mode).
-     *      * Transmitter holding register empty
-     *      * Modem status change.
-     */
-    uint8_t uRegIirNew = UART_REG_IIR_IP_NO_INT;
-    if (   (pThis->uRegLsr & UART_REG_LSR_BITS_IIR_RCL)
-        && (pThis->uRegIer & UART_REG_IER_ELSI))
-        uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_RCL);
-    else if (   (pThis->uRegLsr & UART_REG_LSR_DR)
-             && (pThis->uRegIer & UART_REG_IER_ERBFI)
-             && (   !(pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
-                 || pThis->FifoRecv.cbUsed >= pThis->FifoRecv.cbItl))
-        uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_RDA);
-    else if (   (pThis->uRegLsr & UART_REG_LSR_THRE)
-             && (pThis->uRegIer & UART_REG_IER_ETBEI))
-        uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_THRE);
-    else if (   (pThis->uRegMsr & UART_REG_MSR_BITS_IIR_MS)
-             && (pThis->uRegIer & UART_REG_IER_EDSSI))
-        uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_MS);
-
-    /** @todo Character timeout indication for FIFO mode. */
-
-    LogFlowFunc(("    uRegIirNew=%#x uRegIir=%#x\n", uRegIirNew, pThis->uRegIir));
-
-    /* Change interrupt only if the interrupt status really changed from the previous value. */
-    if (uRegIirNew != (pThis->uRegIir & UART_REG_IIR_CHANGED_MASK))
-    {
-        LogFlow(("    Interrupt source changed from %#x -> %#x (IRQ %d -> %d)\n",
-                 pThis->uRegIir, uRegIirNew,
-                 pThis->uRegIir == UART_REG_IIR_IP_NO_INT ? 0 : 1,
-                 uRegIirNew == UART_REG_IIR_IP_NO_INT ? 0 : 1));
-        if (uRegIirNew == UART_REG_IIR_IP_NO_INT)
-            PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->uIrq, 0);
-        else
-            PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->uIrq, 1);
-    }
-    else
-        LogFlow(("    No change in interrupt source\n"));
-
-    if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
-        uRegIirNew |= UART_REG_IIR_FIFOS_EN;
-
-    pThis->uRegIir = uRegIirNew;
-}
-
-
-/**
- * Clears the given FIFO.
- *
- * @returns nothing.
- * @param   pFifo               The FIFO to clear.
- */
-DECLINLINE(void) serialFifoClear(PSERIALFIFO pFifo)
-{
-    memset(&pFifo->abBuf[0], 0, sizeof(pFifo->abBuf));
-    pFifo->cbUsed   = 0;
-    pFifo->offWrite = 0;
-    pFifo->offRead  = 0;
-}
-
-
-/**
- * Returns the amount of free bytes in the given FIFO.
- *
- * @returns The amount of bytes free in the given FIFO.
- * @param   pFifo               The FIFO.
- */
-DECLINLINE(size_t) serialFifoFreeGet(PSERIALFIFO pFifo)
-{
-    return UART_FIFO_LENGTH - pFifo->cbUsed;
-}
-
-
-/**
- * Puts a new character into the given FIFO.
- *
- * @returns Flag whether the FIFO overflowed.
- * @param   pFifo               The FIFO to put the data into.
- * @param   fOvrWr              Flag whether to overwrite data if the FIFO is full.
- * @param   bData               The data to add.
- */
-DECLINLINE(bool) serialFifoPut(PSERIALFIFO pFifo, bool fOvrWr, uint8_t bData)
-{
-    if (fOvrWr || pFifo->cbUsed < UART_FIFO_LENGTH)
-    {
-        pFifo->abBuf[pFifo->offWrite] = bData;
-        pFifo->offWrite = (pFifo->offWrite + 1) % UART_FIFO_LENGTH;
-    }
-
-    bool fOverFlow = false;
-    if (pFifo->cbUsed < UART_FIFO_LENGTH)
-        pFifo->cbUsed++;
-    else
-    {
-        fOverFlow = true;
-        if (fOvrWr) /* Advance the read position to account for the lost character. */
-           pFifo->offRead = (pFifo->offRead + 1) % UART_FIFO_LENGTH;
-    }
-
-    return fOverFlow;
-}
-
-
-/**
- * Returns the next character in the FIFO.
- *
- * @return Next byte in the FIFO.
- * @param   pFifo               The FIFO to get data from.
- */
-DECLINLINE(uint8_t) serialFifoGet(PSERIALFIFO pFifo)
-{
-    uint8_t bRet = 0;
-
-    if (pFifo->cbUsed)
-    {
-        bRet = pFifo->abBuf[pFifo->offRead];
-        pFifo->offRead = (pFifo->offRead + 1) % UART_FIFO_LENGTH;
-        pFifo->cbUsed--;
-    }
-
-    return bRet;
-}
-
-
-/**
- * Tries to copy the requested amount of data from the given FIFO into the provided buffer.
- *
- * @returns Amount of bytes actually copied.
- * @param   pFifo               The FIFO to copy data from.
- * @param   pvDst               Where to copy the data to.
- * @param   cbCopy              How much to copy.
- */
-DECLINLINE(size_t) serialFifoCopyTo(PSERIALFIFO pFifo, void *pvDst, size_t cbCopy)
-{
-    size_t cbCopied = 0;
-    uint8_t *pbDst = (uint8_t *)pvDst;
-
-    cbCopy = RT_MIN(cbCopy, pFifo->cbUsed);
-    while (cbCopy)
-    {
-        size_t cbThisCopy = RT_MIN(cbCopy, (uint8_t)(UART_FIFO_LENGTH - pFifo->offRead));
-        memcpy(pbDst, &pFifo->abBuf[pFifo->offRead], cbThisCopy);
-
-        pFifo->offRead = (pFifo->offRead + cbThisCopy) % UART_FIFO_LENGTH;
-        pFifo->cbUsed -= cbThisCopy;
-        pbDst    += cbThisCopy;
-        cbCopied += cbThisCopy;
-        cbCopy   -= cbThisCopy;
-    }
-
-    return cbCopied;
-}
-
-
-/**
- * Tries to copy the requested amount of data from the provided buffer into the given FIFO.
- *
- * @returns Amount of bytes actually copied.
- * @param   pFifo               The FIFO to copy data to.
- * @param   pvSrc               Where to copy the data from.
- * @param   cbCopy              How much to copy.
- */
-DECLINLINE(size_t) serialFifoCopyFrom(PSERIALFIFO pFifo, void *pvSrc, size_t cbCopy)
-{
-    size_t cbCopied = 0;
-    uint8_t *pbSrc = (uint8_t *)pvSrc;
-
-    cbCopy = RT_MIN(cbCopy, serialFifoFreeGet(pFifo));
-    while (cbCopy)
-    {
-        size_t cbThisCopy = RT_MIN(cbCopy, (uint8_t)(UART_FIFO_LENGTH - pFifo->offWrite));
-        memcpy(&pFifo->abBuf[pFifo->offWrite], pbSrc, cbThisCopy);
-
-        pFifo->offWrite = (pFifo->offWrite + cbThisCopy) % UART_FIFO_LENGTH;
-        pFifo->cbUsed += cbThisCopy;
-        pbSrc    += cbThisCopy;
-        cbCopied += cbThisCopy;
-        cbCopy   -= cbThisCopy;
-    }
-
-    return cbCopied;
-}
-
-
-#ifdef IN_RING3
-/**
- * Updates the delta bits for the given MSR register value which has the status line
- * bits set.
- *
- * @returns nothing.
- * @param   pThis               The serial port instance.
- * @param   uMsrSts             MSR value with the appropriate status bits set.
- */
-static void serialR3MsrUpdate(PDEVSERIAL pThis, uint8_t uMsrSts)
-{
-    /* Compare current and new states and set remaining bits accordingly. */
-    if ((uMsrSts & UART_REG_MSR_CTS) != (pThis->uRegMsr & UART_REG_MSR_CTS))
-        uMsrSts |= UART_REG_MSR_DCTS;
-    if ((uMsrSts & UART_REG_MSR_DSR) != (pThis->uRegMsr & UART_REG_MSR_DSR))
-        uMsrSts |= UART_REG_MSR_DDSR;
-    if ((uMsrSts & UART_REG_MSR_RI) != 0 && (pThis->uRegMsr & UART_REG_MSR_RI) == 0)
-        uMsrSts |= UART_REG_MSR_TERI;
-    if ((uMsrSts & UART_REG_MSR_DCD) != (pThis->uRegMsr & UART_REG_MSR_DCD))
-        uMsrSts |= UART_REG_MSR_DDCD;
-
-    pThis->uRegMsr = uMsrSts;
-
-    serialIrqUpdate(pThis);
-}
-
-
-/**
- * Updates the serial port parameters of the attached driver with the current configuration.
- *
- * @returns nothing.
- * @param   pThis               The serial port instance.
- */
-static void serialR3ParamsUpdate(PDEVSERIAL pThis)
-{
-    if (   pThis->uRegDivisor != 0
-        && pThis->pDrvSerial)
-    {
-        uint32_t uBps = 115200 / pThis->uRegDivisor; /* This is for PC compatible serial port with a 1.8432 MHz crystal. */
-        unsigned cDataBits = UART_REG_LCR_WLS_GET(pThis->uRegLcr) + 5;
-        PDMSERIALSTOPBITS enmStopBits = PDMSERIALSTOPBITS_ONE;
-        PDMSERIALPARITY enmParity = PDMSERIALPARITY_NONE;
-
-        if (pThis->uRegLcr & UART_REG_LCR_STB)
-        {
-            enmStopBits = cDataBits == 5 ? PDMSERIALSTOPBITS_ONEPOINTFIVE : PDMSERIALSTOPBITS_TWO;
-        }
-
-        if (pThis->uRegLcr & UART_REG_LCR_PEN)
-        {
-            /* Select the correct parity mode based on the even and stick parity bits. */
-            switch (pThis->uRegLcr & (UART_REG_LCR_EPS | UART_REG_LCR_PAR_STICK))
-            {
-                case 0:
-                    enmParity = PDMSERIALPARITY_ODD;
-                    break;
-                case UART_REG_LCR_EPS:
-                    enmParity = PDMSERIALPARITY_EVEN;
-                    break;
-                case UART_REG_LCR_EPS | UART_REG_LCR_PAR_STICK:
-                    enmParity = PDMSERIALPARITY_SPACE;
-                    break;
-                case UART_REG_LCR_PAR_STICK:
-                    enmParity = PDMSERIALPARITY_MARK;
-                    break;
-                default:
-                    /* We should never get here as all cases where caught earlier. */
-                    AssertMsgFailed(("This shouldn't happen at all: %#x\n",
-                                     pThis->uRegLcr & (UART_REG_LCR_EPS | UART_REG_LCR_PAR_STICK)));
-            }
-        }
-
-        LogFlowFunc(("Changing parameters to: %u,%s,%u,%s\n",
-                     uBps, s_aszParity[enmParity], cDataBits, s_aszStopBits[enmStopBits]));
-
-        int rc = pThis->pDrvSerial->pfnChgParams(pThis->pDrvSerial, uBps, enmParity, cDataBits, enmStopBits);
-        if (RT_FAILURE(rc))
-            LogRelMax(10, ("Serial#%d: Failed to change parameters to %u,%s,%u,%s -> %Rrc\n",
-                           pThis->pDevInsR3->iInstance, uBps, s_aszParity[enmParity], cDataBits, s_aszStopBits[enmStopBits], rc));
-    }
-}
-
-
-/**
- * Updates the internal device state with the given PDM status line states.
- *
- * @returns nothing.
- * @param   pThis               The serial port instance.
- * @param   fStsLines           The PDM status line states.
- */
-static void serialR3StsLinesUpdate(PDEVSERIAL pThis, uint32_t fStsLines)
-{
-    uint8_t uRegMsrNew = 0; /* The new MSR value. */
-
-    if (fStsLines & PDMISERIALPORT_STS_LINE_DCD)
-        uRegMsrNew |= UART_REG_MSR_DCD;
-    if (fStsLines & PDMISERIALPORT_STS_LINE_RI)
-        uRegMsrNew |= UART_REG_MSR_RI;
-    if (fStsLines & PDMISERIALPORT_STS_LINE_DSR)
-        uRegMsrNew |= UART_REG_MSR_DSR;
-    if (fStsLines & PDMISERIALPORT_STS_LINE_CTS)
-        uRegMsrNew |= UART_REG_MSR_CTS;
-
-    serialR3MsrUpdate(pThis, uRegMsrNew);
-}
-
-
-/**
- * Fills up the receive FIFO with as much data as possible.
- *
- * @returns nothing.
- * @param   pThis               The serial port instance.
- */
-static void serialR3RecvFifoFill(PDEVSERIAL pThis)
-{
-    LogFlowFunc(("pThis=%#p\n", pThis));
-
-    PSERIALFIFO pFifo = &pThis->FifoRecv;
-    size_t cbFill = RT_MIN(serialFifoFreeGet(pFifo),
-                           ASMAtomicReadU32(&pThis->cbAvailRdr));
-    size_t cbFilled = 0;
-
-    while (cbFilled < cbFill)
-    {
-        size_t cbThisRead = RT_MIN(cbFill, (uint8_t)(UART_FIFO_LENGTH - pFifo->offWrite));
-        size_t cbRead = 0;
-        int rc = pThis->pDrvSerial->pfnReadRdr(pThis->pDrvSerial, &pFifo->abBuf[pFifo->offWrite], cbThisRead, &cbRead);
-        /*Assert(RT_SUCCESS(rc) && cbRead == cbThisRead);*/ RT_NOREF(rc);
-
-        pFifo->offWrite = (pFifo->offWrite + cbRead) % UART_FIFO_LENGTH;
-        pFifo->cbUsed   += cbRead;
-        cbFilled        += cbRead;
-
-        if (cbRead < cbThisRead)
-            break;
-    }
-
-    if (cbFilled)
-    {
-        UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_DR);
-        serialIrqUpdate(pThis);
-    }
-
-    ASMAtomicSubU32(&pThis->cbAvailRdr, cbFilled);
-}
-
-
-/**
- * Fetches a single byte and writes it to RBR.
- *
- * @returns nothing.
- * @param   pThis               The serial port instance.
- */
-static void serialR3ByteFetch(PDEVSERIAL pThis)
-{
-    if (ASMAtomicReadU32(&pThis->cbAvailRdr))
-    {
-        AssertPtr(pThis->pDrvSerial);
-        size_t cbRead = 0;
-        int rc2 = pThis->pDrvSerial->pfnReadRdr(pThis->pDrvSerial, &pThis->uRegRbr, 1, &cbRead);
-        AssertMsg(RT_SUCCESS(rc2) && cbRead == 1, ("This shouldn't fail and always return one byte!\n"));
-        UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_DR);
-        serialIrqUpdate(pThis);
-    }
-}
-
-
-/**
- * Fetches a ready data based on the FIFO setting.
- *
- * @returns nothing.
- * @param   pThis               The serial port instance.
- */
-static void serialR3DataFetch(PDEVSERIAL pThis)
-{
-    if (pThis->uRegFcr % UART_REG_FCR_FIFO_EN)
-        serialR3RecvFifoFill(pThis);
-    else
-        serialR3ByteFetch(pThis);
-}
-#endif
-
-
-/**
- * Write handler for the THR/DLL register (depending on the DLAB bit in LCR).
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   uVal                The value to write.
- */
-DECLINLINE(int) serialRegThrDllWrite(PDEVSERIAL pThis, uint8_t uVal)
-{
-    int rc = VINF_SUCCESS;
-
-    /* A set DLAB causes a write to the lower 8bits of the divisor latch. */
-    if (pThis->uRegLcr & UART_REG_LCR_DLAB)
-    {
-        if (uVal != (pThis->uRegDivisor & 0xff))
-        {
-#ifndef IN_RING3
-            rc = VINF_IOM_R3_IOPORT_WRITE;
-#else
-            pThis->uRegDivisor = (pThis->uRegDivisor & 0xff00) | uVal;
-            serialR3ParamsUpdate(pThis);
-#endif
-        }
-    }
-    else
-    {
-        if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
-        {
-#ifndef IN_RING3
-            rc = VINF_IOM_R3_IOPORT_WRITE;
-#else
-            serialFifoPut(&pThis->FifoXmit, true /*fOvrWr*/, uVal);
-            UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_THRE | UART_REG_LSR_TEMT);
-            serialIrqUpdate(pThis);
-            if (pThis->pDrvSerial)
-            {
-                int rc2 = pThis->pDrvSerial->pfnDataAvailWrNotify(pThis->pDrvSerial, 1);
-                if (RT_FAILURE(rc2))
-                    LogRelMax(10, ("Serial#%d: Failed to send data with %Rrc\n", rc2));
-            }
-#endif
-        }
-        else
-        {
-            /* Notify the lower driver about available data only if the register was empty before. */
-            if (pThis->uRegLsr & UART_REG_LSR_THRE)
-            {
-#ifndef IN_RING3
-                rc = VINF_IOM_R3_IOPORT_WRITE;
-#else
-                pThis->uRegThr = uVal;
-                UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_THRE | UART_REG_LSR_TEMT);
-                serialIrqUpdate(pThis);
-                if (pThis->pDrvSerial)
-                {
-                    int rc2 = pThis->pDrvSerial->pfnDataAvailWrNotify(pThis->pDrvSerial, 1);
-                    if (RT_FAILURE(rc2))
-                        LogRelMax(10, ("Serial#%d: Failed to send data with %Rrc\n", rc2));
-                }
-#endif
-            }
-            else
-                pThis->uRegThr = uVal;
-        }
-    }
-
-    return rc;
-}
-
-
-/**
- * Write handler for the IER/DLM register (depending on the DLAB bit in LCR).
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   uVal                The value to write.
- */
-DECLINLINE(int) serialRegIerDlmWrite(PDEVSERIAL pThis, uint8_t uVal)
-{
-    int rc = VINF_SUCCESS;
-
-    /* A set DLAB causes a write to the higher 8bits of the divisor latch. */
-    if (pThis->uRegLcr & UART_REG_LCR_DLAB)
-    {
-        if (uVal != (pThis->uRegDivisor & 0xff00) >> 8)
-        {
-#ifndef IN_RING3
-            rc = VINF_IOM_R3_IOPORT_WRITE;
-#else
-            pThis->uRegDivisor = (pThis->uRegDivisor & 0xff) | (uVal << 8);
-            serialR3ParamsUpdate(pThis);
-#endif
-        }
-    }
-    else
-    {
-        pThis->uRegIer = uVal & UART_REG_IER_MASK_WR;
-        serialIrqUpdate(pThis);
-    }
-
-    return rc;
-}
-
-
-/**
- * Write handler for the FCR register.
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   uVal                The value to write.
- */
-DECLINLINE(int) serialRegFcrWrite(PDEVSERIAL pThis, uint8_t uVal)
-{
-    int rc = VINF_SUCCESS;
-
-    if (   pThis->f16550AEnabled
-        && uVal != pThis->uRegFcr)
-    {
-        /* A change in the FIFO enable bit clears both FIFOs automatically. */
-        if ((uVal ^ pThis->uRegFcr) & UART_REG_FCR_FIFO_EN)
-        {
-            serialFifoClear(&pThis->FifoXmit);
-            serialFifoClear(&pThis->FifoRecv);
-
-            /* Fill in the next data. */
-            if (ASMAtomicReadU32(&pThis->cbAvailRdr))
-            {
-#ifndef IN_RING3
-                rc = VINF_IOM_R3_IOPORT_WRITE;
-#else
-                serialR3DataFetch(pThis);
-#endif
-            }
-        }
-
-        if (rc == VINF_SUCCESS)
-        {
-            if (uVal & UART_REG_FCR_RCV_FIFO_RST)
-                serialFifoClear(&pThis->FifoRecv);
-            if (uVal & UART_REG_FCR_XMIT_FIFO_RST)
-                serialFifoClear(&pThis->FifoXmit);
-
-            if (uVal & UART_REG_FCR_FIFO_EN)
-            {
-                switch (UART_REG_FCR_RCV_LVL_IRQ_GET(uVal))
-                {
-                    case UART_REG_FCR_RCV_LVL_IRQ_1:
-                        pThis->FifoRecv.cbItl = 1;
-                        break;
-                    case UART_REG_FCR_RCV_LVL_IRQ_4:
-                        pThis->FifoRecv.cbItl = 4;
-                        break;
-                    case UART_REG_FCR_RCV_LVL_IRQ_8:
-                        pThis->FifoRecv.cbItl = 8;
-                        break;
-                    case UART_REG_FCR_RCV_LVL_IRQ_14:
-                        pThis->FifoRecv.cbItl = 14;
-                        break;
-                    default:
-                        /* Should never occur as all cases are handled earlier. */
-                        AssertMsgFailed(("Impossible to hit!\n"));
-                }
-            }
-
-            /* The FIFO reset bits are self clearing. */
-            pThis->uRegFcr = uVal & UART_REG_FCR_MASK_STICKY;
-            serialIrqUpdate(pThis);
-        }
-    }
-
-    return rc;
-}
-
-
-/**
- * Write handler for the LCR register.
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   uVal                The value to write.
- */
-DECLINLINE(int) serialRegLcrWrite(PDEVSERIAL pThis, uint8_t uVal)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Any change except the DLAB bit causes a switch to R3. */
-    if ((pThis->uRegLcr & ~UART_REG_LCR_DLAB) != (uVal & ~UART_REG_LCR_DLAB))
-    {
-#ifndef IN_RING3
-        rc = VINF_IOM_R3_IOPORT_WRITE;
-#else
-        /* Check whether the BREAK bit changed before updating the LCR value. */
-        bool fBrkEn = RT_BOOL(uVal & UART_REG_LCR_BRK_SET);
-        bool fBrkChg = fBrkEn != RT_BOOL(pThis->uRegLcr & UART_REG_LCR_BRK_SET);
-        pThis->uRegLcr = uVal;
-        serialR3ParamsUpdate(pThis);
-
-        if (   fBrkChg
-            && pThis->pDrvSerial)
-            pThis->pDrvSerial->pfnChgBrk(pThis->pDrvSerial, fBrkEn);
-#endif
-    }
-    else
-        pThis->uRegLcr = uVal;
-
-    return rc;
-}
-
-
-/**
- * Write handler for the MCR register.
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   uVal                The value to write.
- */
-DECLINLINE(int) serialRegMcrWrite(PDEVSERIAL pThis, uint8_t uVal)
-{
-    int rc = VINF_SUCCESS;
-
-    uVal &= UART_REG_MCR_MASK_WR;
-    if (pThis->uRegMcr != uVal)
-    {
-#ifndef IN_RING3
-        rc = VINF_IOM_R3_IOPORT_WRITE;
-#else
-        /*
-         * When loopback mode is activated the RTS, DTR, OUT1 and OUT2 lines are
-         * disconnected and looped back to MSR.
-         */
-        if (   (uVal & UART_REG_MCR_LOOP)
-            && !(pThis->uRegMcr & UART_REG_MCR_LOOP)
-            && pThis->pDrvSerial)
-            pThis->pDrvSerial->pfnChgModemLines(pThis->pDrvSerial, false /*fRts*/, false /*fDtr*/);
-
-        pThis->uRegMcr = uVal;
-        if (uVal & UART_REG_MCR_LOOP)
-        {
-            uint8_t uRegMsrSts = 0;
-
-            if (uVal & UART_REG_MCR_RTS)
-                uRegMsrSts |= UART_REG_MSR_CTS;
-            if (uVal & UART_REG_MCR_DTR)
-                uRegMsrSts |= UART_REG_MSR_DSR;
-            if (uVal & UART_REG_MCR_OUT1)
-                uRegMsrSts |= UART_REG_MSR_RI;
-            if (uVal & UART_REG_MCR_OUT2)
-                uRegMsrSts |= UART_REG_MSR_DCD;
-            serialR3MsrUpdate(pThis, uRegMsrSts);
-        }
-        else if (pThis->pDrvSerial)
-            pThis->pDrvSerial->pfnChgModemLines(pThis->pDrvSerial,
-                                                RT_BOOL(uVal & UART_REG_MCR_RTS),
-                                                RT_BOOL(uVal & UART_REG_MCR_DTR));
-#endif
-    }
-
-    return rc;
-}
-
-
-/**
- * Read handler for the RBR/DLL register (depending on the DLAB bit in LCR).
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   puVal               Where to store the read value on success.
- */
-DECLINLINE(int) serialRegRbrDllRead(PDEVSERIAL pThis, uint32_t *puVal)
-{
-    int rc = VINF_SUCCESS;
-
-    /* A set DLAB causes a read from the lower 8bits of the divisor latch. */
-    if (pThis->uRegLcr & UART_REG_LCR_DLAB)
-        *puVal = pThis->uRegDivisor & 0xff;
-    else
-    {
-        if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
-        {
-            /*
-             * Only go back to R3 if there is new data available for the FIFO
-             * and we would clear the interrupt to fill it up again.
-             */
-            if (   pThis->FifoRecv.cbUsed <= pThis->FifoRecv.cbItl
-                && ASMAtomicReadU32(&pThis->cbAvailRdr) > 0)
-            {
-#ifndef IN_RING3
-                rc = VINF_IOM_R3_IOPORT_READ;
-#else
-                serialR3RecvFifoFill(pThis);
-#endif
-            }
-
-            if (rc == VINF_SUCCESS)
-            {
-                *puVal = serialFifoGet(&pThis->FifoRecv);
-                if (!pThis->FifoRecv.cbUsed)
-                    UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_DR);
-                serialIrqUpdate(pThis);
-            }
-        }
-        else
-        {
-            *puVal = pThis->uRegRbr;
-
-            if (pThis->uRegLsr & UART_REG_LSR_DR)
-            {
-                uint32_t cbAvail = ASMAtomicDecU32(&pThis->cbAvailRdr);
-                if (!cbAvail)
-                {
-                    UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_DR);
-                    serialIrqUpdate(pThis);
-                }
-                else
-                {
-#ifndef IN_RING3
-                    /* Restore state and go back to R3. */
-                    ASMAtomicIncU32(&pThis->cbAvailRdr);
-                    rc = VINF_IOM_R3_IOPORT_READ;
-#else
-                    /* Fetch new data and keep the DR bit set. */
-                    serialR3DataFetch(pThis);
-#endif
-                }
-            }
-        }
-    }
-
-    return rc;
-}
-
-
-/**
- * Read handler for the IER/DLM register (depending on the DLAB bit in LCR).
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   puVal               Where to store the read value on success.
- */
-DECLINLINE(int) serialRegIerDlmRead(PDEVSERIAL pThis, uint32_t *puVal)
-{
-    int rc = VINF_SUCCESS;
-
-    /* A set DLAB causes a read from the upper 8bits of the divisor latch. */
-    if (pThis->uRegLcr & UART_REG_LCR_DLAB)
-        *puVal = (pThis->uRegDivisor & 0xff00) >> 8;
-    else
-        *puVal = pThis->uRegIer;
-
-    return rc;
-}
-
-
-/**
- * Read handler for the IIR register.
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   puVal               Where to store the read value on success.
- */
-DECLINLINE(int) serialRegIirRead(PDEVSERIAL pThis, uint32_t *puVal)
-{
-    *puVal = pThis->uRegIir;
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Read handler for the LSR register.
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   puVal               Where to store the read value on success.
- */
-DECLINLINE(int) serialRegLsrRead(PDEVSERIAL pThis, uint32_t *puVal)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Yield if configured and there is no data available. */
-    if (   !(pThis->uRegLsr & UART_REG_LSR_DR)
-        && pThis->fYieldOnLSRRead)
-    {
-#ifndef IN_RING3
-        return VINF_IOM_R3_IOPORT_READ;
-#else
-        RTThreadYield();
-#endif
-    }
-
-    *puVal = pThis->uRegLsr;
-    /*
-     * Reading this register clears the Overrun (OE), Parity (PE) and Framing (FE) error
-     * as well as the Break Interrupt (BI).
-     */
-    UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_BITS_IIR_RCL);
-    serialIrqUpdate(pThis);
-
-    return rc;
-}
-
-
-/**
- * Read handler for the MSR register.
- *
- * @returns VBox status code.
- * @param   pThis               The serial port instance.
- * @param   puVal               Where to store the read value on success.
- */
-DECLINLINE(int) serialRegMsrRead(PDEVSERIAL pThis, uint32_t *puVal)
-{
-    *puVal = pThis->uRegMsr;
-
-    /* Clear any of the delta bits. */
-    UART_REG_CLR(pThis->uRegMsr, UART_REG_MSR_BITS_IIR_MS);
-    serialIrqUpdate(pThis);
-    return VINF_SUCCESS;
-}
-
-
-#ifdef LOG_ENABLED
-/**
- * Converts the register index into a sensible memnonic.
- *
- * @returns Register memnonic.
- * @param   pThis               The serial port instance.
- * @param   idxReg              Register index.
- * @param   fWrite              Flag whether the register gets written.
- */
-DECLINLINE(const char *) serialRegIdx2Str(PDEVSERIAL pThis, uint8_t idxReg, bool fWrite)
-{
-    const char *psz = "INV";
-
-    switch (idxReg)
-    {
-        /*case UART_REG_THR_DLL_INDEX:*/
-        case UART_REG_RBR_DLL_INDEX:
-            if (pThis->uRegLcr & UART_REG_LCR_DLAB)
-                psz = "DLL";
-            else if (fWrite)
-                psz = "THR";
-            else
-                psz = "RBR";
-            break;
-        case UART_REG_IER_DLM_INDEX:
-            if (pThis->uRegLcr & UART_REG_LCR_DLAB)
-                psz = "DLM";
-            else
-                psz = "IER";
-            break;
-        /*case UART_REG_IIR_INDEX:*/
-        case UART_REG_FCR_INDEX:
-            if (fWrite)
-                psz = "FCR";
-            else
-                psz = "IIR";
-            break;
-        case UART_REG_LCR_INDEX:
-            psz = "LCR";
-            break;
-        case UART_REG_MCR_INDEX:
-            psz = "MCR";
-            break;
-        case UART_REG_LSR_INDEX:
-            psz = "LSR";
-            break;
-        case UART_REG_MSR_INDEX:
-            psz = "MSR";
-            break;
-        case UART_REG_SCR_INDEX:
-            psz = "SCR";
-            break;
-    }
-
-    return psz;
-}
-#endif
-
-/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */
-
-/**
- * @callback_method_impl{FNIOMIOPORTOUT}
- */
-PDMBOTHCBDECL(int) serialIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb)
-{
-    PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
-    Assert(PDMCritSectIsOwner(&pThis->CritSect));
-    RT_NOREF_PV(pvUser);
-
-    uint8_t idxReg = uPort & 0x7;
-    LogFlowFunc(("pDevIns=%#p pvUser=%#p uPort=%RTiop{%s} u32=%#x cb=%u\n",
-                 pDevIns, pvUser, uPort, serialRegIdx2Str(pThis, idxReg, true /*fWrite*/), u32, cb));
-
-    AssertMsgReturn(cb == 1, ("uPort=%#x cb=%d u32=%#x\n", uPort, cb, u32), VINF_SUCCESS);
-
-    int rc = VINF_SUCCESS;
-    uint8_t uVal = (uint8_t)u32;
-    switch (idxReg)
-    {
-        case UART_REG_THR_DLL_INDEX:
-            rc = serialRegThrDllWrite(pThis, uVal);
-            break;
-        case UART_REG_IER_DLM_INDEX:
-            rc = serialRegIerDlmWrite(pThis, uVal);
-            break;
-        case UART_REG_FCR_INDEX:
-            rc = serialRegFcrWrite(pThis, uVal);
-            break;
-        case UART_REG_LCR_INDEX:
-            rc = serialRegLcrWrite(pThis, uVal);
-            break;
-        case UART_REG_MCR_INDEX:
-            rc = serialRegMcrWrite(pThis, uVal);
-            break;
-        case UART_REG_SCR_INDEX:
-            pThis->uRegScr = u32;
-            break;
-        default:
-            break;
-    }
-
-    LogFlowFunc(("-> %Rrc\n", rc));
-    return rc;
-}
-
-
-/**
- * @callback_method_impl{FNIOMIOPORTIN}
- */
-PDMBOTHCBDECL(int) serialIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb)
-{
-    PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
-    Assert(PDMCritSectIsOwner(&pThis->CritSect));
-    RT_NOREF_PV(pvUser);
-
-    if (cb != 1)
-        return VERR_IOM_IOPORT_UNUSED;
-
-    uint8_t idxReg = uPort & 0x7;
-    int rc = VINF_SUCCESS;
-    switch (idxReg)
-    {
-        case UART_REG_RBR_DLL_INDEX:
-            rc = serialRegRbrDllRead(pThis, pu32);
-            break;
-        case UART_REG_IER_DLM_INDEX:
-            rc = serialRegIerDlmRead(pThis, pu32);
-            break;
-        case UART_REG_IIR_INDEX:
-            rc = serialRegIirRead(pThis, pu32);
-            break;
-        case UART_REG_LCR_INDEX:
-            *pu32 = pThis->uRegLcr;
-            break;
-        case UART_REG_MCR_INDEX:
-            *pu32 = pThis->uRegMcr;
-            break;
-        case UART_REG_LSR_INDEX:
-            rc = serialRegLsrRead(pThis, pu32);
-            break;
-        case UART_REG_MSR_INDEX:
-            rc = serialRegMsrRead(pThis, pu32);
-            break;
-        case UART_REG_SCR_INDEX:
-            *pu32 = pThis->uRegScr;
-            break;
-        default:
-            rc = VERR_IOM_IOPORT_UNUSED;
-    }
-
-    LogFlowFunc(("pDevIns=%#p pvUser=%#p uPort=%RTiop{%s} u32=%#x cb=%u -> %Rrc\n",
-                 pDevIns, pvUser, uPort, serialRegIdx2Str(pThis, idxReg, false /*fWrite*/), *pu32, cb, rc));
-    return rc;
-}
-
-
-#ifdef IN_RING3
-
-/* -=-=-=-=-=-=-=-=- PDMISERIALPORT on LUN#0 -=-=-=-=-=-=-=-=- */
-
-
-/**
- * @interface_method_impl{PDMISERIALPORT,pfnDataAvailRdrNotify}
- */
-static DECLCALLBACK(int) serialR3DataAvailRdrNotify(PPDMISERIALPORT pInterface, size_t cbAvail)
-{
-    LogFlowFunc(("pInterface=%#p cbAvail=%zu\n", pInterface, cbAvail));
-    PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
-
-    AssertMsg((uint32_t)cbAvail == cbAvail, ("Too much data available\n"));
-
-    uint32_t cbAvailOld = ASMAtomicAddU32(&pThis->cbAvailRdr, (uint32_t)cbAvail);
-    LogFlow(("    cbAvailRdr=%zu -> cbAvailRdr=%zu\n", cbAvailOld, cbAvail + cbAvailOld));
-    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
-    if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
-        serialR3RecvFifoFill(pThis);
-    else if (!cbAvailOld)
-    {
-        size_t cbRead = 0;
-        int rc = pThis->pDrvSerial->pfnReadRdr(pThis->pDrvSerial, &pThis->uRegRbr, 1, &cbRead);
-        AssertMsg(RT_SUCCESS(rc) && cbRead == 1, ("This shouldn't fail and always return one byte!\n"));
-        UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_DR);
-        serialIrqUpdate(pThis);
-    }
-    PDMCritSectLeave(&pThis->CritSect);
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{PDMISERIALPORT,pfnDataSentNotify}
- */
-static DECLCALLBACK(int) serialR3DataSentNotify(PPDMISERIALPORT pInterface)
-{
-    LogFlowFunc(("pInterface=%#p\n", pInterface));
-    PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
-
-    /* Set the transmitter empty bit because everything was sent. */
-    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
-    UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_TEMT);
-    serialIrqUpdate(pThis);
-    PDMCritSectLeave(&pThis->CritSect);
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{PDMISERIALPORT,pfnReadWr}
- */
-static DECLCALLBACK(int) serialR3ReadWr(PPDMISERIALPORT pInterface, void *pvBuf, size_t cbRead, size_t *pcbRead)
-{
-    LogFlowFunc(("pInterface=%#p pvBuf=%#p cbRead=%zu pcbRead=%#p\n", pInterface, pvBuf, cbRead, pcbRead));
-    PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
-
-    AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER);
-
-    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
-    if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
-    {
-        *pcbRead = serialFifoCopyTo(&pThis->FifoXmit, pvBuf, cbRead);
-        if (!pThis->FifoXmit.cbUsed)
-            UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_THRE);
-        if (*pcbRead)
-            UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_TEMT);
-        serialIrqUpdate(pThis);
-    }
-    else if (!(pThis->uRegLsr & UART_REG_LSR_THRE))
-    {
-        *(uint8_t *)pvBuf = pThis->uRegThr;
-        *pcbRead = 1;
-        UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_THRE);
-        UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_TEMT);
-        serialIrqUpdate(pThis);
-    }
-    else
-    {
-        AssertMsgFailed(("There is no data to read!\n"));
-        *pcbRead = 0;
-    }
-    PDMCritSectLeave(&pThis->CritSect);
-
-    LogFlowFunc(("-> VINF_SUCCESS{*pcbRead=%zu}\n", *pcbRead));
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{PDMISERIALPORT,pfnNotifyStsLinesChanged}
- */
-static DECLCALLBACK(int) serialR3NotifyStsLinesChanged(PPDMISERIALPORT pInterface, uint32_t fNewStatusLines)
-{
-    LogFlowFunc(("pInterface=%#p fNewStatusLines=%#x\n", pInterface, fNewStatusLines));
-    PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
-
-    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
-    serialR3StsLinesUpdate(pThis, fNewStatusLines);
-    PDMCritSectLeave(&pThis->CritSect);
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{PDMISERIALPORT,pfnNotifyBrk}
- */
-static DECLCALLBACK(int) serialR3NotifyBrk(PPDMISERIALPORT pInterface)
-{
-    LogFlowFunc(("pInterface=%#p\n", pInterface));
-    PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ISerialPort);
-
-    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
-    UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_BI);
-    serialIrqUpdate(pThis);
-    PDMCritSectLeave(&pThis->CritSect);
-    return VINF_SUCCESS;
-}
-
-
-/* -=-=-=-=-=-=-=-=- PDMIBASE on LUN#0 -=-=-=-=-=-=-=-=- */
-
-/**
- * @interface_method_impl{PDMIBASE,pfnQueryInterface}
- */
-static DECLCALLBACK(void *) serialR3QueryInterface(PPDMIBASE pInterface, const char *pszIID)
-{
-    PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, IBase);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALPORT, &pThis->ISerialPort);
-    return NULL;
-}
 
 
@@ -1436,8 +132,6 @@
 static DECLCALLBACK(void) serialR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
 {
-    RT_NOREF(offDelta);
-    PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
-
-    pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+    PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
+    uartR3Relocate(&pThis->UartCore, offDelta);
 }
 
@@ -1449,40 +143,5 @@
 {
     PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
-
-    pThis->uRegDivisor = 0x0c; /* Default to 9600 Baud. */
-    pThis->uRegRbr     = 0;
-    pThis->uRegThr     = 0;
-    pThis->uRegIer     = 0;
-    pThis->uRegIir     = UART_REG_IIR_IP_NO_INT;
-    pThis->uRegFcr     = 0;
-    pThis->uRegLcr     = 0; /* 5 data bits, no parity, 1 stop bit. */
-    pThis->uRegMcr     = 0;
-    pThis->uRegLsr     = UART_REG_LSR_THRE | UART_REG_LSR_TEMT;
-    pThis->uRegMsr     = 0; /* Updated below. */
-    pThis->uRegScr     = 0;
-
-    serialFifoClear(&pThis->FifoXmit);
-    serialFifoClear(&pThis->FifoRecv);
-    pThis->FifoRecv.cbItl = 1;
-
-    serialR3ParamsUpdate(pThis);
-    serialIrqUpdate(pThis);
-
-    if (pThis->pDrvSerial)
-    {
-        /* Set the modem lines to reflect the current state. */
-        int rc = pThis->pDrvSerial->pfnChgModemLines(pThis->pDrvSerial, false /*fRts*/, false /*fDtr*/);
-        if (RT_FAILURE(rc))
-            LogRel(("Serial#%d: Failed to set modem lines with %Rrc during reset\n",
-                    pThis->pDevInsR3->iInstance, rc));
-
-        uint32_t fStsLines = 0;
-        rc = pThis->pDrvSerial->pfnQueryStsLines(pThis->pDrvSerial, &fStsLines);
-        if (RT_SUCCESS(rc))
-            serialR3StsLinesUpdate(pThis, fStsLines);
-        else
-            LogRel(("Serial#%d: Failed to query status line status with %Rrc during reset\n",
-                    pThis->pDevInsR3->iInstance, rc));
-    }
+    uartR3Reset(&pThis->UartCore);
 }
 
@@ -1493,42 +152,18 @@
 static DECLCALLBACK(int) serialR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
 {
+    RT_NOREF(fFlags);
+    PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
+    return uartR3Attach(&pThis->UartCore, iLUN);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDetach}
+ */
+static DECLCALLBACK(void) serialR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
     RT_NOREF(iLUN, fFlags);
     PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
-
-    int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Serial Char");
-    if (RT_SUCCESS(rc))
-    {
-        pThis->pDrvSerial = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMISERIALCONNECTOR);
-        if (!pThis->pDrvSerial)
-        {
-            AssertLogRelMsgFailed(("Configuration error: instance %d has no serial interface!\n", pDevIns->iInstance));
-            return VERR_PDM_MISSING_INTERFACE;
-        }
-    }
-    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
-    {
-        pThis->pDrvBase = NULL;
-        pThis->pDrvSerial = NULL;
-        rc = VINF_SUCCESS;
-        LogRel(("Serial#%d: no unit\n", pDevIns->iInstance));
-    }
-    else /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
-        LogRel(("Serial#%d: Failed to attach to serial driver. rc=%Rrc\n", pDevIns->iInstance, rc));
-
-   return rc;
-}
-
-
-/**
- * @interface_method_impl{PDMDEVREG,pfnDetach}
- */
-static DECLCALLBACK(void) serialR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
-{
-    RT_NOREF(iLUN, fFlags);
-    PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
-
-    /* Zero out important members. */
-    pThis->pDrvBase   = NULL;
-    pThis->pDrvSerial = NULL;
+    uartR3Detach(&pThis->UartCore);
 }
 
@@ -1542,5 +177,5 @@
     PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
 
-    PDMR3CritSectDelete(&pThis->CritSect);
+    uartR3Destruct(&pThis->UartCore);
     return VINF_SUCCESS;
 }
@@ -1565,14 +200,4 @@
     pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
     pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
-
-    /* IBase */
-    pThis->IBase.pfnQueryInterface = serialR3QueryInterface;
-
-    /* ISerialPort */
-    pThis->ISerialPort.pfnDataAvailRdrNotify    = serialR3DataAvailRdrNotify;
-    pThis->ISerialPort.pfnDataSentNotify        = serialR3DataSentNotify;
-    pThis->ISerialPort.pfnReadWr                = serialR3ReadWr;
-    pThis->ISerialPort.pfnNotifyStsLinesChanged = serialR3NotifyStsLinesChanged;
-    pThis->ISerialPort.pfnNotifyBrk             = serialR3NotifyBrk;
 
     /*
@@ -1601,5 +226,6 @@
                                 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
 
-    rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &pThis->fYieldOnLSRRead, false);
+    bool fYieldOnLSRRead = false;
+    rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &fYieldOnLSRRead, false);
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc,
@@ -1637,5 +263,6 @@
                                 N_("Configuration error: Failed to get the \"IOBase\" value"));
 
-    rc = CFGMR3QueryBoolDef(pCfg, "Enable16550A", &pThis->f16550AEnabled, true);
+    bool f16550AEnabled = true;
+    rc = CFGMR3QueryBoolDef(pCfg, "Enable16550A", &f16550AEnabled, true);
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc,
@@ -1646,20 +273,12 @@
 
     LogRel(("Serial#%d: emulating %s (IOBase: %04x IRQ: %u)\n",
-            pDevIns->iInstance, pThis->f16550AEnabled ? "16550A" : "16450", uIoBase, uIrq));
-
-    /*
-     * Initialize critical section and the semaphore.  Change the default
-     * critical section to ours so that TM and IOM will enter it before
-     * calling us.
-     *
-     * Note! This must of be done BEFORE creating timers, registering I/O ports
-     *       and other things which might pick up the default CS or end up
-     *       calling back into the device.
-     */
-    rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "Serial#%d", iInstance);
-    AssertRCReturn(rc, rc);
-
-    rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
-    AssertRCReturn(rc, rc);
+            pDevIns->iInstance, f16550AEnabled ? "16550A" : "16450", uIoBase, uIrq));
+
+    /*
+     * Init locks, using explicit locking where necessary.
+     */
+    rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+    if (RT_FAILURE(rc))
+        return rc;
 
     /*
@@ -1672,8 +291,16 @@
         return rc;
 
+    PVM pVM = PDMDevHlpGetVM(pDevIns);
+    RTR0PTR pfnSerialIrqReqR0 = NIL_RTR0PTR;
+    RTRCPTR pfnSerialIrqReqRC = NIL_RTRCPTR;
+
     if (pThis->fRCEnabled)
     {
         rc = PDMDevHlpIOPortRegisterRC(pDevIns, uIoBase, 8, 0, "serialIoPortWrite",
                                       "serialIoPortRead", NULL, NULL, "SERIAL");
+        if (   RT_SUCCESS(rc)
+            && VM_IS_RAW_MODE_ENABLED(pVM))
+            rc = PDMR3LdrGetSymbolRC(pVM, pDevIns->pReg->szRCMod, "serialIrqReq", &pfnSerialIrqReqRC);
+
         if (RT_FAILURE(rc))
             return rc;
@@ -1684,4 +311,7 @@
         rc = PDMDevHlpIOPortRegisterR0(pDevIns, uIoBase, 8, 0, "serialIoPortWrite",
                                       "serialIoPortRead", NULL, NULL, "SERIAL");
+        if (RT_SUCCESS(rc))
+            rc = PDMR3LdrGetSymbolR0(pVM, pDevIns->pReg->szR0Mod, "serialIrqReq", &pfnSerialIrqReqR0);
+
         if (RT_FAILURE(rc))
             return rc;
@@ -1698,29 +328,8 @@
 #endif
 
-    /*
-     * Attach the char driver and get the interfaces.
-     */
-    rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Serial");
-    if (RT_SUCCESS(rc))
-    {
-        pThis->pDrvSerial = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMISERIALCONNECTOR);
-        if (!pThis->pDrvSerial)
-        {
-            AssertLogRelMsgFailed(("Configuration error: instance %d has no serial interface!\n", iInstance));
-            return VERR_PDM_MISSING_INTERFACE;
-        }
-    }
-    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
-    {
-        pThis->pDrvBase   = NULL;
-        pThis->pDrvSerial = NULL;
-        LogRel(("Serial#%d: no unit\n", iInstance));
-    }
-    else
-    {
-        AssertLogRelMsgFailed(("Serial#%d: Failed to attach to char driver. rc=%Rrc\n", iInstance, rc));
-        /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
-        return rc;
-    }
+
+    /* Init the UART core structure. */
+    rc = uartR3Init(&pThis->UartCore, pDevIns, f16550AEnabled ? UARTTYPE_16550A : UARTTYPE_16450, 0,
+                    fYieldOnLSRRead ? UART_CORE_YIELD_ON_LSR_READ : 0, serialIrqReq, pfnSerialIrqReqR0, pfnSerialIrqReqRC);
 
     serialR3Reset(pDevIns);
Index: /trunk/src/VBox/Devices/Serial/UartCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Serial/UartCore.cpp	(revision 73135)
+++ /trunk/src/VBox/Devices/Serial/UartCore.cpp	(revision 73135)
@@ -0,0 +1,1475 @@
+/* $Id$ */
+/** @file
+ * UartCore - UART  (16550A up to 16950) emulation.
+ *
+ * The documentation for this device was taken from the PC16550D spec from TI.
+ */
+
+/*
+ * Copyright (C) 2018 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_SERIAL
+#include <iprt/uuid.h>
+#include <iprt/assert.h>
+
+#include "VBoxDD.h"
+#include "UartCore.h"
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+
+/** The RBR/DLL register index (from the base of the port range). */
+#define UART_REG_RBR_DLL_INDEX               0
+
+/** The THR/DLL register index (from the base of the port range). */
+#define UART_REG_THR_DLL_INDEX               0
+
+/** The IER/DLM register index (from the base of the port range). */
+#define UART_REG_IER_DLM_INDEX               1
+/** Enable received data available interrupt */
+# define UART_REG_IER_ERBFI                  RT_BIT(0)
+/** Enable transmitter holding register empty interrupt */
+# define UART_REG_IER_ETBEI                  RT_BIT(1)
+/** Enable receiver line status interrupt */
+# define UART_REG_IER_ELSI                   RT_BIT(2)
+/** Enable modem status interrupt. */
+# define UART_REG_IER_EDSSI                  RT_BIT(3)
+/** Mask of writeable bits. */
+# define UART_REG_IER_MASK_WR                0x0f
+
+/** The IIR register index (from the base of the port range). */
+#define UART_REG_IIR_INDEX                   2
+/** Interrupt Pending - high means no interrupt pending. */
+# define UART_REG_IIR_IP_NO_INT              RT_BIT(0)
+/** Interrupt identification mask. */
+# define UART_REG_IIR_ID_MASK                0x0e
+/** Sets the interrupt identification to the given value. */
+# define UART_REG_IIR_ID_SET(a_Val)          (((a_Val) << 1) & UART_REG_IIR_ID_MASK)
+/** Receiver Line Status interrupt. */
+#  define UART_REG_IIR_ID_RCL                0x3
+/** Received Data Available interrupt. */
+#  define UART_REG_IIR_ID_RDA                0x2
+/** Character Timeou Indicator interrupt. */
+#  define UART_REG_IIR_ID_CTI                0x6
+/** Transmitter Holding Register Empty interrupt. */
+#  define UART_REG_IIR_ID_THRE               0x1
+/** Modem Status interrupt. */
+#  define UART_REG_IIR_ID_MS                 0x0
+/** FIFOs enabled. */
+# define UART_REG_IIR_FIFOS_EN               0xc0
+/** Bits relevant for checking whether the interrupt status has changed. */
+# define UART_REG_IIR_CHANGED_MASK           0x0f
+
+/** The FCR register index (from the base of the port range). */
+#define UART_REG_FCR_INDEX                   2
+/** Enable the TX/RX FIFOs. */
+# define UART_REG_FCR_FIFO_EN                RT_BIT(0)
+/** Reset the receive FIFO. */
+# define UART_REG_FCR_RCV_FIFO_RST           RT_BIT(1)
+/** Reset the transmit FIFO. */
+# define UART_REG_FCR_XMIT_FIFO_RST          RT_BIT(2)
+/** DMA Mode Select. */
+# define UART_REG_FCR_DMA_MODE_SEL           RT_BIT(3)
+/** Receiver level interrupt trigger. */
+# define UART_REG_FCR_RCV_LVL_IRQ_MASK       0xc0
+/** Returns the receive level trigger value from the given FCR register. */
+# define UART_REG_FCR_RCV_LVL_IRQ_GET(a_Fcr) (((a_Fcr) & UART_REG_FCR_RCV_LVL_IRQ_MASK) >> 6)
+/** RCV Interrupt trigger level - 1 byte. */
+# define UART_REG_FCR_RCV_LVL_IRQ_1          0x0
+/** RCV Interrupt trigger level - 4 bytes. */
+# define UART_REG_FCR_RCV_LVL_IRQ_4          0x1
+/** RCV Interrupt trigger level - 8 bytes. */
+# define UART_REG_FCR_RCV_LVL_IRQ_8          0x2
+/** RCV Interrupt trigger level - 14 bytes. */
+# define UART_REG_FCR_RCV_LVL_IRQ_14         0x3
+/** Mask of writeable bits. */
+# define UART_REG_FCR_MASK_WR                0xcf
+/** Mask of sticky bits. */
+# define UART_REG_FCR_MASK_STICKY            0xc9
+
+/** The LCR register index (from the base of the port range). */
+#define UART_REG_LCR_INDEX                   3
+/** Word Length Select Mask. */
+# define UART_REG_LCR_WLS_MASK               0x3
+/** Returns the WLS value form the given LCR register value. */
+# define UART_REG_LCR_WLS_GET(a_Lcr)         ((a_Lcr) & UART_REG_LCR_WLS_MASK)
+/** Number of stop bits. */
+# define UART_REG_LCR_STB                    RT_BIT(2)
+/** Parity Enable. */
+# define UART_REG_LCR_PEN                    RT_BIT(3)
+/** Even Parity. */
+# define UART_REG_LCR_EPS                    RT_BIT(4)
+/** Stick parity. */
+# define UART_REG_LCR_PAR_STICK              RT_BIT(5)
+/** Set Break. */
+# define UART_REG_LCR_BRK_SET                RT_BIT(6)
+/** Divisor Latch Access Bit. */
+# define UART_REG_LCR_DLAB                   RT_BIT(7)
+
+/** The MCR register index (from the base of the port range). */
+#define UART_REG_MCR_INDEX                   4
+/** Data Terminal Ready. */
+# define UART_REG_MCR_DTR                    RT_BIT(0)
+/** Request To Send. */
+# define UART_REG_MCR_RTS                    RT_BIT(1)
+/** Out1. */
+# define UART_REG_MCR_OUT1                   RT_BIT(2)
+/** Out2. */
+# define UART_REG_MCR_OUT2                   RT_BIT(3)
+/** Loopback connection. */
+# define UART_REG_MCR_LOOP                   RT_BIT(4)
+/** Mask of writeable bits. */
+# define UART_REG_MCR_MASK_WR                0x1f
+
+/** The LSR register index (from the base of the port range). */
+#define UART_REG_LSR_INDEX                   5
+/** Data Ready. */
+# define UART_REG_LSR_DR                     RT_BIT(0)
+/** Overrun Error. */
+# define UART_REG_LSR_OE                     RT_BIT(1)
+/** Parity Error. */
+# define UART_REG_LSR_PE                     RT_BIT(2)
+/** Framing Error. */
+# define UART_REG_LSR_FE                     RT_BIT(3)
+/** Break Interrupt. */
+# define UART_REG_LSR_BI                     RT_BIT(4)
+/** Transmitter Holding Register. */
+# define UART_REG_LSR_THRE                   RT_BIT(5)
+/** Transmitter Empty. */
+# define UART_REG_LSR_TEMT                   RT_BIT(6)
+/** Error in receiver FIFO. */
+# define UART_REG_LSR_RCV_FIFO_ERR           RT_BIT(7)
+/** The bits to check in this register when checking for the RCL interrupt. */
+# define UART_REG_LSR_BITS_IIR_RCL           0x1e
+
+/** The MSR register index (from the base of the port range). */
+#define UART_REG_MSR_INDEX                   6
+/** Delta Clear to Send. */
+# define UART_REG_MSR_DCTS                   RT_BIT(0)
+/** Delta Data Set Ready. */
+# define UART_REG_MSR_DDSR                   RT_BIT(1)
+/** Trailing Edge Ring Indicator. */
+# define UART_REG_MSR_TERI                   RT_BIT(2)
+/** Delta Data Carrier Detect. */
+# define UART_REG_MSR_DDCD                   RT_BIT(3)
+/** Clear to Send. */
+# define UART_REG_MSR_CTS                    RT_BIT(4)
+/** Data Set Ready. */
+# define UART_REG_MSR_DSR                    RT_BIT(5)
+/** Ring Indicator. */
+# define UART_REG_MSR_RI                     RT_BIT(6)
+/** Data Carrier Detect. */
+# define UART_REG_MSR_DCD                    RT_BIT(7)
+/** The bits to check in this register when checking for the MS interrupt. */
+# define UART_REG_MSR_BITS_IIR_MS            0x0f
+
+/** The SCR register index (from the base of the port range). */
+#define UART_REG_SCR_INDEX                   7
+
+/** Set the specified bits in the given register. */
+#define UART_REG_SET(a_Reg, a_Set)           ((a_Reg) |= (a_Set))
+/** Clear the specified bits in the given register. */
+#define UART_REG_CLR(a_Reg, a_Clr)           ((a_Reg) &= ~(a_Clr))
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+#ifdef IN_RING3
+/**
+ * String versions of the parity enum.
+ */
+static const char *s_aszParity[] =
+{
+    "INVALID",
+    "NONE",
+    "EVEN",
+    "ODD",
+    "MARK",
+    "SPACE",
+    "INVALID"
+};
+
+
+/**
+ * String versions of the stop bits enum.
+ */
+static const char *s_aszStopBits[] =
+{
+    "INVALID",
+    "1",
+    "1.5",
+    "2",
+    "INVALID"
+};
+#endif
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+
+
+/**
+ * Updates the IRQ state based on the current device state.
+ *
+ * @returns nothing.
+ * @param   pThis               The UART core instance.
+ */
+static void uartIrqUpdate(PUARTCORE pThis)
+{
+    LogFlowFunc(("pThis=%#p\n", pThis));
+
+    /*
+     * The interrupt uses a priority scheme, only the interrupt with the
+     * highest priority is indicated in the interrupt identification register.
+     *
+     * The priorities are as follows (high to low):
+     *      * Receiver line status
+     *      * Received data available
+     *      * Character timeout indication (only in FIFO mode).
+     *      * Transmitter holding register empty
+     *      * Modem status change.
+     */
+    uint8_t uRegIirNew = UART_REG_IIR_IP_NO_INT;
+    if (   (pThis->uRegLsr & UART_REG_LSR_BITS_IIR_RCL)
+        && (pThis->uRegIer & UART_REG_IER_ELSI))
+        uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_RCL);
+    else if (   (pThis->uRegLsr & UART_REG_LSR_DR)
+             && (pThis->uRegIer & UART_REG_IER_ERBFI)
+             && (   !(pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
+                 || pThis->FifoRecv.cbUsed >= pThis->FifoRecv.cbItl))
+        uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_RDA);
+    else if (   (pThis->uRegLsr & UART_REG_LSR_THRE)
+             && (pThis->uRegIer & UART_REG_IER_ETBEI))
+        uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_THRE);
+    else if (   (pThis->uRegMsr & UART_REG_MSR_BITS_IIR_MS)
+             && (pThis->uRegIer & UART_REG_IER_EDSSI))
+        uRegIirNew = UART_REG_IIR_ID_SET(UART_REG_IIR_ID_MS);
+
+    /** @todo Character timeout indication for FIFO mode. */
+
+    LogFlowFunc(("    uRegIirNew=%#x uRegIir=%#x\n", uRegIirNew, pThis->uRegIir));
+
+    /* Change interrupt only if the interrupt status really changed from the previous value. */
+    if (uRegIirNew != (pThis->uRegIir & UART_REG_IIR_CHANGED_MASK))
+    {
+        LogFlow(("    Interrupt source changed from %#x -> %#x (IRQ %d -> %d)\n",
+                 pThis->uRegIir, uRegIirNew,
+                 pThis->uRegIir == UART_REG_IIR_IP_NO_INT ? 0 : 1,
+                 uRegIirNew == UART_REG_IIR_IP_NO_INT ? 0 : 1));
+        if (uRegIirNew == UART_REG_IIR_IP_NO_INT)
+            pThis->CTX_SUFF(pfnUartIrqReq)(pThis->CTX_SUFF(pDevIns), pThis, pThis->iLUN, 0);
+        else
+            pThis->CTX_SUFF(pfnUartIrqReq)(pThis->CTX_SUFF(pDevIns), pThis, pThis->iLUN, 1);
+    }
+    else
+        LogFlow(("    No change in interrupt source\n"));
+
+    if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
+        uRegIirNew |= UART_REG_IIR_FIFOS_EN;
+
+    pThis->uRegIir = uRegIirNew;
+}
+
+
+/**
+ * Clears the given FIFO.
+ *
+ * @returns nothing.
+ * @param   pFifo               The FIFO to clear.
+ */
+DECLINLINE(void) uartFifoClear(PUARTFIFO pFifo)
+{
+    memset(&pFifo->abBuf[0], 0, sizeof(pFifo->abBuf));
+    pFifo->cbUsed   = 0;
+    pFifo->offWrite = 0;
+    pFifo->offRead  = 0;
+}
+
+
+/**
+ * Returns the amount of free bytes in the given FIFO.
+ *
+ * @returns The amount of bytes free in the given FIFO.
+ * @param   pFifo               The FIFO.
+ */
+DECLINLINE(size_t) uartFifoFreeGet(PUARTFIFO pFifo)
+{
+    return UART_FIFO_LENGTH - pFifo->cbUsed;
+}
+
+
+/**
+ * Puts a new character into the given FIFO.
+ *
+ * @returns Flag whether the FIFO overflowed.
+ * @param   pFifo               The FIFO to put the data into.
+ * @param   fOvrWr              Flag whether to overwrite data if the FIFO is full.
+ * @param   bData               The data to add.
+ */
+DECLINLINE(bool) uartFifoPut(PUARTFIFO pFifo, bool fOvrWr, uint8_t bData)
+{
+    if (fOvrWr || pFifo->cbUsed < UART_FIFO_LENGTH)
+    {
+        pFifo->abBuf[pFifo->offWrite] = bData;
+        pFifo->offWrite = (pFifo->offWrite + 1) % UART_FIFO_LENGTH;
+    }
+
+    bool fOverFlow = false;
+    if (pFifo->cbUsed < UART_FIFO_LENGTH)
+        pFifo->cbUsed++;
+    else
+    {
+        fOverFlow = true;
+        if (fOvrWr) /* Advance the read position to account for the lost character. */
+           pFifo->offRead = (pFifo->offRead + 1) % UART_FIFO_LENGTH;
+    }
+
+    return fOverFlow;
+}
+
+
+/**
+ * Returns the next character in the FIFO.
+ *
+ * @return Next byte in the FIFO.
+ * @param   pFifo               The FIFO to get data from.
+ */
+DECLINLINE(uint8_t) uartFifoGet(PUARTFIFO pFifo)
+{
+    uint8_t bRet = 0;
+
+    if (pFifo->cbUsed)
+    {
+        bRet = pFifo->abBuf[pFifo->offRead];
+        pFifo->offRead = (pFifo->offRead + 1) % UART_FIFO_LENGTH;
+        pFifo->cbUsed--;
+    }
+
+    return bRet;
+}
+
+
+/**
+ * Tries to copy the requested amount of data from the given FIFO into the provided buffer.
+ *
+ * @returns Amount of bytes actually copied.
+ * @param   pFifo               The FIFO to copy data from.
+ * @param   pvDst               Where to copy the data to.
+ * @param   cbCopy              How much to copy.
+ */
+DECLINLINE(size_t) uartFifoCopyTo(PUARTFIFO pFifo, void *pvDst, size_t cbCopy)
+{
+    size_t cbCopied = 0;
+    uint8_t *pbDst = (uint8_t *)pvDst;
+
+    cbCopy = RT_MIN(cbCopy, pFifo->cbUsed);
+    while (cbCopy)
+    {
+        size_t cbThisCopy = RT_MIN(cbCopy, (uint8_t)(UART_FIFO_LENGTH - pFifo->offRead));
+        memcpy(pbDst, &pFifo->abBuf[pFifo->offRead], cbThisCopy);
+
+        pFifo->offRead = (pFifo->offRead + cbThisCopy) % UART_FIFO_LENGTH;
+        pFifo->cbUsed -= cbThisCopy;
+        pbDst    += cbThisCopy;
+        cbCopied += cbThisCopy;
+        cbCopy   -= cbThisCopy;
+    }
+
+    return cbCopied;
+}
+
+
+/**
+ * Tries to copy the requested amount of data from the provided buffer into the given FIFO.
+ *
+ * @returns Amount of bytes actually copied.
+ * @param   pFifo               The FIFO to copy data to.
+ * @param   pvSrc               Where to copy the data from.
+ * @param   cbCopy              How much to copy.
+ */
+DECLINLINE(size_t) uartFifoCopyFrom(PUARTFIFO pFifo, void *pvSrc, size_t cbCopy)
+{
+    size_t cbCopied = 0;
+    uint8_t *pbSrc = (uint8_t *)pvSrc;
+
+    cbCopy = RT_MIN(cbCopy, uartFifoFreeGet(pFifo));
+    while (cbCopy)
+    {
+        size_t cbThisCopy = RT_MIN(cbCopy, (uint8_t)(UART_FIFO_LENGTH - pFifo->offWrite));
+        memcpy(&pFifo->abBuf[pFifo->offWrite], pbSrc, cbThisCopy);
+
+        pFifo->offWrite = (pFifo->offWrite + cbThisCopy) % UART_FIFO_LENGTH;
+        pFifo->cbUsed += cbThisCopy;
+        pbSrc    += cbThisCopy;
+        cbCopied += cbThisCopy;
+        cbCopy   -= cbThisCopy;
+    }
+
+    return cbCopied;
+}
+
+
+#ifdef IN_RING3
+/**
+ * Updates the delta bits for the given MSR register value which has the status line
+ * bits set.
+ *
+ * @returns nothing.
+ * @param   pThis               The serial port instance.
+ * @param   uMsrSts             MSR value with the appropriate status bits set.
+ */
+static void uartR3MsrUpdate(PUARTCORE pThis, uint8_t uMsrSts)
+{
+    /* Compare current and new states and set remaining bits accordingly. */
+    if ((uMsrSts & UART_REG_MSR_CTS) != (pThis->uRegMsr & UART_REG_MSR_CTS))
+        uMsrSts |= UART_REG_MSR_DCTS;
+    if ((uMsrSts & UART_REG_MSR_DSR) != (pThis->uRegMsr & UART_REG_MSR_DSR))
+        uMsrSts |= UART_REG_MSR_DDSR;
+    if ((uMsrSts & UART_REG_MSR_RI) != 0 && (pThis->uRegMsr & UART_REG_MSR_RI) == 0)
+        uMsrSts |= UART_REG_MSR_TERI;
+    if ((uMsrSts & UART_REG_MSR_DCD) != (pThis->uRegMsr & UART_REG_MSR_DCD))
+        uMsrSts |= UART_REG_MSR_DDCD;
+
+    pThis->uRegMsr = uMsrSts;
+
+    uartIrqUpdate(pThis);
+}
+
+
+/**
+ * Updates the serial port parameters of the attached driver with the current configuration.
+ *
+ * @returns nothing.
+ * @param   pThis               The serial port instance.
+ */
+static void uartR3ParamsUpdate(PUARTCORE pThis)
+{
+    if (   pThis->uRegDivisor != 0
+        && pThis->pDrvSerial)
+    {
+        uint32_t uBps = 115200 / pThis->uRegDivisor; /* This is for PC compatible serial port with a 1.8432 MHz crystal. */
+        unsigned cDataBits = UART_REG_LCR_WLS_GET(pThis->uRegLcr) + 5;
+        PDMSERIALSTOPBITS enmStopBits = PDMSERIALSTOPBITS_ONE;
+        PDMSERIALPARITY enmParity = PDMSERIALPARITY_NONE;
+
+        if (pThis->uRegLcr & UART_REG_LCR_STB)
+        {
+            enmStopBits = cDataBits == 5 ? PDMSERIALSTOPBITS_ONEPOINTFIVE : PDMSERIALSTOPBITS_TWO;
+        }
+
+        if (pThis->uRegLcr & UART_REG_LCR_PEN)
+        {
+            /* Select the correct parity mode based on the even and stick parity bits. */
+            switch (pThis->uRegLcr & (UART_REG_LCR_EPS | UART_REG_LCR_PAR_STICK))
+            {
+                case 0:
+                    enmParity = PDMSERIALPARITY_ODD;
+                    break;
+                case UART_REG_LCR_EPS:
+                    enmParity = PDMSERIALPARITY_EVEN;
+                    break;
+                case UART_REG_LCR_EPS | UART_REG_LCR_PAR_STICK:
+                    enmParity = PDMSERIALPARITY_SPACE;
+                    break;
+                case UART_REG_LCR_PAR_STICK:
+                    enmParity = PDMSERIALPARITY_MARK;
+                    break;
+                default:
+                    /* We should never get here as all cases where caught earlier. */
+                    AssertMsgFailed(("This shouldn't happen at all: %#x\n",
+                                     pThis->uRegLcr & (UART_REG_LCR_EPS | UART_REG_LCR_PAR_STICK)));
+            }
+        }
+
+        LogFlowFunc(("Changing parameters to: %u,%s,%u,%s\n",
+                     uBps, s_aszParity[enmParity], cDataBits, s_aszStopBits[enmStopBits]));
+
+        int rc = pThis->pDrvSerial->pfnChgParams(pThis->pDrvSerial, uBps, enmParity, cDataBits, enmStopBits);
+        if (RT_FAILURE(rc))
+            LogRelMax(10, ("Serial#%d: Failed to change parameters to %u,%s,%u,%s -> %Rrc\n",
+                           pThis->pDevInsR3->iInstance, uBps, s_aszParity[enmParity], cDataBits, s_aszStopBits[enmStopBits], rc));
+    }
+}
+
+
+/**
+ * Updates the internal device state with the given PDM status line states.
+ *
+ * @returns nothing.
+ * @param   pThis               The serial port instance.
+ * @param   fStsLines           The PDM status line states.
+ */
+static void uartR3StsLinesUpdate(PUARTCORE pThis, uint32_t fStsLines)
+{
+    uint8_t uRegMsrNew = 0; /* The new MSR value. */
+
+    if (fStsLines & PDMISERIALPORT_STS_LINE_DCD)
+        uRegMsrNew |= UART_REG_MSR_DCD;
+    if (fStsLines & PDMISERIALPORT_STS_LINE_RI)
+        uRegMsrNew |= UART_REG_MSR_RI;
+    if (fStsLines & PDMISERIALPORT_STS_LINE_DSR)
+        uRegMsrNew |= UART_REG_MSR_DSR;
+    if (fStsLines & PDMISERIALPORT_STS_LINE_CTS)
+        uRegMsrNew |= UART_REG_MSR_CTS;
+
+    uartR3MsrUpdate(pThis, uRegMsrNew);
+}
+
+
+/**
+ * Fills up the receive FIFO with as much data as possible.
+ *
+ * @returns nothing.
+ * @param   pThis               The serial port instance.
+ */
+static void uartR3RecvFifoFill(PUARTCORE pThis)
+{
+    LogFlowFunc(("pThis=%#p\n", pThis));
+
+    PUARTFIFO pFifo = &pThis->FifoRecv;
+    size_t cbFill = RT_MIN(uartFifoFreeGet(pFifo),
+                           ASMAtomicReadU32(&pThis->cbAvailRdr));
+    size_t cbFilled = 0;
+
+    while (cbFilled < cbFill)
+    {
+        size_t cbThisRead = RT_MIN(cbFill, (uint8_t)(UART_FIFO_LENGTH - pFifo->offWrite));
+        size_t cbRead = 0;
+        int rc = pThis->pDrvSerial->pfnReadRdr(pThis->pDrvSerial, &pFifo->abBuf[pFifo->offWrite], cbThisRead, &cbRead);
+        /*Assert(RT_SUCCESS(rc) && cbRead == cbThisRead);*/ RT_NOREF(rc);
+
+        pFifo->offWrite = (pFifo->offWrite + cbRead) % UART_FIFO_LENGTH;
+        pFifo->cbUsed   += cbRead;
+        cbFilled        += cbRead;
+
+        if (cbRead < cbThisRead)
+            break;
+    }
+
+    if (cbFilled)
+    {
+        UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_DR);
+        uartIrqUpdate(pThis);
+    }
+
+    ASMAtomicSubU32(&pThis->cbAvailRdr, cbFilled);
+}
+
+
+/**
+ * Fetches a single byte and writes it to RBR.
+ *
+ * @returns nothing.
+ * @param   pThis               The serial port instance.
+ */
+static void uartR3ByteFetch(PUARTCORE pThis)
+{
+    if (ASMAtomicReadU32(&pThis->cbAvailRdr))
+    {
+        AssertPtr(pThis->pDrvSerial);
+        size_t cbRead = 0;
+        int rc2 = pThis->pDrvSerial->pfnReadRdr(pThis->pDrvSerial, &pThis->uRegRbr, 1, &cbRead);
+        AssertMsg(RT_SUCCESS(rc2) && cbRead == 1, ("This shouldn't fail and always return one byte!\n"));
+        UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_DR);
+        uartIrqUpdate(pThis);
+    }
+}
+
+
+/**
+ * Fetches a ready data based on the FIFO setting.
+ *
+ * @returns nothing.
+ * @param   pThis               The serial port instance.
+ */
+static void uartR3DataFetch(PUARTCORE pThis)
+{
+    if (pThis->uRegFcr % UART_REG_FCR_FIFO_EN)
+        uartR3RecvFifoFill(pThis);
+    else
+        uartR3ByteFetch(pThis);
+}
+#endif
+
+
+/**
+ * Write handler for the THR/DLL register (depending on the DLAB bit in LCR).
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   uVal                The value to write.
+ */
+DECLINLINE(int) uartRegThrDllWrite(PUARTCORE pThis, uint8_t uVal)
+{
+    int rc = VINF_SUCCESS;
+
+    /* A set DLAB causes a write to the lower 8bits of the divisor latch. */
+    if (pThis->uRegLcr & UART_REG_LCR_DLAB)
+    {
+        if (uVal != (pThis->uRegDivisor & 0xff))
+        {
+#ifndef IN_RING3
+            rc = VINF_IOM_R3_IOPORT_WRITE;
+#else
+            pThis->uRegDivisor = (pThis->uRegDivisor & 0xff00) | uVal;
+            uartR3ParamsUpdate(pThis);
+#endif
+        }
+    }
+    else
+    {
+        if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
+        {
+#ifndef IN_RING3
+            rc = VINF_IOM_R3_IOPORT_WRITE;
+#else
+            uartFifoPut(&pThis->FifoXmit, true /*fOvrWr*/, uVal);
+            UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_THRE | UART_REG_LSR_TEMT);
+            uartIrqUpdate(pThis);
+            if (pThis->pDrvSerial)
+            {
+                int rc2 = pThis->pDrvSerial->pfnDataAvailWrNotify(pThis->pDrvSerial, 1);
+                if (RT_FAILURE(rc2))
+                    LogRelMax(10, ("Serial#%d: Failed to send data with %Rrc\n", rc2));
+            }
+#endif
+        }
+        else
+        {
+            /* Notify the lower driver about available data only if the register was empty before. */
+            if (pThis->uRegLsr & UART_REG_LSR_THRE)
+            {
+#ifndef IN_RING3
+                rc = VINF_IOM_R3_IOPORT_WRITE;
+#else
+                pThis->uRegThr = uVal;
+                UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_THRE | UART_REG_LSR_TEMT);
+                uartIrqUpdate(pThis);
+                if (pThis->pDrvSerial)
+                {
+                    int rc2 = pThis->pDrvSerial->pfnDataAvailWrNotify(pThis->pDrvSerial, 1);
+                    if (RT_FAILURE(rc2))
+                        LogRelMax(10, ("Serial#%d: Failed to send data with %Rrc\n", rc2));
+                }
+#endif
+            }
+            else
+                pThis->uRegThr = uVal;
+        }
+    }
+
+    return rc;
+}
+
+
+/**
+ * Write handler for the IER/DLM register (depending on the DLAB bit in LCR).
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   uVal                The value to write.
+ */
+DECLINLINE(int) uartRegIerDlmWrite(PUARTCORE pThis, uint8_t uVal)
+{
+    int rc = VINF_SUCCESS;
+
+    /* A set DLAB causes a write to the higher 8bits of the divisor latch. */
+    if (pThis->uRegLcr & UART_REG_LCR_DLAB)
+    {
+        if (uVal != (pThis->uRegDivisor & 0xff00) >> 8)
+        {
+#ifndef IN_RING3
+            rc = VINF_IOM_R3_IOPORT_WRITE;
+#else
+            pThis->uRegDivisor = (pThis->uRegDivisor & 0xff) | (uVal << 8);
+            uartR3ParamsUpdate(pThis);
+#endif
+        }
+    }
+    else
+    {
+        pThis->uRegIer = uVal & UART_REG_IER_MASK_WR;
+        uartIrqUpdate(pThis);
+    }
+
+    return rc;
+}
+
+
+/**
+ * Write handler for the FCR register.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   uVal                The value to write.
+ */
+DECLINLINE(int) uartRegFcrWrite(PUARTCORE pThis, uint8_t uVal)
+{
+    int rc = VINF_SUCCESS;
+
+    if (   pThis->enmType >= UARTTYPE_16550A
+        && uVal != pThis->uRegFcr)
+    {
+        /* A change in the FIFO enable bit clears both FIFOs automatically. */
+        if ((uVal ^ pThis->uRegFcr) & UART_REG_FCR_FIFO_EN)
+        {
+            uartFifoClear(&pThis->FifoXmit);
+            uartFifoClear(&pThis->FifoRecv);
+
+            /* Fill in the next data. */
+            if (ASMAtomicReadU32(&pThis->cbAvailRdr))
+            {
+#ifndef IN_RING3
+                rc = VINF_IOM_R3_IOPORT_WRITE;
+#else
+                uartR3DataFetch(pThis);
+#endif
+            }
+        }
+
+        if (rc == VINF_SUCCESS)
+        {
+            if (uVal & UART_REG_FCR_RCV_FIFO_RST)
+                uartFifoClear(&pThis->FifoRecv);
+            if (uVal & UART_REG_FCR_XMIT_FIFO_RST)
+                uartFifoClear(&pThis->FifoXmit);
+
+            if (uVal & UART_REG_FCR_FIFO_EN)
+            {
+                switch (UART_REG_FCR_RCV_LVL_IRQ_GET(uVal))
+                {
+                    case UART_REG_FCR_RCV_LVL_IRQ_1:
+                        pThis->FifoRecv.cbItl = 1;
+                        break;
+                    case UART_REG_FCR_RCV_LVL_IRQ_4:
+                        pThis->FifoRecv.cbItl = 4;
+                        break;
+                    case UART_REG_FCR_RCV_LVL_IRQ_8:
+                        pThis->FifoRecv.cbItl = 8;
+                        break;
+                    case UART_REG_FCR_RCV_LVL_IRQ_14:
+                        pThis->FifoRecv.cbItl = 14;
+                        break;
+                    default:
+                        /* Should never occur as all cases are handled earlier. */
+                        AssertMsgFailed(("Impossible to hit!\n"));
+                }
+            }
+
+            /* The FIFO reset bits are self clearing. */
+            pThis->uRegFcr = uVal & UART_REG_FCR_MASK_STICKY;
+            uartIrqUpdate(pThis);
+        }
+    }
+
+    return rc;
+}
+
+
+/**
+ * Write handler for the LCR register.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   uVal                The value to write.
+ */
+DECLINLINE(int) uartRegLcrWrite(PUARTCORE pThis, uint8_t uVal)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Any change except the DLAB bit causes a switch to R3. */
+    if ((pThis->uRegLcr & ~UART_REG_LCR_DLAB) != (uVal & ~UART_REG_LCR_DLAB))
+    {
+#ifndef IN_RING3
+        rc = VINF_IOM_R3_IOPORT_WRITE;
+#else
+        /* Check whether the BREAK bit changed before updating the LCR value. */
+        bool fBrkEn = RT_BOOL(uVal & UART_REG_LCR_BRK_SET);
+        bool fBrkChg = fBrkEn != RT_BOOL(pThis->uRegLcr & UART_REG_LCR_BRK_SET);
+        pThis->uRegLcr = uVal;
+        uartR3ParamsUpdate(pThis);
+
+        if (   fBrkChg
+            && pThis->pDrvSerial)
+            pThis->pDrvSerial->pfnChgBrk(pThis->pDrvSerial, fBrkEn);
+#endif
+    }
+    else
+        pThis->uRegLcr = uVal;
+
+    return rc;
+}
+
+
+/**
+ * Write handler for the MCR register.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   uVal                The value to write.
+ */
+DECLINLINE(int) uartRegMcrWrite(PUARTCORE pThis, uint8_t uVal)
+{
+    int rc = VINF_SUCCESS;
+
+    uVal &= UART_REG_MCR_MASK_WR;
+    if (pThis->uRegMcr != uVal)
+    {
+#ifndef IN_RING3
+        rc = VINF_IOM_R3_IOPORT_WRITE;
+#else
+        /*
+         * When loopback mode is activated the RTS, DTR, OUT1 and OUT2 lines are
+         * disconnected and looped back to MSR.
+         */
+        if (   (uVal & UART_REG_MCR_LOOP)
+            && !(pThis->uRegMcr & UART_REG_MCR_LOOP)
+            && pThis->pDrvSerial)
+            pThis->pDrvSerial->pfnChgModemLines(pThis->pDrvSerial, false /*fRts*/, false /*fDtr*/);
+
+        pThis->uRegMcr = uVal;
+        if (uVal & UART_REG_MCR_LOOP)
+        {
+            uint8_t uRegMsrSts = 0;
+
+            if (uVal & UART_REG_MCR_RTS)
+                uRegMsrSts |= UART_REG_MSR_CTS;
+            if (uVal & UART_REG_MCR_DTR)
+                uRegMsrSts |= UART_REG_MSR_DSR;
+            if (uVal & UART_REG_MCR_OUT1)
+                uRegMsrSts |= UART_REG_MSR_RI;
+            if (uVal & UART_REG_MCR_OUT2)
+                uRegMsrSts |= UART_REG_MSR_DCD;
+            uartR3MsrUpdate(pThis, uRegMsrSts);
+        }
+        else if (pThis->pDrvSerial)
+            pThis->pDrvSerial->pfnChgModemLines(pThis->pDrvSerial,
+                                                RT_BOOL(uVal & UART_REG_MCR_RTS),
+                                                RT_BOOL(uVal & UART_REG_MCR_DTR));
+#endif
+    }
+
+    return rc;
+}
+
+
+/**
+ * Read handler for the RBR/DLL register (depending on the DLAB bit in LCR).
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   puVal               Where to store the read value on success.
+ */
+DECLINLINE(int) uartRegRbrDllRead(PUARTCORE pThis, uint32_t *puVal)
+{
+    int rc = VINF_SUCCESS;
+
+    /* A set DLAB causes a read from the lower 8bits of the divisor latch. */
+    if (pThis->uRegLcr & UART_REG_LCR_DLAB)
+        *puVal = pThis->uRegDivisor & 0xff;
+    else
+    {
+        if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
+        {
+            /*
+             * Only go back to R3 if there is new data available for the FIFO
+             * and we would clear the interrupt to fill it up again.
+             */
+            if (   pThis->FifoRecv.cbUsed <= pThis->FifoRecv.cbItl
+                && ASMAtomicReadU32(&pThis->cbAvailRdr) > 0)
+            {
+#ifndef IN_RING3
+                rc = VINF_IOM_R3_IOPORT_READ;
+#else
+                uartR3RecvFifoFill(pThis);
+#endif
+            }
+
+            if (rc == VINF_SUCCESS)
+            {
+                *puVal = uartFifoGet(&pThis->FifoRecv);
+                if (!pThis->FifoRecv.cbUsed)
+                    UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_DR);
+                uartIrqUpdate(pThis);
+            }
+        }
+        else
+        {
+            *puVal = pThis->uRegRbr;
+
+            if (pThis->uRegLsr & UART_REG_LSR_DR)
+            {
+                uint32_t cbAvail = ASMAtomicDecU32(&pThis->cbAvailRdr);
+                if (!cbAvail)
+                {
+                    UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_DR);
+                    uartIrqUpdate(pThis);
+                }
+                else
+                {
+#ifndef IN_RING3
+                    /* Restore state and go back to R3. */
+                    ASMAtomicIncU32(&pThis->cbAvailRdr);
+                    rc = VINF_IOM_R3_IOPORT_READ;
+#else
+                    /* Fetch new data and keep the DR bit set. */
+                    uartR3DataFetch(pThis);
+#endif
+                }
+            }
+        }
+    }
+
+    return rc;
+}
+
+
+/**
+ * Read handler for the IER/DLM register (depending on the DLAB bit in LCR).
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   puVal               Where to store the read value on success.
+ */
+DECLINLINE(int) uartRegIerDlmRead(PUARTCORE pThis, uint32_t *puVal)
+{
+    int rc = VINF_SUCCESS;
+
+    /* A set DLAB causes a read from the upper 8bits of the divisor latch. */
+    if (pThis->uRegLcr & UART_REG_LCR_DLAB)
+        *puVal = (pThis->uRegDivisor & 0xff00) >> 8;
+    else
+        *puVal = pThis->uRegIer;
+
+    return rc;
+}
+
+
+/**
+ * Read handler for the IIR register.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   puVal               Where to store the read value on success.
+ */
+DECLINLINE(int) uartRegIirRead(PUARTCORE pThis, uint32_t *puVal)
+{
+    *puVal = pThis->uRegIir;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Read handler for the LSR register.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   puVal               Where to store the read value on success.
+ */
+DECLINLINE(int) uartRegLsrRead(PUARTCORE pThis, uint32_t *puVal)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Yield if configured and there is no data available. */
+    if (   !(pThis->uRegLsr & UART_REG_LSR_DR)
+        && (pThis->fFlags & UART_CORE_YIELD_ON_LSR_READ))
+    {
+#ifndef IN_RING3
+        return VINF_IOM_R3_IOPORT_READ;
+#else
+        RTThreadYield();
+#endif
+    }
+
+    *puVal = pThis->uRegLsr;
+    /*
+     * Reading this register clears the Overrun (OE), Parity (PE) and Framing (FE) error
+     * as well as the Break Interrupt (BI).
+     */
+    UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_BITS_IIR_RCL);
+    uartIrqUpdate(pThis);
+
+    return rc;
+}
+
+
+/**
+ * Read handler for the MSR register.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The serial port instance.
+ * @param   puVal               Where to store the read value on success.
+ */
+DECLINLINE(int) uartRegMsrRead(PUARTCORE pThis, uint32_t *puVal)
+{
+    *puVal = pThis->uRegMsr;
+
+    /* Clear any of the delta bits. */
+    UART_REG_CLR(pThis->uRegMsr, UART_REG_MSR_BITS_IIR_MS);
+    uartIrqUpdate(pThis);
+    return VINF_SUCCESS;
+}
+
+
+#ifdef LOG_ENABLED
+/**
+ * Converts the register index into a sensible memnonic.
+ *
+ * @returns Register memnonic.
+ * @param   pThis               The serial port instance.
+ * @param   idxReg              Register index.
+ * @param   fWrite              Flag whether the register gets written.
+ */
+DECLINLINE(const char *) uartRegIdx2Str(PUARTCORE pThis, uint8_t idxReg, bool fWrite)
+{
+    const char *psz = "INV";
+
+    switch (idxReg)
+    {
+        /*case UART_REG_THR_DLL_INDEX:*/
+        case UART_REG_RBR_DLL_INDEX:
+            if (pThis->uRegLcr & UART_REG_LCR_DLAB)
+                psz = "DLL";
+            else if (fWrite)
+                psz = "THR";
+            else
+                psz = "RBR";
+            break;
+        case UART_REG_IER_DLM_INDEX:
+            if (pThis->uRegLcr & UART_REG_LCR_DLAB)
+                psz = "DLM";
+            else
+                psz = "IER";
+            break;
+        /*case UART_REG_IIR_INDEX:*/
+        case UART_REG_FCR_INDEX:
+            if (fWrite)
+                psz = "FCR";
+            else
+                psz = "IIR";
+            break;
+        case UART_REG_LCR_INDEX:
+            psz = "LCR";
+            break;
+        case UART_REG_MCR_INDEX:
+            psz = "MCR";
+            break;
+        case UART_REG_LSR_INDEX:
+            psz = "LSR";
+            break;
+        case UART_REG_MSR_INDEX:
+            psz = "MSR";
+            break;
+        case UART_REG_SCR_INDEX:
+            psz = "SCR";
+            break;
+    }
+
+    return psz;
+}
+#endif
+
+
+DECLHIDDEN(int) uartRegWrite(PUARTCORE pThis, uint32_t uReg, uint32_t u32, size_t cb)
+{
+    int rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_R3_IOPORT_WRITE);
+    if (rc != VINF_SUCCESS)
+        return rc;
+
+    uint8_t idxReg = uReg & 0x7;
+    LogFlowFunc(("pThis=%#p uReg=%u{%s} u32=%#x cb=%u\n",
+                 pThis, uReg, uartRegIdx2Str(pThis, idxReg, true /*fWrite*/), u32, cb));
+
+    AssertMsgReturn(cb == 1, ("uReg=%#x cb=%d u32=%#x\n", uReg, cb, u32), VINF_SUCCESS);
+
+    uint8_t uVal = (uint8_t)u32;
+    switch (idxReg)
+    {
+        case UART_REG_THR_DLL_INDEX:
+            rc = uartRegThrDllWrite(pThis, uVal);
+            break;
+        case UART_REG_IER_DLM_INDEX:
+            rc = uartRegIerDlmWrite(pThis, uVal);
+            break;
+        case UART_REG_FCR_INDEX:
+            rc = uartRegFcrWrite(pThis, uVal);
+            break;
+        case UART_REG_LCR_INDEX:
+            rc = uartRegLcrWrite(pThis, uVal);
+            break;
+        case UART_REG_MCR_INDEX:
+            rc = uartRegMcrWrite(pThis, uVal);
+            break;
+        case UART_REG_SCR_INDEX:
+            pThis->uRegScr = u32;
+            break;
+        default:
+            break;
+    }
+
+    PDMCritSectLeave(&pThis->CritSect);
+    LogFlowFunc(("-> %Rrc\n", rc));
+    return rc;
+}
+
+
+DECLHIDDEN(int) uartRegRead(PUARTCORE pThis, uint32_t uReg, uint32_t *pu32, size_t cb)
+{
+    if (cb != 1)
+        return VERR_IOM_IOPORT_UNUSED;
+
+    int rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_R3_IOPORT_READ);
+    if (rc != VINF_SUCCESS)
+        return rc;
+
+    uint8_t idxReg = uReg & 0x7;
+    switch (idxReg)
+    {
+        case UART_REG_RBR_DLL_INDEX:
+            rc = uartRegRbrDllRead(pThis, pu32);
+            break;
+        case UART_REG_IER_DLM_INDEX:
+            rc = uartRegIerDlmRead(pThis, pu32);
+            break;
+        case UART_REG_IIR_INDEX:
+            rc = uartRegIirRead(pThis, pu32);
+            break;
+        case UART_REG_LCR_INDEX:
+            *pu32 = pThis->uRegLcr;
+            break;
+        case UART_REG_MCR_INDEX:
+            *pu32 = pThis->uRegMcr;
+            break;
+        case UART_REG_LSR_INDEX:
+            rc = uartRegLsrRead(pThis, pu32);
+            break;
+        case UART_REG_MSR_INDEX:
+            rc = uartRegMsrRead(pThis, pu32);
+            break;
+        case UART_REG_SCR_INDEX:
+            *pu32 = pThis->uRegScr;
+            break;
+        default:
+            rc = VERR_IOM_IOPORT_UNUSED;
+    }
+
+    PDMCritSectLeave(&pThis->CritSect);
+    LogFlowFunc(("pThis=%#p uReg=%u{%s} u32=%#x cb=%u -> %Rrc\n",
+                 pThis, uReg, uartRegIdx2Str(pThis, idxReg, false /*fWrite*/), *pu32, cb, rc));
+    return rc;
+}
+
+
+#ifdef IN_RING3
+
+/* -=-=-=-=-=-=-=-=- PDMISERIALPORT on LUN#0 -=-=-=-=-=-=-=-=- */
+
+
+/**
+ * @interface_method_impl{PDMISERIALPORT,pfnDataAvailRdrNotify}
+ */
+static DECLCALLBACK(int) uartR3DataAvailRdrNotify(PPDMISERIALPORT pInterface, size_t cbAvail)
+{
+    LogFlowFunc(("pInterface=%#p cbAvail=%zu\n", pInterface, cbAvail));
+    PUARTCORE pThis = RT_FROM_MEMBER(pInterface, UARTCORE, ISerialPort);
+
+    AssertMsg((uint32_t)cbAvail == cbAvail, ("Too much data available\n"));
+
+    uint32_t cbAvailOld = ASMAtomicAddU32(&pThis->cbAvailRdr, (uint32_t)cbAvail);
+    LogFlow(("    cbAvailRdr=%zu -> cbAvailRdr=%zu\n", cbAvailOld, cbAvail + cbAvailOld));
+    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
+    if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
+        uartR3RecvFifoFill(pThis);
+    else if (!cbAvailOld)
+    {
+        size_t cbRead = 0;
+        int rc = pThis->pDrvSerial->pfnReadRdr(pThis->pDrvSerial, &pThis->uRegRbr, 1, &cbRead);
+        AssertMsg(RT_SUCCESS(rc) && cbRead == 1, ("This shouldn't fail and always return one byte!\n"));
+        UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_DR);
+        uartIrqUpdate(pThis);
+    }
+    PDMCritSectLeave(&pThis->CritSect);
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMISERIALPORT,pfnDataSentNotify}
+ */
+static DECLCALLBACK(int) uartR3DataSentNotify(PPDMISERIALPORT pInterface)
+{
+    LogFlowFunc(("pInterface=%#p\n", pInterface));
+    PUARTCORE pThis = RT_FROM_MEMBER(pInterface, UARTCORE, ISerialPort);
+
+    /* Set the transmitter empty bit because everything was sent. */
+    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
+    UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_TEMT);
+    uartIrqUpdate(pThis);
+    PDMCritSectLeave(&pThis->CritSect);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMISERIALPORT,pfnReadWr}
+ */
+static DECLCALLBACK(int) uartR3ReadWr(PPDMISERIALPORT pInterface, void *pvBuf, size_t cbRead, size_t *pcbRead)
+{
+    LogFlowFunc(("pInterface=%#p pvBuf=%#p cbRead=%zu pcbRead=%#p\n", pInterface, pvBuf, cbRead, pcbRead));
+    PUARTCORE pThis = RT_FROM_MEMBER(pInterface, UARTCORE, ISerialPort);
+
+    AssertReturn(cbRead > 0, VERR_INVALID_PARAMETER);
+
+    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
+    if (pThis->uRegFcr & UART_REG_FCR_FIFO_EN)
+    {
+        *pcbRead = uartFifoCopyTo(&pThis->FifoXmit, pvBuf, cbRead);
+        if (!pThis->FifoXmit.cbUsed)
+            UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_THRE);
+        if (*pcbRead)
+            UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_TEMT);
+        uartIrqUpdate(pThis);
+    }
+    else if (!(pThis->uRegLsr & UART_REG_LSR_THRE))
+    {
+        *(uint8_t *)pvBuf = pThis->uRegThr;
+        *pcbRead = 1;
+        UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_THRE);
+        UART_REG_CLR(pThis->uRegLsr, UART_REG_LSR_TEMT);
+        uartIrqUpdate(pThis);
+    }
+    else
+    {
+        AssertMsgFailed(("There is no data to read!\n"));
+        *pcbRead = 0;
+    }
+    PDMCritSectLeave(&pThis->CritSect);
+
+    LogFlowFunc(("-> VINF_SUCCESS{*pcbRead=%zu}\n", *pcbRead));
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMISERIALPORT,pfnNotifyStsLinesChanged}
+ */
+static DECLCALLBACK(int) uartR3NotifyStsLinesChanged(PPDMISERIALPORT pInterface, uint32_t fNewStatusLines)
+{
+    LogFlowFunc(("pInterface=%#p fNewStatusLines=%#x\n", pInterface, fNewStatusLines));
+    PUARTCORE pThis = RT_FROM_MEMBER(pInterface, UARTCORE, ISerialPort);
+
+    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
+    uartR3StsLinesUpdate(pThis, fNewStatusLines);
+    PDMCritSectLeave(&pThis->CritSect);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMISERIALPORT,pfnNotifyBrk}
+ */
+static DECLCALLBACK(int) uartR3NotifyBrk(PPDMISERIALPORT pInterface)
+{
+    LogFlowFunc(("pInterface=%#p\n", pInterface));
+    PUARTCORE pThis = RT_FROM_MEMBER(pInterface, UARTCORE, ISerialPort);
+
+    PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
+    UART_REG_SET(pThis->uRegLsr, UART_REG_LSR_BI);
+    uartIrqUpdate(pThis);
+    PDMCritSectLeave(&pThis->CritSect);
+    return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=-=-=-=- PDMIBASE -=-=-=-=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) uartR3QueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PUARTCORE pThis = RT_FROM_MEMBER(pInterface, UARTCORE, IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALPORT, &pThis->ISerialPort);
+    return NULL;
+}
+
+
+DECLHIDDEN(void) uartR3Relocate(PUARTCORE pThis, RTGCINTPTR offDelta)
+{
+    RT_NOREF(offDelta);
+    pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pThis->pDevInsR3);
+}
+
+
+DECLHIDDEN(void) uartR3Reset(PUARTCORE pThis)
+{
+    pThis->uRegDivisor = 0x0c; /* Default to 9600 Baud. */
+    pThis->uRegRbr     = 0;
+    pThis->uRegThr     = 0;
+    pThis->uRegIer     = 0;
+    pThis->uRegIir     = UART_REG_IIR_IP_NO_INT;
+    pThis->uRegFcr     = 0;
+    pThis->uRegLcr     = 0; /* 5 data bits, no parity, 1 stop bit. */
+    pThis->uRegMcr     = 0;
+    pThis->uRegLsr     = UART_REG_LSR_THRE | UART_REG_LSR_TEMT;
+    pThis->uRegMsr     = 0; /* Updated below. */
+    pThis->uRegScr     = 0;
+
+    uartFifoClear(&pThis->FifoXmit);
+    uartFifoClear(&pThis->FifoRecv);
+    pThis->FifoRecv.cbItl = 1;
+
+    uartR3ParamsUpdate(pThis);
+    uartIrqUpdate(pThis);
+
+    if (pThis->pDrvSerial)
+    {
+        /* Set the modem lines to reflect the current state. */
+        int rc = pThis->pDrvSerial->pfnChgModemLines(pThis->pDrvSerial, false /*fRts*/, false /*fDtr*/);
+        if (RT_FAILURE(rc))
+            LogRel(("Serial#%d: Failed to set modem lines with %Rrc during reset\n",
+                    pThis->pDevInsR3->iInstance, rc));
+
+        uint32_t fStsLines = 0;
+        rc = pThis->pDrvSerial->pfnQueryStsLines(pThis->pDrvSerial, &fStsLines);
+        if (RT_SUCCESS(rc))
+            uartR3StsLinesUpdate(pThis, fStsLines);
+        else
+            LogRel(("Serial#%d: Failed to query status line status with %Rrc during reset\n",
+                    pThis->pDevInsR3->iInstance, rc));
+    }
+}
+
+
+DECLHIDDEN(int) uartR3Attach(PUARTCORE pThis, unsigned iLUN)
+{
+    int rc = PDMDevHlpDriverAttach(pThis->pDevInsR3, iLUN, &pThis->IBase, &pThis->pDrvBase, "Serial Char");
+    if (RT_SUCCESS(rc))
+    {
+        pThis->pDrvSerial = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMISERIALCONNECTOR);
+        if (!pThis->pDrvSerial)
+        {
+            AssertLogRelMsgFailed(("Configuration error: instance %d has no serial interface!\n", pThis->pDevInsR3->iInstance));
+            return VERR_PDM_MISSING_INTERFACE;
+        }
+    }
+    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+    {
+        pThis->pDrvBase = NULL;
+        pThis->pDrvSerial = NULL;
+        rc = VINF_SUCCESS;
+        LogRel(("Serial#%d: no unit\n", pThis->pDevInsR3->iInstance));
+    }
+    else /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
+        LogRel(("Serial#%d: Failed to attach to serial driver. rc=%Rrc\n", pThis->pDevInsR3->iInstance, rc));
+
+   return rc;
+}
+
+
+DECLHIDDEN(void) uartR3Detach(PUARTCORE pThis)
+{
+    /* Zero out important members. */
+    pThis->pDrvBase   = NULL;
+    pThis->pDrvSerial = NULL;
+}
+
+
+DECLHIDDEN(void) uartR3Destruct(PUARTCORE pThis)
+{
+    PDMR3CritSectDelete(&pThis->CritSect);
+}
+
+
+DECLHIDDEN(int) uartR3Init(PUARTCORE pThis, PPDMDEVINS pDevInsR3, UARTTYPE enmType, unsigned iLUN, uint32_t fFlags,
+                           R3PTRTYPE(PFNUARTCOREIRQREQ) pfnUartIrqReqR3, R0PTRTYPE(PFNUARTCOREIRQREQ) pfnUartIrqReqR0,
+                           RCPTRTYPE(PFNUARTCOREIRQREQ) pfnUartIrqReqRC)
+{
+    int rc = VINF_SUCCESS;
+
+    /*
+     * Initialize the instance data.
+     * (Do this early or the destructor might choke on something!)
+     */
+    pThis->pDevInsR3                            = pDevInsR3;
+    pThis->pDevInsR0                            = PDMDEVINS_2_R0PTR(pDevInsR3);
+    pThis->pDevInsRC                            = PDMDEVINS_2_RCPTR(pDevInsR3);
+    pThis->iLUN                                 = iLUN;
+    pThis->enmType                              = enmType;
+    pThis->fFlags                               = fFlags;
+    pThis->pfnUartIrqReqR3                      = pfnUartIrqReqR3;
+    pThis->pfnUartIrqReqR0                      = pfnUartIrqReqR0;
+    pThis->pfnUartIrqReqRC                      = pfnUartIrqReqRC;
+
+    /* IBase */
+    pThis->IBase.pfnQueryInterface              = uartR3QueryInterface;
+
+    /* ISerialPort */
+    pThis->ISerialPort.pfnDataAvailRdrNotify    = uartR3DataAvailRdrNotify;
+    pThis->ISerialPort.pfnDataSentNotify        = uartR3DataSentNotify;
+    pThis->ISerialPort.pfnReadWr                = uartR3ReadWr;
+    pThis->ISerialPort.pfnNotifyStsLinesChanged = uartR3NotifyStsLinesChanged;
+    pThis->ISerialPort.pfnNotifyBrk             = uartR3NotifyBrk;
+
+    rc = PDMDevHlpCritSectInit(pDevInsR3, &pThis->CritSect, RT_SRC_POS, "Serial#%d", iLUN);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Attach the char driver and get the interfaces.
+     */
+    rc = PDMDevHlpDriverAttach(pDevInsR3, iLUN, &pThis->IBase, &pThis->pDrvBase, "UART");
+    if (RT_SUCCESS(rc))
+    {
+        pThis->pDrvSerial = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMISERIALCONNECTOR);
+        if (!pThis->pDrvSerial)
+        {
+            AssertLogRelMsgFailed(("Configuration error: instance %d has no serial interface!\n", iLUN));
+            return VERR_PDM_MISSING_INTERFACE;
+        }
+    }
+    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+    {
+        pThis->pDrvBase   = NULL;
+        pThis->pDrvSerial = NULL;
+        LogRel(("Serial#%d: no unit\n", iLUN));
+    }
+    else
+    {
+        AssertLogRelMsgFailed(("Serial#%d: Failed to attach to char driver. rc=%Rrc\n", iLUN, rc));
+        /* Don't call VMSetError here as we assume that the driver already set an appropriate error */
+        return rc;
+    }
+
+    uartR3Reset(pThis);
+    return VINF_SUCCESS;
+}
+
+#endif /* IN_RING3 */
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Index: /trunk/src/VBox/Devices/Serial/UartCore.h
===================================================================
--- /trunk/src/VBox/Devices/Serial/UartCore.h	(revision 73135)
+++ /trunk/src/VBox/Devices/Serial/UartCore.h	(revision 73135)
@@ -0,0 +1,262 @@
+/* $Id$ */
+/** @file
+ * UartCore - UART  (16550A up to 16950) emulation.
+ *
+ * The documentation for this device was taken from the PC16550D spec from TI.
+ */
+
+/*
+ * Copyright (C) 2018 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 ___UartCore_h
+#define ___UartCore_h
+
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmserialifs.h>
+#include <iprt/assert.h>
+
+RT_C_DECLS_BEGIN
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+
+/** Size of a FIFO. */
+#define UART_FIFO_LENGTH                     16
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+
+/** Pointer to the UART core state. */
+typedef struct UARTCORE *PUARTCORE;
+
+
+/**
+ * UART core IRQ request callback to let the core instance raise/clear interrupt requests.
+ *
+ * @returns nothing.
+ * @param   pDevIns             The owning device instance.
+ * @param   pThis               The UART core instance.
+ * @param   iLUN                The LUN associated with the UART core.
+ * @param   iLvl                The interrupt level.
+ */
+typedef DECLCALLBACK(void) FNUARTCOREIRQREQ(PPDMDEVINS pDevIns, PUARTCORE pThis, unsigned iLUN, int iLvl);
+/** Pointer to a UART core IRQ request callback. */
+typedef FNUARTCOREIRQREQ *PFNUARTCOREIRQREQ;
+
+
+/**
+ * UART type.
+ */
+typedef enum UARTTYPE
+{
+    /** Invalid UART type. */
+    UARTTYPE_INVALID = 0,
+    /** 16450 UART type. */
+    UARTTYPE_16450,
+    /** 16550A UART type. */
+    UARTTYPE_16550A,
+    /** 32bit hack. */
+    UARTTYPE_32BIT_HACK = 0x7fffffff
+} UARTTYPE;
+
+
+/**
+ * UART FIFO.
+ */
+typedef struct UARTFIFO
+{
+    /** Current amount of bytes used. */
+    uint8_t                         cbUsed;
+    /** Next index to write to. */
+    uint8_t                         offWrite;
+    /** Next index to read from. */
+    uint8_t                         offRead;
+    /** The interrupt trigger level (only used for the receive FIFO). */
+    uint8_t                         cbItl;
+    /** The data in the FIFO. */
+    uint8_t                         abBuf[UART_FIFO_LENGTH];
+} UARTFIFO;
+/** Pointer to a FIFO. */
+typedef UARTFIFO *PUARTFIFO;
+
+
+/**
+ * UART core device.
+ *
+ * @implements  PDMIBASE
+ * @implements  PDMISERIALPORT
+ */
+typedef struct UARTCORE
+{
+    /** Access critical section. */
+    PDMCRITSECT                     CritSect;
+    /** Pointer to the device instance - R3 Ptr. */
+    PPDMDEVINSR3                    pDevInsR3;
+    /** Pointer to the device instance - R0 Ptr. */
+    PPDMDEVINSR0                    pDevInsR0;
+    /** Pointer to the device instance - RC Ptr. */
+    PPDMDEVINSRC                    pDevInsRC;
+    /** The LUN on the owning device instance for this core. */
+    uint32_t                        iLUN;
+        /** LUN\#0: The base interface. */
+    PDMIBASE                        IBase;
+    /** LUN\#0: The serial port interface. */
+    PDMISERIALPORT                  ISerialPort;
+    /** Pointer to the attached base driver. */
+    R3PTRTYPE(PPDMIBASE)            pDrvBase;
+    /** Pointer to the attached serial driver. */
+    R3PTRTYPE(PPDMISERIALCONNECTOR) pDrvSerial;
+    /** Configuration flags. */
+    uint32_t                        fFlags;
+    /** The selected UART type. */
+    UARTTYPE                        enmType;
+
+    /** R3 interrupt request callback of the owning device. */
+    R3PTRTYPE(PFNUARTCOREIRQREQ)    pfnUartIrqReqR3;
+    /** R0 interrupt request callback of the owning device. */
+    R0PTRTYPE(PFNUARTCOREIRQREQ)    pfnUartIrqReqR0;
+    /** RC interrupt request callback of the owning device. */
+    RCPTRTYPE(PFNUARTCOREIRQREQ)    pfnUartIrqReqRC;
+
+    /** The divisor register (DLAB = 1). */
+    uint16_t                        uRegDivisor;
+    /** The Receiver Buffer Register (RBR, DLAB = 0). */
+    uint8_t                         uRegRbr;
+    /** The Transmitter Holding Register (THR, DLAB = 0). */
+    uint8_t                         uRegThr;
+    /** The Interrupt Enable Register (IER, DLAB = 0). */
+    uint8_t                         uRegIer;
+    /** The Interrupt Identification Register (IIR). */
+    uint8_t                         uRegIir;
+    /** The FIFO Control Register (FCR). */
+    uint8_t                         uRegFcr;
+    /** The Line Control Register (LCR). */
+    uint8_t                         uRegLcr;
+    /** The Modem Control Register (MCR). */
+    uint8_t                         uRegMcr;
+    /** The Line Status Register (LSR). */
+    uint8_t                         uRegLsr;
+    /** The Modem Status Register (MSR). */
+    uint8_t                         uRegMsr;
+    /** The Scratch Register (SCR). */
+    uint8_t                         uRegScr;
+
+    /** The transmit FIFO. */
+    UARTFIFO                        FifoXmit;
+    /** The receive FIFO. */
+    UARTFIFO                        FifoRecv;
+
+    /** Number of bytes available for reading from the layer below. */
+    volatile uint32_t               cbAvailRdr;
+
+#if defined(IN_RC) || HC_ARCH_BITS == 32
+    uint32_t                        uAlignment;
+#endif
+} UARTCORE;
+
+AssertCompileSizeAlignment(UARTCORE, 8);
+
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+/** Flag whether to yield the CPU on an LSR read. */
+#define UART_CORE_YIELD_ON_LSR_READ      RT_BIT_32(0)
+
+/**
+ * Performs a register write to the given register offset.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The UART core instance.
+ * @param   uReg                The register offset (byte offset) to start writing to.
+ * @param   u32                 The value to write.
+ * @param   cb                  Number of bytes to write.
+ */
+DECLHIDDEN(int) uartRegWrite(PUARTCORE pThis, uint32_t uReg, uint32_t u32, size_t cb);
+
+/**
+ * Performs a register read from the given register offset.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The UART core instance.
+ * @param   uReg                The register offset (byte offset) to start reading from.
+ * @param   pu32                Where to store the read value.
+ * @param   cb                  Number of bytes to read.
+ */
+DECLHIDDEN(int) uartRegRead(PUARTCORE pThis, uint32_t uReg, uint32_t *pu32, size_t cb);
+
+# ifdef IN_RING3
+/**
+ * Initializes the given UART core instance using the provided configuration.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The UART core instance to initialize.
+ * @param   enmType             The type of UART emulated.
+ * @param   iLUN                The LUN the UART should look for attached drivers.
+ * @param   fFlags              Additional flags controlling device behavior.
+ * @param   pfnUartIrqReqR3     Pointer to the R3 interrupt request callback.
+ * @param   pfnUartIrqReqR0     Pointer to the R0 interrupt request callback.
+ * @param   pfnUartIrqReqRC     Pointer to the RC interrupt request callback.
+ */
+DECLHIDDEN(int) uartR3Init(PUARTCORE pThis, PPDMDEVINS pDevInsR3, UARTTYPE enmType, unsigned iLUN, uint32_t fFlags,
+                           R3PTRTYPE(PFNUARTCOREIRQREQ) pfnUartIrqReqR3, R0PTRTYPE(PFNUARTCOREIRQREQ) pfnUartIrqReqR0,
+                           RCPTRTYPE(PFNUARTCOREIRQREQ) pfnUartIrqReqRC);
+
+/**
+ * Destroys the given UART core instance freeing all allocated resources.
+ *
+ * @returns nothing.
+ * @param   pThis               The UART core instance.
+ */
+DECLHIDDEN(void) uartR3Destruct(PUARTCORE pThis);
+
+/**
+ * Detaches any attached driver from the given UART core instance.
+ *
+ * @returns nothing.
+ * @param   pThis               The UART core instance.
+ */
+DECLHIDDEN(void) uartR3Detach(PUARTCORE pThis);
+
+/**
+ * Attaches the given UART core instance to the drivers at the given LUN.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The UART core instance.
+ */
+DECLHIDDEN(int) uartR3Attach(PUARTCORE pThis, unsigned iLUN);
+
+/**
+ * Resets the given UART core instance.
+ *
+ * @returns nothing.
+ * @param   pThis               The UART core instance.
+ */
+DECLHIDDEN(void) uartR3Reset(PUARTCORE pThis);
+
+/**
+ * Relocates an RC pointers of the given UART core instance
+ *
+ * @returns nothing.
+ * @param   pThis               The UART core instance.
+ * @param   offDelta            The delta to relocate RC pointers with.
+ */
+DECLHIDDEN(void) uartR3Relocate(PUARTCORE pThis, RTGCINTPTR offDelta);
+
+# endif
+
+#endif
+
+RT_C_DECLS_END
+
+#endif
Index: /trunk/src/VBox/Devices/build/VBoxDD.cpp
===================================================================
--- /trunk/src/VBox/Devices/build/VBoxDD.cpp	(revision 73134)
+++ /trunk/src/VBox/Devices/build/VBoxDD.cpp	(revision 73135)
@@ -160,4 +160,9 @@
     if (RT_FAILURE(rc))
         return rc;
+#ifdef VBOX_WITH_NEW_SERIAL
+    rc = pCallbacks->pfnRegister(pCallbacks, &g_DeviceOxPcie958);
+    if (RT_FAILURE(rc))
+        return rc;
+#endif
     rc = pCallbacks->pfnRegister(pCallbacks, &g_DeviceParallelPort);
     if (RT_FAILURE(rc))
Index: /trunk/src/VBox/Devices/build/VBoxDD.h
===================================================================
--- /trunk/src/VBox/Devices/build/VBoxDD.h	(revision 73134)
+++ /trunk/src/VBox/Devices/build/VBoxDD.h	(revision 73135)
@@ -67,4 +67,7 @@
 extern const PDMDEVREG g_DeviceFloppyController;
 extern const PDMDEVREG g_DeviceSerialPort;
+#ifdef VBOX_WITH_NEW_SERIAL
+extern const PDMDEVREG g_DeviceOxPcie958;
+#endif
 extern const PDMDEVREG g_DeviceParallelPort;
 #ifdef VBOX_WITH_AHCI
Index: /trunk/src/VBox/Devices/testcase/tstDeviceStructSize.cpp
===================================================================
--- /trunk/src/VBox/Devices/testcase/tstDeviceStructSize.cpp	(revision 73134)
+++ /trunk/src/VBox/Devices/testcase/tstDeviceStructSize.cpp	(revision 73135)
@@ -114,4 +114,5 @@
 #else
 # include "../Serial/DevSerialNew.cpp"
+# include "../Serial/DevOxPcie958.cpp"
 #endif
 #ifdef VBOX_WITH_AHCI
@@ -400,5 +401,6 @@
     CHECK_MEMBER_ALIGNMENT(SerialState, CritSect, 8);
 #else
-    CHECK_MEMBER_ALIGNMENT(DEVSERIAL, CritSect, 8);
+    CHECK_MEMBER_ALIGNMENT(DEVSERIAL, UartCore, 8);
+    CHECK_MEMBER_ALIGNMENT(UARTCORE, CritSect, 8);
 #endif
 #ifdef VBOX_WITH_VMSVGA
Index: /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp
===================================================================
--- /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 73134)
+++ /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 73135)
@@ -113,4 +113,6 @@
 #else
 # include "../Serial/DevSerialNew.cpp"
+# include "../Serial/DevOxPcie958.cpp"
+# include "../Serial/UartCore.h"
 #endif
 #ifdef VBOX_WITH_AHCI
@@ -1317,34 +1319,61 @@
     GEN_CHECK_OFF(SerialState, char_transmit_time);
 #else
+    /* Serial/UartCore.cpp */
+    GEN_CHECK_SIZE(UARTCORE);
+    GEN_CHECK_OFF(UARTCORE, CritSect);
+    GEN_CHECK_OFF(UARTCORE, pDevInsR3);
+    GEN_CHECK_OFF(UARTCORE, pDevInsR0);
+    GEN_CHECK_OFF(UARTCORE, pDevInsRC);
+    GEN_CHECK_OFF(UARTCORE, iLUN);
+    GEN_CHECK_OFF(UARTCORE, IBase);
+    GEN_CHECK_OFF(UARTCORE, ISerialPort);
+    GEN_CHECK_OFF(UARTCORE, pDrvBase);
+    GEN_CHECK_OFF(UARTCORE, pDrvSerial);
+    GEN_CHECK_OFF(UARTCORE, fFlags);
+    GEN_CHECK_OFF(UARTCORE, enmType);
+    GEN_CHECK_OFF(UARTCORE, pfnUartIrqReqR3);
+    GEN_CHECK_OFF(UARTCORE, pfnUartIrqReqR0);
+    GEN_CHECK_OFF(UARTCORE, pfnUartIrqReqRC);
+    GEN_CHECK_OFF(UARTCORE, uRegDivisor);
+    GEN_CHECK_OFF(UARTCORE, uRegRbr);
+    GEN_CHECK_OFF(UARTCORE, uRegThr);
+    GEN_CHECK_OFF(UARTCORE, uRegIer);
+    GEN_CHECK_OFF(UARTCORE, uRegIir);
+    GEN_CHECK_OFF(UARTCORE, uRegFcr);
+    GEN_CHECK_OFF(UARTCORE, uRegLcr);
+    GEN_CHECK_OFF(UARTCORE, uRegMcr);
+    GEN_CHECK_OFF(UARTCORE, uRegLsr);
+    GEN_CHECK_OFF(UARTCORE, uRegMsr);
+    GEN_CHECK_OFF(UARTCORE, uRegScr);
+    GEN_CHECK_OFF(UARTCORE, FifoXmit);
+    GEN_CHECK_OFF(UARTCORE, FifoRecv);
+    GEN_CHECK_OFF(UARTCORE, cbAvailRdr);
+
     /* Serial/DevSerialNew.cpp */
     GEN_CHECK_SIZE(DEVSERIAL);
-    GEN_CHECK_OFF(DEVSERIAL, CritSect);
     GEN_CHECK_OFF(DEVSERIAL, pDevInsR3);
     GEN_CHECK_OFF(DEVSERIAL, pDevInsR0);
     GEN_CHECK_OFF(DEVSERIAL, pDevInsRC);
-    GEN_CHECK_OFF(DEVSERIAL, IBase);
-    GEN_CHECK_OFF(DEVSERIAL, ISerialPort);
-    GEN_CHECK_OFF(DEVSERIAL, pDrvBase);
-    GEN_CHECK_OFF(DEVSERIAL, pDrvSerial);
     GEN_CHECK_OFF(DEVSERIAL, fR0Enabled);
     GEN_CHECK_OFF(DEVSERIAL, fRCEnabled);
-    GEN_CHECK_OFF(DEVSERIAL, f16550AEnabled);
-    GEN_CHECK_OFF(DEVSERIAL, fYieldOnLSRRead);
     GEN_CHECK_OFF(DEVSERIAL, uIrq);
     GEN_CHECK_OFF(DEVSERIAL, PortBase);
-    GEN_CHECK_OFF(DEVSERIAL, uRegDivisor);
-    GEN_CHECK_OFF(DEVSERIAL, uRegRbr);
-    GEN_CHECK_OFF(DEVSERIAL, uRegThr);
-    GEN_CHECK_OFF(DEVSERIAL, uRegIer);
-    GEN_CHECK_OFF(DEVSERIAL, uRegIir);
-    GEN_CHECK_OFF(DEVSERIAL, uRegFcr);
-    GEN_CHECK_OFF(DEVSERIAL, uRegLcr);
-    GEN_CHECK_OFF(DEVSERIAL, uRegMcr);
-    GEN_CHECK_OFF(DEVSERIAL, uRegLsr);
-    GEN_CHECK_OFF(DEVSERIAL, uRegMsr);
-    GEN_CHECK_OFF(DEVSERIAL, uRegScr);
-    GEN_CHECK_OFF(DEVSERIAL, FifoXmit);
-    GEN_CHECK_OFF(DEVSERIAL, FifoRecv);
-    GEN_CHECK_OFF(DEVSERIAL, cbAvailRdr);
+    GEN_CHECK_OFF(DEVSERIAL, UartCore);
+
+    /* Serial/DevOxPcie958.cpp */
+    GEN_CHECK_SIZE(DEVOX958);
+    GEN_CHECK_OFF(DEVOX958, PciDev);
+    GEN_CHECK_OFF(DEVOX958, pDevInsR3);
+    GEN_CHECK_OFF(DEVOX958, pDevInsR0);
+    GEN_CHECK_OFF(DEVOX958, pDevInsRC);
+    GEN_CHECK_OFF(DEVOX958, fR0Enabled);
+    GEN_CHECK_OFF(DEVOX958, fRCEnabled);
+    GEN_CHECK_OFF(DEVOX958, u32RegIrqStsGlob);
+    GEN_CHECK_OFF(DEVOX958, u32RegIrqEnGlob);
+    GEN_CHECK_OFF(DEVOX958, u32RegIrqEnWake);
+    GEN_CHECK_OFF(DEVOX958, cUarts);
+    GEN_CHECK_OFF(DEVOX958, GCPhysMMIO);
+    GEN_CHECK_OFF(DEVOX958, aUarts);
+    GEN_CHECK_OFF(DEVOX958, aUarts[OX958_UARTS_MAX - 1]);
 #endif
 
