From 02d02b4ff77a03a9b8b4839891d517dd6db31c5d Mon Sep 17 00:00:00 2001
From: Peng Liang <liangpeng10@huawei.com>
Date: Wed, 9 Dec 2020 19:35:08 +0800
Subject: [PATCH] target/arm: Fix write redundant values to kvm

After modifying the value of a ID register, we'd better to try to write
it to KVM so that we can known the value is acceptable for KVM.
Because it may modify the registers' values of KVM, it's not suitable
for other registers.

(cherry-picked from a0d7a9de807639fcfcbe1fe037cb8772d459a9cf)
Signed-off-by: Peng Liang <liangpeng10@huawei.com>
---
 target/arm/helper.c | 73 ++++++++++++++++++++++++++++++---------------
 1 file changed, 49 insertions(+), 24 deletions(-)

diff --git a/target/arm/helper.c b/target/arm/helper.c
index b262f5d6c5..bddd355fa0 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;
     }
-- 
2.27.0

