qemu-arm
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-arm] [PATCH v1 17/17] arm: boot: Support big-endian elfs


From: Peter Crosthwaite
Subject: [Qemu-arm] [PATCH v1 17/17] arm: boot: Support big-endian elfs
Date: Sun, 17 Jan 2016 23:12:44 -0800

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 <address@hidden>
---

 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]