[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)


  • To: dmkhn@xxxxxxxxx
  • From: Jan Beulich <jbeulich@xxxxxxxx>
  • Date: Mon, 4 Aug 2025 12:53:36 +0200
  • Autocrypt: addr=jbeulich@xxxxxxxx; keydata= xsDiBFk3nEQRBADAEaSw6zC/EJkiwGPXbWtPxl2xCdSoeepS07jW8UgcHNurfHvUzogEq5xk hu507c3BarVjyWCJOylMNR98Yd8VqD9UfmX0Hb8/BrA+Hl6/DB/eqGptrf4BSRwcZQM32aZK 7Pj2XbGWIUrZrd70x1eAP9QE3P79Y2oLrsCgbZJfEwCgvz9JjGmQqQkRiTVzlZVCJYcyGGsD /0tbFCzD2h20ahe8rC1gbb3K3qk+LpBtvjBu1RY9drYk0NymiGbJWZgab6t1jM7sk2vuf0Py O9Hf9XBmK0uE9IgMaiCpc32XV9oASz6UJebwkX+zF2jG5I1BfnO9g7KlotcA/v5ClMjgo6Gl MDY4HxoSRu3i1cqqSDtVlt+AOVBJBACrZcnHAUSuCXBPy0jOlBhxPqRWv6ND4c9PH1xjQ3NP nxJuMBS8rnNg22uyfAgmBKNLpLgAGVRMZGaGoJObGf72s6TeIqKJo/LtggAS9qAUiuKVnygo 3wjfkS9A3DRO+SpU7JqWdsveeIQyeyEJ/8PTowmSQLakF+3fote9ybzd880fSmFuIEJldWxp Y2ggPGpiZXVsaWNoQHN1c2UuY29tPsJgBBMRAgAgBQJZN5xEAhsDBgsJCAcDAgQVAggDBBYC AwECHgECF4AACgkQoDSui/t3IH4J+wCfQ5jHdEjCRHj23O/5ttg9r9OIruwAn3103WUITZee e7Sbg12UgcQ5lv7SzsFNBFk3nEQQCACCuTjCjFOUdi5Nm244F+78kLghRcin/awv+IrTcIWF hUpSs1Y91iQQ7KItirz5uwCPlwejSJDQJLIS+QtJHaXDXeV6NI0Uef1hP20+y8qydDiVkv6l IreXjTb7DvksRgJNvCkWtYnlS3mYvQ9NzS9PhyALWbXnH6sIJd2O9lKS1Mrfq+y0IXCP10eS FFGg+Av3IQeFatkJAyju0PPthyTqxSI4lZYuJVPknzgaeuJv/2NccrPvmeDg6Coe7ZIeQ8Yj t0ARxu2xytAkkLCel1Lz1WLmwLstV30g80nkgZf/wr+/BXJW/oIvRlonUkxv+IbBM3dX2OV8 AmRv1ySWPTP7AAMFB/9PQK/VtlNUJvg8GXj9ootzrteGfVZVVT4XBJkfwBcpC/XcPzldjv+3 HYudvpdNK3lLujXeA5fLOH+Z/G9WBc5pFVSMocI71I8bT8lIAzreg0WvkWg5V2WZsUMlnDL9 mpwIGFhlbM3gfDMs7MPMu8YQRFVdUvtSpaAs8OFfGQ0ia3LGZcjA6Ik2+xcqscEJzNH+qh8V m5jjp28yZgaqTaRbg3M/+MTbMpicpZuqF4rnB0AQD12/3BNWDR6bmh+EkYSMcEIpQmBM51qM EKYTQGybRCjpnKHGOxG0rfFY1085mBDZCH5Kx0cl0HVJuQKC+dV2ZY5AqjcKwAxpE75MLFkr wkkEGBECAAkFAlk3nEQCGwwACgkQoDSui/t3IH7nnwCfcJWUDUFKdCsBH/E5d+0ZnMQi+G0A nAuWpQkjM1ASeQwSHEeAWPgskBQL
  • Cc: andrew.cooper3@xxxxxxxxxx, anthony.perard@xxxxxxxxxx, julien@xxxxxxx, michal.orzel@xxxxxxx, roger.pau@xxxxxxxxxx, sstabellini@xxxxxxxxxx, dmukhin@xxxxxxxx, xen-devel@xxxxxxxxxxxxxxxxxxxx
  • Delivery-date: Mon, 04 Aug 2025 10:54:00 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

On 31.07.2025 21:22, dmkhn@xxxxxxxxx wrote:
> From: Denis Mukhin <dmukhin@xxxxxxxx> 
> 
> Add initial in-hypervisor emulator for NS8250/NS16x50-compatible UARTs under
> CONFIG_VUART_NS16550 for x86 port of Xen.
> 
> x86 port of Xen lacks vUART facility similar to Arm's SBSA emulator to support
> x86 guest OS bring up in the embedded setups.
> 
> In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
> early guest firmware and/or OS bringup debugging, because it eliminates
> dependency on the external emulator (qemu) being operational by the time
> domains are created.
> 
> The emulator also allows to forward the physical console input to the x86
> domain which is useful when a system has only one physical UART for early
> debugging and this UART is owned by Xen. Such functionality is limited to dom0
> use currently.
> 
> By default, CONFIG_VUART_NS16550 enables emulation of NS16550 at I/O port
> 0x3f8, IRQ#4 in guest OS (legacy COM1).
> 
> Legacy COM resources can be selected at built-time and cannot be configured
> per-domain via .cfg or DT yet.
> 
> Introduce new emulation flag for virtual UART on x86 and plumb it through
> domain creation code so NS16550 emulator can be instantiated properly.
> 
> Please refer to the NS16550 emulator code for full list of limitations.
> 
> Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx>
> ---
> Changes since v3:
> - feedback addressed
> - adjusted to new vUART framework APIs
> - Link to v3: 
> https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-21-c5d36b31d66c@xxxxxxxx/
> ---
>  xen/arch/x86/hvm/hvm.c                |    9 +
>  xen/arch/x86/include/asm/domain.h     |    4 +-
>  xen/arch/x86/include/asm/hvm/domain.h |    4 +
>  xen/common/emul/vuart/Kconfig         |   48 ++
>  xen/common/emul/vuart/Makefile        |    1 +
>  xen/common/emul/vuart/vuart-ns16550.c | 1009 +++++++++++++++++++++++++
>  xen/common/emul/vuart/vuart.c         |    4 +
>  xen/include/public/arch-x86/xen.h     |    4 +-
>  xen/include/xen/resource.h            |    3 +
>  9 files changed, 1084 insertions(+), 2 deletions(-)
>  create mode 100644 xen/common/emul/vuart/vuart-ns16550.c

Overall I think this patch is too large to sensibly review. Surely base 
structure
and then (incrementally) fleshing out of the hooks can be separated from one
another?

> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -31,6 +31,7 @@
>  #include <xen/nospec.h>
>  #include <xen/vm_event.h>
>  #include <xen/console.h>
> +#include <xen/vuart.h>
>  #include <asm/shadow.h>
>  #include <asm/hap.h>
>  #include <asm/current.h>
> @@ -702,6 +703,10 @@ int hvm_domain_initialise(struct domain *d,
>      if ( rc != 0 )
>          goto fail1;
>  
> +    rc = vuart_init(d, NULL);
> +    if ( rc != 0 )
> +        goto out_vioapic_deinit;
> +
>      stdvga_init(d);
>  
>      rtc_init(d);
> @@ -725,6 +730,8 @@ int hvm_domain_initialise(struct domain *d,
>      return 0;
>  
>   fail2:
> +    vuart_deinit(d);
> + out_vioapic_deinit:
>      vioapic_deinit(d);
>   fail1:
>      if ( is_hardware_domain(d) )

Would be better if vuart_deinit() was idempotent, and hence could be called
unconditionally here.

> @@ -787,6 +794,8 @@ void hvm_domain_destroy(struct domain *d)
>      if ( hvm_funcs.domain_destroy )
>          alternative_vcall(hvm_funcs.domain_destroy, d);
>  
> +    vuart_deinit(d);

You require a fair level of idempotency already anyway, as a domain may not
have any vUART, so this call already needs to be "capabale" of doing nothing.

> --- a/xen/arch/x86/include/asm/hvm/domain.h
> +++ b/xen/arch/x86/include/asm/hvm/domain.h
> @@ -149,6 +149,10 @@ struct hvm_domain {
>  #ifdef CONFIG_MEM_SHARING
>      struct mem_sharing_domain mem_sharing;
>  #endif
> +
> +#ifdef CONFIG_VUART_NS16550
> +    void *vuart; /* Virtual UART handle. */
> +#endif
>  };

With your framework you allow for multiple vUART drivers. Either the field
looks misnamed or the CONFIG_* option checked is the wrong one.

Also, why's this x86-specific? NS16550s can exist anywhere, can't they?
(The present, but presumably temporary tying to x86 looks to be the use of
I/O ports.)

> --- a/xen/common/emul/vuart/Kconfig
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -3,4 +3,52 @@ config HAS_VUART
>  
>  menu "UART Emulation"
>  
> +config VUART_NS16550
> +     bool "NS16550-compatible UART Emulation" if EXPERT
> +     depends on X86 && HVM
> +     select HAS_VUART
> +     help
> +       In-hypervisor NS16550/NS16x50 UART emulation.
> +
> +       Only legacy PC I/O ports are emulated.
> +
> +       This is strictly for testing purposes (such as early HVM guest 
> console),
> +       and not appropriate for use in production.
> +
> +choice VUART_NS16550_PC
> +     prompt "IBM PC COM resources"
> +     depends on VUART_NS16550
> +     default VUART_NS16550_PC_COM1
> +     help
> +       Default emulated NS16550 resources.
> +
> +config VUART_NS16550_PC_COM1
> +     bool "COM1 (I/O port 0x3f8, IRQ#4)"
> +
> +config VUART_NS16550_PC_COM2
> +     bool "COM2 (I/O port 0x2f8, IRQ#3)"
> +
> +config VUART_NS16550_PC_COM3
> +     bool "COM3 (I/O port 0x3e8, IRQ#4)"
> +
> +config VUART_NS16550_PC_COM4
> +     bool "COM4 (I/O port 0x2e8, IRQ#3)"
> +
> +endchoice
> +
> +config VUART_NS16550_LOG_LEVEL
> +     int "UART emulator verbosity level"
> +     range 0 3
> +     default "1"
> +     depends on VUART_NS16550
> +     help
> +       Set the default log level of UART emulator.
> +       See include/xen/config.h for more details.

For someone merely running kconfig but not otherwise knowing the sources,
this isn't an overly helful pointer. But I question the need for such a
control anyway, and I think I did say so already before.

> +config VUART_NS16550_DEBUG
> +     bool "UART emulator development debugging"
> +     depends on VUART_NS16550

&& DEBUG ?

> --- a/xen/common/emul/vuart/Makefile
> +++ b/xen/common/emul/vuart/Makefile
> @@ -1 +1,2 @@
>  obj-$(CONFIG_HAS_VUART) += vuart.o
> +obj-$(CONFIG_VUART_NS16550) += vuart-ns16550.o

I don't think files in this directory need a vuart- name prefix.

> --- /dev/null
> +++ b/xen/common/emul/vuart/vuart-ns16550.c
> @@ -0,0 +1,1009 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * NS16550-compatible UART Emulator.
> + *
> + * See:
> + * - Serial and UART Tutorial:
> + *     
> https://download.freebsd.org/doc/en/articles/serial-uart/serial-uart_en.pdf
> + * - UART w/ 16 byte FIFO:
> + *     https://www.ti.com/lit/ds/symlink/tl16c550c.pdf
> + * - UART w/ 64 byte FIFO:
> + *     https://www.ti.com/lit/ds/symlink/tl16c750.pdf
> + *
> + * Limitations:
> + * - Only x86;
> + * - Only HVM domains support (build-time), PVH domains are not supported 
> yet;
> + * - Only legacy COM{1,2,3,4} resources via Kconfig, custom I/O ports/IRQs
> + *   are not supported;
> + * - Only Xen console as a backend, no inter-domain communication (similar to
> + *   vpl011 on Arm);
> + * - Only 8n1 emulation (8-bit data, no parity, 1 stop bit);
> + * - No toolstack integration;
> + * - No baud rate emulation (reports 115200 baud to the guest OS);
> + * - No FIFO-less mode emulation;
> + * - No RX FIFO interrupt moderation (FCR) emulation;
> + * - No integration w/ VM snapshotting (HVM_REGISTER_SAVE_RESTORE() and
> + *   friends);
> + * - No ISA IRQ sharing allowed;
> + * - No MMIO-based UART emulation.
> + */
> +
> +#define pr_prefix               "ns16550"
> +#define pr_fmt(fmt)             pr_prefix ": " fmt
> +#define pr_log_level            CONFIG_VUART_NS16550_LOG_LEVEL
> +
> +#include <xen/8250-uart.h>
> +#include <xen/console.h>
> +#include <xen/iocap.h>
> +#include <xen/ioreq.h>
> +#include <xen/resource.h>
> +#include <xen/vuart.h>
> +#include <xen/xvmalloc.h>
> +
> +#include <public/io/console.h>

Except for cases where Xen itself runs as a guest, I don't think any of these
headers should be used in Xen sources. If I'm not mistaken, ...

> +/*
> + * Virtual NS16550 device state.
> + */
> +struct vuart_ns16550 {
> +    struct xencons_interface cons;      /* Emulated RX/TX FIFOs */

... this also isn't to communicate with some remote, but merely to use some
of the fields conveniently.

> +    uint8_t regs[NS16550_EMU_REGS_NUM]; /* Emulated registers */
> +    unsigned int irq;                   /* Emulated IRQ# */
> +    uint64_t io_addr;                   /* Emulated I/O region base address 
> */
> +    uint64_t io_size;                   /* Emulated I/O region size */

These are huge; for the size that's true even if considering future MMIO-
based emulation.

> +    const char *name;                   /* Device name */
> +    struct domain *owner;               /* Owner domain */
> +    spinlock_t lock;                    /* Protection */
> +};
> +
> +/*
> + * Virtual device description.
> + */
> +struct virtdev_desc {
> +    const char *name;
> +    const struct resource *res;
> +};
> +
> +/*
> + * Legacy IBM PC NS16550 resources.
> + * There are only 4 I/O port ranges, hardcoding all of them here.
> + */
> +static const struct virtdev_desc x86_pc_uarts[4] = {
> +    [0] = {
> +        .name = "COM1",
> +        .res = (const struct resource[]){
> +            { .type = IORESOURCE_IO,  .addr = 0x3f8, .size = 
> NS16550_REGS_NUM },
> +            { .type = IORESOURCE_IRQ, .addr = 4,     .size = 1 },
> +            { .type = IORESOURCE_UNKNOWN },
> +        },
> +    },
> +    [1] = {
> +        .name = "COM2",
> +        .res = (const struct resource[]){
> +            { .type = IORESOURCE_IO,  .addr = 0x2f8, .size = 
> NS16550_REGS_NUM },
> +            { .type = IORESOURCE_IRQ, .addr = 3,     .size = 1 },
> +            { .type = IORESOURCE_UNKNOWN },
> +        },
> +    },
> +    [2] = {
> +        .name = "COM3",
> +        .res = (const struct resource[]){
> +            { .type = IORESOURCE_IO,  .addr = 0x3e8, .size = 
> NS16550_REGS_NUM },
> +            { .type = IORESOURCE_IRQ, .addr = 4,     .size = 1 },
> +            { .type = IORESOURCE_UNKNOWN },
> +        },
> +    },
> +    [3] = {
> +        .name = "COM4",
> +        .res = (const struct resource[]){
> +            { .type = IORESOURCE_IO,  .addr = 0x2e8, .size = 
> NS16550_REGS_NUM },
> +            { .type = IORESOURCE_IRQ, .addr = 3,     .size = 1 },
> +            { .type = IORESOURCE_UNKNOWN },
> +        },
> +    },
> +};

The choice of COMn is at build time. Why do we need all four configurations
resident not only in the binary, but even at (post-init) runtime? Also, the
way you do initialization of .res, I think adding __initconst to the main
array wouldn't have the effect of pulling all those inti .init.* as well.
For the time being I simply don't see the need for the extra level of
indirection: All instances have two entries (plus the then likely not
necessary sentinel).

> +static bool cf_check ns16550_iir_check_lsi(const struct vuart_ns16550 *vdev)
> +{
> +    return !!(vdev->regs[UART_LSR] & UART_LSR_MASK);

No need for !! (also elsewhere).

> --- a/xen/include/xen/resource.h
> +++ b/xen/include/xen/resource.h
> @@ -31,4 +31,7 @@ struct resource {
>  
>  #define resource_size(res)      ((res)->size)
>  
> +#define for_each_resource(res) \
> +    for ( ; (res) && (res)->type != IORESOURCE_UNKNOWN; (res)++ )

I'm not sure this is a good generic #define; imo it wants keeping local to
the one file that uses it.

Jan



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.