aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2012-09-02 22:33:20 +0100
committerPeter Maydell <peter.maydell@linaro.org>2012-09-02 22:33:20 +0100
commit81722617a5f8df2076b5cf05ccd457176ac98c1a (patch)
tree3ac4c398e27bf6cf39da63b0c72301def07bc454
parentc9f83e14f1fe56fac09437748e0a3d309f043e1a (diff)
target-arm: Convert TCG to using (index,value) list for cp migrationkvm-arm-working
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>
-rw-r--r--target-arm/cpu-qom.h6
-rw-r--r--target-arm/cpu.h7
-rw-r--r--target-arm/helper.c97
3 files changed, 108 insertions, 2 deletions
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index beabf9a0a9..af97ebfa94 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -60,6 +60,12 @@ typedef struct ARMCPU {
/* 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 453f5f81d0..51ee073455 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -583,14 +583,14 @@ struct ARMCPRegInfo {
* 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 @@ static inline bool cp_access_ok(CPUARMState *env,
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 3db824f50a..4534280fa3 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -64,6 +64,103 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
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;