On Fri, 2026-07-03 at 12:24 +0800, Yanfeng Liu wrote: > This adds optional UART1 to RiscV virt board if required at > runtime to simplify multicore development. > > 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 > > Signed-off-by: Yanfeng Liu <[email protected]> > --- > docs/system/riscv/virt.rst | 5 ++++- > hw/riscv/virt-acpi-build.c | 12 ++++++++---- > hw/riscv/virt.c | 39 ++++++++++++++++++++++++++++-------- > -- > include/hw/riscv/virt.h | 3 +++ > 4 files changed, 44 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..9a112ccdef 100644 > --- a/hw/riscv/virt.c > +++ b/hw/riscv/virt.c > @@ -96,6 +96,7 @@ static const MemMapEntry virt_memmap[] = { > [VIRT_APLIC_S] = { 0xd000000, APLIC_SIZE(VIRT_CPUS_MAX) }, > [VIRT_UART0] = { 0x10000000, 0x100 }, > [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, > + [VIRT_UART1] = { 0x1000a000, 0x100 },
This could be at address 0x10000100 instead and squish up against UART0. Otherwise: Reviewed-by: Alistair Francis <[email protected]> Alistair > [VIRT_FW_CFG] = { 0x10100000, 0x18 }, > [VIRT_FLASH] = { 0x20000000, 0x4000000 }, > [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE }, > @@ -186,7 +187,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 +834,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 +1035,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 +1579,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 */
