Message ID | 1327701190-24822-1-git-send-email-grant.likely@secretlab.ca |
---|---|
State | New |
Headers | show |
> 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. Adding annother machine feels wrong. Why does the board specific code need to know about this at all? You already going it via a global variable, so can't this be entirely contained within arm_boot.c? If the board file is involved, why is it asking the user? > + versatile_init(ram_size, > + boot_device, > + kernel_filename, kernel_cmdline, > + initrd_filename, cpu_model, 0xffffffff); This only works because we're currently too dumb to emulate the differences between the two board variants. What we probably want to be doing is shipping/constructing device trees for the boards we implement, with an option to turn this on/off. Requiring a user to invent their own seems deeply sub-optimal given we know exactly what hardware we're emulating. A user that needs to provide their own FDT seems like a fairly rare corner case. This gets slightly more interesting when you have custom machine variants (i.e. once we fix the object model, and have proper dynamic machine construction). Even then I'd expect the FDT to be derived from/specificed by the machine description, not a separate option. Paul
On Fri, Jan 27, 2012 at 10:34:01PM +0000, Paul Brook wrote: > > 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. > > Adding annother machine feels wrong. Why does the board specific code need to > know about this at all? You already going it via a global variable, so can't > this be entirely contained within arm_boot.c? Mostly because the infrastructure isn't yet in place to pass the .dtb file through to the arm_boot code (or maybe it is; what is the best way to pass command line data through to the arm_boot.c code (or similar for other architectures)? > If the board file is involved, why is it asking the user? There is a lot of configuration in the .dts file that the QEMU user may want to manipulate; particularly when using QEMU for testing embedded platforms. The direction I want to go is to select the machine model based on the top level DT compatible property (making -M optional), and then also allow a lot of the machine layout to be driven by DT data. ie. populate emulated devices from DT data. I believe this is how Edgar is using the microblaze model, but I don't think those patches have been upstreamed yet. I hope that microblaze, ARM and powerpc can all use the same model here. > > > + versatile_init(ram_size, > > + boot_device, > > + kernel_filename, kernel_cmdline, > > + initrd_filename, cpu_model, 0xffffffff); > > This only works because we're currently too dumb to emulate the differences > between the two board variants. Yeah, this is a hack so I could play with forcing the machine id. I'll remove it. > What we probably want to be doing is shipping/constructing device trees for > the boards we implement, with an option to turn this on/off. Requiring a user > to invent their own seems deeply sub-optimal given we know exactly what > hardware we're emulating. A user that needs to provide their own FDT seems > like a fairly rare corner case. I disagree. QEMU may want to ship stock .dts files, but it will absolutely be a common use case for embedded developers to pass in their own .dtb file. > This gets slightly more interesting when you have custom machine variants > (i.e. once we fix the object model, and have proper dynamic machine > construction). Even then I'd expect the FDT to be derived from/specificed by > the machine description, not a separate option. I started with going down that route, but switched to this model after playing with it and noticing that it doesn't seem to fit as well for embedded development as providing a .dtb file and having QEMU construct a machine that matches the data. g.
> There is a lot of configuration in the .dts file that the QEMU user may > want to manipulate; particularly when using QEMU for testing embedded > platforms. The direction I want to go is to select the machine model based > on the top level DT compatible property (making -M optional), and then > also allow a lot of the machine layout to be driven by DT data. ie. > populate emulated devices from DT data. I want to remove -M, and the associated board specific knowledge altogether. A system that can only create limited variants of hardcoded boards is IMO not a viable medium/long term solution. > > This gets slightly more interesting when you have custom machine variants > > (i.e. once we fix the object model, and have proper dynamic machine > > construction). Even then I'd expect the FDT to be derived > > from/specificed by the machine description, not a separate option. > > I started with going down that route, but switched to this model after > playing with it and noticing that it doesn't seem to fit as well for > embedded development as providing a .dtb file and having QEMU > construct a machine that matches the data. Problem is that a FDT doesn't really tell you everything you need to know in order to construct the machine. If you look back in the archives you should find that I tried something similar when I first added the qdev code. While it appears to be a nice idea at first, it doesn't actually work do well in practice. It's maybe OK for simple embedded systems with a single system bus, but when you start looking at more sophisticated machines it doesn't fit so well. In particular when you look at things like PCI, devices connected via multiple busses (a sound codec with both I2C [command] and I2S [data] connections), and some of the stranger bus topographies (some ppc boards have lots of weirdness in the FDT) the requirements of qemu and linux start to diverge. Clearly we need to have some sort of FDT support. However I'm unconvinced that it's the correct format for the primary data structure. For one thing it's a hierarchical tree structure, and in my experience describing links outside that structure gets hairy. One of the things we're doing with QOM is move from a hierarchical tree/bus to a more general connected web of links between devices. Paul
On Sat, Jan 28, 2012 at 11:48:37AM -0700, Grant Likely wrote: > On Fri, Jan 27, 2012 at 10:34:01PM +0000, Paul Brook wrote: > > > 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. > > > > Adding annother machine feels wrong. Why does the board specific code need to > > know about this at all? You already going it via a global variable, so can't > > this be entirely contained within arm_boot.c? > > Mostly because the infrastructure isn't yet in place to pass the .dtb file > through to the arm_boot code (or maybe it is; what is the best way to pass > command line data through to the arm_boot.c code (or similar for other > architectures)? > > > If the board file is involved, why is it asking the user? > > There is a lot of configuration in the .dts file that the QEMU user may want > to manipulate; particularly when using QEMU for testing embedded platforms. > The direction I want to go is to select the machine model based on the top > level DT compatible property (making -M optional), and then also allow a lot > of the machine layout to be driven by DT data. ie. populate emulated devices > from DT data. > > I believe this is how Edgar is using the microblaze model, but I don't > think those patches have been upstreamed yet. I hope that microblaze, > ARM and powerpc can all use the same model here. Yes, that's right. > > > > > + versatile_init(ram_size, > > > + boot_device, > > > + kernel_filename, kernel_cmdline, > > > + initrd_filename, cpu_model, 0xffffffff); > > > > This only works because we're currently too dumb to emulate the differences > > between the two board variants. > > Yeah, this is a hack so I could play with forcing the machine id. > I'll remove it. > > > What we probably want to be doing is shipping/constructing device trees for > > the boards we implement, with an option to turn this on/off. Requiring a user > > to invent their own seems deeply sub-optimal given we know exactly what > > hardware we're emulating. A user that needs to provide their own FDT seems > > like a fairly rare corner case. > > I disagree. QEMU may want to ship stock .dts files, but it will > absolutely be a common use case for embedded developers to pass in > their own .dtb file. > > > This gets slightly more interesting when you have custom machine variants > > (i.e. once we fix the object model, and have proper dynamic machine > > construction). Even then I'd expect the FDT to be derived from/specificed by > > the machine description, not a separate option. > > I started with going down that route, but switched to this model after > playing with it and noticing that it doesn't seem to fit as well for > embedded development as providing a .dtb file and having QEMU > construct a machine that matches the data. That's the way I see it too. Our use-case is to build the virtual machine from the dtb. We then run the exact same software that runs on real hw. I don't know the details on how practical dtb's are for describing more complex bus hierarchies, maybe Peter has more info on that. Cheers, Edgar
On Mon, Jan 30, 2012 at 6:42 AM, Edgar E. Iglesias <edgar.iglesias@gmail.com > wrote: > On Sat, Jan 28, 2012 at 11:48:37AM -0700, Grant Likely wrote: > > On Fri, Jan 27, 2012 at 10:34:01PM +0000, Paul Brook wrote: > > > > 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. > > > > > > Adding annother machine feels wrong. Why does the board specific code > need to > > > know about this at all? You already going it via a global variable, so > can't > > > this be entirely contained within arm_boot.c? > > > > Mostly because the infrastructure isn't yet in place to pass the .dtb > file > > through to the arm_boot code (or maybe it is; what is the best way to > pass > > command line data through to the arm_boot.c code (or similar for other > > architectures)? > > > > > If the board file is involved, why is it asking the user? > > > > There is a lot of configuration in the .dts file that the QEMU user may > want > > to manipulate; particularly when using QEMU for testing embedded > platforms. > > The direction I want to go is to select the machine model based on the > top > > level DT compatible property (making -M optional), and then also allow a > lot > > of the machine layout to be driven by DT data. ie. populate emulated > devices > > from DT data. > > > > I believe this is how Edgar is using the microblaze model, but I don't > > think those patches have been upstreamed yet. I hope that microblaze, > > ARM and powerpc can all use the same model here. > > Yes, that's right. > > > > > > > > + versatile_init(ram_size, > > > > + boot_device, > > > > + kernel_filename, kernel_cmdline, > > > > + initrd_filename, cpu_model, 0xffffffff); > > > > > > This only works because we're currently too dumb to emulate the > differences > > > between the two board variants. > > > > Yeah, this is a hack so I could play with forcing the machine id. > > I'll remove it. > > > > > What we probably want to be doing is shipping/constructing device > trees for > > > the boards we implement, with an option to turn this on/off. > Requiring a user > > > to invent their own seems deeply sub-optimal given we know exactly what > > > hardware we're emulating. A user that needs to provide their own FDT > seems > > > like a fairly rare corner case. > > > > I disagree. QEMU may want to ship stock .dts files, but it will > > absolutely be a common use case for embedded developers to pass in > > their own .dtb file. > > > > > This gets slightly more interesting when you have custom machine > variants > > > (i.e. once we fix the object model, and have proper dynamic machine > > > construction). Even then I'd expect the FDT to be derived > from/specificed by > > > the machine description, not a separate option. > > > > I started with going down that route, but switched to this model after > > playing with it and noticing that it doesn't seem to fit as well for > > embedded development as providing a .dtb file and having QEMU > > construct a machine that matches the data. > Another major issue with that is DTBs have little bits of software in them as well, such as the "chosen" node, which would need to be populated before passing off to software. Coding these dtb properties into machine models would be sub-optimal, you would need some way of parameterizing certain dtb properties at boot time. The easiest approach to this is just passing in your own dtb file. > > That's the way I see it too. Our use-case is to build the virtual machine > from the dtb. We then run the exact same software that runs on real hw. > I don't know the details on how practical dtb's are for describing more > complex bus hierarchies, maybe Peter has more info on that. > > Dtbs are capable of describing pretty much anything you want, the main issue is ambiguity on how the dts data structure is used to describe complex or irregular bus heirachies. Im our approach, device models themselves describe how dts syntax is interpreted wrt to machine creation. This means different device models can interpret dts node handles or subnode relationships in any arbitrary way, which would proivde you a backend with which you could implement complex bus heirachies. For example, a device tree node with compatibility "simple-bus" will instantiate all its subnodes as elements on the parent bus (or system bus in the case of the dtb root node). There is however a software hook which would allow you to implement more complex behaviours such as address range remapping for bus bridges (based on the ranges property). We didnt do this, as the memory API wasnt adequate at the time, but recent updates would now make this possible. As another example, a USB controller could interpret its children as USB devices and create the corresponding USB device model interconnects. We successfully prototyped this idea out of tree for SPI busses, but the mechanism is there to do USB, PCI, SD and the other usual suspects. Just needs to be implemented and the framework reworked for QOM. Cheers, > Edgar >
On Mon, Jan 30, 2012 at 09:54:45AM +1000, Peter Crosthwaite wrote: > On Mon, Jan 30, 2012 at 6:42 AM, Edgar E. Iglesias <edgar.iglesias@gmail.com > > On Sat, Jan 28, 2012 at 11:48:37AM -0700, Grant Likely wrote: > > > On Fri, Jan 27, 2012 at 10:34:01PM +0000, Paul Brook wrote: > Another major issue with that is DTBs have little bits of software in them > as well, such as the "chosen" node, which would need to be populated before > passing off to software. Coding these dtb properties into machine models > would be sub-optimal, you would need some way of parameterizing certain dtb > properties at boot time. The easiest approach to this is just passing in > your own dtb file. I wouldn't say it that way. dtbs don't contain any sort of executable code. It is only data. However, it is true that the bootloader is expected to update properties in the /chosen node for the kernel command line and initrd base address, to update the /memory node, and on some systems to set the Ethernet mac address properties if that is not discoverable from the simulated HW model. Since that data is knowledge held by QEMU internally, it is appropriate for QEMU to modify the dtb with that data, and this patch does exactly that. g.
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);