From 7a482198b2dfb2bad5030eb183a3566c81ad4d80 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 14 Feb 2018 20:35:14 -0500 Subject: [PATCH] target-arm: Add debug check for mismatched cpreg resets It's easy to accidentally define two cpregs which both try to reset the same underlying state field (for instance a clash between an AArch64 EL3 definition and an AArch32 banked register definition). if the two definitions disagree about the reset value then the result is dependent on which one happened to be reached last in the hashtable enumeration. Add a consistency check to detect and assert in these cases: after reset, we run a second pass where we check that the reset operation doesn't change the value of the register. Backports commit 49a661910c1374858602a3002b67115893673c25 from qemu --- qemu/target-arm/cpu.c | 22 ++++++++++++++++++++++ qemu/target-arm/cpu.h | 3 +++ qemu/target-arm/helper.c | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/qemu/target-arm/cpu.c b/qemu/target-arm/cpu.c index fc8fe154..85ae20cf 100644 --- a/qemu/target-arm/cpu.c +++ b/qemu/target-arm/cpu.c @@ -76,6 +76,27 @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) } } +static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque) +{ + /* Purely an assertion check: we've already done reset once, + * so now check that running the reset for the cpreg doesn't + * change its value. This traps bugs where two different cpregs + * both try to reset the same state field but to different values. + */ + ARMCPRegInfo *ri = value; + ARMCPU *cpu = opaque; + uint64_t oldvalue, newvalue; + + if (ri->type & (ARM_CP_SPECIAL | ARM_CP_ALIAS | ARM_CP_NO_RAW)) { + return; + } + + oldvalue = read_raw_cp_reg(&cpu->env, ri); + cp_reg_reset(key, value, opaque); + newvalue = read_raw_cp_reg(&cpu->env, ri); + assert(oldvalue == newvalue); +} + /* CPUClass::reset() */ static void arm_cpu_reset(CPUState *s) { @@ -87,6 +108,7 @@ static void arm_cpu_reset(CPUState *s) memset(env, 0, offsetof(CPUARMState, features)); g_hash_table_foreach(cpu->cp_regs, cp_reg_reset, cpu); + g_hash_table_foreach(cpu->cp_regs, cp_reg_check_reset, cpu); env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid; env->vfp.xregs[ARM_VFP_MVFR0] = cpu->mvfr0; env->vfp.xregs[ARM_VFP_MVFR1] = cpu->mvfr1; diff --git a/qemu/target-arm/cpu.h b/qemu/target-arm/cpu.h index a8d1873b..52938ecc 100644 --- a/qemu/target-arm/cpu.h +++ b/qemu/target-arm/cpu.h @@ -1463,6 +1463,9 @@ static inline bool cp_access_ok(int current_el, return (ri->access >> ((current_el * 2) + isread)) & 1; } +/* Raw read of a coprocessor register (as needed for migration, etc) */ +uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri); + /** * write_list_to_cpustate * @cpu: ARMCPU diff --git a/qemu/target-arm/helper.c b/qemu/target-arm/helper.c index 266312eb..a78d3b8c 100644 --- a/qemu/target-arm/helper.c +++ b/qemu/target-arm/helper.c @@ -46,7 +46,7 @@ static void *raw_ptr(CPUARMState *env, const ARMCPRegInfo *ri) return (char *)env + ri->fieldoffset; } -static uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri) +uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri) { /* Raw read of a coprocessor register (as needed for migration, etc). */ if (ri->type & ARM_CP_CONST) {