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

[PATCH v8 11/26] emul/ns16x50: implement THR register


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: dmukhin@xxxxxxxx
  • Date: Wed, 11 Nov 2026 11:47:45 -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=1QRcdiXRmtacN/pNnj4vEqZJ9tCPmHxrkEtP4X2KZNs=; b=ij13LwzdKpNQBxkg6eVMml2WWDHr4Pu9EKf2U50O2Vc1YncqTfPXsuvZ7QMEPqVJIcvKcon+wUn/PwsWhRhR+UrlrQx9wKEr30oIpKVmdBuMBQrrJKe+YdkopKY7TMpELXP5Tlmfi5U4slvL7Rbjk3pSazLXAtQ/yKJKcOf4Qm0Celr+dwTyeIasiF1i1cf6dsgaSMjotRmVHHIn2lbDV7w0o9BpGnDUd/2HXOwUHHmnmCsTt0OBjWheB5JWUwslTAOdMpC17nAUVKvUhRiCkvVs8uWrHjkjlOTR1qHh1+WT/aNgaatIRWcT5gIkvSu4CQrAcrJr0m8IqglacI62/Q==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=laSwNeqrsaHruB/DqHBGUIwbEQBXUwh87yIjz7nwsPFtvAnfi/Mooga7TfnodUGHvPINxChBuMAIXe3XtnJRvCDh4UcDy0H2G80vty/OzS6G+brMMPQ8eIs+4ILpyM+EzVITlgP8VpGMEhbzjob1hUbKhPPXme8E8LDeczpM395tjJajWBpM2lr/WqalQgcQ2gROtrINK7hq7bOGD2xaVE5lx2tMuxBzStXdCRtBzPrpTs/1uR3AYs2MoUjgt7Dov4iztgCAZfKS1hxUCpU63yX2iFYdQuRD9QfIWnhY41rv0lGNCsTrkYhlaJWV98S/bt34QqdhtZw8oPCwLauCdg==
  • 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:51 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Pser-m365-app: SER-APP

From: Denis Mukhin <dmukhin@xxxxxxxx>

Add THR register emulation to the I/O port handlder.

Add TX FIFO management code since THR depends on TX FIFO.

TX FIFOs is emulated by using xencons_interface which conveniently
provides primitives for buffer management and later can be used for
inter-domain communication similarly to vpl011.

Account for DLL == 0: in this case, disable transmitter.

Add UART_IIR_THR interrupt reason handling since it depends on THR
register access.

Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx>
---
 xen/common/emul/vuart/ns16x50.c | 121 +++++++++++++++++++++++++++++++-
 1 file changed, 120 insertions(+), 1 deletion(-)

diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index d04caf2baf2c..8d3845a8a4cd 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -98,6 +98,7 @@ struct vuart_ns16x50 {
     spinlock_t lock;                    /* Protection */
     struct xencons_interface cons;      /* Emulated RX/TX FIFOs */
     bool irq_asserted;                  /* IRQ line status */
+    bool thr_pending;                   /* THR interrupt reason status */
 };
 
 static inline void ns16x50_lock_init(struct vuart_ns16x50 *vdev)
@@ -185,6 +186,72 @@ static int ns16x50_fifo_rx_putchar(struct vuart_ns16x50 
*vdev, char c)
     return rc;
 }
 
+static bool ns16x50_fifo_tx_full(const struct vuart_ns16x50 *vdev)
+{
+    const struct xencons_interface *cons = &vdev->cons;
+
+    return cons->out_prod - cons->out_cons == ARRAY_SIZE(cons->out);
+}
+
+static void ns16x50_fifo_tx_reset(struct vuart_ns16x50 *vdev)
+{
+    struct xencons_interface *cons = &vdev->cons;
+
+    cons->out_cons = cons->out_prod;
+}
+
+/*
+ * Flush cached output to Xen console.
+ */
+static void ns16x50_fifo_tx_flush(struct vuart_ns16x50 *vdev)
+{
+    struct xencons_interface *cons = &vdev->cons;
+    struct domain *d = vdev->owner;
+    XENCONS_RING_IDX i, j, n, m, len = cons->out_prod - cons->out_cons;
+
+    ASSERT(len <= ARRAY_SIZE(cons->out));
+    if ( !len )
+        return;
+
+    /* Tail of the FIFO */
+    i = MASK_XENCONS_IDX(cons->out_cons, cons->out);
+    n = min_t(XENCONS_RING_IDX, len, ARRAY_SIZE(cons->out) - i);
+
+    /* Head of the FIFO */
+    j = 0;
+    m = len - n;
+
+    /* Linearize the FIFO */
+    if ( m )
+        guest_printk(d, guest_prefix "%.*s%.*s",
+                     n, &cons->out[i],
+                     m, &cons->out[j]);
+    else
+        guest_printk(d, guest_prefix "%.*s", n, &cons->out[i]);
+
+    cons->out_cons += len;
+}
+
+/*
+ * Accumulate guest OS output before sending to Xen console.
+ */
+static void ns16x50_fifo_tx_putchar(struct vuart_ns16x50 *vdev, char ch)
+{
+    struct xencons_interface *cons = &vdev->cons;
+
+    if ( !is_console_printable(ch) )
+        return;
+
+    if ( !ns16x50_fifo_tx_full(vdev) )
+    {
+        cons->out[MASK_XENCONS_IDX(cons->out_prod, cons->out)] = ch;
+        cons->out_prod++;
+    }
+
+    if ( ch == '\n' || ch == '\0' || ns16x50_fifo_tx_full(vdev) )
+        ns16x50_fifo_tx_flush(vdev);
+}
+
 static bool ns16x50_is_tranceiver_enabled(const struct vuart_ns16x50 *vdev)
 {
     BUG_ON(vdev->regs[UART_LCR] & UART_LCR_DLAB);
@@ -205,7 +272,7 @@ static bool cf_check ns16x50_iir_check_rda(const struct 
vuart_ns16x50 *vdev)
 
 static bool cf_check ns16x50_iir_check_thr(const struct vuart_ns16x50 *vdev)
 {
-    return false;
+    return vdev->thr_pending;
 }
 
 static bool cf_check ns16x50_iir_check_msi(const struct vuart_ns16x50 *vdev)
@@ -316,6 +383,38 @@ static int ns16x50_io_write8(
         }
 
         /* DLAB=0 */
+        if ( !ns16x50_is_tranceiver_enabled(vdev) )
+            break;
+
+        /*
+         * THR is no longer empty.
+         */
+        vdev->thr_pending = false;
+        regs[UART_LSR] &= ~(UART_LSR_THRE | UART_LSR_TEMT);
+
+        /*
+         * Drop the IRQ line before completing this immediate
+         * transmission, if THRE was its only reason.
+         */
+        ns16x50_irq_check(vdev);
+
+        if ( regs[UART_MCR] & UART_MCR_LOOP )
+        {
+            if ( ns16x50_fifo_rx_putchar(vdev, val) )
+                regs[UART_LSR] |= UART_LSR_OE;
+
+            regs[UART_LSR] |= UART_LSR_DR;
+        }
+        else
+            ns16x50_fifo_tx_putchar(vdev, val);
+
+        /*
+         * Backend transmission is synchronous in this emulator,
+         * so THR is immediately empty again.
+         */
+        regs[UART_LSR] |= UART_LSR_THRE | UART_LSR_TEMT;
+        vdev->thr_pending = true;
+
         break;
 
     case UART_IER: /* DLAB=0 --> UART_IER, DLAB=1 --> UART_DLM */
@@ -327,7 +426,13 @@ static int ns16x50_io_write8(
         /* DLAB=0 */
         else
         {
+            uint8_t old = regs[UART_IER];
+
             regs[UART_IER] = val & UART_IER_ALL_INTR;
+
+            if ( (old ^ regs[UART_IER]) & UART_IER_ETHREI )
+                vdev->thr_pending = ( (regs[UART_IER] & UART_IER_ETHREI) &&
+                                       (regs[UART_LSR] & UART_LSR_THRE) );
         }
         break;
 
@@ -497,6 +602,14 @@ static int ns16x50_io_read8(
 
     case UART_IIR: /* RO */
         val = ns16x50_iir_get(vdev);
+
+        /*
+         * Reading IIR when THRE is the reported reason clears the
+         * THRE interrupt latch.
+         */
+        if ( (val & UART_IIR_IMA) == UART_IIR_THR )
+            vdev->thr_pending = false;
+
         break;
 
     case UART_LCR:
@@ -679,6 +792,7 @@ static void ns16x50_reset(struct vuart_ns16x50 *vdev)
 
     memset(vdev->regs, 0, sizeof(vdev->regs));
     vdev->irq_asserted = false;
+    vdev->thr_pending = false;
 
     vdev->regs[UART_LSR] = UART_LSR_THRE | UART_LSR_TEMT;
     vdev->regs[UART_MSR] = UART_MSR_DCD |
@@ -722,8 +836,13 @@ static int ns16x50_init(void *arg)
 static void cf_check ns16x50_deinit(void *arg)
 {
     struct vuart_ns16x50 *vdev = arg;
+    unsigned long flags;
 
     ASSERT(vdev);
+
+    ns16x50_lock(vdev, &flags);
+    ns16x50_fifo_tx_flush(vdev);
+    ns16x50_unlock(vdev, &flags);
 }
 
 static void * cf_check ns16x50_alloc(struct domain *d, const struct vuart_info 
*info)
-- 
2.56.0




 


Rackspace

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