QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node);
}
+static ARMCPRegMigTolerance *find_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx)
+{
+ ARMCPRegMigTolerance *t;
+ QLIST_FOREACH(t, &cpu->cpreg_mig_tolerances, node) {
+ if (t->kvmidx == kvmidx) {
+ return t;
+ }
+ }
+ return NULL;
+}
+
+void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
+ uint64_t mask, uint64_t value,
+ ARMCPRegMigToleranceType type)
+{
+ ARMCPRegMigTolerance *entry;
+
+ /* make sure the kvmidx has not tolerance already registered */
+ assert(!find_mig_tolerance(cpu, kvmidx));
+
+ assert(type == ToleranceNotOnBothEnds ||
+ type == ToleranceOnlySrcTestValue);
+
+ entry = g_new0(ARMCPRegMigTolerance, 1);
+
+ entry->kvmidx = kvmidx;
+ entry->mask = mask;
+ entry->value = value;
+ entry->type = type;
+
+ QLIST_INSERT_HEAD(&cpu->cpreg_mig_tolerances, entry, node);
+}
+
+bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
+ uint64_t vmstate_value, uint64_t local_value,
+ ARMCPRegMigToleranceType type)
+{
+ ARMCPRegMigTolerance *t = find_mig_tolerance(cpu, kvmidx);
+ uint64_t diff, diff_outside_mask, field;
+
+ if (!t || t->type != type) {
+ return false;
+ }
+
+ if (type == ToleranceNotOnBothEnds) {
+ return true;
+ }
+
+ if (type == ToleranceOnlySrcTestValue &&
+ ((vmstate_value & t->mask) == t->value)) {
+ return true;
+ }
+
+ /* Need to check the mask */
+ diff = vmstate_value ^ local_value;
+ diff_outside_mask = diff & ~t->mask;
+
+ if (diff_outside_mask) {
+ /* there are differences outside of the mask */
+ return false;
+ }
+ if (type == ToleranceDiffInMask) {
+ /* differences only in the field, tolerance matched */
+ return true;
+ }
+ /* need to compare field value against authorized ones */
+ field = vmstate_value & t->mask;
+ if (type == ToleranceFieldLT && (field < t->value)) {
+ return true;
+ }
+ if (type == ToleranceFieldGT && (field > t->value)) {
+ return true;
+ }
+ return false;
+}
+
static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
{
/* Reset a single ARMCPRegInfo register */
QLIST_INIT(&cpu->pre_el_change_hooks);
QLIST_INIT(&cpu->el_change_hooks);
+ QLIST_INIT(&cpu->cpreg_mig_tolerances);
#ifdef CONFIG_USER_ONLY
# ifdef TARGET_AARCH64
{
ARMCPU *cpu = ARM_CPU(obj);
ARMELChangeHook *hook, *next;
+ ARMCPRegMigTolerance *t, *n;
g_hash_table_destroy(cpu->cp_regs);
QLIST_REMOVE(hook, node);
g_free(hook);
}
+ QLIST_FOREACH_SAFE(t, &cpu->cpreg_mig_tolerances, node, n) {
+ QLIST_REMOVE(t, node);
+ g_free(t);
+ }
#ifndef CONFIG_USER_ONLY
if (cpu->pmu_timer) {
timer_free(cpu->pmu_timer);
/* Used in FEAT_MEC to set the MECIDWidthm1 field in the MECIDR_EL2 register. */
#define MECID_WIDTH 16
+typedef enum {
+ ToleranceNotOnBothEnds,
+ ToleranceOnlySrcTestValue,
+ ToleranceDiffInMask,
+ ToleranceFieldLT,
+ ToleranceFieldGT,
+} ARMCPRegMigToleranceType;
+
+typedef struct ARMCPRegMigTolerance {
+ uint64_t kvmidx;
+ uint64_t mask;
+ uint64_t value;
+ ARMCPRegMigToleranceType type;
+ QLIST_ENTRY(ARMCPRegMigTolerance) node;
+} ARMCPRegMigTolerance;
+
+/**
+ * arm_register_cpreg_mig_tolerance:
+ * Register a migration tolerance wrt one given cpreg identified by its
+ * @kvmidx. Calling this function twice for the same @kvmidx is a
+ * programming error and will cause an assertion failure.
+ *
+ * @cpu: vcpu to apply the migration tolerance on
+ * @kvmidx: kvm index of the cpreg the tolerance applies to
+ * @mask: bitmask where a difference is tolerated
+ * (relevant with ToleranceDiffInMask)
+ * @value: value the bitmask field is compared with
+ * (relevant with ToleranceFieldLT and ToleranceFieldGT)
+ * @type: type of the migration tolerance:
+ * - ToleranceNotOnBothEnds (cpreg index is allowed to be only present
+ * on one end)
+ * - ToleranceOnlySrcTestValue (cpreg index is allowed to be only
+ * present in source if its value @mask field matches @value)
+ * - ToleranceDiffInMask (mismatch in cpreg values are only tolerated
+ * if differences are within @mask)
+ * - ToleranceFieldLT (mismatch in cpreg values are only tolerated
+ * if incoming @bitmask field value is less than @value)
+ * - ToleranceFieldGT (mismatch in cpreg values are only tolerated
+ * if incoming @bitmask field value is greater than @value)
+ */
+void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
+ uint64_t mask, uint64_t value,
+ ARMCPRegMigToleranceType type);
+
+/**
+ * arm_cpu_match_cpreg_mig_tolerance:
+ * Check whether a tolerance of type @type exists for a given @kvmidx
+ * and the tolerance criterion is satisfied
+ */
+bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
+ uint64_t vmstate_value, uint64_t local_value,
+ ARMCPRegMigToleranceType type);
+
+
#endif