aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;