diff mbox

[1/3] arm: basic support for ARMv4/ARMv4T emulation

Message ID 1301924326-7457-2-git-send-email-dbaryshkov@gmail.com
State New
Headers show

Commit Message

Dmitry Baryshkov April 4, 2011, 1:38 p.m. UTC
Currently target-arm/ assumes at least ARMv5 core. Add support for
handling also ARMv4/ARMv4T. This changes the following instructions:

BX(v4T and later)

BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
SMULxy, SMULWxy, STC2 (v5 and later)

All instructions that are "v5TE and later" are also bound to just v5, as
that's how it was before.

This patch doesn _not_ include disabling of cp15 access and base-updated
data abort model (that will be required to emulate chips based on a
ARM7TDMI), because:
* no ARM7TDMI chips are currently emulated (or planned)
* those features aren't strictly necessary for my purposes (SA-1 core
  emulation).

All v5 models are handled as they are v5T. Internally we still have a
check if the model is a v5(T) or v5TE, but as all emulated cores are
v5TE, those two cases are simply aliased (for now).

Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
Sanders <vince@kyllikki.org>.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
 target-arm/cpu.h       |    4 ++-
 target-arm/helper.c    |   29 ++++++++++++++++++++++-
 target-arm/translate.c |   59 +++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 80 insertions(+), 12 deletions(-)

Comments

Peter Maydell April 6, 2011, 1:56 p.m. UTC | #1
On 4 April 2011 14:38, Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
> Currently target-arm/ assumes at least ARMv5 core. Add support for
> handling also ARMv4/ARMv4T. This changes the following instructions:

> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

-- PMM
Dmitry Baryshkov April 6, 2011, 5:41 p.m. UTC | #2
On 4/6/11, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 4 April 2011 14:38, Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
>> Currently target-arm/ assumes at least ARMv5 core. Add support for
>> handling also ARMv4/ARMv4T. This changes the following instructions:
>
>> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
>
> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>

Please commit then, as I don't have commit access.
Peter Maydell April 6, 2011, 5:50 p.m. UTC | #3
On 6 April 2011 18:41, Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
> On 4/6/11, Peter Maydell <peter.maydell@linaro.org> wrote:
>> On 4 April 2011 14:38, Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
>>> Currently target-arm/ assumes at least ARMv5 core. Add support for
>>> handling also ARMv4/ARMv4T. This changes the following instructions:
>>
>>> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
>>
>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>
> Please commit then, as I don't have commit access.

Neither do I :-)  I'm assuming Aurelien will also review this;
if all else fails it will go into my set of ARM patches for the
next pull request I make.

-- PMM
Aurelien Jarno April 10, 2011, 11:25 a.m. UTC | #4
On Mon, Apr 04, 2011 at 05:38:44PM +0400, Dmitry Eremin-Solenikov wrote:
> Currently target-arm/ assumes at least ARMv5 core. Add support for
> handling also ARMv4/ARMv4T. This changes the following instructions:
> 
> BX(v4T and later)
> 
> BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
> MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
> SMULxy, SMULWxy, STC2 (v5 and later)
> 
> All instructions that are "v5TE and later" are also bound to just v5, as
> that's how it was before.
> 
> This patch doesn _not_ include disabling of cp15 access and base-updated
> data abort model (that will be required to emulate chips based on a
> ARM7TDMI), because:
> * no ARM7TDMI chips are currently emulated (or planned)
> * those features aren't strictly necessary for my purposes (SA-1 core
>   emulation).
> 
> All v5 models are handled as they are v5T. Internally we still have a
> check if the model is a v5(T) or v5TE, but as all emulated cores are
> v5TE, those two cases are simply aliased (for now).
> 
> Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
> which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
> Sanders <vince@kyllikki.org>.
> 
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> ---
>  target-arm/cpu.h       |    4 ++-
>  target-arm/helper.c    |   29 ++++++++++++++++++++++-
>  target-arm/translate.c |   59 +++++++++++++++++++++++++++++++++++++++--------
>  3 files changed, 80 insertions(+), 12 deletions(-)

Thanks, applied.

> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 1ae7982..e247a7a 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -360,7 +360,9 @@ enum arm_features {
>      ARM_FEATURE_M, /* Microcontroller profile.  */
>      ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling.  */
>      ARM_FEATURE_THUMB2EE,
> -    ARM_FEATURE_V7MP    /* v7 Multiprocessing Extensions */
> +    ARM_FEATURE_V7MP,    /* v7 Multiprocessing Extensions */
> +    ARM_FEATURE_V4T,
> +    ARM_FEATURE_V5,
>  };
>  
>  static inline int arm_feature(CPUARMState *env, int feature)
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index 6788a4c..ce9a9d8 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>      env->cp15.c0_cpuid = id;
>      switch (id) {
>      case ARM_CPUID_ARM926:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_VFP);
>          env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
>          env->cp15.c0_cachetype = 0x1dd20d2;
>          env->cp15.c1_sys = 0x00090078;
>          break;
>      case ARM_CPUID_ARM946:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_MPU);
>          env->cp15.c0_cachetype = 0x0f004006;
>          env->cp15.c1_sys = 0x00000078;
>          break;
>      case ARM_CPUID_ARM1026:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_VFP);
>          set_feature(env, ARM_FEATURE_AUXCR);
>          env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
> @@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>          break;
>      case ARM_CPUID_ARM1136_R2:
>      case ARM_CPUID_ARM1136:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_VFP);
>          set_feature(env, ARM_FEATURE_AUXCR);
> @@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>          env->cp15.c1_sys = 0x00050078;
>          break;
>      case ARM_CPUID_ARM11MPCORE:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_V6K);
>          set_feature(env, ARM_FEATURE_VFP);
> @@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>          env->cp15.c0_cachetype = 0x1dd20d2;
>          break;
>      case ARM_CPUID_CORTEXA8:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_V6K);
>          set_feature(env, ARM_FEATURE_V7);
> @@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>          env->cp15.c1_sys = 0x00c50078;
>          break;
>      case ARM_CPUID_CORTEXA9:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_V6K);
>          set_feature(env, ARM_FEATURE_V7);
> @@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>          env->cp15.c1_sys = 0x00c50078;
>          break;
>      case ARM_CPUID_CORTEXM3:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_THUMB2);
>          set_feature(env, ARM_FEATURE_V7);
> @@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>          set_feature(env, ARM_FEATURE_DIV);
>          break;
>      case ARM_CPUID_ANY: /* For userspace emulation.  */
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_V6K);
>          set_feature(env, ARM_FEATURE_V7);
> @@ -161,6 +179,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>          break;
>      case ARM_CPUID_TI915T:
>      case ARM_CPUID_TI925T:
> +        set_feature(env, ARM_FEATURE_V4T);
>          set_feature(env, ARM_FEATURE_OMAPCP);
>          env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring.  */
>          env->cp15.c0_cachetype = 0x5109149;
> @@ -173,6 +192,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>      case ARM_CPUID_PXA260:
>      case ARM_CPUID_PXA261:
>      case ARM_CPUID_PXA262:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_XSCALE);
>          /* JTAG_ID is ((id << 28) | 0x09265013) */
>          env->cp15.c0_cachetype = 0xd172172;
> @@ -184,6 +205,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>      case ARM_CPUID_PXA270_B1:
>      case ARM_CPUID_PXA270_C0:
>      case ARM_CPUID_PXA270_C5:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_XSCALE);
>          /* JTAG_ID is ((id << 28) | 0x09265013) */
>          set_feature(env, ARM_FEATURE_IWMMXT);
> @@ -856,7 +879,11 @@ void do_interrupt(CPUARMState *env)
>      /* Switch to the new mode, and to the correct instruction set.  */
>      env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
>      env->uncached_cpsr |= mask;
> -    env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
> +    /* this is a lie, as the was no c1_sys on V4T/V5, but who cares
> +     * and we should just guard the thumb mode on V4 */
> +    if (arm_feature(env, ARM_FEATURE_V4T)) {
> +        env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
> +    }
>      env->regs[14] = env->regs[15] + offset;
>      env->regs[15] = addr;
>      env->interrupt_request |= CPU_INTERRUPT_EXITTB;
> diff --git a/target-arm/translate.c b/target-arm/translate.c
> index 33417e6..05a58d4 100644
> --- a/target-arm/translate.c
> +++ b/target-arm/translate.c
> @@ -34,6 +34,10 @@
>  #define GEN_HELPER 1
>  #include "helpers.h"
>  
> +#define ENABLE_ARCH_4T    arm_feature(env, ARM_FEATURE_V4T)
> +#define ENABLE_ARCH_5     arm_feature(env, ARM_FEATURE_V5)
> +/* currently all emulated v5 cores are also v5TE, so don't bother */
> +#define ENABLE_ARCH_5TE   arm_feature(env, ARM_FEATURE_V5)
>  #define ENABLE_ARCH_5J    0
>  #define ENABLE_ARCH_6     arm_feature(env, ARM_FEATURE_V6)
>  #define ENABLE_ARCH_6K   arm_feature(env, ARM_FEATURE_V6K)
> @@ -750,6 +754,20 @@ static inline void store_reg_bx(CPUState *env, DisasContext *s,
>      }
>  }
>  
> +/* Variant of store_reg which uses branch&exchange logic when storing
> + * to r15 in ARM architecture v5T and above. This is used for storing
> + * the results of a LDR/LDM/POP into r15, and corresponds to the cases
> + * in the ARM ARM which use the LoadWritePC() pseudocode function. */
> +static inline void store_reg_from_load(CPUState *env, DisasContext *s,
> +                                int reg, TCGv var)
> +{
> +    if (reg == 15 && ENABLE_ARCH_5) {
> +        gen_bx(s, var);
> +    } else {
> +        store_reg(s, reg, var);
> +    }
> +}
> +
>  static inline TCGv gen_ld8s(TCGv addr, int index)
>  {
>      TCGv tmp = tcg_temp_new_i32();
> @@ -3443,6 +3461,10 @@ static uint32_t msr_mask(CPUState *env, DisasContext *s, int flags, int spsr) {
>  
>      /* Mask out undefined bits.  */
>      mask &= ~CPSR_RESERVED;
> +    if (!arm_feature(env, ARM_FEATURE_V4T))
> +        mask &= ~CPSR_T;
> +    if (!arm_feature(env, ARM_FEATURE_V5))
> +        mask &= ~CPSR_Q; /* V5TE in reality*/
>      if (!arm_feature(env, ARM_FEATURE_V6))
>          mask &= ~(CPSR_E | CPSR_GE);
>      if (!arm_feature(env, ARM_FEATURE_THUMB2))
> @@ -6145,6 +6167,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>          goto illegal_op;
>      cond = insn >> 28;
>      if (cond == 0xf){
> +        /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we
> +         * choose to UNDEF. In ARMv5 and above the space is used
> +         * for miscellaneous unconditional instructions.
> +         */
> +        ARCH(5);
> +
>          /* Unconditional instructions.  */
>          if (((insn >> 25) & 7) == 1) {
>              /* NEON Data processing.  */
> @@ -6173,6 +6201,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                  }
>              }
>              /* Otherwise PLD; v5TE+ */
> +            ARCH(5TE);
>              return;
>          }
>          if (((insn & 0x0f70f000) == 0x0450f000) ||
> @@ -6309,6 +6338,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>              val += (offset << 2) | ((insn >> 23) & 2) | 1;
>              /* pipeline offset */
>              val += 4;
> +            /* protected by ARCH(5); above, near the start of uncond block */
>              gen_bx_im(s, val);
>              return;
>          } else if ((insn & 0x0e000f00) == 0x0c000100) {
> @@ -6320,6 +6350,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>              }
>          } else if ((insn & 0x0fe00000) == 0x0c400000) {
>              /* Coprocessor double register transfer.  */
> +            ARCH(5TE);
>          } else if ((insn & 0x0f000010) == 0x0e000010) {
>              /* Additional coprocessor register transfer.  */
>          } else if ((insn & 0x0ff10020) == 0x01000000) {
> @@ -6420,10 +6451,12 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>          case 0x1:
>              if (op1 == 1) {
>                  /* branch/exchange thumb (bx).  */
> +                ARCH(4T);
>                  tmp = load_reg(s, rm);
>                  gen_bx(s, tmp);
>              } else if (op1 == 3) {
>                  /* clz */
> +                ARCH(5);
>                  rd = (insn >> 12) & 0xf;
>                  tmp = load_reg(s, rm);
>                  gen_helper_clz(tmp, tmp);
> @@ -6446,6 +6479,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>              if (op1 != 1)
>                goto illegal_op;
>  
> +            ARCH(5);
>              /* branch link/exchange thumb (blx) */
>              tmp = load_reg(s, rm);
>              tmp2 = tcg_temp_new_i32();
> @@ -6454,6 +6488,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>              gen_bx(s, tmp);
>              break;
>          case 0x5: /* saturating add/subtract */
> +            ARCH(5TE);
>              rd = (insn >> 12) & 0xf;
>              rn = (insn >> 16) & 0xf;
>              tmp = load_reg(s, rm);
> @@ -6475,12 +6510,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                  goto illegal_op;
>              }
>              /* bkpt */
> +            ARCH(5);
>              gen_exception_insn(s, 4, EXCP_BKPT);
>              break;
>          case 0x8: /* signed multiply */
>          case 0xa:
>          case 0xc:
>          case 0xe:
> +            ARCH(5TE);
>              rs = (insn >> 8) & 0xf;
>              rn = (insn >> 12) & 0xf;
>              rd = (insn >> 16) & 0xf;
> @@ -6876,6 +6913,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                      }
>                      load = 1;
>                  } else if (sh & 2) {
> +                    ARCH(5TE);
>                      /* doubleword */
>                      if (sh & 1) {
>                          /* store */
> @@ -7216,10 +7254,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>              }
>              if (insn & (1 << 20)) {
>                  /* Complete the load.  */
> -                if (rd == 15)
> -                    gen_bx(s, tmp);
> -                else
> -                    store_reg(s, rd, tmp);
> +                store_reg_from_load(env, s, rd, tmp);
>              }
>              break;
>          case 0x08:
> @@ -7272,9 +7307,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                          if (insn & (1 << 20)) {
>                              /* load */
>                              tmp = gen_ld32(addr, IS_USER(s));
> -                            if (i == 15) {
> -                                gen_bx(s, tmp);
> -                            } else if (user) {
> +                            if (user) {
>                                  tmp2 = tcg_const_i32(i);
>                                  gen_helper_set_user_reg(tmp2, tmp);
>                                  tcg_temp_free_i32(tmp2);
> @@ -7283,7 +7316,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                                  loaded_var = tmp;
>                                  loaded_base = 1;
>                              } else {
> -                                store_reg(s, i, tmp);
> +                                store_reg_from_load(env, s, i, tmp);
>                              }
>                          } else {
>                              /* store */
> @@ -7483,6 +7516,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
>             16-bit instructions to get correct prefetch abort behavior.  */
>          insn = insn_hw1;
>          if ((insn & (1 << 12)) == 0) {
> +            ARCH(5);
>              /* Second half of blx.  */
>              offset = ((insn & 0x7ff) << 1);
>              tmp = load_reg(s, 14);
> @@ -8079,6 +8113,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
>                  } else {
>                      /* blx */
>                      offset &= ~(uint32_t)2;
> +                    /* thumb2 bx, no need to check */
>                      gen_bx_im(s, offset);
>                  }
>              } else if (((insn >> 23) & 7) == 7) {
> @@ -8660,11 +8695,13 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
>              case 3:/* branch [and link] exchange thumb register */
>                  tmp = load_reg(s, rm);
>                  if (insn & (1 << 7)) {
> +                    ARCH(5);
>                      val = (uint32_t)s->pc | 1;
>                      tmp2 = tcg_temp_new_i32();
>                      tcg_gen_movi_i32(tmp2, val);
>                      store_reg(s, 14, tmp2);
>                  }
> +                /* already thumb, no need to check */
>                  gen_bx(s, tmp);
>                  break;
>              }
> @@ -9024,8 +9061,9 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
>              /* write back the new stack pointer */
>              store_reg(s, 13, addr);
>              /* set the new PC value */
> -            if ((insn & 0x0900) == 0x0900)
> -                gen_bx(s, tmp);
> +            if ((insn & 0x0900) == 0x0900) {
> +                store_reg_from_load(env, s, 15, tmp);
> +            }
>              break;
>  
>          case 1: case 3: case 9: case 11: /* czb */
> @@ -9056,6 +9094,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
>              break;
>  
>          case 0xe: /* bkpt */
> +            ARCH(5);
>              gen_exception_insn(s, 2, EXCP_BKPT);
>              break;
>  
> -- 
> 1.7.4.1
> 
> 
>
Marek Vasut April 10, 2011, 12:24 p.m. UTC | #5
On Monday 04 April 2011 15:38:44 Dmitry Eremin-Solenikov wrote:
> Currently target-arm/ assumes at least ARMv5 core. Add support for
> handling also ARMv4/ARMv4T. This changes the following instructions:
> 
> BX(v4T and later)
> 
> BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
> MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
> SMULxy, SMULWxy, STC2 (v5 and later)
> 
> All instructions that are "v5TE and later" are also bound to just v5, as
> that's how it was before.
> 
> This patch doesn _not_ include disabling of cp15 access and base-updated
> data abort model (that will be required to emulate chips based on a
> ARM7TDMI), because:
> * no ARM7TDMI chips are currently emulated (or planned)

Hi,

this will come handy as I plan to add support for certain arm7tdmi chip (forward 
port at91sam7 patchset)

Cheers

> * those features aren't strictly necessary for my purposes (SA-1 core
>   emulation).
> 
> All v5 models are handled as they are v5T. Internally we still have a
> check if the model is a v5(T) or v5TE, but as all emulated cores are
> v5TE, those two cases are simply aliased (for now).
> 
> Patch is heavily based on patch by Filip Navara <filip.navara@gmail.com>
> which in turn is based on work by Ulrich Hecht <uli@suse.de> and Vincent
> Sanders <vince@kyllikki.org>.
> 
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> ---
>  target-arm/cpu.h       |    4 ++-
>  target-arm/helper.c    |   29 ++++++++++++++++++++++-
>  target-arm/translate.c |   59
> +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 80
> insertions(+), 12 deletions(-)
> 
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 1ae7982..e247a7a 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -360,7 +360,9 @@ enum arm_features {
>      ARM_FEATURE_M, /* Microcontroller profile.  */
>      ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling.  */
>      ARM_FEATURE_THUMB2EE,
> -    ARM_FEATURE_V7MP    /* v7 Multiprocessing Extensions */
> +    ARM_FEATURE_V7MP,    /* v7 Multiprocessing Extensions */
> +    ARM_FEATURE_V4T,
> +    ARM_FEATURE_V5,
>  };
> 
>  static inline int arm_feature(CPUARMState *env, int feature)
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index 6788a4c..ce9a9d8 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -48,17 +48,23 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) env->cp15.c0_cpuid = id;
>      switch (id) {
>      case ARM_CPUID_ARM926:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_VFP);
>          env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
>          env->cp15.c0_cachetype = 0x1dd20d2;
>          env->cp15.c1_sys = 0x00090078;
>          break;
>      case ARM_CPUID_ARM946:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_MPU);
>          env->cp15.c0_cachetype = 0x0f004006;
>          env->cp15.c1_sys = 0x00000078;
>          break;
>      case ARM_CPUID_ARM1026:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_VFP);
>          set_feature(env, ARM_FEATURE_AUXCR);
>          env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
> @@ -67,6 +73,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t
> id) break;
>      case ARM_CPUID_ARM1136_R2:
>      case ARM_CPUID_ARM1136:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_VFP);
>          set_feature(env, ARM_FEATURE_AUXCR);
> @@ -79,6 +87,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t
> id) env->cp15.c1_sys = 0x00050078;
>          break;
>      case ARM_CPUID_ARM11MPCORE:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_V6K);
>          set_feature(env, ARM_FEATURE_VFP);
> @@ -91,6 +101,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) env->cp15.c0_cachetype = 0x1dd20d2;
>          break;
>      case ARM_CPUID_CORTEXA8:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_V6K);
>          set_feature(env, ARM_FEATURE_V7);
> @@ -113,6 +125,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) env->cp15.c1_sys = 0x00c50078;
>          break;
>      case ARM_CPUID_CORTEXA9:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_V6K);
>          set_feature(env, ARM_FEATURE_V7);
> @@ -140,6 +154,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) env->cp15.c1_sys = 0x00c50078;
>          break;
>      case ARM_CPUID_CORTEXM3:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_THUMB2);
>          set_feature(env, ARM_FEATURE_V7);
> @@ -147,6 +163,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) set_feature(env, ARM_FEATURE_DIV);
>          break;
>      case ARM_CPUID_ANY: /* For userspace emulation.  */
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_V6);
>          set_feature(env, ARM_FEATURE_V6K);
>          set_feature(env, ARM_FEATURE_V7);
> @@ -161,6 +179,7 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) break;
>      case ARM_CPUID_TI915T:
>      case ARM_CPUID_TI925T:
> +        set_feature(env, ARM_FEATURE_V4T);
>          set_feature(env, ARM_FEATURE_OMAPCP);
>          env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring.  */
>          env->cp15.c0_cachetype = 0x5109149;
> @@ -173,6 +192,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) case ARM_CPUID_PXA260:
>      case ARM_CPUID_PXA261:
>      case ARM_CPUID_PXA262:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_XSCALE);
>          /* JTAG_ID is ((id << 28) | 0x09265013) */
>          env->cp15.c0_cachetype = 0xd172172;
> @@ -184,6 +205,8 @@ static void cpu_reset_model_id(CPUARMState *env,
> uint32_t id) case ARM_CPUID_PXA270_B1:
>      case ARM_CPUID_PXA270_C0:
>      case ARM_CPUID_PXA270_C5:
> +        set_feature(env, ARM_FEATURE_V4T);
> +        set_feature(env, ARM_FEATURE_V5);
>          set_feature(env, ARM_FEATURE_XSCALE);
>          /* JTAG_ID is ((id << 28) | 0x09265013) */
>          set_feature(env, ARM_FEATURE_IWMMXT);
> @@ -856,7 +879,11 @@ void do_interrupt(CPUARMState *env)
>      /* Switch to the new mode, and to the correct instruction set.  */
>      env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
>      env->uncached_cpsr |= mask;
> -    env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
> +    /* this is a lie, as the was no c1_sys on V4T/V5, but who cares
> +     * and we should just guard the thumb mode on V4 */
> +    if (arm_feature(env, ARM_FEATURE_V4T)) {
> +        env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
> +    }
>      env->regs[14] = env->regs[15] + offset;
>      env->regs[15] = addr;
>      env->interrupt_request |= CPU_INTERRUPT_EXITTB;
> diff --git a/target-arm/translate.c b/target-arm/translate.c
> index 33417e6..05a58d4 100644
> --- a/target-arm/translate.c
> +++ b/target-arm/translate.c
> @@ -34,6 +34,10 @@
>  #define GEN_HELPER 1
>  #include "helpers.h"
> 
> +#define ENABLE_ARCH_4T    arm_feature(env, ARM_FEATURE_V4T)
> +#define ENABLE_ARCH_5     arm_feature(env, ARM_FEATURE_V5)
> +/* currently all emulated v5 cores are also v5TE, so don't bother */
> +#define ENABLE_ARCH_5TE   arm_feature(env, ARM_FEATURE_V5)
>  #define ENABLE_ARCH_5J    0
>  #define ENABLE_ARCH_6     arm_feature(env, ARM_FEATURE_V6)
>  #define ENABLE_ARCH_6K   arm_feature(env, ARM_FEATURE_V6K)
> @@ -750,6 +754,20 @@ static inline void store_reg_bx(CPUState *env,
> DisasContext *s, }
>  }
> 
> +/* Variant of store_reg which uses branch&exchange logic when storing
> + * to r15 in ARM architecture v5T and above. This is used for storing
> + * the results of a LDR/LDM/POP into r15, and corresponds to the cases
> + * in the ARM ARM which use the LoadWritePC() pseudocode function. */
> +static inline void store_reg_from_load(CPUState *env, DisasContext *s,
> +                                int reg, TCGv var)
> +{
> +    if (reg == 15 && ENABLE_ARCH_5) {
> +        gen_bx(s, var);
> +    } else {
> +        store_reg(s, reg, var);
> +    }
> +}
> +
>  static inline TCGv gen_ld8s(TCGv addr, int index)
>  {
>      TCGv tmp = tcg_temp_new_i32();
> @@ -3443,6 +3461,10 @@ static uint32_t msr_mask(CPUState *env, DisasContext
> *s, int flags, int spsr) {
> 
>      /* Mask out undefined bits.  */
>      mask &= ~CPSR_RESERVED;
> +    if (!arm_feature(env, ARM_FEATURE_V4T))
> +        mask &= ~CPSR_T;
> +    if (!arm_feature(env, ARM_FEATURE_V5))
> +        mask &= ~CPSR_Q; /* V5TE in reality*/
>      if (!arm_feature(env, ARM_FEATURE_V6))
>          mask &= ~(CPSR_E | CPSR_GE);
>      if (!arm_feature(env, ARM_FEATURE_THUMB2))
> @@ -6145,6 +6167,12 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) goto illegal_op;
>      cond = insn >> 28;
>      if (cond == 0xf){
> +        /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we
> +         * choose to UNDEF. In ARMv5 and above the space is used
> +         * for miscellaneous unconditional instructions.
> +         */
> +        ARCH(5);
> +
>          /* Unconditional instructions.  */
>          if (((insn >> 25) & 7) == 1) {
>              /* NEON Data processing.  */
> @@ -6173,6 +6201,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) }
>              }
>              /* Otherwise PLD; v5TE+ */
> +            ARCH(5TE);
>              return;
>          }
>          if (((insn & 0x0f70f000) == 0x0450f000) ||
> @@ -6309,6 +6338,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) val += (offset << 2) | ((insn >> 23) & 2) | 1;
>              /* pipeline offset */
>              val += 4;
> +            /* protected by ARCH(5); above, near the start of uncond block
> */ gen_bx_im(s, val);
>              return;
>          } else if ((insn & 0x0e000f00) == 0x0c000100) {
> @@ -6320,6 +6350,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) }
>          } else if ((insn & 0x0fe00000) == 0x0c400000) {
>              /* Coprocessor double register transfer.  */
> +            ARCH(5TE);
>          } else if ((insn & 0x0f000010) == 0x0e000010) {
>              /* Additional coprocessor register transfer.  */
>          } else if ((insn & 0x0ff10020) == 0x01000000) {
> @@ -6420,10 +6451,12 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) case 0x1:
>              if (op1 == 1) {
>                  /* branch/exchange thumb (bx).  */
> +                ARCH(4T);
>                  tmp = load_reg(s, rm);
>                  gen_bx(s, tmp);
>              } else if (op1 == 3) {
>                  /* clz */
> +                ARCH(5);
>                  rd = (insn >> 12) & 0xf;
>                  tmp = load_reg(s, rm);
>                  gen_helper_clz(tmp, tmp);
> @@ -6446,6 +6479,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) if (op1 != 1)
>                goto illegal_op;
> 
> +            ARCH(5);
>              /* branch link/exchange thumb (blx) */
>              tmp = load_reg(s, rm);
>              tmp2 = tcg_temp_new_i32();
> @@ -6454,6 +6488,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) gen_bx(s, tmp);
>              break;
>          case 0x5: /* saturating add/subtract */
> +            ARCH(5TE);
>              rd = (insn >> 12) & 0xf;
>              rn = (insn >> 16) & 0xf;
>              tmp = load_reg(s, rm);
> @@ -6475,12 +6510,14 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) goto illegal_op;
>              }
>              /* bkpt */
> +            ARCH(5);
>              gen_exception_insn(s, 4, EXCP_BKPT);
>              break;
>          case 0x8: /* signed multiply */
>          case 0xa:
>          case 0xc:
>          case 0xe:
> +            ARCH(5TE);
>              rs = (insn >> 8) & 0xf;
>              rn = (insn >> 12) & 0xf;
>              rd = (insn >> 16) & 0xf;
> @@ -6876,6 +6913,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) }
>                      load = 1;
>                  } else if (sh & 2) {
> +                    ARCH(5TE);
>                      /* doubleword */
>                      if (sh & 1) {
>                          /* store */
> @@ -7216,10 +7254,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) }
>              if (insn & (1 << 20)) {
>                  /* Complete the load.  */
> -                if (rd == 15)
> -                    gen_bx(s, tmp);
> -                else
> -                    store_reg(s, rd, tmp);
> +                store_reg_from_load(env, s, rd, tmp);
>              }
>              break;
>          case 0x08:
> @@ -7272,9 +7307,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) if (insn & (1 << 20)) {
>                              /* load */
>                              tmp = gen_ld32(addr, IS_USER(s));
> -                            if (i == 15) {
> -                                gen_bx(s, tmp);
> -                            } else if (user) {
> +                            if (user) {
>                                  tmp2 = tcg_const_i32(i);
>                                  gen_helper_set_user_reg(tmp2, tmp);
>                                  tcg_temp_free_i32(tmp2);
> @@ -7283,7 +7316,7 @@ static void disas_arm_insn(CPUState * env,
> DisasContext *s) loaded_var = tmp;
>                                  loaded_base = 1;
>                              } else {
> -                                store_reg(s, i, tmp);
> +                                store_reg_from_load(env, s, i, tmp);
>                              }
>                          } else {
>                              /* store */
> @@ -7483,6 +7516,7 @@ static int disas_thumb2_insn(CPUState *env,
> DisasContext *s, uint16_t insn_hw1) 16-bit instructions to get correct
> prefetch abort behavior.  */ insn = insn_hw1;
>          if ((insn & (1 << 12)) == 0) {
> +            ARCH(5);
>              /* Second half of blx.  */
>              offset = ((insn & 0x7ff) << 1);
>              tmp = load_reg(s, 14);
> @@ -8079,6 +8113,7 @@ static int disas_thumb2_insn(CPUState *env,
> DisasContext *s, uint16_t insn_hw1) } else {
>                      /* blx */
>                      offset &= ~(uint32_t)2;
> +                    /* thumb2 bx, no need to check */
>                      gen_bx_im(s, offset);
>                  }
>              } else if (((insn >> 23) & 7) == 7) {
> @@ -8660,11 +8695,13 @@ static void disas_thumb_insn(CPUState *env,
> DisasContext *s) case 3:/* branch [and link] exchange thumb register */
> tmp = load_reg(s, rm);
>                  if (insn & (1 << 7)) {
> +                    ARCH(5);
>                      val = (uint32_t)s->pc | 1;
>                      tmp2 = tcg_temp_new_i32();
>                      tcg_gen_movi_i32(tmp2, val);
>                      store_reg(s, 14, tmp2);
>                  }
> +                /* already thumb, no need to check */
>                  gen_bx(s, tmp);
>                  break;
>              }
> @@ -9024,8 +9061,9 @@ static void disas_thumb_insn(CPUState *env,
> DisasContext *s) /* write back the new stack pointer */
>              store_reg(s, 13, addr);
>              /* set the new PC value */
> -            if ((insn & 0x0900) == 0x0900)
> -                gen_bx(s, tmp);
> +            if ((insn & 0x0900) == 0x0900) {
> +                store_reg_from_load(env, s, 15, tmp);
> +            }
>              break;
> 
>          case 1: case 3: case 9: case 11: /* czb */
> @@ -9056,6 +9094,7 @@ static void disas_thumb_insn(CPUState *env,
> DisasContext *s) break;
> 
>          case 0xe: /* bkpt */
> +            ARCH(5);
>              gen_exception_insn(s, 2, EXCP_BKPT);
>              break;
Dmitry Baryshkov April 11, 2011, 3:18 p.m. UTC | #6
Hi,

On 4/10/11, Marek Vasut <marek.vasut@gmail.com> wrote:
> On Monday 04 April 2011 15:38:44 Dmitry Eremin-Solenikov wrote:
>> Currently target-arm/ assumes at least ARMv5 core. Add support for
>> handling also ARMv4/ARMv4T. This changes the following instructions:
>>
>> BX(v4T and later)
>>
>> BKPT, BLX, CDP2, CLZ, LDC2, LDRD, MCRR, MCRR2, MRRC, MCRR, MRC2, MRRC,
>> MRRC2, PLD QADD, QDADD, QDSUB, QSUB, STRD, SMLAxy, SMLALxy, SMLAWxy,
>> SMULxy, SMULWxy, STC2 (v5 and later)
>>
>> All instructions that are "v5TE and later" are also bound to just v5, as
>> that's how it was before.
>>
>> This patch doesn _not_ include disabling of cp15 access and base-updated
>> data abort model (that will be required to emulate chips based on a
>> ARM7TDMI), because:
>> * no ARM7TDMI chips are currently emulated (or planned)
>
> Hi,
>
> this will come handy as I plan to add support for certain arm7tdmi chip
> (forward
> port at91sam7 patchset)

Please be sure to check for the base-updated/base-restored part of the original
patch as I'm nearly sure that it will be required for your chips.
diff mbox

Patch

diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1ae7982..e247a7a 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -360,7 +360,9 @@  enum arm_features {
     ARM_FEATURE_M, /* Microcontroller profile.  */
     ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling.  */
     ARM_FEATURE_THUMB2EE,
-    ARM_FEATURE_V7MP    /* v7 Multiprocessing Extensions */
+    ARM_FEATURE_V7MP,    /* v7 Multiprocessing Extensions */
+    ARM_FEATURE_V4T,
+    ARM_FEATURE_V5,
 };
 
 static inline int arm_feature(CPUARMState *env, int feature)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 6788a4c..ce9a9d8 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -48,17 +48,23 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
     env->cp15.c0_cpuid = id;
     switch (id) {
     case ARM_CPUID_ARM926:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_VFP);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
         env->cp15.c0_cachetype = 0x1dd20d2;
         env->cp15.c1_sys = 0x00090078;
         break;
     case ARM_CPUID_ARM946:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_MPU);
         env->cp15.c0_cachetype = 0x0f004006;
         env->cp15.c1_sys = 0x00000078;
         break;
     case ARM_CPUID_ARM1026:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_VFP);
         set_feature(env, ARM_FEATURE_AUXCR);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
@@ -67,6 +73,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         break;
     case ARM_CPUID_ARM1136_R2:
     case ARM_CPUID_ARM1136:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_VFP);
         set_feature(env, ARM_FEATURE_AUXCR);
@@ -79,6 +87,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c1_sys = 0x00050078;
         break;
     case ARM_CPUID_ARM11MPCORE:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_V6K);
         set_feature(env, ARM_FEATURE_VFP);
@@ -91,6 +101,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c0_cachetype = 0x1dd20d2;
         break;
     case ARM_CPUID_CORTEXA8:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_V6K);
         set_feature(env, ARM_FEATURE_V7);
@@ -113,6 +125,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c1_sys = 0x00c50078;
         break;
     case ARM_CPUID_CORTEXA9:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_V6K);
         set_feature(env, ARM_FEATURE_V7);
@@ -140,6 +154,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c1_sys = 0x00c50078;
         break;
     case ARM_CPUID_CORTEXM3:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_THUMB2);
         set_feature(env, ARM_FEATURE_V7);
@@ -147,6 +163,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         set_feature(env, ARM_FEATURE_DIV);
         break;
     case ARM_CPUID_ANY: /* For userspace emulation.  */
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_V6K);
         set_feature(env, ARM_FEATURE_V7);
@@ -161,6 +179,7 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         break;
     case ARM_CPUID_TI915T:
     case ARM_CPUID_TI925T:
+        set_feature(env, ARM_FEATURE_V4T);
         set_feature(env, ARM_FEATURE_OMAPCP);
         env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring.  */
         env->cp15.c0_cachetype = 0x5109149;
@@ -173,6 +192,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
     case ARM_CPUID_PXA260:
     case ARM_CPUID_PXA261:
     case ARM_CPUID_PXA262:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_XSCALE);
         /* JTAG_ID is ((id << 28) | 0x09265013) */
         env->cp15.c0_cachetype = 0xd172172;
@@ -184,6 +205,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
     case ARM_CPUID_PXA270_B1:
     case ARM_CPUID_PXA270_C0:
     case ARM_CPUID_PXA270_C5:
+        set_feature(env, ARM_FEATURE_V4T);
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_XSCALE);
         /* JTAG_ID is ((id << 28) | 0x09265013) */
         set_feature(env, ARM_FEATURE_IWMMXT);
@@ -856,7 +879,11 @@  void do_interrupt(CPUARMState *env)
     /* Switch to the new mode, and to the correct instruction set.  */
     env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
     env->uncached_cpsr |= mask;
-    env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
+    /* this is a lie, as the was no c1_sys on V4T/V5, but who cares
+     * and we should just guard the thumb mode on V4 */
+    if (arm_feature(env, ARM_FEATURE_V4T)) {
+        env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
+    }
     env->regs[14] = env->regs[15] + offset;
     env->regs[15] = addr;
     env->interrupt_request |= CPU_INTERRUPT_EXITTB;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 33417e6..05a58d4 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -34,6 +34,10 @@ 
 #define GEN_HELPER 1
 #include "helpers.h"
 
+#define ENABLE_ARCH_4T    arm_feature(env, ARM_FEATURE_V4T)
+#define ENABLE_ARCH_5     arm_feature(env, ARM_FEATURE_V5)
+/* currently all emulated v5 cores are also v5TE, so don't bother */
+#define ENABLE_ARCH_5TE   arm_feature(env, ARM_FEATURE_V5)
 #define ENABLE_ARCH_5J    0
 #define ENABLE_ARCH_6     arm_feature(env, ARM_FEATURE_V6)
 #define ENABLE_ARCH_6K   arm_feature(env, ARM_FEATURE_V6K)
@@ -750,6 +754,20 @@  static inline void store_reg_bx(CPUState *env, DisasContext *s,
     }
 }
 
+/* Variant of store_reg which uses branch&exchange logic when storing
+ * to r15 in ARM architecture v5T and above. This is used for storing
+ * the results of a LDR/LDM/POP into r15, and corresponds to the cases
+ * in the ARM ARM which use the LoadWritePC() pseudocode function. */
+static inline void store_reg_from_load(CPUState *env, DisasContext *s,
+                                int reg, TCGv var)
+{
+    if (reg == 15 && ENABLE_ARCH_5) {
+        gen_bx(s, var);
+    } else {
+        store_reg(s, reg, var);
+    }
+}
+
 static inline TCGv gen_ld8s(TCGv addr, int index)
 {
     TCGv tmp = tcg_temp_new_i32();
@@ -3443,6 +3461,10 @@  static uint32_t msr_mask(CPUState *env, DisasContext *s, int flags, int spsr) {
 
     /* Mask out undefined bits.  */
     mask &= ~CPSR_RESERVED;
+    if (!arm_feature(env, ARM_FEATURE_V4T))
+        mask &= ~CPSR_T;
+    if (!arm_feature(env, ARM_FEATURE_V5))
+        mask &= ~CPSR_Q; /* V5TE in reality*/
     if (!arm_feature(env, ARM_FEATURE_V6))
         mask &= ~(CPSR_E | CPSR_GE);
     if (!arm_feature(env, ARM_FEATURE_THUMB2))
@@ -6145,6 +6167,12 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
         goto illegal_op;
     cond = insn >> 28;
     if (cond == 0xf){
+        /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we
+         * choose to UNDEF. In ARMv5 and above the space is used
+         * for miscellaneous unconditional instructions.
+         */
+        ARCH(5);
+
         /* Unconditional instructions.  */
         if (((insn >> 25) & 7) == 1) {
             /* NEON Data processing.  */
@@ -6173,6 +6201,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                 }
             }
             /* Otherwise PLD; v5TE+ */
+            ARCH(5TE);
             return;
         }
         if (((insn & 0x0f70f000) == 0x0450f000) ||
@@ -6309,6 +6338,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
             val += (offset << 2) | ((insn >> 23) & 2) | 1;
             /* pipeline offset */
             val += 4;
+            /* protected by ARCH(5); above, near the start of uncond block */
             gen_bx_im(s, val);
             return;
         } else if ((insn & 0x0e000f00) == 0x0c000100) {
@@ -6320,6 +6350,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
             }
         } else if ((insn & 0x0fe00000) == 0x0c400000) {
             /* Coprocessor double register transfer.  */
+            ARCH(5TE);
         } else if ((insn & 0x0f000010) == 0x0e000010) {
             /* Additional coprocessor register transfer.  */
         } else if ((insn & 0x0ff10020) == 0x01000000) {
@@ -6420,10 +6451,12 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
         case 0x1:
             if (op1 == 1) {
                 /* branch/exchange thumb (bx).  */
+                ARCH(4T);
                 tmp = load_reg(s, rm);
                 gen_bx(s, tmp);
             } else if (op1 == 3) {
                 /* clz */
+                ARCH(5);
                 rd = (insn >> 12) & 0xf;
                 tmp = load_reg(s, rm);
                 gen_helper_clz(tmp, tmp);
@@ -6446,6 +6479,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
             if (op1 != 1)
               goto illegal_op;
 
+            ARCH(5);
             /* branch link/exchange thumb (blx) */
             tmp = load_reg(s, rm);
             tmp2 = tcg_temp_new_i32();
@@ -6454,6 +6488,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
             gen_bx(s, tmp);
             break;
         case 0x5: /* saturating add/subtract */
+            ARCH(5TE);
             rd = (insn >> 12) & 0xf;
             rn = (insn >> 16) & 0xf;
             tmp = load_reg(s, rm);
@@ -6475,12 +6510,14 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                 goto illegal_op;
             }
             /* bkpt */
+            ARCH(5);
             gen_exception_insn(s, 4, EXCP_BKPT);
             break;
         case 0x8: /* signed multiply */
         case 0xa:
         case 0xc:
         case 0xe:
+            ARCH(5TE);
             rs = (insn >> 8) & 0xf;
             rn = (insn >> 12) & 0xf;
             rd = (insn >> 16) & 0xf;
@@ -6876,6 +6913,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                     }
                     load = 1;
                 } else if (sh & 2) {
+                    ARCH(5TE);
                     /* doubleword */
                     if (sh & 1) {
                         /* store */
@@ -7216,10 +7254,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
             }
             if (insn & (1 << 20)) {
                 /* Complete the load.  */
-                if (rd == 15)
-                    gen_bx(s, tmp);
-                else
-                    store_reg(s, rd, tmp);
+                store_reg_from_load(env, s, rd, tmp);
             }
             break;
         case 0x08:
@@ -7272,9 +7307,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                         if (insn & (1 << 20)) {
                             /* load */
                             tmp = gen_ld32(addr, IS_USER(s));
-                            if (i == 15) {
-                                gen_bx(s, tmp);
-                            } else if (user) {
+                            if (user) {
                                 tmp2 = tcg_const_i32(i);
                                 gen_helper_set_user_reg(tmp2, tmp);
                                 tcg_temp_free_i32(tmp2);
@@ -7283,7 +7316,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                                 loaded_var = tmp;
                                 loaded_base = 1;
                             } else {
-                                store_reg(s, i, tmp);
+                                store_reg_from_load(env, s, i, tmp);
                             }
                         } else {
                             /* store */
@@ -7483,6 +7516,7 @@  static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
            16-bit instructions to get correct prefetch abort behavior.  */
         insn = insn_hw1;
         if ((insn & (1 << 12)) == 0) {
+            ARCH(5);
             /* Second half of blx.  */
             offset = ((insn & 0x7ff) << 1);
             tmp = load_reg(s, 14);
@@ -8079,6 +8113,7 @@  static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                 } else {
                     /* blx */
                     offset &= ~(uint32_t)2;
+                    /* thumb2 bx, no need to check */
                     gen_bx_im(s, offset);
                 }
             } else if (((insn >> 23) & 7) == 7) {
@@ -8660,11 +8695,13 @@  static void disas_thumb_insn(CPUState *env, DisasContext *s)
             case 3:/* branch [and link] exchange thumb register */
                 tmp = load_reg(s, rm);
                 if (insn & (1 << 7)) {
+                    ARCH(5);
                     val = (uint32_t)s->pc | 1;
                     tmp2 = tcg_temp_new_i32();
                     tcg_gen_movi_i32(tmp2, val);
                     store_reg(s, 14, tmp2);
                 }
+                /* already thumb, no need to check */
                 gen_bx(s, tmp);
                 break;
             }
@@ -9024,8 +9061,9 @@  static void disas_thumb_insn(CPUState *env, DisasContext *s)
             /* write back the new stack pointer */
             store_reg(s, 13, addr);
             /* set the new PC value */
-            if ((insn & 0x0900) == 0x0900)
-                gen_bx(s, tmp);
+            if ((insn & 0x0900) == 0x0900) {
+                store_reg_from_load(env, s, 15, tmp);
+            }
             break;
 
         case 1: case 3: case 9: case 11: /* czb */
@@ -9056,6 +9094,7 @@  static void disas_thumb_insn(CPUState *env, DisasContext *s)
             break;
 
         case 0xe: /* bkpt */
+            ARCH(5);
             gen_exception_insn(s, 2, EXCP_BKPT);
             break;