diff --git a/target/arm/helper.c b/target/arm/helper.c index b262f5d6c506bed9ee92066117d0aee051aea0f8..bddd355fa0203093ad9aa6ee925170beb5338cac 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -252,6 +252,16 @@ static bool raw_accessors_invalid(const ARMCPRegInfo *ri) return true; } +static bool is_id_reg(const ARMCPRegInfo *ri) +{ + /* + * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2), + * where 1<=crm<8, 0<=op2<8. + */ + return ri->opc0 == 3 && ri->opc1 == 0 && ri->crn == 0 && + ri->crm > 0 && ri->crm < 8; +} + bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) { /* Write the coprocessor state from cpu->env to the (index,value) list. */ @@ -268,38 +278,53 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) ok = false; continue; } - /* - * (Op0, Op1, CRn, CRm, Op2) of ID registers is (3, 0, 0, crm, op2), - * where 1<=crm<8, 0<=op2<8. Let's give ID registers a chance to - * synchronize to kvm. - */ - if ((ri->type & ARM_CP_NO_RAW) && !(kvm_sync && - ri->opc0 == 3 && ri->opc1 == 0 && ri->crn == 0 && ri->crm > 0)) { + if ((ri->type & ARM_CP_NO_RAW) && !(kvm_sync && is_id_reg(ri))) { continue; } newval = read_raw_cp_reg(&cpu->env, ri); if (kvm_sync) { - /* Only sync if we can sync to KVM successfully. */ - uint64_t oldval; - uint64_t kvmval; + if (is_id_reg(ri)) { + /* Only sync if we can sync to KVM successfully. */ + uint64_t oldval; + uint64_t kvmval; - if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &oldval)) { - continue; - } - if (oldval == newval) { - continue; - } + if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &oldval)) { + continue; + } + if (oldval == newval) { + continue; + } - if (kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &newval)) { - continue; - } - if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &kvmval) || - kvmval != newval) { - continue; - } + if (kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &newval)) { + continue; + } + if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &kvmval) || + kvmval != newval) { + continue; + } + + kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &oldval); + } else { + /* + * Only sync if the previous list->cpustate sync succeeded. + * Rather than tracking the success/failure state for every + * item in the list, we just recheck "does the raw write we must + * have made in write_list_to_cpustate() read back OK" here. + */ + uint64_t oldval = cpu->cpreg_values[i]; + + if (oldval == newval) { + continue; + } - kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &oldval); + write_raw_cp_reg(&cpu->env, ri, oldval); + if (read_raw_cp_reg(&cpu->env, ri) != oldval) { + continue; + } + + write_raw_cp_reg(&cpu->env, ri, newval); + } } cpu->cpreg_values[i] = newval; }