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;
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);
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);
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);
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);
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);
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);
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;
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;
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);
/* 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;
#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)
}
}
+/* 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();
/* 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))
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. */
}
}
/* Otherwise PLD; v5TE+ */
+ ARCH(5TE);
return;
}
if (((insn & 0x0f70f000) == 0x0450f000) ||
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) {
}
} 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) {
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);
if (op1 != 1)
goto illegal_op;
+ ARCH(5);
/* branch link/exchange thumb (blx) */
tmp = load_reg(s, rm);
tmp2 = tcg_temp_new_i32();
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);
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;
}
load = 1;
} else if (sh & 2) {
+ ARCH(5TE);
/* doubleword */
if (sh & 1) {
/* store */
}
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:
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);
loaded_var = tmp;
loaded_base = 1;
} else {
- store_reg(s, i, tmp);
+ store_reg_from_load(env, s, i, tmp);
}
} else {
/* store */
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);
} else {
/* blx */
offset &= ~(uint32_t)2;
+ /* thumb2 bx, no need to check */
gen_bx_im(s, offset);
}
} else if (((insn >> 23) & 7) == 7) {
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;
}
/* 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 */
break;
case 0xe: /* bkpt */
+ ARCH(5);
gen_exception_insn(s, 2, EXCP_BKPT);
break;