From: Denis Mukhin <dmuk...@ford.com> Add RBR/THR registers emulation to the I/O port handlder.
Also, add RX/TX FIFO management code since RBR depends on RX FIFO and THR depends on TX FIFO. FIFOs are 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). FIFOs are 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. Signed-off-by: Denis Mukhin <dmuk...@ford.com> --- Changes since v4: - new patch --- xen/common/emul/vuart/ns16x50.c | 135 ++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c index 20597cc36b35..efb2f4c6441c 100644 --- a/xen/common/emul/vuart/ns16x50.c +++ b/xen/common/emul/vuart/ns16x50.c @@ -92,6 +92,119 @@ static bool ns16x50_fifo_rx_empty(const struct vuart_ns16x50 *vdev) 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; +} + +static int ns16x50_fifo_rx_getchar(struct vuart_ns16x50 *vdev) +{ + struct xencons_interface *cons = &vdev->cons; + int rc; + + if ( ns16x50_fifo_rx_empty(vdev) ) + { + ns16x50_debug(vdev, "RX FIFO empty\n"); + rc = -ENODATA; + } + else + { + rc = cons->in[MASK_XENCONS_IDX(cons->in_cons, cons->in)]; + cons->in_cons++; + } + + return rc; +} + +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 bool ns16x50_fifo_tx_empty(const struct vuart_ns16x50 *vdev) +{ + const struct xencons_interface *cons = &vdev->cons; + + return cons->out_prod == cons->out_cons; +} + +static void ns16x50_fifo_tx_reset(struct vuart_ns16x50 *vdev) +{ + struct xencons_interface *cons = &vdev->cons; + + cons->out_prod = 0; + ASSERT(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; + + if ( ns16x50_fifo_tx_empty(vdev) ) + return; + + ASSERT(cons->out_prod < ARRAY_SIZE(cons->out)); + cons->out[cons->out_prod] = '\0'; + cons->out_prod++; + + guest_printk(d, guest_prefix "%s", cons->out); + + ns16x50_fifo_tx_reset(vdev); +} + +/* + * 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 ( ch != '\0' ) + { + cons->out[cons->out_prod] = ch; + cons->out_prod++; + } + + if ( cons->out_prod == ARRAY_SIZE(cons->out) - 1 || + ch == '\n' || ch == '\0' ) + ns16x50_fifo_tx_flush(vdev); +} + static inline uint8_t cf_check ns16x50_dlab_get(const struct vuart_ns16x50 *vdev) { return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0; @@ -228,6 +341,16 @@ static int ns16x50_io_write8( { switch ( reg ) { + case UART_THR: + if ( regs[UART_MCR] & UART_MCR_LOOP ) + rc = ns16x50_fifo_rx_putchar(vdev, val); + else + ns16x50_fifo_tx_putchar(vdev, val); + + rc = 0; + + break; + case UART_IER: /* * NB: Make sure THR interrupt is re-triggered once guest OS @@ -312,6 +435,14 @@ static int ns16x50_io_read8( else { switch ( reg ) { + case UART_RBR: + rc = ns16x50_fifo_rx_getchar(vdev); + if ( rc >= 0 ) + val = (uint8_t)rc; + + rc = 0; + break; + case UART_IER: val = regs[UART_IER]; break; @@ -499,6 +630,10 @@ static void cf_check ns16x50_deinit(void *arg) struct vuart_ns16x50 *vdev = arg; ASSERT(vdev); + + spin_lock(&vdev->lock); + ns16x50_fifo_tx_flush(vdev); + spin_unlock(&vdev->lock); } static void * cf_check ns16x50_alloc(struct domain *d, const struct vuart_info *info) -- 2.51.0