From fc4cc9d95fbf37c57d21b35b1412f5a4263ef4f2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 26 Feb 2021 10:56:57 -0500 Subject: [PATCH] target/arm: Convert A32 coprocessor insns to decodetree Convert the A32 coprocessor instructions to decodetree. Note that this corrects an underdecoding: for the 64-bit access case (MRRC/MCRR) we did not check that bits [24:21] were 0b0010, so we would incorrectly treat LDC/STC as MRRC/MCRR rather than UNDEFing them. The decodetree versions of these insns assume the coprocessor is in the range 0..7 or 14..15. This is architecturally sensible (as per the comments) and OK in practice for QEMU because the only uses of the ARMCPRegInfo infrastructure we have that aren't for coprocessors 14 or 15 are the pxa2xx use of coprocessor 6. We add an assertion to the define_one_arm_cp_reg_with_opaque() function to catch any accidental future attempts to use it to define coprocessor registers for invalid coprocessors. Backports commit cd8be50e58f63413c033531d3273c0e44851684f from qemu --- qemu/target/arm/a32.decode | 19 ++++++++++ qemu/target/arm/helper.c | 29 +++++++++++++++ qemu/target/arm/translate.c | 73 ++++++++++++++++++++++++++++++++----- 3 files changed, 111 insertions(+), 10 deletions(-) diff --git a/qemu/target/arm/a32.decode b/qemu/target/arm/a32.decode index 0bd952c0..4dfd9139 100644 --- a/qemu/target/arm/a32.decode +++ b/qemu/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/qemu/target/arm/helper.c b/qemu/target/arm/helper.c index a1e7a0a9..a594650e 100644 --- a/qemu/target/arm/helper.c +++ b/qemu/target/arm/helper.c @@ -8102,6 +8102,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/qemu/target/arm/translate.c b/qemu/target/arm/translate.c index cbe54bfb..20bdc6f5 100644 --- a/qemu/target/arm/translate.c +++ b/qemu/target/arm/translate.c @@ -5317,6 +5317,68 @@ static int t16_pop_list(DisasContext *s, int x) #include "decode-t32.inc.c" #include "decode-t16.inc.c" +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(DisasContext *s, TCGv_i32 dst, TCGv_i32 a, TCGv_i32 b) { @@ -8552,16 +8614,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int 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: