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:
Grant Likely 2012-03-02 11:56:38 +00:00 committed by Peter Maydell
parent 41c1e2f54e
commit 412beee6a0
7 changed files with 120 additions and 6 deletions

View file

@ -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,

View file

@ -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;