From: Ruslan Ruslichenko <[email protected]> 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 <[email protected]> --- 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 ([email protected]) + * + * 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
