diff options
Diffstat (limited to 'target-arm/translate.c')
-rw-r--r-- | target-arm/translate.c | 474 |
1 files changed, 150 insertions, 324 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c index 437d9dbf0e..a2a0ecddad 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -2439,226 +2439,6 @@ static int disas_dsp_insn(CPUARMState *env, DisasContext *s, uint32_t insn) return 1; } -/* Disassemble system coprocessor instruction. Return nonzero if - instruction is not defined. */ -static int disas_cp_insn(CPUARMState *env, DisasContext *s, uint32_t insn) -{ - TCGv tmp, tmp2; - uint32_t rd = (insn >> 12) & 0xf; - uint32_t cp = (insn >> 8) & 0xf; - if (IS_USER(s)) { - return 1; - } - - if (insn & ARM_CP_RW_BIT) { - if (!env->cp[cp].cp_read) - return 1; - gen_set_pc_im(s->pc); - tmp = tcg_temp_new_i32(); - tmp2 = tcg_const_i32(insn); - gen_helper_get_cp(tmp, cpu_env, tmp2); - tcg_temp_free(tmp2); - store_reg(s, rd, tmp); - } else { - if (!env->cp[cp].cp_write) - return 1; - gen_set_pc_im(s->pc); - tmp = load_reg(s, rd); - tmp2 = tcg_const_i32(insn); - gen_helper_set_cp(cpu_env, tmp2, tmp); - tcg_temp_free(tmp2); - tcg_temp_free_i32(tmp); - } - return 0; -} - -static int cp15_user_ok(CPUARMState *env, uint32_t insn) -{ - int cpn = (insn >> 16) & 0xf; - int cpm = insn & 0xf; - int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38); - - if (arm_feature(env, ARM_FEATURE_V7) && cpn == 9) { - /* Performance monitor registers fall into three categories: - * (a) always UNDEF in usermode - * (b) UNDEF only if PMUSERENR.EN is 0 - * (c) always read OK and UNDEF on write (PMUSERENR only) - */ - if ((cpm == 12 && (op < 6)) || - (cpm == 13 && (op < 3))) { - return env->cp15.c9_pmuserenr; - } else if (cpm == 14 && op == 0 && (insn & ARM_CP_RW_BIT)) { - /* PMUSERENR, read only */ - return 1; - } - return 0; - } - - if (cpn == 13 && cpm == 0) { - /* TLS register. */ - if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT))) - return 1; - } - return 0; -} - -static int cp15_tls_load_store(CPUARMState *env, DisasContext *s, uint32_t insn, uint32_t rd) -{ - TCGv tmp; - int cpn = (insn >> 16) & 0xf; - int cpm = insn & 0xf; - int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38); - - if (!arm_feature(env, ARM_FEATURE_V6K)) - return 0; - - if (!(cpn == 13 && cpm == 0)) - return 0; - - if (insn & ARM_CP_RW_BIT) { - switch (op) { - case 2: - tmp = load_cpu_field(cp15.c13_tls1); - break; - case 3: - tmp = load_cpu_field(cp15.c13_tls2); - break; - case 4: - tmp = load_cpu_field(cp15.c13_tls3); - break; - default: - return 0; - } - store_reg(s, rd, tmp); - - } else { - tmp = load_reg(s, rd); - switch (op) { - case 2: - store_cpu_field(tmp, cp15.c13_tls1); - break; - case 3: - store_cpu_field(tmp, cp15.c13_tls2); - break; - case 4: - store_cpu_field(tmp, cp15.c13_tls3); - break; - default: - tcg_temp_free_i32(tmp); - return 0; - } - } - return 1; -} - -/* Disassemble system coprocessor (cp15) instruction. Return nonzero if - instruction is not defined. */ -static int disas_cp15_insn(CPUARMState *env, DisasContext *s, uint32_t insn) -{ - uint32_t rd; - TCGv tmp, tmp2; - - /* M profile cores use memory mapped registers instead of cp15. */ - if (arm_feature(env, ARM_FEATURE_M)) - return 1; - - if ((insn & (1 << 25)) == 0) { - if (insn & (1 << 20)) { - /* mrrc */ - return 1; - } - /* mcrr. Used for block cache operations, so implement as no-op. */ - return 0; - } - if ((insn & (1 << 4)) == 0) { - /* cdp */ - return 1; - } - /* We special case a number of cp15 instructions which were used - * for things which are real instructions in ARMv7. This allows - * them to work in linux-user mode which doesn't provide functional - * get_cp15/set_cp15 helpers, and is more efficient anyway. - */ - switch ((insn & 0x0fff0fff)) { - case 0x0e070f90: - /* 0,c7,c0,4: Standard v6 WFI (also used in some pre-v6 cores). - * In v7, this must NOP. - */ - if (IS_USER(s)) { - return 1; - } - if (!arm_feature(env, ARM_FEATURE_V7)) { - /* Wait for interrupt. */ - gen_set_pc_im(s->pc); - s->is_jmp = DISAS_WFI; - } - return 0; - case 0x0e070f58: - /* 0,c7,c8,2: Not all pre-v6 cores implemented this WFI, - * so this is slightly over-broad. - */ - if (!IS_USER(s) && !arm_feature(env, ARM_FEATURE_V6)) { - /* Wait for interrupt. */ - gen_set_pc_im(s->pc); - s->is_jmp = DISAS_WFI; - return 0; - } - /* Otherwise continue to handle via helper function. - * In particular, on v7 and some v6 cores this is one of - * the VA-PA registers. - */ - break; - case 0x0e070f3d: - /* 0,c7,c13,1: prefetch-by-MVA in v6, NOP in v7 */ - if (arm_feature(env, ARM_FEATURE_V6)) { - return IS_USER(s) ? 1 : 0; - } - break; - case 0x0e070f95: /* 0,c7,c5,4 : ISB */ - case 0x0e070f9a: /* 0,c7,c10,4: DSB */ - case 0x0e070fba: /* 0,c7,c10,5: DMB */ - /* Barriers in both v6 and v7 */ - if (arm_feature(env, ARM_FEATURE_V6)) { - return 0; - } - break; - default: - break; - } - - if (IS_USER(s) && !cp15_user_ok(env, insn)) { - return 1; - } - - rd = (insn >> 12) & 0xf; - - if (cp15_tls_load_store(env, s, insn, rd)) - return 0; - - tmp2 = tcg_const_i32(insn); - if (insn & ARM_CP_RW_BIT) { - tmp = tcg_temp_new_i32(); - gen_helper_get_cp15(tmp, cpu_env, tmp2); - /* If the destination register is r15 then sets condition codes. */ - if (rd != 15) - store_reg(s, rd, tmp); - else - tcg_temp_free_i32(tmp); - } else { - tmp = load_reg(s, rd); - gen_helper_set_cp15(cpu_env, tmp2, tmp); - tcg_temp_free_i32(tmp); - /* Normally we would always end the TB here, but Linux - * arch/arm/mach-pxa/sleep.S expects two instructions following - * an MMU enable to execute from cache. Imitate this behaviour. */ - if (!arm_feature(env, ARM_FEATURE_XSCALE) || - (insn & 0x0fff0fff) != 0x0e010f10) - gen_lookup_tb(s); - } - tcg_temp_free_i32(tmp2); - return 0; -} - #define VFP_REG_SHR(x, n) (((n) > 0) ? (x) >> (n) : (x) << -(n)) #define VFP_SREG(insn, bigbit, smallbit) \ ((VFP_REG_SHR(insn, bigbit - 1) & 0x1e) | (((insn) >> (smallbit)) & 1)) @@ -6388,104 +6168,18 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins return 0; } -static int disas_cp14_read(CPUARMState * env, DisasContext *s, uint32_t insn) -{ - int crn = (insn >> 16) & 0xf; - int crm = insn & 0xf; - int op1 = (insn >> 21) & 7; - int op2 = (insn >> 5) & 7; - int rt = (insn >> 12) & 0xf; - TCGv tmp; - - /* Minimal set of debug registers, since we don't support debug */ - if (op1 == 0 && crn == 0 && op2 == 0) { - switch (crm) { - case 0: - /* DBGDIDR: just RAZ. In particular this means the - * "debug architecture version" bits will read as - * a reserved value, which should cause Linux to - * not try to use the debug hardware. - */ - tmp = tcg_const_i32(0); - store_reg(s, rt, tmp); - return 0; - case 1: - case 2: - /* DBGDRAR and DBGDSAR: v7 only. Always RAZ since we - * don't implement memory mapped debug components - */ - if (ENABLE_ARCH_7) { - tmp = tcg_const_i32(0); - store_reg(s, rt, tmp); - return 0; - } - break; - default: - break; - } - } - - if (arm_feature(env, ARM_FEATURE_THUMB2EE)) { - if (op1 == 6 && crn == 0 && crm == 0 && op2 == 0) { - /* TEECR */ - if (IS_USER(s)) - return 1; - tmp = load_cpu_field(teecr); - store_reg(s, rt, tmp); - return 0; - } - if (op1 == 6 && crn == 1 && crm == 0 && op2 == 0) { - /* TEEHBR */ - if (IS_USER(s) && (env->teecr & 1)) - return 1; - tmp = load_cpu_field(teehbr); - store_reg(s, rt, tmp); - return 0; - } - } - return 1; -} - -static int disas_cp14_write(CPUARMState * env, DisasContext *s, uint32_t insn) -{ - int crn = (insn >> 16) & 0xf; - int crm = insn & 0xf; - int op1 = (insn >> 21) & 7; - int op2 = (insn >> 5) & 7; - int rt = (insn >> 12) & 0xf; - TCGv tmp; - - if (arm_feature(env, ARM_FEATURE_THUMB2EE)) { - if (op1 == 6 && crn == 0 && crm == 0 && op2 == 0) { - /* TEECR */ - if (IS_USER(s)) - return 1; - tmp = load_reg(s, rt); - gen_helper_set_teecr(cpu_env, tmp); - tcg_temp_free_i32(tmp); - return 0; - } - if (op1 == 6 && crn == 1 && crm == 0 && op2 == 0) { - /* TEEHBR */ - if (IS_USER(s) && (env->teecr & 1)) - return 1; - tmp = load_reg(s, rt); - store_cpu_field(tmp, teehbr); - return 0; - } - } - return 1; -} - static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) { - int cpnum; + int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2; + const ARMCPRegInfo *ri; + ARMCPU *cpu = arm_env_get_cpu(env); cpnum = (insn >> 8) & 0xf; if (arm_feature(env, ARM_FEATURE_XSCALE) && ((env->cp15.c15_cpar ^ 0x3fff) & (1 << cpnum))) return 1; + /* First check for coprocessor space used for actual instructions */ switch (cpnum) { case 0: case 1: @@ -6498,22 +6192,154 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn) case 10: case 11: return disas_vfp_insn (env, s, insn); - case 14: - /* Coprocessors 7-15 are architecturally reserved by ARM. - Unfortunately Intel decided to ignore this. */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) - goto board; - if (insn & (1 << 20)) - return disas_cp14_read(env, s, insn); - else - return disas_cp14_write(env, s, insn); - case 15: - return disas_cp15_insn (env, s, insn); default: - board: - /* Unknown coprocessor. See if the board has hooked it. */ - return disas_cp_insn (env, s, insn); + break; } + + /* Otherwise treat as a generic register access */ + is64 = (insn & (1 << 25)) == 0; + if (!is64 && ((insn & (1 << 4)) == 0)) { + /* cdp */ + return 1; + } + + crm = insn & 0xf; + if (is64) { + crn = 0; + opc1 = (insn >> 4) & 0xf; + opc2 = 0; + rt2 = (insn >> 16) & 0xf; + } else { + crn = (insn >> 16) & 0xf; + opc1 = (insn >> 21) & 7; + opc2 = (insn >> 5) & 7; + rt2 = 0; + } + isread = (insn >> 20) & 1; + rt = (insn >> 12) & 0xf; + + ri = get_arm_cp_reginfo(cpu, + ENCODE_CP_REG(cpnum, is64, crn, crm, opc1, opc2)); + if (ri) { + /* Check access permissions */ + if (!cp_access_ok(env, ri, isread)) { + return 1; + } + + /* Handle special cases first */ + switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) { + case ARM_CP_NOP: + return 0; + case ARM_CP_WFI: + if (isread) { + return 1; + } + gen_set_pc_im(s->pc); + s->is_jmp = DISAS_WFI; + break; + default: + break; + } + + if (isread) { + /* Read */ + if (is64) { + TCGv_i64 tmp64; + TCGv_i32 tmp; + if (ri->type & ARM_CP_CONST) { + tmp64 = tcg_const_i64(ri->resetvalue); + } else if (ri->readfn) { + TCGv_ptr tmpptr; + gen_set_pc_im(s->pc); + tmp64 = tcg_temp_new_i64(); + tmpptr = tcg_const_ptr(ri); + gen_helper_get_cp_reg64(tmp64, cpu_env, tmpptr); + tcg_temp_free_ptr(tmpptr); + } else { + tmp64 = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp64, cpu_env, ri->fieldoffset); + } + tmp = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp, tmp64); + store_reg(s, rt, tmp); + tcg_gen_shri_i64(tmp64, tmp64, 32); + tcg_gen_trunc_i64_i32(tmp, tmp64); + store_reg(s, rt2, tmp); + } else { + TCGv tmp; + if (ri->type & ARM_CP_CONST) { + tmp = tcg_const_i32(ri->resetvalue); + } else if (ri->readfn) { + TCGv_ptr tmpptr; + gen_set_pc_im(s->pc); + tmp = tcg_temp_new_i32(); + tmpptr = tcg_const_ptr(ri); + gen_helper_get_cp_reg(tmp, cpu_env, tmpptr); + tcg_temp_free_ptr(tmpptr); + } else { + tmp = load_cpu_offset(ri->fieldoffset); + } + if (rt == 15) { + /* Destination register of r15 for 32 bit loads sets + * the condition codes from the high 4 bits of the value + */ + gen_set_nzcv(tmp); + tcg_temp_free_i32(tmp); + } else { + store_reg(s, rt, tmp); + } + } + } else { + /* Write */ + if (ri->type & ARM_CP_CONST) { + /* If not forbidden by access permissions, treat as WI */ + return 0; + } + + if (is64) { + TCGv tmplo, tmphi; + TCGv_i64 tmp64 = tcg_temp_new_i64(); + tmplo = load_reg(s, rt); + tmphi = load_reg(s, rt2); + tcg_gen_concat_i32_i64(tmp64, tmplo, tmphi); + tcg_temp_free_i32(tmplo); + tcg_temp_free_i32(tmphi); + if (ri->writefn) { + TCGv_ptr tmpptr = tcg_const_ptr(ri); + gen_set_pc_im(s->pc); + gen_helper_set_cp_reg64(cpu_env, tmpptr, tmp64); + tcg_temp_free_ptr(tmpptr); + } else { + tcg_gen_st_i64(tmp64, cpu_env, ri->fieldoffset); + } + tcg_temp_free_i64(tmp64); + } else { + if (ri->writefn) { + TCGv tmp; + TCGv_ptr tmpptr; + gen_set_pc_im(s->pc); + tmp = load_reg(s, rt); + tmpptr = tcg_const_ptr(ri); + gen_helper_set_cp_reg(cpu_env, tmpptr, tmp); + tcg_temp_free_ptr(tmpptr); + tcg_temp_free_i32(tmp); + } else { + TCGv tmp = load_reg(s, rt); + store_cpu_offset(tmp, ri->fieldoffset); + } + } + /* We default to ending the TB on a coprocessor register write, + * but allow this to be suppressed by the register definition + * (usually only necessary to work around guest bugs). + */ + if (!(ri->type & ARM_CP_SUPPRESS_TB_END)) { + gen_lookup_tb(s); + } + } + return 0; + } + + return 1; } |