Duplicate the cpreg_{indexes,values,array_len} array as cpreg128_{indexes,values,array_len}. Similarly for cpreg_vmstate_{indexes,values,array_len}.
Split the values between the two arrays during write_cpustate_to_list, write_list_to_cpustate, count_cpreg, and add_cpreg_to_list. Signed-off-by: Richard Henderson <richard.hender...@linaro.org> --- target/arm/cpu.h | 19 +++++-- target/arm/helper.c | 125 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 116 insertions(+), 28 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 919bd3d7eb..a5a8779aba 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -911,25 +911,36 @@ struct ArchCPU { /* Coprocessor information */ GHashTable *cp_regs; - /* For marshalling (mostly coprocessor) register state between the + /* + * For marshalling (mostly coprocessor) register state between the * kernel and QEMU (for KVM) and between two QEMUs (for migration), * we use these arrays. */ - /* List of register indexes managed via these arrays; (full KVM style - * 64 bit indexes, not CPRegInfo 32 bit indexes) + /* + * List of register indexes managed via these arrays (full KVM style + * 64 bit indexes, not CPRegInfo 32 bit indexes). The registers are + * segregated by size, with 64-bit registers in cpreg_indexes and + * 128-bit registers in cpreg128_indexes. */ uint64_t *cpreg_indexes; + uint64_t *cpreg128_indexes; /* Values of the registers (cpreg_indexes[i]'s value is cpreg_values[i]) */ uint64_t *cpreg_values; + Int128 *cpreg128_values; /* Length of the indexes, values, reset_values arrays */ int32_t cpreg_array_len; - /* These are used only for migration: incoming data arrives in + int32_t cpreg128_array_len; + /* + * These are used only for migration: incoming data arrives in * these fields and is sanity checked in post_load before copying * to the working data structures above. */ uint64_t *cpreg_vmstate_indexes; + uint64_t *cpreg128_vmstate_indexes; uint64_t *cpreg_vmstate_values; + Int128 *cpreg128_vmstate_values; int32_t cpreg_vmstate_array_len; + int32_t cpreg128_vmstate_array_len; DynamicGDBFeatureInfo dyn_sysreg_feature; DynamicGDBFeatureInfo dyn_svereg_feature; diff --git a/target/arm/helper.c b/target/arm/helper.c index 3efc14da3a..6f20d3986e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -207,10 +207,11 @@ static bool raw_accessors_invalid(const ARMCPRegInfo *ri) bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) { /* Write the coprocessor state from cpu->env to the (index,value) list. */ - int i; + int i, n; bool ok = true; - for (i = 0; i < cpu->cpreg_array_len; i++) { + n = cpu->cpreg_array_len; + for (i = 0; i < n; i++) { uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]); const ARMCPRegInfo *ri; uint64_t newval; @@ -220,6 +221,10 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) ok = false; continue; } + if (ri->type & ARM_CP_128BIT) { + ok = false; + continue; + } if (ri->type & ARM_CP_NO_RAW) { continue; } @@ -247,35 +252,77 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) } cpu->cpreg_values[i] = newval; } - return ok; -} -bool write_list_to_cpustate(ARMCPU *cpu) -{ - int i; - bool ok = true; + n = cpu->cpreg128_array_len; + if (n == 0) { + return ok; + } + assert(!kvm_sync); - for (i = 0; i < cpu->cpreg_array_len; i++) { - uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]); - uint64_t v = cpu->cpreg_values[i]; - const ARMCPRegInfo *ri; + for (i = 0; i < n; i++) { + uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg128_indexes[i]); + const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); - ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); if (!ri) { ok = false; continue; } + if (!(ri->type & ARM_CP_128BIT)) { + ok = false; + continue; + } if (ri->type & ARM_CP_NO_RAW) { continue; } - /* - * Write value and confirm it reads back as written - * (to catch read-only registers and partially read-only - * registers where the incoming migration value doesn't match) - */ - write_raw_cp_reg(&cpu->env, ri, v); - if (read_raw_cp_reg(&cpu->env, ri) != v) { + + cpu->cpreg128_values[i] = read_raw_cp_reg128(&cpu->env, ri); + } + return ok; +} + +bool write_list_to_cpustate(ARMCPU *cpu) +{ + int i, n; + bool ok = true; + + n = cpu->cpreg_array_len; + for (i = 0; i < n; i++) { + uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]); + const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); + + if (!ri) { ok = false; + } else if (ri->type & ARM_CP_128BIT) { + ok = false; + } else if (!(ri->type & ARM_CP_NO_RAW)) { + /* + * Write value and confirm it reads back as written + * (to catch read-only registers and partially read-only + * registers where the incoming migration value doesn't match) + */ + uint64_t v = cpu->cpreg_values[i]; + write_raw_cp_reg(&cpu->env, ri, v); + if (read_raw_cp_reg(&cpu->env, ri) != v) { + ok = false; + } + } + } + + n = cpu->cpreg128_array_len; + for (i = 0; i < n; i++) { + uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg128_indexes[i]); + const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); + + if (!ri) { + ok = false; + } else if (!(ri->type & ARM_CP_128BIT)) { + ok = false; + } else if (!(ri->type & ARM_CP_NO_RAW)) { + Int128 v = cpu->cpreg128_values[i]; + write_raw_cp_reg128(&cpu->env, ri, v); + if (int128_ne(read_raw_cp_reg128(&cpu->env, ri), v)) { + ok = false; + } } } return ok; @@ -288,9 +335,14 @@ static void add_cpreg_to_list(gpointer key, gpointer value, gpointer opaque) const ARMCPRegInfo *ri = value; if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_ALIAS))) { - cpu->cpreg_indexes[cpu->cpreg_array_len] = cpreg_to_kvm_id(regidx); + uint64_t idx = cpreg_to_kvm_id(regidx); + /* The value array need not be initialized at this point */ - cpu->cpreg_array_len++; + if (ri->type & ARM_CP_128BIT) { + cpu->cpreg128_indexes[cpu->cpreg128_array_len++] = idx; + } else { + cpu->cpreg_indexes[cpu->cpreg_array_len++] = idx; + } } } @@ -300,7 +352,11 @@ static void count_cpreg(gpointer key, gpointer value, gpointer opaque) const ARMCPRegInfo *ri = value; if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_ALIAS))) { - cpu->cpreg_array_len++; + if (ri->type & ARM_CP_128BIT) { + cpu->cpreg128_array_len++; + } else { + cpu->cpreg_array_len++; + } } } @@ -310,9 +366,10 @@ void init_cpreg_list(ARMCPU *cpu) * Initialise the cpreg_tuples[] array based on the cp_regs hash. * Note that we require cpreg_tuples[] to be sorted by key ID. */ - int arraylen; + int arraylen, array128len; cpu->cpreg_array_len = 0; + cpu->cpreg128_array_len = 0; g_hash_table_foreach(cpu->cp_regs, count_cpreg, cpu); arraylen = cpu->cpreg_array_len; @@ -330,13 +387,33 @@ void init_cpreg_list(ARMCPU *cpu) cpu->cpreg_vmstate_array_len = arraylen; cpu->cpreg_array_len = 0; + array128len = cpu->cpreg128_array_len; + if (array128len) { + cpu->cpreg128_indexes = g_new(uint64_t, array128len); + cpu->cpreg128_values = g_new(Int128, array128len); + cpu->cpreg128_vmstate_indexes = g_new(uint64_t, array128len); + cpu->cpreg128_vmstate_values = g_new(Int128, array128len); + } else { + cpu->cpreg128_indexes = NULL; + cpu->cpreg128_values = NULL; + cpu->cpreg128_vmstate_indexes = NULL; + cpu->cpreg128_vmstate_values = NULL; + } + cpu->cpreg128_vmstate_array_len = array128len; + cpu->cpreg128_array_len = 0; + g_hash_table_foreach(cpu->cp_regs, add_cpreg_to_list, cpu); assert(cpu->cpreg_array_len == arraylen); + assert(cpu->cpreg128_array_len == array128len); if (arraylen) { qsort(cpu->cpreg_indexes, arraylen, sizeof(uint64_t), compare_u64); } + if (array128len) { + qsort(cpu->cpreg128_indexes, array128len, + sizeof(uint64_t), compare_u64); + } } bool arm_pan_enabled(CPUARMState *env) -- 2.43.0