From 61fbff5a8e3f7dcb8c24b1c2edfabaf8ae4a2777 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Fri, 24 Aug 2018 23:34:42 +0100 Subject: [PATCH] riscv: virt machine: multiple UARTs Attach up to 16 serial devices, densely packed in the memory map and registered in the FDT. To ensure that BBL continues to find the default one, we register it last so that it ends up at the top of the fdt. Signed-off-by: Nathaniel Wesley Filardo --- hw/riscv/virt.c | 126 ++++++++++++++++++++++++++-------------- include/hw/riscv/virt.h | 2 +- 2 files changed, 85 insertions(+), 43 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 66dc4e07c3f..ba1fdfb82f2 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -44,6 +44,12 @@ #include +/* Each UART is given 0x100 bytes of memory map despite not needing nearly + * that much. Despite that, the existing memory map still supports up to + * 16 uarts, which is probably plenty. + */ +#define MAX_UARTS 16 + static const struct MemmapEntry { hwaddr base; hwaddr size; @@ -59,7 +65,7 @@ static const struct MemmapEntry { }; static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, - uint64_t mem_size, const char *cmdline) + uint64_t mem_size, const char *cmdline, uint32_t *out_plic_handle) { void *fdt; int cpu; @@ -200,28 +206,52 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size); + g_free(nodename); - nodename = g_strdup_printf("/uart@%lx", - (long)memmap[VIRT_UART0].base); - qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a"); - qemu_fdt_setprop_cells(fdt, nodename, "reg", - 0x0, memmap[VIRT_UART0].base, - 0x0, memmap[VIRT_UART0].size); - qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 3686400); - qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); - qemu_fdt_setprop_cells(fdt, nodename, "interrupts", UART0_IRQ); - + /* Install UART0 as /chosen/stdout-path */ + nodename = g_strdup_printf("/uart@%lx", (long)memmap[VIRT_UART0].base); qemu_fdt_add_subnode(fdt, "/chosen"); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); + g_free(nodename); if (cmdline) { qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); } - g_free(nodename); + + if (out_plic_handle) { + *out_plic_handle = plic_phandle; + } return fdt; } +static void +riscv_virt_serial_init(RISCVVirtState *s, const struct MemmapEntry *memmap, + MemoryRegion *system_memory, void *fdt, + uint32_t plic_phandle, int hdix, int uix) +{ + hwaddr addr; + char *nodename; + + addr = memmap[VIRT_UART0].base + uix * memmap[VIRT_UART0].size; + + /* Wire to memory */ + serial_mm_init(system_memory, addr, 0, + qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ + uix), 399193, + serial_hd(hdix), DEVICE_LITTLE_ENDIAN); + + /* Register with FDT */ + nodename = g_strdup_printf("/uart@%lx", (long)addr); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, addr, + 0x0, memmap[VIRT_UART0].size); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 3686400); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", UART0_IRQ + uix); + g_free(nodename); +} + static void riscv_virt_board_init(MachineState *machine) { const struct MemmapEntry *memmap = virt_memmap; @@ -232,8 +262,9 @@ static void riscv_virt_board_init(MachineState *machine) MemoryRegion *mask_rom = g_new(MemoryRegion, 1); char *plic_hart_config; size_t plic_hart_config_len; - int i; + int i, j; void *fdt; + uint32_t plic_phandle; hwaddr firmware_entry; /* Initialize SOC */ @@ -253,7 +284,44 @@ static void riscv_virt_board_init(MachineState *machine) main_mem); /* create device tree */ - fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); + fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, + &plic_phandle); + + /* create PLIC hart topology configuration string */ + plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus; + plic_hart_config = g_malloc0(plic_hart_config_len); + for (i = 0; i < smp_cpus; i++) { + if (i != 0) { + strncat(plic_hart_config, ",", plic_hart_config_len); + } + strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG, plic_hart_config_len); + plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1); + } + + /* create PLIC */ + s->plic = sifive_plic_create(memmap[VIRT_PLIC].base, + plic_hart_config, + VIRT_PLIC_NUM_SOURCES, + VIRT_PLIC_NUM_PRIORITIES, + VIRT_PLIC_PRIORITY_BASE, + VIRT_PLIC_PENDING_BASE, + VIRT_PLIC_ENABLE_BASE, + VIRT_PLIC_ENABLE_STRIDE, + VIRT_PLIC_CONTEXT_BASE, + VIRT_PLIC_CONTEXT_STRIDE, + memmap[VIRT_PLIC].size); + + /* Attach UARTs */ + for (i = 1, j = 1; (i < serial_max_hds()) && (j < MAX_UARTS); i++) { + if (serial_hd(i) == NULL) { + continue; + } + riscv_virt_serial_init(s, memmap, system_memory, fdt, plic_phandle, + i, j); + j++; + } + /* Attach the debug UART last, so BBL finds it first */ + riscv_virt_serial_init(s, memmap, system_memory, fdt, plic_phandle, 0, 0); /* boot rom */ memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom", @@ -303,29 +371,7 @@ static void riscv_virt_board_init(MachineState *machine) memmap[VIRT_MROM].base + sizeof(reset_vec), &address_space_memory); - /* create PLIC hart topology configuration string */ - plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus; - plic_hart_config = g_malloc0(plic_hart_config_len); - for (i = 0; i < smp_cpus; i++) { - if (i != 0) { - strncat(plic_hart_config, ",", plic_hart_config_len); - } - strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG, plic_hart_config_len); - plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1); - } - - /* MMIO */ - s->plic = sifive_plic_create(memmap[VIRT_PLIC].base, - plic_hart_config, - VIRT_PLIC_NUM_SOURCES, - VIRT_PLIC_NUM_PRIORITIES, - VIRT_PLIC_PRIORITY_BASE, - VIRT_PLIC_PENDING_BASE, - VIRT_PLIC_ENABLE_BASE, - VIRT_PLIC_ENABLE_STRIDE, - VIRT_PLIC_CONTEXT_BASE, - VIRT_PLIC_CONTEXT_STRIDE, - memmap[VIRT_PLIC].size); + /* Other MMIO: CLINT, TEST */ sifive_clint_create(memmap[VIRT_CLINT].base, memmap[VIRT_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); @@ -337,10 +383,6 @@ static void riscv_virt_board_init(MachineState *machine) qdev_get_gpio_in(DEVICE(s->plic), VIRTIO_IRQ + i)); } - serial_mm_init(system_memory, memmap[VIRT_UART0].base, - 0, qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ), 399193, - serial_hd(0), DEVICE_LITTLE_ENDIAN); - g_free(plic_hart_config); } diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 91163d6cbfe..4f3ae823b81 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -42,7 +42,7 @@ enum { }; enum { - UART0_IRQ = 10, + UART0_IRQ = 10, /* up to 25, inclusive */ VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, VIRTIO_NDEV = 10