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

[XTF PATCH v1 5/6] common: add simplistic NS16550-compatible UART driver


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: dmukhin@xxxxxxxx
  • Date: Wed, 11 Nov 2026 11:51:08 -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=kPB8feFs0W+8NaBr9o27RH94qglQiHpoq+WeRob3TK4=; b=UlfMzC1ZvyZwjAhzOPNDvVsUONlVcGQQJtoS3qia4jK/00sg2Id0jkihqU9pYLxpmV8kV7E+/glzYvmNLu01w7HZG5fiJWVL93v+4VbC54NwtRQRkpnzUWQHe1p943YMmsB4K9gxklUxZQKB/DvDMrlrAbUnNmwze2WgVRz+nVnVIGXpaXxsaxp9d4d6lek4fHG1H/CIbucR07VmU2+mVLSZUwviOQOTxBmUc8USq1Uk7FKDMFwdxpQysL1iZ/09d6D1Ay2h5njTLBaZWRGcW3cY/TfuTMbjfAeCGUxITz/ilf2RrmkABB/0yI3MV4I8EXlFBguFyRs2Z63BF613iQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=v4C8fkqPTNcVbQSdZnpLAoBZanHnNS9iAUg4JWkezzbqd4UIDMdx2S6Nsnx7or89oWlq444UtgOt2BWeRExsgRh2nduXZaLi47LxN0jOIPetbOUySz0r9APYbjyZQ/ZGsHpQJy/Z4zoseWwmW6sB30iAiG486IpktQQRVGAUFodv2MXwiMBWWXKWg8dNRAiK1WtJEgNCVrpQDaF9myrLWKdzyWWseDs5VFbi5p7c5/mN5zXB6fCarEyQWgpXomBsq/IwpF7Bni3zW88KzKzlUaV8qUJs3lmKYFoWcPya70HK+h0dgea4Zq9IRKd/IhlWhmtA55yI5lns3UaLJRTHPg==
  • 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, dmukhin@xxxxxxx, Denis Mukhin <dmukhin@xxxxxxxx>
  • Delivery-date: Wed, 11 Nov 2026 19:51:48 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Pser-m365-app: SER-APP

From: Denis Mukhin <dmukhin@xxxxxxxx>

Add a miniature NS16550-compatible driver for hypervisor validation.

Implement polling mode and putchar only.

Place the code in a common place since NS16550 hardware is not limited to
x86 systems (although the current driver uses few x86-ishms like I/O ports).

Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx>
---
 build/files.mk        |   1 +
 common/console.c      |   3 +-
 common/ns16550.c      | 153 ++++++++++++++++++++++++++++++++++++++++++
 include/xtf/console.h |  15 +++--
 include/xtf/ns16550.h | 117 ++++++++++++++++++++++++++++++++
 5 files changed, 284 insertions(+), 5 deletions(-)
 create mode 100644 common/ns16550.c
 create mode 100644 include/xtf/ns16550.h

diff --git a/build/files.mk b/build/files.mk
index 11624a89a21c..841ea9ebc70a 100644
--- a/build/files.mk
+++ b/build/files.mk
@@ -34,6 +34,7 @@ obj-hvm += $(ROOT)/arch/x86/hvm/head.o
 obj-hvm += $(ROOT)/arch/x86/hvm/pagetables.o
 obj-hvm += $(ROOT)/arch/x86/hvm/traps.o
 obj-hvm += $(ROOT)/arch/x86/io-apic.o
+obj-hvm += $(ROOT)/common/ns16550.o
 
 # Arguably common objects, but PV guests will have no interest in them.
 obj-hvm += $(ROOT)/arch/x86/vmx.o
diff --git a/common/console.c b/common/console.c
index b3e89473a7b4..02ff90c5428d 100644
--- a/common/console.c
+++ b/common/console.c
@@ -13,8 +13,9 @@
  * - Xen hypervisor console
  * - PV console
  * - Qemu debug console
+ * - NS16550-compatible emulator
  */
-static cons_output_cb output_fns[3];
+static cons_output_cb output_fns[4];
 static unsigned int nr_cons_cb;
 
 /* Guest PV console details. */
diff --git a/common/ns16550.c b/common/ns16550.c
new file mode 100644
index 000000000000..fa2bae5ebf98
--- /dev/null
+++ b/common/ns16550.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/**
+ * Simple NS16550-compatible device driver.
+ */
+
+#include <arch/config.h>
+#include <arch/traps.h>
+
+#include <xtf/console.h>
+#include <xtf/ns16550.h>
+#include <xtf/types.h>
+
+static const struct ns16550 *ns16550;
+
+static inline unsigned long
+ns16550_reg(const struct ns16550 *uart, unsigned int reg)
+{
+    return uart->base + (reg << uart->reg_shift);
+}
+
+uint8_t ns16550_inb(const struct ns16550 *uart, unsigned int reg)
+{
+    unsigned long addr = ns16550_reg(uart, reg);
+
+    return inb(addr);
+}
+
+void ns16550_outb(const struct ns16550 *uart, unsigned int reg,
+                  unsigned char val)
+{
+    unsigned long addr = ns16550_reg(uart, reg);
+
+    outb(val, addr);
+}
+
+void ns16550_wait_tx(const struct ns16550 *uart)
+{
+    while ( !(ns16550_inb(uart, UART_LSR) & UART_LSR_THRE) )
+        cpu_relax();
+}
+
+void ns16550_drain_rx(const struct ns16550 *uart)
+{
+    unsigned int i;
+
+    for ( i = 0; i < 128 && (ns16550_inb(uart, UART_LSR) & UART_LSR_DR); i++ )
+        (void)ns16550_inb(uart, UART_RBR);
+}
+
+void ns16550_putc(const struct ns16550 *uart, char c)
+{
+    ns16550_wait_tx(uart);
+
+    /* Inject carriage return. */
+    if ( c == '\n' )
+    {
+        ns16550_outb(uart, UART_THR, '\r');
+        ns16550_wait_tx(uart);
+    }
+
+    ns16550_outb(uart, UART_THR, c);
+}
+
+static void ns16550_write(const struct ns16550 *uart, const char *buf,
+                          size_t len)
+{
+    size_t i;
+
+    for ( i = 0; i < len; i++ )
+        ns16550_putc(uart, buf[i]);
+}
+
+void ns16550_puts(const struct ns16550 *uart, const char *buf)
+{
+    ns16550_write(uart, buf, strlen(buf));
+}
+
+int ns16550_init(const struct ns16550 *uart)
+{
+    unsigned int divisor = uart->clk_freq / (16 * uart->baud);
+
+    if ( !divisor )
+        divisor = 1;
+
+    /* Disable interrupts. */
+    ns16550_outb(uart, UART_IER, 0x00);
+
+    /* Set baud rate. */
+    ns16550_outb(uart, UART_LCR, UART_LCR_DLAB);
+    ns16550_outb(uart, UART_DLL, divisor & 0xff);
+    ns16550_outb(uart, UART_DLM, (divisor >> 8) & 0xff);
+
+    /* Support only 8 data bits, no parity, 1 stop bit. */
+    ns16550_outb(uart, UART_LCR, UART_LCR_8N1); /* DLAB=0 */
+
+    /* Reset FIFOs. */
+    ns16550_outb(uart, UART_FCR,
+                 UART_FCR_ENABLE | UART_FCR_CLRX |
+                 UART_FCR_CLTX   | UART_FCR_TRG14);
+
+    /* Assert modem control lines. */
+    ns16550_outb(uart, UART_MCR,
+                 UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
+
+    /* Drain any pending status/data to avoid stale state. */
+    (void)ns16550_inb(uart, UART_LSR);
+    (void)ns16550_inb(uart, UART_RBR);
+    (void)ns16550_inb(uart, UART_IIR);
+    (void)ns16550_inb(uart, UART_MSR);
+
+    return 0;
+}
+
+/*
+ * Console driver.
+ */
+
+static void ns16550_console_write(const char *buf, size_t len)
+{
+    if (ns16550)
+        ns16550_write(ns16550, buf, len);
+}
+
+void ns16550_console_puts(const char *buf)
+{
+    ns16550_console_write(buf, strlen(buf));
+}
+
+int ns16550_console_register(const struct ns16550 *uart)
+{
+    if ( ns16550_init(uart) )
+        return -1;
+
+    register_console_callback(ns16550_console_write);
+    ns16550 = uart;
+
+    return 0;
+}
+
+void ns16550_console_unregister(void)
+{
+    ns16550 = NULL;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xtf/console.h b/include/xtf/console.h
index 16a6a23e3f56..4830e511b080 100644
--- a/include/xtf/console.h
+++ b/include/xtf/console.h
@@ -3,6 +3,7 @@
 
 #include <xtf/libc.h>
 #include <xtf/compiler.h>
+#include <xtf/ns16550.h>
 
 #include <xen/event_channel.h>
 #include <xen/io/console.h>
@@ -16,17 +17,23 @@ typedef void (*cons_output_cb)(const char *buf, size_t len);
  */
 void register_console_callback(cons_output_cb cb);
 
+void vprintk(const char *fmt, va_list args) __printf(1, 0);
+void printk(const char *fmt, ...) __printf(1, 2);
+
 /*
  * Initialise the PV console.  Will register a callback.
  */
 void init_pv_console(xencons_interface_t *ring,
                      evtchn_port_t port);
-
-void vprintk(const char *fmt, va_list args) __printf(1, 0);
-void printk(const char *fmt, ...) __printf(1, 2);
-
 size_t pv_console_read_some(char *buf, size_t len);
 
+/*
+ * NS16550-backed console.
+ */
+int ns16550_console_register(const struct ns16550 *uart);
+void ns16550_console_unregister(void);
+void ns16550_console_puts(const char *buf);
+
 #endif /* XTF_CONSOLE_H */
 
 /*
diff --git a/include/xtf/ns16550.h b/include/xtf/ns16550.h
new file mode 100644
index 000000000000..5db515b7fb91
--- /dev/null
+++ b/include/xtf/ns16550.h
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/**
+ * Simple console driver for NS16550-compatible UART.
+ */
+
+#ifndef XTF_NS16550_H
+#define XTF_NS16550_H
+
+#define UART_RBR            0x00
+#define UART_THR            0x00
+#define UART_DLL            0x00
+#define UART_IER            0x01
+#define   UART_IER_ERDAI    0x01
+#define   UART_IER_ETHREI   0x02
+#define   UART_IER_ELSI     0x04
+#define   UART_IER_EMSI     0x08
+#define   UART_IER_MASK     0x0f
+#define   UART_IER_RSRVD    0xf0
+#define UART_DLM            0x01
+#define UART_IIR            0x02
+#define   UART_IIR_NOINT    0x01
+#define   UART_IIR_IMA      0x06
+#define   UART_IIR_LSI      0x06
+#define   UART_IIR_RDA      0x04
+#define   UART_IIR_THR      0x02
+#define   UART_IIR_MSI      0x00
+#define   UART_IIR_BSY      0x07
+#define   UART_IIR_FE       0xc0
+#define   UART_IIR_MASK     0x0f
+#define UART_FCR            0x02
+#define   UART_FCR_ENABLE   0x01
+#define   UART_FCR_CLRX     0x02
+#define   UART_FCR_CLTX     0x04
+#define   UART_FCR_DMA      0x08
+#define   UART_FCR_TRG1     0x00
+#define   UART_FCR_TRG4     0x40
+#define   UART_FCR_TRG8     0x80
+#define   UART_FCR_TRG14    0xc0
+#define UART_LCR            0x03
+#define   UART_LCR_WLEN5    0x00
+#define   UART_LCR_WLEN6    0x01
+#define   UART_LCR_WLEN7    0x02
+#define   UART_LCR_WLEN8    0x03
+#define   UART_LCR_STB      0x04
+#define   UART_LCR_PE       0x08
+#define   UART_LCR_EP       0x10
+#define   UART_LCR_SP       0x20
+#define   UART_LCR_SB       0x40
+#define   UART_LCR_DLAB     0x80
+#define   UART_LCR_7N2      (UART_LCR_WLEN7 | UART_LCR_STB)
+#define   UART_LCR_8N1      (UART_LCR_WLEN8)
+#define   UART_LSR_ERRORS   (UART_LSR_OE | UART_LSR_PE | \
+                             UART_LSR_FE | UART_LSR_BI)
+#define UART_MCR            0x04
+#define   UART_MCR_DTR      0x01
+#define   UART_MCR_RTS      0x02
+#define   UART_MCR_OUT1     0x04
+#define   UART_MCR_OUT2     0x08
+#define   UART_MCR_LOOP     0x10
+#define   UART_MCR_RSRVD0   0x20
+#define   UART_MCR_TCRTLR   0x40
+#define   UART_MCR_RSRVD1   0x80
+#define   UART_MCR_MASK     (UART_MCR_DTR | UART_MCR_RTS | \
+                             UART_MCR_OUT1 | UART_MCR_OUT2 | \
+                             UART_MCR_LOOP)
+#define UART_LSR            0x05
+#define   UART_LSR_DR       0x01
+#define   UART_LSR_OE       0x02
+#define   UART_LSR_PE       0x04
+#define   UART_LSR_FE       0x08
+#define   UART_LSR_BI       0x10
+#define   UART_LSR_THRE     0x20
+#define   UART_LSR_TEMT     0x40
+#define   UART_LSR_ERR      0x80
+#define UART_MSR            0x06
+#define   UART_MSR_DCTS     0x01
+#define   UART_MSR_DDSR     0x02
+#define   UART_MSR_TERI     0x04
+#define   UART_MSR_DDCD     0x08
+#define   UART_MSR_CTS      0x10
+#define   UART_MSR_DSR      0x20
+#define   UART_MSR_RI       0x40
+#define   UART_MSR_DCD      0x80
+#define UART_SCR            0x07
+
+struct ns16550 {
+    const char *name;
+    unsigned long base;
+    unsigned int reg_shift;
+    unsigned int reg_width;
+    unsigned int clk_freq;
+    unsigned int baud;
+};
+
+uint8_t ns16550_inb(const struct ns16550 *uart, unsigned int reg);
+void ns16550_outb(const struct ns16550 *uart, unsigned int reg,
+                  unsigned char val);
+
+void ns16550_wait_tx(const struct ns16550 *uart);
+void ns16550_drain_rx(const struct ns16550 *uart);
+
+void ns16550_putc(const struct ns16550 *uart, char c);
+void ns16550_puts(const struct ns16550 *uart, const char *buf);
+
+int ns16550_init(const struct ns16550 *uart);
+
+#endif /* XTF_NS16550_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 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®.