On Sun, 2026-08-23 at 18:55 -0300, Daniel Henrique Barboza wrote:
> 
> 
> On 7/17/2026 11:16 PM, Yanfeng Liu wrote:
> > This adds optional UART1 to RiscV virt board if required at
> > runtime to simplify multicore development.
> 
> Can you please elaborate on the use case?  We can add multiple serial
> devices already.  E.g. this will add 4 serials:
> 
I was playing Bao hypervisor and found that having a 2nd NMIO UART can
ba handy.  Previoously when trying AMP I added 2nd MMIO UART to aid
debugging. 

Since this happened to me at least twice, I am wondering someone else
might also need it. Besides many real devices have muitlple UARTs.

> 
> 
> $ ./build/qemu-system-riscv64 -M virt --nodefaults \
>           -serial stdio \
>           -serial tcp::4444,server,nowait \
>           -serial pty  \
>           -serial file:uart4.log \
>           --nographic
> 
> 
> The difference here is that the additional serials won't have memory
> mappings
> and fdt nodes.
> 

how these additional UARTs can be used by guests if they dont have
MMIO?


Regards,
Yanfeng


> 
> Thanks,
> Daniel
> 
> > 
> > Note that UART0 remains default serial_hd(0) and it is:
> > 
> > - the lowest address UART
> > - first serial in DTB
> > - behind /aliases/serial0 in DTB
> > - the /chosen/stdout-path in DTB
> > 
> > Note that UART1 is placed at different page from UART0 to
> > support page level isolation.
> > 
> > Signed-off-by: Yanfeng Liu <[email protected]>
> > ---
> >   docs/system/riscv/virt.rst |  5 ++++-
> >   hw/riscv/virt-acpi-build.c | 12 ++++++++----
> >   hw/riscv/virt.c            | 40 ++++++++++++++++++++++++++++-----
> > -----
> >   include/hw/riscv/virt.h    |  3 +++
> >   4 files changed, 45 insertions(+), 15 deletions(-)
> > 
> > diff --git a/docs/system/riscv/virt.rst
> > b/docs/system/riscv/virt.rst
> > index 60850970ce..45ac63ac77 100644
> > --- a/docs/system/riscv/virt.rst
> > +++ b/docs/system/riscv/virt.rst
> > @@ -16,7 +16,7 @@ The ``virt`` machine supports the following
> > devices:
> >   * Core Local Interruptor (CLINT)
> >   * Platform-Level Interrupt Controller (PLIC)
> >   * CFI parallel NOR flash memory
> > -* 1 NS16550 compatible UART
> > +* Either 1 or 2 NS16550 compatible UARTs
> >   * 1 Google Goldfish RTC
> >   * 1 SiFive Test device
> >   * 8 virtio-mmio transport devices
> > @@ -27,6 +27,9 @@ The hypervisor extension has been enabled for the
> > default CPU, so virtual
> >   machines with hypervisor extension can simply be used without
> > explicitly
> >   declaring.
> >   
> > +The second UART only exists if a backend is configured explicitly
> > (e.g.
> > +with a second `-serial` command line option).
> > +
> >   Hardware configuration information
> >   ----------------------------------
> >   
> > diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-
> > build.c
> > index 413d47d70e..597751c84a 100644
> > --- a/hw/riscv/virt-acpi-build.c
> > +++ b/hw/riscv/virt-acpi-build.c
> > @@ -169,11 +169,11 @@ static void acpi_dsdt_add_plic_aplic(Aml
> > *scope, uint8_t socket_count,
> >   
> >   static void
> >   acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
> > -                    uint32_t uart_irq)
> > +                    uint32_t uart_irq, int uartidx)
> >   {
> > -    Aml *dev = aml_device("COM0");
> > +    Aml *dev = aml_device("COM%d", uartidx);
> >       aml_append(dev, aml_name_decl("_HID",
> > aml_string("RSCV0003")));
> > -    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
> > +    aml_append(dev, aml_name_decl("_UID", aml_int(uartidx)));
> >   
> >       Aml *crs = aml_resource_template();
> >       aml_append(crs, aml_memory32_fixed(uart_memmap->base,
> > @@ -479,7 +479,11 @@ static void build_dsdt(GArray *table_data,
> >                                    memmap[VIRT_APLIC_S].size,
> > "RSCV0002");
> >       }
> >   
> > -    acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ);
> > +    acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ, 0);
> > +    if (s->uart1_present) {
> > +        acpi_dsdt_add_uart(scope, &memmap[VIRT_UART1], UART1_IRQ,
> > 1);
> > +    }
> > +
> >       if (virt_is_iommu_sys_enabled(s)) {
> >           acpi_dsdt_add_iommu_sys(scope, &memmap[VIRT_IOMMU_SYS],
> > IOMMU_SYS_IRQ);
> >       }
> > diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
> > index 33775a61fd..4703ac58f8 100644
> > --- a/hw/riscv/virt.c
> > +++ b/hw/riscv/virt.c
> > @@ -96,6 +96,8 @@ static const MemMapEntry virt_memmap[] = {
> >       [VIRT_APLIC_S] =      {  0xd000000, APLIC_SIZE(VIRT_CPUS_MAX)
> > },
> >       [VIRT_UART0] =        { 0x10000000,         0x100 },
> >       [VIRT_VIRTIO] =       { 0x10001000,        0x1000 },
> > +    /* UART1 supports page isolation from UART0 */
> > +    [VIRT_UART1] =        { 0x1000a000,         0x100 },
> >       [VIRT_FW_CFG] =       { 0x10100000,          0x18 },
> >       [VIRT_FLASH] =        { 0x20000000,     0x4000000 },
> >       [VIRT_IMSIC_M] =      { 0x24000000, VIRT_IMSIC_MAX_SIZE },
> > @@ -186,7 +188,8 @@ static void create_pcie_irq_map(RISCVVirtState
> > *s, void *fdt, char *nodename,
> >                             FDT_MAX_INT_MAP_WIDTH] = {};
> >       uint32_t *irq_map = full_irq_map;
> >   
> > -    /* This code creates a standard swizzle of interrupts such
> > that
> > +    /*
> > +     * This code creates a standard swizzle of interrupts such
> > that
> >        * each device's first interrupt is based on it's PCI_SLOT
> > number.
> >        * (See pci_swizzle_map_irq_fn())
> >        *
> > @@ -832,28 +835,38 @@ static void create_fdt_reset(RISCVVirtState
> > *s, uint32_t *phandle)
> >   }
> >   
> >   static void create_fdt_uart(RISCVVirtState *s,
> > -                            uint32_t irq_mmio_phandle)
> > +                            uint32_t irq_mmio_phandle, int memId,
> > int irqNo)
> >   {
> >       g_autofree char *name = NULL;
> >       MachineState *ms = MACHINE(s);
> >   
> >       name = g_strdup_printf("/soc/serial@%"HWADDR_PRIx,
> > -                           s->memmap[VIRT_UART0].base);
> > +                           s->memmap[memId].base);
> >       qemu_fdt_add_subnode(ms->fdt, name);
> >       qemu_fdt_setprop_string(ms->fdt, name, "compatible",
> > "ns16550a");
> >       qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg",
> > -                                 2, s->memmap[VIRT_UART0].base,
> > -                                 2, s->memmap[VIRT_UART0].size);
> > +                                 2, s->memmap[memId].base,
> > +                                 2, s->memmap[memId].size);
> >       qemu_fdt_setprop_cell(ms->fdt, name, "clock-frequency",
> > 3686400);
> >       qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent",
> > irq_mmio_phandle);
> >       if (s->aia_type == VIRT_AIA_TYPE_NONE) {
> > -        qemu_fdt_setprop_cell(ms->fdt, name, "interrupts",
> > UART0_IRQ);
> > +        qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", irqNo);
> >       } else {
> > -        qemu_fdt_setprop_cells(ms->fdt, name, "interrupts",
> > UART0_IRQ, 0x4);
> > +        qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", irqNo,
> > 0x4);
> > +    }
> > +
> > +    if (VIRT_UART0 == memId) {
> > +        qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path",
> > name);
> > +        qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0",
> > name);
> >       }
> > +}
> >   
> > -    qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path",
> > name);
> > -    qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name);
> > +static void create_fdt_uarts(RISCVVirtState *s, uint32_t
> > irq_mmio_phandle)
> > +{
> > +    if (s->uart1_present) {
> > +        create_fdt_uart(s, irq_mmio_phandle, VIRT_UART1,
> > UART1_IRQ);
> > +    }
> > +    create_fdt_uart(s, irq_mmio_phandle, VIRT_UART0, UART0_IRQ);
> >   }
> >   
> >   static void create_fdt_rtc(RISCVVirtState *s,
> > @@ -1023,7 +1036,7 @@ static void finalize_fdt(RISCVVirtState *s)
> >   
> >       create_fdt_reset(s, &phandle);
> >   
> > -    create_fdt_uart(s, irq_mmio_phandle);
> > +    create_fdt_uarts(s, irq_mmio_phandle);
> >   
> >       create_fdt_rtc(s, irq_mmio_phandle);
> >   }
> > @@ -1567,6 +1580,13 @@ static void virt_machine_init(MachineState
> > *machine)
> >           0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193,
> >           serial_hd(0), DEVICE_LITTLE_ENDIAN);
> >   
> > +    if (serial_hd(1)) {
> > +        serial_mm_init(system_memory, s->memmap[VIRT_UART1].base,
> > +            0, qdev_get_gpio_in(mmio_irqchip, UART1_IRQ), 399193,
> > +            serial_hd(1), DEVICE_LITTLE_ENDIAN);
> > +        s->uart1_present = true;
> > +    }
> > +
> >       sysbus_create_simple("goldfish_rtc", s-
> > >memmap[VIRT_RTC].base,
> >           qdev_get_gpio_in(mmio_irqchip, RTC_IRQ));
> >   
> > diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
> > index 18a2a323a3..7d57c8292e 100644
> > --- a/include/hw/riscv/virt.h
> > +++ b/include/hw/riscv/virt.h
> > @@ -59,6 +59,7 @@ struct RISCVVirtState {
> >       int aia_guests;
> >       char *oem_id;
> >       char *oem_table_id;
> > +    bool uart1_present;
> >       OnOffAuto acpi;
> >       const MemMapEntry *memmap;
> >       struct GPEXHost *gpex_host;
> > @@ -78,6 +79,7 @@ enum {
> >       VIRT_APLIC_S,
> >       VIRT_UART0,
> >       VIRT_VIRTIO,
> > +    VIRT_UART1,
> >       VIRT_FW_CFG,
> >       VIRT_IMSIC_M,
> >       VIRT_IMSIC_S,
> > @@ -93,6 +95,7 @@ enum {
> >   enum {
> >       UART0_IRQ = 10,
> >       RTC_IRQ = 11,
> > +    UART1_IRQ = 12,
> >       VIRTIO_IRQ = 1, /* 1 to 8 */
> >       VIRTIO_COUNT = 8,
> >       PCIE_IRQ = 0x20, /* 32 to 35 */

Reply via email to