From patchwork Fri Jan 27 21:53:10 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Grant Likely X-Patchwork-Id: 138363 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 8C93F1007DB for ; Sat, 28 Jan 2012 08:53:28 +1100 (EST) Received: from localhost ([::1]:44130 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Rqtja-0007yP-Gc for incoming@patchwork.ozlabs.org; Fri, 27 Jan 2012 16:53:26 -0500 Received: from eggs.gnu.org ([140.186.70.92]:60678) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RqtjS-0007yB-AT for qemu-devel@nongnu.org; Fri, 27 Jan 2012 16:53:20 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RqtjQ-00049T-0J for qemu-devel@nongnu.org; Fri, 27 Jan 2012 16:53:18 -0500 Received: from mail-pz0-f45.google.com ([209.85.210.45]:35987) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RqtjP-00049N-Lh for qemu-devel@nongnu.org; Fri, 27 Jan 2012 16:53:15 -0500 Received: by dajr28 with SMTP id r28so2216901daj.4 for ; Fri, 27 Jan 2012 13:53:13 -0800 (PST) Received: by 10.68.212.161 with SMTP id nl1mr17533829pbc.38.1327701193702; Fri, 27 Jan 2012 13:53:13 -0800 (PST) Received: from localhost (S0106d8b37715ee14.cg.shawcable.net. [68.146.14.168]) by mx.google.com with ESMTPS id x4sm7859991pbx.16.2012.01.27.13.53.12 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 27 Jan 2012 13:53:13 -0800 (PST) Received: by localhost (Postfix, from userid 1000) id 0B62F3E0A75; Fri, 27 Jan 2012 14:53:12 -0700 (MST) From: Grant Likely To: qemu-devel@nongnu.org Date: Fri, 27 Jan 2012 14:53:10 -0700 Message-Id: <1327701190-24822-1-git-send-email-grant.likely@secretlab.ca> X-Mailer: git-send-email 1.7.5.4 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.210.45 Cc: Peter Maydell , Rob Herring , Grant Likely , Paul Brook , "Edgar E. Iglesias" , Jeremy Kerr Subject: [Qemu-devel] [PATCH] arm: add device tree support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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. This version of the patch only adds support to the arm versatile and versatile express platforms. Other platforms can easily be added. Signed-off-by: Jeremy Kerr [grant.likely: Changed from platform specifying dtb to user option] [grant.likely: Squashed in initrd and memory property manipulation patches] Signed-off-by: Grant Likely Cc: Paul Brook Cc: Peter Maydell Cc: Rob Herring Cc: Edgar E. Iglesias --- I've been working with this patch for quite a while now. It works well for me. It should be easy to extend to add support for other architectures too. Note: I've done something ugly here for passing the dtb filename into the machine->init() function by using a global variable. I did it this way to avoid changing the ->init() function prototype which would mean a lot of functions need to be changed, but I'm not happy with it. Alternately, I could change the prototype to accept an arguments structure which can easily be extended in the future to support more machine arguments without requires every machine to be changed. g. Makefile.target | 1 + configure | 3 ++ hw/arm-misc.h | 1 + hw/arm_boot.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++----- hw/boards.h | 1 + hw/versatilepb.c | 20 +++++++++++ hw/vexpress.c | 1 + qemu-options.hx | 9 +++++ vl.c | 11 ++++++ 9 files changed, 133 insertions(+), 9 deletions(-) diff --git a/Makefile.target b/Makefile.target index 68481a3..5e465ec 100644 --- a/Makefile.target +++ b/Makefile.target @@ -363,6 +363,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 diff --git a/configure b/configure index f69e08f..0c2deab 100755 --- a/configure +++ b/configure @@ -3411,6 +3411,9 @@ 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 + if test "$fdt" = "yes" ; then + target_libs_softmmu="$fdt_libs" + fi ;; cris) target_nptl="yes" diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 5e5204b..4b55fb8 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -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, diff --git a/hw/arm_boot.c b/hw/arm_boot.c index 5f163fd..93edeb1 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -7,11 +7,13 @@ * This code is licensed under the GPL. */ +#include "config.h" #include "hw.h" #include "arm-misc.h" #include "sysemu.h" #include "loader.h" #include "elf.h" +#include "device_tree.h" #define KERNEL_ARGS_ADDR 0x100 #define KERNEL_LOAD_ADDR 0x00010000 @@ -207,6 +209,66 @@ 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; + + if (!binfo->dtb_filename) + return 0; + + 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, "Platform requested a device tree, " + "but qemu was compiled without fdt support\n"); + return -1; +#endif +} + static void do_cpu_reset(void *opaque) { CPUState *env = opaque; @@ -221,12 +283,14 @@ 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, info->initrd_size, + if (!info->dtb_filename) { + if (old_param) { + set_kernel_args_old(info, info->initrd_size, + info->loader_start); + } else { + set_kernel_args(info, info->initrd_size, info->loader_start); - } else { - set_kernel_args(info, info->initrd_size, - info->loader_start); + } } } else { info->secondary_cpu_reset_hook(env, info); @@ -239,10 +303,10 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) { int kernel_size; int initrd_size; - int n; + int n, rc; int is_linux = 0; uint64_t elf_entry; - target_phys_addr_t entry; + target_phys_addr_t entry, dtb_start; int big_endian; /* Load the kernel. */ @@ -301,8 +365,22 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) } else { initrd_size = 0; } + info->initrd_size = initrd_size; + + /* Place the DTB after the initrd in memory */ + dtb_start = TARGET_PAGE_ALIGN(info->loader_start + INITRD_LOAD_ADDR + + initrd_size); + rc = load_dtb(dtb_start, info); + if (rc) + exit(1); + 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) + 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]); @@ -312,7 +390,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; diff --git a/hw/boards.h b/hw/boards.h index f6d3784..d06776c 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -34,5 +34,6 @@ typedef struct QEMUMachine { int qemu_register_machine(QEMUMachine *m); extern QEMUMachine *current_machine; +extern const char *current_dtb_filename; #endif diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 3f7490c..7549c24 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -317,6 +317,7 @@ static void versatile_init(ram_addr_t ram_size, versatile_binfo.kernel_filename = kernel_filename; versatile_binfo.kernel_cmdline = kernel_cmdline; versatile_binfo.initrd_filename = initrd_filename; + versatile_binfo.dtb_filename = current_dtb_filename; versatile_binfo.board_id = board_id; arm_load_kernel(env, &versatile_binfo); } @@ -343,6 +344,17 @@ static void vab_init(ram_addr_t ram_size, initrd_filename, cpu_model, 0x25e); } +static void vdt_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + versatile_init(ram_size, + boot_device, + kernel_filename, kernel_cmdline, + initrd_filename, cpu_model, 0xffffffff); +} + static QEMUMachine versatilepb_machine = { .name = "versatilepb", .desc = "ARM Versatile/PB (ARM926EJ-S)", @@ -357,10 +369,18 @@ static QEMUMachine versatileab_machine = { .use_scsi = 1, }; +static QEMUMachine versatiledt_machine = { + .name = "versatiledt", + .desc = "ARM Versatile (device tree)", + .init = vdt_init, + .use_scsi = 1, +}; + static void versatile_machine_init(void) { qemu_register_machine(&versatilepb_machine); qemu_register_machine(&versatileab_machine); + qemu_register_machine(&versatiledt_machine); } machine_init(versatile_machine_init); diff --git a/hw/vexpress.c b/hw/vexpress.c index 64fab45..ecfb585 100644 --- a/hw/vexpress.c +++ b/hw/vexpress.c @@ -219,6 +219,7 @@ static void vexpress_a9_init(ram_addr_t ram_size, vexpress_binfo.kernel_filename = kernel_filename; vexpress_binfo.kernel_cmdline = kernel_cmdline; vexpress_binfo.initrd_filename = initrd_filename; + vexpress_binfo.dtb_filename = current_dtb_filename; vexpress_binfo.nb_cpus = smp_cpus; vexpress_binfo.board_id = VEXPRESS_BOARD_ID; vexpress_binfo.loader_start = 0x60000000; diff --git a/qemu-options.hx b/qemu-options.hx index 3a07ae8..0a01baa 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1964,6 +1964,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 a 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 diff --git a/vl.c b/vl.c index d88a18c..f42f1fc 100644 --- a/vl.c +++ b/vl.c @@ -1154,6 +1154,7 @@ void pcmcia_info(Monitor *mon) static QEMUMachine *first_machine = NULL; QEMUMachine *current_machine = NULL; +const char *current_dtb_filename = NULL; int qemu_register_machine(QEMUMachine *m) { @@ -2166,6 +2167,7 @@ int main(int argc, char **argv, char **envp) int snapshot, linux_boot; const char *icount_option = NULL; const char *initrd_filename; + const char *dtb_filename = NULL; const char *kernel_filename, *kernel_cmdline; char boot_devices[33] = "cad"; /* default to HD->floppy->CD-ROM */ DisplayState *ds; @@ -2304,6 +2306,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_initrd: initrd_filename = optarg; break; + case QEMU_OPTION_dtb: + dtb_filename = optarg; + break; case QEMU_OPTION_hda: { char buf[256]; @@ -3241,6 +3246,11 @@ int main(int argc, char **argv, char **envp) exit(1); } + if (!linux_boot && dtb_filename != NULL) { + fprintf(stderr, "-dtb only allowed with -kernel option\n"); + exit(1); + } + os_set_line_buffering(); if (init_timer_alarm() < 0) { @@ -3374,6 +3384,7 @@ int main(int argc, char **argv, char **envp) qdev_machine_init(); + current_dtb_filename = dtb_filename; machine->init(ram_size, boot_devices, kernel_filename, kernel_cmdline, initrd_filename, cpu_model);