@@ -35,6 +35,7 @@
#include "arm_ldst.h"
#include "exec/cpu_ldst.h"
#endif
+#include "kvm_arm.h"
#define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */
@@ -355,6 +356,16 @@ static bool raw_accessors_invalid(const ARMCPRegInfo *ri)
return true;
}
+static inline bool is_id_register(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. */
@@ -371,30 +382,43 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync)
ok = false;
continue;
}
- if (ri->type & ARM_CP_NO_RAW) {
+ /* Let's give ID registers a chance to synchronize to kvm. */
+ if ((ri->type & ARM_CP_NO_RAW) && !(kvm_sync && is_id_register(ri))) {
continue;
}
newval = read_raw_cp_reg(&cpu->env, ri);
if (kvm_sync) {
- /*
- * 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];
+ /* 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;
}
- write_raw_cp_reg(&cpu->env, ri, oldval);
- if (read_raw_cp_reg(&cpu->env, ri) != oldval) {
+ if (kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &newval)) {
+ if (is_id_register(ri)) {
+ ok = false;
+ error_report("Cannot set ID regsiter %s: %s", ri->name,
+ strerror(errno));
+ }
+ continue;
+ }
+ if (kvm_arm_get_one_reg(cpu, cpu->cpreg_indexes[i], &kvmval) ||
+ kvmval != newval) {
+ if (is_id_register(ri)) {
+ ok = false;
+ error_report("Setting ID register %s doesn't effect",
+ ri->name);
+ }
continue;
}
- write_raw_cp_reg(&cpu->env, ri, newval);
+ kvm_arm_set_one_reg(cpu, cpu->cpreg_indexes[i], &oldval);
}
cpu->cpreg_values[i] = newval;
}
@@ -506,6 +506,44 @@ out:
return ret;
}
+int kvm_arm_get_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *target)
+{
+ uint32_t v32;
+ int ret;
+
+ switch (regidx & KVM_REG_SIZE_MASK) {
+ case KVM_REG_SIZE_U32:
+ ret = kvm_get_one_reg(CPU(cpu), regidx, &v32);
+ if (ret == 0) {
+ *target = v32;
+ }
+ return ret;
+ case KVM_REG_SIZE_U64:
+ return kvm_get_one_reg(CPU(cpu), regidx, target);
+ default:
+ return -1;
+ }
+}
+
+int kvm_arm_set_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *source)
+{
+ uint32_t v32;
+
+ switch (regidx & KVM_REG_SIZE_MASK) {
+ case KVM_REG_SIZE_U32:
+ v32 = *source;
+ if (v32 != *source) {
+ error_report("the value of source is too large");
+ return -1;
+ }
+ return kvm_set_one_reg(CPU(cpu), regidx, &v32);
+ case KVM_REG_SIZE_U64:
+ return kvm_set_one_reg(CPU(cpu), regidx, source);
+ default:
+ return -1;
+ }
+}
+
bool write_kvmstate_to_list(ARMCPU *cpu)
{
CPUState *cs = CPU(cpu);
@@ -535,4 +535,7 @@ static inline const char *its_class_name(void)
}
}
+int kvm_arm_get_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *target);
+int kvm_arm_set_one_reg(ARMCPU *cpu, uint64_t regidx, uint64_t *source);
+
#endif