From 8c4f0eb94cc65ee32a12feba88d0b32e3665d5ea Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:16 +0000 Subject: [PATCH 01/30] target-arm: Correct handling of writes to CPSR mode bits from gdb in usermode In helper.c the expression (env->uncached_cpsr & CPSR_M) != CPSR_USER is always true; the right hand side was supposed to be ARM_CPU_MODE_USR (an error in commit cb01d391). Since the incorrect expression was always true, this just meant that commit cb01d391 had no effect. However simply changing the RHS here would reveal a logic error: if the mode is USR we wish to completely ignore the attempt to set the mode bits, which means that we must clear the CPSR_M bits from mask to avoid the uncached_cpsr bits being updated at the end of the function. Move the condition into the correct place in the code, fix its RHS constant, and add a comment about the fact that we must be doing a gdbstub write if we're in user mode. Fixes: https://bugs.launchpad.net/qemu/+bug/1550503 Signed-off-by: Peter Maydell Reviewed-by: Sergey Fedorov Message-id: 1456764438-30015-1-git-send-email-peter.maydell@linaro.org --- target-arm/helper.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 18c82967d3..935f13b7ed 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -5490,9 +5490,16 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, env->daif |= val & CPSR_AIF & mask; if (write_type != CPSRWriteRaw && - (env->uncached_cpsr & CPSR_M) != CPSR_USER && ((env->uncached_cpsr ^ val) & mask & CPSR_M)) { - if (bad_mode_switch(env, val & CPSR_M, write_type)) { + if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) { + /* Note that we can only get here in USR mode if this is a + * gdb stub write; for this case we follow the architectural + * behaviour for guest writes in USR mode of ignoring an attempt + * to switch mode. (Those are caught by translate.c for writes + * triggered by guest instructions.) + */ + mask &= ~CPSR_M; + } else if (bad_mode_switch(env, val & CPSR_M, write_type)) { /* Attempt to switch to an invalid mode: this is UNPREDICTABLE in * v7, and has defined behaviour in v8: * + leave CPSR.M untouched From 71c2768433048990c882d058c8b2f62bc17f46e6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:16 +0000 Subject: [PATCH 02/30] virt: Lift the maximum RAM limit from 30GB to 255GB The virt board restricts guests to only 30GB of RAM. This is a hangover from the vexpress-a15 board, and there's no inherent reason for it. 30GB is smaller than you might reasonably want to provision a VM for on a beefy server machine. Raise the limit to 255GB. We choose 255GB because the available space we currently have below the 1TB boundary is up to the 512GB mark, but we don't want to paint ourselves into a corner by assigning it all to RAM. So we make half of it available for RAM, with the 256GB..512GB range available for future non-RAM expansion purposes. If we need to provide more RAM to VMs in the future then we need to: * allocate a second bank of RAM starting at 2TB and working up * fix the DT and ACPI table generation code in QEMU to correctly report two split lumps of RAM to the guest * fix KVM in the host kernel to allow guests with >40 bit address spaces The last of these is obviously the trickiest, but it seems reasonable to assume that anybody configuring a VM with a quarter of a terabyte of RAM will be doing it on a host with more than a terabyte of physical address space. Signed-off-by: Peter Maydell Reviewed-by: Christoffer Dall Tested-by: Wei Huang Message-id: 1456402182-11651-1-git-send-email-peter.maydell@linaro.org --- hw/arm/virt.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 44bbbea92b..7a56b4671b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -95,6 +95,23 @@ typedef struct { #define VIRT_MACHINE_CLASS(klass) \ OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE) +/* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means + * RAM can go up to the 256GB mark, leaving 256GB of the physical + * address space unallocated and free for future use between 256G and 512G. + * If we need to provide more RAM to VMs in the future then we need to: + * * allocate a second bank of RAM starting at 2TB and working up + * * fix the DT and ACPI table generation code in QEMU to correctly + * report two split lumps of RAM to the guest + * * fix KVM in the host kernel to allow guests with >40 bit address spaces + * (We don't want to fill all the way up to 512GB with RAM because + * we might want it for non-RAM purposes later. Conversely it seems + * reasonable to assume that anybody configuring a VM with a quarter + * of a terabyte of RAM will be doing it on a host with more than a + * terabyte of physical address space.) + */ +#define RAMLIMIT_GB 255 +#define RAMLIMIT_BYTES (RAMLIMIT_GB * 1024ULL * 1024 * 1024) + /* Addresses and sizes of our components. * 0..128MB is space for a flash device so we can run bootrom code such as UEFI. * 128MB..256MB is used for miscellaneous device I/O. @@ -130,7 +147,7 @@ static const MemMapEntry a15memmap[] = { [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 }, [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, - [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 }, + [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES }, /* Second PCIe window, 512GB wide at the 512GB boundary */ [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL }, }; @@ -1066,7 +1083,7 @@ static void machvirt_init(MachineState *machine) vbi->smp_cpus = smp_cpus; if (machine->ram_size > vbi->memmap[VIRT_MEM].size) { - error_report("mach-virt: cannot model more than 30GB RAM"); + error_report("mach-virt: cannot model more than %dGB RAM", RAMLIMIT_GB); exit(1); } From 0719e71e5297f68b6b4500aa74e1b49d59806342 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:17 +0000 Subject: [PATCH 03/30] sd.c: Handle NULL block backend in sd_get_inserted() The sd.c SD card emulation code can be in a state where the SDState BlockBackend pointer is NULL; this is treated as "card not present". Add a missing check to sd_get_inserted() so that we don't segfault in this situation. (This could be provoked by the guest writing to the SDHCI register to do a reset on a xilinx-zynq-a9 board; it will also happen at startup when sdhci implements its DeviceClass reset method.) Signed-off-by: Peter Maydell Reviewed-by: Alistair Francis Message-id: 1456493044-10025-2-git-send-email-peter.maydell@linaro.org --- hw/sd/sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index edb6b32690..00c320d00b 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -449,7 +449,7 @@ static void sd_reset(DeviceState *dev) static bool sd_get_inserted(SDState *sd) { - return blk_is_inserted(sd->blk); + return sd->blk && blk_is_inserted(sd->blk); } static bool sd_get_readonly(SDState *sd) From 8b41c30525a529ee69505909908a704fc134d21b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:17 +0000 Subject: [PATCH 04/30] sdhci: Implement DeviceClass reset The sdhci device was missing a DeviceClass reset method; implement it. Poweron reset looks the same as reset commanded by the guest via the device registers, apart from modelling of the rpi 'pending insert interrupt on powerup' quirk. Signed-off-by: Peter Maydell Reviewed-by: Andrew Baumann Message-id: 1456493044-10025-3-git-send-email-peter.maydell@linaro.org --- hw/sd/sdhci.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index e087c17ad7..d28b5871fc 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -207,6 +207,21 @@ static void sdhci_reset(SDHCIState *s) s->pending_insert_state = false; } +static void sdhci_poweron_reset(DeviceState *dev) +{ + /* QOM (ie power-on) reset. This is identical to reset + * commanded via device register apart from handling of the + * 'pending insert on powerup' quirk. + */ + SDHCIState *s = (SDHCIState *)dev; + + sdhci_reset(s); + + if (s->pending_insert_quirk) { + s->pending_insert_state = true; + } +} + static void sdhci_data_transfer(void *opaque); static void sdhci_send_command(SDHCIState *s) @@ -1290,6 +1305,7 @@ static void sdhci_pci_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->vmsd = &sdhci_vmstate; dc->props = sdhci_pci_properties; + dc->reset = sdhci_poweron_reset; } static const TypeInfo sdhci_pci_info = { @@ -1332,10 +1348,6 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp) memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci", SDHC_REGISTERS_MAP_SIZE); sysbus_init_mmio(sbd, &s->iomem); - - if (s->pending_insert_quirk) { - s->pending_insert_state = true; - } } static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) @@ -1345,6 +1357,7 @@ static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) dc->vmsd = &sdhci_vmstate; dc->props = sdhci_sysbus_properties; dc->realize = sdhci_sysbus_realize; + dc->reset = sdhci_poweron_reset; } static const TypeInfo sdhci_sysbus_info = { From 83ec1923cdc794de7a782e99e26daa1d91ca14e9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:17 +0000 Subject: [PATCH 05/30] hw/arm/virt: Provide a secure-only RAM if booting in Secure mode If we're booting in Secure mode, provide a secure-only RAM (just 16MB) so that secure firmware has somewhere to run from that won't be accessible to the Non-secure guest. Signed-off-by: Peter Maydell Message-id: 1455288361-30117-2-git-send-email-peter.maydell@linaro.org --- hw/arm/virt.c | 23 +++++++++++++++++++++++ include/hw/arm/virt.h | 1 + 2 files changed, 24 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 7a56b4671b..a7e6a744aa 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -144,6 +144,7 @@ static const MemMapEntry a15memmap[] = { [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, + [VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 }, [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 }, [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, @@ -977,6 +978,27 @@ static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic) sysbus_mmio_get_region(s, 0)); } +static void create_secure_ram(VirtBoardInfo *vbi, MemoryRegion *secure_sysmem) +{ + MemoryRegion *secram = g_new(MemoryRegion, 1); + char *nodename; + hwaddr base = vbi->memmap[VIRT_SECURE_MEM].base; + hwaddr size = vbi->memmap[VIRT_SECURE_MEM].size; + + memory_region_init_ram(secram, NULL, "virt.secure-ram", size, &error_fatal); + vmstate_register_ram_global(secram); + memory_region_add_subregion(secure_sysmem, base, secram); + + nodename = g_strdup_printf("/secram@%" PRIx64, base); + qemu_fdt_add_subnode(vbi->fdt, nodename); + qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "memory"); + qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay"); + + g_free(nodename); +} + static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) { const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; @@ -1169,6 +1191,7 @@ static void machvirt_init(MachineState *machine) create_uart(vbi, pic, VIRT_UART, sysmem); if (vms->secure) { + create_secure_ram(vbi, secure_sysmem); create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem); } diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 1ce7847ae6..ecd8589603 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -61,6 +61,7 @@ enum { VIRT_PCIE_MMIO_HIGH, VIRT_GPIO, VIRT_SECURE_UART, + VIRT_SECURE_MEM, }; typedef struct MemMapEntry { From 76151cacfe956248a25b38b5e8429465584f47bb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:17 +0000 Subject: [PATCH 06/30] loader: Add load_image_mr() to load ROM image to a MemoryRegion Add a new function load_image_mr(), which behaves like load_image_targphys() except that it loads the ROM image to a specified MemoryRegion rather than to a specified physical address. This is useful when a ROM blob needs to be loaded to a particular flash or ROM device but the address of that device in the machine's address space is not known. (For instance, ROMs in devices, or ROMs which might exist in a different address space to the system address space.) Signed-off-by: Peter Maydell Message-id: 1455288361-30117-3-git-send-email-peter.maydell@linaro.org Reviewed-by: Paolo Bonzini Reviewed-by: Michael S. Tsirkin --- hw/core/loader.c | 35 +++++++++++++++++++++++++++++++---- include/hw/loader.h | 18 ++++++++++++++++-- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index 3a57415bf8..260b3d62cb 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -147,6 +147,28 @@ int load_image_targphys(const char *filename, return size; } +int load_image_mr(const char *filename, MemoryRegion *mr) +{ + int size; + + if (!memory_access_is_direct(mr, false)) { + /* Can only load an image into RAM or ROM */ + return -1; + } + + size = get_image_size(filename); + + if (size > memory_region_size(mr)) { + return -1; + } + if (size > 0) { + if (rom_add_file_mr(filename, mr, -1) < 0) { + return -1; + } + } + return size; +} + void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size, const char *source) { @@ -751,7 +773,7 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name) int rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, - bool option_rom) + bool option_rom, MemoryRegion *mr) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); Rom *rom; @@ -818,7 +840,12 @@ int rom_add_file(const char *file, const char *fw_dir, fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize); } else { - snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr); + if (mr) { + rom->mr = mr; + snprintf(devpath, sizeof(devpath), "/rom@%s", file); + } else { + snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr); + } } add_boot_device_path(bootindex, NULL, devpath); @@ -892,12 +919,12 @@ int rom_add_elf_program(const char *name, void *data, size_t datasize, int rom_add_vga(const char *file) { - return rom_add_file(file, "vgaroms", 0, -1, true); + return rom_add_file(file, "vgaroms", 0, -1, true, NULL); } int rom_add_option(const char *file, int32_t bootindex) { - return rom_add_file(file, "genroms", 0, bootindex, true); + return rom_add_file(file, "genroms", 0, bootindex, true, NULL); } static void rom_reset(void *unused) diff --git a/include/hw/loader.h b/include/hw/loader.h index f7b43ab62f..09c3764875 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -16,6 +16,18 @@ int load_image(const char *filename, uint8_t *addr); /* deprecated */ ssize_t load_image_size(const char *filename, void *addr, size_t size); int load_image_targphys(const char *filename, hwaddr, uint64_t max_sz); +/** + * load_image_mr: load an image into a memory region + * @filename: Path to the image file + * @mr: Memory Region to load into + * + * Load the specified file into the memory region. + * The file loaded is registered as a ROM, so its contents will be + * reinstated whenever the system is reset. + * If the file is larger than the memory region's size the call will fail. + * Returns -1 on failure, or the size of the file. + */ +int load_image_mr(const char *filename, MemoryRegion *mr); /* This is the limit on the maximum uncompressed image size that * load_image_gzipped_buffer() and load_image_gzipped() will read. It prevents @@ -67,7 +79,7 @@ extern bool rom_file_has_mr; int rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, - bool option_rom); + bool option_rom, MemoryRegion *mr); MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, size_t max_len, hwaddr addr, const char *fw_file_name, @@ -82,9 +94,11 @@ void *rom_ptr(hwaddr addr); void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ - rom_add_file(_f, NULL, _a, _i, false) + rom_add_file(_f, NULL, _a, _i, false, NULL) #define rom_add_blob_fixed(_f, _b, _l, _a) \ rom_add_blob(_f, _b, _l, _l, _a, NULL, NULL, NULL) +#define rom_add_file_mr(_f, _mr, _i) \ + rom_add_file(_f, NULL, 0, _i, false, mr) #define PC_ROM_MIN_VGA 0xc0000 #define PC_ROM_MIN_OPTION 0xc8000 From 16f4a8dc5c671eb176121d576ef28d1e49b84bfe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:17 +0000 Subject: [PATCH 07/30] hw/arm/virt: Load bios image to MemoryRegion, not physaddr If we're loading a BIOS image into the first flash device, load it into the flash's memory region specifically, not into the physical address where the flash resides. This will make a difference when the flash might be in the Secure address space rather than the Nonsecure one. Signed-off-by: Peter Maydell Message-id: 1455288361-30117-4-git-send-email-peter.maydell@linaro.org --- hw/arm/virt.c | 55 ++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index a7e6a744aa..c1d28327d0 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -696,13 +696,14 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) } static void create_one_flash(const char *name, hwaddr flashbase, - hwaddr flashsize) + hwaddr flashsize, const char *file) { /* 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"); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); const uint64_t sectorlength = 256 * 1024; if (dinfo) { @@ -722,7 +723,30 @@ static void create_one_flash(const char *name, hwaddr flashbase, qdev_prop_set_string(dev, "name", name); qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, flashbase); + sysbus_mmio_map(sbd, 0, flashbase); + + if (file) { + char *fn; + int image_size; + + 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, file); + if (!fn) { + error_report("Could not find ROM image '%s'", file); + exit(1); + } + image_size = load_image_mr(fn, sysbus_mmio_get_region(sbd, 0)); + g_free(fn); + if (image_size < 0) { + error_report("Could not load ROM image '%s'", file); + exit(1); + } + } } static void create_flash(const VirtBoardInfo *vbi) @@ -734,31 +758,8 @@ static void create_flash(const VirtBoardInfo *vbi) hwaddr flashbase = vbi->memmap[VIRT_FLASH].base; char *nodename; - if (bios_name) { - char *fn; - int image_size; - - 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) { - error_report("Could not find ROM image '%s'", bios_name); - exit(1); - } - image_size = load_image_targphys(fn, flashbase, flashsize); - g_free(fn); - if (image_size < 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); + create_one_flash("virt.flash0", flashbase, flashsize, bios_name); + create_one_flash("virt.flash1", flashbase + flashsize, flashsize, NULL); nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); qemu_fdt_add_subnode(vbi->fdt, nodename); From 738a5d9fbbd242b4a16cd1837fd3ded610ef1b43 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:18 +0000 Subject: [PATCH 08/30] hw/arm/virt: Make first flash device Secure-only if booting secure If the virt board is started with the 'secure' property set to request a Secure setup, then make the first flash device be visible only to the Secure world. This is a breaking change, but I don't expect it to be noticed by anybody, because running TZ-aware guests isn't common and those guests are generally going to be booting from the flash and implicitly expecting their Non-secure guests to not touch it. Signed-off-by: Peter Maydell Message-id: 1455288361-30117-5-git-send-email-peter.maydell@linaro.org --- hw/arm/virt.c | 63 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c1d28327d0..e53e1ceb20 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -696,7 +696,8 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic) } static void create_one_flash(const char *name, hwaddr flashbase, - hwaddr flashsize, const char *file) + hwaddr flashsize, const char *file, + MemoryRegion *sysmem) { /* Create and map a single flash device. We use the same * parameters as the flash devices on the Versatile Express board. @@ -723,7 +724,8 @@ static void create_one_flash(const char *name, hwaddr flashbase, qdev_prop_set_string(dev, "name", name); qdev_init_nofail(dev); - sysbus_mmio_map(sbd, 0, flashbase); + memory_region_add_subregion(sysmem, flashbase, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); if (file) { char *fn; @@ -749,26 +751,59 @@ static void create_one_flash(const char *name, hwaddr flashbase, } } -static void create_flash(const VirtBoardInfo *vbi) +static void create_flash(const VirtBoardInfo *vbi, + MemoryRegion *sysmem, + MemoryRegion *secure_sysmem) { /* Create two flash devices to fill the VIRT_FLASH space in the memmap. * Any file passed via -bios goes in the first of these. + * sysmem is the system memory space. secure_sysmem is the secure view + * of the system, and the first flash device should be made visible only + * there. The second flash device is visible to both secure and nonsecure. + * If sysmem == secure_sysmem this means there is no separate Secure + * address space and both flash devices are generally visible. */ hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2; hwaddr flashbase = vbi->memmap[VIRT_FLASH].base; char *nodename; - create_one_flash("virt.flash0", flashbase, flashsize, bios_name); - create_one_flash("virt.flash1", flashbase + flashsize, flashsize, NULL); + create_one_flash("virt.flash0", flashbase, flashsize, + bios_name, secure_sysmem); + create_one_flash("virt.flash1", flashbase + flashsize, flashsize, + NULL, sysmem); - 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); + if (sysmem == secure_sysmem) { + /* Report both flash devices as a single node in the DT */ + 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); + } else { + /* Report the devices as separate nodes so we can mark one as + * only visible to the secure world. + */ + nodename = g_strdup_printf("/secflash@%" 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); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); + qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled"); + qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay"); + g_free(nodename); + + 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 + flashsize, 2, flashsize); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4); + g_free(nodename); + } } static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as) @@ -1185,7 +1220,7 @@ static void machvirt_init(MachineState *machine) machine->ram_size); memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram); - create_flash(vbi); + create_flash(vbi, sysmem, secure_sysmem ? secure_sysmem : sysmem); create_gic(vbi, pic, gic_version, vms->secure); From 4824a61a6dc1ce768be5c0a81f16224618104a63 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:18 +0000 Subject: [PATCH 09/30] hw/arm/virt: Assume EL3 boot rom will handle PSCI if one is provided If the user passes us an EL3 boot rom, then it is going to want to implement the PSCI interface itself. In this case, disable QEMU's internal PSCI implementation so it does not get in the way, and instead start all CPUs in an SMP configuration at once (the boot rom will catch them all and pen up the secondaries until needed). The boot rom code is also responsible for editing the device tree to include any necessary information about its own PSCI implementation before eventually passing it to a NonSecure guest. (This "start all CPUs at once" approach is what both ARM Trusted Firmware and UEFI expect, since it is what the ARM Foundation Model does; the other approach would be to provide some emulated hardware for "start the secondaries" but this is simplest.) This is a compatibility break, but I don't believe that anybody was using a secure boot ROM with an SMP configuration. Such a setup would be somewhat broken since there was nothing preventing nonsecure guest code from calling the QEMU PSCI function to start up a secondary core in a way that completely bypassed the secure world. Signed-off-by: Peter Maydell Reviewed-by: Laszlo Ersek Message-id: 1456853976-7592-1-git-send-email-peter.maydell@linaro.org --- hw/arm/virt.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e53e1ceb20..8c6c99625f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -73,6 +73,7 @@ typedef struct VirtBoardInfo { uint32_t clock_phandle; uint32_t gic_phandle; uint32_t v2m_phandle; + bool using_psci; } VirtBoardInfo; typedef struct { @@ -248,6 +249,10 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi) void *fdt = vbi->fdt; ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0)); + if (!vbi->using_psci) { + return; + } + qemu_fdt_add_subnode(fdt, "/psci"); if (armcpu->psci_version == 2) { const char comp[] = "arm,psci-0.2\0arm,psci"; @@ -359,7 +364,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", armcpu->dtb_compatible); - if (vbi->smp_cpus > 1) { + if (vbi->using_psci && vbi->smp_cpus > 1) { qemu_fdt_setprop_string(vbi->fdt, nodename, "enable-method", "psci"); } @@ -1095,6 +1100,7 @@ static void machvirt_init(MachineState *machine) VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state); VirtGuestInfo *guest_info = &guest_info_state->info; char **cpustr; + bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); if (!cpu_model) { cpu_model = "cortex-a15"; @@ -1122,6 +1128,15 @@ static void machvirt_init(MachineState *machine) exit(1); } + /* If we have an EL3 boot ROM then the assumption is that it will + * implement PSCI itself, so disable QEMU's internal implementation + * so it doesn't get in the way. Instead of starting secondary + * CPUs in PSCI powerdown state we will start them all running and + * let the boot ROM sort them out. + * The usual case is that we do use QEMU's PSCI implementation. + */ + vbi->using_psci = !(vms->secure && firmware_loaded); + /* The maximum number of CPUs depends on the GIC version, or on how * many redistributors we can fit into the memory map. */ @@ -1189,12 +1204,15 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, false, "has_el3", NULL); } - object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, "psci-conduit", - NULL); + if (vbi->using_psci) { + object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, + "psci-conduit", NULL); - /* Secondary CPUs start in PSCI powered-down state */ - if (n > 0) { - object_property_set_bool(cpuobj, true, "start-powered-off", NULL); + /* Secondary CPUs start in PSCI powered-down state */ + if (n > 0) { + object_property_set_bool(cpuobj, true, + "start-powered-off", NULL); + } } if (object_property_find(cpuobj, "reset-cbar", NULL)) { @@ -1263,7 +1281,7 @@ static void machvirt_init(MachineState *machine) vbi->bootinfo.board_id = -1; vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base; vbi->bootinfo.get_dtb = machvirt_dtb; - vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); + vbi->bootinfo.firmware_loaded = firmware_loaded; arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); /* From eab713941ab83c6d30739e430357e817bf81dad1 Mon Sep 17 00:00:00 2001 From: Andrew Baumann Date: Fri, 4 Mar 2016 11:30:18 +0000 Subject: [PATCH 10/30] bcm2835_mbox/property: replace ldl_phys/stl_phys with endian-specific accesses PMM pointed out that ldl_phys and stl_phys are dependent on the CPU's endianness, whereas device model code should be independent of it. This changes the relevant Raspberry Pi devices to explicitly call the little-endian variants. Signed-off-by: Andrew Baumann Message-id: 1456880233-22568-1-git-send-email-Andrew.Baumann@microsoft.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/bcm2835_mbox.c | 6 +++--- hw/misc/bcm2835_property.c | 38 +++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c index 500baba7c0..106585a7bf 100644 --- a/hw/misc/bcm2835_mbox.c +++ b/hw/misc/bcm2835_mbox.c @@ -98,7 +98,7 @@ static void bcm2835_mbox_update(BCM2835MboxState *s) */ for (n = 0; n < MBOX_CHAN_COUNT; n++) { while (s->available[n] && !(s->mbox[0].status & ARM_MS_FULL)) { - value = ldl_phys(&s->mbox_as, n << MBOX_AS_CHAN_SHIFT); + value = ldl_le_phys(&s->mbox_as, n << MBOX_AS_CHAN_SHIFT); assert(value != MBOX_INVALID_DATA); /* Pending interrupt but no data */ mbox_push(&s->mbox[0], value); } @@ -207,12 +207,12 @@ static void bcm2835_mbox_write(void *opaque, hwaddr offset, ch = value & 0xf; if (ch < MBOX_CHAN_COUNT) { childaddr = ch << MBOX_AS_CHAN_SHIFT; - if (ldl_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) { + if (ldl_le_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) { /* Child busy, push delayed. Push it in the arm->vc mbox */ mbox_push(&s->mbox[1], value); } else { /* Push it directly to the child device */ - stl_phys(&s->mbox_as, childaddr, value); + stl_le_phys(&s->mbox_as, childaddr, value); } } else { /* Invalid channel number */ diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 581922abd7..41fbbe3e7f 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -22,20 +22,20 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) s->addr = value; - tot_len = ldl_phys(&s->dma_as, value); + tot_len = ldl_le_phys(&s->dma_as, value); /* @(addr + 4) : Buffer response code */ value = s->addr + 8; while (value + 8 <= s->addr + tot_len) { - tag = ldl_phys(&s->dma_as, value); - bufsize = ldl_phys(&s->dma_as, value + 4); + tag = ldl_le_phys(&s->dma_as, value); + bufsize = ldl_le_phys(&s->dma_as, value + 4); /* @(value + 8) : Request/response indicator */ resplen = 0; switch (tag) { case 0x00000000: /* End tag */ break; case 0x00000001: /* Get firmware revision */ - stl_phys(&s->dma_as, value + 12, 346337); + stl_le_phys(&s->dma_as, value + 12, 346337); resplen = 4; break; case 0x00010001: /* Get board model */ @@ -44,7 +44,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00010002: /* Get board revision */ - stl_phys(&s->dma_as, value + 12, s->board_rev); + stl_le_phys(&s->dma_as, value + 12, s->board_rev); resplen = 4; break; case 0x00010003: /* Get board MAC address */ @@ -58,24 +58,24 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; case 0x00010005: /* Get ARM memory */ /* base */ - stl_phys(&s->dma_as, value + 12, 0); + stl_le_phys(&s->dma_as, value + 12, 0); /* size */ - stl_phys(&s->dma_as, value + 16, s->ram_size); + stl_le_phys(&s->dma_as, value + 16, s->ram_size); resplen = 8; break; case 0x00028001: /* Set power state */ /* Assume that whatever device they asked for exists, * and we'll just claim we set it to the desired state */ - tmp = ldl_phys(&s->dma_as, value + 16); - stl_phys(&s->dma_as, value + 16, (tmp & 1)); + tmp = ldl_le_phys(&s->dma_as, value + 16); + stl_le_phys(&s->dma_as, value + 16, (tmp & 1)); resplen = 8; break; /* Clocks */ case 0x00030001: /* Get clock state */ - stl_phys(&s->dma_as, value + 16, 0x1); + stl_le_phys(&s->dma_as, value + 16, 0x1); resplen = 8; break; @@ -88,15 +88,15 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00030002: /* Get clock rate */ case 0x00030004: /* Get max clock rate */ case 0x00030007: /* Get min clock rate */ - switch (ldl_phys(&s->dma_as, value + 12)) { + switch (ldl_le_phys(&s->dma_as, value + 12)) { case 1: /* EMMC */ - stl_phys(&s->dma_as, value + 16, 50000000); + stl_le_phys(&s->dma_as, value + 16, 50000000); break; case 2: /* UART */ - stl_phys(&s->dma_as, value + 16, 3000000); + stl_le_phys(&s->dma_as, value + 16, 3000000); break; default: - stl_phys(&s->dma_as, value + 16, 700000000); + stl_le_phys(&s->dma_as, value + 16, 700000000); break; } resplen = 8; @@ -113,19 +113,19 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* Temperature */ case 0x00030006: /* Get temperature */ - stl_phys(&s->dma_as, value + 16, 25000); + stl_le_phys(&s->dma_as, value + 16, 25000); resplen = 8; break; case 0x0003000A: /* Get max temperature */ - stl_phys(&s->dma_as, value + 16, 99000); + stl_le_phys(&s->dma_as, value + 16, 99000); resplen = 8; break; case 0x00060001: /* Get DMA channels */ /* channels 2-5 */ - stl_phys(&s->dma_as, value + 12, 0x003C); + stl_le_phys(&s->dma_as, value + 12, 0x003C); resplen = 4; break; @@ -143,12 +143,12 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; } - stl_phys(&s->dma_as, value + 8, (1 << 31) | resplen); + stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen); value += bufsize + 12; } /* Buffer response code */ - stl_phys(&s->dma_as, s->addr + 4, (1 << 31)); + stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31)); } static uint64_t bcm2835_property_read(void *opaque, hwaddr offset, From a0e1e6d705b44c3e71621fbf137bb3faf1719b33 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 Mar 2016 11:30:18 +0000 Subject: [PATCH 11/30] linux-user: arm: fix coding style for some linux-user signal functions Reviewed-by: Peter Maydell Signed-off-by: Paolo Bonzini Signed-off-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- linux-user/signal.c | 110 ++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 962111cfdf..96e86c0a29 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1536,82 +1536,84 @@ static void setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ CPUARMState *env, abi_ulong mask) { - __put_user(env->regs[0], &sc->arm_r0); - __put_user(env->regs[1], &sc->arm_r1); - __put_user(env->regs[2], &sc->arm_r2); - __put_user(env->regs[3], &sc->arm_r3); - __put_user(env->regs[4], &sc->arm_r4); - __put_user(env->regs[5], &sc->arm_r5); - __put_user(env->regs[6], &sc->arm_r6); - __put_user(env->regs[7], &sc->arm_r7); - __put_user(env->regs[8], &sc->arm_r8); - __put_user(env->regs[9], &sc->arm_r9); - __put_user(env->regs[10], &sc->arm_r10); - __put_user(env->regs[11], &sc->arm_fp); - __put_user(env->regs[12], &sc->arm_ip); - __put_user(env->regs[13], &sc->arm_sp); - __put_user(env->regs[14], &sc->arm_lr); - __put_user(env->regs[15], &sc->arm_pc); + __put_user(env->regs[0], &sc->arm_r0); + __put_user(env->regs[1], &sc->arm_r1); + __put_user(env->regs[2], &sc->arm_r2); + __put_user(env->regs[3], &sc->arm_r3); + __put_user(env->regs[4], &sc->arm_r4); + __put_user(env->regs[5], &sc->arm_r5); + __put_user(env->regs[6], &sc->arm_r6); + __put_user(env->regs[7], &sc->arm_r7); + __put_user(env->regs[8], &sc->arm_r8); + __put_user(env->regs[9], &sc->arm_r9); + __put_user(env->regs[10], &sc->arm_r10); + __put_user(env->regs[11], &sc->arm_fp); + __put_user(env->regs[12], &sc->arm_ip); + __put_user(env->regs[13], &sc->arm_sp); + __put_user(env->regs[14], &sc->arm_lr); + __put_user(env->regs[15], &sc->arm_pc); #ifdef TARGET_CONFIG_CPU_32 - __put_user(cpsr_read(env), &sc->arm_cpsr); + __put_user(cpsr_read(env), &sc->arm_cpsr); #endif - __put_user(/* current->thread.trap_no */ 0, &sc->trap_no); - __put_user(/* current->thread.error_code */ 0, &sc->error_code); - __put_user(/* current->thread.address */ 0, &sc->fault_address); - __put_user(mask, &sc->oldmask); + __put_user(/* current->thread.trap_no */ 0, &sc->trap_no); + __put_user(/* current->thread.error_code */ 0, &sc->error_code); + __put_user(/* current->thread.address */ 0, &sc->fault_address); + __put_user(mask, &sc->oldmask); } static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) { - unsigned long sp = regs->regs[13]; + unsigned long sp = regs->regs[13]; - /* - * This is the X/Open sanctioned signal stack switching. - */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - /* - * ATPCS B01 mandates 8-byte alignment - */ - return (sp - framesize) & ~7; + /* + * This is the X/Open sanctioned signal stack switching. + */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + /* + * ATPCS B01 mandates 8-byte alignment + */ + return (sp - framesize) & ~7; } static void setup_return(CPUARMState *env, struct target_sigaction *ka, abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) { - abi_ulong handler = ka->_sa_handler; - abi_ulong retcode; - int thumb = handler & 1; - uint32_t cpsr = cpsr_read(env); + abi_ulong handler = ka->_sa_handler; + abi_ulong retcode; + int thumb = handler & 1; + uint32_t cpsr = cpsr_read(env); - cpsr &= ~CPSR_IT; - if (thumb) { - cpsr |= CPSR_T; - } else { - cpsr &= ~CPSR_T; - } + cpsr &= ~CPSR_IT; + if (thumb) { + cpsr |= CPSR_T; + } else { + cpsr &= ~CPSR_T; + } - if (ka->sa_flags & TARGET_SA_RESTORER) { - retcode = ka->sa_restorer; - } else { - unsigned int idx = thumb; + if (ka->sa_flags & TARGET_SA_RESTORER) { + retcode = ka->sa_restorer; + } else { + unsigned int idx = thumb; - if (ka->sa_flags & TARGET_SA_SIGINFO) - idx += 2; + if (ka->sa_flags & TARGET_SA_SIGINFO) { + idx += 2; + } __put_user(retcodes[idx], rc); - retcode = rc_addr + thumb; - } + retcode = rc_addr + thumb; + } - env->regs[0] = usig; - env->regs[13] = frame_addr; - env->regs[14] = retcode; - env->regs[15] = handler & (thumb ? ~1 : ~3); - cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr); + env->regs[0] = usig; + env->regs[13] = frame_addr; + env->regs[14] = retcode; + env->regs[15] = handler & (thumb ? ~1 : ~3); + cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr); } static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) From 49017bd8b4395ba380c45c2fc41f4353fc2f0f06 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 Mar 2016 11:30:18 +0000 Subject: [PATCH 12/30] linux-user: arm: pass env to get_user_code_* This matches the idiom used by get_user_data_* later in the series, and will help when bswap_code will be replaced by SCTLR.B. Reviewed-by: Peter Maydell Signed-off-by: Paolo Bonzini Signed-off-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- linux-user/main.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 700724effe..bcb9f66570 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -435,17 +435,17 @@ void cpu_loop(CPUX86State *env) #ifdef TARGET_ARM -#define get_user_code_u32(x, gaddr, doswap) \ +#define get_user_code_u32(x, gaddr, env) \ ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && (doswap)) { \ + if (!__r && (env)->bswap_code) { \ (x) = bswap32(x); \ } \ __r; \ }) -#define get_user_code_u16(x, gaddr, doswap) \ +#define get_user_code_u16(x, gaddr, env) \ ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && (doswap)) { \ + if (!__r && (env)->bswap_code) { \ (x) = bswap16(x); \ } \ __r; \ @@ -692,7 +692,7 @@ void cpu_loop(CPUARMState *env) /* we handle the FPU emulation here, as Linux */ /* we get the opcode */ /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(opcode, env->regs[15], env->bswap_code); + get_user_code_u32(opcode, env->regs[15], env); rc = EmulateAll(opcode, &ts->fpa, env); if (rc == 0) { /* illegal instruction */ @@ -762,25 +762,23 @@ void cpu_loop(CPUARMState *env) if (trapnr == EXCP_BKPT) { if (env->thumb) { /* FIXME - what to do if get_user() fails? */ - get_user_code_u16(insn, env->regs[15], env->bswap_code); + get_user_code_u16(insn, env->regs[15], env); n = insn & 0xff; env->regs[15] += 2; } else { /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(insn, env->regs[15], env->bswap_code); + get_user_code_u32(insn, env->regs[15], env); n = (insn & 0xf) | ((insn >> 4) & 0xff0); env->regs[15] += 4; } } else { if (env->thumb) { /* FIXME - what to do if get_user() fails? */ - get_user_code_u16(insn, env->regs[15] - 2, - env->bswap_code); + get_user_code_u16(insn, env->regs[15] - 2, env); n = insn & 0xff; } else { /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(insn, env->regs[15] - 4, - env->bswap_code); + get_user_code_u32(insn, env->regs[15] - 4, env); n = insn & 0xffffff; } } From f9fd40ebe4f55e0048e002925b8d65e66d56e7a7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 Mar 2016 11:30:19 +0000 Subject: [PATCH 13/30] target-arm: implement SCTLR.B, drop bswap_code bswap_code is a CPU property of sorts ("is the iside endianness the opposite way round to TARGET_WORDS_BIGENDIAN?") but it is not the actual CPU state involved here which is SCTLR.B (set for BE32 binaries, clear for BE8). Replace bswap_code with SCTLR.B, and pass that to arm_ld*_code. The next patches will make data fetches honor both SCTLR.B and CPSR.E appropriately. Signed-off-by: Paolo Bonzini [PC changes: * rebased on master (Jan 2016) * s/TARGET_USER_ONLY/CONFIG_USER_ONLY * Use bswap_code() for disas_set_info() instead of raw sctlr_b ] Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- linux-user/main.c | 10 +++++--- target-arm/arm_ldst.h | 8 +++---- target-arm/cpu.c | 2 +- target-arm/cpu.h | 47 +++++++++++++++++++++++++++++++------- target-arm/helper.c | 8 +++---- target-arm/translate-a64.c | 6 ++--- target-arm/translate.c | 16 ++++++------- target-arm/translate.h | 2 +- 8 files changed, 67 insertions(+), 32 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index bcb9f66570..fe2a8dd6db 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -437,7 +437,7 @@ void cpu_loop(CPUX86State *env) #define get_user_code_u32(x, gaddr, env) \ ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && (env)->bswap_code) { \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ (x) = bswap32(x); \ } \ __r; \ @@ -445,7 +445,7 @@ void cpu_loop(CPUX86State *env) #define get_user_code_u16(x, gaddr, env) \ ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && (env)->bswap_code) { \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ (x) = bswap16(x); \ } \ __r; \ @@ -4449,11 +4449,15 @@ int main(int argc, char **argv, char **envp) for(i = 0; i < 16; i++) { env->regs[i] = regs->uregs[i]; } +#ifdef TARGET_WORDS_BIGENDIAN /* Enable BE8. */ if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 && (info->elf_flags & EF_ARM_BE8)) { - env->bswap_code = 1; + /* nothing for now, CPSR.E not emulated yet */ + } else { + env->cp15.sctlr_el[1] |= SCTLR_B; } +#endif } #elif defined(TARGET_UNICORE32) { diff --git a/target-arm/arm_ldst.h b/target-arm/arm_ldst.h index b1ece01731..35c2c43919 100644 --- a/target-arm/arm_ldst.h +++ b/target-arm/arm_ldst.h @@ -25,10 +25,10 @@ /* Load an instruction and return it in the standard little-endian order */ static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr, - bool do_swap) + bool sctlr_b) { uint32_t insn = cpu_ldl_code(env, addr); - if (do_swap) { + if (bswap_code(sctlr_b)) { return bswap32(insn); } return insn; @@ -36,10 +36,10 @@ static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr, /* Ditto, for a halfword (Thumb) instruction */ static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr, - bool do_swap) + bool sctlr_b) { uint16_t insn = cpu_lduw_code(env, addr); - if (do_swap) { + if (bswap_code(sctlr_b)) { return bswap16(insn); } return insn; diff --git a/target-arm/cpu.c b/target-arm/cpu.c index e95b0307a6..001fccf481 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -427,7 +427,7 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) } else { info->print_insn = print_insn_arm; } - if (env->bswap_code) { + if (bswap_code(arm_sctlr_b(env))) { #ifdef TARGET_WORDS_BIGENDIAN info->endian = BFD_ENDIAN_LITTLE; #else diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 744f052a67..61b8b03f65 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -478,9 +478,6 @@ typedef struct CPUARMState { uint32_t cregs[16]; } iwmmxt; - /* For mixed endian mode. */ - bool bswap_code; - #if defined(CONFIG_USER_ONLY) /* For usermode syscall translation. */ int eabi; @@ -1898,6 +1895,19 @@ static inline bool arm_singlestep_active(CPUARMState *env) && arm_generate_debug_exceptions(env); } +static inline bool arm_sctlr_b(CPUARMState *env) +{ + return + /* We need not implement SCTLR.ITD in user-mode emulation, so + * let linux-user ignore the fact that it conflicts with SCTLR_B. + * This lets people run BE32 binaries with "-cpu any". + */ +#ifndef CONFIG_USER_ONLY + !arm_feature(env, ARM_FEATURE_V7) && +#endif + (env->cp15.sctlr_el[1] & SCTLR_B) != 0; +} + #include "exec/cpu-all.h" /* Bit usage in the TB flags field: bit 31 indicates whether we are @@ -1928,8 +1938,8 @@ static inline bool arm_singlestep_active(CPUARMState *env) #define ARM_TBFLAG_VFPEN_MASK (1 << ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC_SHIFT 8 #define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT) -#define ARM_TBFLAG_BSWAP_CODE_SHIFT 16 -#define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT) +#define ARM_TBFLAG_SCTLR_B_SHIFT 16 +#define ARM_TBFLAG_SCTLR_B_MASK (1 << ARM_TBFLAG_SCTLR_B_SHIFT) /* We store the bottom two bits of the CPAR as TB flags and handle * checks on the other bits at runtime */ @@ -1965,13 +1975,34 @@ static inline bool arm_singlestep_active(CPUARMState *env) (((F) & ARM_TBFLAG_VFPEN_MASK) >> ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC(F) \ (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT) -#define ARM_TBFLAG_BSWAP_CODE(F) \ - (((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT) +#define ARM_TBFLAG_SCTLR_B(F) \ + (((F) & ARM_TBFLAG_SCTLR_B_MASK) >> ARM_TBFLAG_SCTLR_B_SHIFT) #define ARM_TBFLAG_XSCALE_CPAR(F) \ (((F) & ARM_TBFLAG_XSCALE_CPAR_MASK) >> ARM_TBFLAG_XSCALE_CPAR_SHIFT) #define ARM_TBFLAG_NS(F) \ (((F) & ARM_TBFLAG_NS_MASK) >> ARM_TBFLAG_NS_SHIFT) +static inline bool bswap_code(bool sctlr_b) +{ +#ifdef CONFIG_USER_ONLY + /* BE8 (SCTLR.B = 0, TARGET_WORDS_BIGENDIAN = 1) is mixed endian. + * The invalid combination SCTLR.B=1/CPSR.E=1/TARGET_WORDS_BIGENDIAN=0 + * would also end up as a mixed-endian mode with BE code, LE data. + */ + return +#ifdef TARGET_WORDS_BIGENDIAN + 1 ^ +#endif + sctlr_b; +#else + /* We do not implement BE32 mode for system-mode emulation, but + * anyway it would always do little-endian accesses with + * TARGET_WORDS_BIGENDIAN = 0. + */ + return 0; +#endif +} + /* Return the exception level to which FP-disabled exceptions should * be taken, or 0 if FP is enabled. */ @@ -2049,7 +2080,7 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT) - | (env->bswap_code << ARM_TBFLAG_BSWAP_CODE_SHIFT); + | (arm_sctlr_b(env) << ARM_TBFLAG_SCTLR_B_SHIFT); if (!(access_secure_reg(env))) { *flags |= ARM_TBFLAG_NS_MASK; } diff --git a/target-arm/helper.c b/target-arm/helper.c index 935f13b7ed..0a0e85d5c0 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -5848,7 +5848,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) case EXCP_BKPT: if (semihosting_enabled()) { int nr; - nr = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff; + nr = arm_lduw_code(env, env->regs[15], arm_sctlr_b(env)) & 0xff; if (nr == 0xab) { env->regs[15] += 2; qemu_log_mask(CPU_LOG_INT, @@ -6386,13 +6386,13 @@ static inline bool check_for_semihosting(CPUState *cs) case EXCP_SWI: /* Check for semihosting interrupt. */ if (env->thumb) { - imm = arm_lduw_code(env, env->regs[15] - 2, env->bswap_code) + imm = arm_lduw_code(env, env->regs[15] - 2, arm_sctlr_b(env)) & 0xff; if (imm == 0xab) { break; } } else { - imm = arm_ldl_code(env, env->regs[15] - 4, env->bswap_code) + imm = arm_ldl_code(env, env->regs[15] - 4, arm_sctlr_b(env)) & 0xffffff; if (imm == 0x123456) { break; @@ -6402,7 +6402,7 @@ static inline bool check_for_semihosting(CPUState *cs) case EXCP_BKPT: /* See if this is a semihosting syscall. */ if (env->thumb) { - imm = arm_lduw_code(env, env->regs[15], env->bswap_code) + imm = arm_lduw_code(env, env->regs[15], arm_sctlr_b(env)) & 0xff; if (imm == 0xab) { env->regs[15] += 2; diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 7f65aeab64..f6dd44b86f 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -10966,7 +10966,7 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) { uint32_t insn; - insn = arm_ldl_code(env, s->pc, s->bswap_code); + insn = arm_ldl_code(env, s->pc, s->sctlr_b); s->insn = insn; s->pc += 4; @@ -11031,7 +11031,7 @@ void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb) dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); dc->thumb = 0; - dc->bswap_code = 0; + dc->sctlr_b = 0; dc->condexec_mask = 0; dc->condexec_cond = 0; dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); @@ -11217,7 +11217,7 @@ done_generating: qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); log_target_disas(cs, pc_start, dc->pc - pc_start, - 4 | (dc->bswap_code << 1)); + 4 | (bswap_code(dc->sctlr_b) ? 2 : 0)); qemu_log("\n"); } #endif diff --git a/target-arm/translate.c b/target-arm/translate.c index c29c47f006..b52bbc3d1d 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7770,7 +7770,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if ((insn & 0x0ffffdff) == 0x01010000) { ARCH(6); /* setend */ - if (((insn >> 9) & 1) != s->bswap_code) { + if (((insn >> 9) & 1) != bswap_code(s->sctlr_b)) { /* Dynamic endianness switching not implemented. */ qemu_log_mask(LOG_UNIMP, "arm: unimplemented setend\n"); goto illegal_op; @@ -9286,7 +9286,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw /* Fall through to 32-bit decode. */ } - insn = arm_lduw_code(env, s->pc, s->bswap_code); + insn = arm_lduw_code(env, s->pc, s->sctlr_b); s->pc += 2; insn |= (uint32_t)insn_hw1 << 16; @@ -10528,7 +10528,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } } - insn = arm_lduw_code(env, s->pc, s->bswap_code); + insn = arm_lduw_code(env, s->pc, s->sctlr_b); s->pc += 2; switch (insn >> 12) { @@ -11099,7 +11099,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) case 2: /* setend */ ARCH(6); - if (((insn >> 3) & 1) != s->bswap_code) { + if (((insn >> 3) & 1) != bswap_code(s->sctlr_b)) { /* Dynamic endianness switching not implemented. */ qemu_log_mask(LOG_UNIMP, "arm: unimplemented setend\n"); goto illegal_op; @@ -11253,7 +11253,7 @@ static bool insn_crosses_page(CPUARMState *env, DisasContext *s) } /* This must be a Thumb insn */ - insn = arm_lduw_code(env, s->pc, s->bswap_code); + insn = arm_lduw_code(env, s->pc, s->sctlr_b); if ((insn >> 11) >= 0x1d) { /* Top five bits 0b11101 / 0b11110 / 0b11111 : this is the @@ -11307,7 +11307,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); dc->thumb = ARM_TBFLAG_THUMB(tb->flags); - dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags); + dc->sctlr_b = ARM_TBFLAG_SCTLR_B(tb->flags); dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); @@ -11487,7 +11487,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) } } } else { - unsigned int insn = arm_ldl_code(env, dc->pc, dc->bswap_code); + unsigned int insn = arm_ldl_code(env, dc->pc, dc->sctlr_b); dc->pc += 4; disas_arm_insn(dc, insn); } @@ -11644,7 +11644,7 @@ done_generating: qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); log_target_disas(cs, pc_start, dc->pc - pc_start, - dc->thumb | (dc->bswap_code << 1)); + dc->thumb | (dc->sctlr_b << 1)); qemu_log("\n"); } #endif diff --git a/target-arm/translate.h b/target-arm/translate.h index 82e3f6bf06..54452322c9 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -16,7 +16,7 @@ typedef struct DisasContext { struct TranslationBlock *tb; int singlestep_enabled; int thumb; - int bswap_code; + int sctlr_b; #if !defined(CONFIG_USER_ONLY) int user; #endif From ed50ff7875d61a75517c92deb0444d73fbbca878 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:19 +0000 Subject: [PATCH 14/30] target-arm: cpu: Move cpu_is_big_endian to header There is a CPU data endianness test that is used to drive the virtio_big_endian test. Move this up to the header so it can be more generally used for endian tests. The KVM specific cpu_syncronize_state call is left behind in the virtio specific function. Rename it arm_cpu-data_is_big_endian() to more accurately capture that this is for data accesses only. Reviewed-by: Alistair Francis Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/cpu.c | 19 +++---------------- target-arm/cpu.h | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 001fccf481..352d9f883d 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -369,26 +369,13 @@ static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) #endif } -static bool arm_cpu_is_big_endian(CPUState *cs) +static bool arm_cpu_virtio_is_big_endian(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - int cur_el; cpu_synchronize_state(cs); - - /* In 32bit guest endianness is determined by looking at CPSR's E bit */ - if (!is_a64(env)) { - return (env->uncached_cpsr & CPSR_E) ? 1 : 0; - } - - cur_el = arm_current_el(env); - - if (cur_el == 0) { - return (env->cp15.sctlr_el[1] & SCTLR_E0E) != 0; - } - - return (env->cp15.sctlr_el[cur_el] & SCTLR_EE) != 0; + return arm_cpu_data_is_big_endian(env); } #endif @@ -1476,7 +1463,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->get_phys_page_attrs_debug = arm_cpu_get_phys_page_attrs_debug; cc->asidx_from_attrs = arm_asidx_from_attrs; cc->vmsd = &vmstate_arm_cpu; - cc->virtio_is_big_endian = arm_cpu_is_big_endian; + cc->virtio_is_big_endian = arm_cpu_virtio_is_big_endian; cc->write_elf64_note = arm_cpu_write_elf64_note; cc->write_elf32_note = arm_cpu_write_elf32_note; #endif diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 61b8b03f65..75e5ea0a43 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -1908,6 +1908,25 @@ static inline bool arm_sctlr_b(CPUARMState *env) (env->cp15.sctlr_el[1] & SCTLR_B) != 0; } +/* Return true if the processor is in big-endian mode. */ +static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) +{ + int cur_el; + + /* In 32bit endianness is determined by looking at CPSR's E bit */ + if (!is_a64(env)) { + return (env->uncached_cpsr & CPSR_E) ? 1 : 0; + } + + cur_el = arm_current_el(env); + + if (cur_el == 0) { + return (env->cp15.sctlr_el[1] & SCTLR_E0E) != 0; + } + + return (env->cp15.sctlr_el[cur_el] & SCTLR_EE) != 0; +} + #include "exec/cpu-all.h" /* Bit usage in the TB flags field: bit 31 indicates whether we are From b2e62d9a7b9a2eb10e451a57813bad168376e122 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:19 +0000 Subject: [PATCH 15/30] arm: cpu: handle BE32 user-mode as BE endian with address manipulations on subword accesses (to give the illusion of BE). But user-mode cannot tell the difference and is already implemented as straight BE. So handle the difference in the endianess query, where USER mode is BE and system is not. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/cpu.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 75e5ea0a43..ab0ea92baf 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -1915,7 +1915,22 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) /* In 32bit endianness is determined by looking at CPSR's E bit */ if (!is_a64(env)) { - return (env->uncached_cpsr & CPSR_E) ? 1 : 0; + return +#ifdef CONFIG_USER_ONLY + /* In system mode, BE32 is modelled in line with the + * architecture (as word-invariant big-endianness), where loads + * and stores are done little endian but from addresses which + * are adjusted by XORing with the appropriate constant. So the + * endianness to use for the raw data access is not affected by + * SCTLR.B. + * In user mode, however, we model BE32 as byte-invariant + * big-endianness (because user-only code cannot tell the + * difference), and so we need to use a data access endianness + * that depends on SCTLR.B. + */ + arm_sctlr_b(env) || +#endif + ((env->uncached_cpsr & CPSR_E) ? 1 : 0); } cur_el = arm_current_el(env); From 9c5a7460389e92d230362a983b5a4158d6a1cefe Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:19 +0000 Subject: [PATCH 16/30] linux-user: arm: set CPSR.E/SCTLR.E0E correctly for BE mode If doing big-endian linux-user mode, set both the CPSR.E and SCTLR.E0E bits. This sets big-endian mode for data accesses. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- linux-user/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux-user/main.c b/linux-user/main.c index fe2a8dd6db..510b3b7740 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -4453,7 +4453,8 @@ int main(int argc, char **argv, char **envp) /* Enable BE8. */ if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 && (info->elf_flags & EF_ARM_BE8)) { - /* nothing for now, CPSR.E not emulated yet */ + env->uncached_cpsr |= CPSR_E; + env->cp15.sctlr_el[1] |= SCTLR_E0E; } else { env->cp15.sctlr_el[1] |= SCTLR_B; } From c3ae85fc8f36b64fb73038214e4359f1e470d169 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 Mar 2016 11:30:19 +0000 Subject: [PATCH 17/30] linux-user: arm: handle CPSR.E correctly in strex emulation Now that CPSR.E is set correctly, prepare for when setend will be able to change it; bswap data in and out of strex manually by comparing SCTLR.B, CPSR.E and TARGET_WORDS_BIGENDIAN (we do not have the luxury of using TCGMemOps). Signed-off-by: Paolo Bonzini [ PC changes: * Moved SCTLR/CPSR logic to arm_cpu_data_is_big_endian ] Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- linux-user/main.c | 50 ++++++++++++++++++++++++++++++++++++++++------- target-arm/cpu.h | 11 +++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 510b3b7740..2b1e7552da 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -451,6 +451,38 @@ void cpu_loop(CPUX86State *env) __r; \ }) +#define get_user_data_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_data_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define put_user_data_u32(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap32(__x); \ + } \ + put_user_u32(__x, (gaddr)); \ + }) + +#define put_user_data_u16(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap16(__x); \ + } \ + put_user_u16(__x, (gaddr)); \ + }) + #ifdef TARGET_ABI32 /* Commpage handling -- there is no commpage for AArch64 */ @@ -610,11 +642,11 @@ static int do_strex(CPUARMState *env) segv = get_user_u8(val, addr); break; case 1: - segv = get_user_u16(val, addr); + segv = get_user_data_u16(val, addr, env); break; case 2: case 3: - segv = get_user_u32(val, addr); + segv = get_user_data_u32(val, addr, env); break; default: abort(); @@ -625,12 +657,16 @@ static int do_strex(CPUARMState *env) } if (size == 3) { uint32_t valhi; - segv = get_user_u32(valhi, addr + 4); + segv = get_user_data_u32(valhi, addr + 4, env); if (segv) { env->exception.vaddress = addr + 4; goto done; } - val = deposit64(val, 32, 32, valhi); + if (arm_cpu_bswap_data(env)) { + val = deposit64((uint64_t)valhi, 32, 32, val); + } else { + val = deposit64(val, 32, 32, valhi); + } } if (val != env->exclusive_val) { goto fail; @@ -642,11 +678,11 @@ static int do_strex(CPUARMState *env) segv = put_user_u8(val, addr); break; case 1: - segv = put_user_u16(val, addr); + segv = put_user_data_u16(val, addr, env); break; case 2: case 3: - segv = put_user_u32(val, addr); + segv = put_user_data_u32(val, addr, env); break; } if (segv) { @@ -655,7 +691,7 @@ static int do_strex(CPUARMState *env) } if (size == 3) { val = env->regs[(env->exclusive_info >> 12) & 0xf]; - segv = put_user_u32(val, addr + 4); + segv = put_user_data_u32(val, addr + 4, env); if (segv) { env->exception.vaddress = addr + 4; goto done; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index ab0ea92baf..cbf171cc23 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -2102,6 +2102,17 @@ static inline int fp_exception_el(CPUARMState *env) return 0; } +#ifdef CONFIG_USER_ONLY +static inline bool arm_cpu_bswap_data(CPUARMState *env) +{ + return +#ifdef TARGET_WORDS_BIGENDIAN + 1 ^ +#endif + arm_cpu_data_is_big_endian(env); +} +#endif + static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { From 73462dddf670c32c45c8ea359658092b0365b2d4 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:20 +0000 Subject: [PATCH 18/30] target-arm: implement SCTLR.EE Implement SCTLR.EE bit which controls data endianess for exceptions and page table translations. SCTLR.EE is mirrored to the CPSR.E bit on exception entry. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/helper.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 0a0e85d5c0..eaded41969 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -6241,6 +6241,11 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) env->condexec_bits = 0; /* Switch to the new mode, and to the correct instruction set. */ env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; + /* Set new mode endianness */ + env->uncached_cpsr &= ~CPSR_E; + if (env->cp15.sctlr_el[arm_current_el(env)] & SCTLR_EE) { + env->uncached_cpsr |= ~CPSR_E; + } env->daif |= mask; /* this is a lie, as the was no c1_sys on V4T/V5, but who cares * and we should just guard the thumb mode on V4 */ @@ -6527,6 +6532,12 @@ static inline bool regime_translation_disabled(CPUARMState *env, return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0; } +static inline bool regime_translation_big_endian(CPUARMState *env, + ARMMMUIdx mmu_idx) +{ + return (regime_sctlr(env, mmu_idx) & SCTLR_EE) != 0; +} + /* Return the TCR controlling this translation regime */ static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) { @@ -6849,7 +6860,11 @@ static uint32_t arm_ldl_ptw(CPUState *cs, hwaddr addr, bool is_secure, if (fi->s1ptw) { return 0; } - return address_space_ldl(as, addr, attrs, NULL); + if (regime_translation_big_endian(env, mmu_idx)) { + return address_space_ldl_be(as, addr, attrs, NULL); + } else { + return address_space_ldl_le(as, addr, attrs, NULL); + } } static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure, @@ -6867,7 +6882,11 @@ static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure, if (fi->s1ptw) { return 0; } - return address_space_ldq(as, addr, attrs, NULL); + if (regime_translation_big_endian(env, mmu_idx)) { + return address_space_ldq_be(as, addr, attrs, NULL); + } else { + return address_space_ldq_le(as, addr, attrs, NULL); + } } static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, From 12dcc3217dc86b7dccee293b2f6e3fa4f7b014ae Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 Mar 2016 11:30:20 +0000 Subject: [PATCH 19/30] target-arm: pass DisasContext to gen_aa32_ld*/st* We'll need the DisasContext in the next patch to retrieve the desired endianness, so pass it as a whole to gen_aa32_ld*/st*. Unfortunately we cannot let those functions call get_mem_index, because of user-mode load/store instructions. Signed-off-by: Paolo Bonzini [ PC changes: * Fix long lines ] Reviewed-by: Peter Maydell Signed-off-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- target-arm/translate.c | 270 ++++++++++++++++++++++------------------- 1 file changed, 142 insertions(+), 128 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index b52bbc3d1d..41c3c7fcb6 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -921,23 +921,27 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) #if TARGET_LONG_BITS == 32 #define DO_GEN_LD(SUFF, OPC) \ -static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ +static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 addr, int index) \ { \ tcg_gen_qemu_ld_i32(val, addr, index, (OPC)); \ } #define DO_GEN_ST(SUFF, OPC) \ -static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ +static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 addr, int index) \ { \ tcg_gen_qemu_st_i32(val, addr, index, (OPC)); \ } -static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) +static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, + TCGv_i32 addr, int index) { tcg_gen_qemu_ld_i64(val, addr, index, MO_TEQ); } -static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) +static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, + TCGv_i32 addr, int index) { tcg_gen_qemu_st_i64(val, addr, index, MO_TEQ); } @@ -945,7 +949,8 @@ static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) #else #define DO_GEN_LD(SUFF, OPC) \ -static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ +static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 addr, int index) \ { \ TCGv addr64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ @@ -954,7 +959,8 @@ static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ } #define DO_GEN_ST(SUFF, OPC) \ -static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ +static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 addr, int index) \ { \ TCGv addr64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ @@ -962,7 +968,8 @@ static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ tcg_temp_free(addr64); \ } -static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) +static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, + TCGv_i32 addr, int index) { TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); @@ -970,7 +977,8 @@ static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) tcg_temp_free(addr64); } -static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) +static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, + TCGv_i32 addr, int index) { TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); @@ -1285,18 +1293,18 @@ VFP_GEN_FIX(ulto, ) static inline void gen_vfp_ld(DisasContext *s, int dp, TCGv_i32 addr) { if (dp) { - gen_aa32_ld64(cpu_F0d, addr, get_mem_index(s)); + gen_aa32_ld64(s, cpu_F0d, addr, get_mem_index(s)); } else { - gen_aa32_ld32u(cpu_F0s, addr, get_mem_index(s)); + gen_aa32_ld32u(s, cpu_F0s, addr, get_mem_index(s)); } } static inline void gen_vfp_st(DisasContext *s, int dp, TCGv_i32 addr) { if (dp) { - gen_aa32_st64(cpu_F0d, addr, get_mem_index(s)); + gen_aa32_st64(s, cpu_F0d, addr, get_mem_index(s)); } else { - gen_aa32_st32(cpu_F0s, addr, get_mem_index(s)); + gen_aa32_st32(s, cpu_F0s, addr, get_mem_index(s)); } } @@ -1632,24 +1640,24 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) if (insn & ARM_CP_RW_BIT) { if ((insn >> 28) == 0xf) { /* WLDRW wCx */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); iwmmxt_store_creg(wrd, tmp); } else { i = 1; if (insn & (1 << 8)) { if (insn & (1 << 22)) { /* WLDRD */ - gen_aa32_ld64(cpu_M0, addr, get_mem_index(s)); + gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); i = 0; } else { /* WLDRW wRd */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); } } else { tmp = tcg_temp_new_i32(); if (insn & (1 << 22)) { /* WLDRH */ - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); } else { /* WLDRB */ - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); } } if (i) { @@ -1661,24 +1669,24 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } else { if ((insn >> 28) == 0xf) { /* WSTRW wCx */ tmp = iwmmxt_load_creg(wrd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } else { gen_op_iwmmxt_movq_M0_wRn(wrd); tmp = tcg_temp_new_i32(); if (insn & (1 << 8)) { if (insn & (1 << 22)) { /* WSTRD */ - gen_aa32_st64(cpu_M0, addr, get_mem_index(s)); + gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); } else { /* WSTRW wRd */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } } else { if (insn & (1 << 22)) { /* WSTRH */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); } else { /* WSTRB */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); } } } @@ -2743,15 +2751,15 @@ static TCGv_i32 gen_load_and_replicate(DisasContext *s, TCGv_i32 addr, int size) TCGv_i32 tmp = tcg_temp_new_i32(); switch (size) { case 0: - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); gen_neon_dup_u8(tmp, 0); break; case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); gen_neon_dup_low16(tmp); break; case 2: - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; default: /* Avoid compiler warnings. */ abort(); @@ -4449,11 +4457,11 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) if (size == 3) { tmp64 = tcg_temp_new_i64(); if (load) { - gen_aa32_ld64(tmp64, addr, get_mem_index(s)); + gen_aa32_ld64(s, tmp64, addr, get_mem_index(s)); neon_store_reg64(tmp64, rd); } else { neon_load_reg64(tmp64, rd); - gen_aa32_st64(tmp64, addr, get_mem_index(s)); + gen_aa32_st64(s, tmp64, addr, get_mem_index(s)); } tcg_temp_free_i64(tmp64); tcg_gen_addi_i32(addr, addr, stride); @@ -4462,21 +4470,21 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) if (size == 2) { if (load) { tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); neon_store_reg(rd, pass, tmp); } else { tmp = neon_load_reg(rd, pass); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, stride); } else if (size == 1) { if (load) { tmp = tcg_temp_new_i32(); - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, stride); tmp2 = tcg_temp_new_i32(); - gen_aa32_ld16u(tmp2, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp2, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, stride); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp, tmp, tmp2); @@ -4486,10 +4494,10 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) tmp = neon_load_reg(rd, pass); tmp2 = tcg_temp_new_i32(); tcg_gen_shri_i32(tmp2, tmp, 16); - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, stride); - gen_aa32_st16(tmp2, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp2, addr, get_mem_index(s)); tcg_temp_free_i32(tmp2); tcg_gen_addi_i32(addr, addr, stride); } @@ -4498,7 +4506,7 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) TCGV_UNUSED_I32(tmp2); for (n = 0; n < 4; n++) { tmp = tcg_temp_new_i32(); - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, stride); if (n == 0) { tmp2 = tmp; @@ -4518,7 +4526,7 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) } else { tcg_gen_shri_i32(tmp, tmp2, n * 8); } - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, stride); } @@ -4642,13 +4650,13 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) tmp = tcg_temp_new_i32(); switch (size) { case 0: - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 2: - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; default: /* Avoid compiler warnings. */ abort(); @@ -4666,13 +4674,13 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) tcg_gen_shri_i32(tmp, tmp, shift); switch (size) { case 0: - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); break; case 2: - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); break; } tcg_temp_free_i32(tmp); @@ -7435,14 +7443,14 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, switch (size) { case 0: - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_ld16ua(tmp, addr, get_mem_index(s)); + gen_aa32_ld16ua(s, tmp, addr, get_mem_index(s)); break; case 2: case 3: - gen_aa32_ld32ua(tmp, addr, get_mem_index(s)); + gen_aa32_ld32ua(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -7453,7 +7461,7 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, TCGv_i32 tmp3 = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp2, addr, 4); - gen_aa32_ld32u(tmp3, tmp2, get_mem_index(s)); + gen_aa32_ld32u(s, tmp3, tmp2, get_mem_index(s)); tcg_temp_free_i32(tmp2); tcg_gen_concat_i32_i64(cpu_exclusive_val, tmp, tmp3); store_reg(s, rt2, tmp3); @@ -7504,14 +7512,14 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tmp = tcg_temp_new_i32(); switch (size) { case 0: - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 2: case 3: - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -7522,7 +7530,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, TCGv_i32 tmp2 = tcg_temp_new_i32(); TCGv_i32 tmp3 = tcg_temp_new_i32(); tcg_gen_addi_i32(tmp2, addr, 4); - gen_aa32_ld32u(tmp3, tmp2, get_mem_index(s)); + gen_aa32_ld32u(s, tmp3, tmp2, get_mem_index(s)); tcg_temp_free_i32(tmp2); tcg_gen_concat_i32_i64(val64, tmp, tmp3); tcg_temp_free_i32(tmp3); @@ -7537,14 +7545,14 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tmp = load_reg(s, rt); switch (size) { case 0: - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); break; case 2: case 3: - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -7553,7 +7561,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, if (size == 3) { tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rt2); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_gen_movi_i32(cpu_R[rd], 0); @@ -7659,11 +7667,11 @@ static void gen_srs(DisasContext *s, } tcg_gen_addi_i32(addr, addr, offset); tmp = load_reg(s, 14); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tmp = load_cpu_field(spsr); tcg_gen_addi_i32(addr, addr, 4); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); if (writeback) { switch (amode) { @@ -7822,10 +7830,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tcg_gen_addi_i32(addr, addr, offset); /* Load PC into tmp and CPSR into tmp2. */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, 4); tmp2 = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp2, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp2, addr, get_mem_index(s)); if (insn & (1 << 21)) { /* Base writeback. */ switch (i) { @@ -8441,13 +8449,16 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = tcg_temp_new_i32(); switch (op1) { case 0: /* lda */ - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, + get_mem_index(s)); break; case 2: /* ldab */ - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, + get_mem_index(s)); break; case 3: /* ldah */ - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, + get_mem_index(s)); break; default: abort(); @@ -8458,13 +8469,16 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = load_reg(s, rm); switch (op1) { case 0: /* stl */ - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, + get_mem_index(s)); break; case 2: /* stlb */ - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, + get_mem_index(s)); break; case 3: /* stlh */ - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, + get_mem_index(s)); break; default: abort(); @@ -8519,11 +8533,11 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = load_reg(s, rm); tmp2 = tcg_temp_new_i32(); if (insn & (1 << 22)) { - gen_aa32_ld8u(tmp2, addr, get_mem_index(s)); - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp2, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); } else { - gen_aa32_ld32u(tmp2, addr, get_mem_index(s)); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp2, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } tcg_temp_free_i32(tmp); tcg_temp_free_i32(addr); @@ -8558,20 +8572,20 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (!load) { /* store */ tmp = load_reg(s, rd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rd + 1); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } else { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); rd++; } address_offset = -4; @@ -8580,20 +8594,20 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tmp = tcg_temp_new_i32(); switch (sh) { case 1: - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 2: - gen_aa32_ld8s(tmp, addr, get_mem_index(s)); + gen_aa32_ld8s(s, tmp, addr, get_mem_index(s)); break; default: case 3: - gen_aa32_ld16s(tmp, addr, get_mem_index(s)); + gen_aa32_ld16s(s, tmp, addr, get_mem_index(s)); break; } } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } /* Perform base writeback before the loaded value to @@ -8946,17 +8960,17 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) /* load */ tmp = tcg_temp_new_i32(); if (insn & (1 << 22)) { - gen_aa32_ld8u(tmp, tmp2, i); + gen_aa32_ld8u(s, tmp, tmp2, i); } else { - gen_aa32_ld32u(tmp, tmp2, i); + gen_aa32_ld32u(s, tmp, tmp2, i); } } else { /* store */ tmp = load_reg(s, rd); if (insn & (1 << 22)) { - gen_aa32_st8(tmp, tmp2, i); + gen_aa32_st8(s, tmp, tmp2, i); } else { - gen_aa32_st32(tmp, tmp2, i); + gen_aa32_st32(s, tmp, tmp2, i); } tcg_temp_free_i32(tmp); } @@ -9029,7 +9043,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (is_load) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); if (user) { tmp2 = tcg_const_i32(i); gen_helper_set_user_reg(cpu_env, tmp2, tmp); @@ -9056,7 +9070,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } else { tmp = load_reg(s, i); } - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } j++; @@ -9323,20 +9337,20 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw if (insn & (1 << 20)) { /* ldrd */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rs, tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* strd */ tmp = load_reg(s, rs); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } if (insn & (1 << 21)) { @@ -9374,11 +9388,11 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tcg_gen_add_i32(addr, addr, tmp); tcg_temp_free_i32(tmp); tmp = tcg_temp_new_i32(); - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); } else { /* tbb */ tcg_temp_free_i32(tmp); tmp = tcg_temp_new_i32(); - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); } tcg_temp_free_i32(addr); tcg_gen_shli_i32(tmp, tmp, 1); @@ -9415,13 +9429,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = tcg_temp_new_i32(); switch (op) { case 0: /* ldab */ - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 1: /* ldah */ - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 2: /* lda */ - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -9431,13 +9445,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: /* stlb */ - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); break; case 1: /* stlh */ - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); break; case 2: /* stl */ - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); break; default: abort(); @@ -9465,10 +9479,10 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tcg_gen_addi_i32(addr, addr, -8); /* Load PC into tmp and CPSR into tmp2. */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); tcg_gen_addi_i32(addr, addr, 4); tmp2 = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp2, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp2, addr, get_mem_index(s)); if (insn & (1 << 21)) { /* Base writeback. */ if (insn & (1 << 24)) { @@ -9507,7 +9521,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw if (insn & (1 << 20)) { /* Load. */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); if (i == 15) { gen_bx(s, tmp); } else if (i == rn) { @@ -9519,7 +9533,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } else { /* Store. */ tmp = load_reg(s, i); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, 4); @@ -10449,19 +10463,19 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = tcg_temp_new_i32(); switch (op) { case 0: - gen_aa32_ld8u(tmp, addr, memidx); + gen_aa32_ld8u(s, tmp, addr, memidx); break; case 4: - gen_aa32_ld8s(tmp, addr, memidx); + gen_aa32_ld8s(s, tmp, addr, memidx); break; case 1: - gen_aa32_ld16u(tmp, addr, memidx); + gen_aa32_ld16u(s, tmp, addr, memidx); break; case 5: - gen_aa32_ld16s(tmp, addr, memidx); + gen_aa32_ld16s(s, tmp, addr, memidx); break; case 2: - gen_aa32_ld32u(tmp, addr, memidx); + gen_aa32_ld32u(s, tmp, addr, memidx); break; default: tcg_temp_free_i32(tmp); @@ -10478,13 +10492,13 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp = load_reg(s, rs); switch (op) { case 0: - gen_aa32_st8(tmp, addr, memidx); + gen_aa32_st8(s, tmp, addr, memidx); break; case 1: - gen_aa32_st16(tmp, addr, memidx); + gen_aa32_st16(s, tmp, addr, memidx); break; case 2: - gen_aa32_st32(tmp, addr, memidx); + gen_aa32_st32(s, tmp, addr, memidx); break; default: tcg_temp_free_i32(tmp); @@ -10621,7 +10635,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) addr = tcg_temp_new_i32(); tcg_gen_movi_i32(addr, val); tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(addr); store_reg(s, rd, tmp); break; @@ -10824,28 +10838,28 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) switch (op) { case 0: /* str */ - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); break; case 1: /* strh */ - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); break; case 2: /* strb */ - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); break; case 3: /* ldrsb */ - gen_aa32_ld8s(tmp, addr, get_mem_index(s)); + gen_aa32_ld8s(s, tmp, addr, get_mem_index(s)); break; case 4: /* ldr */ - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); break; case 5: /* ldrh */ - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); break; case 6: /* ldrb */ - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 7: /* ldrsh */ - gen_aa32_ld16s(tmp, addr, get_mem_index(s)); + gen_aa32_ld16s(s, tmp, addr, get_mem_index(s)); break; } if (op >= 3) { /* load */ @@ -10867,12 +10881,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -10889,12 +10903,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld8u(tmp, addr, get_mem_index(s)); + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st8(tmp, addr, get_mem_index(s)); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -10911,12 +10925,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld16u(tmp, addr, get_mem_index(s)); + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st16(tmp, addr, get_mem_index(s)); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -10932,12 +10946,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); @@ -11005,12 +11019,12 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* pop */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, i, tmp); } else { /* push */ tmp = load_reg(s, i); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } /* advance to the next address. */ @@ -11022,13 +11036,13 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* pop pc */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); /* don't set the pc until the rest of the instruction has completed */ } else { /* push lr */ tmp = load_reg(s, 14); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, 4); @@ -11158,7 +11172,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(tmp, addr, get_mem_index(s)); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); if (i == rn) { loaded_var = tmp; } else { @@ -11167,7 +11181,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } else { /* store */ tmp = load_reg(s, i); - gen_aa32_st32(tmp, addr, get_mem_index(s)); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } /* advance to the next address */ From dacf0a2ff7d39ab12bd90f2f5496a3889facd54a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 Mar 2016 11:30:20 +0000 Subject: [PATCH 20/30] target-arm: introduce disas flag for endianness Introduce a disas flag for setting the CPU data endianness. This allows control of the endianness from the CPU state rather than hard-coding it to TARGET_WORDS_BIGENDIAN. Signed-off-by: Paolo Bonzini [ PC changes: * Split off as new patch from original: "target-arm: introduce tbflag for CPSR.E" * Wrote commit message from scratch ] Reviewed-by: Peter Maydell Signed-off-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- target-arm/translate-a64.c | 1 + target-arm/translate.c | 39 +++++++++++++++++++++++--------------- target-arm/translate.h | 1 + 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index f6dd44b86f..88b95ab138 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -11032,6 +11032,7 @@ void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb) !arm_el_is_aa64(env, 3); dc->thumb = 0; dc->sctlr_b = 0; + dc->be_data = MO_TE; dc->condexec_mask = 0; dc->condexec_cond = 0; dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); diff --git a/target-arm/translate.c b/target-arm/translate.c index 41c3c7fcb6..2d4b1ccf1d 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -924,26 +924,30 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 addr, int index) \ { \ - tcg_gen_qemu_ld_i32(val, addr, index, (OPC)); \ + TCGMemOp opc = (OPC) | s->be_data; \ + tcg_gen_qemu_ld_i32(val, addr, index, opc); \ } #define DO_GEN_ST(SUFF, OPC) \ static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 addr, int index) \ { \ - tcg_gen_qemu_st_i32(val, addr, index, (OPC)); \ + TCGMemOp opc = (OPC) | s->be_data; \ + tcg_gen_qemu_st_i32(val, addr, index, opc); \ } static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, TCGv_i32 addr, int index) { - tcg_gen_qemu_ld_i64(val, addr, index, MO_TEQ); + TCGMemOp opc = MO_Q | s->be_data; + tcg_gen_qemu_ld_i64(val, addr, index, opc); } static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, TCGv_i32 addr, int index) { - tcg_gen_qemu_st_i64(val, addr, index, MO_TEQ); + TCGMemOp opc = MO_Q | s->be_data; + tcg_gen_qemu_st_i64(val, addr, index, opc); } #else @@ -952,9 +956,10 @@ static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 addr, int index) \ { \ + TCGMemOp opc = (OPC) | s->be_data; \ TCGv addr64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ - tcg_gen_qemu_ld_i32(val, addr64, index, OPC); \ + tcg_gen_qemu_ld_i32(val, addr64, index, opc); \ tcg_temp_free(addr64); \ } @@ -962,27 +967,30 @@ static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 addr, int index) \ { \ + TCGMemOp opc = (OPC) | s->be_data; \ TCGv addr64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ - tcg_gen_qemu_st_i32(val, addr64, index, OPC); \ + tcg_gen_qemu_st_i32(val, addr64, index, opc); \ tcg_temp_free(addr64); \ } static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, TCGv_i32 addr, int index) { + TCGMemOp opc = MO_Q | s->be_data; TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); - tcg_gen_qemu_ld_i64(val, addr64, index, MO_TEQ); + tcg_gen_qemu_ld_i64(val, addr64, index, opc); tcg_temp_free(addr64); } static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, TCGv_i32 addr, int index) { + TCGMemOp opc = MO_Q | s->be_data; TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); - tcg_gen_qemu_st_i64(val, addr64, index, MO_TEQ); + tcg_gen_qemu_st_i64(val, addr64, index, opc); tcg_temp_free(addr64); } @@ -990,15 +998,15 @@ static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, DO_GEN_LD(8s, MO_SB) DO_GEN_LD(8u, MO_UB) -DO_GEN_LD(16s, MO_TESW) -DO_GEN_LD(16u, MO_TEUW) -DO_GEN_LD(32u, MO_TEUL) +DO_GEN_LD(16s, MO_SW) +DO_GEN_LD(16u, MO_UW) +DO_GEN_LD(32u, MO_UL) /* 'a' variants include an alignment check */ -DO_GEN_LD(16ua, MO_TEUW | MO_ALIGN) -DO_GEN_LD(32ua, MO_TEUL | MO_ALIGN) +DO_GEN_LD(16ua, MO_UW | MO_ALIGN) +DO_GEN_LD(32ua, MO_UL | MO_ALIGN) DO_GEN_ST(8, MO_UB) -DO_GEN_ST(16, MO_TEUW) -DO_GEN_ST(32, MO_TEUL) +DO_GEN_ST(16, MO_UW) +DO_GEN_ST(32, MO_UL) static inline void gen_set_pc_im(DisasContext *s, target_ulong val) { @@ -11322,6 +11330,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) !arm_el_is_aa64(env, 3); dc->thumb = ARM_TBFLAG_THUMB(tb->flags); dc->sctlr_b = ARM_TBFLAG_SCTLR_B(tb->flags); + dc->be_data = MO_TE; dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); diff --git a/target-arm/translate.h b/target-arm/translate.h index 54452322c9..6a18d7badc 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -17,6 +17,7 @@ typedef struct DisasContext { int singlestep_enabled; int thumb; int sctlr_b; + TCGMemOp be_data; #if !defined(CONFIG_USER_ONLY) int user; #endif From aa6489da4e297fb3ffcbc09b50afd700395b6386 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:20 +0000 Subject: [PATCH 21/30] target-arm: a64: Add endianness support Set the dc->mo_endianness flag for AA64 and use it in all ldst ops. Reviewed-by: Peter Maydell Signed-off-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- target-arm/translate-a64.c | 49 +++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 88b95ab138..539e6d9c69 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -723,7 +723,7 @@ static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source, TCGv_i64 tcg_addr, int size, int memidx) { g_assert(size <= 3); - tcg_gen_qemu_st_i64(source, tcg_addr, memidx, MO_TE + size); + tcg_gen_qemu_st_i64(source, tcg_addr, memidx, s->be_data + size); } static void do_gpr_st(DisasContext *s, TCGv_i64 source, @@ -738,7 +738,7 @@ static void do_gpr_st(DisasContext *s, TCGv_i64 source, static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, int size, bool is_signed, bool extend, int memidx) { - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; g_assert(size <= 3); @@ -770,13 +770,18 @@ static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size) TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_ld_i64(tmp, cpu_env, fp_reg_offset(s, srcidx, MO_64)); if (size < 4) { - tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TE + size); + tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), + s->be_data + size); } else { + bool be = s->be_data == MO_BE; TCGv_i64 tcg_hiaddr = tcg_temp_new_i64(); - tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TEQ); - tcg_gen_ld_i64(tmp, cpu_env, fp_reg_hi_offset(s, srcidx)); + tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); - tcg_gen_qemu_st_i64(tmp, tcg_hiaddr, get_mem_index(s), MO_TEQ); + tcg_gen_qemu_st_i64(tmp, be ? tcg_hiaddr : tcg_addr, get_mem_index(s), + s->be_data | MO_Q); + tcg_gen_ld_i64(tmp, cpu_env, fp_reg_hi_offset(s, srcidx)); + tcg_gen_qemu_st_i64(tmp, be ? tcg_addr : tcg_hiaddr, get_mem_index(s), + s->be_data | MO_Q); tcg_temp_free_i64(tcg_hiaddr); } @@ -793,17 +798,21 @@ static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size) TCGv_i64 tmphi; if (size < 4) { - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; tmphi = tcg_const_i64(0); tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), memop); } else { + bool be = s->be_data == MO_BE; TCGv_i64 tcg_hiaddr; + tmphi = tcg_temp_new_i64(); tcg_hiaddr = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), MO_TEQ); tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8); - tcg_gen_qemu_ld_i64(tmphi, tcg_hiaddr, get_mem_index(s), MO_TEQ); + tcg_gen_qemu_ld_i64(tmplo, be ? tcg_hiaddr : tcg_addr, get_mem_index(s), + s->be_data | MO_Q); + tcg_gen_qemu_ld_i64(tmphi, be ? tcg_addr : tcg_hiaddr, get_mem_index(s), + s->be_data | MO_Q); tcg_temp_free_i64(tcg_hiaddr); } @@ -942,7 +951,7 @@ static void clear_vec_high(DisasContext *s, int rd) static void do_vec_st(DisasContext *s, int srcidx, int element, TCGv_i64 tcg_addr, int size) { - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; TCGv_i64 tcg_tmp = tcg_temp_new_i64(); read_vec_element(s, tcg_tmp, srcidx, element, size); @@ -955,7 +964,7 @@ static void do_vec_st(DisasContext *s, int srcidx, int element, static void do_vec_ld(DisasContext *s, int destidx, int element, TCGv_i64 tcg_addr, int size) { - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; TCGv_i64 tcg_tmp = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, get_mem_index(s), memop); @@ -1702,7 +1711,7 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, TCGv_i64 addr, int size, bool is_pair) { TCGv_i64 tmp = tcg_temp_new_i64(); - TCGMemOp memop = MO_TE + size; + TCGMemOp memop = s->be_data + size; g_assert(size <= 3); tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), memop); @@ -1764,7 +1773,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label); tmp = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), MO_TE + size); + tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), s->be_data + size); tcg_gen_brcond_i64(TCG_COND_NE, tmp, cpu_exclusive_val, fail_label); tcg_temp_free_i64(tmp); @@ -1773,7 +1782,8 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, TCGv_i64 tmphi = tcg_temp_new_i64(); tcg_gen_addi_i64(addrhi, addr, 1 << size); - tcg_gen_qemu_ld_i64(tmphi, addrhi, get_mem_index(s), MO_TE + size); + tcg_gen_qemu_ld_i64(tmphi, addrhi, get_mem_index(s), + s->be_data + size); tcg_gen_brcond_i64(TCG_COND_NE, tmphi, cpu_exclusive_high, fail_label); tcg_temp_free_i64(tmphi); @@ -1781,13 +1791,14 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, } /* We seem to still have the exclusive monitor, so do the store */ - tcg_gen_qemu_st_i64(cpu_reg(s, rt), addr, get_mem_index(s), MO_TE + size); + tcg_gen_qemu_st_i64(cpu_reg(s, rt), addr, get_mem_index(s), + s->be_data + size); if (is_pair) { TCGv_i64 addrhi = tcg_temp_new_i64(); tcg_gen_addi_i64(addrhi, addr, 1 << size); tcg_gen_qemu_st_i64(cpu_reg(s, rt2), addrhi, - get_mem_index(s), MO_TE + size); + get_mem_index(s), s->be_data + size); tcg_temp_free_i64(addrhi); } @@ -2602,7 +2613,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) TCGv_i64 tcg_tmp = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, - get_mem_index(s), MO_TE + scale); + get_mem_index(s), s->be_data + scale); switch (scale) { case 0: mulconst = 0x0101010101010101ULL; @@ -2632,9 +2643,9 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) } else { /* Load/store one element per register */ if (is_load) { - do_vec_ld(s, rt, index, tcg_addr, MO_TE + scale); + do_vec_ld(s, rt, index, tcg_addr, s->be_data + scale); } else { - do_vec_st(s, rt, index, tcg_addr, MO_TE + scale); + do_vec_st(s, rt, index, tcg_addr, s->be_data + scale); } } tcg_gen_addi_i64(tcg_addr, tcg_addr, ebytes); From 91cca2cda9823b1e7a049cb308a05104b5076cba Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:20 +0000 Subject: [PATCH 22/30] target-arm: introduce tbflag for endianness Introduce a tbflags for endianness, set based upon the CPUs current endianness. This in turn propagates through to the disas endianness flag. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/cpu.h | 7 +++++++ target-arm/translate-a64.c | 2 +- target-arm/translate.c | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index cbf171cc23..279c91fefd 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -1985,6 +1985,8 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) */ #define ARM_TBFLAG_NS_SHIFT 19 #define ARM_TBFLAG_NS_MASK (1 << ARM_TBFLAG_NS_SHIFT) +#define ARM_TBFLAG_BE_DATA_SHIFT 20 +#define ARM_TBFLAG_BE_DATA_MASK (1 << ARM_TBFLAG_BE_DATA_SHIFT) /* Bit usage when in AArch64 state: currently we have no A64 specific bits */ @@ -2015,6 +2017,8 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) (((F) & ARM_TBFLAG_XSCALE_CPAR_MASK) >> ARM_TBFLAG_XSCALE_CPAR_SHIFT) #define ARM_TBFLAG_NS(F) \ (((F) & ARM_TBFLAG_NS_MASK) >> ARM_TBFLAG_NS_SHIFT) +#define ARM_TBFLAG_BE_DATA(F) \ + (((F) & ARM_TBFLAG_BE_DATA_MASK) >> ARM_TBFLAG_BE_DATA_SHIFT) static inline bool bswap_code(bool sctlr_b) { @@ -2157,6 +2161,9 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, } } } + if (arm_cpu_data_is_big_endian(env)) { + *flags |= ARM_TBFLAG_BE_DATA_MASK; + } *flags |= fp_exception_el(env) << ARM_TBFLAG_FPEXC_EL_SHIFT; *cs_base = 0; diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 539e6d9c69..f0c73df5b0 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -11043,7 +11043,7 @@ void gen_intermediate_code_a64(ARMCPU *cpu, TranslationBlock *tb) !arm_el_is_aa64(env, 3); dc->thumb = 0; dc->sctlr_b = 0; - dc->be_data = MO_TE; + dc->be_data = ARM_TBFLAG_BE_DATA(tb->flags) ? MO_BE : MO_LE; dc->condexec_mask = 0; dc->condexec_cond = 0; dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); diff --git a/target-arm/translate.c b/target-arm/translate.c index 2d4b1ccf1d..c430fec7ad 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -11330,7 +11330,7 @@ void gen_intermediate_code(CPUARMState *env, TranslationBlock *tb) !arm_el_is_aa64(env, 3); dc->thumb = ARM_TBFLAG_THUMB(tb->flags); dc->sctlr_b = ARM_TBFLAG_SCTLR_B(tb->flags); - dc->be_data = MO_TE; + dc->be_data = ARM_TBFLAG_BE_DATA(tb->flags) ? MO_BE : MO_LE; dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; dc->mmu_idx = ARM_TBFLAG_MMUIDX(tb->flags); From 9886ecdf31165de2d4b8bccc1a220bd6ac8bc192 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 Mar 2016 11:30:21 +0000 Subject: [PATCH 23/30] target-arm: implement setend Since this is not a high-performance path, just use a helper to flip the E bit and force a lookup in the hash table since the flags have changed. Signed-off-by: Paolo Bonzini Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/helper.h | 1 + target-arm/op_helper.c | 5 +++++ target-arm/translate.c | 14 ++++++-------- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/target-arm/helper.h b/target-arm/helper.h index ea13202b17..e3d09d93bd 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -48,6 +48,7 @@ DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) DEF_HELPER_2(exception_internal, void, env, i32) DEF_HELPER_4(exception_with_syndrome, void, env, i32, i32, i32) +DEF_HELPER_1(setend, void, env) DEF_HELPER_1(wfi, void, env) DEF_HELPER_1(wfe, void, env) DEF_HELPER_1(yield, void, env) diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 4881e34177..92fde0a68c 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -296,6 +296,11 @@ uint32_t HELPER(usat16)(CPUARMState *env, uint32_t x, uint32_t shift) return res; } +void HELPER(setend)(CPUARMState *env) +{ + env->uncached_cpsr ^= CPSR_E; +} + /* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped. * The function returns the target EL (1-3) if the instruction is to be trapped; * otherwise it returns 0 indicating it is not trapped. diff --git a/target-arm/translate.c b/target-arm/translate.c index c430fec7ad..c23ddb3bf2 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7786,10 +7786,9 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if ((insn & 0x0ffffdff) == 0x01010000) { ARCH(6); /* setend */ - if (((insn >> 9) & 1) != bswap_code(s->sctlr_b)) { - /* Dynamic endianness switching not implemented. */ - qemu_log_mask(LOG_UNIMP, "arm: unimplemented setend\n"); - goto illegal_op; + if (((insn >> 9) & 1) != !!(s->be_data == MO_BE)) { + gen_helper_setend(cpu_env); + s->is_jmp = DISAS_UPDATE; } return; } else if ((insn & 0x0fffff00) == 0x057ff000) { @@ -11121,10 +11120,9 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) case 2: /* setend */ ARCH(6); - if (((insn >> 3) & 1) != bswap_code(s->sctlr_b)) { - /* Dynamic endianness switching not implemented. */ - qemu_log_mask(LOG_UNIMP, "arm: unimplemented setend\n"); - goto illegal_op; + if (((insn >> 3) & 1) != !!(s->be_data == MO_BE)) { + gen_helper_setend(cpu_env); + s->is_jmp = DISAS_UPDATE; } break; case 3: From e334bd3190f6c4ca12f1d40d316dc471c70009ab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 Mar 2016 11:30:21 +0000 Subject: [PATCH 24/30] target-arm: implement BE32 mode in system emulation System emulation only has a little-endian target; BE32 mode is implemented by adjusting the low bits of the address for every byte and halfword load and store. 64-bit accesses flip the low and high words. Signed-off-by: Paolo Bonzini [PC changes: * rebased against master (Jan 2016) ] Reviewed-by: Peter Maydell Signed-off-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- target-arm/cpu.h | 5 +-- target-arm/translate.c | 86 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 18 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 279c91fefd..066ff678dc 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -2033,9 +2033,8 @@ static inline bool bswap_code(bool sctlr_b) #endif sctlr_b; #else - /* We do not implement BE32 mode for system-mode emulation, but - * anyway it would always do little-endian accesses with - * TARGET_WORDS_BIGENDIAN = 0. + /* All code access in ARM is little endian, and there are no loaders + * doing swaps that need to be reversed */ return 0; #endif diff --git a/target-arm/translate.c b/target-arm/translate.c index c23ddb3bf2..25db09e9f5 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -911,6 +911,12 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) } } +#ifdef CONFIG_USER_ONLY +#define IS_USER_ONLY 1 +#else +#define IS_USER_ONLY 0 +#endif + /* Abstractions of "generate code to do a guest load/store for * AArch32", where a vaddr is always 32 bits (and is zero * extended if we're a 64 bit core) and data is also @@ -920,19 +926,35 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) */ #if TARGET_LONG_BITS == 32 -#define DO_GEN_LD(SUFF, OPC) \ +#define DO_GEN_LD(SUFF, OPC, BE32_XOR) \ static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 addr, int index) \ { \ TCGMemOp opc = (OPC) | s->be_data; \ + /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ + if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ + TCGv addr_be = tcg_temp_new(); \ + tcg_gen_xori_i32(addr_be, addr, BE32_XOR); \ + tcg_gen_qemu_ld_i32(val, addr_be, index, opc); \ + tcg_temp_free(addr_be); \ + return; \ + } \ tcg_gen_qemu_ld_i32(val, addr, index, opc); \ } -#define DO_GEN_ST(SUFF, OPC) \ +#define DO_GEN_ST(SUFF, OPC, BE32_XOR) \ static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 addr, int index) \ { \ TCGMemOp opc = (OPC) | s->be_data; \ + /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ + if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ + TCGv addr_be = tcg_temp_new(); \ + tcg_gen_xori_i32(addr_be, addr, BE32_XOR); \ + tcg_gen_qemu_st_i32(val, addr_be, index, opc); \ + tcg_temp_free(addr_be); \ + return; \ + } \ tcg_gen_qemu_st_i32(val, addr, index, opc); \ } @@ -941,35 +963,55 @@ static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, { TCGMemOp opc = MO_Q | s->be_data; tcg_gen_qemu_ld_i64(val, addr, index, opc); + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b) { + tcg_gen_rotri_i64(val, val, 32); + } } static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, TCGv_i32 addr, int index) { TCGMemOp opc = MO_Q | s->be_data; + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_rotri_i64(tmp, val, 32); + tcg_gen_qemu_st_i64(tmp, addr, index, opc); + tcg_temp_free_i64(tmp); + return; + } tcg_gen_qemu_st_i64(val, addr, index, opc); } #else -#define DO_GEN_LD(SUFF, OPC) \ +#define DO_GEN_LD(SUFF, OPC, BE32_XOR) \ static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 addr, int index) \ { \ TCGMemOp opc = (OPC) | s->be_data; \ TCGv addr64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ + /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ + if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ + tcg_gen_xori_i64(addr64, addr64, BE32_XOR); \ + } \ tcg_gen_qemu_ld_i32(val, addr64, index, opc); \ tcg_temp_free(addr64); \ } -#define DO_GEN_ST(SUFF, OPC) \ +#define DO_GEN_ST(SUFF, OPC, BE32_XOR) \ static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ TCGv_i32 addr, int index) \ { \ TCGMemOp opc = (OPC) | s->be_data; \ TCGv addr64 = tcg_temp_new(); \ tcg_gen_extu_i32_i64(addr64, addr); \ + /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ + if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ + tcg_gen_xori_i64(addr64, addr64, BE32_XOR); \ + } \ tcg_gen_qemu_st_i32(val, addr64, index, opc); \ tcg_temp_free(addr64); \ } @@ -981,6 +1023,11 @@ static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); tcg_gen_qemu_ld_i64(val, addr64, index, opc); + + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b) { + tcg_gen_rotri_i64(val, val, 32); + } tcg_temp_free(addr64); } @@ -990,23 +1037,32 @@ static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, TCGMemOp opc = MO_Q | s->be_data; TCGv addr64 = tcg_temp_new(); tcg_gen_extu_i32_i64(addr64, addr); - tcg_gen_qemu_st_i64(val, addr64, index, opc); + + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b) { + TCGv tmp = tcg_temp_new(); + tcg_gen_rotri_i64(tmp, val, 32); + tcg_gen_qemu_st_i64(tmp, addr64, index, opc); + tcg_temp_free(tmp); + } else { + tcg_gen_qemu_st_i64(val, addr64, index, opc); + } tcg_temp_free(addr64); } #endif -DO_GEN_LD(8s, MO_SB) -DO_GEN_LD(8u, MO_UB) -DO_GEN_LD(16s, MO_SW) -DO_GEN_LD(16u, MO_UW) -DO_GEN_LD(32u, MO_UL) +DO_GEN_LD(8s, MO_SB, 3) +DO_GEN_LD(8u, MO_UB, 3) +DO_GEN_LD(16s, MO_SW, 2) +DO_GEN_LD(16u, MO_UW, 2) +DO_GEN_LD(32u, MO_UL, 0) /* 'a' variants include an alignment check */ -DO_GEN_LD(16ua, MO_UW | MO_ALIGN) -DO_GEN_LD(32ua, MO_UL | MO_ALIGN) -DO_GEN_ST(8, MO_UB) -DO_GEN_ST(16, MO_UW) -DO_GEN_ST(32, MO_UL) +DO_GEN_LD(16ua, MO_UW | MO_ALIGN, 2) +DO_GEN_LD(32ua, MO_UL | MO_ALIGN, 0) +DO_GEN_ST(8, MO_UB, 3) +DO_GEN_ST(16, MO_UW, 2) +DO_GEN_ST(32, MO_UL, 0) static inline void gen_set_pc_im(DisasContext *s, target_ulong val) { From 04ae712a9f31fe21eacfcaeb689c7e6d0a7e251c Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:21 +0000 Subject: [PATCH 25/30] loader: add API to load elf header Add an API to load an elf header header from a file. Populates a buffer with the header contents, as well as a boolean for whether the elf is 64b or not. Both arguments are optional. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell [PMM: Fix typo in comment] Signed-off-by: Peter Maydell --- hw/core/loader.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/loader.h | 13 +++++++++++ 2 files changed, 68 insertions(+) diff --git a/hw/core/loader.c b/hw/core/loader.c index 260b3d62cb..125aa05de6 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -354,6 +354,61 @@ const char *load_elf_strerror(int error) } } +void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp) +{ + int fd; + uint8_t e_ident_local[EI_NIDENT]; + uint8_t *e_ident; + size_t hdr_size, off; + bool is64l; + + if (!hdr) { + hdr = e_ident_local; + } + e_ident = hdr; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) { + error_setg_errno(errp, errno, "Failed to open file: %s", filename); + return; + } + if (read(fd, hdr, EI_NIDENT) != EI_NIDENT) { + error_setg_errno(errp, errno, "Failed to read file: %s", filename); + goto fail; + } + if (e_ident[0] != ELFMAG0 || + e_ident[1] != ELFMAG1 || + e_ident[2] != ELFMAG2 || + e_ident[3] != ELFMAG3) { + error_setg(errp, "Bad ELF magic"); + goto fail; + } + + is64l = e_ident[EI_CLASS] == ELFCLASS64; + hdr_size = is64l ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr); + if (is64) { + *is64 = is64l; + } + + off = EI_NIDENT; + while (hdr != e_ident_local && off < hdr_size) { + size_t br = read(fd, hdr + off, hdr_size - off); + switch (br) { + case 0: + error_setg(errp, "File too short: %s", filename); + goto fail; + case -1: + error_setg_errno(errp, errno, "Failed to read file: %s", + filename); + goto fail; + } + off += br; + } + +fail: + close(fd); +} + /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, diff --git a/include/hw/loader.h b/include/hw/loader.h index 09c3764875..358da55eb6 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -48,6 +48,19 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb); + +/** load_elf_hdr: + * @filename: Path of ELF file + * @hdr: Buffer to populate with header data. Header data will not be + * filled if set to NULL. + * @is64: Set to true if the ELF is 64bit. Ignored if set to NULL + * @errp: Populated with an error in failure cases + * + * Inspect an ELF file's header. Read its full header contents into a + * buffer and/or determine if the ELF is 64bit. + */ +void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp); + int load_aout(const char *filename, hwaddr addr, int max_sz, int bswap_needed, hwaddr target_page_size); int load_uimage(const char *filename, hwaddr *ep, From 140b7ce5fff6fc660b2484dfd9d33ed2e42ec5a9 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:21 +0000 Subject: [PATCH 26/30] loader: load_elf(): Add doc comment Document the usage of load_elf() for clarity on current features. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- include/hw/loader.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/hw/loader.h b/include/hw/loader.h index 358da55eb6..5485906095 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -44,6 +44,29 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); #define ELF_LOAD_WRONG_ARCH -3 #define ELF_LOAD_WRONG_ENDIAN -4 const char *load_elf_strerror(int error); + +/** load_elf: + * @filename: Path of ELF file + * @translate_fn: optional function to translate load addresses + * @translate_opaque: opaque data passed to @translate_fn + * @pentry: Populated with program entry point. Ignored if NULL. + * @lowaddr: Populated with lowest loaded address. Ignored if NULL. + * @highaddr: Populated with highest loaded address. Ignored if NULL. + * @bigendian: Expected ELF endianness. 0 for LE otherwise BE + * @elf_machine: Expected ELF machine type + * @clear_lsb: Set to mask off LSB of addresses (Some architectures use + * this for non-address data) + * + * Load an ELF file's contents to the emulated system's address space. + * Clients may optionally specify a callback to perform address + * translations. @pentry, @lowaddr and @highaddr are optional pointers + * which will be populated with various load information. @bigendian and + * @elf_machine give the expected endianness and machine for the ELF the + * load will fail if the target ELF does not match. Some architectures + * have some architecture-specific behaviours that come into effect when + * their particular values for @elf_machine are set. + */ + int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, int elf_machine, From 7ef295ea5b412cbaf82f719ccd49efb51296e841 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:21 +0000 Subject: [PATCH 27/30] loader: Add data swap option to load-elf Some CPUs are of an opposite data-endianness to other components in the system. Sometimes elfs have the data sections layed out with this CPU data-endianness accounting for when loaded via the CPU, so byte swaps (relative to other system components) will occur. The leading example, is ARM's BE32 mode, which is is basically LE with address manipulation on half-word and byte accesses to access the hw/byte reversed address. This means that word data is invariant across LE and BE32. This also means that instructions are still LE. The expectation is that the elf will be loaded via the CPU in this endianness scheme, which means the data in the elf is reversed at compile time. As QEMU loads via the system memory directly, rather than the CPU, we need a mechanism to reverse elf data endianness to implement this possibility. Reviewed-by: Peter Maydell Signed-off-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- hw/alpha/dp264.c | 4 ++-- hw/arm/armv7m.c | 2 +- hw/arm/boot.c | 2 +- hw/core/loader.c | 9 ++++++--- hw/cris/boot.c | 2 +- hw/i386/multiboot.c | 3 ++- hw/lm32/lm32_boards.c | 4 ++-- hw/lm32/milkymist.c | 2 +- hw/m68k/an5206.c | 2 +- hw/m68k/dummy_m68k.c | 2 +- hw/m68k/mcf5208.c | 2 +- hw/microblaze/boot.c | 4 ++-- hw/mips/mips_fulong2e.c | 2 +- hw/mips/mips_malta.c | 2 +- hw/mips/mips_mipssim.c | 2 +- hw/mips/mips_r4k.c | 2 +- hw/moxie/moxiesim.c | 3 ++- hw/openrisc/openrisc_sim.c | 3 ++- hw/pci-host/prep.c | 2 +- hw/ppc/e500.c | 2 +- hw/ppc/mac_newworld.c | 5 +++-- hw/ppc/mac_oldworld.c | 5 +++-- hw/ppc/ppc440_bamboo.c | 3 ++- hw/ppc/spapr.c | 6 ++++-- hw/ppc/virtex_ml507.c | 3 ++- hw/s390x/ipl.c | 4 ++-- hw/sparc/leon3.c | 2 +- hw/sparc/sun4m.c | 4 ++-- hw/sparc64/sun4u.c | 4 ++-- hw/tricore/tricore_testboard.c | 2 +- hw/xtensa/sim.c | 4 ++-- hw/xtensa/xtfpga.c | 2 +- include/hw/elf_ops.h | 22 +++++++++++++++++++++- include/hw/loader.h | 5 ++++- 34 files changed, 81 insertions(+), 46 deletions(-) diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 992d1b234d..7c5989bdc7 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -111,7 +111,7 @@ static void clipper_init(MachineState *machine) } size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys, NULL, &palcode_entry, &palcode_low, &palcode_high, - 0, EM_ALPHA, 0); + 0, EM_ALPHA, 0, 0); if (size < 0) { error_report("could not load palcode '%s'", palcode_filename); exit(1); @@ -131,7 +131,7 @@ static void clipper_init(MachineState *machine) size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys, NULL, &kernel_entry, &kernel_low, &kernel_high, - 0, EM_ALPHA, 0); + 0, EM_ALPHA, 0, 0); if (size < 0) { error_report("could not load kernel '%s'", kernel_filename); exit(1); diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index f3973f721a..ed7d97fc21 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -211,7 +211,7 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, if (kernel_filename) { image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, - NULL, big_endian, EM_ARM, 1); + NULL, big_endian, EM_ARM, 1, 0); if (image_size < 0) { image_size = load_image_targphys(kernel_filename, 0, mem_size); lowaddr = 0; diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 0a56d34cfe..17400beb58 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -755,7 +755,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) /* Assume that raw images are linux kernels, and ELF images are not. */ kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry, &elf_low_addr, &elf_high_addr, big_endian, - elf_machine, 1); + elf_machine, 1, 0); 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. diff --git a/hw/core/loader.c b/hw/core/loader.c index 125aa05de6..8e8031ca3c 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -412,7 +412,8 @@ fail: /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb) + uint64_t *highaddr, int big_endian, int elf_machine, + int clear_lsb, int data_swab) { int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; @@ -451,10 +452,12 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), lseek(fd, 0, SEEK_SET); if (e_ident[EI_CLASS] == ELFCLASS64) { ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab, - pentry, lowaddr, highaddr, elf_machine, clear_lsb); + pentry, lowaddr, highaddr, elf_machine, clear_lsb, + data_swab); } else { ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab, - pentry, lowaddr, highaddr, elf_machine, clear_lsb); + pentry, lowaddr, highaddr, elf_machine, clear_lsb, + data_swab); } fail: diff --git a/hw/cris/boot.c b/hw/cris/boot.c index 6608160e0d..42485a4ca0 100644 --- a/hw/cris/boot.c +++ b/hw/cris/boot.c @@ -73,7 +73,7 @@ void cris_load_image(CRISCPU *cpu, struct cris_load_info *li) /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis devboard SDK. */ image_size = load_elf(li->image_filename, translate_kernel_address, NULL, - &entry, NULL, &high, 0, EM_CRIS, 0); + &entry, NULL, &high, 0, EM_CRIS, 0, 0); li->entry = entry; if (image_size < 0) { /* Takes a kimage from the axis devboard SDK. */ diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index c4d7d8328f..9e164e65d9 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -196,7 +196,8 @@ int load_multiboot(FWCfgState *fw_cfg, } kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_low, &elf_high, 0, I386_ELF_MACHINE, 0); + &elf_low, &elf_high, 0, I386_ELF_MACHINE, + 0, 0); if (kernel_size < 0) { fprintf(stderr, "Error while loading elf kernel\n"); exit(1); diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index efa6f91fd2..c5a848b06c 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -143,7 +143,7 @@ static void lm32_evr_init(MachineState *machine) int kernel_size; kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1, EM_LATTICEMICO32, 0); + 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; if (kernel_size < 0) { @@ -245,7 +245,7 @@ static void lm32_uclinux_init(MachineState *machine) int kernel_size; kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1, EM_LATTICEMICO32, 0); + 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; if (kernel_size < 0) { diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index 5a37b4a1c1..f71492ef7e 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -177,7 +177,7 @@ milkymist_init(MachineState *machine) /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1, EM_LATTICEMICO32, 0); + 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; if (kernel_size < 0) { diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index d87b945f05..85f72770d7 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -73,7 +73,7 @@ static void an5206_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, 1, EM_68K, 0); + NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, diff --git a/hw/m68k/dummy_m68k.c b/hw/m68k/dummy_m68k.c index a213bcf307..3c2174b505 100644 --- a/hw/m68k/dummy_m68k.c +++ b/hw/m68k/dummy_m68k.c @@ -50,7 +50,7 @@ static void dummy_m68k_init(MachineState *machine) /* Load kernel. */ if (kernel_filename) { kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, 1, EM_68K, 0); + NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 9597e861ab..4f49d34a8f 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -276,7 +276,7 @@ static void mcf5208evb_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, 1, EM_68K, 0); + NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 26cc3786f4..c24014a1f3 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -142,12 +142,12 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, &low, &high, - big_endian, EM_MICROBLAZE, 0); + big_endian, EM_MICROBLAZE, 0, 0); base32 = entry; if (base32 == 0xc0000000) { kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, &entry, NULL, NULL, - big_endian, EM_MICROBLAZE, 0); + big_endian, EM_MICROBLAZE, 0, 0); } /* Always boot into physical ram. */ boot_info.bootstrap_pc = (uint32_t)entry; diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 184c404454..4e5581b167 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -117,7 +117,7 @@ static int64_t load_kernel (CPUMIPSState *env) if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low, - (uint64_t *)&kernel_high, 0, EM_MIPS, 1) < 0) { + (uint64_t *)&kernel_high, 0, EM_MIPS, 1, 0) < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", loaderparams.kernel_filename); exit(1); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index c04aa2b8cc..f5173c42de 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -796,7 +796,7 @@ static int64_t load_kernel (void) if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high, - big_endian, EM_MIPS, 1) < 0) { + big_endian, EM_MIPS, 1, 0) < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", loaderparams.kernel_filename); exit(1); diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c index 8951ae97d3..1ecff44a54 100644 --- a/hw/mips/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -70,7 +70,7 @@ static int64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&entry, NULL, (uint64_t *)&kernel_high, big_endian, - EM_MIPS, 1); + EM_MIPS, 1, 0); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) entry = (int32_t)entry; diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index b6625aeee4..724b1e9d51 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -88,7 +88,7 @@ static int64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&entry, NULL, (uint64_t *)&kernel_high, big_endian, - EM_MIPS, 1); + EM_MIPS, 1, 0); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) entry = (int32_t)entry; diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c index 9191ae9603..d88c9428e0 100644 --- a/hw/moxie/moxiesim.c +++ b/hw/moxie/moxiesim.c @@ -54,7 +54,8 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params) ram_addr_t initrd_offset; kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL, - &entry, &kernel_low, &kernel_high, 1, EM_MOXIE, 0); + &entry, &kernel_low, &kernel_high, 1, EM_MOXIE, + 0, 0); if (kernel_size <= 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 25c637aba7..46418c30f7 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -69,7 +69,8 @@ static void cpu_openrisc_load_kernel(ram_addr_t ram_size, if (kernel_filename && !qtest_enabled()) { kernel_size = load_elf(kernel_filename, NULL, NULL, - &elf_entry, NULL, NULL, 1, EM_OPENRISC, 1); + &elf_entry, NULL, NULL, 1, EM_OPENRISC, + 1, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 5dc550fe5e..49cdaab36b 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -313,7 +313,7 @@ static void raven_realize(PCIDevice *d, Error **errp) if (filename) { if (s->elf_machine != EM_NONE) { bios_size = load_elf(filename, NULL, NULL, NULL, - NULL, NULL, 1, s->elf_machine, 0); + NULL, NULL, 1, s->elf_machine, 0, 0); } if (bios_size < 0) { bios_size = get_image_size(filename); diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index bd84e9ac13..09154fa813 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -1017,7 +1017,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL, - 1, PPC_ELF_MACHINE, 0); + 1, PPC_ELF_MACHINE, 0, 0); if (bios_size < 0) { /* * Hrm. No ELF image? Try a uImage, maybe someone is giving us an diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index f95086b787..f0a36b3133 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -221,7 +221,7 @@ static void ppc_core99_init(MachineState *machine) /* Load OpenBIOS (ELF) */ if (filename) { bios_size = load_elf(filename, NULL, NULL, NULL, - NULL, NULL, 1, PPC_ELF_MACHINE, 0); + NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); } else { @@ -244,7 +244,8 @@ static void ppc_core99_init(MachineState *machine) kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, ram_size - kernel_base, bswap_needed, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 898439860c..d952713313 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -149,7 +149,7 @@ static void ppc_heathrow_init(MachineState *machine) /* Load OpenBIOS (ELF) */ if (filename) { bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL, - 1, PPC_ELF_MACHINE, 0); + 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); } else { bios_size = -1; @@ -170,7 +170,8 @@ static void ppc_heathrow_init(MachineState *machine) #endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, ram_size - kernel_base, bswap_needed, diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index e535a9f266..5c535b18a2 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -256,7 +256,8 @@ static void bamboo_init(MachineState *machine) NULL, NULL); if (success < 0) { success = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, 0); + &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); entry = elf_entry; loadaddr = elf_lowaddr; } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index e9d4abf06a..64c4acce06 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1942,11 +1942,13 @@ static void ppc_spapr_init(MachineState *machine) uint64_t lowaddr = 0; kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); if (kernel_size == ELF_LOAD_WRONG_ENDIAN) { kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE, + 0, 0); kernel_le = kernel_size > 0; } if (kernel_size < 0) { diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index a902c88277..b807a08c28 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -258,7 +258,8 @@ static void virtex_init(MachineState *machine) /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, - &entry, &low, &high, 1, PPC_ELF_MACHINE, 0); + &entry, &low, &high, 1, PPC_ELF_MACHINE, + 0, 0); boot_info.bootstrap_pc = entry & 0x00ffffff; if (kernel_size < 0) { diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index c9cf7cce64..41ff002069 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -101,7 +101,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) bios_size = load_elf(bios_filename, bios_translate_addr, &fwbase, &ipl->bios_start_addr, NULL, NULL, 1, - EM_S390, 0); + EM_S390, 0, 0); if (bios_size > 0) { /* Adjust ELF start address to final location */ ipl->bios_start_addr += fwbase; @@ -124,7 +124,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) if (ipl->kernel) { kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL, - NULL, 1, EM_S390, 0); + NULL, 1, EM_S390, 0, 0); if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); } diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 07c5c850ef..c579f5b9ea 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -194,7 +194,7 @@ static void leon3_generic_hw_init(MachineState *machine) uint64_t entry; kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1 /* big endian */, EM_SPARC, 0); + 1 /* big endian */, EM_SPARC, 0, 0); if (kernel_size < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 20dc341710..eebef37897 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -279,7 +279,7 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, bswap_needed = 0; #endif kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, NULL, NULL, 1, EM_SPARC, 0); + NULL, NULL, NULL, 1, EM_SPARC, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, RAM_size - KERNEL_LOAD_ADDR, bswap_needed, @@ -723,7 +723,7 @@ static void prom_init(hwaddr addr, const char *bios_name) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { ret = load_elf(filename, translate_prom_address, &addr, NULL, - NULL, NULL, 1, EM_SPARC, 0); + NULL, NULL, 1, EM_SPARC, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index add1e752f3..0a6f453858 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -187,7 +187,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, bswap_needed = 0; #endif kernel_size = load_elf(kernel_filename, NULL, NULL, kernel_entry, - kernel_addr, &kernel_top, 1, EM_SPARCV9, 0); + kernel_addr, &kernel_top, 1, EM_SPARCV9, 0, 0); if (kernel_size < 0) { *kernel_addr = KERNEL_LOAD_ADDR; *kernel_entry = KERNEL_LOAD_ADDR; @@ -633,7 +633,7 @@ static void prom_init(hwaddr addr, const char *bios_name) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { ret = load_elf(filename, translate_prom_address, &addr, - NULL, NULL, NULL, 1, EM_SPARCV9, 0); + NULL, NULL, NULL, 1, EM_SPARCV9, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index 9392571f1f..3cadb6521c 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -45,7 +45,7 @@ static void tricore_load_kernel(CPUTriCoreState *env) kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL, NULL, (uint64_t *)&entry, NULL, NULL, 0, - EM_TRICORE, 1); + EM_TRICORE, 1, 0); if (kernel_size <= 0) { error_report("qemu: no kernel file '%s'", tricoretb_binfo.kernel_filename); diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 3a5060b03b..23050e8fb7 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -94,10 +94,10 @@ static void xtensa_sim_init(MachineState *machine) uint64_t elf_lowaddr; #ifdef TARGET_WORDS_BIGENDIAN int success = load_elf(kernel_filename, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, 1, EM_XTENSA, 0); + &elf_entry, &elf_lowaddr, NULL, 1, EM_XTENSA, 0, 0); #else int success = load_elf(kernel_filename, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, 0, EM_XTENSA, 0); + &elf_entry, &elf_lowaddr, NULL, 0, EM_XTENSA, 0, 0); #endif if (success > 0) { env->pc = elf_entry; diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index fe7684d41c..ed09b9d809 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -355,7 +355,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) uint64_t elf_entry; uint64_t elf_lowaddr; int success = load_elf(kernel_filename, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, be, EM_XTENSA, 0); + &elf_entry, &elf_lowaddr, NULL, be, EM_XTENSA, 0, 0); if (success > 0) { entry_point = elf_entry; } else { diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index 0010c441d9..f510e7ec2a 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -263,7 +263,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, void *translate_opaque, int must_swab, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - int elf_machine, int clear_lsb) + int elf_machine, int clear_lsb, int data_swab) { struct elfhdr ehdr; struct elf_phdr *phdr = NULL, *ph; @@ -366,6 +366,26 @@ static int glue(load_elf, SZ)(const char *name, int fd, addr = ph->p_paddr; } + if (data_swab) { + int j; + for (j = 0; j < file_size; j += (1 << data_swab)) { + uint8_t *dp = data + j; + switch (data_swab) { + case (1): + *(uint16_t *)dp = bswap16(*(uint16_t *)dp); + break; + case (2): + *(uint32_t *)dp = bswap32(*(uint32_t *)dp); + break; + case (3): + *(uint64_t *)dp = bswap64(*(uint64_t *)dp); + break; + default: + g_assert_not_reached(); + } + } + } + /* the entry pointer in the ELF header is a virtual * address, if the text segments paddr and vaddr differ * we need to adjust the entry */ diff --git a/include/hw/loader.h b/include/hw/loader.h index 5485906095..0ba780852c 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -56,6 +56,9 @@ const char *load_elf_strerror(int error); * @elf_machine: Expected ELF machine type * @clear_lsb: Set to mask off LSB of addresses (Some architectures use * this for non-address data) + * @data_swab: Set to order of byte swapping for data. 0 for no swap, 1 + * for swapping bytes within halfwords, 2 for bytes within + * words and 3 for within doublewords. * * Load an ELF file's contents to the emulated system's address space. * Clients may optionally specify a callback to perform address @@ -70,7 +73,7 @@ const char *load_elf_strerror(int error); int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, int elf_machine, - int clear_lsb); + int clear_lsb, int data_swab); /** load_elf_hdr: * @filename: Path of ELF file From 9776f636455b6f0d9c14dce112242ed653f954b4 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Fri, 4 Mar 2016 11:30:21 +0000 Subject: [PATCH 28/30] arm: boot: Support big-endian elfs Support ARM big-endian ELF files in system-mode emulation. When loading an elf, determine the endianness mode expected by the elf, and set the relevant CPU state accordingly. With this, big-endian modes are now fully supported via system-mode LE, so there is no need to restrict the elf loading to the TARGET endianness so the ifdeffery on TARGET_WORDS_BIGENDIAN goes away. Signed-off-by: Peter Crosthwaite Reviewed-by: Peter Maydell [PMM: fix typo in comments] Signed-off-by: Peter Maydell --- hw/arm/boot.c | 93 +++++++++++++++++++++++++++++++++++++++----- include/hw/arm/arm.h | 9 +++++ 2 files changed, 92 insertions(+), 10 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 17400beb58..8ba0e4272a 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -518,9 +518,34 @@ static void do_cpu_reset(void *opaque) cpu_reset(cs); if (info) { if (!info->is_linux) { + int i; /* Jump to the entry point. */ uint64_t entry = info->entry; + switch (info->endianness) { + case ARM_ENDIANNESS_LE: + env->cp15.sctlr_el[1] &= ~SCTLR_E0E; + for (i = 1; i < 4; ++i) { + env->cp15.sctlr_el[i] &= ~SCTLR_EE; + } + env->uncached_cpsr &= ~CPSR_E; + break; + case ARM_ENDIANNESS_BE8: + env->cp15.sctlr_el[1] |= SCTLR_E0E; + for (i = 1; i < 4; ++i) { + env->cp15.sctlr_el[i] |= SCTLR_EE; + } + env->uncached_cpsr |= CPSR_E; + break; + case ARM_ENDIANNESS_BE32: + env->cp15.sctlr_el[1] |= SCTLR_B; + break; + case ARM_ENDIANNESS_UNKNOWN: + break; /* Board's decision */ + default: + g_assert_not_reached(); + } + if (!env->aarch64) { env->thumb = info->entry & 1; entry &= 0xfffffffe; @@ -638,6 +663,62 @@ static int do_arm_linux_init(Object *obj, void *opaque) return 0; } +static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + int elf_machine) +{ + bool elf_is64; + union { + Elf32_Ehdr h32; + Elf64_Ehdr h64; + } elf_header; + int data_swab = 0; + bool big_endian; + uint64_t ret = -1; + Error *err = NULL; + + + load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err); + if (err) { + return ret; + } + + if (elf_is64) { + big_endian = elf_header.h64.e_ident[EI_DATA] == ELFDATA2MSB; + info->endianness = big_endian ? ARM_ENDIANNESS_BE8 + : ARM_ENDIANNESS_LE; + } else { + big_endian = elf_header.h32.e_ident[EI_DATA] == ELFDATA2MSB; + if (big_endian) { + if (bswap32(elf_header.h32.e_flags) & EF_ARM_BE8) { + info->endianness = ARM_ENDIANNESS_BE8; + } else { + info->endianness = ARM_ENDIANNESS_BE32; + /* In BE32, the CPU has a different view of the per-byte + * address map than the rest of the system. BE32 ELF files + * are organised such that they can be programmed through + * the CPU's per-word byte-reversed view of the world. QEMU + * however loads ELF files independently of the CPU. So + * tell the ELF loader to byte reverse the data for us. + */ + data_swab = 2; + } + } else { + info->endianness = ARM_ENDIANNESS_LE; + } + } + + ret = load_elf(info->kernel_filename, NULL, NULL, + pentry, lowaddr, highaddr, big_endian, elf_machine, + 1, data_swab); + if (ret <= 0) { + /* The header loaded but the image didn't */ + exit(1); + } + + return ret; +} + static void arm_load_kernel_notify(Notifier *notifier, void *data) { CPUState *cs; @@ -647,7 +728,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) uint64_t elf_entry, elf_low_addr, elf_high_addr; int elf_machine; hwaddr entry, kernel_load_offset; - int big_endian; static const ARMInsnFixup *primary_loader; ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier, notifier, notifier); @@ -733,12 +813,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) if (info->nb_cpus == 0) info->nb_cpus = 1; -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - /* We want to put the initrd far enough into RAM that when the * kernel is uncompressed it will not clobber the initrd. However * on boards without much RAM we must ensure that we still leave @@ -753,9 +827,8 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) MIN(info->ram_size / 2, 128 * 1024 * 1024); /* Assume that raw images are linux kernels, and ELF images are not. */ - kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry, - &elf_low_addr, &elf_high_addr, big_endian, - elf_machine, 1, 0); + kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr, + &elf_high_addr, elf_machine); 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. diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index 52ecf4aa8f..b2517f9a43 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -16,6 +16,13 @@ #include "qemu/notify.h" #include "cpu.h" +typedef enum { + ARM_ENDIANNESS_UNKNOWN = 0, + ARM_ENDIANNESS_LE, + ARM_ENDIANNESS_BE8, + ARM_ENDIANNESS_BE32, +} arm_endianness; + /* armv7m.c */ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, const char *kernel_filename, const char *cpu_model); @@ -103,6 +110,8 @@ struct arm_boot_info { * changing to non-secure state if implementing a non-secure boot */ bool secure_board_setup; + + arm_endianness endianness; }; /** From a55c910e0b18aee2f67b129f0046b53cb8c42f21 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Mar 2016 11:30:22 +0000 Subject: [PATCH 29/30] hw/intc/arm_gic.c: Implement GICv2 GICC_DIR The GICv2 introduces a new CPU interface register GICC_DIR, which allows an OS to split the "priority drop" and "deactivate interrupt" parts of interrupt completion. Implement this register. (Note that the register is at offset 0x1000 in the CPU interface, which means it is on a different 4K page from all the other registers.) Signed-off-by: Peter Maydell Reviewed-by: Sergey Fedorov Message-id: 1456854176-7813-1-git-send-email-peter.maydell@linaro.org --- hw/cpu/a15mpcore.c | 2 +- hw/intc/arm_gic.c | 45 +++++++++++++++++++++++++++++++++++++++- hw/intc/arm_gic_common.c | 2 +- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index e9063ad6d3..a221b8fe7b 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -109,7 +109,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x0fff -- reserved * 0x1000-0x1fff -- GIC Distributor - * 0x2000-0x2fff -- GIC CPU interface + * 0x2000-0x3fff -- GIC CPU interface * 0x4000-0x4fff -- GIC virtual interface control (not modelled) * 0x5000-0x5fff -- GIC virtual interface control (not modelled) * 0x6000-0x7fff -- GIC virtual CPU interface (not modelled) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 60ab9b858b..0834c2f1a7 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -500,6 +500,41 @@ static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs) } } +/* Return true if we should split priority drop and interrupt deactivation, + * ie whether the relevant EOIMode bit is set. + */ +static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs) +{ + if (s->revision != 2) { + /* Before GICv2 prio-drop and deactivate are not separable */ + return false; + } + if (s->security_extn && !attrs.secure) { + return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE_NS; + } + return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE; +} + +static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) +{ + int cm = 1 << cpu; + int group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); + + if (!gic_eoi_split(s, cpu, attrs)) { + /* This is UNPREDICTABLE; we choose to ignore it */ + qemu_log_mask(LOG_GUEST_ERROR, + "gic_deactivate_irq: GICC_DIR write when EOIMode clear"); + return; + } + + if (s->security_extn && !attrs.secure && !group) { + DPRINTF("Non-secure DI for Group0 interrupt %d ignored\n", irq); + return; + } + + GIC_CLEAR_ACTIVE(irq, cm); +} + void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) { int cm = 1 << cpu; @@ -544,7 +579,11 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) */ gic_drop_prio(s, cpu, group); - GIC_CLEAR_ACTIVE(irq, cm); + + /* In GICv2 the guest can choose to split priority-drop and deactivate */ + if (!gic_eoi_split(s, cpu, attrs)) { + GIC_CLEAR_ACTIVE(irq, cm); + } gic_update(s); } @@ -1210,6 +1249,10 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, s->nsapr[regno][cpu] = value; break; } + case 0x1000: + /* GICC_DIR */ + gic_deactivate_irq(s, cpu, value & 0x3ff, attrs); + break; default: qemu_log_mask(LOG_GUEST_ERROR, "gic_cpu_write: Bad offset %x\n", (int)offset); diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index ac8cf42eb8..707d00ded4 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -121,7 +121,7 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, * neither it can use KVM. */ memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL, - s, "gic_cpu", s->revision == 2 ? 0x1000 : 0x100); + s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100); sysbus_init_mmio(sbd, &s->cpuiomem[0]); } } From ba63cf47a93041137a94e86b7d0cd87fc896949b Mon Sep 17 00:00:00 2001 From: Ralf-Philipp Weinmann Date: Fri, 4 Mar 2016 11:30:22 +0000 Subject: [PATCH 30/30] target-arm: Only trap SRS from S-EL1 if specified mode is MON Commit cbc0326b6fb9 caused SRS instructions executed from Secure EL1 to trap to EL3 even if the specified mode was not monitor mode. According to the ARMv8 Architecture reference manual [F6.1.203], ALL of the following conditions need to be met for SRS to trap to EL3: * It is executed at Secure PL1. * The specified mode is monitor mode. * EL3 is using AArch64. Correct the condition governing the trap to EL3 to check the specified mode. Signed-off-by: Ralf-Philipp Weinmann Message-id: 20160222224251.GA11654@beta.comsecuris.com Reviewed-by: Peter Maydell [PMM: tweaked comment text to read 'specified mode'; edited commit message] Signed-off-by: Peter Maydell --- target-arm/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 25db09e9f5..025c7a53eb 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7655,6 +7655,7 @@ static void gen_srs(DisasContext *s, /* SRS is: * - trapped to EL3 if EL3 is AArch64 and we are at Secure EL1 + * and specified mode is monitor mode * - UNDEFINED in Hyp mode * - UNPREDICTABLE in User or System mode * - UNPREDICTABLE if the specified mode is: @@ -7664,7 +7665,7 @@ static void gen_srs(DisasContext *s, * -- Monitor, if we are Non-secure * For the UNPREDICTABLE cases we choose to UNDEF. */ - if (s->current_el == 1 && !s->ns) { + if (s->current_el == 1 && !s->ns && mode == ARM_CPU_MODE_MON) { gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), 3); return; }