diff options
-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; |