Patchwork [17/17] Userspace ARM BE8 support

login
register
mail settings
Submitter riku.voipio@linaro.org
Date April 6, 2012, 4:35 p.m.
Message ID <d8fd2954996255ba6ad610917e7849832d0120b7.1333729958.git.riku.voipio@linaro.org>
Download mbox | patch
Permalink /patch/151251/
State New
Headers show

Comments

riku.voipio@linaro.org - April 6, 2012, 4:35 p.m.
From: Paul Brook <paul@codesourcery.com>

Add support for ARM BE8 userspace binaries.
i.e. big-endian data and little-endian code.
In principle LE8 mode is also possible, but AFAIK has never actually
been implemented/used.

System emulation doesn't have any useable big-endian board models,
but should in principle work once you fix that.
Dynamic endianness switching requires messing with data accesses,
preferably with TCG cooperation, and is orthogonal to BE8 support.

Signed-off-by: Paul Brook <paul@codesourcery.com>
[PMM: various changes, mostly as per my suggestions in code review:
 * rebase
 * use EF_ defines rather than hardcoded constants
 * make bswap_code a bool for future VMSTATE macro compatibility
 * update comment in cpu.h about TB flags bit field usage
 * factor out load-code-and-swap into arm_ld*_code functions and
   get_user_code* macros
 * fix stray trailing space at end of line
 * added braces in disas.c to satisfy checkpatch
]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
---
 disas.c                |   18 +++++++++++++-----
 linux-user/elfload.c   |    1 +
 linux-user/main.c      |   34 +++++++++++++++++++++++++++++-----
 linux-user/qemu.h      |    1 +
 target-arm/cpu.h       |   32 ++++++++++++++++++++++++++++++--
 target-arm/helper.c    |    9 +++++----
 target-arm/translate.c |   11 +++++++----
 7 files changed, 86 insertions(+), 20 deletions(-)

Patch

diff --git a/disas.c b/disas.c
index 9485824..4f2c4e4 100644
--- a/disas.c
+++ b/disas.c
@@ -138,7 +138,7 @@  print_insn_thumb1(bfd_vma pc, disassemble_info *info)
 /* Disassemble this for me please... (debugging). 'flags' has the following
    values:
     i386 - 1 means 16 bit code, 2 means 64 bit code
-    arm  - nonzero means thumb code
+    arm  - bit 0 = thumb, bit 1 = reverse endian
     ppc  - nonzero means little endian
     other targets - unused
  */
@@ -169,10 +169,18 @@  void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
         disasm_info.mach = bfd_mach_i386_i386;
     print_insn = print_insn_i386;
 #elif defined(TARGET_ARM)
-    if (flags)
-	print_insn = print_insn_thumb1;
-    else
-	print_insn = print_insn_arm;
+    if (flags & 1) {
+        print_insn = print_insn_thumb1;
+    } else {
+        print_insn = print_insn_arm;
+    }
+    if (flags & 2) {
+#ifdef TARGET_WORDS_BIGENDIAN
+        disasm_info.endian = BFD_ENDIAN_LITTLE;
+#else
+        disasm_info.endian = BFD_ENDIAN_BIG;
+#endif
+    }
 #elif defined(TARGET_SPARC)
     print_insn = print_insn_sparc;
 #ifdef TARGET_SPARC64
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 4ce9743..f3b1552 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1576,6 +1576,7 @@  static void load_elf_image(const char *image_name, int image_fd,
     info->start_data = -1;
     info->end_data = 0;
     info->brk = 0;
+    info->elf_flags = ehdr->e_flags;
 
     for (i = 0; i < ehdr->e_phnum; i++) {
         struct elf_phdr *eppnt = phdr + i;
diff --git a/linux-user/main.c b/linux-user/main.c
index 23ad357..191b750 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -33,6 +33,7 @@ 
 #include "tcg.h"
 #include "qemu-timer.h"
 #include "envlist.h"
+#include "elf.h"
 
 #define DEBUG_LOGFILE "/tmp/qemu.log"
 
@@ -474,6 +475,22 @@  void cpu_loop(CPUX86State *env)
 
 #ifdef TARGET_ARM
 
+#define get_user_code_u32(x, gaddr, doswap)             \
+    ({ abi_long __r = get_user_u32((x), (gaddr));       \
+        if (!__r && (doswap)) {                         \
+            (x) = bswap32(x);                           \
+        }                                               \
+        __r;                                            \
+    })
+
+#define get_user_code_u16(x, gaddr, doswap)             \
+    ({ abi_long __r = get_user_u16((x), (gaddr));       \
+        if (!__r && (doswap)) {                         \
+            (x) = bswap16(x);                           \
+        }                                               \
+        __r;                                            \
+    })
+
 /*
  * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt
  * Input:
@@ -707,7 +724,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_u32(opcode, env->regs[15]);
+                get_user_code_u32(opcode, env->regs[15], env->bswap_code);
 
                 rc = EmulateAll(opcode, &ts->fpa, env);
                 if (rc == 0) { /* illegal instruction */
@@ -777,23 +794,25 @@  void cpu_loop(CPUARMState *env)
                 if (trapnr == EXCP_BKPT) {
                     if (env->thumb) {
                         /* FIXME - what to do if get_user() fails? */
-                        get_user_u16(insn, env->regs[15]);
+                        get_user_code_u16(insn, env->regs[15], env->bswap_code);
                         n = insn & 0xff;
                         env->regs[15] += 2;
                     } else {
                         /* FIXME - what to do if get_user() fails? */
-                        get_user_u32(insn, env->regs[15]);
+                        get_user_code_u32(insn, env->regs[15], env->bswap_code);
                         n = (insn & 0xf) | ((insn >> 4) & 0xff0);
                         env->regs[15] += 4;
                     }
                 } else {
                     if (env->thumb) {
                         /* FIXME - what to do if get_user() fails? */
-                        get_user_u16(insn, env->regs[15] - 2);
+                        get_user_code_u16(insn, env->regs[15] - 2,
+                                          env->bswap_code);
                         n = insn & 0xff;
                     } else {
                         /* FIXME - what to do if get_user() fails? */
-                        get_user_u32(insn, env->regs[15] - 4);
+                        get_user_code_u32(insn, env->regs[15] - 4,
+                                          env->bswap_code);
                         n = insn & 0xffffff;
                     }
                 }
@@ -3657,6 +3676,11 @@  int main(int argc, char **argv, char **envp)
         for(i = 0; i < 16; i++) {
             env->regs[i] = regs->uregs[i];
         }
+        /* Enable BE8.  */
+        if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4
+            && (info->elf_flags & EF_ARM_BE8)) {
+            env->bswap_code = 1;
+        }
     }
 #elif defined(TARGET_UNICORE32)
     {
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index dd74cc0..7b299b7 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -51,6 +51,7 @@  struct image_info {
         abi_ulong       auxv_len;
         abi_ulong       arg_start;
         abi_ulong       arg_end;
+        uint32_t        elf_flags;
 	int		personality;
 #ifdef CONFIG_USE_FDPIC
         abi_ulong       loadmap_addr;
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index e176c5f..c208c80 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -216,6 +216,9 @@  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;
@@ -491,7 +494,9 @@  static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp)
 #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)
-/* Bits 31..16 are currently unused. */
+#define ARM_TBFLAG_BSWAP_CODE_SHIFT 16
+#define ARM_TBFLAG_BSWAP_CODE_MASK  (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT)
+/* Bits 31..17 are currently unused. */
 
 /* some convenience accessor macros */
 #define ARM_TBFLAG_THUMB(F) \
@@ -506,6 +511,8 @@  static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp)
     (((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)
 
 static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
                                         target_ulong *cs_base, int *flags)
@@ -516,7 +523,8 @@  static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
     *flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT)
         | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT)
         | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT)
-        | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT);
+        | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT)
+        | (env->bswap_code << ARM_TBFLAG_BSWAP_CODE_SHIFT);
     if (arm_feature(env, ARM_FEATURE_M)) {
         privmode = !((env->v7m.exception == 0) && (env->v7m.control & 1));
     } else {
@@ -543,4 +551,24 @@  static inline void cpu_pc_from_tb(CPUARMState *env, TranslationBlock *tb)
     env->regs[15] = tb->pc;
 }
 
+/* Load an instruction and return it in the standard little-endian order */
+static inline uint32_t arm_ldl_code(uint32_t addr, bool do_swap)
+{
+    uint32_t insn = ldl_code(addr);
+    if (do_swap) {
+        return bswap32(insn);
+    }
+    return insn;
+}
+
+/* Ditto, for a halfword (Thumb) instruction */
+static inline uint16_t arm_lduw_code(uint32_t addr, bool do_swap)
+{
+    uint16_t insn = lduw_code(addr);
+    if (do_swap) {
+        return bswap16(insn);
+    }
+    return insn;
+}
+
 #endif
diff --git a/target-arm/helper.c b/target-arm/helper.c
index d974b57..28f127b 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -842,7 +842,7 @@  static void do_interrupt_v7m(CPUARMState *env)
     case EXCP_BKPT:
         if (semihosting_enabled) {
             int nr;
-            nr = lduw_code(env->regs[15]) & 0xff;
+            nr = arm_lduw_code(env->regs[15], env->bswap_code) & 0xff;
             if (nr == 0xab) {
                 env->regs[15] += 2;
                 env->regs[0] = do_arm_semihosting(env);
@@ -914,9 +914,10 @@  void do_interrupt(CPUARMState *env)
         if (semihosting_enabled) {
             /* Check for semihosting interrupt.  */
             if (env->thumb) {
-                mask = lduw_code(env->regs[15] - 2) & 0xff;
+                mask = arm_lduw_code(env->regs[15] - 2, env->bswap_code) & 0xff;
             } else {
-                mask = ldl_code(env->regs[15] - 4) & 0xffffff;
+                mask = arm_ldl_code(env->regs[15] - 4, env->bswap_code)
+                    & 0xffffff;
             }
             /* Only intercept calls from privileged modes, to provide some
                semblance of security.  */
@@ -936,7 +937,7 @@  void do_interrupt(CPUARMState *env)
     case EXCP_BKPT:
         /* See if this is a semihosting syscall.  */
         if (env->thumb && semihosting_enabled) {
-            mask = lduw_code(env->regs[15]) & 0xff;
+            mask = arm_lduw_code(env->regs[15], env->bswap_code) & 0xff;
             if (mask == 0xab
                   && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
                 env->regs[15] += 2;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 46d1d3e..7a3c7d6 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -59,6 +59,7 @@  typedef struct DisasContext {
     struct TranslationBlock *tb;
     int singlestep_enabled;
     int thumb;
+    int bswap_code;
 #if !defined(CONFIG_USER_ONLY)
     int user;
 #endif
@@ -6705,7 +6706,7 @@  static void disas_arm_insn(CPUARMState * env, DisasContext *s)
     TCGv addr;
     TCGv_i64 tmp64;
 
-    insn = ldl_code(s->pc);
+    insn = arm_ldl_code(s->pc, s->bswap_code);
     s->pc += 4;
 
     /* M variants do not implement ARM mode.  */
@@ -8133,7 +8134,7 @@  static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
         /* Fall through to 32-bit decode.  */
     }
 
-    insn = lduw_code(s->pc);
+    insn = arm_lduw_code(s->pc, s->bswap_code);
     s->pc += 2;
     insn |= (uint32_t)insn_hw1 << 16;
 
@@ -9163,7 +9164,7 @@  static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
         }
     }
 
-    insn = lduw_code(s->pc);
+    insn = arm_lduw_code(s->pc, s->bswap_code);
     s->pc += 2;
 
     switch (insn >> 12) {
@@ -9872,6 +9873,7 @@  static inline void gen_intermediate_code_internal(CPUARMState *env,
     dc->singlestep_enabled = env->singlestep_enabled;
     dc->condjmp = 0;
     dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
+    dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags);
     dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
     dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
 #if !defined(CONFIG_USER_ONLY)
@@ -10105,7 +10107,8 @@  done_generating:
     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
         qemu_log("----------------\n");
         qemu_log("IN: %s\n", lookup_symbol(pc_start));
-        log_target_disas(pc_start, dc->pc - pc_start, dc->thumb);
+        log_target_disas(pc_start, dc->pc - pc_start,
+                         dc->thumb | (dc->bswap_code << 1));
         qemu_log("\n");
     }
 #endif