target-arm: Convert TCG to using (index,value) list for cp migration

Convert the TCG ARM target to using an (index,value) list for migrating
coprocessors. The primary benefit of the (index,value) list is for
passing state between KVM and QEMU, but it works for TCG-to-TCG
migration as well and is a useful self-contained first step.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index beabf9a..af97ebf 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -60,6 +60,12 @@
 
     /* Coprocessor information */
     GHashTable *cp_regs;
+    /* List of (register index, value) tuples which we use for marshalling
+     * register state between the kernel and QEMU (for KVM)  and between
+     * two QEMUs (for migration).
+     */
+    uint64_t *cpreg_tuples;
+    int cpreg_tuples_len;
 
     /* The instance init functions for implementation-specific subclasses
      * set these fields to specify the implementation-dependent values of
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 453f5f8..51ee073 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -583,14 +583,14 @@
      * migration. This only needs to be provided if there is also a
      * readfn and it makes an access permission check.
      */
-    CPReadfn *raw_readfn;
+    CPReadFn *raw_readfn;
     /* Function for doing a "raw" write; used when we need to copy KVM
      * kernel coprocessor state into userspace, or for inbound
      * migration. This only needs to be provided if there is also a
      * readfn and it makes an access permission check or masks out
      * "unwritable" bits.
      */
-    CPWritefn *raw_writefn;
+    CPWriteFn *raw_writefn;
     /* Function for resetting the register. If NULL, then reset will be done
      * by writing resetvalue to the field specified in fieldoffset. If
      * fieldoffset is 0 then no reset will be done.
@@ -634,6 +634,9 @@
     return (ri->access >> ((arm_current_pl(env) * 2) + isread)) & 1;
 }
 
+bool write_cp_state_to_list(ARMCPU *cpu, bool fail_on_error);
+bool write_list_to_cp_state(ARMCPU *cpu, bool fail_on_error);
+
 /* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3.
    Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are
    conventional cores (ie. Application or Realtime profile).  */
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 3db824f..4534280 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -64,6 +64,103 @@
     return 0;
 }
 
+static int read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri,
+                           uint64_t *v)
+{
+    /* Raw read of a coprocessor register (as needed for migration, etc)
+     * return 0 on success, 1 if the read is impossible for some reason.
+     */
+    assert(!(ri->type & ARM_CP_SPECIAL));
+    if (ri->type & ARM_CP_CONST) {
+        *v = ri->resetvalue;
+    } else if (ri->raw_readfn) {
+        return ri->raw_readfn(env, ri, v);
+    } else if (ri->readfn) {
+        return ri->readfn(env, ri, v);
+    } else {
+        if (ri->type & ARM_CP_64BIT) {
+            *v = CPREG_FIELD64(env, ri);
+        } else {
+            *v = CPREG_FIELD32(env, ri);
+        }
+    }
+    return 0;
+}
+
+static int write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri,
+                            int64_t v)
+{
+    /* Raw write of a coprocessor register (as needed for migration, etc).
+     * Return 0 on success, 1 if the write is impossible for some reason.
+     */
+    assert(!(ri->type & ARM_CP_SPECIAL));
+    if (ri->type & ARM_CP_CONST) {
+        return 0;
+    } else if (ri->raw_writefn) {
+        return ri->writefn(env, ri, v);
+    } else if (ri->writefn) {
+        return ri->writefn(env, ri, v);
+    } else {
+        if (ri->type & ARM_CP_64BIT) {
+            CPREG_FIELD64(env, ri) = v;
+        } else {
+            CPREG_FIELD32(env, ri) = v;
+        }
+    }
+    return 0;
+}
+
+bool write_cp_state_to_list(ARMCPU *cpu, bool fail_on_error)
+{
+    /* Write the coprocessor state from cpu->env to the (index,value) list. */
+    int i;
+    for (i = 0; i < cpu->cpreg_tuples_len; i += 2) {
+        uint64_t regidx = cpu->cpreg_tuples[i];
+        const ARMCPRegInfo *ri;
+        uint64_t v;
+        ri = get_arm_cp_reginfo(cpu, regidx);
+        if (!ri) {
+            if (fail_on_error) {
+                return false;
+            }
+            continue;
+        }
+        if (ri->type & (ARM_CP_NO_MIGRATE|ARM_CP_SPECIAL)) {
+            continue;
+        }
+        if (read_raw_cp_reg(&cpu->env, ri, &v)) {
+            if (fail_on_error) {
+                return false;
+            }
+            continue;
+        }
+        cpu->cpreg_tuples[i + 1] = v;
+    }
+    return true;
+}
+
+bool write_list_to_cp_state(ARMCPU *cpu, bool fail_on_error)
+{
+    int i;
+    for (i = 0; i < cpu->cpreg_tuples_len; i += 2) {
+        uint64_t regidx = cpu->cpreg_tuples[i];
+        uint64_t v = cpu->cpreg_tuples[i + 1];
+        const ARMCPRegInfo *ri;
+        ri = get_arm_cp_reginfo(cpu, regidx);
+        if (!ri) {
+            if (fail_on_error) {
+                return false;
+            }
+            continue;
+        }
+        if (write_raw_cp_reg(&cpu->env, ri, v) && fail_on_error) {
+            return false;
+        }
+    }
+    return true;
+}
+
+
 static int dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
 {
     env->cp15.c3 = value;