arm: add device tree support
If compiled with CONFIG_FDT, allow user to specify a device tree file using the -dtb argument. If the machine supports it then the dtb will be loaded into memory and passed to the kernel on boot. Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: Grant Likely <grant.likely@secretlab.ca> [Peter Maydell: Use machine opt rather than global to pass dtb filename] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
41c1e2f54e
commit
412beee6a0
@ -373,6 +373,7 @@ obj-arm-y += vexpress.o
|
||||
obj-arm-y += strongarm.o
|
||||
obj-arm-y += collie.o
|
||||
obj-arm-y += pl041.o lm4549.o
|
||||
obj-arm-$(CONFIG_FDT) += device_tree.o
|
||||
|
||||
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
|
||||
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
|
||||
|
1
configure
vendored
1
configure
vendored
@ -3485,6 +3485,7 @@ case "$target_arch2" in
|
||||
gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
|
||||
target_phys_bits=32
|
||||
target_llong_alignment=4
|
||||
target_libs_softmmu="$fdt_libs"
|
||||
;;
|
||||
cris)
|
||||
target_nptl="yes"
|
||||
|
@ -29,6 +29,7 @@ struct arm_boot_info {
|
||||
const char *kernel_filename;
|
||||
const char *kernel_cmdline;
|
||||
const char *initrd_filename;
|
||||
const char *dtb_filename;
|
||||
target_phys_addr_t loader_start;
|
||||
/* multicore boards that use the default secondary core boot functions
|
||||
* need to put the address of the secondary boot code, the boot reg,
|
||||
|
102
hw/arm_boot.c
102
hw/arm_boot.c
@ -7,11 +7,14 @@
|
||||
* This code is licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "hw.h"
|
||||
#include "arm-misc.h"
|
||||
#include "sysemu.h"
|
||||
#include "boards.h"
|
||||
#include "loader.h"
|
||||
#include "elf.h"
|
||||
#include "device_tree.h"
|
||||
|
||||
#define KERNEL_ARGS_ADDR 0x100
|
||||
#define KERNEL_LOAD_ADDR 0x00010000
|
||||
@ -208,6 +211,67 @@ static void set_kernel_args_old(const struct arm_boot_info *info)
|
||||
}
|
||||
}
|
||||
|
||||
static int load_dtb(target_phys_addr_t addr, const struct arm_boot_info *binfo)
|
||||
{
|
||||
#ifdef CONFIG_FDT
|
||||
uint32_t mem_reg_property[] = { cpu_to_be32(binfo->loader_start),
|
||||
cpu_to_be32(binfo->ram_size) };
|
||||
void *fdt = NULL;
|
||||
char *filename;
|
||||
int size, rc;
|
||||
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename);
|
||||
if (!filename) {
|
||||
fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fdt = load_device_tree(filename, &size);
|
||||
if (!fdt) {
|
||||
fprintf(stderr, "Couldn't open dtb file %s\n", filename);
|
||||
g_free(filename);
|
||||
return -1;
|
||||
}
|
||||
g_free(filename);
|
||||
|
||||
rc = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
|
||||
sizeof(mem_reg_property));
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "couldn't set /memory/reg\n");
|
||||
}
|
||||
|
||||
rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
|
||||
binfo->kernel_cmdline);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
||||
}
|
||||
|
||||
if (binfo->initrd_size) {
|
||||
rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
|
||||
binfo->loader_start + INITRD_LOAD_ADDR);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
|
||||
}
|
||||
|
||||
rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
|
||||
binfo->loader_start + INITRD_LOAD_ADDR +
|
||||
binfo->initrd_size);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
|
||||
}
|
||||
}
|
||||
|
||||
cpu_physical_memory_write(addr, fdt, size);
|
||||
|
||||
return 0;
|
||||
|
||||
#else
|
||||
fprintf(stderr, "Device tree requested, "
|
||||
"but qemu was compiled without fdt support\n");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void do_cpu_reset(void *opaque)
|
||||
{
|
||||
CPUState *env = opaque;
|
||||
@ -222,10 +286,12 @@ static void do_cpu_reset(void *opaque)
|
||||
} else {
|
||||
if (env == first_cpu) {
|
||||
env->regs[15] = info->loader_start;
|
||||
if (old_param) {
|
||||
set_kernel_args_old(info);
|
||||
} else {
|
||||
set_kernel_args(info);
|
||||
if (!info->dtb_filename) {
|
||||
if (old_param) {
|
||||
set_kernel_args_old(info);
|
||||
} else {
|
||||
set_kernel_args(info);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info->secondary_cpu_reset_hook(env, info);
|
||||
@ -243,6 +309,7 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
|
||||
uint64_t elf_entry;
|
||||
target_phys_addr_t entry;
|
||||
int big_endian;
|
||||
QemuOpts *machine_opts;
|
||||
|
||||
/* Load the kernel. */
|
||||
if (!info->kernel_filename) {
|
||||
@ -250,6 +317,13 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
|
||||
if (machine_opts) {
|
||||
info->dtb_filename = qemu_opt_get(machine_opts, "dtb");
|
||||
} else {
|
||||
info->dtb_filename = NULL;
|
||||
}
|
||||
|
||||
if (!info->secondary_cpu_reset_hook) {
|
||||
info->secondary_cpu_reset_hook = default_reset_secondary;
|
||||
}
|
||||
@ -300,8 +374,25 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
|
||||
} else {
|
||||
initrd_size = 0;
|
||||
}
|
||||
info->initrd_size = initrd_size;
|
||||
|
||||
bootloader[4] = info->board_id;
|
||||
bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
|
||||
|
||||
/* for device tree boot, we pass the DTB directly in r2. Otherwise
|
||||
* we point to the kernel args.
|
||||
*/
|
||||
if (info->dtb_filename) {
|
||||
/* Place the DTB after the initrd in memory */
|
||||
target_phys_addr_t dtb_start = TARGET_PAGE_ALIGN(info->loader_start
|
||||
+ INITRD_LOAD_ADDR
|
||||
+ initrd_size);
|
||||
if (load_dtb(dtb_start, info)) {
|
||||
exit(1);
|
||||
}
|
||||
bootloader[5] = dtb_start;
|
||||
} else {
|
||||
bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
|
||||
}
|
||||
bootloader[6] = entry;
|
||||
for (n = 0; n < sizeof(bootloader) / 4; n++) {
|
||||
bootloader[n] = tswap32(bootloader[n]);
|
||||
@ -311,7 +402,6 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
|
||||
if (info->nb_cpus > 1) {
|
||||
info->write_secondary_boot(env, info);
|
||||
}
|
||||
info->initrd_size = initrd_size;
|
||||
}
|
||||
info->is_linux = is_linux;
|
||||
|
||||
|
@ -578,6 +578,10 @@ static QemuOptsList qemu_machine_opts = {
|
||||
.name = "append",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Linux kernel command line",
|
||||
}, {
|
||||
.name = "dtb",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Linux kernel device tree file",
|
||||
},
|
||||
{ /* End of list */ }
|
||||
},
|
||||
|
@ -2037,6 +2037,15 @@ Use @var{file1} and @var{file2} as modules and pass arg=foo as parameter to the
|
||||
first module.
|
||||
ETEXI
|
||||
|
||||
DEF("dtb", HAS_ARG, QEMU_OPTION_dtb, \
|
||||
"-dtb file use 'file' as device tree image\n", QEMU_ARCH_ARM)
|
||||
STEXI
|
||||
@item -dtb @var{file}
|
||||
@findex -dtb
|
||||
Use @var{file} as a device tree binary (dtb) image and pass it to the kernel
|
||||
on boot.
|
||||
ETEXI
|
||||
|
||||
STEXI
|
||||
@end table
|
||||
ETEXI
|
||||
|
8
vl.c
8
vl.c
@ -2527,6 +2527,9 @@ int main(int argc, char **argv, char **envp)
|
||||
case QEMU_OPTION_append:
|
||||
qemu_opts_set(qemu_find_opts("machine"), 0, "append", optarg);
|
||||
break;
|
||||
case QEMU_OPTION_dtb:
|
||||
qemu_opts_set(qemu_find_opts("machine"), 0, "dtb", optarg);
|
||||
break;
|
||||
case QEMU_OPTION_cdrom:
|
||||
drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS);
|
||||
break;
|
||||
@ -3346,6 +3349,11 @@ int main(int argc, char **argv, char **envp)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!linux_boot && machine_opts && qemu_opt_get(machine_opts, "dtb")) {
|
||||
fprintf(stderr, "-dtb only allowed with -kernel option\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
os_set_line_buffering();
|
||||
|
||||
if (init_timer_alarm() < 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user