aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Percival <cperciva@FreeBSD.org>2022-10-13 18:01:03 +0000
committerColin Percival <cperciva@FreeBSD.org>2022-10-18 06:02:21 +0000
commitc4b68e7e53bb352be3fa16995b99764c03097e66 (patch)
tree512a9b2d9507dd64309b7cb0e99f8d7bb38b4c6e
parent782105f7c89821552304245d71e107b867a3e934 (diff)
downloadsrc-c4b68e7e53bb352be3fa16995b99764c03097e66.tar.gz
src-c4b68e7e53bb352be3fa16995b99764c03097e66.zip
ns8250: Check if flush via FCR succeeded
The emulated UART in the Firecracker VMM (aka the implementation in the rust-vmm/vm-superio project) includes FIFOs but does not implement the FCR register, which is used by ns8250_flush to flush the FIFOs. Check the LSR to see if there is still data in the FIFOs and call ns8250_drain if necessary. Discussed with: emaste, imp, jrtc27 Sponsored by: https://patreon.com/cperciva Differential Revision: https://reviews.freebsd.org/D36979
-rw-r--r--sys/dev/uart/uart_dev_ns8250.c19
1 files changed, 19 insertions, 0 deletions
diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c
index 182831da7611..d0eadeded943 100644
--- a/sys/dev/uart/uart_dev_ns8250.c
+++ b/sys/dev/uart/uart_dev_ns8250.c
@@ -210,6 +210,8 @@ static void
ns8250_flush(struct uart_bas *bas, int what)
{
uint8_t fcr;
+ uint8_t lsr;
+ int drain = 0;
fcr = FCR_ENABLE;
#ifdef CPU_XBURST
@@ -221,6 +223,23 @@ ns8250_flush(struct uart_bas *bas, int what)
fcr |= FCR_RCV_RST;
uart_setreg(bas, REG_FCR, fcr);
uart_barrier(bas);
+
+ /*
+ * Detect and work around emulated UARTs which don't implement the
+ * FCR register; on these systems we need to drain the FIFO since
+ * the flush we request doesn't happen. One such system is the
+ * Firecracker VMM, aka. the rust-vmm/vm-superio emulation code:
+ * https://github.com/rust-vmm/vm-superio/issues/83
+ */
+ lsr = uart_getreg(bas, REG_LSR);
+ if ((lsr & LSR_TEMT) && (what & UART_FLUSH_TRANSMITTER))
+ drain |= UART_DRAIN_TRANSMITTER;
+ if ((lsr & LSR_RXRDY) && (what & UART_FLUSH_RECEIVER))
+ drain |= UART_DRAIN_RECEIVER;
+ if (drain != 0) {
+ printf("ns8250: UART FCR is broken\n");
+ ns8250_drain(bas, drain);
+ }
}
static int