aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--target/arm/a32.decode19
-rw-r--r--target/arm/helper.c29
-rw-r--r--target/arm/translate.c74
3 files changed, 111 insertions, 11 deletions
diff --git a/target/arm/a32.decode b/target/arm/a32.decode
index 0bd952c069..4dfd9139bf 100644
--- a/target/arm/a32.decode
+++ b/target/arm/a32.decode
@@ -47,6 +47,8 @@
&bfi rd rn lsb msb
&sat rd rn satimm imm sh
&pkh rd rn rm imm tb
+&mcr cp opc1 crn crm opc2 rt
+&mcrr cp opc1 crm rt rt2
# Data-processing (register)
@@ -529,6 +531,23 @@ LDM_a32 ---- 100 b:1 i:1 u:1 w:1 1 rn:4 list:16 &ldst_block
B .... 1010 ........................ @branch
BL .... 1011 ........................ @branch
+# Coprocessor instructions
+
+# We decode MCR, MCR, MRRC and MCRR only, because for QEMU the
+# other coprocessor instructions always UNDEF.
+# The trans_ functions for these will ignore cp values 8..13 for v7 or
+# earlier, and 0..13 for v8 and later, because those areas of the
+# encoding space may be used for other things, such as VFP or Neon.
+
+@mcr ---- .... opc1:3 . crn:4 rt:4 cp:4 opc2:3 . crm:4 &mcr
+@mcrr ---- .... .... rt2:4 rt:4 cp:4 opc1:4 crm:4 &mcrr
+
+MCRR .... 1100 0100 .... .... .... .... .... @mcrr
+MRRC .... 1100 0101 .... .... .... .... .... @mcrr
+
+MCR .... 1110 ... 0 .... .... .... ... 1 .... @mcr
+MRC .... 1110 ... 1 .... .... .... ... 1 .... @mcr
+
# Supervisor call
SVC ---- 1111 imm:24 &i
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 455c92b891..6b4f0eb533 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -8462,6 +8462,35 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu,
assert((r->state != ARM_CP_STATE_AA32) || (r->opc0 == 0));
/* AArch64 regs are all 64 bit so ARM_CP_64BIT is meaningless */
assert((r->state != ARM_CP_STATE_AA64) || !(r->type & ARM_CP_64BIT));
+ /*
+ * This API is only for Arm's system coprocessors (14 and 15) or
+ * (M-profile or v7A-and-earlier only) for implementation defined
+ * coprocessors in the range 0..7. Our decode assumes this, since
+ * 8..13 can be used for other insns including VFP and Neon. See
+ * valid_cp() in translate.c. Assert here that we haven't tried
+ * to use an invalid coprocessor number.
+ */
+ switch (r->state) {
+ case ARM_CP_STATE_BOTH:
+ /* 0 has a special meaning, but otherwise the same rules as AA32. */
+ if (r->cp == 0) {
+ break;
+ }
+ /* fall through */
+ case ARM_CP_STATE_AA32:
+ if (arm_feature(&cpu->env, ARM_FEATURE_V8) &&
+ !arm_feature(&cpu->env, ARM_FEATURE_M)) {
+ assert(r->cp >= 14 && r->cp <= 15);
+ } else {
+ assert(r->cp < 8 || (r->cp >= 14 && r->cp <= 15));
+ }
+ break;
+ case ARM_CP_STATE_AA64:
+ assert(r->cp == 0 || r->cp == CP_REG_ARM64_SYSREG_CP);
+ break;
+ default:
+ g_assert_not_reached();
+ }
/* The AArch64 pseudocode CheckSystemAccess() specifies that op1
* encodes a minimum access level for the register. We roll this
* runtime check into our general permission check code, so check
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 01591f0e32..6ee920eec5 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -5237,6 +5237,68 @@ static int t16_pop_list(DisasContext *s, int x)
#include "decode-t32.c.inc"
#include "decode-t16.c.inc"
+static bool valid_cp(DisasContext *s, int cp)
+{
+ /*
+ * Return true if this coprocessor field indicates something
+ * that's really a possible coprocessor.
+ * For v7 and earlier, coprocessors 8..15 were reserved for Arm use,
+ * and of those only cp14 and cp15 were used for registers.
+ * cp10 and cp11 were used for VFP and Neon, whose decode is
+ * dealt with elsewhere. With the advent of fp16, cp9 is also
+ * now part of VFP.
+ * For v8A and later, the encoding has been tightened so that
+ * only cp14 and cp15 are valid, and other values aren't considered
+ * to be in the coprocessor-instruction space at all. v8M still
+ * permits coprocessors 0..7.
+ */
+ if (arm_dc_feature(s, ARM_FEATURE_V8) &&
+ !arm_dc_feature(s, ARM_FEATURE_M)) {
+ return cp >= 14;
+ }
+ return cp < 8 || cp >= 14;
+}
+
+static bool trans_MCR(DisasContext *s, arg_MCR *a)
+{
+ if (!valid_cp(s, a->cp)) {
+ return false;
+ }
+ do_coproc_insn(s, a->cp, false, a->opc1, a->crn, a->crm, a->opc2,
+ false, a->rt, 0);
+ return true;
+}
+
+static bool trans_MRC(DisasContext *s, arg_MRC *a)
+{
+ if (!valid_cp(s, a->cp)) {
+ return false;
+ }
+ do_coproc_insn(s, a->cp, false, a->opc1, a->crn, a->crm, a->opc2,
+ true, a->rt, 0);
+ return true;
+}
+
+static bool trans_MCRR(DisasContext *s, arg_MCRR *a)
+{
+ if (!valid_cp(s, a->cp)) {
+ return false;
+ }
+ do_coproc_insn(s, a->cp, true, a->opc1, 0, a->crm, 0,
+ false, a->rt, a->rt2);
+ return true;
+}
+
+static bool trans_MRRC(DisasContext *s, arg_MRRC *a)
+{
+ if (!valid_cp(s, a->cp)) {
+ return false;
+ }
+ do_coproc_insn(s, a->cp, true, a->opc1, 0, a->crm, 0,
+ true, a->rt, a->rt2);
+ return true;
+}
+
/* Helpers to swap operands for reverse-subtract. */
static void gen_rsb(TCGv_i32 dst, TCGv_i32 a, TCGv_i32 b)
{
@@ -8293,17 +8355,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
disas_xscale_insn(s, insn);
break;
}
-
- if ((cpnum & 0xe) == 10) {
- /* VFP, but failed disas_vfp. */
- goto illegal_op;
- }
-
- if (disas_coproc_insn(s, insn)) {
- /* Coprocessor. */
- goto illegal_op;
- }
- break;
+ /* fall through */
}
default:
illegal_op: