@samitouri / QOSamiQemu / commits / 2a99140258

riscv/virt: Add optional UART1

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 Note that UART1 is placed at different page from UART0 to support page level isolation. Signed-off-by: Yanfeng Liu <yfliu2008@qq.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <tencent_4EEBFC805F3E59863BEDC38094EF5A109206@qq.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>

Yanfeng Liu committed Jul 18, 2026 at 10:16 UTC 2a99140258feaf8bd05df5b16698dde6aec1148c
4 files changed +45 -15
docs/system/riscv/virt.rst
+4 -1
@@ -16,7 +16,7 @@ The ``virt`` machine supports the following devices:
16 * Core Local Interruptor (CLINT)
17 * Platform-Level Interrupt Controller (PLIC)
18 * CFI parallel NOR flash memory
19 -* 1 NS16550 compatible UART
19 +* Either 1 or 2 NS16550 compatible UARTs
20 * 1 Google Goldfish RTC
21 * 1 SiFive Test device
22 * 8 virtio-mmio transport devices
@@ -27,6 +27,9 @@ The hypervisor extension has been enabled for the default CPU, so virtual
27 machines with hypervisor extension can simply be used without explicitly
28 declaring.
29
30 +The second UART only exists if a backend is configured explicitly (e.g.
31 +with a second ``-serial`` command line option).
32 +
33 Hardware configuration information
34 ----------------------------------
35
hw/riscv/virt-acpi-build.c
+8 -4
@@ -177,11 +177,11 @@ static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count,
177
178 static void
179 acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
180 - uint32_t uart_irq)
180 + uint32_t uart_irq, int uartidx)
181 {
182 - Aml *dev = aml_device("COM0");
182 + Aml *dev = aml_device("COM%d", uartidx);
183 aml_append(dev, aml_name_decl("_HID", aml_string("RSCV0003")));
184 - aml_append(dev, aml_name_decl("_UID", aml_int(0)));
184 + aml_append(dev, aml_name_decl("_UID", aml_int(uartidx)));
185
186 Aml *crs = aml_resource_template();
187 aml_append(crs, aml_memory32_fixed(uart_memmap->base,
@@ -490,7 +490,11 @@ static void build_dsdt(GArray *table_data,
490 memmap[VIRT_APLIC_S].size, "RSCV0002");
491 }
492
493 - acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ);
493 + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ, 0);
494 + if (s->uart1_present) {
495 + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART1], UART1_IRQ, 1);
496 + }
497 +
498 if (virt_is_iommu_sys_enabled(s)) {
499 acpi_dsdt_add_iommu_sys(scope, &memmap[VIRT_IOMMU_SYS], IOMMU_SYS_IRQ);
500 }
hw/riscv/virt.c
+30 -10
@@ -97,6 +97,8 @@ static const MemMapEntry virt_memmap[] = {
97 [VIRT_APLIC_S] = { 0xd000000, APLIC_SIZE(VIRT_CPUS_MAX) },
98 [VIRT_UART0] = { 0x10000000, 0x100 },
99 [VIRT_VIRTIO] = { 0x10001000, 0x1000 },
100 + /* UART1 supports page isolation from UART0 */
101 + [VIRT_UART1] = { 0x1000a000, 0x100 },
102 [VIRT_FW_CFG] = { 0x10100000, 0x18 },
103 [VIRT_FLASH] = { 0x20000000, 0x4000000 },
104 [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE },
@@ -187,7 +189,8 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename,
189 FDT_MAX_INT_MAP_WIDTH] = {};
190 uint32_t *irq_map = full_irq_map;
191
190 - /* This code creates a standard swizzle of interrupts such that
192 + /*
193 + * This code creates a standard swizzle of interrupts such that
194 * each device's first interrupt is based on it's PCI_SLOT number.
195 * (See pci_swizzle_map_irq_fn())
196 *
@@ -805,28 +808,38 @@ static void create_fdt_reset(RISCVVirtState *s, uint32_t *phandle)
808 }
809
810 static void create_fdt_uart(RISCVVirtState *s,
808 - uint32_t irq_mmio_phandle)
811 + uint32_t irq_mmio_phandle, int memId, int irqNo)
812 {
813 g_autofree char *name = NULL;
814 MachineState *ms = MACHINE(s);
815
816 name = g_strdup_printf("/soc/serial@%"HWADDR_PRIx,
814 - s->memmap[VIRT_UART0].base);
817 + s->memmap[memId].base);
818 qemu_fdt_add_subnode(ms->fdt, name);
819 qemu_fdt_setprop_string(ms->fdt, name, "compatible", "ns16550a");
820 qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg",
818 - 2, s->memmap[VIRT_UART0].base,
819 - 2, s->memmap[VIRT_UART0].size);
821 + 2, s->memmap[memId].base,
822 + 2, s->memmap[memId].size);
823 qemu_fdt_setprop_cell(ms->fdt, name, "clock-frequency", 3686400);
824 qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle);
825 if (s->aia_type == VIRT_AIA_TYPE_NONE) {
823 - qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", UART0_IRQ);
826 + qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", irqNo);
827 } else {
825 - qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", UART0_IRQ, 0x4);
828 + qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", irqNo, 0x4);
829 + }
830 +
831 + if (VIRT_UART0 == memId) {
832 + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name);
833 + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name);
834 }
835 +}
836
828 - qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name);
829 - qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name);
837 +static void create_fdt_uarts(RISCVVirtState *s, uint32_t irq_mmio_phandle)
838 +{
839 + if (s->uart1_present) {
840 + create_fdt_uart(s, irq_mmio_phandle, VIRT_UART1, UART1_IRQ);
841 + }
842 + create_fdt_uart(s, irq_mmio_phandle, VIRT_UART0, UART0_IRQ);
843 }
844
845 static void create_fdt_rtc(RISCVVirtState *s,
@@ -996,7 +1009,7 @@ static void finalize_fdt(RISCVVirtState *s)
1009
1010 create_fdt_reset(s, &phandle);
1011
999 - create_fdt_uart(s, irq_mmio_phandle);
1012 + create_fdt_uarts(s, irq_mmio_phandle);
1013
1014 create_fdt_rtc(s, irq_mmio_phandle);
1015 }
@@ -1486,6 +1499,13 @@ static void virt_machine_init(MachineState *machine)
1499 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193,
1500 serial_hd(0), DEVICE_LITTLE_ENDIAN);
1501
1502 + if (serial_hd(1)) {
1503 + serial_mm_init(system_memory, s->memmap[VIRT_UART1].base,
1504 + 0, qdev_get_gpio_in(mmio_irqchip, UART1_IRQ), 399193,
1505 + serial_hd(1), DEVICE_LITTLE_ENDIAN);
1506 + s->uart1_present = true;
1507 + }
1508 +
1509 sysbus_create_simple("goldfish_rtc", s->memmap[VIRT_RTC].base,
1510 qdev_get_gpio_in(mmio_irqchip, RTC_IRQ));
1511
include/hw/riscv/virt.h
+3
@@ -59,6 +59,7 @@ struct RISCVVirtState {
59 int aia_guests;
60 char *oem_id;
61 char *oem_table_id;
62 + bool uart1_present;
63 OnOffAuto acpi;
64 const MemMapEntry *memmap;
65 struct GPEXHost *gpex_host;
@@ -79,6 +80,7 @@ enum {
80 VIRT_APLIC_S,
81 VIRT_UART0,
82 VIRT_VIRTIO,
83 + VIRT_UART1,
84 VIRT_FW_CFG,
85 VIRT_IMSIC_M,
86 VIRT_IMSIC_S,
@@ -94,6 +96,7 @@ enum {
96 enum {
97 UART0_IRQ = 10,
98 RTC_IRQ = 11,
99 + UART1_IRQ = 12,
100 VIRTIO_IRQ = 1, /* 1 to 8 */
101 VIRTIO_COUNT = 8,
102 PCIE_IRQ = 0x20, /* 32 to 35 */