diff options
author | Colin Percival <cperciva@FreeBSD.org> | 2022-10-13 18:01:03 +0000 |
---|---|---|
committer | Colin Percival <cperciva@FreeBSD.org> | 2022-10-18 06:02:21 +0000 |
commit | c4b68e7e53bb352be3fa16995b99764c03097e66 (patch) | |
tree | 512a9b2d9507dd64309b7cb0e99f8d7bb38b4c6e /sys | |
parent | 782105f7c89821552304245d71e107b867a3e934 (diff) | |
download | src-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
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/uart/uart_dev_ns8250.c | 19 |
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 |