diff options
-rw-r--r-- | Makefile.target | 1 | ||||
-rwxr-xr-x | configure | 1 | ||||
-rw-r--r-- | hw/arm-misc.h | 1 | ||||
-rw-r--r-- | hw/arm11mpcore.c | 20 | ||||
-rw-r--r-- | hw/arm_boot.c | 102 | ||||
-rw-r--r-- | hw/arm_gic.c | 50 | ||||
-rw-r--r-- | qemu-config.c | 4 | ||||
-rw-r--r-- | qemu-options.hx | 9 | ||||
-rw-r--r-- | vl.c | 8 |
9 files changed, 161 insertions, 35 deletions
diff --git a/Makefile.target b/Makefile.target index 692c9d7..343eb78 100644 --- a/Makefile.target +++ b/Makefile.target @@ -374,6 +374,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 @@ -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" diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 306013a..734bd82 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/arm11mpcore.c b/hw/arm11mpcore.c index 102348b..c67b70f 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -202,16 +202,7 @@ static int realview_mpcore_init(SysBusDevice *dev) } static Property mpcore_rirq_properties[] = { - DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), - /* The ARM11 MPCORE TRM says the on-chip controller may have - * anything from 0 to 224 external interrupt IRQ lines (with another - * 32 internal). We default to 32+32, which is the number provided by - * the ARM11 MPCore test chip in the Realview Versatile Express - * coretile. Other boards may differ and should set this property - * appropriately. Some Linux kernels may not boot if the hardware - * has more IRQ lines than the kernel expects. - */ - DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64), + DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), DEFINE_PROP_END_OF_LIST(), }; @@ -233,6 +224,15 @@ static TypeInfo mpcore_rirq_info = { static Property mpcore_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), + /* The ARM11 MPCORE TRM says the on-chip controller may have + * anything from 0 to 224 external interrupt IRQ lines (with another + * 32 internal). We default to 32+32, which is the number provided by + * the ARM11 MPCore test chip in the Realview Versatile Express + * coretile. Other boards may differ and should set this property + * appropriately. Some Linux kernels may not boot if the hardware + * has more IRQ lines than the kernel expects. + */ + DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm_boot.c b/hw/arm_boot.c index 2ef25ca..fc66910 100644 --- a/hw/arm_boot.c +++ b/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; diff --git a/hw/arm_gic.c b/hw/arm_gic.c index cf582a5..d8a7a19 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -13,6 +13,8 @@ /* Maximum number of possible interrupts, determined by the GIC architecture */ #define GIC_MAXIRQ 1020 +/* First 32 are private to each CPU (SGIs and PPIs). */ +#define GIC_INTERNAL 32 //#define DEBUG_GIC #ifdef DEBUG_GIC @@ -73,8 +75,9 @@ typedef struct gic_irq_state #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger -#define GIC_GET_PRIORITY(irq, cpu) \ - (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32]) +#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ + s->priority1[irq][cpu] : \ + s->priority2[(irq) - GIC_INTERNAL]) #ifdef NVIC #define GIC_TARGET(irq) 1 #else @@ -92,8 +95,8 @@ typedef struct gic_state #ifndef NVIC int irq_target[GIC_MAXIRQ]; #endif - int priority1[32][NCPU]; - int priority2[GIC_MAXIRQ - 32]; + int priority1[GIC_INTERNAL][NCPU]; + int priority2[GIC_MAXIRQ - GIC_INTERNAL]; int last_active[GIC_MAXIRQ][NCPU]; int priority_mask[NCPU]; @@ -174,7 +177,7 @@ static void gic_set_irq(void *opaque, int irq, int level) { gic_state *s = (gic_state *)opaque; /* The first external input line is internal interrupt 32. */ - irq += 32; + irq += GIC_INTERNAL; if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK)) return; @@ -316,7 +319,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (irq >= s->num_irq) goto bad_reg; res = 0; - mask = (irq < 32) ? cm : ALL_CPU_MASK; + mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; for (i = 0; i < 8; i++) { if (GIC_TEST_PENDING(irq + i, mask)) { res |= (1 << i); @@ -328,7 +331,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (irq >= s->num_irq) goto bad_reg; res = 0; - mask = (irq < 32) ? cm : ALL_CPU_MASK; + mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; for (i = 0; i < 8; i++) { if (GIC_TEST_ACTIVE(irq + i, mask)) { res |= (1 << i); @@ -435,8 +438,8 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, value = 0xff; for (i = 0; i < 8; i++) { if (value & (1 << i)) { - int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq); - int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK; + int mask = (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq); + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (!GIC_TEST_ENABLED(irq + i, cm)) { DPRINTF("Enabled IRQ %d\n", irq + i); @@ -460,7 +463,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, value = 0; for (i = 0; i < 8; i++) { if (value & (1 << i)) { - int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK; + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (GIC_TEST_ENABLED(irq + i, cm)) { DPRINTF("Disabled IRQ %d\n", irq + i); @@ -502,10 +505,10 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, irq = (offset - 0x400) + GIC_BASE_IRQ; if (irq >= s->num_irq) goto bad_reg; - if (irq < 32) { + if (irq < GIC_INTERNAL) { s->priority1[irq][cpu] = value; } else { - s->priority2[irq - 32] = value; + s->priority2[irq - GIC_INTERNAL] = value; } #ifndef NVIC } else if (offset < 0xc00) { @@ -515,7 +518,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, goto bad_reg; if (irq < 29) value = 0; - else if (irq < 32) + else if (irq < GIC_INTERNAL) value = ALL_CPU_MASK; s->irq_target[irq] = value & ALL_CPU_MASK; } else if (offset < 0xf00) { @@ -523,7 +526,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; if (irq >= s->num_irq) goto bad_reg; - if (irq < 32) + if (irq < GIC_INTERNAL) value |= 0xaa; for (i = 0; i < 4; i++) { if (value & (1 << (i * 2))) { @@ -736,7 +739,7 @@ static void gic_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->enabled); for (i = 0; i < NUM_CPU(s); i++) { qemu_put_be32(f, s->cpu_enabled[i]); - for (j = 0; j < 32; j++) + for (j = 0; j < GIC_INTERNAL; j++) qemu_put_be32(f, s->priority1[j][i]); for (j = 0; j < s->num_irq; j++) qemu_put_be32(f, s->last_active[j][i]); @@ -745,7 +748,7 @@ static void gic_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->running_priority[i]); qemu_put_be32(f, s->current_pending[i]); } - for (i = 0; i < s->num_irq - 32; i++) { + for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { qemu_put_be32(f, s->priority2[i]); } for (i = 0; i < s->num_irq; i++) { @@ -773,7 +776,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) s->enabled = qemu_get_be32(f); for (i = 0; i < NUM_CPU(s); i++) { s->cpu_enabled[i] = qemu_get_be32(f); - for (j = 0; j < 32; j++) + for (j = 0; j < GIC_INTERNAL; j++) s->priority1[j][i] = qemu_get_be32(f); for (j = 0; j < s->num_irq; j++) s->last_active[j][i] = qemu_get_be32(f); @@ -782,7 +785,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) s->running_priority[i] = qemu_get_be32(f); s->current_pending[i] = qemu_get_be32(f); } - for (i = 0; i < s->num_irq - 32; i++) { + for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { s->priority2[i] = qemu_get_be32(f); } for (i = 0; i < s->num_irq; i++) { @@ -816,7 +819,16 @@ static void gic_init(gic_state *s, int num_irq) hw_error("requested %u interrupt lines exceeds GIC maximum %d\n", num_irq, GIC_MAXIRQ); } - qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - 32); + /* ITLinesNumber is represented as (N / 32) - 1 (see + * gic_dist_readb) so this is an implementation imposed + * restriction, not an architectural one: + */ + if (s->num_irq < 32 || (s->num_irq % 32)) { + hw_error("%d interrupt lines unsupported: not divisible by 32\n", + num_irq); + } + + qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL); for (i = 0; i < NUM_CPU(s); i++) { sysbus_init_irq(&s->busdev, &s->parent_irq[i]); } diff --git a/qemu-config.c b/qemu-config.c index 7d9da78..be84a03 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -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 */ } }, diff --git a/qemu-options.hx b/qemu-options.hx index b129996..e38799c 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -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 @@ -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) { |