5555enum {
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
6262enum {
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+
8690static 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
465661static 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