From: Denis Mukhin <dmuk...@ford.com> Add RBR register emulation to the I/O port handlder.
Add RX FIFO management code since RBR depends on RX FIFO. RX FIFO is not emulated as per UART specs for simplicity (not need to emulate baud rate). Emulator does not emulate NS8250 (no FIFO), NS16550a (16 bytes) or NS16750 (64 bytes). RX FIFO is emulated by means of using xencons_interface which conveniently provides primitives for buffer management and later can be used for inter-domain communication similarly to vpl011. Add UART_LSR_DR handling since it depends on RBR register access. Finally, implement put_rx() vUART hook for placing a character into the emulated RX FIFO from console driver. That implements physical console forwarding to the guest OS over emulated NS16550. Signed-off-by: Denis Mukhin <dmuk...@ford.com> --- Changes since v5: - new patch - Link to v5 (both THR/RBR): https://lore.kernel.org/xen-devel/20250828235409.2835815-7-dmuk...@ford.com/ - Link to v5 (rx_put): https://lore.kernel.org/xen-devel/20250828235409.2835815-12-dmuk...@ford.com/ --- xen/common/emul/vuart/ns16x50.c | 121 +++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c index a8ec9f6c3a04..cac5128f0573 100644 --- a/xen/common/emul/vuart/ns16x50.c +++ b/xen/common/emul/vuart/ns16x50.c @@ -86,6 +86,68 @@ struct vuart_ns16x50 { struct xencons_interface cons; /* Emulated RX/TX FIFOs */ }; +static bool ns16x50_fifo_rx_empty(const struct vuart_ns16x50 *vdev) +{ + const struct xencons_interface *cons = &vdev->cons; + + return cons->in_prod == cons->in_cons; +} + +static bool ns16x50_fifo_rx_full(const struct vuart_ns16x50 *vdev) +{ + const struct xencons_interface *cons = &vdev->cons; + + return cons->in_prod - cons->in_cons == ARRAY_SIZE(cons->in); +} + +static void ns16x50_fifo_rx_reset(struct vuart_ns16x50 *vdev) +{ + struct xencons_interface *cons = &vdev->cons; + + cons->in_cons = cons->in_prod; +} + +/* + * Transfer character from RX FIFO and return the RX FIFO status after the + * transfer. + */ +static int ns16x50_fifo_rx_getchar(struct vuart_ns16x50 *vdev, uint8_t *ptr) +{ + struct xencons_interface *cons = &vdev->cons; + + if ( ns16x50_fifo_rx_empty(vdev) ) + return -ENODATA; + + *ptr = cons->in[MASK_XENCONS_IDX(cons->in_cons, cons->in)]; + cons->in_cons++; + + return ns16x50_fifo_rx_empty(vdev) ? -ENODATA : 0; +} + +static int ns16x50_fifo_rx_putchar(struct vuart_ns16x50 *vdev, char c) +{ + struct xencons_interface *cons = &vdev->cons; + int rc; + + /* + * FIFO-less 8250/16450 UARTs: newly arrived word overwrites the contents + * of the THR. + */ + if ( ns16x50_fifo_rx_full(vdev) ) + { + ns16x50_debug(vdev, "RX FIFO full; resetting\n"); + ns16x50_fifo_rx_reset(vdev); + rc = -ENOSPC; + } + else + rc = 0; + + cons->in[MASK_XENCONS_IDX(cons->in_prod, cons->in)] = c; + cons->in_prod++; + + return rc; +} + static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev) { return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0; @@ -98,7 +160,7 @@ static bool cf_check ns16x50_iir_check_lsi(const struct vuart_ns16x50 *vdev) static bool cf_check ns16x50_iir_check_rda(const struct vuart_ns16x50 *vdev) { - return false; + return !ns16x50_fifo_rx_empty(vdev); } static bool cf_check ns16x50_iir_check_thr(const struct vuart_ns16x50 *vdev) @@ -360,6 +422,17 @@ static int ns16x50_io_read8( { switch ( reg ) { + case UART_RBR: + /* NB: do not forget to clear overrun condition */ + regs[UART_LSR] &= ~UART_LSR_OE; + + if ( ns16x50_fifo_rx_getchar(vdev, &val) ) + regs[UART_LSR] &= ~UART_LSR_DR; + else + regs[UART_LSR] |= UART_LSR_DR; + + break; + case UART_IER: val = regs[UART_IER]; break; @@ -610,13 +683,57 @@ static void cf_check ns16x50_free(void *arg) xvfree(arg); } +static int cf_check ns16x50_put_rx(void *arg, char ch) +{ + struct vuart_ns16x50 *vdev = arg; + uint8_t *regs; + uint8_t dlab; + int rc = -EBUSY; + + spin_lock(&vdev->lock); + + dlab = ns16x50_dlab_get(vdev); + regs = vdev->regs; + + if ( dlab ) + ns16x50_debug(vdev, "THR/RBR access disabled: DLAB=1\n"); + else if ( regs[UART_MCR] & UART_MCR_LOOP ) + ns16x50_debug(vdev, "THR/RBR access disabled: loopback mode\n"); + else + { + const struct domain *d = vdev->owner; + + /* + * Echo the user input on Xen console iff Xen console input is owned + * by ns16x50 domain. + * NB: use 'console_timestamps=none' to disable Xen timestamps. + */ + if ( is_console_printable(ch) ) + guest_printk(d, "%c", ch); + + if ( ns16x50_fifo_rx_putchar(vdev, ch) ) + regs[UART_LSR] |= UART_LSR_OE; + + regs[UART_LSR] |= UART_LSR_DR; + + /* TODO: check FCR when to fire an interrupt */ + ns16x50_irq_check(vdev); + + rc = 0; + } + + spin_unlock(&vdev->lock); + + return rc; +} + #define ns16x50_emulator \ { \ .compatible = "ns16550", \ .alloc = ns16x50_alloc, \ .free = ns16x50_free, \ .dump_state = NULL, \ - .put_rx = NULL, \ + .put_rx = ns16x50_put_rx, \ } VUART_REGISTER(ns16x50, ns16x50_emulator); -- 2.51.0