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

[PATCH v8 01/26] emul/vuart: introduce framework for UART emulators


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: dmukhin@xxxxxxxx
  • Date: Wed, 11 Nov 2026 11:47:35 -0800
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 205.220.161.53) 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=lUomy5f5YccVuBZxfGEiWjptgDkSNA0y/rCaFp30pvo=; b=xJ+YPSj/PUlJI4LBu24iaWHzbDfAfwMIWy2kywLCPe53/wRQIwcC9FD5irBl72pEKAF+HEa+UbHkY3VHD0zO3sGe8jNZdVdekFyrmVOuH+raKD0kfK5KtUKLHhggvjRfiXrOoQoAFPn9ZItCONpnJKXnwyPiwgrgQTSJEW+YIuDDITVao+AqH1kBVN4+j612VXBqvNC84oZi2jx+2y4cFg4xBizXKXH8WLeyUm/RkQP/h2aWY5sxnFbCeJGbOzosBTAkA4LBVZE6ojgX2kL3BvL8Q0vD+KK+eNmlROC/AgqerAYGKW+JHwjHDi5Y+32jnOwEdVMLEZxCdACB1eVICQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=yy3ZgTWcDCNoacNE7IcLdWuxqxluMaTqgaFhLqmRWvdxyfPQ2k1iTXWTJn48cENeePgpcFbDlgXqwmD2jPf1wRJ5WvmdUOHhsDYbujGlGQXXxJrCmZgdTzIUfi7xwVTfMUYMyJUcCP0ff6M18+nxMhSo5xX/M6PDLabndiQ47rxHzDkFlvWKKYLGExD65DHuECvC69KNSXODTUs8FmDRIg7gRfWb/sIAsieP/i5esyoqzxIdrKyBE9gP+9+YIlzzKr4hPC5pivvHFYNxL6250eLOsfOrkAa3K3/qnx0dVszKM4clVSBGnGXpPhkQisDEv8n4Ja6CDY+tWSj3X/AD/w==
  • 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>

Introduce a driver framework to abstract UART emulators in the hypervisor.

That allows for architecture-independent handling of virtual UARTs in
the console driver and simplifies enabling new UART emulators.

The framework is built under CONFIG_VUART_FRAMEWORK, which will be
automatically enabled once the user enables any UART emulator.

Current implementation supports maximum of one vUART of each kind per
domain.

Use new domain_has_vuart() in the console driver code to check whether
to forward console input to the domain using vUART.

Enable console forwarding over vUART for hardware domains with a vUART.
That enables console forwarding to dom0 on x86, since console can be
forwarded only to Xen, dom0 and pvshim on x86 as of now.

Note: existing vUARTs are deliberately *not* hooked to the new framework
to minimize the scope of the patch: vpl011 (i.e. SBSA) emulator and
"vuart" (i.e.  minimalistic MMIO-mapped dtuart for hwdoms on Arm) are
kept unmodified.

No functional changes for non-x86 architectures.

Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx>
---
 xen/arch/arm/xen.lds.S           |   1 +
 xen/arch/ppc/xen.lds.S           |   1 +
 xen/arch/riscv/xen.lds.S         |   1 +
 xen/arch/x86/xen.lds.S           |   1 +
 xen/common/Kconfig               |   2 +
 xen/common/Makefile              |   1 +
 xen/common/emul/Kconfig          |   6 ++
 xen/common/emul/Makefile         |   1 +
 xen/common/emul/vuart/Kconfig    |   6 ++
 xen/common/emul/vuart/Makefile   |   1 +
 xen/common/emul/vuart/ns165x50.c |   0
 xen/common/emul/vuart/vuart.c    | 173 +++++++++++++++++++++++++++++++
 xen/common/keyhandler.c          |   3 +
 xen/drivers/char/console.c       |   6 +-
 xen/include/xen/sched.h          |   5 +
 xen/include/xen/serial.h         |   5 +
 xen/include/xen/vuart.h          | 115 ++++++++++++++++++++
 xen/include/xen/xen.lds.h        |  10 ++
 18 files changed, 337 insertions(+), 1 deletion(-)
 create mode 100644 xen/common/emul/Kconfig
 create mode 100644 xen/common/emul/Makefile
 create mode 100644 xen/common/emul/vuart/Kconfig
 create mode 100644 xen/common/emul/vuart/Makefile
 create mode 100644 xen/common/emul/vuart/ns165x50.c
 create mode 100644 xen/common/emul/vuart/vuart.c
 create mode 100644 xen/include/xen/vuart.h

diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
index 613511390ff4..423b8e717539 100644
--- a/xen/arch/arm/xen.lds.S
+++ b/xen/arch/arm/xen.lds.S
@@ -58,6 +58,7 @@ SECTIONS
        *(.rodata)
        *(.rodata.*)
        VPCI_ARRAY
+       VUART_ARRAY
        *(.data.rel.ro)
        *(.data.rel.ro.*)
 
diff --git a/xen/arch/ppc/xen.lds.S b/xen/arch/ppc/xen.lds.S
index 37256c8865a9..228dbe0055c1 100644
--- a/xen/arch/ppc/xen.lds.S
+++ b/xen/arch/ppc/xen.lds.S
@@ -52,6 +52,7 @@ SECTIONS
         *(.rodata)
         *(.rodata.*)
         VPCI_ARRAY
+        VUART_ARRAY
         *(.data.rel.ro)
         *(.data.rel.ro.*)
 
diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S
index d9375a961697..ca34974f5adf 100644
--- a/xen/arch/riscv/xen.lds.S
+++ b/xen/arch/riscv/xen.lds.S
@@ -49,6 +49,7 @@ SECTIONS
         *(.rodata)
         *(.rodata.*)
         VPCI_ARRAY
+        VUART_ARRAY
         *(.data.rel.ro)
         *(.data.rel.ro.*)
 
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 8f943e11ead4..e57ab6ff74f3 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -123,6 +123,7 @@ SECTIONS
        *(.rodata)
        *(.rodata.*)
        VPCI_ARRAY
+       VUART_ARRAY
        *(.data.rel.ro)
        *(.data.rel.ro.*)
 
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 5b289e444fa5..7f102a5ab3fc 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -675,4 +675,6 @@ config PM_STATS
          Enable collection of performance management statistics to aid in
          analyzing and tuning power/performance characteristics of the system
 
+source "common/emul/Kconfig"
+
 endmenu
diff --git a/xen/common/Makefile b/xen/common/Makefile
index f69d47d18934..60f2d9087589 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
 obj-$(CONFIG_IOREQ_SERVER) += dm.o
 obj-y += domain.o
 obj-y += domid.o
+obj-y += emul/
 obj-$(CONFIG_HAS_SHARED_INFO) += event_2l.o
 obj-y += event_channel.o
 obj-$(CONFIG_EVTCHN_FIFO) += event_fifo.o
diff --git a/xen/common/emul/Kconfig b/xen/common/emul/Kconfig
new file mode 100644
index 000000000000..7c6764d1756b
--- /dev/null
+++ b/xen/common/emul/Kconfig
@@ -0,0 +1,6 @@
+menu "Domain Emulation Features"
+       visible if EXPERT
+
+source "common/emul/vuart/Kconfig"
+
+endmenu
diff --git a/xen/common/emul/Makefile b/xen/common/emul/Makefile
new file mode 100644
index 000000000000..ae0b575c3901
--- /dev/null
+++ b/xen/common/emul/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VUART_FRAMEWORK) += vuart/
diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
new file mode 100644
index 000000000000..ce1b976b7da7
--- /dev/null
+++ b/xen/common/emul/vuart/Kconfig
@@ -0,0 +1,6 @@
+config VUART_FRAMEWORK
+       bool
+
+menu "UART Emulation"
+
+endmenu
diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
new file mode 100644
index 000000000000..97f792dc6641
--- /dev/null
+++ b/xen/common/emul/vuart/Makefile
@@ -0,0 +1 @@
+obj-y += vuart.o
diff --git a/xen/common/emul/vuart/ns165x50.c b/xen/common/emul/vuart/ns165x50.c
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
new file mode 100644
index 000000000000..bd3b756fb4d6
--- /dev/null
+++ b/xen/common/emul/vuart/vuart.c
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * UART emulator framework.
+ *
+ */
+
+#include <asm/types.h>
+
+#include <xen/err.h>
+#include <xen/resource.h>
+#include <xen/sched.h>
+#include <xen/vuart.h>
+#include <xen/xvmalloc.h>
+
+#define for_each_emulator(e) \
+    for ( e = vuart_array_start; e < vuart_array_end; e++ )
+
+extern const struct vuart_emulator vuart_array_start[];
+extern const struct vuart_emulator vuart_array_end[];
+
+static const struct vuart_emulator *
+vuart_match_by_compatible(const struct domain *d, const char *compat)
+{
+    const struct vuart_emulator *emulator;
+
+    for_each_emulator(emulator)
+        if ( emulator->compatible &&
+             !strncmp(compat, emulator->compatible,
+                      strlen(emulator->compatible)) )
+            return emulator;
+
+    return NULL;
+}
+
+const static struct vuart *
+vuart_find_by_flags(const struct domain *d, unsigned int flags)
+{
+    const struct vuart *vuart = d->console->vuart;
+
+    if ( vuart && (vuart->info->flags & flags) )
+        return vuart;
+
+    return NULL;
+}
+
+struct vuart *vuart_find_by_resource(const struct domain *d,
+                                     unsigned int type,
+                                     unsigned long addr,
+                                     unsigned long size)
+{
+    struct vuart *vuart = d->console->vuart;
+
+    if ( vuart &&
+         (type & vuart->info->base_type) &&
+         addr >= vuart->info->base_addr &&
+         addr + size <= vuart->info->base_addr + vuart->info->size )
+        return vuart;
+
+    return NULL;
+}
+
+int vuart_init(struct domain *d, const struct vuart_info *info)
+{
+    const struct vuart_emulator *emulator;
+    struct vuart *vuart;
+    int rc;
+
+    if ( !info )
+        return 0; /* Nothing to do */
+
+    if ( d->console->vuart )
+        return -EBUSY;
+
+    emulator = vuart_match_by_compatible(d, info->compatible);
+    if ( !emulator )
+        return -ENODEV;
+
+    vuart = xzalloc(typeof(*vuart));
+    if ( !vuart )
+        return -ENOMEM;
+
+    vuart->info = xvzalloc(typeof(*vuart->info));
+    if ( !vuart->info )
+    {
+        rc = -ENOMEM;
+        goto err_out1;
+    }
+    memcpy(vuart->info, info, sizeof(*info));
+
+    vuart->vdev = emulator->alloc(d, vuart->info);
+    if ( IS_ERR(vuart->vdev) )
+    {
+        rc = PTR_ERR(vuart->vdev);
+        goto err_out2;
+    }
+
+    vuart->emulator = emulator;
+    vuart->owner = d;
+
+    d->console->input_allowed = true;
+    d->console->vuart = vuart;
+
+    return 0;
+
+ err_out2:
+    xvfree(vuart->info);
+ err_out1:
+    xvfree(vuart);
+
+    return rc;
+}
+
+/*
+ * Release any resources taken by UART emulators.
+ *
+ * NB: no flags are cleared, since currently exit() is called only during
+ * domain destroy.
+ */
+void vuart_deinit(struct domain *d)
+{
+    struct vuart *vuart = d->console->vuart;
+
+    if ( vuart )
+    {
+        vuart->emulator->free(vuart->vdev);
+        xvfree(vuart->info);
+    }
+
+    XVFREE(d->console->vuart);
+}
+
+/*
+ * Print emulated UART state on the console.
+ *
+ * Must be called under rcu_lock_domain().
+ */
+void vuart_dump_state(const struct domain *d)
+{
+    struct vuart *vuart = d->console->vuart;
+
+    if ( vuart && vuart->emulator->dump_state )
+        vuart->emulator->dump_state(vuart->vdev);
+}
+
+/*
+ * Put character to the first emulated UART's FIFO with the physical console
+ * forwarding enabled.
+ *
+ * Must be called under rcu_lock_domain().
+ */
+int vuart_put_rx(const struct domain *d, char c)
+{
+    const struct vuart *vuart = vuart_find_by_flags(d, VUART_CONSOLE_INPUT);
+
+    if ( vuart && vuart->emulator->put_rx )
+        return vuart->emulator->put_rx(vuart->vdev, c);
+
+    return -ENODEV;
+}
+
+bool domain_has_vuart(const struct domain *d)
+{
+    return vuart_find_by_flags(d, VUART_CONSOLE_INPUT);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c
index 7103bf20a7c9..b5012c9cb26d 100644
--- a/xen/common/keyhandler.c
+++ b/xen/common/keyhandler.c
@@ -22,6 +22,7 @@
 #include <xen/mm.h>
 #include <xen/watchdog.h>
 #include <xen/init.h>
+#include <xen/vuart.h>
 #include <asm/div64.h>
 
 static unsigned char keypress_key;
@@ -360,6 +361,8 @@ static void cf_check dump_domains(unsigned char key)
                            v->periodic_period / 1000000);
             }
         }
+
+        vuart_dump_state(d);
     }
 
     for_each_domain ( d )
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index e4df0bbc2eec..62ca32e5392a 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -33,6 +33,7 @@
 #include <xen/dom0less-build.h>
 #include <xen/sections.h>
 #include <xen/consoled.h>
+#include <xen/vuart.h>
 
 #ifdef CONFIG_X86
 #include <asm/guest.h>
@@ -639,11 +640,12 @@ static void __serial_rx(char c)
     if ( !d )
         return;
 
-    if ( is_hardware_domain(d) )
+    if ( is_hardware_domain(d) && !domain_has_vuart(d) )
     {
         /*
          * Deliver input to the hardware domain buffer, unless it is
          * already full.
+         * NB: must be the first check: hardware domain may have emulated UART.
          */
         if ( (serial_rx_prod - serial_rx_cons) != SERIAL_RX_SIZE )
             serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
@@ -654,6 +656,8 @@ static void __serial_rx(char c)
          */
         send_global_virq(VIRQ_CONSOLE);
     }
+    else if ( domain_has_vuart(d) )
+        rc = vuart_put_rx(d, c);
 #ifdef CONFIG_SBSA_VUART_CONSOLE
     else
         /* Deliver input to the emulated UART. */
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index ce1fa636db36..4fa7f62490ca 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -24,6 +24,7 @@
 #include <asm/atomic.h>
 #include <asm/current.h>
 #include <xen/vpci.h>
+#include <xen/vuart.h>
 #include <xen/wait.h>
 #include <public/xen.h>
 #include <public/domctl.h>
@@ -382,6 +383,10 @@ struct domain_console {
     unsigned int idx;
     spinlock_t lock;
     char buf[256];
+
+#ifdef CONFIG_VUART_FRAMEWORK
+    struct vuart *vuart;
+#endif
 };
 
 enum __packed domain_shutdown_state {
diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h
index 3a71da767dd7..ec481893fede 100644
--- a/xen/include/xen/serial.h
+++ b/xen/include/xen/serial.h
@@ -32,10 +32,15 @@ enum serial_port_state {
 
 struct vuart_info {
     paddr_t base_addr;          /* Base address of the UART */
+    unsigned int base_type;     /* Base I/O resource type */
     unsigned long size;         /* Size of the memory region */
     unsigned long data_off;     /* Data register offset */
     unsigned long status_off;   /* Status register offset */
     unsigned long status;       /* Ready status value */
+    unsigned long flags;        /* Emulation flags */
+    unsigned int irq;           /* Emulated IRQ # */
+    char compatible[16];        /* Compatible string */
+    char name[16];              /* User-friendly name */
 };
 
 struct serial_port {
diff --git a/xen/include/xen/vuart.h b/xen/include/xen/vuart.h
new file mode 100644
index 000000000000..b5785b3c6b8f
--- /dev/null
+++ b/xen/include/xen/vuart.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * UART emulator framework.
+ *
+ */
+
+#ifndef XEN_VUART_H
+#define XEN_VUART_H
+
+#include <xen/serial.h>
+#include <public/xen.h>
+
+struct vuart_emulator;
+
+enum {
+    VUART_CONSOLE_INPUT = BIT(0, U), /* Physical console input forwarding. */
+};
+
+/*
+ * FIXME: #ifdef is temporary to avoid clash with
+ *   arch/arm/include/asm/domain.h
+ */
+#ifdef CONFIG_VUART_FRAMEWORK
+struct vuart {
+    const struct vuart_emulator *emulator;
+    struct vuart_info *info;
+    struct domain *owner;
+    void *vdev;
+};
+#endif
+
+struct vuart_emulator {
+    /* UART compatible string. Cannot be NULL or empty. */
+    const char *compatible;
+
+    /*
+     * Allocate emulated UART state (RX/TX FIFOs, locks, initialize registers,
+     * hook I/O handlers, etc.)
+     * Cannot be NULL.
+     */
+    void *(*alloc)(struct domain *d, const struct vuart_info *info);
+
+    /*
+     * Release resources used to emulate UART state (flush RX/TX FIFOs, unhook
+     * I/O handlers, etc.).
+     * Cannot be NULL.
+     */
+    void (*free)(void *arg);
+
+    /*
+     * Print emulated UART state (e.g. emulated registers) on the diagnostic
+     * console.
+     * Can be NULL.
+     */
+    void (*dump_state)(void *arg);
+
+    /*
+     * Place character to the emulated RX FIFO.
+     * Used to forward physical console input to the guest OS.
+     * Can be NULL.
+     */
+    int (*put_rx)(void *arg, char c);
+};
+
+#define VUART_REGISTER(name, x) \
+    static const struct vuart_emulator name##_entry \
+        __used_section(".data.rel.ro.vuart") = x
+
+struct vuart *vuart_find_by_resource(const struct domain *d,
+                                     unsigned int type,
+                                     unsigned long addr,
+                                     unsigned long size);
+
+int vuart_put_rx(const struct domain *d, char c);
+
+#ifdef CONFIG_VUART_FRAMEWORK
+
+int vuart_init(struct domain *d, const struct vuart_info *info);
+void vuart_deinit(struct domain *d);
+void vuart_dump_state(const struct domain *d);
+bool domain_has_vuart(const struct domain *d);
+
+#else
+
+static inline int vuart_init(struct domain *d, const struct vuart_info *info)
+{
+    return 0;
+}
+
+static inline void vuart_deinit(struct domain *d)
+{
+}
+
+static inline void vuart_dump_state(const struct domain *d)
+{
+}
+
+static inline bool domain_has_vuart(const struct domain *d)
+{
+    return false;
+}
+
+#endif /* CONFIG_VUART_FRAMEWORK */
+
+#endif /* XEN_VUART_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
index 958f8256b0fa..5e630d371471 100644
--- a/xen/include/xen/xen.lds.h
+++ b/xen/include/xen/xen.lds.h
@@ -231,6 +231,16 @@
 #define VPCI_ARRAY
 #endif
 
+#ifdef CONFIG_VUART_FRAMEWORK
+#define VUART_ARRAY              \
+       . = ALIGN(POINTER_ALIGN); \
+       vuart_array_start = .;    \
+       *(.data.rel.ro.vuart)     \
+       vuart_array_end = .;
+#else
+#define VUART_ARRAY
+#endif
+
 #define EX_TABLE                  \
         . = ALIGN(4);             \
         __start___ex_table = .;   \
-- 
2.56.0




 


Rackspace

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