[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)



On Thu, 31 Jul 2025, 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
> 
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index b7edb1d6555d..1156e7ebcc4c 100644
> --- 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) )
> @@ -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);
> +
>      vioapic_deinit(d);
>  
>      XFREE(d->arch.hvm.pl_time);
> diff --git a/xen/arch/x86/include/asm/domain.h 
> b/xen/arch/x86/include/asm/domain.h
> index eafd5cfc903d..1ecc7c2cae32 100644
> --- a/xen/arch/x86/include/asm/domain.h
> +++ b/xen/arch/x86/include/asm/domain.h
> @@ -468,6 +468,7 @@ struct arch_domain
>  #define X86_EMU_IOMMU    XEN_X86_EMU_IOMMU
>  #define X86_EMU_USE_PIRQ XEN_X86_EMU_USE_PIRQ
>  #define X86_EMU_VPCI     XEN_X86_EMU_VPCI
> +#define X86_EMU_NS16550  XEN_X86_EMU_NS16550
>  #else
>  #define X86_EMU_LAPIC    0
>  #define X86_EMU_HPET     0
> @@ -479,6 +480,7 @@ struct arch_domain
>  #define X86_EMU_IOMMU    0
>  #define X86_EMU_USE_PIRQ 0
>  #define X86_EMU_VPCI     0
> +#define X86_EMU_NS16550  0
>  #endif
>  
>  #define X86_EMU_PIT     XEN_X86_EMU_PIT
> @@ -489,7 +491,7 @@ struct arch_domain
>                                   X86_EMU_IOAPIC | X86_EMU_PIC |         \
>                                   X86_EMU_VGA | X86_EMU_IOMMU |          \
>                                   X86_EMU_PIT | X86_EMU_USE_PIRQ |       \
> -                                 X86_EMU_VPCI)
> +                                 X86_EMU_VPCI | X86_EMU_NS16550)
>  
>  #define has_vlapic(d)      (!!((d)->emulation_flags & X86_EMU_LAPIC))
>  #define has_vhpet(d)       (!!((d)->emulation_flags & X86_EMU_HPET))
> diff --git a/xen/arch/x86/include/asm/hvm/domain.h 
> b/xen/arch/x86/include/asm/hvm/domain.h
> index 333501d5f2ac..9945b16d1a6e 100644
> --- 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
>  };
>  
>  #endif /* __ASM_X86_HVM_DOMAIN_H__ */
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> index 02f7dd6dc1a1..ebefd90d913e 100644
> --- 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.
> +
> +config VUART_NS16550_DEBUG
> +     bool "UART emulator development debugging"
> +     depends on VUART_NS16550
> +     help
> +       Enable development debugging.
> +
>  endmenu
> diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
> index c6400b001e85..85650ca5d8ce 100644
> --- 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
> diff --git a/xen/common/emul/vuart/vuart-ns16550.c 
> b/xen/common/emul/vuart/vuart-ns16550.c
> new file mode 100644
> index 000000000000..48bbf58264fe
> --- /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>
> +
> +#define pr_err(fmt, args...) do { \
> +    gprintk(KERN_ERR, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#define pr_warn(fmt, args...) do { \
> +    if ( pr_log_level >= 1) \
> +        gprintk(KERN_WARNING, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#define pr_info(fmt, args...) do { \
> +    if ( pr_log_level >= 2 ) \
> +        gprintk(KERN_INFO, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#define pr_debug(fmt, args...) do { \
> +    if ( pr_log_level >= 3 ) \
> +        gprintk(KERN_DEBUG, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#if defined(CONFIG_VUART_NS16550_PC_COM1)
> +#define X86_PC_UART_IDX         0
> +#elif defined(CONFIG_VUART_NS16550_PC_COM2)
> +#define X86_PC_UART_IDX         1
> +#elif defined(CONFIG_VUART_NS16550_PC_COM3)
> +#define X86_PC_UART_IDX         2
> +#elif defined(CONFIG_VUART_NS16550_PC_COM4)
> +#define X86_PC_UART_IDX         3
> +#else
> +#error "Unsupported I/O port"
> +#endif
> +
> +#ifdef CONFIG_VUART_NS16550_DEBUG
> +#define guest_prefix            "FROM GUEST "
> +#else
> +#define guest_prefix            ""
> +#endif
> +
> +/*
> + * Number of supported registers in the UART.
> + */
> +#define NS16550_REGS_NUM        ( UART_SCR + 1 )
> +
> +/*
> + * Number of emulated registers.
> + *
> + * - Emulated registers [0..NS16550_REGS_NUM] are R/W registers for DLAB=0.
> + * - DLAB=1, R/W, DLL            = NS16550_REGS_NUM + 0
> + * - DLAB=1, R/W, DLM            = NS16550_REGS_NUM + 1
> + * -         R/O, IIR (IIR_THR)  = NS16550_REGS_NUM + 2
> + */
> +#define NS16550_EMU_REGS_NUM    ( NS16550_REGS_NUM + 3 )
> +
> +/*
> + * Virtual NS16550 device state.
> + */
> +struct vuart_ns16550 {
> +    struct xencons_interface cons;      /* Emulated RX/TX FIFOs */
> +    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 */
> +    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 },
> +        },
> +    },
> +};
> +
> +static bool ns16550_fifo_rx_empty(const struct vuart_ns16550 *vdev)
> +{
> +    const struct xencons_interface *cons = &vdev->cons;
> +
> +    return cons->in_prod == cons->in_cons;
> +}
> +
> +static bool ns16550_fifo_rx_full(const struct vuart_ns16550 *vdev)
> +{
> +    const struct xencons_interface *cons = &vdev->cons;
> +
> +    return cons->in_prod - cons->in_cons == ARRAY_SIZE(cons->in);
> +}
> +
> +static void ns16550_fifo_rx_reset(struct vuart_ns16550 *vdev)
> +{
> +    struct xencons_interface *cons = &vdev->cons;
> +
> +    cons->in_cons = cons->in_prod;
> +}
> +
> +static int ns16550_fifo_rx_getchar(struct vuart_ns16550 *vdev)
> +{
> +    struct xencons_interface *cons = &vdev->cons;
> +    int rc;
> +
> +    if ( ns16550_fifo_rx_empty(vdev) )
> +    {
> +        pr_debug("%s: RX FIFO empty\n", vdev->name);
> +        rc = -ENODATA;
> +    }
> +    else
> +    {
> +        rc = cons->in[MASK_XENCONS_IDX(cons->in_cons, cons->in)];
> +        cons->in_cons++;
> +    }
> +
> +    return rc;
> +}
> +
> +static int ns16550_fifo_rx_putchar(struct vuart_ns16550 *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 ( ns16550_fifo_rx_full(vdev) )
> +    {
> +        pr_debug("%s: RX FIFO full; resetting\n", vdev->name);
> +        ns16550_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 ns16550_fifo_tx_empty(const struct vuart_ns16550 *vdev)
> +{
> +    const struct xencons_interface *cons = &vdev->cons;
> +
> +    return cons->out_prod == cons->out_cons;
> +}
> +
> +static void ns16550_fifo_tx_reset(struct vuart_ns16550 *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 ns16550_fifo_tx_flush(struct vuart_ns16550 *vdev)
> +{
> +    struct xencons_interface *cons = &vdev->cons;
> +
> +    if ( ns16550_fifo_tx_empty(vdev) )
> +        return;
> +
> +    ASSERT(cons->out_prod < ARRAY_SIZE(cons->out));
> +    cons->out[cons->out_prod] = '\0';
> +    cons->out_prod++;
> +
> +    guest_printk(vdev->owner, guest_prefix "%s", cons->out);
> +
> +    ns16550_fifo_tx_reset(vdev);
> +}
> +
> +/*
> + * Accumulate guest OS output before sending to Xen console.
> + */
> +static void ns16550_fifo_tx_putchar(struct vuart_ns16550 *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' )
> +        ns16550_fifo_tx_flush(vdev);
> +}
> +
> +static inline uint8_t cf_check ns16550_dlab_get(const struct vuart_ns16550 
> *vdev)
> +{
> +    return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0;
> +}
> +
> +static bool cf_check ns16550_iir_check_lsi(const struct vuart_ns16550 *vdev)
> +{
> +    return !!(vdev->regs[UART_LSR] & UART_LSR_MASK);
> +}
> +
> +static bool cf_check ns16550_iir_check_rda(const struct vuart_ns16550 *vdev)
> +{
> +    return !ns16550_fifo_rx_empty(vdev);
> +}
> +
> +static bool cf_check ns16550_iir_check_thr(const struct vuart_ns16550 *vdev)
> +{
> +    return !!(vdev->regs[NS16550_REGS_NUM + UART_IIR] & UART_IIR_THR);
> +}
> +
> +static bool cf_check ns16550_iir_check_msi(const struct vuart_ns16550 *vdev)
> +{
> +    return !!(vdev->regs[UART_MSR] & UART_MSR_CHANGE);
> +}
> +
> +/*
> + * Get the interrupt identity reason.
> + *
> + * IIR is re-calculated once called, because NS16550 always reports high
> + * priority events first.
> + * regs[NS16550_REGS_NUM + UART_IIR] is used to store THR reason only.
> + */
> +static uint8_t ns16550_iir_get(const struct vuart_ns16550 *vdev)
> +{
> +    /*
> +     * Interrupt identity reasons by priority.
> +     * NB: high priority are at lower indexes below.
> +     */
> +    static const struct {
> +        bool (*check)(const struct vuart_ns16550 *vdev);
> +        uint8_t ier;
> +        uint8_t iir;
> +    } iir_by_prio[] = {
> +        [0] = { ns16550_iir_check_lsi, UART_IER_ELSI,   UART_IIR_LSI },
> +        [1] = { ns16550_iir_check_rda, UART_IER_ERDAI,  UART_IIR_RDA },
> +        [2] = { ns16550_iir_check_thr, UART_IER_ETHREI, UART_IIR_THR },
> +        [3] = { ns16550_iir_check_msi, UART_IER_EMSI,   UART_IIR_MSI },
> +    };
> +    const uint8_t *regs = vdev->regs;
> +    uint8_t iir = 0;
> +    unsigned int i;
> +
> +    /*
> +     * NB: every interaction w/ NS16550 registers (except DLAB=1) goes
> +     * through that call.
> +     */
> +    ASSERT(spin_is_locked(&vdev->lock));
> +
> +    for ( i = 0; i < ARRAY_SIZE(iir_by_prio); i++ )
> +    {
> +        if ( (regs[UART_IER] & iir_by_prio[i].ier) &&
> +             iir_by_prio[i].check(vdev) )
> +            break;
> +
> +    }
> +    if ( i == ARRAY_SIZE(iir_by_prio) )
> +        iir |= UART_IIR_NOINT;
> +    else
> +        iir |= iir_by_prio[i].iir;
> +
> +    if ( regs[UART_FCR] & UART_FCR_ENABLE )
> +        iir |= UART_IIR_FE;
> +
> +    return iir;
> +}
> +
> +static void ns16550_irq_assert(const struct vuart_ns16550 *vdev)
> +{
> +    struct domain *d = vdev->owner;
> +    int vector;
> +
> +    if ( has_vpic(d) ) /* HVM */
> +        vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
> +    else
> +        ASSERT_UNREACHABLE();
> +
> +    pr_debug("%s: IRQ#%d vector %d assert\n", vdev->name, vdev->irq, vector);
> +}
> +
> +static void ns16550_irq_deassert(const struct vuart_ns16550 *vdev)
> +{
> +    struct domain *d = vdev->owner;
> +
> +    if ( has_vpic(d) ) /* HVM */
> +        hvm_isa_irq_deassert(d, vdev->irq);
> +    else
> +        ASSERT_UNREACHABLE();
> +
> +    pr_debug("%s: IRQ#%d deassert\n", vdev->name, vdev->irq);
> +}
> +
> +/*
> + * Assert/deassert virtual NS16550 interrupt line.
> + */
> +static void ns16550_irq_check(const struct vuart_ns16550 *vdev)
> +{
> +    uint8_t iir = ns16550_iir_get(vdev);
> +
> +    if ( iir & UART_IIR_NOINT )
> +        ns16550_irq_assert(vdev);
> +    else
> +        ns16550_irq_deassert(vdev);
> +
> +    pr_debug("%s: IRQ#%d IIR 0x%02x %s\n", vdev->name, vdev->irq, iir,
> +             (iir & UART_IIR_NOINT) ? "deassert" : "assert");
> +}
> +
> +/*
> + * Emulate 8-bit write access to NS16550 register.
> + */
> +static int ns16550_io_write8(
> +    struct vuart_ns16550 *vdev, uint32_t reg, uint8_t *data)
> +{
> +    uint8_t *regs = vdev->regs;
> +    uint8_t val = *data;
> +    int rc = 0;
> +
> +    if ( ns16550_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> +        regs[NS16550_REGS_NUM + reg] = val;
> +    else
> +    {
> +        switch ( reg )
> +        {
> +        case UART_THR:
> +            if ( regs[UART_MCR] & UART_MCR_LOOP )
> +            {
> +                (void)ns16550_fifo_rx_putchar(vdev, val);
> +                regs[UART_LSR] |= UART_LSR_OE;

Why is UART_LSR_OE set unconditionally here instead of checking if
ns16550_fifo_rx_putchar returned -ENOSPC?


> +            }
> +            else
> +                ns16550_fifo_tx_putchar(vdev, val);
> +
> +            regs[NS16550_REGS_NUM + UART_IIR] |= UART_IIR_THR;
> +
> +            break;
> +
> +        case UART_IER:
> +            /*
> +             * NB: Make sure THR interrupt is re-triggered once guest OS
> +             * re-enabled ETHREI in EIR.
> +             */
> +            if ( val & regs[UART_IER] & UART_IER_ETHREI )
> +                regs[NS16550_REGS_NUM + UART_IIR] |= UART_IIR_THR;
> +
> +            regs[UART_IER] = val & UART_IER_MASK;
> +
> +            break;
> +
> +        case UART_FCR: /* WO */
> +            if ( val & UART_FCR_RESERVED0 )
> +                pr_warn("%s: FCR: attempt to set reserved bit: %x\n",
> +                        vdev->name, UART_FCR_RESERVED0);
> +
> +            if ( val & UART_FCR_RESERVED1 )
> +                pr_warn("%s: FCR: attempt to set reserved bit: %x\n",
> +                        vdev->name, UART_FCR_RESERVED1);
> +
> +            if ( val & UART_FCR_CLRX )
> +                ns16550_fifo_rx_reset(vdev);
> +
> +            if ( val & UART_FCR_CLTX )
> +                ns16550_fifo_tx_flush(vdev);
> +
> +            if ( val & UART_FCR_ENABLE )
> +                val &= UART_FCR_ENABLE | UART_FCR_DMA | UART_FCR_TRG_MASK;
> +            else
> +                val = 0;
> +
> +            regs[UART_FCR] = val;
> +
> +            break;
> +
> +        case UART_LCR:
> +            regs[UART_LCR] = val;
> +            break;
> +
> +        case UART_MCR: {
> +            uint8_t msr_curr, msr_next, msr_delta;
> +
> +            msr_curr = regs[UART_MSR];
> +            msr_next = 0;
> +            msr_delta = 0;
> +
> +            if ( val & UART_MCR_RESERVED0 )
> +                pr_warn("%s: MCR: attempt to set reserved bit: %x\n",
> +                        vdev->name, UART_MCR_RESERVED0);
> +
> +            if ( val & UART_MCR_TCRTLR )
> +                pr_warn("%s: MCR: not supported: %x\n",
> +                        vdev->name, UART_MCR_TCRTLR);
> +
> +            if ( val & UART_MCR_RESERVED1 )
> +                pr_warn("%s: MCR: attempt to set reserved bit: %x\n",
> +                        vdev->name, UART_MCR_RESERVED1);
> +
> +            /* Set modem status */
> +            if ( val & UART_MCR_LOOP )
> +            {
> +                if ( val & UART_MCR_DTR )
> +                    msr_next |= UART_MSR_DSR;
> +                if ( val & UART_MCR_RTS )
> +                    msr_next |= UART_MSR_CTS;
> +                if ( val & UART_MCR_OUT1 )
> +                    msr_next |= UART_MSR_RI;
> +                if ( val & UART_MCR_OUT2 )
> +                    msr_next |= UART_MSR_DCD;
> +            }
> +            else
> +                msr_next |= UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
> +
> +            /* Calculate changes in modem status */
> +            if ( (msr_curr & UART_MSR_CTS) ^ (msr_next & UART_MSR_CTS) )
> +                msr_delta |= UART_MSR_DCTS;
> +            if ( (msr_curr & UART_MCR_RTS) ^ (msr_next & UART_MCR_RTS) )
> +                msr_delta |= UART_MSR_DDSR;

Should we check UART_MSR_DSR instead of UART_MCR_RTS to set
UART_MSR_DDSR ?


> +            if ( (msr_curr & UART_MSR_RI)  & (msr_next & UART_MSR_RI) )
> +                msr_delta |= UART_MSR_TERI;
> +            if ( (msr_curr & UART_MSR_DCD) ^ (msr_next & UART_MSR_DCD) )
> +                msr_delta |= UART_MSR_DDCD;
> +
> +            regs[UART_MCR] = val & UART_MCR_MASK;
> +            regs[UART_MSR] = msr_next | msr_delta;
> +
> +            break;
> +        }
> +
> +        /* NB: Firmware (e.g. OVMF) may rely on SCR presence. */
> +        case UART_SCR:
> +            regs[UART_SCR] = val;
> +            break;
> +
> +        case UART_LSR: /* RO */
> +        case UART_MSR: /* RO */
> +        default:
> +            rc = -EINVAL;
> +            break;
> +        }
> +
> +        ns16550_irq_check(vdev);
> +    }
> +
> +    return rc;
> +}
> +
> +/*
> + * Emulate 16-bit write access to NS16550 register.
> + * NB: some guest OSes use outw() to access UART_DLL.
> + */
> +static int ns16550_io_write16(
> +    struct vuart_ns16550 *vdev, uint32_t reg, uint16_t *data)
> +{
> +    uint16_t val = *data;
> +    int rc;
> +
> +    if ( ns16550_dlab_get(vdev) && reg == UART_DLL )
> +    {
> +        vdev->regs[NS16550_REGS_NUM + UART_DLL] = val & 0xff;
> +        vdev->regs[NS16550_REGS_NUM + UART_DLM] = (val >> 8) & 0xff;
> +        rc = 0;
> +    }
> +    else
> +        rc = -EINVAL;
> +
> +    return rc;
> +}
> +
> +/*
> + * Emulate write access to NS16550 register.
> + */
> +static int ns16550_io_write(
> +    struct vuart_ns16550 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
> +{
> +    int rc;
> +
> +    switch ( size )
> +    {
> +    case 1:
> +        rc = ns16550_io_write8(vdev, reg, (uint8_t *)data);
> +        break;
> +
> +    case 2:
> +        rc = ns16550_io_write16(vdev, reg, (uint16_t *)data);
> +        break;
> +
> +    default:
> +        rc = -EINVAL;
> +        break;
> +    }
> +
> +    return rc;
> +}
> +
> +/*
> + * Emulate 8-bit read access to NS16550 register.
> + */
> +static int ns16550_io_read8(
> +    struct vuart_ns16550 *vdev, uint32_t reg, uint8_t *data)
> +{
> +    uint8_t *regs = vdev->regs;
> +    uint8_t val = 0xff;
> +    int rc = 0;
> +
> +    if ( ns16550_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> +        val = regs[NS16550_REGS_NUM + reg];
> +    else {
> +        switch ( reg )
> +        {
> +        case UART_RBR:
> +            /* NB: do not forget to clear overrun condition */
> +            regs[UART_LSR] &= ~UART_LSR_OE;
> +
> +            rc = ns16550_fifo_rx_getchar(vdev);
> +            if ( rc >= 0 )
> +                val = (uint8_t)rc;
> +
> +            rc = 0;
> +            break;
> +
> +        case UART_IER:
> +            val = regs[UART_IER];
> +            break;
> +
> +        case UART_IIR: /* RO */
> +            val = ns16550_iir_get(vdev);
> +
> +            /* NB: clear IIR scratch location */
> +            if ( val & UART_IIR_THR )
> +                regs[NS16550_REGS_NUM + UART_IIR] &= ~UART_IIR_THR;
> +
> +            break;
> +
> +        case UART_LCR:
> +            val = regs[UART_LCR];
> +            break;
> +
> +        case UART_MCR:
> +            val = regs[UART_MCR];
> +            break;
> +
> +        case UART_LSR:
> +            val = regs[UART_LSR] | UART_LSR_THRE | UART_LSR_TEMT;
> +            if ( ns16550_fifo_rx_empty(vdev) )
> +                val &= ~UART_LSR_DR;
> +            else
> +                val |= UART_LSR_DR;
> +
> +            regs[UART_LSR] = val & ~UART_LSR_MASK;
> +
> +            break;
> +
> +        case UART_MSR:
> +            val = regs[UART_MSR];
> +            regs[UART_MSR] &= ~UART_MSR_CHANGE;
> +            break;
> +
> +        case UART_SCR:
> +            val = regs[UART_SCR];
> +            break;
> +
> +        default:
> +            rc = -EINVAL;
> +            break;
> +        }
> +
> +        ns16550_irq_check(vdev);
> +    }
> +
> +    *data = val;
> +
> +    return rc;
> +}
> +
> +/*
> + * Emulate 16-bit read access to NS16550 register.
> + */
> +static int ns16550_io_read16(
> +    struct vuart_ns16550 *vdev, uint32_t reg, uint16_t *data)
> +{
> +    uint16_t val = 0xffff;
> +    int rc = -EINVAL;
> +
> +    if ( ns16550_dlab_get(vdev) && reg == UART_DLL )
> +    {
> +        val = vdev->regs[NS16550_REGS_NUM + UART_DLM] << 8 |
> +              vdev->regs[NS16550_REGS_NUM + UART_DLL];
> +        rc = 0;
> +    }
> +
> +    *data = val;
> +
> +    return rc;
> +}
> +
> +/*
> + * Emulate read access to NS16550 register.
> + */
> +static int ns16550_io_read(
> +    struct vuart_ns16550 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
> +{
> +    int rc;
> +
> +    switch ( size )
> +    {
> +    case 1:
> +        rc = ns16550_io_read8(vdev, reg, (uint8_t *)data);
> +        break;
> +
> +    case 2:
> +        rc = ns16550_io_read16(vdev, reg, (uint16_t *)data);
> +        break;
> +
> +    default:
> +        *data = 0xffffffff;
> +        rc = -EINVAL;
> +        break;
> +    }
> +
> +    return rc;
> +}
> +
> +static void cf_check ns16550_dump_state(const struct domain *d)
> +{
> +    struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> +    const struct xencons_interface *cons;
> +    const uint8_t *regs;
> +
> +    if ( !vdev )
> +        return;
> +
> +    /* Allow printing state in case of a deadlock. */
> +    if ( !spin_trylock(&vdev->lock) )
> +        return;
> +
> +    cons = &vdev->cons;
> +    regs = &vdev->regs[0];
> +
> +    printk("Virtual " pr_prefix " (%s) I/O port 0x%04"PRIx64" IRQ#%d owner 
> %pd\n",
> +            vdev->name, vdev->io_addr, vdev->irq, vdev->owner);
> +
> +    printk("  RX FIFO size %ld in_prod %d in_cons %d used %d\n",
> +            ARRAY_SIZE(cons->in), cons->in_prod, cons->in_cons,
> +            cons->in_prod - cons->in_cons);
> +
> +    printk("  TX FIFO size %ld out_prod %d out_cons %d used %d\n",
> +            ARRAY_SIZE(cons->out), cons->out_prod, cons->out_cons,
> +            cons->out_prod - cons->out_cons);
> +
> +    printk("  %02"PRIx8" RBR %02"PRIx8" THR %02"PRIx8" DLL %02"PRIx8" DLM 
> %02"PRIx8"\n",
> +            UART_RBR,
> +            cons->in[MASK_XENCONS_IDX(cons->in_prod, cons)],
> +            cons->out[MASK_XENCONS_IDX(cons->out_prod, cons)],
> +            regs[NS16550_REGS_NUM + UART_DLL],
> +            regs[NS16550_REGS_NUM + UART_DLM]);
> +
> +    printk("  %02"PRIx8" IER %02"PRIx8"\n", UART_IER, regs[UART_IER]);
> +
> +    printk("  %02"PRIx8" FCR %02"PRIx8" IIR %02"PRIx8"\n",
> +            UART_FCR, regs[UART_FCR], ns16550_iir_get(vdev));
> +
> +    printk("  %02"PRIx8" LCR %02"PRIx8"\n", UART_LCR, regs[UART_LCR]);
> +    printk("  %02"PRIx8" MCR %02"PRIx8"\n", UART_MCR, regs[UART_MCR]);
> +    printk("  %02"PRIx8" LSR %02"PRIx8"\n", UART_LSR, regs[UART_LSR]);
> +    printk("  %02"PRIx8" MSR %02"PRIx8"\n", UART_MSR, regs[UART_MSR]);
> +    printk("  %02"PRIx8" SCR %02"PRIx8"\n", UART_SCR, regs[UART_SCR]);
> +
> +    spin_unlock(&vdev->lock);
> +}
> +
> +/*
> + * Emulate I/O access to NS16550 register.
> + * Note, emulation always returns X86EMUL_OKAY, once I/O port trap is 
> enabled.
> + */
> +static int cf_check ns16550_io_handle(
> +    int dir, unsigned int addr, unsigned int size, uint32_t *data)
> +{
> +#define op(dir)     (((dir) == IOREQ_WRITE) ? 'W' : 'R')
> +    struct domain *d = rcu_lock_current_domain();
> +    struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> +    uint32_t reg;
> +    unsigned dlab;
> +    int rc;
> +
> +    if ( !vdev )
> +    {
> +        pr_err("%s: %c io 0x%04x %d: not initialized\n",
> +                vdev->name, op(dir), addr, size);
> +
> +        ASSERT_UNREACHABLE();
> +        goto out;
> +    }
> +
> +    if ( d != vdev->owner )
> +    {
> +        pr_err("%s: %c io 0x%04x %d: does not match current domain %pv\n",
> +                vdev->name, op(dir), addr, size, d);
> +
> +        ASSERT_UNREACHABLE();
> +        goto out;
> +    }
> +
> +    reg = addr - vdev->io_addr;
> +    if ( !IS_ALIGNED(reg, size) )
> +    {
> +        pr_err("%s: %c 0x%04x %d: unaligned access\n",
> +                vdev->name, op(dir), addr, size);
> +        goto out;
> +    }
> +
> +    dlab = ns16550_dlab_get(vdev);
> +    if ( reg >= NS16550_REGS_NUM )
> +    {
> +        pr_err("%s: %c io 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": not 
> implemented\n",
> +                vdev->name, op(dir), addr, size,
> +                dlab, reg, *data);
> +        goto out;
> +    }
> +
> +    spin_lock(&vdev->lock);
> +
> +    if ( dir == IOREQ_WRITE )
> +    {
> +        pr_debug("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> +                 vdev->name, op(dir), addr, size,
> +                 dlab, reg, *data);
> +        rc = ns16550_io_write(vdev, reg, size, data);
> +    }
> +    else
> +    {
> +        rc = ns16550_io_read(vdev, reg, size, data);
> +        pr_debug("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> +                 vdev->name, op(dir), addr, size,
> +                 dlab, reg, *data);
> +    }
> +    if ( rc < 0 )
> +        pr_err("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": 
> unsupported access\n",
> +               vdev->name, op(dir), addr, size,
> +               dlab, reg, *data);
> +
> +    spin_unlock(&vdev->lock);
> +#ifdef CONFIG_VUART_NS16550_DEBUG
> +    ns16550_dump_state(d);
> +#endif
> +
> +out:
> +    rcu_unlock_domain(d);
> +
> +    return X86EMUL_OKAY;
> +#undef op
> +}
> +
> +static int cf_check ns16550_init(struct domain *d,
> +                                 struct vuart_params *params)
> +{
> +    const struct virtdev_desc *desc = &x86_pc_uarts[X86_PC_UART_IDX];
> +    const struct resource *r = desc->res;
> +    const uint16_t divisor = (UART_CLOCK_HZ / 115200) >> 4;
> +    struct vuart_ns16550 *vdev;
> +    int rc;
> +
> +    BUG_ON(d->arch.hvm.vuart);
> +
> +    if ( !is_hvm_domain(d) )
> +    {
> +        pr_err("%s: not an HVM domain\n", desc->name);
> +        return -ENOSYS;
> +    }
> +
> +    vdev = xvzalloc(typeof(*vdev));
> +    if ( !vdev )
> +    {
> +        pr_err("%s: failed to allocate memory\n", desc->name);
> +        return -ENOMEM;
> +    }
> +
> +    for_each_resource(r)
> +    {
> +        if ( r->type & IORESOURCE_IO )
> +        {
> +            /* Disallow sharing physical I/O port */
> +            rc = ioports_deny_access(d, r->addr, r->addr + r->size - 1);
> +            if ( rc )
> +            {
> +                pr_err("%s: virtual I/O port range 
> [0x%04x"PRIx64"..0x%04x"PRIx64"]: conflict w/ physical range\n",
> +                        desc->name,
> +                        (unsigned int)r->addr,
> +                        (unsigned int)(r->addr + r->size - 1));
> +                return rc;
> +            }
> +
> +            register_portio_handler(d, r->addr, r->size, ns16550_io_handle);
> +
> +            vdev->io_addr = r->addr;
> +            vdev->io_size = r->size;
> +        }
> +        else if ( r->type & IORESOURCE_IRQ )
> +        {
> +            /* Disallow sharing physical IRQ */
> +            rc = irq_deny_access(d, r->addr);
> +            if ( rc )
> +            {
> +                pr_err("%s: virtual IRQ#%"PRIu64": conflict w/ physical IRQ: 
> %d\n",
> +                        desc->name, r->addr, rc);
> +                return rc;
> +            }
> +
> +            vdev->irq = r->addr;
> +        }
> +        else
> +            ASSERT_UNREACHABLE();
> +    }
> +
> +    spin_lock_init(&vdev->lock);
> +
> +    vdev->owner = d;
> +    vdev->name = desc->name;
> +
> +    /* NB: report 115200 baud rate */
> +    vdev->regs[NS16550_REGS_NUM + UART_DLL] = divisor & 0xff;
> +    vdev->regs[NS16550_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
> +
> +    /* NS16550 shall assert UART_IIR_THR whenever transmitter is empty. */
> +    vdev->regs[NS16550_REGS_NUM + UART_IIR] = UART_IIR_THR;
> +
> +    d->arch.hvm.vuart = vdev;
> +
> +    spin_lock(&vdev->lock);
> +    ns16550_irq_check(vdev);
> +    spin_unlock(&vdev->lock);
> +
> +    return 0;
> +}
> +
> +static void cf_check ns16550_deinit(struct domain *d)
> +{
> +    struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> +
> +    if ( !vdev )
> +        return;
> +
> +    spin_lock(&vdev->lock);
> +
> +    ns16550_fifo_tx_flush(vdev);
> +
> +    spin_unlock(&vdev->lock);
> +
> +    XVFREE(d->arch.hvm.vuart);
> +}
> +
> +static int cf_check ns16550_put_rx(struct domain *d, char ch)
> +{
> +    struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> +    uint8_t *regs;
> +    uint8_t dlab;
> +    int rc;
> +
> +    ASSERT(d == vdev->owner);
> +    if ( !vdev )
> +        return -ENODEV;
> +
> +    spin_lock(&vdev->lock);
> +
> +    dlab = ns16550_dlab_get(vdev);
> +    regs = vdev->regs;
> +
> +    if ( dlab )
> +    {
> +        pr_debug("%s: THR/RBR access disabled: DLAB=1\n", vdev->name);
> +        rc = -EBUSY;
> +    }
> +    else if ( regs[UART_MCR] & UART_MCR_LOOP )
> +    {
> +        pr_debug("%s: THR/RBR access disabled: loopback mode\n", vdev->name);
> +        rc = -EBUSY;
> +    }
> +    else
> +    {
> +        uint8_t val = 0;
> +
> +        rc = ns16550_fifo_rx_putchar(vdev, ch);
> +        if ( rc == -ENOSPC )
> +            val |= UART_LSR_OE;
> +
> +        /* NB: UART_LSR_DR is also set when UART_LSR is accessed. */
> +        regs[UART_LSR] |= UART_LSR_DR | val;
> +
> +        /*
> +         * Echo the user input on Xen console iff Xen console input is owned
> +         * by NS16550 domain.
> +         * NB: use 'console_timestamps=none' to disable Xen timestamps.
> +         */
> +        if ( is_console_printable(ch) )
> +            guest_printk(d, "%c", ch);
> +
> +        /* FIXME: check FCR when to fire an interrupt */
> +        ns16550_irq_check(vdev);
> +    }
> +
> +    spin_unlock(&vdev->lock);
> +#ifdef CONFIG_VUART_NS16550_DEBUG
> +    ns16550_dump_state(d);
> +#endif
> +
> +    return rc;
> +}
> +
> +static const struct vuart_ops ns16550_ops = {
> +    .add_node   = NULL,
> +    .init       = ns16550_init,
> +    .deinit     = ns16550_deinit,
> +    .dump_state = ns16550_dump_state,
> +    .put_rx     = ns16550_put_rx,
> +};
> +
> +VUART_REGISTER(ns16550, &ns16550_ops);
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> index 14a7f8bd8b79..7971813a723d 100644
> --- a/xen/common/emul/vuart/vuart.c
> +++ b/xen/common/emul/vuart/vuart.c
> @@ -99,6 +99,10 @@ bool domain_has_vuart(const struct domain *d)
>  {
>      uint32_t mask = 0;
>  
> +#ifdef CONFIG_VUART_NS16550
> +    mask |= XEN_X86_EMU_NS16550;
> +#endif
> +
>      return !!(d->emulation_flags & mask);
>  }
>  
> diff --git a/xen/include/public/arch-x86/xen.h 
> b/xen/include/public/arch-x86/xen.h
> index fc2487986642..f905e1252c70 100644
> --- a/xen/include/public/arch-x86/xen.h
> +++ b/xen/include/public/arch-x86/xen.h
> @@ -283,13 +283,15 @@ struct xen_arch_domainconfig {
>  #define XEN_X86_EMU_USE_PIRQ        (1U<<_XEN_X86_EMU_USE_PIRQ)
>  #define _XEN_X86_EMU_VPCI           10
>  #define XEN_X86_EMU_VPCI            (1U<<_XEN_X86_EMU_VPCI)
> +#define _XEN_X86_EMU_NS16550        11
> +#define XEN_X86_EMU_NS16550         (1U<<_XEN_X86_EMU_NS16550)
>  
>  #define XEN_X86_EMU_ALL             (XEN_X86_EMU_LAPIC | XEN_X86_EMU_HPET |  
> \
>                                       XEN_X86_EMU_PM | XEN_X86_EMU_RTC |      
> \
>                                       XEN_X86_EMU_IOAPIC | XEN_X86_EMU_PIC |  
> \
>                                       XEN_X86_EMU_VGA | XEN_X86_EMU_IOMMU |   
> \
>                                       XEN_X86_EMU_PIT | XEN_X86_EMU_USE_PIRQ 
> |\
> -                                     XEN_X86_EMU_VPCI)
> +                                     XEN_X86_EMU_VPCI | XEN_X86_EMU_NS16550)
>      uint32_t emulation_flags;
>  
>  /*
> diff --git a/xen/include/xen/resource.h b/xen/include/xen/resource.h
> index 5d103631288d..56fb8101edd6 100644
> --- 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)++ )
> +
>  #endif /* XEN__RESOURCE_H */
> -- 
> 2.34.1
> 
> 



 


Rackspace

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