Skip to content

Commit 2eb36e6

Browse files
committed
hw/riscv/nemu: Add device tree generation, DTB loading and virtio devices
1 parent 4643f51 commit 2eb36e6

File tree

1 file changed

+223
-8
lines changed

1 file changed

+223
-8
lines changed

hw/riscv/nemu.c

Lines changed: 223 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555
enum {
5656
UART0_IRQ = 10,
5757
RTC_IRQ = 11,
58-
VIRTIO_IRQ = 1, /* From IRQ to (IRQ + COUNT - 1) */
59-
VIRTIO_COUNT = 1,
58+
VIRTIO_IRQ = 5, /* 1 to 8 */
59+
VIRTIO_COUNT = 2,
6060
};
6161

6262
enum {
@@ -74,7 +74,8 @@ enum {
7474
* Freedom E310 G000 supports 51 interrupt sources. We use the value
7575
* of G002 and G003, so it is 53 (including interrupt source 0).
7676
*/
77-
#define PLIC_NUM_SOURCES 53
77+
#define PLIC_NUM_SOURCES 32
78+
// #define PLIC_NUM_SOURCES 53
7879
#define PLIC_NUM_PRIORITIES 7
7980
#define PLIC_PRIORITY_BASE 0x00
8081
#define PLIC_PENDING_BASE 0x1000
@@ -83,11 +84,14 @@ enum {
8384
#define PLIC_CONTEXT_BASE 0x200000
8485
#define PLIC_CONTEXT_STRIDE 0x1000
8586

87+
/* Timebase frequency from NEMU riscv64-xs_defconfig: 10 MHz */
88+
#define NEMU_TIMEBASE_FREQ 10000000
89+
8690
static const MemMapEntry nemu_memmap[] = {
8791
[NEMU_MROM] = { 0x1000, 0xf000 },
88-
[NEMU_VIRTIO] = { 0x10001000, 0x1000 },
89-
[NEMU_PLIC] = { 0x3c000000, 0x4000000 },
92+
[NEMU_VIRTIO] ={ 0x10001000, 0x1000 },
9093
[NEMU_CLINT] = { 0x38000000, 0x10000 },
94+
[NEMU_PLIC] = { 0x3c000000, 0x4000000 },
9195
[NEMU_UARTLITE] = { 0x40600000, 0x1000 },
9296
[NEMU_GCPT] = { 0x50000000, 0x8000000 },
9397
[NEMU_DRAM] = { 0x80000000, 0x0 },
@@ -455,11 +459,203 @@ static void nemu_load_firmware(MachineState *machine)
455459
info_report("%s %lx", machine->kernel_filename, kernel_entry);
456460
}
457461

458-
prepare_start:
462+
prepare_start:;
463+
uint64_t fdt_load_addr = riscv_compute_fdt_addr(memmap[NEMU_DRAM].base,
464+
machine->ram_size,
465+
machine);
466+
/* On checkpoint restore, guest RAM is already inflated — skip loading the
467+
* FDT to avoid overwriting restored state near the top of RAM. */
468+
if (!s->nemu_args.checkpoint) {
469+
riscv_load_fdt(fdt_load_addr, machine->fdt);
470+
}
459471
/* load the reset vector */
460472
riscv_setup_rom_reset_vec(machine, &s->soc[0], memmap[NEMU_DRAM].base,
461473
memmap[NEMU_MROM].base, memmap[NEMU_MROM].size,
462-
memmap[NEMU_DRAM].base, memmap[NEMU_DRAM].base);
474+
memmap[NEMU_DRAM].base, fdt_load_addr);
475+
}
476+
477+
/*
478+
* DTSGen.py vs NEMU (riscv64-xs_defconfig) device gap analysis:
479+
* cpus INCLUDED NEMU has CPUs with interrupt controllers
480+
* memory INCLUDED NEMU DRAM at 0x80000000
481+
* clint @ 0x38000000 INCLUDED CONFIG_CLINT_MMIO=0x38000000
482+
* plic @ 0x3c000000 INCLUDED CONFIG_PLIC_ADDRESS=0x3c000000
483+
* uartlite @ 0x40600000 INCLUDED CONFIG_UARTLITE_MMIO=0x40600000
484+
* rng-seed EXCLUDED QEMU internal, no real NEMU device
485+
* reserved-memory EXCLUDED GCPT (0x50000000) is QEMU checkpoint
486+
* infrastructure, not exported to guest
487+
* nemu_sdhci EXCLUDED not needed per project requirements
488+
* virtio-mmio EXCLUDED QEMU-only addition, absent from NEMU
489+
*/
490+
static void nemu_create_fdt(NEMUState *s, const MemMapEntry *memmap,
491+
MachineState *machine)
492+
{
493+
int fdt_size;
494+
int cpu;
495+
char *nodename;
496+
uint32_t phandle = 1;
497+
uint32_t uart_plic_phandle = 0;
498+
uint32_t *intc_phandles;
499+
int num_harts = machine->smp.cpus;
500+
int num_sockets = riscv_socket_count(machine);
501+
502+
static const char * const clint_compat[] = {
503+
"sifive,clint0", "riscv,clint0"
504+
};
505+
static const char * const plic_compat[] = {
506+
"sifive,plic-1.0.0", "riscv,plic0"
507+
};
508+
509+
machine->fdt = create_device_tree(&fdt_size);
510+
if (!machine->fdt) {
511+
error_report("create_device_tree() failed");
512+
exit(1);
513+
}
514+
515+
/* Root node */
516+
qemu_fdt_setprop_string(machine->fdt, "/", "model", "XiangShan");
517+
qemu_fdt_setprop_string(machine->fdt, "/", "compatible", "xiangshan,nemu-board");
518+
qemu_fdt_setprop_cell(machine->fdt, "/", "#size-cells", 0x2);
519+
qemu_fdt_setprop_cell(machine->fdt, "/", "#address-cells", 0x2);
520+
521+
/* /chosen */
522+
qemu_fdt_add_subnode(machine->fdt, "/chosen");
523+
if (machine->kernel_cmdline && machine->kernel_cmdline[0]) {
524+
qemu_fdt_setprop_string(machine->fdt, "/chosen", "bootargs",
525+
machine->kernel_cmdline);
526+
}
527+
528+
/* /cpus */
529+
qemu_fdt_add_subnode(machine->fdt, "/cpus");
530+
qemu_fdt_setprop_cell(machine->fdt, "/cpus", "#size-cells", 0x0);
531+
qemu_fdt_setprop_cell(machine->fdt, "/cpus", "#address-cells", 0x1);
532+
qemu_fdt_setprop_cell(machine->fdt, "/cpus", "timebase-frequency",
533+
NEMU_TIMEBASE_FREQ);
534+
535+
intc_phandles = g_new0(uint32_t, num_harts);
536+
537+
/* Iterate per-socket so we index soc[i].harts[] correctly and use the
538+
* actual hartid (mhartid) for DT node names and reg properties. */
539+
cpu = 0;
540+
for (int sock = 0; sock < num_sockets; sock++) {
541+
int sock_harts = riscv_socket_hart_count(machine, sock);
542+
for (int h = 0; h < sock_harts; h++, cpu++) {
543+
RISCVCPU *cpu_ptr = &s->soc[sock].harts[h];
544+
uint32_t hartid = (uint32_t)cpu_ptr->env.mhartid;
545+
char *cpu_name = g_strdup_printf("/cpus/cpu@%u", hartid);
546+
char *intc_name = g_strdup_printf("/cpus/cpu@%u/interrupt-controller", hartid);
547+
548+
intc_phandles[cpu] = phandle++;
549+
550+
qemu_fdt_add_subnode(machine->fdt, cpu_name);
551+
qemu_fdt_setprop_string(machine->fdt, cpu_name, "compatible", "riscv");
552+
qemu_fdt_setprop_string(machine->fdt, cpu_name, "device_type", "cpu");
553+
qemu_fdt_setprop_string(machine->fdt, cpu_name, "status", "okay");
554+
if (cpu_ptr->cfg.satp_mode.supported != 0) {
555+
bool is_32bit = riscv_cpu_mxl(&cpu_ptr->env) == MXL_RV32;
556+
uint8_t satp_max = satp_mode_max_from_map(cpu_ptr->cfg.satp_mode.map);
557+
qemu_fdt_setprop_string(machine->fdt, cpu_name, "mmu-type",
558+
satp_mode_str(satp_max, is_32bit));
559+
}
560+
qemu_fdt_setprop_cell(machine->fdt, cpu_name, "reg", hartid);
561+
riscv_isa_write_fdt(cpu_ptr, machine->fdt, cpu_name);
562+
563+
qemu_fdt_add_subnode(machine->fdt, intc_name);
564+
qemu_fdt_setprop_string(machine->fdt, intc_name, "compatible",
565+
"riscv,cpu-intc");
566+
qemu_fdt_setprop(machine->fdt, intc_name, "interrupt-controller", NULL, 0);
567+
qemu_fdt_setprop_cell(machine->fdt, intc_name, "#interrupt-cells", 1);
568+
qemu_fdt_setprop_cell(machine->fdt, intc_name, "phandle",
569+
intc_phandles[cpu]);
570+
571+
g_free(cpu_name);
572+
g_free(intc_name);
573+
} /* h loop */
574+
} /* sock loop */
575+
576+
/* /memory */
577+
nodename = g_strdup_printf("/memory@%" PRIx64, (uint64_t)memmap[NEMU_DRAM].base);
578+
qemu_fdt_add_subnode(machine->fdt, nodename);
579+
qemu_fdt_setprop_string(machine->fdt, nodename, "device_type", "memory");
580+
qemu_fdt_setprop_cells(machine->fdt, nodename, "reg",
581+
0x0, (uint32_t)memmap[NEMU_DRAM].base,
582+
(uint32_t)(machine->ram_size >> 32), (uint32_t)machine->ram_size);
583+
g_free(nodename);
584+
585+
/* /soc */
586+
qemu_fdt_add_subnode(machine->fdt, "/soc");
587+
qemu_fdt_setprop(machine->fdt, "/soc", "ranges", NULL, 0);
588+
qemu_fdt_setprop_string(machine->fdt, "/soc", "compatible", "simple-bus");
589+
qemu_fdt_setprop_cell(machine->fdt, "/soc", "#size-cells", 0x2);
590+
qemu_fdt_setprop_cell(machine->fdt, "/soc", "#address-cells", 0x2);
591+
592+
cpu = 0;
593+
for (int sock = 0; sock < num_sockets; sock++) {
594+
int sock_harts = riscv_socket_hart_count(machine, sock);
595+
uint64_t clint_base = memmap[NEMU_CLINT].base +
596+
(uint64_t)sock * memmap[NEMU_CLINT].size;
597+
uint64_t plic_base = memmap[NEMU_PLIC].base +
598+
(uint64_t)sock * memmap[NEMU_PLIC].size;
599+
uint32_t *clint_cells = g_new0(uint32_t, sock_harts * 4);
600+
uint32_t *plic_cells = g_new0(uint32_t, sock_harts * 4);
601+
602+
for (int h = 0; h < sock_harts; h++, cpu++) {
603+
clint_cells[h * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
604+
clint_cells[h * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
605+
clint_cells[h * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
606+
clint_cells[h * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
607+
plic_cells[h * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
608+
plic_cells[h * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
609+
plic_cells[h * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
610+
plic_cells[h * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
611+
}
612+
613+
nodename = g_strdup_printf("/soc/clint@%" PRIx64, clint_base);
614+
qemu_fdt_add_subnode(machine->fdt, nodename);
615+
qemu_fdt_setprop_string_array(machine->fdt, nodename, "compatible",
616+
(char **)&clint_compat,
617+
ARRAY_SIZE(clint_compat));
618+
qemu_fdt_setprop_cells(machine->fdt, nodename, "reg",
619+
0x0, (uint32_t)clint_base, 0x0, memmap[NEMU_CLINT].size);
620+
qemu_fdt_setprop(machine->fdt, nodename, "interrupts-extended",
621+
clint_cells, sock_harts * sizeof(uint32_t) * 4);
622+
g_free(nodename);
623+
g_free(clint_cells);
624+
625+
uint32_t plic_phandle = phandle++;
626+
if (sock == 0) {
627+
uart_plic_phandle = plic_phandle;
628+
}
629+
nodename = g_strdup_printf("/soc/plic@%" PRIx64, plic_base);
630+
qemu_fdt_add_subnode(machine->fdt, nodename);
631+
qemu_fdt_setprop_cell(machine->fdt, nodename, "#interrupt-cells", 1);
632+
qemu_fdt_setprop_cell(machine->fdt, nodename, "#address-cells", 0);
633+
qemu_fdt_setprop_string_array(machine->fdt, nodename, "compatible",
634+
(char **)&plic_compat,
635+
ARRAY_SIZE(plic_compat));
636+
qemu_fdt_setprop(machine->fdt, nodename, "interrupt-controller", NULL, 0);
637+
qemu_fdt_setprop(machine->fdt, nodename, "interrupts-extended",
638+
plic_cells, sock_harts * sizeof(uint32_t) * 4);
639+
qemu_fdt_setprop_cells(machine->fdt, nodename, "reg",
640+
0x0, (uint32_t)plic_base, 0x0, memmap[NEMU_PLIC].size);
641+
qemu_fdt_setprop_cell(machine->fdt, nodename, "riscv,ndev", PLIC_NUM_SOURCES - 1);
642+
qemu_fdt_setprop_cell(machine->fdt, nodename, "phandle", plic_phandle);
643+
g_free(nodename);
644+
g_free(plic_cells);
645+
}
646+
g_free(intc_phandles);
647+
648+
/* UARTLite */
649+
nodename = g_strdup_printf("/soc/serial@%" PRIx64, (uint64_t)memmap[NEMU_UARTLITE].base);
650+
qemu_fdt_add_subnode(machine->fdt, nodename);
651+
qemu_fdt_setprop_string(machine->fdt, nodename, "compatible",
652+
"xlnx,xps-uartlite-1.00.a");
653+
qemu_fdt_setprop_cells(machine->fdt, nodename, "reg",
654+
0x0, memmap[NEMU_UARTLITE].base, 0x0, memmap[NEMU_UARTLITE].size);
655+
qemu_fdt_setprop_cell(machine->fdt, nodename, "interrupts", UART0_IRQ);
656+
qemu_fdt_setprop_cell(machine->fdt, nodename, "interrupt-parent", uart_plic_phandle);
657+
qemu_fdt_setprop_string(machine->fdt, "/chosen", "stdout-path", nodename);
658+
g_free(nodename);
463659
}
464660

465661
static DeviceState *nemu_create_plic(const MemMapEntry *memmap, int socket,
@@ -578,13 +774,32 @@ static void nemu_machine_init(MachineState *machine)
578774
memory_region_add_subregion(system_memory, memmap[NEMU_UARTLITE].base,
579775
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
580776

581-
/* VirtIO MMIO devices */
777+
/*
778+
* VirtIO MMIO devices: instantiated for QEMU-internal use only
779+
* (block device for checkpoint image loading, serial for sync).
780+
* These are absent from the NEMU ISA simulator and are intentionally
781+
* excluded from the generated device tree so guest software does not
782+
* attempt to probe them. External DTBs passed via -dtb may include
783+
* virtio-mmio nodes if desired.
784+
*/
582785
for (i = 0; i < VIRTIO_COUNT; i++) {
583786
sysbus_create_simple("virtio-mmio",
584787
memmap[NEMU_VIRTIO].base + i * memmap[NEMU_VIRTIO].size,
585788
qdev_get_gpio_in(s->irqchip[0], VIRTIO_IRQ + i));
586789
}
587790

791+
/* Load or generate device tree */
792+
if (machine->dtb) {
793+
int fdt_size;
794+
machine->fdt = load_device_tree(machine->dtb, &fdt_size);
795+
if (!machine->fdt) {
796+
error_report("load_device_tree() failed");
797+
exit(1);
798+
}
799+
} else {
800+
nemu_create_fdt(s, memmap, machine);
801+
}
802+
588803
simpoint_init(machine);
589804
nemu_load_firmware(machine);
590805
multicore_checkpoint_init(machine);

0 commit comments

Comments
 (0)