The 'virt' RISC-V machine hardcodes its DRAM base to 0x80000000.
Real-world RISC-V SoCs put DRAM elsewhere (SpacemiT K1 at 0x40000000,
SpacemiT K3 at 0x100000000, etc.), which forces ports that target
those boards to build separate kernels for QEMU testing.

Add a 'dram-base' string property that lets users move VIRT_DRAM up
without rebuilding QEMU.  Values must be 2 MiB-aligned and at or
above the default 0x80000000 to avoid colliding with statically-
placed MMIO regions (PCIE_MMIO ends at 0x80000000).  Lower bases
would also require moving PCIE_MMIO/IMSIC/etc. and are rejected with
an explicit error pointing at the cause.

Example:
  qemu-system-riscv64 -machine virt,dram-base=0x100000000 -m 6G ...
runs OpenSBI at firmware base 0x100000000, matching K3 memory layout.

Signed-off-by: Shawn Rutledge <[email protected]>
---
 hw/riscv/virt.c         | 59 ++++++++++++++++++++++++++++++++++++++++-
 include/hw/riscv/virt.h |  2 ++
 2 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 51bac47a91..6ccc98842f 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -20,6 +20,7 @@
 
 #include "qemu/osdep.h"
 #include "qemu/units.h"
+#include "qemu/cutils.h"
 #include "qemu/error-report.h"
 #include "qemu/guest-random.h"
 #include "qapi/error.h"
@@ -1307,7 +1308,13 @@ static void virt_machine_init(MachineState *machine)
     int i, base_hartid, hart_count;
     int socket_count = riscv_socket_count(machine);
 
-    s->memmap = virt_memmap;
+    if (s->dram_base) {
+        s->memmap_storage = g_memdup2(virt_memmap, sizeof(virt_memmap));
+        s->memmap_storage[VIRT_DRAM].base = s->dram_base;
+        s->memmap = s->memmap_storage;
+    } else {
+        s->memmap = virt_memmap;
+    }
 
     /* Check socket count limit */
     if (VIRT_SOCKETS_MAX < socket_count) {
@@ -1545,6 +1552,7 @@ static void virt_machine_instance_finalize(Object *obj)
     }
     g_free(s->oem_id);
     g_free(s->oem_table_id);
+    g_free(s->memmap_storage);
 }
 
 static void virt_machine_instance_init(Object *obj)
@@ -1616,6 +1624,46 @@ static void virt_set_aia(Object *obj, const char *val, 
Error **errp)
     }
 }
 
+static char *virt_get_dram_base(Object *obj, Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+    uint64_t val = s->dram_base ? s->dram_base : virt_memmap[VIRT_DRAM].base;
+
+    return g_strdup_printf("0x%" PRIx64, val);
+}
+
+static void virt_set_dram_base(Object *obj, const char *val, Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+    const char *endptr;
+    uint64_t base;
+
+    if (qemu_strtou64(val, &endptr, 0, &base) < 0 || *endptr != '\0') {
+        error_setg(errp, "Invalid dram-base value '%s'", val);
+        return;
+    }
+    /*
+     * DRAM must clear all statically-placed MMIO regions in virt_memmap[]
+     * (PCIE_MMIO ends at 0x80000000) and be 2 MiB-aligned so a standard
+     * OpenSBI + kernel layout (firmware at base, kernel at base+0x200000)
+     * fits.  Values below the default base would collide with on-board
+     * MMIO and are rejected.
+     */
+    if (base < virt_memmap[VIRT_DRAM].base) {
+        error_setg(errp,
+                   "dram-base 0x%" PRIx64 " is below default 0x%" PRIx64
+                   "; would collide with static MMIO regions",
+                   base, (uint64_t)virt_memmap[VIRT_DRAM].base);
+        return;
+    }
+    if (base & (2 * MiB - 1)) {
+        error_setg(errp, "dram-base 0x%" PRIx64 " must be 2 MiB-aligned",
+                   base);
+        return;
+    }
+    s->dram_base = base;
+}
+
 static bool virt_get_aclint(Object *obj, Error **errp)
 {
     RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
@@ -1744,6 +1792,15 @@ static void virt_machine_class_init(ObjectClass *oc, 
const void *data)
     machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
 #endif
 
+    object_class_property_add_str(oc, "dram-base", virt_get_dram_base,
+                                  virt_set_dram_base);
+    object_class_property_set_description(oc, "dram-base",
+                                          "Base physical address of DRAM "
+                                          "(default 0x80000000). Must be "
+                                          "2 MiB-aligned and at or above "
+                                          "the default to avoid colliding "
+                                          "with statically-placed MMIO.");
+
     object_class_property_add_bool(oc, "aclint", virt_get_aclint,
                                    virt_set_aclint);
     object_class_property_set_description(oc, "aclint",
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index 36a2def410..be8a4ac049 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -61,6 +61,8 @@ struct RISCVVirtState {
     char *oem_table_id;
     OnOffAuto acpi;
     const MemMapEntry *memmap;
+    MemMapEntry *memmap_storage; /* g_malloc'd copy when dram-base override is 
set */
+    uint64_t dram_base;          /* 0 = use compiled-in default (0x80000000) */
     struct GPEXHost *gpex_host;
     OnOffAuto iommu_sys;
     uint16_t pci_iommu_bdf;
-- 
2.55.0


Reply via email to