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

[XTF PATCH v1 6/6] x86: add NS16550-compatible emulator test


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: dmukhin@xxxxxxxx
  • Date: Wed, 11 Nov 2026 11:51:09 -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=RupmFEZXLXA0Lt5h6ULLMTC0jzaklR3AE+Zv1tOgKrQ=; b=A719e2/PADNdHg6ohJn0IoQKHNbo8UU9CHWFhvKzElAxC9MTg8i8gV3Mf6fkAYiJc4vI0axr+UxNm8cRyfGoTB+Kd1NvQKsBwuFExMsXtpO8ps7yXkTb0b/K9/40WJ8/EzIArzZa9f5tFEX9Fp0WHR5O7Bhcp8J8MSWSS6/EqzHar6BSpXosyCw+HZLWezlirALBEJi0mga+bN+7TteU3+58ZdnnY1+L2C5h3hkNM6lyoYbyEEMASQwKFLX46T0Zo0NX+a6f7HDa09gKoHZP1WKtJC40zZtxfp5zvIO9eA/gZxQ+kqbxm/S1ZA/dFUfSEpP4ix/7EqpptmuR2gbRFw==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=eJfNkKVmThIigQLsdl3FLZIPq8g+SL0hPk5fid2CjHWOLTMW1jXqtOSVdX32YZ/bqVkm5RqMHoZRKp7st1jBAYKhRnYNW1H/iDuWuzBkCYw4Xua7CFqiVfepL3IlGrPM8Vjjo+ceHOz/yaoY1nng946JoLpT8X9gJj2eZvgvjMspztIvx6uh6mJSvhEtbGufR3hAvEJ00sejAWArxPbFXFgjkykW6Yd5xOj3uQX1vSROuxh3c8YH3MNHNJXPoNAUKdcSfJRNyZXbFi2mryyWDjDkDO4T4gblbyyPoFaIyfAxsE+aLy7q7r4OJOrPPxZ0Li/ewU2rfb9a8fsAAUJ/sg==
  • 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:52 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Pser-m365-app: SER-APP

From: Denis Mukhin <dmukhin@xxxxxxxx>

Add initial test suite for NS16550-compatible emulator.

Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx>
---
 tests/emul-ns16x50/.gitignore |   3 +
 tests/emul-ns16x50/Makefile   |   9 +
 tests/emul-ns16x50/main.c     | 644 ++++++++++++++++++++++++++++++++++
 tests/emul-ns16x50/patterns.c |  65 ++++
 4 files changed, 721 insertions(+)
 create mode 100644 tests/emul-ns16x50/.gitignore
 create mode 100644 tests/emul-ns16x50/Makefile
 create mode 100644 tests/emul-ns16x50/main.c
 create mode 100644 tests/emul-ns16x50/patterns.c

diff --git a/tests/emul-ns16x50/.gitignore b/tests/emul-ns16x50/.gitignore
new file mode 100644
index 000000000000..28fbd61de7b5
--- /dev/null
+++ b/tests/emul-ns16x50/.gitignore
@@ -0,0 +1,3 @@
+*.swo
+*.swp
+
diff --git a/tests/emul-ns16x50/Makefile b/tests/emul-ns16x50/Makefile
new file mode 100644
index 000000000000..11ddfc82fb74
--- /dev/null
+++ b/tests/emul-ns16x50/Makefile
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := emul-ns16x50
+CATEGORY  := functional
+TEST-ENVS := hvm64
+
+obj-perenv += main.o patterns.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/emul-ns16x50/main.c b/tests/emul-ns16x50/main.c
new file mode 100644
index 000000000000..39a566d2b1d5
--- /dev/null
+++ b/tests/emul-ns16x50/main.c
@@ -0,0 +1,644 @@
+/**
+ * Tests for NS16550-compatible emulator.
+ */
+
+#include <xtf.h>
+#include <xtf/ns16550.h>
+
+const char test_title[] = "NS16550-compatible emulator tests";
+
+extern const char *pattern_4096;
+
+#define COM_PORT(n,b) \
+{ \
+    .name = n, \
+    .base = b, \
+    .reg_shift = 0, \
+    .reg_width = 1, \
+    .clk_freq = 1843200U, \
+    .baud = 115200U, \
+}
+
+/*
+ * PC COM ports.
+ */
+static const struct ns16550 com_ports[] = {
+    COM_PORT("com1", 0x3f8U),
+    COM_PORT("com2", 0x2f8U),
+    COM_PORT("com3", 0x3e8U),
+    COM_PORT("com4", 0x2e8U),
+};
+
+static const struct ns16550 *uart;
+
+static int ns16550_parse_cmdline(const char *s)
+{
+    const char *console = "console=";
+    size_t console_len = strlen(console);
+    unsigned int idx;
+
+    if (!s)
+        return -1;
+
+    /* console=com2 */
+    while ( *s && *s == ' ' )
+        s++;
+
+    if ( strncmp(s, console, console_len) )
+        return -1;
+
+    s += console_len;
+
+    for ( idx = 0; idx < ARRAY_SIZE(com_ports); idx++ )
+        if ( !strncmp(s, com_ports[idx].name, strlen(com_ports[idx].name)) )
+            break;
+
+    if ( idx == ARRAY_SIZE(com_ports) )
+        return -1;
+
+    return idx;
+}
+
+void test_setup(void)
+{
+    const char *cmdline;
+    int idx;
+
+    if ( !IS_DEFINED(CONFIG_HVM) )
+        return xtf_skip("N/A\n");
+
+    cmdline = (const char *)(uintptr_t)pvh_start_info->cmdline_paddr;
+    idx = ns16550_parse_cmdline(cmdline);
+    if ( idx < 0 )
+        return xtf_failure("not configured\n");
+
+    uart = &com_ports[idx];
+    printk("Testing with %s (0x%lx)...\n", uart->name, uart->base);
+
+    if ( ns16550_init(uart) )
+        return xtf_failure("failed to initialize\n");
+}
+
+static void test_thr(const char *banner)
+{
+    uint8_t iir;
+
+    ns16550_outb(uart, UART_IER, UART_IER_ETHREI);
+
+    ns16550_puts(uart, pattern_4096);
+
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( (iir & (UART_IIR_NOINT | UART_IIR_THR)) == UART_IIR_NOINT )
+        return xtf_failure("%s: expect UART_IIR_THR\n", banner);
+
+    ns16550_outb(uart, UART_IER, 0);
+}
+
+static void test_scr(const char *banner)
+{
+    unsigned int in, out;
+
+    /* Enable all interrupt reasons */
+    ns16550_outb(uart, UART_IER,
+                 UART_IER_ERDAI | UART_IER_ETHREI |
+                 UART_IER_ELSI  | UART_IER_EMSI);
+
+    for ( in = 0; in < 256; in++ )
+    {
+        ns16550_outb(uart, UART_SCR, in);
+        out = ns16550_inb(uart, UART_SCR);
+        if ( in != out )
+            return xtf_failure("%s: wrote 0x%02x, read 0x%02x\n",
+                               banner, in, out);
+    }
+
+    ns16550_outb(uart, UART_IER, 0);
+}
+
+static void test_lcr(const char *banner)
+{
+    static const uint8_t patterns[] = {
+        0,
+        UART_LCR_WLEN8,
+        UART_LCR_WLEN8 | UART_LCR_STB,
+        UART_LCR_WLEN7 | UART_LCR_PE | UART_LCR_EP,
+        UART_LCR_WLEN8 | UART_LCR_SB,
+        UART_LCR_WLEN8 | UART_LCR_DLAB,
+        0xff,
+    };
+    unsigned int i;
+
+    for ( i = 0; i < ARRAY_SIZE(patterns); i++ )
+    {
+        uint8_t r;
+
+        ns16550_outb(uart, UART_LCR, patterns[i]);
+        r = ns16550_inb(uart, UART_LCR);
+        if ( r != patterns[i] )
+            xtf_failure("%s: wrote %#x, read %#x\n",
+                        banner, patterns[i], r);
+    }
+
+    ns16550_outb(uart, UART_LCR, UART_LCR_8N1);
+}
+
+static void test_ier(const char *banner)
+{
+    uint8_t in, out;
+
+    /* Test valid permutations. */
+    for ( out = 0; out <= UART_IER_MASK; out++ )
+    {
+        ns16550_outb(uart, UART_IER, out);
+        in = ns16550_inb(uart, UART_IER);
+        if ( in != out )
+            xtf_failure("%s: wrote %#x, read %#x\n",
+                        banner, out, in);
+    }
+
+    /* Ensure reserved bits are not touched. */
+    out = 0xff;
+    ns16550_outb(uart, UART_IER, out);
+    in = ns16550_inb(uart, UART_IER);
+    if ( in != UART_IER_MASK )
+        xtf_failure("%s: wrote %x, read %#x (expected %x)\n",
+                    banner, out, in, UART_IER_MASK);
+}
+
+static void test_divisor_latch(const char *banner)
+{
+    /*
+     * divisor = 115200 / baud
+     * divisor 0 is invalid
+     *
+     * DLL = divisor & 0xff
+     * DLM = divisor >> 8
+     */
+    static const struct {
+        uint8_t dll;
+        uint8_t dlm;
+    } baud_rates[] = {
+        { 0x00, 0x00 },     /* unsupported */
+        { 0xff, 0xff },     /* unsupported */
+        { 0x00, 0x09 },     /* 50 baud */
+        { 0x00, 0x06 },     /* 75 baud */
+        { 0x17, 0x04 },     /* 110 baud (approx) */
+        { 0x59, 0x03 },     /* 134.5 baud (approx) */
+        { 0x00, 0x03 },     /* 150 baud */
+        { 0x80, 0x01 },     /* 300 baud */
+        { 0xc0, 0x00 },     /* 600 baud */
+        { 0x60, 0x00 },     /* 1200 baud */
+        { 0x40, 0x00 },     /* 1800 baud */
+        { 0x3a, 0x00 },     /* 2000 baud (approx) */
+        { 0x30, 0x00 },     /* 2400 baud */
+        { 0x20, 0x00 },     /* 3600 baud */
+        { 0x18, 0x00 },     /* 4800 baud */
+        { 0x10, 0x00 },     /* 7200 baud */
+        { 0x0c, 0x00 },     /* 9600 baud */
+        { 0x08, 0x00 },     /* 14400 baud */
+        { 0x06, 0x00 },     /* 19200 baud */
+        { 0x04, 0x00 },     /* 28800 baud */
+        { 0x03, 0x00 },     /* 38400 baud */
+        { 0x02, 0x00 },     /* 57600 baud */
+        { 0x01, 0x00 },     /* 115200 baud */
+    };
+    unsigned i, n;
+
+    /* DLAB=0 */
+    ns16550_outb(uart, UART_LCR, UART_LCR_8N1);
+    /* Plant IER - that will be asserted later */
+    ns16550_outb(uart, UART_IER, UART_IER_MASK);
+
+    for ( i = 0; i < ARRAY_SIZE(baud_rates); i++ )
+    {
+        uint8_t dll, dlm, r;
+
+        /* DLAB=1 */
+        ns16550_outb(uart, UART_LCR, UART_LCR_DLAB);
+
+        /* Pre-poison to catch any cross-write. */
+        ns16550_outb(uart, UART_DLL, ~baud_rates[i].dll);
+        ns16550_outb(uart, UART_DLM, ~baud_rates[i].dlm);
+
+        ns16550_outb(uart, UART_DLL, baud_rates[i].dll);
+        ns16550_outb(uart, UART_DLM, baud_rates[i].dlm);
+
+        dll = ns16550_inb(uart, UART_DLL);
+        dlm = ns16550_inb(uart, UART_DLM);
+
+        if ( dll != baud_rates[i].dll || dlm != baud_rates[i].dlm )
+            xtf_failure("%s: 1st attempt failed: wrote {%#x,%#x}, read 
{%#x,%#x}\n",
+                        banner,
+                        baud_rates[i].dll, baud_rates[i].dlm,
+                        dll, dlm);
+
+        /* Flip DLAB, plant THR, assert on planted IER */
+        for ( n = 0; n < 8; n++ )
+        {
+            ns16550_outb(uart, UART_LCR, UART_LCR_8N1);
+
+            /* THR offset is the same as DLL */
+            ns16550_outb(uart, UART_THR, '\0');
+
+            /* IER offset is the same as DLM */
+            r = ns16550_inb(uart, UART_IER);
+            if ( r != UART_IER_MASK )
+                xtf_failure("%s: IER not survided DLAB flip %d: read %#x 
expect %#x\n",
+                            banner, n, r, UART_IER_MASK);
+
+            ns16550_outb(uart, UART_LCR, UART_LCR_DLAB);
+        }
+
+        /* Check divisor latch survives */
+        dll = ns16550_inb(uart, UART_DLL);
+        dlm = ns16550_inb(uart, UART_DLM);
+
+        if ( dll != baud_rates[i].dll || dlm != baud_rates[i].dlm )
+            xtf_failure("%s: 2nd attempt failed: wrote {%#x,%#x}, read 
{%#x,%#x}\n",
+                        banner,
+                        baud_rates[i].dll, baud_rates[i].dlm,
+                        dll, dlm);
+
+        /* DLAB=0 */
+        ns16550_outb(uart, UART_LCR, UART_LCR_8N1);
+    }
+}
+
+static void test_mcr(const char *banner)
+{
+    uint8_t in, out;
+
+    /*
+     * Test combinations of DTR/RTS/OUT1/OUT2 except LOOP, which
+     * is exercised by a separate test.
+     */
+    for ( out = 0; out <= (UART_MCR_DTR | UART_MCR_RTS |
+                           UART_MCR_OUT1 | UART_MCR_OUT2); out++ )
+    {
+        ns16550_outb(uart, UART_MCR, out);
+        in = ns16550_inb(uart, UART_MCR);
+        if ( in != out )
+            xtf_failure("%s: wrote %#x, read %#x\n", banner, in, out);
+    }
+
+    /*
+     * Reserved bits must read as zero.
+     */
+    ns16550_outb(uart, UART_MCR, 0xff);
+    in = ns16550_inb(uart, UART_MCR);
+    if ( in & ~UART_MCR_MASK )
+        xtf_failure("%s: reserved bits readable: %#x\n", banner, in);
+
+    ns16550_outb(uart, UART_MCR, 0);
+}
+
+static void test_fcr_toggle(const char *banner)
+{
+    uint8_t iir;
+
+    ns16550_outb(uart, UART_FCR, 0);
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( iir & UART_IIR_FE )
+        xtf_failure("%s: IIR FIFO bits set with UART_FCR_ENABLE clear: %#x\n",
+                    banner, iir);
+
+    ns16550_outb(uart, UART_FCR, UART_FCR_ENABLE);
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( (iir & UART_IIR_FE) != UART_IIR_FE )
+        xtf_failure("%s: IIR FIFO bits not 0b11 with UART_FCR_ENABLE set: 
%#x\n",
+                    banner, iir);
+
+    ns16550_outb(uart, UART_FCR, 0);
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( iir & UART_IIR_FE )
+        xtf_failure("%s: IIR FIFO bits stuck after UART_FCR_ENABLE clear: 
%#x\n",
+                    banner, iir);
+}
+
+static void test_fcr_trigger(const char *banner)
+{
+    static const uint8_t levels[] = {
+        UART_FCR_TRG1,
+        UART_FCR_TRG4,
+        UART_FCR_TRG8,
+        UART_FCR_TRG14,
+    };
+    unsigned int i;
+
+    for ( i = 0; i < ARRAY_SIZE(levels); i++ )
+    {
+        uint8_t iir;
+
+        ns16550_outb(uart, UART_FCR, UART_FCR_ENABLE | levels[i]);
+        iir = ns16550_inb(uart, UART_IIR);
+
+#ifdef STRICT
+        if ( !(iir & UART_IIR_NOINT) )
+            xtf_failure("%s: FCR trigger %#x raised an interrupt (IIR %#x)\n",
+                        banner, levels[i], iir);
+#endif
+        if ( (iir & UART_IIR_FE) != UART_IIR_FE )
+            xtf_failure("%s: FIFO indication lost at trigger %#x (IIR %#x)\n",
+                        banner, levels[i], iir);
+    }
+}
+
+static void test_fcr_clear(const char *banner)
+{
+    unsigned int i;
+    uint8_t lsr;
+
+    ns16550_outb(uart, UART_IER, UART_IER_MASK);
+
+    for ( i = 0; i < 16; i++ )
+    {
+        ns16550_outb(uart, UART_FCR,
+                     UART_FCR_ENABLE | UART_FCR_CLRX | UART_FCR_CLTX);
+    }
+
+    /* Plant a byte via loopback. */
+    ns16550_outb(uart, UART_MCR, UART_MCR_LOOP);
+    ns16550_outb(uart, UART_THR, 'A');
+
+    lsr = ns16550_inb(uart, UART_LSR);
+    if ( lsr & UART_LSR_DR )
+    {
+        ns16550_outb(uart, UART_FCR, UART_FCR_ENABLE | UART_FCR_CLRX);
+        lsr = ns16550_inb(uart, UART_LSR);
+        if ( lsr & UART_LSR_DR )
+            xtf_failure("%s: DR still set after UART_FCR_CLRX (LSR %#x)\n",
+                        banner, lsr);
+
+        ns16550_outb(uart, UART_MCR, 0);
+        ns16550_outb(uart, UART_FCR, 0);
+    }
+    else
+    {
+        return xtf_failure("%s: loopback byte not delivered (LSR %#x)\n",
+                           banner, lsr);
+    }
+}
+
+/*
+ * MCR.LOOP.  Data path first, then the modem path (in loopback the
+ * four MCR outputs route to the four MSR inputs: DTR->DSR, RTS->CTS,
+ * OUT1->RI, OUT2->DCD).
+ */
+static void test_loopback_data_path(const char *banner)
+{
+    unsigned int v;
+
+    ns16550_outb(uart, UART_LCR, UART_LCR_8N1);
+    ns16550_outb(uart, UART_IER, 0);
+    ns16550_outb(uart, UART_FCR, UART_FCR_CLRX | UART_FCR_CLTX);
+
+    /* Discard any stale delta state. */
+    ns16550_inb(uart, UART_MSR);
+
+    ns16550_outb(uart, UART_MCR, UART_MCR_LOOP);
+    if ( !(ns16550_inb(uart, UART_MCR) & UART_MCR_LOOP) )
+        return xtf_failure("%s: UART_MCR_LOOP did not stick\n",
+                           banner);
+
+    for ( v = 0; v < 256; v++ )
+    {
+        uint8_t lsr, r;
+
+        lsr = ns16550_inb(uart, UART_LSR);
+        if ( !(lsr & UART_LSR_THRE) )
+            return xtf_failure("%s: THRE clear before THR read %#x (LSR 
%#x)\n",
+                               banner, v, lsr);
+
+        ns16550_outb(uart, UART_THR, v);
+
+        lsr = ns16550_inb(uart, UART_LSR);
+        if ( !(lsr & UART_LSR_DR) )
+            return xtf_failure("%s: DR clear after THT read %#x (LSR %#x)\n",
+                               banner, v, lsr);
+        if ( lsr & UART_LSR_ERRORS )
+            return xtf_failure("%s: error status after THR read %#x (LSR 
%#x)\n",
+                               banner, v, lsr);
+
+        r = ns16550_inb(uart, UART_RBR);
+        if ( r != v )
+            return xtf_failure("%s: loopback corrupted byte: sent %#x, got 
%#x\n",
+                               banner, v, r);
+
+        lsr = ns16550_inb(uart, UART_LSR);
+        if ( lsr & UART_LSR_DR )
+            return xtf_failure("%s: DR still set after draining RBR (LSR 
%#x)\n",
+                               banner, lsr);
+    }
+}
+
+static void test_loopback_modem_path(const char *banner)
+{
+    uint8_t msr, mask;
+
+    ns16550_outb(uart, UART_LCR, UART_LCR_8N1);
+    ns16550_outb(uart, UART_IER, 0);
+    ns16550_outb(uart, UART_FCR, UART_FCR_CLRX | UART_FCR_CLTX);
+
+    /* Discard any stale delta state. */
+    ns16550_inb(uart, UART_MSR);
+    ns16550_outb(uart, UART_MCR, UART_MCR_LOOP | UART_MCR_DTR |
+                                 UART_MCR_RTS | UART_MCR_OUT1 |
+                                 UART_MCR_OUT2);
+
+    if ( !(ns16550_inb(uart, UART_MCR) & UART_MCR_LOOP) )
+        return xtf_failure("%s: UART_MCR_LOOP did not stick\n",
+                           banner);
+
+    msr = ns16550_inb(uart, UART_MSR);
+    mask = UART_MSR_CTS | UART_MSR_DSR | UART_MSR_RI | UART_MSR_DCD |
+           UART_MSR_DCTS | UART_MSR_DDSR | UART_MSR_TERI | UART_MSR_DDCD;
+    if ( msr != mask )
+        xtf_failure("%s: MSR after raising all outputs: %#x (expected %#x)\n",
+                    banner, msr, mask);
+
+    /* The read above consumed the deltas; lines persist. */
+    msr = ns16550_inb(uart, UART_MSR);
+    mask = UART_MSR_CTS | UART_MSR_DSR | UART_MSR_RI | UART_MSR_DCD;
+    if ( msr != mask )
+        xtf_failure("%s: MSR deltas not cleared by read: %#x (expected %#x)\n",
+                    banner, msr, mask);
+
+    /*
+     * Drop all outputs.  Lines fall, DCTS/DDSR/DDCD latch again, and
+     * this time no TERI.
+     */
+    ns16550_outb(uart, UART_MCR, UART_MCR_LOOP);
+
+    msr = ns16550_inb(uart, UART_MSR);
+    mask = UART_MSR_DCTS | UART_MSR_DDSR | UART_MSR_DDCD;
+    if ( msr != mask )
+        xtf_failure("%s: MSR after dropping all outputs: %#x (expected %#x)\n",
+                    banner, msr, mask);
+
+    msr = ns16550_inb(uart, UART_MSR);
+    if ( msr )
+        xtf_failure("%s: MSR not clear after delta consumption: %#x\n",
+                    banner, msr);
+}
+
+static void test_iir_noint(const char *banner)
+{
+    uint8_t iir;
+
+    ns16550_outb(uart, UART_FCR, UART_FCR_CLRX | UART_FCR_CLTX);
+    ns16550_outb(uart, UART_MCR, UART_MCR_LOOP);
+    ns16550_outb(uart, UART_IER, 0);
+    ns16550_outb(uart, UART_THR, '\0');
+
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( !(iir & UART_IIR_NOINT) )
+        xtf_failure("%s: interrupt reported with IER == 0: IIR %#x\n",
+                    banner, iir);
+}
+
+static void test_iir_thr_source(const char *banner)
+{
+    uint8_t iir;
+
+    ns16550_outb(uart, UART_FCR, UART_FCR_CLRX | UART_FCR_CLTX);
+    ns16550_outb(uart, UART_MCR, UART_MCR_LOOP);
+
+    /*
+     * THR is already empty, so enabling ETHREI must immediately
+     * assert the THR-empty source.
+     */
+    ns16550_outb(uart, UART_IER, UART_IER_ETHREI);
+
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( (iir & UART_IIR_MASK) != UART_IIR_THR )
+        xtf_failure("%s: THR source not asserted on ETHREI enable: IIR %#x\n",
+                    banner, iir);
+
+    ns16550_outb(uart, UART_THR, '\0');
+    ns16550_drain_rx(uart);
+
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( (iir & UART_IIR_MASK) != UART_IIR_THR )
+        xtf_failure("%s: THR source not regenerated by THR write: IIR %#x\n",
+                    banner, iir);
+
+    /* Disabling ETHREI must retract the indication. */
+    ns16550_outb(uart, UART_IER, 0);
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( !(iir & UART_IIR_NOINT) )
+        xtf_failure("%s: THR source survives ETHREI disable: IIR %#x\n",
+                    banner, iir);
+}
+
+static void test_iir_rda_source(const char *banner)
+{
+    uint8_t iir;
+
+    ns16550_outb(uart, UART_FCR, UART_FCR_CLRX | UART_FCR_CLTX);
+    ns16550_outb(uart, UART_MCR, UART_MCR_LOOP);
+    ns16550_outb(uart, UART_IER, UART_IER_ERDAI);
+
+    /* Nothing pending yet. */
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( !(iir & UART_IIR_NOINT) )
+        xtf_failure("%s: RDA asserted with no Rx data: IIR %#x\n",
+                    banner, iir);
+
+    /* Plant a byte; RDA must assert. */
+    ns16550_outb(uart, UART_THR, '\0');
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( (iir & UART_IIR_MASK) != UART_IIR_RDA )
+        xtf_failure("%s: RDA not asserted with Rx data pending: IIR %#x\n",
+                    banner, iir);
+
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( (iir & UART_IIR_MASK) != UART_IIR_RDA )
+        xtf_failure("%s: RDA cleared by IIR read: IIR %#x\n",
+                    banner, iir);
+
+    ns16550_inb(uart, UART_RBR);
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( !(iir & UART_IIR_NOINT) )
+        xtf_failure("%s: RDA not cleared by draining RBR: IIR %#x\n",
+                    banner, iir);
+}
+
+static void test_iir_priority(const char *banner)
+{
+    uint8_t iir;
+
+    ns16550_outb(uart, UART_IER, UART_IER_ERDAI | UART_IER_ETHREI);
+    ns16550_outb(uart, UART_THR, '\0');
+
+    /*
+     * Priority order is LSI > RDA > timeout > THR > MSI
+     */
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( (iir & UART_IIR_MASK) != UART_IIR_RDA )
+        xtf_failure("%s: RDA does not outrank THR: IIR %#x\n",
+                    banner, iir);
+
+    /*
+     * Drain Rx; THR-empty is still outstanding (the THR write above
+     * re-armed it, and no IIR read has identified it as the source
+     * while it was the highest priority), so IIR falls back to THR.
+     */
+    ns16550_inb(uart, UART_RBR);
+    iir = ns16550_inb(uart, UART_IIR);
+    if ( (iir & UART_IIR_MASK) != UART_IIR_THR )
+        xtf_failure("%s: THR not reported after RDA drained: IIR %#x\n",
+                    banner, iir);
+}
+
+struct test_case {
+    const char *banner;
+    void (*run)(const char *banner);
+};
+
+#define TEST_CASE(b, r)     { .banner = (b), .run = (r) }
+
+static const struct test_case test_suite[] = {
+    TEST_CASE("THR",            test_thr),
+    TEST_CASE("SCR",            test_scr),
+    TEST_CASE("LCR",            test_lcr),
+    TEST_CASE("IER",            test_ier),
+    TEST_CASE("DLL/DLM",        test_divisor_latch),
+    TEST_CASE("MCR",            test_mcr),
+    TEST_CASE("FCR-togggle",    test_fcr_toggle),
+    TEST_CASE("FCR-trigger",    test_fcr_trigger),
+    TEST_CASE("FCR-clear",      test_fcr_clear),
+    TEST_CASE("LOOPBACK-data",  test_loopback_data_path),
+    TEST_CASE("LOOPBACK-modem", test_loopback_modem_path),
+    TEST_CASE("IIR-noint",      test_iir_noint),
+    TEST_CASE("IIR-thr",        test_iir_thr_source),
+    TEST_CASE("IIR-rda",        test_iir_rda_source),
+    TEST_CASE("IIR-prio",       test_iir_priority),
+};
+
+void test_main(void)
+{
+    unsigned i;
+
+    for ( i = 0; i < ARRAY_SIZE(test_suite); i++ )
+    {
+        const struct test_case *t = &test_suite[i];
+
+        printk("  Test: %s\n", t->banner);
+        t->run(t->banner);
+    }
+
+    if ( !xtf_status_reported() )
+        xtf_success(NULL);
+
+    xtf_report_status();
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tests/emul-ns16x50/patterns.c b/tests/emul-ns16x50/patterns.c
new file mode 100644
index 000000000000..b47cb6345364
--- /dev/null
+++ b/tests/emul-ns16x50/patterns.c
@@ -0,0 +1,65 @@
+const char *pattern_4096 =
+    "The quick brown fox jumps over the lazy dog. The quick brown fox"
+    " jumps over the lazy dog. The quick brown fox jumps over the laz"
+    "y dog. The quick brown fox jumps over the lazy dog. The quick br"
+    "own fox jumps over the lazy dog. The quick brown fox jumps over "
+    "the lazy dog. The quick brown fox jumps over the lazy dog. The q"
+    "uick brown fox jumps over the lazy dog. The quick brown fox jump"
+    "s over the lazy dog. The quick brown fox jumps over the lazy dog"
+    ". The quick brown fox jumps over the lazy dog. The quick brown f"
+    "ox jumps over the lazy dog. The quick brown fox jumps over the l"
+    "azy dog. The quick brown fox jumps over the lazy dog. The quick "
+    "brown fox jumps over the lazy dog. The quick brown fox jumps ove"
+    "r the lazy dog. The quick brown fox jumps over the lazy dog. The"
+    " quick brown fox jumps over the lazy dog. The quick brown fox ju"
+    "mps over the lazy dog. The quick brown fox jumps over the lazy d"
+    "og. The quick brown fox jumps over the lazy dog. The quick brown"
+    " fox jumps over the lazy dog. The quick brown fox jumps over the"
+    " lazy dog. The quick brown fox jumps over the lazy dog. The quic"
+    "k brown fox jumps over the lazy dog. The quick brown fox jumps o"
+    "ver the lazy dog. The quick brown fox jumps over the lazy dog. T"
+    "he quick brown fox jumps over the lazy dog. The quick brown fox "
+    "jumps over the lazy dog. The quick brown fox jumps over the lazy"
+    " dog. The quick brown fox jumps over the lazy dog. The quick bro"
+    "wn fox jumps over the lazy dog. The quick brown fox jumps over t"
+    "he lazy dog. The quick brown fox jumps over the lazy dog. The qu"
+    "ick brown fox jumps over the lazy dog. The quick brown fox jumps"
+    " over the lazy dog. The quick brown fox jumps over the lazy dog."
+    " The quick brown fox jumps over the lazy dog. The quick brown fo"
+    "x jumps over the lazy dog. The quick brown fox jumps over the la"
+    "zy dog. The quick brown fox jumps over the lazy dog. The quick b"
+    "rown fox jumps over the lazy dog. The quick brown fox jumps over"
+    " the lazy dog. The quick brown fox jumps over the lazy dog. The "
+    "quick brown fox jumps over the lazy dog. The quick brown fox jum"
+    "ps over the lazy dog. The quick brown fox jumps over the lazy do"
+    "g. The quick brown fox jumps over the lazy dog. The quick brown "
+    "fox jumps over the lazy dog. The quick brown fox jumps over the "
+    "lazy dog. The quick brown fox jumps over the lazy dog. The quick"
+    " brown fox jumps over the lazy dog. The quick brown fox jumps ov"
+    "er the lazy dog. The quick brown fox jumps over the lazy dog. Th"
+    "e quick brown fox jumps over the lazy dog. The quick brown fox j"
+    "umps over the lazy dog. The quick brown fox jumps over the lazy "
+    "dog. The quick brown fox jumps over the lazy dog. The quick brow"
+    "n fox jumps over the lazy dog. The quick brown fox jumps over th"
+    "e lazy dog. The quick brown fox jumps over the lazy dog. The qui"
+    "ck brown fox jumps over the lazy dog. The quick brown fox jumps "
+    "over the lazy dog. The quick brown fox jumps over the lazy dog. "
+    "The quick brown fox jumps over the lazy dog. The quick brown fox"
+    " jumps over the lazy dog. The quick brown fox jumps over the laz"
+    "y dog. The quick brown fox jumps over the lazy dog. The quick br"
+    "own fox jumps over the lazy dog. The quick brown fox jumps over "
+    "the lazy dog. The quick brown fox jumps over the lazy dog. The q"
+    "uick brown fox jumps over the lazy dog. The quick brown fox jump"
+    "s over the lazy dog. The quick brown fox jumps over the lazy dog"
+    ". The quick brown fox jumps over the lazy dog. The quick brown f"
+    "ox jumps over the lazy dog. The quick brown fox jumps over the l"
+    "azy dog. The quick brown fox jumps over the lazy dog. The quick "
+    "brown fox jumps over the lazy dog. The quick brown fox jumps ove"
+    "r the lazy dog. The quick brown fox jumps over the lazy dog. The"
+    " quick brown fox jumps over the lazy dog. The quick brown fox ju"
+    "mps over the lazy dog. The quick brown fox jumps over the lazy d"
+    "og. The quick brown fox jumps over the lazy dog. The quick brown"
+    " fox jumps over the lazy dog. The quick brown fox jumps over the"
+    " lazy dog. The quick brown fox jumps over the lazy dog. The quic"
+    "k brown fox jumps over the lazy dog. The quick brown fox jumps o"
+    "ver the lazy dog. The quick brown fox jumps over the lazy dog.\n";
-- 
2.56.0




 


Rackspace

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