qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop
@ 2025-03-10  1:28 Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 1/7] hw/char/pl011: Add transmit FIFO to PL011State Philippe Mathieu-Daudé
                   ` (7 more replies)
  0 siblings, 8 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-03-10  1:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini,
	Philippe Mathieu-Daudé

Hi,

This series add support for (async) FIFO on the transmit path
of the PL011 UART.

Since v6:
- Incorporated Peter's fixes in corresponding patches

Since v5:
- Rebased (few patches already merged)
- Do not forbid disabled UART/receiver (Peter)
- Use fifo8_peek API for wrapped buffer (Mark)

Since v4:
- Rebased (loopback)
- Addressed Richard & Juan migration comments
- Split in smaller patches

Since v3:
- Document migration bits (Alex, Richard)
- Just check FIFO is not empty in pl011_xmit_fifo_state_needed (rth)
- In pl011_xmit check TX enabled first, and ignore < 8-bit TX (rth)

Since v2:
- Added R-b tags
- Addressed Richard comments on migration

Since v1:
- Restrict pl011_ops[] impl access_size,
- Do not check transmitter is enabled (Peter),
- Addressed Alex's review comments,
- Simplified migration trying to care about backward compat,
  but still unsure...

Philippe Mathieu-Daudé (7):
  hw/char/pl011: Add transmit FIFO to PL011State
  hw/char/pl011: Introduce pl011_xmit()
  hw/char/pl011: Factor pl011_xmit_cb() out as GSource
  hw/char/pl011: Trace FIFO enablement
  hw/char/pl011: Consider TX FIFO overrun error
  hw/char/pl011: Drain TX FIFO when no backend connected
  hw/char/pl011: Implement TX FIFO

 include/hw/char/pl011.h |   2 +
 hw/char/pl011.c         | 150 +++++++++++++++++++++++++++++++++++++---
 hw/char/trace-events    |   8 +++
 3 files changed, 150 insertions(+), 10 deletions(-)

-- 
2.47.1



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v7 1/7] hw/char/pl011: Add transmit FIFO to PL011State
  2025-03-10  1:28 [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Philippe Mathieu-Daudé
@ 2025-03-10  1:28 ` Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 2/7] hw/char/pl011: Introduce pl011_xmit() Philippe Mathieu-Daudé
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-03-10  1:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini,
	Philippe Mathieu-Daudé

In order to make the next commit easier to review,
introduce the transmit FIFO, but do not yet use it.

We only migrate the TX FIFO if it is in use.

When migrating from new to old VM:
- if the fifo is empty, migration will still work because
   of the subsection.
- if the fifo is not empty, the subsection will be ignored,
  with the only consequence being that some characters will
  be dropped.

Since the FIFO is created empty, we don't need a migration
pre_load() handler.

Uninline pl011_reset_tx_fifo().

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 include/hw/char/pl011.h |  2 ++
 hw/char/pl011.c         | 37 +++++++++++++++++++++++++++++++++++--
 2 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h
index 4fcaf3d7d30..e8d95961f66 100644
--- a/include/hw/char/pl011.h
+++ b/include/hw/char/pl011.h
@@ -18,6 +18,7 @@
 #include "hw/sysbus.h"
 #include "chardev/char-fe.h"
 #include "qom/object.h"
+#include "qemu/fifo8.h"
 
 #define TYPE_PL011 "pl011"
 OBJECT_DECLARE_SIMPLE_TYPE(PL011State, PL011)
@@ -52,6 +53,7 @@ struct PL011State {
     Clock *clk;
     bool migrate_clk;
     const unsigned char *id;
+    Fifo8 xmit_fifo;
 };
 
 DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr);
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 23a9db8c57c..0e9ad5d5d90 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -167,11 +167,13 @@ static inline void pl011_reset_rx_fifo(PL011State *s)
     s->flags |= PL011_FLAG_RXFE;
 }
 
-static inline void pl011_reset_tx_fifo(PL011State *s)
+static void pl011_reset_tx_fifo(PL011State *s)
 {
     /* Reset FIFO flags */
     s->flags &= ~PL011_FLAG_TXFF;
     s->flags |= PL011_FLAG_TXFE;
+
+    fifo8_reset(&s->xmit_fifo);
 }
 
 static void pl011_fifo_rx_put(void *opaque, uint32_t value)
@@ -560,6 +562,24 @@ static const VMStateDescription vmstate_pl011_clock = {
     }
 };
 
+static bool pl011_xmit_fifo_state_needed(void *opaque)
+{
+    PL011State* s = opaque;
+
+    return pl011_is_fifo_enabled(s) && !fifo8_is_empty(&s->xmit_fifo);
+}
+
+static const VMStateDescription vmstate_pl011_xmit_fifo = {
+    .name = "pl011/xmit_fifo",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = pl011_xmit_fifo_state_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_FIFO8(xmit_fifo, PL011State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static int pl011_post_load(void *opaque, int version_id)
 {
     PL011State* s = opaque;
@@ -614,7 +634,11 @@ static const VMStateDescription vmstate_pl011 = {
     .subsections = (const VMStateDescription * const []) {
         &vmstate_pl011_clock,
         NULL
-    }
+    },
+    .subsections = (const VMStateDescription * []) {
+        &vmstate_pl011_xmit_fifo,
+        NULL
+    },
 };
 
 static const Property pl011_properties[] = {
@@ -628,6 +652,7 @@ static void pl011_init(Object *obj)
     PL011State *s = PL011(obj);
     int i;
 
+    fifo8_create(&s->xmit_fifo, PL011_FIFO_DEPTH);
     memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000);
     sysbus_init_mmio(sbd, &s->iomem);
     for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
@@ -640,6 +665,13 @@ static void pl011_init(Object *obj)
     s->id = pl011_id_arm;
 }
 
+static void pl011_finalize(Object *obj)
+{
+    PL011State *s = PL011(obj);
+
+    fifo8_destroy(&s->xmit_fifo);
+}
+
 static void pl011_realize(DeviceState *dev, Error **errp)
 {
     PL011State *s = PL011(dev);
@@ -683,6 +715,7 @@ static const TypeInfo pl011_arm_info = {
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(PL011State),
     .instance_init = pl011_init,
+    .instance_finalize = pl011_finalize,
     .class_init    = pl011_class_init,
 };
 
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v7 2/7] hw/char/pl011: Introduce pl011_xmit()
  2025-03-10  1:28 [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 1/7] hw/char/pl011: Add transmit FIFO to PL011State Philippe Mathieu-Daudé
@ 2025-03-10  1:28 ` Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 3/7] hw/char/pl011: Factor pl011_xmit_cb() out as GSource Philippe Mathieu-Daudé
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-03-10  1:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini,
	Philippe Mathieu-Daudé

Extract pl011_xmit() from pl011_write_txdata().
Use the FIFO to pass the single character to be transmitted.
Update flags appropriately.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/char/pl011.c      | 43 ++++++++++++++++++++++++++++++++++++-------
 hw/char/trace-events |  3 +++
 2 files changed, 39 insertions(+), 7 deletions(-)

diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 0e9ad5d5d90..113b29cd9e6 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -226,6 +226,36 @@ static void pl011_loopback_tx(PL011State *s, uint32_t value)
     pl011_fifo_rx_put(s, value);
 }
 
+static void pl011_xmit(PL011State *s)
+{
+    int bytes_consumed;
+    uint8_t buf[PL011_FIFO_DEPTH];
+    uint32_t count;
+    bool emptied_fifo;
+
+    count = fifo8_num_used(&s->xmit_fifo);
+    trace_pl011_fifo_tx_xmit_used(count);
+
+    buf[0] = fifo8_pop(&s->xmit_fifo);
+    bytes_consumed = 1;
+
+    /*
+     * XXX this blocks entire thread. Rewrite to use
+     * qemu_chr_fe_write and background I/O callbacks
+     */
+    qemu_chr_fe_write_all(&s->chr, buf, bytes_consumed);
+    trace_pl011_fifo_tx_xmit_consumed(bytes_consumed);
+    s->int_level |= INT_TX;
+    s->flags &= ~PL011_FLAG_TXFF;
+
+    emptied_fifo = fifo8_is_empty(&s->xmit_fifo);
+    if (emptied_fifo) {
+        s->flags |= PL011_FLAG_TXFE;
+    }
+
+    pl011_update(s);
+}
+
 static void pl011_write_txdata(PL011State *s, uint8_t data)
 {
     if (!(s->cr & CR_UARTEN)) {
@@ -237,14 +267,13 @@ static void pl011_write_txdata(PL011State *s, uint8_t data)
                       "PL011 data written to disabled TX UART\n");
     }
 
-    /*
-     * XXX this blocks entire thread. Rewrite to use
-     * qemu_chr_fe_write and background I/O callbacks
-     */
-    qemu_chr_fe_write_all(&s->chr, &data, 1);
+    trace_pl011_fifo_tx_put(data);
     pl011_loopback_tx(s, data);
-    s->int_level |= INT_TX;
-    pl011_update(s);
+    fifo8_push(&s->xmit_fifo, data);
+    s->flags |= PL011_FLAG_TXFF;
+    s->flags &= ~PL011_FLAG_TXFE;
+
+    pl011_xmit(s);
 }
 
 static uint32_t pl011_read_rxdata(PL011State *s)
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 05a33036c12..1bab98fb5f3 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -65,6 +65,9 @@ pl011_write(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x val
 pl011_can_receive(uint32_t lcr, unsigned rx_fifo_used, size_t rx_fifo_depth, unsigned rx_fifo_available) "LCR 0x%02x, RX FIFO used %u/%zu, can_receive %u chars"
 pl011_fifo_rx_put(uint32_t c, unsigned read_count, size_t rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%zu depth used"
 pl011_fifo_rx_full(void) "RX FIFO now full, RXFF set"
+pl011_fifo_tx_put(uint8_t byte) "TX FIFO push char [0x%02x]"
+pl011_fifo_tx_xmit_used(unsigned sent) "TX FIFO used %u chars"
+pl011_fifo_tx_xmit_consumed(unsigned sent) "TX FIFO consumed %u chars"
 pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")"
 pl011_receive(int size) "recv %d chars"
 
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v7 3/7] hw/char/pl011: Factor pl011_xmit_cb() out as GSource
  2025-03-10  1:28 [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 1/7] hw/char/pl011: Add transmit FIFO to PL011State Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 2/7] hw/char/pl011: Introduce pl011_xmit() Philippe Mathieu-Daudé
@ 2025-03-10  1:28 ` Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 4/7] hw/char/pl011: Trace FIFO enablement Philippe Mathieu-Daudé
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-03-10  1:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini,
	Philippe Mathieu-Daudé

Implement pl011_xmit_cb() using the FEWatchFunc prototype
to register it as GSource. While the return value is not
yet used, we return G_SOURCE_REMOVE, meaning the GSource is
removed from the main loop (because we only send one char).

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/char/pl011.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 113b29cd9e6..18ea03a52f4 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -226,8 +226,9 @@ static void pl011_loopback_tx(PL011State *s, uint32_t value)
     pl011_fifo_rx_put(s, value);
 }
 
-static void pl011_xmit(PL011State *s)
+static gboolean pl011_xmit_cb(void *do_not_use, GIOCondition cond, void *opaque)
 {
+    PL011State *s = opaque;
     int bytes_consumed;
     uint8_t buf[PL011_FIFO_DEPTH];
     uint32_t count;
@@ -254,6 +255,13 @@ static void pl011_xmit(PL011State *s)
     }
 
     pl011_update(s);
+
+    return G_SOURCE_REMOVE;
+}
+
+static void pl011_xmit(PL011State *s)
+{
+    (void)pl011_xmit_cb(NULL, G_IO_OUT, s);
 }
 
 static void pl011_write_txdata(PL011State *s, uint8_t data)
@@ -630,6 +638,11 @@ static int pl011_post_load(void *opaque, int version_id)
         s->read_pos = 0;
     }
 
+    if (!fifo8_is_empty(&s->xmit_fifo)) {
+        /* Reschedule another transmission */
+        qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, pl011_xmit_cb, s);
+    }
+
     s->ibrd &= IBRD_MASK;
     s->fbrd &= FBRD_MASK;
 
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v7 4/7] hw/char/pl011: Trace FIFO enablement
  2025-03-10  1:28 [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Philippe Mathieu-Daudé
                   ` (2 preceding siblings ...)
  2025-03-10  1:28 ` [PATCH v7 3/7] hw/char/pl011: Factor pl011_xmit_cb() out as GSource Philippe Mathieu-Daudé
@ 2025-03-10  1:28 ` Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 5/7] hw/char/pl011: Consider TX FIFO overrun error Philippe Mathieu-Daudé
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-03-10  1:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini,
	Philippe Mathieu-Daudé

Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/char/pl011.c      | 3 ++-
 hw/char/trace-events | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 18ea03a52f4..34a5cb3af5d 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -480,8 +480,9 @@ static void pl011_write(void *opaque, hwaddr offset,
         pl011_trace_baudrate_change(s);
         break;
     case 11: /* UARTLCR_H */
-        /* Reset the FIFO state on FIFO enable or disable */
         if ((s->lcr ^ value) & LCR_FEN) {
+            /* Reset the FIFO state on FIFO enable or disable */
+            trace_pl011_fifo_enable(value & LCR_FEN);
             pl011_reset_rx_fifo(s);
             pl011_reset_tx_fifo(s);
         }
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 1bab98fb5f3..c857f4c4b38 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -63,6 +63,7 @@ pl011_read(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x valu
 pl011_read_fifo(unsigned rx_fifo_used, size_t rx_fifo_depth) "RX FIFO read, used %u/%zu"
 pl011_write(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s"
 pl011_can_receive(uint32_t lcr, unsigned rx_fifo_used, size_t rx_fifo_depth, unsigned rx_fifo_available) "LCR 0x%02x, RX FIFO used %u/%zu, can_receive %u chars"
+pl011_fifo_enable(bool enable) "enable:%u"
 pl011_fifo_rx_put(uint32_t c, unsigned read_count, size_t rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%zu depth used"
 pl011_fifo_rx_full(void) "RX FIFO now full, RXFF set"
 pl011_fifo_tx_put(uint8_t byte) "TX FIFO push char [0x%02x]"
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v7 5/7] hw/char/pl011: Consider TX FIFO overrun error
  2025-03-10  1:28 [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Philippe Mathieu-Daudé
                   ` (3 preceding siblings ...)
  2025-03-10  1:28 ` [PATCH v7 4/7] hw/char/pl011: Trace FIFO enablement Philippe Mathieu-Daudé
@ 2025-03-10  1:28 ` Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 6/7] hw/char/pl011: Drain TX FIFO when no backend connected Philippe Mathieu-Daudé
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-03-10  1:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini,
	Philippe Mathieu-Daudé

When transmission is disabled, characters are still queued
to the FIFO which eventually overruns. Report that error
condition in the status register.

Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/char/pl011.c      | 22 ++++++++++++++++++++++
 hw/char/trace-events |  2 ++
 2 files changed, 24 insertions(+)

diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 34a5cb3af5d..f67ce951ac9 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -61,6 +61,9 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr)
 /* Data Register, UARTDR */
 #define DR_BE   (1 << 10)
 
+/* Receive Status Register/Error Clear Register, UARTRSR/UARTECR */
+#define RSR_OE  (1 << 3)
+
 /* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */
 #define INT_OE (1 << 10)
 #define INT_BE (1 << 9)
@@ -157,6 +160,18 @@ static inline unsigned pl011_get_fifo_depth(PL011State *s)
     return pl011_is_fifo_enabled(s) ? PL011_FIFO_DEPTH : 1;
 }
 
+static bool pl011_is_tx_fifo_full(PL011State *s)
+{
+    bool fifo_enabled = pl011_is_fifo_enabled(s);
+    bool tx_fifo_full = fifo_enabled
+                        ? fifo8_is_full(&s->xmit_fifo)
+                        : !fifo8_is_empty(&s->xmit_fifo);
+
+    trace_pl011_fifo_tx_is_full(fifo_enabled ? "FIFO" : "CHAR", tx_fifo_full);
+
+    return tx_fifo_full;
+}
+
 static inline void pl011_reset_rx_fifo(PL011State *s)
 {
     s->read_count = 0;
@@ -275,6 +290,13 @@ static void pl011_write_txdata(PL011State *s, uint8_t data)
                       "PL011 data written to disabled TX UART\n");
     }
 
+    if (pl011_is_tx_fifo_full(s)) {
+        /* The FIFO is already full. Content remains valid. */
+        trace_pl011_fifo_tx_overrun();
+        s->rsr |= RSR_OE;
+        return;
+    }
+
     trace_pl011_fifo_tx_put(data);
     pl011_loopback_tx(s, data);
     fifo8_push(&s->xmit_fifo, data);
diff --git a/hw/char/trace-events b/hw/char/trace-events
index c857f4c4b38..d52f511a1e2 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -66,9 +66,11 @@ pl011_can_receive(uint32_t lcr, unsigned rx_fifo_used, size_t rx_fifo_depth, uns
 pl011_fifo_enable(bool enable) "enable:%u"
 pl011_fifo_rx_put(uint32_t c, unsigned read_count, size_t rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%zu depth used"
 pl011_fifo_rx_full(void) "RX FIFO now full, RXFF set"
+pl011_fifo_tx_is_full(const char *desc, bool full) "mode:%s full:%u"
 pl011_fifo_tx_put(uint8_t byte) "TX FIFO push char [0x%02x]"
 pl011_fifo_tx_xmit_used(unsigned sent) "TX FIFO used %u chars"
 pl011_fifo_tx_xmit_consumed(unsigned sent) "TX FIFO consumed %u chars"
+pl011_fifo_tx_overrun(void) "TX FIFO overrun"
 pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")"
 pl011_receive(int size) "recv %d chars"
 
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v7 6/7] hw/char/pl011: Drain TX FIFO when no backend connected
  2025-03-10  1:28 [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Philippe Mathieu-Daudé
                   ` (4 preceding siblings ...)
  2025-03-10  1:28 ` [PATCH v7 5/7] hw/char/pl011: Consider TX FIFO overrun error Philippe Mathieu-Daudé
@ 2025-03-10  1:28 ` Philippe Mathieu-Daudé
  2025-03-10  1:28 ` [PATCH v7 7/7] hw/char/pl011: Implement TX FIFO Philippe Mathieu-Daudé
  2025-03-10 14:42 ` [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Peter Maydell
  7 siblings, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-03-10  1:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini,
	Philippe Mathieu-Daudé

When no character backend is connected, the PL011 frontend
just drains the FIFO.

Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/char/pl011.c      | 13 +++++++++++++
 hw/char/trace-events |  1 +
 2 files changed, 14 insertions(+)

diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index f67ce951ac9..6d26a3e13f8 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -241,6 +241,13 @@ static void pl011_loopback_tx(PL011State *s, uint32_t value)
     pl011_fifo_rx_put(s, value);
 }
 
+static void pl011_drain_tx(PL011State *s)
+{
+    trace_pl011_fifo_tx_drain(fifo8_num_used(&s->xmit_fifo));
+    pl011_reset_tx_fifo(s);
+    s->rsr &= ~RSR_OE;
+}
+
 static gboolean pl011_xmit_cb(void *do_not_use, GIOCondition cond, void *opaque)
 {
     PL011State *s = opaque;
@@ -252,6 +259,12 @@ static gboolean pl011_xmit_cb(void *do_not_use, GIOCondition cond, void *opaque)
     count = fifo8_num_used(&s->xmit_fifo);
     trace_pl011_fifo_tx_xmit_used(count);
 
+    if (!qemu_chr_fe_backend_connected(&s->chr)) {
+        /* Instant drain the fifo when there's no back-end. */
+        pl011_drain_tx(s);
+        return G_SOURCE_REMOVE;
+    }
+
     buf[0] = fifo8_pop(&s->xmit_fifo);
     bytes_consumed = 1;
 
diff --git a/hw/char/trace-events b/hw/char/trace-events
index d52f511a1e2..730d6292a2d 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -71,6 +71,7 @@ pl011_fifo_tx_put(uint8_t byte) "TX FIFO push char [0x%02x]"
 pl011_fifo_tx_xmit_used(unsigned sent) "TX FIFO used %u chars"
 pl011_fifo_tx_xmit_consumed(unsigned sent) "TX FIFO consumed %u chars"
 pl011_fifo_tx_overrun(void) "TX FIFO overrun"
+pl011_fifo_tx_drain(unsigned drained) "TX FIFO draining %u chars"
 pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")"
 pl011_receive(int size) "recv %d chars"
 
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v7 7/7] hw/char/pl011: Implement TX FIFO
  2025-03-10  1:28 [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Philippe Mathieu-Daudé
                   ` (5 preceding siblings ...)
  2025-03-10  1:28 ` [PATCH v7 6/7] hw/char/pl011: Drain TX FIFO when no backend connected Philippe Mathieu-Daudé
@ 2025-03-10  1:28 ` Philippe Mathieu-Daudé
  2025-03-10 14:42 ` [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Peter Maydell
  7 siblings, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-03-10  1:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini,
	Philippe Mathieu-Daudé, Mikko Rapeli

If the UART back-end chardev doesn't drain data as fast as stdout
does or blocks, buffer in the TX FIFO to try again later.

This avoids having the IO-thread busy waiting on chardev back-ends,
reported recently when testing the Trusted Reference Stack and
using the socket backend.

Implement registering a front-end 'watch' callback on back-end
events, so we can resume transmitting when the back-end is writable
again, not blocking the main loop.

Similarly to the RX FIFO path, FIFO level selection is not
implemented (interrupt is triggered when a single byte is available
in the FIFO).

Reported-by: Mikko Rapeli <mikko.rapeli@linaro.org>
Suggested-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/char/pl011.c      | 35 +++++++++++++++++++++++++++--------
 hw/char/trace-events |  1 +
 2 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 6d26a3e13f8..d93cd8b3f0e 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -258,6 +258,10 @@ static gboolean pl011_xmit_cb(void *do_not_use, GIOCondition cond, void *opaque)
 
     count = fifo8_num_used(&s->xmit_fifo);
     trace_pl011_fifo_tx_xmit_used(count);
+    if (count < 1) {
+        /* FIFO empty */
+        return G_SOURCE_REMOVE;
+    }
 
     if (!qemu_chr_fe_backend_connected(&s->chr)) {
         /* Instant drain the fifo when there's no back-end. */
@@ -265,15 +269,23 @@ static gboolean pl011_xmit_cb(void *do_not_use, GIOCondition cond, void *opaque)
         return G_SOURCE_REMOVE;
     }
 
-    buf[0] = fifo8_pop(&s->xmit_fifo);
-    bytes_consumed = 1;
+    count = fifo8_peek_buf(&s->xmit_fifo, buf, fifo8_num_used(&s->xmit_fifo));
+    trace_pl011_fifo_tx_xmit_peek(count);
 
-    /*
-     * XXX this blocks entire thread. Rewrite to use
-     * qemu_chr_fe_write and background I/O callbacks
-     */
-    qemu_chr_fe_write_all(&s->chr, buf, bytes_consumed);
+    /* Transmit as much data as we can. */
+    bytes_consumed = qemu_chr_fe_write(&s->chr, buf, count);
     trace_pl011_fifo_tx_xmit_consumed(bytes_consumed);
+    if (bytes_consumed < 0) {
+        /* Error in back-end: drain the fifo. */
+        pl011_drain_tx(s);
+        return G_SOURCE_REMOVE;
+    } else if (bytes_consumed == 0) {
+        /* Couldn't send anything, try again later */
+        return G_SOURCE_CONTINUE;
+    }
+
+    /* Pop the data we could transmit. */
+    fifo8_drop(&s->xmit_fifo, bytes_consumed);
     s->int_level |= INT_TX;
     s->flags &= ~PL011_FLAG_TXFF;
 
@@ -284,6 +296,11 @@ static gboolean pl011_xmit_cb(void *do_not_use, GIOCondition cond, void *opaque)
 
     pl011_update(s);
 
+    if (!emptied_fifo) {
+        /* Reschedule another transmission if we couldn't transmit all. */
+        return G_SOURCE_CONTINUE;
+    }
+
     return G_SOURCE_REMOVE;
 }
 
@@ -313,7 +330,9 @@ static void pl011_write_txdata(PL011State *s, uint8_t data)
     trace_pl011_fifo_tx_put(data);
     pl011_loopback_tx(s, data);
     fifo8_push(&s->xmit_fifo, data);
-    s->flags |= PL011_FLAG_TXFF;
+    if (pl011_is_tx_fifo_full(s)) {
+        s->flags |= PL011_FLAG_TXFF;
+    }
     s->flags &= ~PL011_FLAG_TXFE;
 
     pl011_xmit(s);
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 730d6292a2d..c377f971a6c 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -68,6 +68,7 @@ pl011_fifo_rx_put(uint32_t c, unsigned read_count, size_t rx_fifo_depth) "RX FIF
 pl011_fifo_rx_full(void) "RX FIFO now full, RXFF set"
 pl011_fifo_tx_is_full(const char *desc, bool full) "mode:%s full:%u"
 pl011_fifo_tx_put(uint8_t byte) "TX FIFO push char [0x%02x]"
+pl011_fifo_tx_xmit_peek(unsigned sent) "TX FIFO peek %u chars"
 pl011_fifo_tx_xmit_used(unsigned sent) "TX FIFO used %u chars"
 pl011_fifo_tx_xmit_consumed(unsigned sent) "TX FIFO consumed %u chars"
 pl011_fifo_tx_overrun(void) "TX FIFO overrun"
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop
  2025-03-10  1:28 [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Philippe Mathieu-Daudé
                   ` (6 preceding siblings ...)
  2025-03-10  1:28 ` [PATCH v7 7/7] hw/char/pl011: Implement TX FIFO Philippe Mathieu-Daudé
@ 2025-03-10 14:42 ` Peter Maydell
  2025-03-10 17:28   ` Peter Maydell
  7 siblings, 1 reply; 12+ messages in thread
From: Peter Maydell @ 2025-03-10 14:42 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: qemu-devel, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini

On Mon, 10 Mar 2025 at 01:28, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> Hi,
>
> This series add support for (async) FIFO on the transmit path
> of the PL011 UART.

This hasn't made the last pre-softfreeze arm pullreq, but
I think we can reasonably call "don't do blocking I/O"
enough of a bugfix for it to be ok to go in early in the
freeze cycle for rc0.

I've applied it to target-arm.next.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop
  2025-03-10 14:42 ` [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Peter Maydell
@ 2025-03-10 17:28   ` Peter Maydell
  2025-03-11 10:33     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 12+ messages in thread
From: Peter Maydell @ 2025-03-10 17:28 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: qemu-devel, Marc-André Lureau, Alex Bennée,
	Mark Cave-Ayland, qemu-arm, Paolo Bonzini

On Mon, 10 Mar 2025 at 14:42, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Mon, 10 Mar 2025 at 01:28, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
> >
> > Hi,
> >
> > This series add support for (async) FIFO on the transmit path
> > of the PL011 UART.
>
> This hasn't made the last pre-softfreeze arm pullreq, but
> I think we can reasonably call "don't do blocking I/O"
> enough of a bugfix for it to be ok to go in early in the
> freeze cycle for rc0.
>
> I've applied it to target-arm.next.

...but it still fails 'make check-functional', though in a
less easy-to-reproduce way than it did. The problem turns out
to be that when the guest kernel is doing its earlycon
output (which is by polling, not interrupt driven) the output
can be corrupted, which makes the aarch64/test_arm_virt test
fail to find the "Kernel command line:" output it is looking for.

This seems to be because the pl011 code and the chardev
code disagree about how "couldn't write anything" is
reported. pl011 here is looking for "0 means wrote nothing",
but the chardev code reports it as "-1 and errno is EAGAIN".

I think the chardev code is actually what we need to fix here,
because it makes basically no effort to guarantee that the
errno from the underlying write is still in 'errno' by the
time qemu_chr_fe_write() returns. In particular it may
call qemu_chr_write_log() or replay_char_write_event_save(),
both of which will happily trash errno if something fails
during their execution.

So my long-term preference for fixing this is:
 * fix up any callsites that can't handle a 0 return for
   "wrote no bytes"
 * make (and document) qemu_chr_fe_write()'s return value be
    - 0 == wrote no bytes
    - >0 == wrote some bytes
    - <0 == a negative-errno indicating a definite error


I had planned in the meantime that we could deal with
this by squashing in this change to the last patch in
this series:

--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -275,6 +275,9 @@ static gboolean pl011_xmit_cb(void *do_not_use,
GIOCondition cond, void *opaque)
     /* Transmit as much data as we can. */
     bytes_consumed = qemu_chr_fe_write(&s->chr, buf, count);
     trace_pl011_fifo_tx_xmit_consumed(bytes_consumed);
+    if (bytes_consumed < 0 && errno == EAGAIN) {
+        bytes_consumed = 0;
+    }
     if (bytes_consumed < 0) {
         /* Error in back-end: drain the fifo. */
         printf("oops, bytes_consumed = %d errno = %d\n",
bytes_consumed, errno);


which makes the code handle both "returns 0" and "returns -1
with errno=EAGAIN" as "try again later".

But even with that I still see the check-functional
test failing on a clang sanitizer build, though without
any clear reason why. It's intermittent; running the
test like this:

(cd build/arm-clang/ ; PYTHONPATH=../../python:../../tests/functional
QEMU_TEST_QEMU_BINARY=./qemu-system-aarch64 ./pyvenv/bin/python3
../../tests/functional/test_arm_virt.py)

I got one pass once but mostly it hangs after printing
some of the early console output. A debug build seems
more reliable, oddly.

I'll try to continue investigating this this week, but
in the meantime I'm going to have to drop this series
from target-arm.next again, I'm afraid :-(

thanks
-- PMM


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop
  2025-03-10 17:28   ` Peter Maydell
@ 2025-03-11 10:33     ` Philippe Mathieu-Daudé
  2025-03-11 18:36       ` Peter Maydell
  0 siblings, 1 reply; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-03-11 10:33 UTC (permalink / raw)
  To: Peter Maydell, Marc-André Lureau, Paolo Bonzini
  Cc: qemu-devel, Alex Bennée, Mark Cave-Ayland, qemu-arm

Hi Peter,

On 10/3/25 18:28, Peter Maydell wrote:
> On Mon, 10 Mar 2025 at 14:42, Peter Maydell <peter.maydell@linaro.org> wrote:
>>
>> On Mon, 10 Mar 2025 at 01:28, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>>>
>>> Hi,
>>>
>>> This series add support for (async) FIFO on the transmit path
>>> of the PL011 UART.
>>
>> This hasn't made the last pre-softfreeze arm pullreq, but
>> I think we can reasonably call "don't do blocking I/O"
>> enough of a bugfix for it to be ok to go in early in the
>> freeze cycle for rc0.
>>
>> I've applied it to target-arm.next.
> 
> ...but it still fails 'make check-functional', though in a
> less easy-to-reproduce way than it did. The problem turns out
> to be that when the guest kernel is doing its earlycon
> output (which is by polling, not interrupt driven) the output
> can be corrupted, which makes the aarch64/test_arm_virt test
> fail to find the "Kernel command line:" output it is looking for.

Thanks for keeping investigating...

> This seems to be because the pl011 code and the chardev
> code disagree about how "couldn't write anything" is
> reported. pl011 here is looking for "0 means wrote nothing",
> but the chardev code reports it as "-1 and errno is EAGAIN".
> 
> I think the chardev code is actually what we need to fix here,
> because it makes basically no effort to guarantee that the
> errno from the underlying write is still in 'errno' by the
> time qemu_chr_fe_write() returns. In particular it may
> call qemu_chr_write_log() or replay_char_write_event_save(),
> both of which will happily trash errno if something fails
> during their execution.

IIUC when retrying qemu_chr_write_buffer(s, buf, len, ofs) could
write less than @len (but still writing few bytes, that information
is stored in @offset) and return -errno, discarding @offset partial
write len.

> So my long-term preference for fixing this is:
>   * fix up any callsites that can't handle a 0 return for
>     "wrote no bytes"
>   * make (and document) qemu_chr_fe_write()'s return value be
>      - 0 == wrote no bytes
>      - >0 == wrote some bytes
>      - <0 == a negative-errno indicating a definite error

This would be an improvement, but not fixing ignored partial
writes mentioned, is that right?

> I had planned in the meantime that we could deal with
> this by squashing in this change to the last patch in
> this series:
> 
> --- a/hw/char/pl011.c
> +++ b/hw/char/pl011.c
> @@ -275,6 +275,9 @@ static gboolean pl011_xmit_cb(void *do_not_use,
> GIOCondition cond, void *opaque)
>       /* Transmit as much data as we can. */
>       bytes_consumed = qemu_chr_fe_write(&s->chr, buf, count);
>       trace_pl011_fifo_tx_xmit_consumed(bytes_consumed);
> +    if (bytes_consumed < 0 && errno == EAGAIN) {
> +        bytes_consumed = 0;
> +    }
>       if (bytes_consumed < 0) {
>           /* Error in back-end: drain the fifo. */
>           printf("oops, bytes_consumed = %d errno = %d\n",
> bytes_consumed, errno);
> 
> 
> which makes the code handle both "returns 0" and "returns -1
> with errno=EAGAIN" as "try again later".
> 
> But even with that I still see the check-functional
> test failing on a clang sanitizer build, though without
> any clear reason why. It's intermittent; running the
> test like this:
> 
> (cd build/arm-clang/ ; PYTHONPATH=../../python:../../tests/functional
> QEMU_TEST_QEMU_BINARY=./qemu-system-aarch64 ./pyvenv/bin/python3
> ../../tests/functional/test_arm_virt.py)
> 
> I got one pass once but mostly it hangs after printing
> some of the early console output. A debug build seems
> more reliable, oddly.
> 
> I'll try to continue investigating this this week, but
> in the meantime I'm going to have to drop this series
> from target-arm.next again, I'm afraid :-(

No worry, I was prepared for another issue :)

Regards,

Phil.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop
  2025-03-11 10:33     ` Philippe Mathieu-Daudé
@ 2025-03-11 18:36       ` Peter Maydell
  0 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-03-11 18:36 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Marc-André Lureau, Paolo Bonzini, qemu-devel,
	Alex Bennée, Mark Cave-Ayland, qemu-arm

On Tue, 11 Mar 2025 at 10:33, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
> On 10/3/25 18:28, Peter Maydell wrote:
> > This seems to be because the pl011 code and the chardev
> > code disagree about how "couldn't write anything" is
> > reported. pl011 here is looking for "0 means wrote nothing",
> > but the chardev code reports it as "-1 and errno is EAGAIN".
> >
> > I think the chardev code is actually what we need to fix here,
> > because it makes basically no effort to guarantee that the
> > errno from the underlying write is still in 'errno' by the
> > time qemu_chr_fe_write() returns. In particular it may
> > call qemu_chr_write_log() or replay_char_write_event_save(),
> > both of which will happily trash errno if something fails
> > during their execution.
>
> IIUC when retrying qemu_chr_write_buffer(s, buf, len, ofs) could
> write less than @len (but still writing few bytes, that information
> is stored in @offset) and return -errno, discarding @offset partial
> write len.

I thought that too when I first read the code, because it's
been written in a way that didn't match what I was expecting
for a "write until you would block, then stop" loop, but on
second reading I decided it could not, at least in the case
where write_all is false. If we write any data at all on the
first cc->chr_write call, we will then break out of the loop
without trying to send any more, so there's no code path
that will then set res to a negative number. (If write_all
is true, then yeah I think we can return an errno rather
than a partial-write count if chr_write fails on the second
time through the loop, but all the callers of that version
of the function ignore errors of any kind anyway, and
certainly don't have any means to handle "only wrote half
the data", otherwise they'd be using the non-blocking
version.)

-- PMM


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2025-03-11 18:38 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-10  1:28 [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Philippe Mathieu-Daudé
2025-03-10  1:28 ` [PATCH v7 1/7] hw/char/pl011: Add transmit FIFO to PL011State Philippe Mathieu-Daudé
2025-03-10  1:28 ` [PATCH v7 2/7] hw/char/pl011: Introduce pl011_xmit() Philippe Mathieu-Daudé
2025-03-10  1:28 ` [PATCH v7 3/7] hw/char/pl011: Factor pl011_xmit_cb() out as GSource Philippe Mathieu-Daudé
2025-03-10  1:28 ` [PATCH v7 4/7] hw/char/pl011: Trace FIFO enablement Philippe Mathieu-Daudé
2025-03-10  1:28 ` [PATCH v7 5/7] hw/char/pl011: Consider TX FIFO overrun error Philippe Mathieu-Daudé
2025-03-10  1:28 ` [PATCH v7 6/7] hw/char/pl011: Drain TX FIFO when no backend connected Philippe Mathieu-Daudé
2025-03-10  1:28 ` [PATCH v7 7/7] hw/char/pl011: Implement TX FIFO Philippe Mathieu-Daudé
2025-03-10 14:42 ` [PATCH v7 0/7] hw/char/pl011: Implement TX (async) FIFO to avoid blocking the main loop Peter Maydell
2025-03-10 17:28   ` Peter Maydell
2025-03-11 10:33     ` Philippe Mathieu-Daudé
2025-03-11 18:36       ` Peter Maydell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).