diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2014-09-12 15:12:26 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2014-09-12 15:12:26 +0100 |
commit | 4c24f4004089a308c5de8ed720cf6bd1746aedd8 (patch) | |
tree | d83f318edd5b987a94d0a16ed8a25a215c645fca /hw/arm | |
parent | 6cd14054b67774cc58a51fca6660cfa1d3c08059 (diff) | |
parent | 92df845070290236d1b28b03453deec1ae9c4263 (diff) |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20140912' into staging
target-arm:
* add "linux,stdout-path" to the virt DTB
* fix a long standing bug with IRQ disabling on Cortex-M CPUs
* implement input interrupt logic in the PL061
* fix failure to load correct SP/PC on reset of Cortex-M CPUs
if the vector table is not in a ROM-blob-in-RAM
* provide flash devices for boot ROMs in the virt board
* implement architectural watchpoints
* fix misimplementation of Inner Shareable TLB operations that
caused instability of guests in TCG SMP configurations
* configure PL011 and PL031 in the virt board correctly with
level-triggered interrupts rather than edge-triggered
* support providing a device tree blob to ROM (firmware)
images as well as to kernels
# gpg: Signature made Fri 12 Sep 2014 14:19:08 BST using RSA key ID 14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
* remotes/pmaydell/tags/pull-target-arm-20140912: (23 commits)
hw/arm/boot: enable DTB support when booting ELF images
hw/arm/boot: load device tree to base of DRAM if no -kernel option was passed
hw/arm/boot: pass an address limit to and return size from load_dtb()
hw/arm/boot: load DTB as a ROM image
hw/arm/virt: fix pl011 and pl031 irq flags
target-arm: Make *IS TLB maintenance ops affect all CPUs
target-arm: Push legacy wildcard TLB ops back into v6
target-arm: Implement minimal DBGVCR, OSDLR_EL1, MDCCSR_EL0
target-arm: Remove comment about MDSCR_EL1 being dummy implementation
target-arm: Set DBGDSCR.MOE for debug exceptions taken to AArch32
target-arm: Implement handling of fired watchpoints
target-arm: Move extended_addresses_enabled() to internals.h
target-arm: Implement setting of watchpoints
cpu-exec: Make debug_excp_handler a QOM CPU method
exec.c: Record watchpoint fault address and direction
exec.c: Provide full set of dummy wp remove functions in user-mode
exec.c: Relax restrictions on watchpoint length and alignment
hw/arm/virt: Provide flash devices for boot ROMs
target-arm: Fix broken indentation in arm_cpu_reest()
target-arm: Fix resetting issues on ARMv7-M CPUs
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/arm')
-rw-r--r-- | hw/arm/boot.c | 71 | ||||
-rw-r--r-- | hw/arm/virt.c | 76 |
2 files changed, 139 insertions, 8 deletions
diff --git a/hw/arm/boot.c b/hw/arm/boot.c index e32f2f4158..c8dc34f086 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -312,7 +312,26 @@ static void set_kernel_args_old(const struct arm_boot_info *info) } } -static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) +/** + * load_dtb() - load a device tree binary image into memory + * @addr: the address to load the image at + * @binfo: struct describing the boot environment + * @addr_limit: upper limit of the available memory area at @addr + * + * Load a device tree supplied by the machine or by the user with the + * '-dtb' command line option, and put it at offset @addr in target + * memory. + * + * If @addr_limit contains a meaningful value (i.e., it is strictly greater + * than @addr), the device tree is only loaded if its size does not exceed + * the limit. + * + * Returns: the size of the device tree image on success, + * 0 if the image size exceeds the limit, + * -1 on errors. + */ +static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, + hwaddr addr_limit) { void *fdt = NULL; int size, rc; @@ -341,6 +360,15 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) } } + if (addr_limit > addr && size > (addr_limit - addr)) { + /* Installing the device tree blob at addr would exceed addr_limit. + * Whether this constitutes failure is up to the caller to decide, + * so just return 0 as size, i.e., no error. + */ + g_free(fdt); + return 0; + } + acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells"); scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells"); if (acells == 0 || scells == 0) { @@ -396,11 +424,14 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) qemu_fdt_dumpdtb(fdt, size); - cpu_physical_memory_write(addr, fdt, size); + /* Put the DTB into the memory map as a ROM image: this will ensure + * the DTB is copied again upon reset, even if addr points into RAM. + */ + rom_add_blob_fixed("dtb", fdt, size, addr); g_free(fdt); - return 0; + return size; fail: g_free(fdt); @@ -451,7 +482,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) int kernel_size; int initrd_size; int is_linux = 0; - uint64_t elf_entry; + uint64_t elf_entry, elf_low_addr, elf_high_addr; int elf_machine; hwaddr entry, kernel_load_offset; int big_endian; @@ -459,6 +490,16 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) /* Load the kernel. */ if (!info->kernel_filename) { + + if (have_dtb(info)) { + /* If we have a device tree blob, but no kernel to supply it to, + * copy it to the base of RAM for a bootloader to pick up. + */ + if (load_dtb(info->loader_start, info, 0) < 0) { + exit(1); + } + } + /* If no kernel specified, do nothing; we will start from address 0 * (typically a boot ROM image) in the same way as hardware. */ @@ -508,7 +549,25 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) /* Assume that raw images are linux kernels, and ELF images are not. */ kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, big_endian, elf_machine, 1); + &elf_low_addr, &elf_high_addr, big_endian, + elf_machine, 1); + if (kernel_size > 0 && have_dtb(info)) { + /* If there is still some room left at the base of RAM, try and put + * the DTB there like we do for images loaded with -bios or -pflash. + */ + if (elf_low_addr > info->loader_start + || elf_high_addr < info->loader_start) { + /* Pass elf_low_addr as address limit to load_dtb if it may be + * pointing into RAM, otherwise pass '0' (no limit) + */ + if (elf_low_addr < info->loader_start) { + elf_low_addr = 0; + } + if (load_dtb(info->loader_start, info, elf_low_addr) < 0) { + exit(1); + } + } + } entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(info->kernel_filename, &entry, NULL, @@ -569,7 +628,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) */ hwaddr dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, 4096); - if (load_dtb(dtb_start, info)) { + if (load_dtb(dtb_start, info, 0) < 0) { exit(1); } fixupcontext[FIXUP_ARGPTR] = dtb_start; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index d6fffc75bd..cc7981cfdc 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -37,6 +37,7 @@ #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "hw/boards.h" +#include "hw/loader.h" #include "exec/address-spaces.h" #include "qemu/bitops.h" #include "qemu/error-report.h" @@ -371,11 +372,13 @@ static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) 2, base, 2, size); qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, - GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + GIC_FDT_IRQ_FLAGS_LEVEL_HI); qemu_fdt_setprop_cells(vbi->fdt, nodename, "clocks", vbi->clock_phandle, vbi->clock_phandle); qemu_fdt_setprop(vbi->fdt, nodename, "clock-names", clocknames, sizeof(clocknames)); + + qemu_fdt_setprop_string(vbi->fdt, "/chosen", "linux,stdout-path", nodename); g_free(nodename); } @@ -396,7 +399,7 @@ static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic) 2, base, 2, size); qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_SPI, irq, - GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + GIC_FDT_IRQ_FLAGS_LEVEL_HI); qemu_fdt_setprop_cell(vbi->fdt, nodename, "clocks", vbi->clock_phandle); qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk"); g_free(nodename); @@ -437,6 +440,73 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) } } +static void create_one_flash(const char *name, hwaddr flashbase, + hwaddr flashsize) +{ + /* Create and map a single flash device. We use the same + * parameters as the flash devices on the Versatile Express board. + */ + DriveInfo *dinfo = drive_get_next(IF_PFLASH); + DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); + const uint64_t sectorlength = 256 * 1024; + + if (dinfo && qdev_prop_set_drive(dev, "drive", dinfo->bdrv)) { + abort(); + } + + qdev_prop_set_uint32(dev, "num-blocks", flashsize / sectorlength); + qdev_prop_set_uint64(dev, "sector-length", sectorlength); + qdev_prop_set_uint8(dev, "width", 4); + qdev_prop_set_uint8(dev, "device-width", 2); + qdev_prop_set_uint8(dev, "big-endian", 0); + qdev_prop_set_uint16(dev, "id0", 0x89); + qdev_prop_set_uint16(dev, "id1", 0x18); + qdev_prop_set_uint16(dev, "id2", 0x00); + qdev_prop_set_uint16(dev, "id3", 0x00); + qdev_prop_set_string(dev, "name", name); + qdev_init_nofail(dev); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, flashbase); +} + +static void create_flash(const VirtBoardInfo *vbi) +{ + /* Create two flash devices to fill the VIRT_FLASH space in the memmap. + * Any file passed via -bios goes in the first of these. + */ + hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2; + hwaddr flashbase = vbi->memmap[VIRT_FLASH].base; + char *nodename; + + if (bios_name) { + const char *fn; + + if (drive_get(IF_PFLASH, 0, 0)) { + error_report("The contents of the first flash device may be " + "specified with -bios or with -drive if=pflash... " + "but you cannot use both options at once"); + exit(1); + } + fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!fn || load_image_targphys(fn, flashbase, flashsize) < 0) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } + } + + create_one_flash("virt.flash0", flashbase, flashsize); + create_one_flash("virt.flash1", flashbase + flashsize, flashsize); + + nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", + 2, flashbase, 2, flashsize, + 2, flashbase + flashsize, 2, flashsize); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); + g_free(nodename); +} + static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) { const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; @@ -514,6 +584,8 @@ static void machvirt_init(MachineState *machine) vmstate_register_ram_global(ram); memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram); + create_flash(vbi); + create_gic(vbi, pic); create_uart(vbi, pic); |