diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2012-09-02 22:33:20 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2012-09-02 22:33:20 +0100 |
commit | 81722617a5f8df2076b5cf05ccd457176ac98c1a (patch) | |
tree | 3ac4c398e27bf6cf39da63b0c72301def07bc454 | |
parent | c9f83e14f1fe56fac09437748e0a3d309f043e1a (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.h | 6 | ||||
-rw-r--r-- | target-arm/cpu.h | 7 | ||||
-rw-r--r-- | target-arm/helper.c | 97 |
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; |