On Thu, 28 Aug 2025, dmuk...@xen.org wrote:
> 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;

The signed integer to char conversion here is not great from a MISRA
perspective. I think it would be better to keep rc as success/failure
return value and take the read char as a pointer parameter.


> +}
> +
> +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);

I am not sure about this.. why resetting the out_prod index? In theory
it could keep increasing until wrap around and still go forward thanks
to the mask


> +}
> +
> +/*
> + * 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;
> +
> + UART_IIR_THR   ASSERT(cons->out_prod < ARRAY_SIZE(cons->out));
> +    cons->out[cons->out_prod] = '\0';

should use MASK_XENCONS_IDX to access the array


> +    cons->out_prod++;
> +
> +    guest_printk(d, guest_prefix "%s", cons->out);
> +
> +    ns16x50_fifo_tx_reset(vdev);

set UART_IIR_THR and call ns16x50_irq_check ?


> +}
> +
> +/*
> + * 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;

should use MASK_XENCONS_IDX to access the array


> +        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);

should UART_IIR_THR be cleared here?


> +            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;

Empty fifo check?


> +            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
> 

Reply via email to