@@ -184,6 +184,8 @@ typedef struct CPUARMState {
MPU write buffer control. */
uint32_t c5_insn; /* Fault status registers. */
uint32_t c5_data;
+ uint32_t c5_adfsr;
+ uint32_t c5_aifsr;
uint32_t c6_region[8]; /* MPU base/size registers. */
uint32_t c6_insn; /* Fault address registers. */
uint32_t c6_data;
@@ -197,6 +199,7 @@ typedef struct CPUARMState {
uint32_t c9_pmxevtyper; /* perf monitor event type */
uint32_t c9_pmuserenr; /* perf monitor user enable */
uint32_t c9_pminten; /* perf monitor interrupt enables */
+ uint32_t c9_l2ctlr; /* L2 Control Register */
uint32_t c12_vbar; /* vector base address register */
uint32_t c13_fcse; /* FCSE PID. */
uint32_t c13_context; /* Context ID. */
@@ -867,6 +870,15 @@ struct ARMCPRegInfo {
uint8_t opc0;
uint8_t opc1;
uint8_t opc2;
+ /* When migrating this flag tells if the register's value has already
+ * been copied. This is used in some unlikely cases where KVM migrates
+ * a register that in TCG is generated by a wildcarded or
+ * ARM_CP_STATE_BOTH parent (hence a not migratable register);
+ * in those cases, we will set the field "parent" to point to the
+ * generating register. Most of the time this field can stay false.
+ * */
+ bool skip_cpreglist;
+ ARMCPRegInfo *parent;
/* Execution state in which this register is visible: ARM_CP_STATE_* */
int state;
/* Register type: ARM_CP_* bits/values */
@@ -995,8 +1007,39 @@ bool write_list_to_cpustate(ARMCPU *cpu);
* Note that we do not stop early on failure -- we will attempt
* reading all registers in the list.
*/
+
bool write_cpustate_to_list(ARMCPU *cpu);
+typedef struct coproc_pair {
+ uint64_t id;
+ uint64_t val;
+} cp_pair;
+
+#ifndef KVM_REG_ARM_CORE
+#define KVM_REG_ARM_CORE (0x0010 << CP_REG_ARM_COPROC_SHIFT)
+#endif
+#ifndef KVM_REG_ARM_DEMUX
+#define KVM_REG_ARM_DEMUX (0x0011 << CP_REG_ARM_COPROC_SHIFT)
+#endif
+#ifndef KVM_REG_ARM_VFP
+#define KVM_REG_ARM_VFP (0x0012 << CP_REG_ARM_COPROC_SHIFT)
+#endif
+/* This method compares two arrays of coprocessor registers; in case of
+ * migration, the array b contains the incoming values. If a not NULL
+ * pointer to an empty list is provided, the method will fill it with
+ * pairs <id, value> of the registers that are in b but not in a.
+ * */
+int compare_cpreg_array(uint32_t len_a, uint64_t *idx_a, uint64_t *val_a,
+ uint32_t len_b, uint64_t *idx_b, uint64_t *val_b,
+ GList **mis_list);
+
+/* This method is supposed to take in input those registers which failed the
+ * copy in compare_cpreg_array and tries to handle them in such a way to allow
+ * migration from KVM to the TCG processor.
+ * It returns the number of registers not handled, -1 in case of error.
+ * */
+int handle_cpreg_kvm2tcg_migration(ARMCPU *cpu, GList *mis_list, uint32_t len);
+
/* 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). */
@@ -197,7 +197,7 @@ bool write_list_to_cpustate(ARMCPU *cpu)
ok = false;
continue;
}
- if (ri->type & ARM_CP_NO_MIGRATE) {
+ if (ri->type & ARM_CP_NO_MIGRATE || (ri->skip_cpreglist)) {
continue;
}
/* Write value and confirm it reads back as written
@@ -1213,6 +1213,10 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
{ .name = "DFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_data),
.resetvalue = 0, },
+ { .name = "ADFSR", .cp = 15, .crn = 5, .crm = 1, .opc1 = 0, .opc2 = 0,
+ .access = PL1_R, .fieldoffset = offsetof(CPUARMState, cp15.c5_adfsr) },
+ { .name = "AIFSR", .cp = 15, .crn = 5, .crm = 1, .opc1 = 0, .opc2 = 1,
+ .access = PL1_R, .fieldoffset = offsetof(CPUARMState, cp15.c5_aifsr) },
REGINFO_SENTINEL
};
@@ -1922,8 +1926,8 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
}
static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r,
- void *opaque, int state,
- int crm, int opc1, int opc2)
+ void *opaque, int state, int crm, int opc1,
+ int opc2, ARMCPRegInfo **parent)
{
/* Private utility function for define_one_arm_cp_reg_with_opaque():
* add a single reginfo struct to the hash table.
@@ -1931,6 +1935,12 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r,
uint32_t *key = g_new(uint32_t, 1);
ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo));
int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0;
+
+ /* these fields are used when migrating from TCG to KVM or the other way
+ * around*/
+ r2->skip_cpreglist = false;
+ r2->parent = NULL;
+
if (r->state == ARM_CP_STATE_BOTH && state == ARM_CP_STATE_AA32) {
/* The AArch32 view of a shared register sees the lower 32 bits
* of a 64 bit backing field. It is not migratable as the AArch64
@@ -1945,6 +1955,9 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r,
r2->fieldoffset += sizeof(uint32_t);
}
#endif
+ if (*parent != NULL) {
+ r2->parent = *parent;
+ }
}
if (state == ARM_CP_STATE_AA64) {
/* To allow abbreviation of ARMCPRegInfo
@@ -1979,6 +1992,21 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r,
((r->opc1 == CP_ANY) && opc1 != 0) ||
((r->opc2 == CP_ANY) && opc2 != 0)) {
r2->type |= ARM_CP_NO_MIGRATE;
+ if (*parent != NULL) {
+ r2->parent = *parent;
+ }
+ }
+
+ /* Check if this register is going to generate other "child"
+ * registers, in this case set the parent pointer.
+ * */
+ bool crm_cond = (r->crm != CP_ANY) ? true : (crm == 0);
+ bool opc1_cond = (r->opc1 != CP_ANY) ? true : (opc1 == 0);
+ bool opc2_cond = (r->opc2 != CP_ANY) ? true : (opc2 == 0);
+ bool state_cond = (state == ARM_CP_STATE_AA64 &&
+ r->state == ARM_CP_STATE_BOTH);
+ if ((crm_cond && opc1_cond && opc2_cond) || state_cond) {
+ *parent = r2;
}
/* Overriding of an existing definition must be explicitly
@@ -2094,22 +2122,149 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu,
}
/* Bad type field probably means missing sentinel at end of reg list */
assert(cptype_valid(r->type));
+ ARMCPRegInfo *parent = NULL;
for (crm = crmmin; crm <= crmmax; crm++) {
for (opc1 = opc1min; opc1 <= opc1max; opc1++) {
for (opc2 = opc2min; opc2 <= opc2max; opc2++) {
- for (state = ARM_CP_STATE_AA32;
- state <= ARM_CP_STATE_AA64; state++) {
+ for (state = ARM_CP_STATE_AA64;
+ state >= ARM_CP_STATE_AA32; state--) {
if (r->state != state && r->state != ARM_CP_STATE_BOTH) {
continue;
}
add_cpreg_to_hashtable(cpu, r, opaque, state,
- crm, opc1, opc2);
+ crm, opc1, opc2, &parent);
}
}
}
}
}
+static void add_pair_to_list(uint64_t id, uint64_t val, GList **list)
+{
+ cp_pair *new = g_malloc(sizeof(cp_pair));
+ new->id = id;
+ new->val = val;
+ *list = g_list_prepend(*list, new);
+}
+
+static void set_skip_cpreglist(gpointer key, gpointer value, gpointer type)
+{
+ ARMCPRegInfo *reg = (ARMCPRegInfo *)value;
+
+ if ((type != NULL && reg->type & *(int *)type) || type == NULL) {
+ reg->skip_cpreglist = true;
+ }
+}
+
+static void unset_skip_cpreglist(gpointer key, gpointer value, gpointer type)
+{
+ ARMCPRegInfo *reg = (ARMCPRegInfo *)value;
+
+ if ((type != NULL && reg->type & *(int *)type) || type == NULL) {
+ reg->skip_cpreglist = false;
+ }
+}
+
+int compare_cpreg_array(uint32_t len_a, uint64_t *idx_a, uint64_t *val_a,
+ uint32_t len_b, uint64_t *idx_b, uint64_t *val_b,
+ GList **mis_list)
+{
+ int i = 0, j = 0, ret = 0;
+
+ while (i < len_a && j < len_b) {
+ if (idx_b[j] > idx_a[i]) {
+ i++;
+ continue;
+ }
+ if (idx_b[j] < idx_a[i]) {
+ if (mis_list != NULL) {
+ add_pair_to_list(idx_b[j], val_b[j], mis_list);
+ }
+ j++;
+
+ ret = -1;
+ continue;
+ }
+ val_a[i] = val_b[j];
+ j++;
+ i++;
+ }
+
+ if ((i == len_a) && (j != len_b)) {
+ if (mis_list != NULL) {
+ for (i = j; i < len_b; i++) {
+ add_pair_to_list(idx_b[i], val_b[i], mis_list);
+ }
+ }
+
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int handle_cpreg_kvm2tcg_migration(ARMCPU *cpu, GList *mis_list, uint32_t len)
+{
+ GList *ptr_l = mis_list;
+
+ /* restore skip_cpreglist flag from last migration */
+ g_hash_table_foreach(cpu->cp_regs, unset_skip_cpreglist, NULL);
+
+ while (ptr_l != NULL) {
+ GList *n = ptr_l->next;
+ cp_pair *el = (cp_pair *)ptr_l->data;
+ const ARMCPRegInfo *ri;
+ /*
+ * KVM migrates all the CSSIDR cp registers while QEMU doesn't.
+ * They don't follow the usual nomenclature.
+ **/
+ switch (el->id & CP_REG_ARM_COPROC_MASK) {
+ case KVM_REG_ARM_CORE:
+ case KVM_REG_ARM_DEMUX:
+ case KVM_REG_ARM_VFP:
+ g_free(el);
+ mis_list = g_list_delete_link(mis_list, ptr_l);
+
+ break;
+ default:
+ /* probably its value has already been copied because
+ * the register is covered by a wilcarded case or it's
+ * a register not migrated by TCG. In any case, the
+ * register type has to be ARM_CP_NO_MIGRATE.
+ */
+ ri = get_arm_cp_reginfo(cpu->cp_regs, kvm_to_cpreg_id(el->id));
+ if (ri) {
+ if ((ri->type & ARM_CP_NO_MIGRATE) == 0) {
+ /* error */
+ return -1;
+ }
+ /* If it has a migratable parent, we raw write its value,
+ * otherwise we just remove it from the list since not
+ * migratable.
+ * */
+ if (ri->parent != NULL && !((*(ri->parent)).type &
+ ARM_CP_NO_MIGRATE)) {
+ write_raw_cp_reg(&cpu->env, ri, el->val);
+ (*(ri->parent)).skip_cpreglist = true;
+ }
+
+ g_free(el);
+ mis_list = g_list_delete_link(mis_list, ptr_l);
+ } else {
+ return -1;
+ }
+ }
+ ptr_l = n;
+ }
+
+ /* set all ARM_CP_CONST as already migrated, this will prevent the migration
+ * from failing when TCG and KVM constant registers do not match */
+ int type = ARM_CP_CONST;
+ g_hash_table_foreach(cpu->cp_regs, set_skip_cpreglist, &type);
+
+ return g_list_length(mis_list);
+}
+
void define_arm_cp_regs_with_opaque(ARMCPU *cpu,
const ARMCPRegInfo *regs, void *opaque)
{
@@ -155,11 +155,13 @@ static void cpu_pre_save(void *opaque)
ARMCPU *cpu = opaque;
if (kvm_enabled()) {
+ cpu->running_kvm = true;
if (!write_kvmstate_to_list(cpu)) {
/* This should never fail */
abort();
}
} else {
+ cpu->running_kvm = false;
if (!write_cpustate_to_list(cpu)) {
/* This should never fail. */
abort();
@@ -176,7 +178,6 @@ static void cpu_pre_save(void *opaque)
static int cpu_post_load(void *opaque, int version_id)
{
ARMCPU *cpu = opaque;
- int i, v;
/* Update the values list from the incoming migration data.
* Anything in the incoming data which we don't know about is
@@ -186,22 +187,42 @@ static int cpu_post_load(void *opaque, int version_id)
* incoming migration index list so we can match the values array
* entries with the right slots in our own values array.
*/
+ GList *missing_regs = NULL;
+ if (compare_cpreg_array(cpu->cpreg_array_len, cpu->cpreg_indexes,
+ cpu->cpreg_values, cpu->cpreg_vmstate_array_len,
+ cpu->cpreg_vmstate_indexes, cpu->cpreg_vmstate_values,
+ &missing_regs)) {
+ /* three different cases:
+ * 1. KVM to TCG migration
+ * 2. TCG to KVM migration
+ * 3. KVM to KVM / TCG to TCG migration*/
+ if (cpu->running_kvm && !kvm_enabled()) {
+ /* case 1. */
+ if (handle_cpreg_kvm2tcg_migration(cpu, missing_regs,
+ g_list_length(missing_regs))) {
+ g_list_free(missing_regs);
+
+ return -1;
+ }
+ /* update the value for future migrations */
+ cpu->running_kvm = false;
+ } else if (!cpu->running_kvm && kvm_enabled()) {
+ /* case 2. TODO */
+ cpu->running_kvm = true;
+ g_list_free(missing_regs);
+
+ return -1;
+ } else {
+ /* case 3. - regular migration, an error occurred */
+ g_list_free(missing_regs);
- for (i = 0, v = 0; i < cpu->cpreg_array_len
- && v < cpu->cpreg_vmstate_array_len; i++) {
- if (cpu->cpreg_vmstate_indexes[v] > cpu->cpreg_indexes[i]) {
- /* register in our list but not incoming : skip it */
- continue;
- }
- if (cpu->cpreg_vmstate_indexes[v] < cpu->cpreg_indexes[i]) {
- /* register in their list but not ours: fail migration */
return -1;
}
- /* matching register, copy the value over */
- cpu->cpreg_values[i] = cpu->cpreg_vmstate_values[v];
- v++;
}
+ /* free the list */
+ g_list_free(missing_regs);
+
if (kvm_enabled()) {
if (!write_list_to_kvmstate(cpu)) {
return -1;
@@ -238,6 +259,7 @@ const VMStateDescription vmstate_arm_cpu = {
.offset = 0,
},
VMSTATE_UINT32(env.spsr, ARMCPU),
+ VMSTATE_BOOL(running_kvm, ARMCPU),
VMSTATE_UINT32_ARRAY(env.banked_spsr, ARMCPU, 6),
VMSTATE_UINT32_ARRAY(env.banked_r13, ARMCPU, 6),
VMSTATE_UINT32_ARRAY(env.banked_r14, ARMCPU, 6),
CPUARMState: * added adfsr cp register. * added aifsr cp register. These registers have been added because they are migrated by KVM. This prevents the migration from failing when trying to copy their values. ARMCPRegInfo: * added a pointer to the parent that generated the register (if any). * a flag to inform that we have already copied the value of the register: this prevents the register from being overwritten by the parent register value, which could be not set. helper.c: * added mechanism to track the cp register "parent". * compare_cpreg_array(): compare the incoming cp registers with the cpreg_list keeping a list of the registers that do not succeeded the match. * handle_cpreg_kvm2tcg_migration(): try to solve the mismatch of cp registers coming from KVM; without this additional step the migration would fail even if it's feasible. Signed-off-by: Alvise Rigo <a.rigo@virtualopensystems.com> --- target-arm/cpu.h | 43 +++++++++++++ target-arm/helper.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++-- target-arm/machine.c | 46 ++++++++++---- 3 files changed, 238 insertions(+), 18 deletions(-)