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(-)
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>
---
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 */
--
2.34.1
On 18/7/26 04:16, 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 > > Note that UART1 is placed at different page from UART0 to > support page level isolation. > > Signed-off-by: Yanfeng Liu <yfliu2008@qq.com> > --- > 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(-) Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
© 2016 - 2026 Red Hat, Inc.