aboutsummaryrefslogtreecommitdiff
path: root/target-arm/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'target-arm/translate.c')
-rw-r--r--target-arm/translate.c158
1 files changed, 157 insertions, 1 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 81725d1687..c63781cf35 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -6478,13 +6478,15 @@ static int disas_cp14_write(CPUARMState * env, DisasContext *s, uint32_t insn)
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;
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:
@@ -6497,6 +6499,160 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
case 10:
case 11:
return disas_vfp_insn (env, s, insn);
+ default:
+ break;
+ }
+
+ /* Otherwise treat as a generic register access */
+ is64 = (insn & (1 << 25)) == 0;
+ // TODO should we be decoding anything else here?
+ if (!is64 && ((insn & (1 << 4)) == 0)) {
+ /* cdp */
+ return 1;
+ }
+
+ crm = insn & 0xf;
+ if (is64) {
+ crn = 0;
+ opc1 = (insn >> 4) & 0xf;
+ opc2 = 0;
+ } else {
+ crn = (insn >> 16) & 0xf;
+ opc1 = (insn >> 21) & 7;
+ opc2 = (insn >> 5) & 7;
+ }
+ isread = (insn >> 20) & 1;
+ rt = (insn >> 12) & 0xf;
+ rt2 = (insn >> 16) & 0xf;
+
+ ri = get_arm_cp_reginfo(env,
+ 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) {
+ 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);
+ }
+ }
+ /* 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.
+ * FIXME this should be done via some kind of do/don't end TB
+ * flag in the reginfo struct.
+ */
+ if (!arm_feature(env, ARM_FEATURE_XSCALE) ||
+ (insn & 0x0fff0fff) != 0x0e010f10) {
+ gen_lookup_tb(s);
+ }
+ }
+ return 0;
+ }
+
+ /* Fallback code: handle coprocessor registers not yet converted
+ * to ARMCPRegInfo.
+ */
+ switch (cpnum) {
case 14:
/* Coprocessors 7-15 are architecturally reserved by ARM.
Unfortunately Intel decided to ignore this. */