[PATCH 19/27] hw/arm: add generic ARM machine initialized by FDT

Ruslan Ruslichenko posted 27 patches 1 week, 4 days ago
Maintainers: Peter Maydell <peter.maydell@linaro.org>, Alistair Francis <alistair@alistair23.me>, "Edgar E. Iglesias" <edgar.iglesias@gmail.com>, Eduardo Habkost <eduardo@habkost.net>, Marcel Apfelbaum <marcel.apfelbaum@gmail.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Yanan Wang <wangyanan55@huawei.com>, Zhao Liu <zhao1.liu@intel.com>, Paolo Bonzini <pbonzini@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, David Gibson <david@gibson.dropbear.id.au>, Peter Xu <peterx@redhat.com>
[PATCH 19/27] hw/arm: add generic ARM machine initialized by FDT
Posted by Ruslan Ruslichenko 1 week, 4 days ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

This patch introduces new ARM machine model, which is
fully instantiated from a Device Tree description.

This model uses fdt_generic framework to dynamically
construct system topology based on provided DTB, instead
of standard machines which rely on hardcoded models in
C files.

This allows to contruct machines with custom memory maps,
device structures without the need to modify and re-build
QEMU sources each time.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/arm/arm_generic_fdt.c | 166 +++++++++++++++++++++++++++++++++++++++
 hw/arm/meson.build       |   2 +
 2 files changed, 168 insertions(+)
 create mode 100644 hw/arm/arm_generic_fdt.c

diff --git a/hw/arm/arm_generic_fdt.c b/hw/arm/arm_generic_fdt.c
new file mode 100644
index 0000000000..d3912745c7
--- /dev/null
+++ b/hw/arm/arm_generic_fdt.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2012 Xilinx. Inc
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@xilinx.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "hw/core/boards.h"
+#include "hw/core/loader.h"
+#include "hw/core/hw-error.h"
+#include "qapi/error.h"
+#include "hw/block/flash.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
+#include "system/system.h"
+#include "system/qtest.h"
+#include "hw/arm/boot.h"
+#include "hw/arm/machines-qom.h"
+
+#include <libfdt.h>
+#include "hw/core/fdt_generic_util.h"
+
+#define GENERAL_MACHINE_NAME "arm-generic-fdt"
+
+#define QTEST_RUNNING (qtest_enabled() && qtest_driver())
+
+#define SMP_BOOT_ADDR 0xfffffff0
+/* Meaningless, but keeps arm boot happy */
+#define SMP_BOOTREG_ADDR 0xfffffffc
+
+static struct arm_boot_info arm_generic_fdt_binfo = {};
+
+typedef struct {
+    ram_addr_t ram_kernel_base;
+    ram_addr_t ram_kernel_size;
+} memory_info;
+
+static memory_info init_memory(void *fdt, ram_addr_t ram_size)
+{
+    FDTMachineInfo *fdti;
+    char node_path[DT_PATH_LENGTH];
+    MemoryRegion *mem_area;
+    memory_info kernel_info;
+    Error *errp = NULL;
+
+    /* Find a memory node or add new one if needed */
+    while (qemu_devtree_get_node_by_name(fdt, node_path, "memory")) {
+        qemu_fdt_add_subnode(fdt, "/memory@0");
+        qemu_fdt_setprop_cells(fdt, "/memory@0", "reg", 0, ram_size);
+    }
+
+    if (!qemu_fdt_getprop(fdt, "/memory", "compatible", NULL, 0, NULL)) {
+        qemu_fdt_setprop_string(fdt, "/memory", "compatible",
+                                "qemu:memory-region");
+        qemu_fdt_setprop_cells(fdt, "/memory", "qemu,ram", 1);
+    }
+
+    /* Instantiate peripherals from the FDT.  */
+    fdti = fdt_generic_create_machine(fdt, NULL);
+
+    mem_area = MEMORY_REGION(object_resolve_path(node_path, NULL));
+
+    /*
+     * Look for the optional kernel-base prop. If not found fallback to
+     * start of memory.
+     */
+    kernel_info.ram_kernel_base = qemu_fdt_getprop_sized_cell(fdt, "/",
+                                              "kernel-base", 0, 2, &errp);
+    if (errp) {
+        kernel_info.ram_kernel_base = object_property_get_int(OBJECT(mem_area),
+                                                              "addr", NULL);
+    }
+
+    kernel_info.ram_kernel_size = object_property_get_int(OBJECT(mem_area),
+                                                          "size", NULL);
+
+    if (kernel_info.ram_kernel_size == -1) {
+        kernel_info.ram_kernel_size = ram_size;
+    }
+
+    fdt_init_destroy_fdti(fdti);
+
+    return kernel_info;
+}
+
+static void arm_generic_fdt_init(MachineState *machine)
+{
+    void *fdt = NULL, *sw_fdt = NULL;
+    int fdt_size, sw_fdt_size;
+    const char *dtb_arg, *hw_dtb_arg;
+    memory_info kernel_info;
+
+    dtb_arg = machine->dtb;
+    hw_dtb_arg = machine->hw_dtb;
+    if (!dtb_arg && !hw_dtb_arg) {
+        if (!QTEST_RUNNING) {
+            /*
+             * Just return without error if running qtest, as we never have a
+             * device tree
+             */
+            hw_error("DTB must be specified for %s machine model\n",
+                     MACHINE_GET_CLASS(machine)->name);
+        }
+        return;
+    }
+
+    /* Software dtb is always the -dtb arg */
+    if (dtb_arg) {
+        sw_fdt = load_device_tree(dtb_arg, &sw_fdt_size);
+        if (!sw_fdt) {
+            error_report("Error: Unable to load Device Tree %s", dtb_arg);
+            exit(1);
+        }
+    }
+
+    /* If the user provided a -hw-dtb, use it as the hw description.  */
+    if (hw_dtb_arg) {
+        fdt = load_device_tree(hw_dtb_arg, &fdt_size);
+        if (!fdt) {
+            error_report("Error: Unable to load Device Tree %s", hw_dtb_arg);
+            exit(1);
+        }
+    } else if (sw_fdt) {
+        fdt = sw_fdt;
+        fdt_size = sw_fdt_size;
+    }
+
+    kernel_info = init_memory(fdt, machine->ram_size);
+
+    arm_generic_fdt_binfo.ram_size = kernel_info.ram_kernel_size;
+    arm_generic_fdt_binfo.kernel_filename = machine->kernel_filename;
+    arm_generic_fdt_binfo.kernel_cmdline = machine->kernel_cmdline;
+    arm_generic_fdt_binfo.initrd_filename = machine->initrd_filename;
+    arm_generic_fdt_binfo.smp_loader_start = SMP_BOOT_ADDR;
+    arm_generic_fdt_binfo.smp_bootreg_addr = SMP_BOOTREG_ADDR;
+    arm_generic_fdt_binfo.board_id = 0xd32;
+    arm_generic_fdt_binfo.loader_start = kernel_info.ram_kernel_base;
+    arm_generic_fdt_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC;
+
+    if (machine->kernel_filename) {
+        arm_load_kernel(ARM_CPU(first_cpu), machine, &arm_generic_fdt_binfo);
+    }
+
+    return;
+}
+
+static void arm_generic_fdt_machine_init(MachineClass *mc)
+{
+    mc->desc = "ARM device tree driven machine model";
+    mc->init = arm_generic_fdt_init;
+    mc->max_cpus = 64;
+    mc->default_cpus = 64;
+}
+
+DEFINE_MACHINE_ARM("arm-generic-fdt", arm_generic_fdt_machine_init)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index aeaf654790..e606630c86 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -1,4 +1,6 @@
 arm_ss = ss.source_set()
+arm_ss.add(files('arm_generic_fdt.c'))
+
 arm_common_ss = ss.source_set()
 arm_common_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c'))
 arm_common_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
-- 
2.43.0