From e1701b069f3da5ceab7125385fb725fe0397b2bd Mon Sep 17 00:00:00 2001 From: Andrew Baumann Date: Sat, 17 Feb 2018 19:28:14 -0500 Subject: [PATCH] target-arm: raise exception on misaligned LDREX operands Qemu does not generally perform alignment checks. However, the ARM ARM requires implementation of alignment exceptions for a number of cases including LDREX, and Windows-on-ARM relies on this. This change adds plumbing to enable alignment checks on loads using MO_ALIGN, a do_unaligned_access hook to raise the exception (data abort), and uses the new aligned loads in LDREX (for all but single-byte loads). Backports commit 30901475b91ef1f46304404ab4bfe89097f61b96 from qemu --- qemu/aarch64.h | 2 ++ qemu/aarch64eb.h | 2 ++ qemu/arm.h | 2 ++ qemu/armeb.h | 2 ++ qemu/header_gen.py | 2 ++ qemu/m68k.h | 2 ++ qemu/mips.h | 2 ++ qemu/mips64.h | 2 ++ qemu/mips64el.h | 2 ++ qemu/mipsel.h | 2 ++ qemu/powerpc.h | 2 ++ qemu/sparc.h | 2 ++ qemu/sparc64.h | 2 ++ qemu/target-arm/cpu.c | 1 + qemu/target-arm/helper.c | 8 ++++++++ qemu/target-arm/internals.h | 7 +++++++ qemu/target-arm/op_helper.c | 40 ++++++++++++++++++++++++++++++++++++- qemu/target-arm/translate.c | 11 ++++++---- qemu/x86_64.h | 2 ++ 19 files changed, 90 insertions(+), 5 deletions(-) diff --git a/qemu/aarch64.h b/qemu/aarch64.h index bffb9ed2..c579b48d 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_aarch64 #define arm_release arm_release_aarch64 #define arm_tlb_fill arm_tlb_fill_aarch64 +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_aarch64 +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_aarch64 #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_aarch64 #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_aarch64 #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_aarch64 diff --git a/qemu/aarch64eb.h b/qemu/aarch64eb.h index a853de48..34723ab0 100644 --- a/qemu/aarch64eb.h +++ b/qemu/aarch64eb.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_aarch64eb #define arm_release arm_release_aarch64eb #define arm_tlb_fill arm_tlb_fill_aarch64eb +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_aarch64eb +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_aarch64eb #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_aarch64eb #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_aarch64eb #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_aarch64eb diff --git a/qemu/arm.h b/qemu/arm.h index 470efc9f..3456887b 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_arm #define arm_release arm_release_arm #define arm_tlb_fill arm_tlb_fill_arm +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_arm +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_arm #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_arm #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_arm #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_arm diff --git a/qemu/armeb.h b/qemu/armeb.h index e014aa20..b7ae2e02 100644 --- a/qemu/armeb.h +++ b/qemu/armeb.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_armeb #define arm_release arm_release_armeb #define arm_tlb_fill arm_tlb_fill_armeb +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_armeb +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_armeb #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_armeb #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_armeb #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_armeb diff --git a/qemu/header_gen.py b/qemu/header_gen.py index f1a22848..32f08e6a 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -30,6 +30,8 @@ symbols = ( 'address_space_stq_be', 'arm_release', 'arm_tlb_fill', + 'arm_regime_using_lpae_format', + 'arm_cpu_do_unaligned_access', 'aarch64_sync_32_to_64', 'aarch64_sync_64_to_32', 'aarch64_tb_set_jmp_target', diff --git a/qemu/m68k.h b/qemu/m68k.h index 9ba6054a..5c5f9a9b 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_m68k #define arm_release arm_release_m68k #define arm_tlb_fill arm_tlb_fill_m68k +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_m68k +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_m68k #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_m68k #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_m68k #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_m68k diff --git a/qemu/mips.h b/qemu/mips.h index c6856cd2..73b312cc 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_mips #define arm_release arm_release_mips #define arm_tlb_fill arm_tlb_fill_mips +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_mips +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_mips #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_mips #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_mips #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index 6dd313c8..a497d6a4 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_mips64 #define arm_release arm_release_mips64 #define arm_tlb_fill arm_tlb_fill_mips64 +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_mips64 +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_mips64 #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_mips64 #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_mips64 #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 380bbc36..fdd8f7ef 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_mips64el #define arm_release arm_release_mips64el #define arm_tlb_fill arm_tlb_fill_mips64el +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_mips64el +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_mips64el #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_mips64el #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_mips64el #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index d885353f..f0c4e043 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_mipsel #define arm_release arm_release_mipsel #define arm_tlb_fill arm_tlb_fill_mipsel +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_mipsel +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_mipsel #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_mipsel #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_mipsel #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_mipsel diff --git a/qemu/powerpc.h b/qemu/powerpc.h index b508372a..22a423c5 100644 --- a/qemu/powerpc.h +++ b/qemu/powerpc.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_powerpc #define arm_release arm_release_powerpc #define arm_tlb_fill arm_tlb_fill_powerpc +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_powerpc +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_powerpc #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_powerpc #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_powerpc #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_powerpc diff --git a/qemu/sparc.h b/qemu/sparc.h index ce48c247..a29ea395 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_sparc #define arm_release arm_release_sparc #define arm_tlb_fill arm_tlb_fill_sparc +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_sparc +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_sparc #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_sparc #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_sparc #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index e04ea43e..d9a812c4 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_sparc64 #define arm_release arm_release_sparc64 #define arm_tlb_fill arm_tlb_fill_sparc64 +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_sparc64 +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_sparc64 #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_sparc64 #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_sparc64 #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_sparc64 diff --git a/qemu/target-arm/cpu.c b/qemu/target-arm/cpu.c index f72322b6..76f0de01 100644 --- a/qemu/target-arm/cpu.c +++ b/qemu/target-arm/cpu.c @@ -1282,6 +1282,7 @@ static void arm_cpu_class_init(struct uc_struct *uc, ObjectClass *oc, void *data cc->handle_mmu_fault = arm_cpu_handle_mmu_fault; #else cc->do_interrupt = arm_cpu_do_interrupt; + cc->do_unaligned_access = arm_cpu_do_unaligned_access; cc->get_phys_page_debug = arm_cpu_get_phys_page_debug; // UNICORN: Commented out //cc->vmsd = &vmstate_arm_cpu; diff --git a/qemu/target-arm/helper.c b/qemu/target-arm/helper.c index 171ea0dd..cfab231b 100644 --- a/qemu/target-arm/helper.c +++ b/qemu/target-arm/helper.c @@ -5339,6 +5339,14 @@ static inline bool regime_using_lpae_format(CPUARMState *env, return false; } +/* Returns true if the translation regime is using LPAE format page tables. + * Used when raising alignment exceptions, whose FSR changes depending on + * whether the long or short descriptor format is in use. */ +bool arm_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + return regime_using_lpae_format(env, mmu_idx); +} + static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { diff --git a/qemu/target-arm/internals.h b/qemu/target-arm/internals.h index 1e00c3b0..c8902e68 100644 --- a/qemu/target-arm/internals.h +++ b/qemu/target-arm/internals.h @@ -443,4 +443,11 @@ struct ARMMMUFaultInfo { bool arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx, uint32_t *fsr, ARMMMUFaultInfo *fi); +/* Return true if the translation regime is using LPAE format page tables */ +bool arm_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx); + +/* Raise a data fault alignment exception for the specified virtual address */ +void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write, + int is_user, uintptr_t retaddr); + #endif diff --git a/qemu/target-arm/op_helper.c b/qemu/target-arm/op_helper.c index f311e723..c43a1ded 100644 --- a/qemu/target-arm/op_helper.c +++ b/qemu/target-arm/op_helper.c @@ -126,7 +126,45 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, raise_exception(env, exc, syn, target_el); } } -#endif + +/* Raise a data fault alignment exception for the specified virtual address */ +void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, int is_write, + int is_user, uintptr_t retaddr) +{ + ARMCPU *cpu = ARM_CPU(cs->uc, cs); + CPUARMState *env = &cpu->env; + int target_el; + bool same_el; + + if (retaddr) { + /* now we have a real cpu fault */ + cpu_restore_state(cs, retaddr); + } + + target_el = exception_target_el(env); + same_el = (arm_current_el(env) == target_el); + + env->exception.vaddress = vaddr; + + /* the DFSR for an alignment fault depends on whether we're using + * the LPAE long descriptor format, or the short descriptor format + */ + if (arm_regime_using_lpae_format(env, cpu_mmu_index(env, false))) { + env->exception.fsr = 0x21; + } else { + env->exception.fsr = 0x1; + } + + if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) { + env->exception.fsr |= (1 << 11); + } + + raise_exception(env, EXCP_DATA_ABORT, + syn_data_abort(same_el, 0, 0, 0, is_write == 1, 0x21), + target_el); +} + +#endif /* !defined(CONFIG_USER_ONLY) */ uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b) { diff --git a/qemu/target-arm/translate.c b/qemu/target-arm/translate.c index 08e8827d..8fbe8c98 100644 --- a/qemu/target-arm/translate.c +++ b/qemu/target-arm/translate.c @@ -955,13 +955,13 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) #define DO_GEN_LD(SUFF, OPC) \ static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, TCGv_i32 addr, int index) \ { \ - tcg_gen_qemu_ld_i32(s->uc, val, addr, index, OPC); \ + tcg_gen_qemu_ld_i32(s->uc, 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(s->uc, val, addr, index, OPC); \ + tcg_gen_qemu_st_i32(s->uc, val, addr, index, (OPC)); \ } static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, TCGv_i32 addr, int index) @@ -1021,6 +1021,9 @@ DO_GEN_LD(8u, MO_UB) DO_GEN_LD(16s, MO_TESW) DO_GEN_LD(16u, MO_TEUW) DO_GEN_LD(32u, MO_TEUL) +/* 'a' variants include an alignment check */ +DO_GEN_LD(16ua, MO_TEUW | MO_ALIGN) +DO_GEN_LD(32ua, MO_TEUL | MO_ALIGN) DO_GEN_ST(8, MO_UB) DO_GEN_ST(16, MO_TEUW) DO_GEN_ST(32, MO_TEUL) @@ -7562,11 +7565,11 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); break; case 1: - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld16ua(s, tmp, addr, get_mem_index(s)); break; case 2: case 3: - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + gen_aa32_ld32ua(s, tmp, addr, get_mem_index(s)); break; default: abort(); diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 2d41b7dc..1d13f3dd 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -24,6 +24,8 @@ #define address_space_stq_be address_space_stq_be_x86_64 #define arm_release arm_release_x86_64 #define arm_tlb_fill arm_tlb_fill_x86_64 +#define arm_regime_using_lpae_format arm_regime_using_lpae_format_x86_64 +#define arm_cpu_do_unaligned_access arm_cpu_do_unaligned_access_x86_64 #define aarch64_sync_32_to_64 aarch64_sync_32_to_64_x86_64 #define aarch64_sync_64_to_32 aarch64_sync_64_to_32_x86_64 #define aarch64_tb_set_jmp_target aarch64_tb_set_jmp_target_x86_64