From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42740) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aL406-0003dJ-Tn for qemu-devel@nongnu.org; Mon, 18 Jan 2016 02:13:21 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aL405-00079H-FX for qemu-devel@nongnu.org; Mon, 18 Jan 2016 02:13:18 -0500 From: Peter Crosthwaite Date: Sun, 17 Jan 2016 23:12:44 -0800 Message-Id: In-Reply-To: References: In-Reply-To: References: Subject: [Qemu-devel] [PATCH v1 17/17] arm: boot: Support big-endian elfs List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: peter.maydell@linaro.org, Peter Crosthwaite , alistair.francis@xilinx.com, sridhar_kulk@yahoo.com, qemu-arm@nongnu.org, pbonzini@redhat.com, piotr.krol@3mdeb.com 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 --- hw/arm/boot.c | 96 ++++++++++++++++++++++++++++++++++++++++++---------- include/hw/arm/arm.h | 9 +++++ 2 files changed, 88 insertions(+), 17 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 0de4269..053c9e8 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -465,9 +465,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; @@ -589,16 +614,23 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) int kernel_size; int initrd_size; int is_linux = 0; + uint64_t elf_entry, elf_low_addr, elf_high_addr; int elf_machine; + bool elf_is64; + union { + Elf32_Ehdr h32; + Elf64_Ehdr h64; + } elf_header; + hwaddr entry, kernel_load_offset; - int big_endian; static const ARMInsnFixup *primary_loader; ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier, notifier, notifier); ARMCPU *cpu = n->cpu; struct arm_boot_info *info = container_of(n, struct arm_boot_info, load_kernel_notifier); + Error *err = NULL; /* The board code is not supposed to set secure_board_setup unless * running its code in secure mode is actually possible, and KVM @@ -678,12 +710,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 @@ -698,16 +724,52 @@ 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); - if (kernel_size > 0 && have_dtb(info)) { - /* If there is still some room left at the base of RAM, try and put - * the DTB there like we do for images loaded with -bios or -pflash. - */ - if (elf_low_addr > info->loader_start - || elf_high_addr < info->loader_start) { - /* Pass elf_low_addr as address limit to load_dtb if it may be + + load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err); + + if (!err) { + int data_swab = 0; + bool big_endian; + + 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 elfs are + * organised such that they can be programmed through the + * CPUs per-word byte-reversed view of the world. QEMU + * however loads elfs 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; + } + } + + kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry, + &elf_low_addr, &elf_high_addr, big_endian, + elf_machine, 1, data_swab); + if (kernel_size <= 0) { + exit(1); + } + + if (have_dtb(info) && (elf_low_addr > info->loader_start || + elf_high_addr < info->loader_start)) { + /* 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. + * + * Pass elf_low_addr as address limit to load_dtb if it may be * pointing into RAM, otherwise pass '0' (no limit) */ if (elf_low_addr < info->loader_start) { diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index c26b0e3..75d77c9 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; }; /** -- 1.9.1