On Fri, Aug 29, 2025 at 12:27:13PM -0700, Stefano Stabellini wrote:
> On Thu, 28 Aug 2025, dmuk...@xen.org wrote:
> > From: Denis Mukhin <dmuk...@ford.com> 
> > 
> > Introduce a driver framework to abstract UART emulators in the hypervisor.
> > 
> > That allows for architecture-independent handling of virtual UARTs in the
> > console driver and simplifies enabling new UART emulators.
> > 
> > The framework is built under CONFIG_VUART_FRAMEWORK, which will be
> > automatically enabled once the user enables any UART emulator.
> > 
> > Current implementation supports maximum of one vUART of each kind per 
> > domain.
> > 
> > Use new domain_has_vuart() in the console driver code to check whether to
> > forward console input to the domain using vUART.
> > 
> > Enable console forwarding over vUART for hardware domains with a vUART. That
> > enables console forwarding to dom0 on x86, since console can be forwarded 
> > only
> > to Xen, dom0 and pvshim on x86 as of now.
> > 
> > Note: existing vUARTs are deliberately *not* hooked to the new framework to
> > minimize the scope of the patch: vpl011 (i.e. SBSA) emulator and "vuart" 
> > (i.e.
> > minimalistic MMIO-mapped dtuart for hwdoms on Arm) are kept unmodified.
> > 
> > No functional changes for non-x86 architectures.
> > 
> > Signed-off-by: Denis Mukhin <dmuk...@ford.com>
> > ---
> > Changes since v4:
> > - addressed feedback
> > - Link to v4: 
> > https://lore.kernel.org/xen-devel/20250731192130.3948419-3-dmuk...@ford.com/
> > ---
> >  xen/arch/arm/xen.lds.S         |   1 +
> >  xen/arch/ppc/xen.lds.S         |   1 +
> >  xen/arch/riscv/xen.lds.S       |   1 +
> >  xen/arch/x86/xen.lds.S         |   1 +
> >  xen/common/Kconfig             |   2 +
> >  xen/common/Makefile            |   1 +
> >  xen/common/emul/Kconfig        |   6 ++
> >  xen/common/emul/Makefile       |   1 +
> >  xen/common/emul/vuart/Kconfig  |   6 ++
> >  xen/common/emul/vuart/Makefile |   1 +
> >  xen/common/emul/vuart/vuart.c  | 156 +++++++++++++++++++++++++++++++++
> >  xen/common/keyhandler.c        |   3 +
> >  xen/drivers/char/console.c     |   6 +-
> >  xen/include/xen/serial.h       |   3 +
> >  xen/include/xen/vuart.h        | 116 ++++++++++++++++++++++++
> >  xen/include/xen/xen.lds.h      |  10 +++
> >  16 files changed, 314 insertions(+), 1 deletion(-)
> >  create mode 100644 xen/common/emul/Kconfig
> >  create mode 100644 xen/common/emul/Makefile
> >  create mode 100644 xen/common/emul/vuart/Kconfig
> >  create mode 100644 xen/common/emul/vuart/Makefile
> >  create mode 100644 xen/common/emul/vuart/vuart.c
> >  create mode 100644 xen/include/xen/vuart.h
> > 
> > diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
> > index db17ff1efa98..cd05b18770f4 100644
> > --- a/xen/arch/arm/xen.lds.S
> > +++ b/xen/arch/arm/xen.lds.S
> > @@ -58,6 +58,7 @@ SECTIONS
> >         *(.rodata)
> >         *(.rodata.*)
> >         VPCI_ARRAY
> > +       VUART_ARRAY
> >         *(.data.rel.ro)
> >         *(.data.rel.ro.*)
> >  
> > diff --git a/xen/arch/ppc/xen.lds.S b/xen/arch/ppc/xen.lds.S
> > index 1de0b77fc6b9..f9d4e5b0dcd8 100644
> > --- a/xen/arch/ppc/xen.lds.S
> > +++ b/xen/arch/ppc/xen.lds.S
> > @@ -52,6 +52,7 @@ SECTIONS
> >          *(.rodata)
> >          *(.rodata.*)
> >          VPCI_ARRAY
> > +        VUART_ARRAY
> >          *(.data.rel.ro)
> >          *(.data.rel.ro.*)
> >  
> > diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S
> > index edcadff90bfe..59dcaa5fef9a 100644
> > --- a/xen/arch/riscv/xen.lds.S
> > +++ b/xen/arch/riscv/xen.lds.S
> > @@ -47,6 +47,7 @@ SECTIONS
> >          *(.rodata)
> >          *(.rodata.*)
> >          VPCI_ARRAY
> > +        VUART_ARRAY
> >          *(.data.rel.ro)
> >          *(.data.rel.ro.*)
> >  
> > diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
> > index 966e514f2034..d877b93a6964 100644
> > --- a/xen/arch/x86/xen.lds.S
> > +++ b/xen/arch/x86/xen.lds.S
> > @@ -132,6 +132,7 @@ SECTIONS
> >         *(.rodata)
> >         *(.rodata.*)
> >         VPCI_ARRAY
> > +       VUART_ARRAY
> >         *(.data.rel.ro)
> >         *(.data.rel.ro.*)
> >  
> > diff --git a/xen/common/Kconfig b/xen/common/Kconfig
> > index 76f9ce705f7a..78a32b69e2b2 100644
> > --- a/xen/common/Kconfig
> > +++ b/xen/common/Kconfig
> > @@ -676,4 +676,6 @@ config PM_STATS
> >       Enable collection of performance management statistics to aid in
> >       analyzing and tuning power/performance characteristics of the system
> >  
> > +source "common/emul/Kconfig"
> > +
> >  endmenu
> > diff --git a/xen/common/Makefile b/xen/common/Makefile
> > index c316957fcb36..c0734480ee4b 100644
> > --- a/xen/common/Makefile
> > +++ b/xen/common/Makefile
> > @@ -11,6 +11,7 @@ obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o
> >  obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
> >  obj-$(CONFIG_IOREQ_SERVER) += dm.o
> >  obj-y += domain.o
> > +obj-y += emul/
> >  obj-y += event_2l.o
> >  obj-y += event_channel.o
> >  obj-$(CONFIG_EVTCHN_FIFO) += event_fifo.o
> > diff --git a/xen/common/emul/Kconfig b/xen/common/emul/Kconfig
> > new file mode 100644
> > index 000000000000..7c6764d1756b
> > --- /dev/null
> > +++ b/xen/common/emul/Kconfig
> > @@ -0,0 +1,6 @@
> > +menu "Domain Emulation Features"
> > +   visible if EXPERT
> > +
> > +source "common/emul/vuart/Kconfig"
> > +
> > +endmenu
> > diff --git a/xen/common/emul/Makefile b/xen/common/emul/Makefile
> > new file mode 100644
> > index 000000000000..ae0b575c3901
> > --- /dev/null
> > +++ b/xen/common/emul/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_VUART_FRAMEWORK) += vuart/
> > diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> > new file mode 100644
> > index 000000000000..ce1b976b7da7
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/Kconfig
> > @@ -0,0 +1,6 @@
> > +config VUART_FRAMEWORK
> > +   bool
> > +
> > +menu "UART Emulation"
> > +
> > +endmenu
> > diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
> > new file mode 100644
> > index 000000000000..97f792dc6641
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/Makefile
> > @@ -0,0 +1 @@
> > +obj-y += vuart.o
> > diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> > new file mode 100644
> > index 000000000000..7b277d00d5c7
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/vuart.c
> > @@ -0,0 +1,156 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * UART emulator framework.
> > + *
> > + * Copyright 2025 Ford Motor Company
> > + */
> > +
> > +#include <xen/err.h>
> > +#include <xen/sched.h>
> > +#include <xen/vuart.h>
> > +#include <xen/xvmalloc.h>
> > +
> > +#define for_each_emulator(e) \
> > +    for ( e = vuart_array_start; e < vuart_array_end; e++ )
> > +
> > +extern const struct vuart_emulator vuart_array_start[];
> > +extern const struct vuart_emulator vuart_array_end[];
> > +
> > +static const struct vuart_emulator *
> > +vuart_match_by_compatible(struct domain *d, const char *compat)
> > +{
> > +    const struct vuart_emulator *emulator;
> > +
> > +    if ( d->console.vuart )
> > +        return NULL;
> > +
> > +    for_each_emulator(emulator)
> > +        if ( emulator->compatible &&
> > +             !strncmp(emulator->compatible, compat,
> > +                      strlen(emulator->compatible)) )
> 
> strncmp will continue until the given count even if compat is shorter

I checked lib/strncmp.c. I'll flip the arguments, so that strncmp() will
return early if compat is shorter than emulator->compatible 

> 
> 
> > +            return emulator;
> > +
> > +    return NULL;
> > +}
> > +
> > +static struct vuart *vuart_find_by_console_permission(const struct domain 
> > *d)
> > +{
> > +    struct vuart *vuart = d->console.vuart;
> > +
> > +    ASSERT(d->console.input_allowed);
> 
> This ASSERT looks suspicious as we haven't even checked that vuart!=NULL
> yet. I would remove it

Ack

> 
> 
> > +    if ( !vuart || !vuart->emulator || !vuart->emulator->put_rx ||
> > +         !(vuart->flags & VUART_CONSOLE_INPUT))
> > +        return NULL;
> > +
> > +    return vuart;
> > +}
> > +
> > +struct vuart *vuart_find_by_io_range(struct domain *d, unsigned long addr,
> > +                                     unsigned long size)
> > +{
> > +    struct vuart *vuart = d->console.vuart;
> > +
> > +    if ( !vuart || !vuart->info )
> > +        return NULL;
> 
> You might as well call vuart_find_by_console_permission that has all the
> checks already

vuart_find_by_io_range() is used in the I/O hook to find the vUART by its
I/O port range.

Using vuart_find_by_console_permission() here may be problematic if the
vUART does not support/enable console forwarding.

I'll keep this check as is for now.

> 
> > +
> > +    if ( addr >= vuart->info->base_addr &&
> > +         addr + size - 1 <= vuart->info->base_addr + vuart->info->size - 1 
> > )
> > +        return vuart;
> > +
> > +    return NULL;
> > +}
> > +
> > +int vuart_init(struct domain *d, struct vuart_info *info)
> > +{
> > +    const struct vuart_emulator *emulator;
> > +    struct vuart *vuart;
> > +    int rc;
> > +
> > +    emulator = vuart_match_by_compatible(d, info->compatible);
> > +    if ( !emulator )
> > +        return -ENODEV;
> > +
> > +    vuart = xzalloc(typeof(*vuart));
> > +    if ( !vuart )
> > +        return -ENOMEM;
> > +
> > +    vuart->info = xvzalloc(typeof(*info));
> > +    if ( !vuart->info )
> > +    {
> > +        rc = -ENOMEM;
> > +        goto err_out;
> > +    }
> > +    memcpy(vuart->info, info, sizeof(*info));
> 
> one thing to note is that the fields (strings) compatible and name are
> copied by address, I am not if it is OK but FYI

Thanks, will update vuart_info struct.
> 
> 
> > +    vuart->vdev = emulator->alloc(d, vuart->info);
> > +    if ( IS_ERR(vuart->vdev) )
> > +    {
> > +        rc = PTR_ERR(vuart->vdev);
> > +        goto err_out;
> 
> this path leads to vuart->info not being freed

Thanks

> 
> 
> > +    }
> > +
> > +    vuart->emulator = emulator;
> > +    vuart->owner = d;
> > +    vuart->flags |= VUART_CONSOLE_INPUT;
> > +
> > +    d->console.input_allowed = true;
> > +    d->console.vuart = vuart;
> > +
> > +    return 0;
> > +
> > + err_out:
> > +    XVFREE(vuart);
> > +    return rc;
> > +}
> > +
> > +/*
> > + * Release any resources taken by UART emulators.
> > + *
> > + * NB: no flags are cleared, since currently exit() is called only during
> > + * domain destroy.
> > + */
> > +void vuart_deinit(struct domain *d)
> > +{
> > +    struct vuart *vuart = d->console.vuart;
> > +
> > +    if ( vuart )
> > +    {
> > +        vuart->emulator->free(vuart);
> 
> should we pass vuart or vuart->vdev here? The emulator state is supposed
> to be vuart->vdev?

That should be vuart->vdev; thanks!

> 
> > +        XVFREE(vuart->info);
> > +    }
> > +
> > +    XVFREE(d->console.vuart);
> > +}
> > +
> > +void vuart_dump_state(const struct domain *d)
> > +{
> > +    struct vuart *vuart = d->console.vuart;
> > +
> > +    if ( vuart )
> > +        vuart->emulator->dump_state(vuart);
> 
> also here vuart->vdev?

Ack

> 
> 
> > +}
> > +
> > +/*
> > + * Put character to the *first* suitable emulated UART's FIFO.
> > + */
> > +int vuart_put_rx(struct domain *d, char c)
> > +{
> > +    struct vuart *vuart = vuart_find_by_console_permission(d);
> > +
> > +    return vuart ? vuart->emulator->put_rx(vuart, c) : -ENODEV;
> 
> and here?

Ack

> 
> 
> > +}
> > +
> > +bool domain_has_vuart(const struct domain *d)
> > +{
> > +    return vuart_find_by_console_permission(d);
> > +}
> > +
> > +/*
> > + * Local variables:
> > + * mode: C
> > + * c-file-style: "BSD"
> > + * c-basic-offset: 4
> > + * indent-tabs-mode: nil
> > + * End:
> > + */
> > diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c
> > index cb6df2823b00..156e64d9eb58 100644
> > --- a/xen/common/keyhandler.c
> > +++ b/xen/common/keyhandler.c
> > @@ -22,6 +22,7 @@
> >  #include <xen/mm.h>
> >  #include <xen/watchdog.h>
> >  #include <xen/init.h>
> > +#include <xen/vuart.h>
> >  #include <asm/div64.h>
> >  
> >  static unsigned char keypress_key;
> > @@ -352,6 +353,8 @@ static void cf_check dump_domains(unsigned char key)
> >                             v->periodic_period / 1000000);
> >              }
> >          }
> > +
> > +        vuart_dump_state(d);
> >      }
> >  
> >      for_each_domain ( d )
> > diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
> > index 9bd5b4825da6..d5164897a776 100644
> > --- a/xen/drivers/char/console.c
> > +++ b/xen/drivers/char/console.c
> > @@ -33,6 +33,7 @@
> >  #include <asm/setup.h>
> >  #include <xen/sections.h>
> >  #include <xen/consoled.h>
> > +#include <xen/vuart.h>
> >  
> >  #ifdef CONFIG_X86
> >  #include <asm/guest.h>
> > @@ -596,11 +597,12 @@ static void __serial_rx(char c)
> >      if ( !d )
> >          return;
> >  
> > -    if ( is_hardware_domain(d) )
> > +    if ( is_hardware_domain(d) && !domain_has_vuart(d) )
> >      {
> >          /*
> >           * Deliver input to the hardware domain buffer, unless it is
> >           * already full.
> > +         * NB: must be the first check: hardware domain may have emulated 
> > UART.
> >           */
> >          if ( (serial_rx_prod - serial_rx_cons) != SERIAL_RX_SIZE )
> >              serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
> > @@ -611,6 +613,8 @@ static void __serial_rx(char c)
> >           */
> >          send_global_virq(VIRQ_CONSOLE);
> >      }
> > +    else if ( domain_has_vuart(d) )
> > +        rc = vuart_put_rx(d, c);
> >  #ifdef CONFIG_SBSA_VUART_CONSOLE
> >      else
> >          /* Deliver input to the emulated UART. */
> > diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h
> > index 8e1844555208..d7e81f098359 100644
> > --- a/xen/include/xen/serial.h
> > +++ b/xen/include/xen/serial.h
> > @@ -36,6 +36,9 @@ struct vuart_info {
> >      unsigned long data_off;     /* Data register offset */
> >      unsigned long status_off;   /* Status register offset */
> >      unsigned long status;       /* Ready status value */
> > +    unsigned int irq;           /* Interrupt */
> > +    const char *compatible;     /* Compatible string */
> > +    const char *name;           /* User-friendly name */
> >  };
> >  
> >  struct serial_port {
> > diff --git a/xen/include/xen/vuart.h b/xen/include/xen/vuart.h
> > new file mode 100644
> > index 000000000000..ca025b4179be
> > --- /dev/null
> > +++ b/xen/include/xen/vuart.h
> > @@ -0,0 +1,116 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * UART emulator framework.
> > + *
> > + * Copyright 2025 Ford Motor Company
> > + */
> > +
> > +#ifndef XEN_VUART_H
> > +#define XEN_VUART_H
> > +
> > +#include <xen/serial.h>
> > +#include <public/xen.h>
> > +
> > +struct vuart_emulator;
> > +
> > +enum {
> > +    VUART_CONSOLE_INPUT = 0U << 1, /** Physical console input forwarding. 
> > */
> 
> code style: single *

Ack

> This flag is zero, is that intended?

Will fix, thanks

> 
> 
> > +};
> > +
> > +
> > +/*
> > + * FIXME: #ifdef is temporary to avoid clash with
> > + *   arch/arm/include/asm/domain.h
> > + */
> > +#ifdef CONFIG_VUART_FRAMEWORK
> > +struct vuart {
> > +    const struct vuart_emulator *emulator;
> > +    struct vuart_info *info;
> > +    struct domain *owner;
> > +    uint32_t flags;
> > +    void *vdev;
> > +};
> > +#endif
> > +
> > +struct vuart_emulator {
> > +    /* UART compatible string. Cannot be NULL or empty. */
> > +    const char *compatible;
> > +
> > +    /*
> > +     * Allocate emulated UART state (RX/TX FIFOs, locks, initialize 
> > registers,
> > +     * hook I/O handlers, etc.)
> > +     * Cannot be NULL.
> > +     */
> > +    void *(*alloc)(struct domain *d, const struct vuart_info *info);
> > +
> > +    /*
> > +     * Release resources used to emulate UART state (flush RX/TX FIFOs, 
> > unhook
> > +     * I/O handlers, etc.).
> > +     * Cannot be NULL.
> > +     */
> > +    void (*free)(void *arg);
> > +
> > +    /*
> > +     * Print emulated UART state, including registers, on the console.
> > +     * Can be NULL.
> > +     */
> > +    void (*dump_state)(void *arg);
> > +
> > +    /*
> > +     * Place character to the emulated RX FIFO.
> > +     * Used to forward physical console input to the guest OS.
> > +     * Can be NULL.
> > +     */
> > +    int (*put_rx)(void *arg, char c);
> > +};
> > +
> > +#define VUART_REGISTER(name, x) \
> > +    static const struct vuart_emulator name##_entry \
> > +        __used_section(".data.rel.ro.vuart") = x
> > +
> > +struct vuart *vuart_find_by_io_range(struct domain *d,
> > +                                     unsigned long base_addr,
> > +                                     unsigned long size);
> > +
> > +int vuart_put_rx(struct domain *d, char c);
> > +
> > +#ifdef CONFIG_VUART_FRAMEWORK
> > +
> > +int vuart_init(struct domain *d, struct vuart_info *info);
> > +void vuart_deinit(struct domain *d);
> > +void vuart_dump_state(const struct domain *d);
> > +bool domain_has_vuart(const struct domain *d);
> > +
> > +#else
> > +
> > +static inline int vuart_init(struct domain *d, struct vuart_info *info)
> > +{
> > +    return 0;
> > +}
> > +
> > +static inline void vuart_deinit(struct domain *d)
> > +{
> > +}
> > +
> > +static inline void vuart_dump_state(const struct domain *d)
> > +{
> > +}
> > +
> > +static inline bool domain_has_vuart(const struct domain *d)
> > +{
> > +    return false;
> > +}
> > +
> > +#endif /* CONFIG_VUART_FRAMEWORK */
> > +
> > +#endif /* XEN_VUART_H */
> > +
> > +/*
> > + * Local variables:
> > + * mode: C
> > + * c-file-style: "BSD"
> > + * c-basic-offset: 4
> > + * indent-tabs-mode: nil
> > + * End:
> > + */
> > +
> > diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
> > index b126dfe88792..2d65f32ddad3 100644
> > --- a/xen/include/xen/xen.lds.h
> > +++ b/xen/include/xen/xen.lds.h
> > @@ -194,4 +194,14 @@
> >  #define VPCI_ARRAY
> >  #endif
> >  
> > +#ifdef CONFIG_VUART_FRAMEWORK
> > +#define VUART_ARRAY              \
> > +       . = ALIGN(POINTER_ALIGN); \
> > +       vuart_array_start = .;    \
> > +       *(.data.rel.ro.vuart)     \
> > +       vuart_array_end = .;
> > +#else
> > +#define VUART_ARRAY
> > +#endif
> > +
> >  #endif /* __XEN_LDS_H__ */
> > -- 
> > 2.51.0
> > 

Reply via email to