[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v8 04/26] emul/ns16x50: implement emulator stub


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: dmukhin@xxxxxxxx
  • Date: Wed, 11 Nov 2026 11:47:38 -0800
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 148.163.138.245) smtp.rcpttodomain=lists.xenproject.org smtp.mailfrom=ford.com; dmarc=pass (p=reject sp=reject pct=100) action=none header.from=ford.com; dkim=pass (signature was verified) header.d=saarlouis.ford.com; dkim=pass (signature was verified) header.d=ford.com; arc=none (0)
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=KtlXuOyEUfBB6ob7e2noKsAOwwJwUbx82BQuyJxLIlo=; b=GWLcq4XQCHduzzAigG5wvWc7rWdbS7mitXSc4XIt5399SOdHN035YelJ+CQiEfCw9h2zBJy0HP1PsazjEL7yysPi2EQlmZ4uDD10s/8fVcgvq+O0bCSCWlcts3JURWJNA74AByGRqruLRAfDpHUlBrjHTnApS98BbXiW7aJhexmrNQwAFqZ0ffObNlPDoxDpAcOZKJ666Mq4RBP0MPTabb8xqq76MIzltahYUBMBFUWQPjy0ayjDFrbFV5Zqfpdzk4HBeS750y80VtEiW1JU83IA9RxtC/xVTpU3NiT2iwhAAfuWUXxZfxVu2C3IOKgEt4PSgSZDGV4iEQJZu+WsGA==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=k3kWjYLME5sou/w46cCC8OoPcMZy8Fl7lJsp3hDcVhdEMTAN/y+20xtmJMGbi+4Vf/caHmzCSs92lmNuCBICSXvGU63Qd7wGFyoGhTum9VvgoLs7oBL3H82l2JuBr6GSUpl99tcG4cTSLSIPvfublXr7FY912v3InJtOCC2+9hbbAJCufHwFMzhJzqOHYgM22fgsw90oSBmi3xG+scU5IqXCfRTv93Mswf+jUheXbTUC5nzl2rmn6HUb2XZm4X83m1IGGn81NnYshWaS9qEVhYabEbmm4/7lqzfzg0uSRE9sDpM61Qs52e5NtQKJW6R+fExajCo9vbyjQg7eW+p6yA==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=ppford header.d=ford.com header.i="@ford.com" header.h="Cc:Content-Transfer-Encoding:Content-Type:Date:From:In-Reply-To:Message-ID:MIME-Version:References:Subject:To"; dkim=pass header.s=selector2-azureford-onmicrosoft-com header.d=azureford.onmicrosoft.com header.i="@azureford.onmicrosoft.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"; dkim=pass header.s=ppserprodsaar header.d=saarlouis.ford.com header.i="@saarlouis.ford.com" header.h="Cc:Content-Transfer-Encoding:Date:From:In-Reply-To:Message-ID:MIME-Version:References:Subject:To"; dkim=pass header.s=ppfserpocford header.d=ford.com header.i="@ford.com" header.h="Cc:Content-Transfer-Encoding:Date:From:In-Reply-To:Message-ID:MIME-Version:References:Subject:To"
  • Cc: andrew.cooper3@xxxxxxxxxx, anthony.perard@xxxxxxxxxx, jbeulich@xxxxxxxx, julien@xxxxxxx, michal.orzel@xxxxxxx, roger@xxxxxxxxxxxxxx, sstabellini@xxxxxxxxxx, xakep.amatop@xxxxxxxxx, dmukhin@xxxxxxx, Denis Mukhin <dmukhin@xxxxxxxx>
  • Delivery-date: Wed, 11 Nov 2026 19:48:38 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Pser-m365-app: SER-APP

From: Denis Mukhin <dmukhin@xxxxxxxx>

Define the placeholder for NS16550 UART emulator.

Implement alloc/free vUART hooks, add stub for I/O port handler,
initialize emulator state and define debug logging facilities.

Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx>
---
 xen/common/emul/vuart/Makefile  |   1 +
 xen/common/emul/vuart/ns16x50.c | 417 ++++++++++++++++++++++++++++++++
 2 files changed, 418 insertions(+)
 create mode 100644 xen/common/emul/vuart/ns16x50.c

diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
index 97f792dc6641..fe904f6cb65d 100644
--- a/xen/common/emul/vuart/Makefile
+++ b/xen/common/emul/vuart/Makefile
@@ -1 +1,2 @@
 obj-y += vuart.o
+obj-$(CONFIG_VUART_NS16X50) += ns16x50.o
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
new file mode 100644
index 000000000000..6e1cfecbaa55
--- /dev/null
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -0,0 +1,417 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NS16550-compatible UART Emulator.
+ *
+ * See:
+ * - Serial and UART Tutorial:
+ *     
https://download.freebsd.org/doc/en/articles/serial-uart/serial-uart_en.pdf
+ * - UART w/ 16 byte FIFO:
+ *     https://www.ti.com/lit/ds/symlink/tl16c550c.pdf
+ * - UART w/ 64 byte FIFO:
+ *     https://www.ti.com/lit/ds/symlink/tl16c750.pdf
+ * - DesignWare DW_apb_uart Databook, v4.02a:
+ *     https://iccircle.com/static/upload/img20240313113905.pdf
+ *
+ * Limitations:
+ * - Only x86;
+ * - Only Xen console as a backend, no inter-domain communication (similar to
+ *   vpl011 on Arm);
+ * - Only 8n1 emulation (8-bit data, no parity, 1 stop bit);
+ * - No baud rate emulation (reports 115200 baud to the guest OS);
+ * - No FIFO-less mode emulation;
+ * - No RX FIFO interrupt moderation (FCR) emulation;
+ * - No integration w/ VM snapshotting (HVM_REGISTER_SAVE_RESTORE() and
+ *   friends);
+ * - No ISA IRQ sharing allowed;
+ * - No MMIO-based UART emulation.
+ */
+
+#define pr_prefix               "ns16x50"
+#define pr_fmt(fmt)             pr_prefix ": " fmt
+
+#ifdef CONFIG_VUART_NS16X50_DEBUG
+#define guest_prefix            "FROM GUEST "
+#define ns16x50_log_level       2
+#else
+#define guest_prefix            ""
+#define ns16x50_log_level       0
+#endif
+
+#include <asm/x86_emulate.h>
+
+#include <xen/8250-uart.h>
+#include <xen/console.h>
+#include <xen/err.h>
+#include <xen/iocap.h>
+#include <xen/resource.h>
+#include <xen/vuart.h>
+#include <xen/xvmalloc.h>
+
+#include <public/io/console.h>
+
+#define ns16x50_log(n, lvl, vdev, fmt, args...) \
+do { \
+    if ( ns16x50_log_level >= n ) \
+        gprintk(lvl, pr_fmt("%s: " fmt), (vdev)->name, ## args); \
+} while (0)
+
+#define ns16x50_err(vdev, fmt, args...) \
+    ns16x50_log(0, XENLOG_ERR, vdev, fmt, ## args)
+#define ns16x50_warn(vdev, fmt, args...) \
+    ns16x50_log(1, XENLOG_WARNING, vdev, fmt, ## args)
+#define ns16x50_info(vdev, fmt, args...) \
+    ns16x50_log(2, XENLOG_INFO, vdev, fmt, ## args)
+#define ns16x50_debug(vdev, fmt, args...) \
+    ns16x50_log(3, XENLOG_DEBUG, vdev, fmt, ## args)
+
+/*
+ * Number of supported registers in the UART.
+ */
+#define NS16X50_REGS_NUM        (UART_SCR + 1)
+
+/*
+ * Number of emulated registers.
+ *
+ * - Emulated registers [0..NS16X50_REGS_NUM] are R/W registers for DLAB=0.
+ * - DLAB=1, R/W, DLL         = (NS16X50_REGS_NUM + 0)
+ * - DLAB=1, R/W, DLM         = (NS16X50_REGS_NUM + 1)
+ */
+#define NS16X50_EMU_REGS_NUM    (NS16X50_REGS_NUM + 2)
+
+/*
+ * Virtual ns16x50 device state.
+ */
+struct vuart_ns16x50 {
+    uint8_t regs[NS16X50_EMU_REGS_NUM]; /* Emulated registers */
+    const struct vuart_info *info;      /* UART description */
+    struct domain *owner;               /* Owner domain */
+    const char *name;                   /* Device name */
+    spinlock_t lock;                    /* Protection */
+    struct xencons_interface cons;      /* Emulated RX/TX FIFOs */
+};
+
+static inline void ns16x50_lock_init(struct vuart_ns16x50 *vdev)
+{
+    spin_lock_init(&vdev->lock);
+}
+
+static inline void ns16x50_lock(struct vuart_ns16x50 *vdev,
+                                unsigned long *flags)
+{
+    spin_lock(&vdev->lock);
+}
+
+static inline bool ns16x50_trylock(struct vuart_ns16x50 *vdev,
+                                   unsigned long *flags)
+{
+    return spin_trylock(&vdev->lock);
+}
+
+static inline void ns16x50_unlock(struct vuart_ns16x50 *vdev,
+                                  unsigned long *flags)
+{
+    spin_unlock(&vdev->lock);
+}
+
+/*
+ * Emulate 8-bit write access to ns16x50 register.
+ */
+static int ns16x50_io_write8(
+    struct vuart_ns16x50 *vdev, uint32_t reg, uint8_t *data)
+{
+    int rc = 0;
+
+    return rc;
+}
+
+/*
+ * Emulate 16-bit write access to ns16x50 register.
+ * NB: some guest OSes use outw() to access UART_DLL.
+ */
+static int ns16x50_io_write16(
+    struct vuart_ns16x50 *vdev, uint32_t reg, uint16_t *data)
+{
+    int rc = -EINVAL;
+
+    return rc;
+}
+
+/*
+ * Emulate write access to ns16x50 register.
+ */
+static int ns16x50_io_write(
+    struct vuart_ns16x50 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
+{
+    int rc;
+
+    switch ( size )
+    {
+    case 1:
+        rc = ns16x50_io_write8(vdev, reg, (uint8_t *)data);
+        break;
+
+    case 2:
+        rc = ns16x50_io_write16(vdev, reg, (uint16_t *)data);
+        break;
+
+    default:
+        rc = -EINVAL;
+        break;
+    }
+
+    return rc;
+}
+
+/*
+ * Emulate 8-bit read access to ns16x50 register.
+ */
+static int ns16x50_io_read8(
+    struct vuart_ns16x50 *vdev, uint32_t reg, uint8_t *data)
+{
+    uint8_t val = UINT8_MAX;
+    int rc = 0;
+
+    *data = val;
+
+    return rc;
+}
+
+/*
+ * Emulate 16-bit read access to ns16x50 register.
+ */
+static int ns16x50_io_read16(
+    struct vuart_ns16x50 *vdev, uint32_t reg, uint16_t *data)
+{
+    uint16_t val = UINT16_MAX;
+    int rc = -EINVAL;
+
+    *data = val;
+
+    return rc;
+}
+
+/*
+ * Emulate read access to ns16x50 register.
+ */
+static int ns16x50_io_read(
+    struct vuart_ns16x50 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
+{
+    int rc;
+
+    switch ( size )
+    {
+    case 1:
+        rc = ns16x50_io_read8(vdev, reg, (uint8_t *)data);
+        break;
+
+    case 2:
+        rc = ns16x50_io_read16(vdev, reg, (uint16_t *)data);
+        break;
+
+    default:
+        *data = UINT32_MAX;
+        rc = -EINVAL;
+        break;
+    }
+
+    return rc;
+}
+
+static void ns16x50_access(int dir, unsigned int type,
+                           unsigned long addr, unsigned int size,
+                           uint32_t *data)
+{
+    const char op = (dir == IOREQ_WRITE) ? 'W' : 'R';
+    struct domain *d = rcu_lock_current_domain();
+    struct vuart *vuart = vuart_find_by_resource(d, type, addr, size);
+    struct vuart_ns16x50 *vdev = NULL;
+    const struct domain *owner;
+    const struct vuart_info *info;
+    const char *as;
+    uint32_t reg;
+    unsigned long flags;
+    int rc;
+
+    if ( !vuart )
+    {
+        printk(XENLOG_ERR "%c @ %#"PRIx64" %d: not initialized\n",
+               op, addr, size);
+
+        ASSERT_UNREACHABLE();
+        goto out;
+    }
+
+    vdev = vuart->vdev;
+    ASSERT(vdev);
+
+    ns16x50_lock(vdev, &flags);
+
+    owner = vuart->owner;
+    ASSERT(owner);
+
+    if ( d != owner )
+    {
+        ns16x50_err(vdev, "%c @ %#"PRIx64" %d: does not match current domain 
%pd\n",
+                    op, addr, size, d);
+
+        ASSERT_UNREACHABLE();
+        goto out_unlock;
+    }
+
+    info = vuart->info;
+    ASSERT(info);
+    as = (info->base_type & IORESOURCE_IO) ? "ioport": "iomem";
+
+    reg = addr - info->base_addr;
+    if ( !IS_ALIGNED(reg, size) )
+    {
+        ns16x50_err(vdev, "%c %s %#"PRIx64" %d: unaligned access\n",
+                    op, as, addr, size);
+        goto out_unlock;
+    }
+
+    if ( reg >= NS16X50_REGS_NUM )
+    {
+        ns16x50_err(vdev, "%c %s %#"PRIx64" %d: LCR=%#02x %#02x %#x: not 
implemented\n",
+                    op, as, addr, size, vdev->regs[UART_LCR], reg, *data);
+        goto out_unlock;
+    }
+
+    if ( dir == IOREQ_WRITE )
+        rc = ns16x50_io_write(vdev, reg, size, data);
+    else
+        rc = ns16x50_io_read(vdev, reg, size, data);
+
+
+    if ( rc == 0 )
+        ns16x50_debug(vdev, "%c %s %#"PRIx64" %d: LCR=%#02x %#02x %#08x\n",
+                      op, as, addr, size, vdev->regs[UART_LCR], reg, *data);
+    else
+        ns16x50_err(vdev, "%c %s %#"PRIx64" %d: LCR=%#02x %#02x %#08x: 
unsupported access\n",
+                    op, as, addr, size, vdev->regs[UART_LCR], reg, *data);
+
+out_unlock:
+    ns16x50_unlock(vdev, &flags);
+out:
+    rcu_unlock_domain(d);
+}
+
+/*
+ * Emulate I/O access to ns16x50 register.
+ * Note, emulation always returns X86EMUL_OKAY, once I/O port trap is enabled.
+ */
+static int cf_check ns16x50_io_handle(
+    int dir, unsigned int addr, unsigned int size, uint32_t *data)
+{
+    ns16x50_access(dir, IORESOURCE_IO, addr, size, data);
+
+    return X86EMUL_OKAY;
+}
+
+static void ns16x50_reset(struct vuart_ns16x50 *vdev)
+{
+    memset(vdev->regs, 0, sizeof(vdev->regs));
+}
+
+static int ns16x50_init(void *arg)
+{
+    struct vuart_ns16x50 *vdev = arg;
+    const struct vuart_info *info;
+    struct domain *d;
+
+    ASSERT(vdev);
+
+    d = vdev->owner;
+    info = vdev->info;
+
+    if ( info->base_type & IORESOURCE_IO )
+    {
+        register_portio_handler(d, info->base_addr, info->size, 
ns16x50_io_handle);
+    }
+    else if ( info->base_type & IORESOURCE_MEM )
+    {
+        BUG_ON("unimplemented");
+    }
+    else
+        ASSERT_UNREACHABLE();
+
+    ns16x50_reset(vdev);
+
+    return 0;
+}
+
+static void cf_check ns16x50_deinit(void *arg)
+{
+    struct vuart_ns16x50 *vdev = arg;
+
+    ASSERT(vdev);
+}
+
+static void * cf_check ns16x50_alloc(struct domain *d, const struct vuart_info 
*info)
+{
+    struct vuart_ns16x50 *vdev;
+    int rc;
+
+    if ( !is_hvm_domain(d) )
+    {
+        ns16x50_err(info, "not an HVM domain\n");
+        return ERR_PTR(-ENOSYS);
+    }
+
+    if ( vuart_find_by_resource(d, info->base_type, info->base_addr,
+                                info->size) )
+    {
+        ns16x50_err(info, "already registered\n");
+        return ERR_PTR(-EBUSY);
+    }
+
+    vdev = xvzalloc(typeof(*vdev));
+    if ( !vdev )
+    {
+        ns16x50_err(info, "failed to allocate memory\n");
+        return ERR_PTR(-ENOMEM);
+    }
+
+    ns16x50_lock_init(vdev);
+
+    vdev->name = info->name;
+    vdev->owner = d;
+    vdev->info = info;
+
+    rc = ns16x50_init(vdev);
+    if ( rc )
+    {
+        xvfree(vdev);
+        return ERR_PTR(rc);
+    }
+
+    return vdev;
+}
+
+static void cf_check ns16x50_free(void *arg)
+{
+    if ( arg )
+        ns16x50_deinit(arg);
+
+    xvfree(arg);
+}
+
+#define ns16x50_emulator                \
+{                                       \
+    .compatible = "ns16550",            \
+    .alloc      = ns16x50_alloc,        \
+    .free       = ns16x50_free,         \
+    .dump_state = NULL,                 \
+    .put_rx     = NULL,                 \
+}
+
+VUART_REGISTER(ns16x50, ns16x50_emulator);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
-- 
2.56.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.