aboutsummaryrefslogtreecommitdiff
path: root/target/arm
diff options
context:
space:
mode:
authorJulian Brown <julian@codesourcery.com>2017-02-07 18:29:59 +0000
committerPeter Maydell <peter.maydell@linaro.org>2017-02-07 18:29:59 +0000
commitf7478a92dd9ee2276bfaa5b7317140d3f9d6a53b (patch)
treef533aa39ca2f33bb53b6135f93f2072069ad9fae /target/arm
parent3a062d5730266b2386eeda68b1a1c6e96451db31 (diff)
Fix Thumb-1 BE32 execution and disassembly.
Thumb-1 code has some issues in BE32 mode (as currently implemented). In short, since bytes are swapped within words at load time for BE32 executables, this also swaps pairs of adjacent Thumb-1 instructions. This patch un-swaps those pairs of instructions again, both for execution, and for disassembly. (The previous version of the patch always read four bytes in arm_read_memory_func and then extracted the proper two bytes, in a probably misguided attempt to match the behaviour of actual hardware as described by e.g. the ARM9TDMI TRM, section 3.3 "Endian effects for instruction fetches". It's less complicated to just read the correct two bytes though.) Signed-off-by: Julian Brown <julian@codesourcery.com> Message-id: ca20462a044848000370318a8bd41dd0a4ed273f.1484929304.git.julian@codesourcery.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target/arm')
-rw-r--r--target/arm/arm_ldst.h10
-rw-r--r--target/arm/cpu.c23
2 files changed, 32 insertions, 1 deletions
diff --git a/target/arm/arm_ldst.h b/target/arm/arm_ldst.h
index a76d89f62c..01587b3ebb 100644
--- a/target/arm/arm_ldst.h
+++ b/target/arm/arm_ldst.h
@@ -39,7 +39,15 @@ static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr,
static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr,
bool sctlr_b)
{
- uint16_t insn = cpu_lduw_code(env, addr);
+ uint16_t insn;
+#ifndef CONFIG_USER_ONLY
+ /* In big-endian (BE32) mode, adjacent Thumb instructions have been swapped
+ within each word. Undo that now. */
+ if (sctlr_b) {
+ addr ^= 2;
+ }
+#endif
+ insn = cpu_lduw_code(env, addr);
if (bswap_code(sctlr_b)) {
return bswap16(insn);
}
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index a8cfd9d0f5..81448caf18 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -446,6 +446,21 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info)
return print_insn_arm(pc | 1, info);
}
+static int arm_read_memory_func(bfd_vma memaddr, bfd_byte *b,
+ int length, struct disassemble_info *info)
+{
+ assert(info->read_memory_inner_func);
+ assert((info->flags & INSN_ARM_BE32) == 0 || length == 2 || length == 4);
+
+ if ((info->flags & INSN_ARM_BE32) != 0 && length == 2) {
+ assert(info->endian == BFD_ENDIAN_LITTLE);
+ return info->read_memory_inner_func(memaddr ^ 2, (bfd_byte *)b, 2,
+ info);
+ } else {
+ return info->read_memory_inner_func(memaddr, b, length, info);
+ }
+}
+
static void arm_disas_set_info(CPUState *cpu, disassemble_info *info)
{
ARMCPU *ac = ARM_CPU(cpu);
@@ -471,6 +486,14 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info)
info->endian = BFD_ENDIAN_BIG;
#endif
}
+ if (info->read_memory_inner_func == NULL) {
+ info->read_memory_inner_func = info->read_memory_func;
+ info->read_memory_func = arm_read_memory_func;
+ }
+ info->flags &= ~INSN_ARM_BE32;
+ if (arm_sctlr_b(env)) {
+ info->flags |= INSN_ARM_BE32;
+ }
}
static void arm_cpu_initfn(Object *obj)