diff mbox series

[v2,3/4] hw/openrisc/openrisc_sim; Add support for loading a device tree

Message ID 20220210123403.2059926-4-shorne@gmail.com (mailing list archive)
State New, archived
Headers show
Series OpenRISC Device Tree Support | expand

Commit Message

Stafford Horne Feb. 10, 2022, 12:34 p.m. UTC
Using the device tree means that qemu can now directly tell
the kernel what hardware is configured rather than use having
to maintain and update a separate device tree file.

This patch adds device tree support for the OpenRISC simulator.
A device tree is built up based on the state of the configure
openrisc simulator.

This is then dumped to memory and the load address is passed to the
kernel in register r3.

Signed-off-by: Stafford Horne <shorne@gmail.com>
---
 configs/targets/or1k-softmmu.mak |   1 +
 hw/openrisc/openrisc_sim.c       | 159 ++++++++++++++++++++++++++++++-
 2 files changed, 156 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak
index 1dfb93e46d..9e1d4a1fb1 100644
--- a/configs/targets/or1k-softmmu.mak
+++ b/configs/targets/or1k-softmmu.mak
@@ -1,2 +1,3 @@ 
 TARGET_ARCH=openrisc
 TARGET_WORDS_BIGENDIAN=y
+TARGET_NEED_FDT=y
diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c
index d12b3e0c5e..d7c26af82c 100644
--- a/hw/openrisc/openrisc_sim.c
+++ b/hw/openrisc/openrisc_sim.c
@@ -29,14 +29,20 @@ 
 #include "net/net.h"
 #include "hw/loader.h"
 #include "hw/qdev-properties.h"
+#include "exec/address-spaces.h"
+#include "sysemu/device_tree.h"
 #include "sysemu/sysemu.h"
 #include "hw/sysbus.h"
 #include "sysemu/qtest.h"
 #include "sysemu/reset.h"
 #include "hw/core/split-irq.h"
 
+#include <libfdt.h>
+
 #define KERNEL_LOAD_ADDR 0x100
 
+#define OR1KSIM_CLK_MHZ 20000000
+
 #define TYPE_OR1KSIM_MACHINE MACHINE_TYPE_NAME("or1k-sim")
 #define OR1KSIM_MACHINE(obj) \
     OBJECT_CHECK(Or1ksimState, (obj), TYPE_OR1KSIM_MACHINE)
@@ -46,6 +52,8 @@  typedef struct Or1ksimState {
     MachineState parent_obj;
 
     /*< public >*/
+    void *fdt;
+    int fdt_size;
 
 } Or1ksimState;
 
@@ -74,6 +82,7 @@  static const struct MemmapEntry {
 
 static struct openrisc_boot_info {
     uint32_t bootstrap_pc;
+    uint32_t fdt_addr;
 } boot_info;
 
 static void main_cpu_reset(void *opaque)
@@ -84,6 +93,7 @@  static void main_cpu_reset(void *opaque)
     cpu_reset(CPU(cpu));
 
     cpu_set_pc(cs, boot_info.bootstrap_pc);
+    cpu_set_gpr(&cpu->env, 3, boot_info.fdt_addr);
 }
 
 static qemu_irq get_cpu_irq(OpenRISCCPU *cpus[], int cpunum, int irq_pin)
@@ -137,26 +147,29 @@  static void openrisc_sim_ompic_init(hwaddr base, int num_cpus,
     sysbus_mmio_map(s, 0, base);
 }
 
-static void openrisc_load_kernel(ram_addr_t ram_size,
+static hwaddr openrisc_load_kernel(ram_addr_t ram_size,
                                  const char *kernel_filename)
 {
     long kernel_size;
     uint64_t elf_entry;
+    uint64_t high_addr;
     hwaddr entry;
 
     if (kernel_filename && !qtest_enabled()) {
         kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
-                               &elf_entry, NULL, NULL, NULL, 1, EM_OPENRISC,
-                               1, 0);
+                               &elf_entry, NULL, &high_addr, NULL, 1,
+                               EM_OPENRISC, 1, 0);
         entry = elf_entry;
         if (kernel_size < 0) {
             kernel_size = load_uimage(kernel_filename,
                                       &entry, NULL, NULL, NULL, NULL);
+            high_addr = entry + kernel_size;
         }
         if (kernel_size < 0) {
             kernel_size = load_image_targphys(kernel_filename,
                                               KERNEL_LOAD_ADDR,
                                               ram_size - KERNEL_LOAD_ADDR);
+            high_addr = KERNEL_LOAD_ADDR + kernel_size;
         }
 
         if (entry <= 0) {
@@ -168,7 +181,139 @@  static void openrisc_load_kernel(ram_addr_t ram_size,
             exit(1);
         }
         boot_info.bootstrap_pc = entry;
+
+        return high_addr;
+    }
+    return 0;
+}
+
+static uint32_t openrisc_load_fdt(Or1ksimState *s, hwaddr load_start,
+    uint64_t mem_size)
+{
+    uint32_t fdt_addr;
+    int fdtsize = fdt_totalsize(s->fdt);
+
+    if (fdtsize <= 0) {
+        error_report("invalid device-tree");
+        exit(1);
+    }
+
+    /* We should put fdt right after the kernel */
+    fdt_addr = ROUND_UP(load_start, 4);
+
+    fdt_pack(s->fdt);
+    /* copy in the device tree */
+    qemu_fdt_dumpdtb(s->fdt, fdtsize);
+
+    rom_add_blob_fixed_as("fdt", s->fdt, fdtsize, fdt_addr,
+                          &address_space_memory);
+
+    return fdt_addr;
+}
+
+static void openrisc_create_fdt(Or1ksimState *s,
+    const struct MemmapEntry *memmap, int num_cpus, uint64_t mem_size,
+    const char *cmdline)
+{
+    void *fdt;
+    int cpu;
+    char *nodename;
+    int pic_ph;
+
+    fdt = s->fdt = create_device_tree(&s->fdt_size);
+    if (!fdt) {
+        error_report("create_device_tree() failed");
+        exit(1);
+    }
+
+    qemu_fdt_setprop_string(fdt, "/", "compatible", "opencores,or1ksim");
+    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x1);
+    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1);
+
+    nodename = g_strdup_printf("/memory@%lx",
+                               (long)memmap[OR1KSIM_DRAM].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+                           memmap[OR1KSIM_DRAM].base, mem_size);
+    qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+    g_free(nodename);
+
+    qemu_fdt_add_subnode(fdt, "/cpus");
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+    qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+
+    for (cpu = 0; cpu < num_cpus; cpu++) {
+        nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_string(fdt, nodename, "compatible",
+                                "opencores,or1200-rtlsvn481");
+        qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+        qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency",
+                              OR1KSIM_CLK_MHZ);
+        g_free(nodename);
+    }
+
+    if (num_cpus > 0) {
+        nodename = g_strdup_printf("/ompic@%lx",
+                                   (long)memmap[OR1KSIM_OMPIC].base);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_string(fdt, nodename, "compatible", "openrisc,ompic");
+        qemu_fdt_setprop_cells(fdt, nodename, "reg",
+                               memmap[OR1KSIM_OMPIC].base,
+                               memmap[OR1KSIM_OMPIC].size);
+        qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
+        qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 0);
+        qemu_fdt_setprop_cell(fdt, nodename, "interrupts", OR1KSIM_OMPIC_IRQ);
+        g_free(nodename);
     }
+
+    nodename = (char *)"/pic";
+    qemu_fdt_add_subnode(fdt, nodename);
+    pic_ph = qemu_fdt_alloc_phandle(fdt);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible",
+                            "opencores,or1k-pic-level");
+    qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
+    qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
+    qemu_fdt_setprop_cell(fdt, nodename, "phandle", pic_ph);
+
+    qemu_fdt_setprop_cell(fdt, "/", "interrupt-parent", pic_ph);
+
+    if (nd_table[0].used) {
+        nodename = g_strdup_printf("/ethoc@%lx",
+                                   (long)memmap[OR1KSIM_ETHOC].base);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_string(fdt, nodename, "compatible", "opencores,ethoc");
+        qemu_fdt_setprop_cells(fdt, nodename, "reg",
+                               memmap[OR1KSIM_ETHOC].base,
+                               memmap[OR1KSIM_ETHOC].size);
+        qemu_fdt_setprop_cell(fdt, nodename, "interrupts", OR1KSIM_ETHOC_IRQ);
+        qemu_fdt_setprop(fdt, nodename, "big-endian", NULL, 0);
+
+        qemu_fdt_add_subnode(fdt, "/aliases");
+        qemu_fdt_setprop_string(fdt, "/aliases", "enet0", nodename);
+        g_free(nodename);
+    }
+
+    nodename = g_strdup_printf("/serial@%lx",
+                               (long)memmap[OR1KSIM_UART].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+                           memmap[OR1KSIM_UART].base,
+                           memmap[OR1KSIM_UART].size);
+    qemu_fdt_setprop_cell(fdt, nodename, "interrupts", OR1KSIM_UART_IRQ);
+    qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", OR1KSIM_CLK_MHZ);
+    qemu_fdt_setprop(fdt, nodename, "big-endian", NULL, 0);
+
+    qemu_fdt_add_subnode(fdt, "/chosen");
+    qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
+    if (cmdline) {
+        qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+    }
+
+    qemu_fdt_setprop_string(fdt, "/aliases", "uart0", nodename);
+
+    g_free(nodename);
 }
 
 static void openrisc_sim_init(MachineState *machine)
@@ -176,7 +321,9 @@  static void openrisc_sim_init(MachineState *machine)
     ram_addr_t ram_size = machine->ram_size;
     const char *kernel_filename = machine->kernel_filename;
     OpenRISCCPU *cpus[2] = {};
+    Or1ksimState *s = OR1KSIM_MACHINE(machine);
     MemoryRegion *ram;
+    hwaddr load_addr;
     qemu_irq serial_irq;
     int n;
     unsigned int smp_cpus = machine->smp.cpus;
@@ -218,7 +365,11 @@  static void openrisc_sim_init(MachineState *machine)
     serial_mm_init(get_system_memory(), or1ksim_memmap[OR1KSIM_UART].base, 0,
                    serial_irq, 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
 
-    openrisc_load_kernel(ram_size, kernel_filename);
+    openrisc_create_fdt(s, or1ksim_memmap, smp_cpus, machine->ram_size,
+                        machine->kernel_cmdline);
+
+    load_addr = openrisc_load_kernel(ram_size, kernel_filename);
+    boot_info.fdt_addr = openrisc_load_fdt(s, load_addr, machine->ram_size);
 }
 
 static void openrisc_sim_machine_init(ObjectClass *oc, void *data)