Re: [RFC PATCH v3 14/19] target/arm/kvm: compute supported values for ID register fields
Khushit Shah <[email protected]> Wed, 5 Aug 2026 09:58:44 +0000
| Newsgroups | dev.linux.lists.kvmarm,org.nongnu.qemu-arm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
> On 26 Jul 2026, at 7:52 PM, Eric Auger <[email protected]> wrote: > > !-------------------------------------------------------------------| > CAUTION: External Email > > |-------------------------------------------------------------------! > > Hi Khushit, > > On 7/16/26 11:38 PM, Khushit Shah wrote: >> Add arm_field_get_supported_values() which, for a given ID register >> field, builds the set of values KVM allows it to take on the live host. >> >> Non-writable fields are pinned to the host value. Writable fields follow >> their per field constraints. This function likely needs change when >> KVM starts exposing a new writable field that is not lower-safe, hence, >> the special handling of TGranX_2/SpecSEI/L1Ip/MIDR/REVIDR/AIDR. > so this looks quite risky. I agree. What are your thoughts on having a authoritative list in QEMU of all writable fields? No matter what KVM exposes as writable, actual fields we allow to be writable will be intersection of KVM writable + QEMU writable. When The QEMU list is updated we make sure things like this function is updated. This get’s rid of cases where we silently support writing to a field. It is similar to x86, QEMU only supports writing to specific CPUID leafs (by the defined Properties) and not just any CPUID leafs. >> >> Cross-field constraints are not modelled; for example ID_AA64ZFR0_EL1 >> is gated by SVE. Reproducing every inter-field dependency in QEMU >> would only duplicate KVM's logic and drift out of sync with it. > > Isn't qmp_query_cpu_model_expansion sufficient? In general upper layers > will try to apply raw named models. If ajustements are needed between > source and destination, we know field candidate values (either the > source or dest one) and this latter can be directly tried using > qmp_query_cpu_model_expansion write. On top of that > qmp_query_cpu_model_expansion can be directly checked against a scratch > vcpu reusing the logic implemented at kernel level. I am not sure if it is acceptable for query_cpu_model_expansion should fail if model is not realisable. On at least x86 it does not. It just returns the state which QEMU would request KVM for the given configurations. Ref: https://github.com/qemu/qemu/blob/3e3ccab106f879b1512f8e0d51a827dd4de30e22/target/i386/cpu.c#L9689 cpu-definitions is maybe a better candidate for algo you are describing, but it will not take the user overrides into considerations and only say if a base model is usable or not >> Signed-off-by: Khushit Shah <[email protected]> >> --- >> target/arm/kvm.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ >> target/arm/kvm_arm.h | 26 ++++++++++++ >> 2 files changed, 125 insertions(+) >> >> diff --git a/target/arm/kvm.c b/target/arm/kvm.c >> index c38b99cfce..8f452f9570 100644 >> --- a/target/arm/kvm.c >> +++ b/target/arm/kvm.c >> @@ -1262,6 +1262,105 @@ bool kvm_arm_cpu_post_load(ARMCPU *cpu) >> return true; >> } >> >> +static bool arm_field_is_signed(const ARM64SysRegField *field) >> +{ >> + return field_matches(field, ID_AA64MMFR0_EL1_IDX, "TGran4") || >> + field_matches(field, ID_AA64MMFR0_EL1_IDX, "TGran64") || >> + field_matches(field, ID_MMFR0_EL1_IDX, "InnerShr") || >> + field_matches(field, ID_MMFR0_EL1_IDX, "OuterShr") || >> + field_matches(field, ID_AA64DFR0_EL1_IDX, "DoubleLock") || >> + field_matches(field, ID_AA64DFR0_EL1_IDX, "PMUVer") || >> + field_matches(field, ID_DFR0_EL1_IDX, "PerfMon") || >> + field_matches(field, ID_AA64PFR1_EL1_IDX, "MTE_frac") || >> + field_matches(field, ID_AA64MMFR4_EL1_IDX, "E2H0") || >> + field_matches(field, ID_DFR1_EL1_IDX, "MTPMU") || >> + field_matches(field, ID_AA64PFR0_EL1_IDX, "FP") || >> + field_matches(field, ID_AA64PFR0_EL1_IDX, "AdvSIMD"); > Can't we extract this from Register.json instead? AFAIK, There is not signedness data in Register.json >> +} >> + >> +static void ranges_add(GArray *ranges, uint64_t min, uint64_t max) >> +{ >> + ArmFieldRange r = { .min = min, .max = max }; >> + g_array_append_val(ranges, r); >> +} >> + >> +void arm_field_get_supported_values(const ARM64SysRegField *field, >> + const ARMISARegisters *host_isar, >> + ArmFieldValueSet **value_set) >> +{ >> + bool is_signed = arm_field_is_signed(field); >> + uint64_t host = extract64(host_isar->idregs[field->index], >> + field->shift, field->length); >> + GArray *ranges = g_array_new(false, false, sizeof(ArmFieldRange)); >> + >> + /* A non-writable field can only ever hold the host value. */ >> + if (!arm_field_is_writable(field)) { >> + ranges_add(ranges, host, host); >> + goto done; > you already get this info from qmp_query_cpu_model_expansion Can you please elaborate how? I thought the current qmp_query_cpu_model_expansion (Your v6) only returns the writable field. (I have not yet looked at v7) >> + } >> + >> + if (field_matches(field, ID_AA64MMFR0_EL1_IDX, "TGran4_2") || >> + field_matches(field, ID_AA64MMFR0_EL1_IDX, "TGran16_2") || >> + field_matches(field, ID_AA64MMFR0_EL1_IDX, "TGran64_2")) { >> + /* Either support the host value or the "off" value */ >> + ranges_add(ranges, host, host); >> + if (host != 1) { /* 1 = "off" */ >> + ranges_add(ranges, 1, 1); >> + } >> + } else if (field_matches(field, CTR_EL0_IDX, "L1Ip")) { >> + /* Only safe to downgrade to VIPT, other values are reserved. */ >> + ranges_add(ranges, host, host); >> + if (host != 2) { /* 2 = "VIPT" */ >> + ranges_add(ranges, 2, 2); >> + } >> + } else if (field_matches(field, ID_AA64MMFR1_EL1_IDX, "SpecSEI") || >> + field_matches(field, ID_MMFR4_EL1_IDX, "SpecSEI")) { >> + /* It is safe to upgrade SpecSEI to 1, other values are reserved. */ >> + ranges_add(ranges, host, host); >> + if (host != 1) { >> + ranges_add(ranges, 1, 1); >> + } >> + } else if (field->index == MIDR_EL1_IDX || >> + field->index == REVIDR_EL1_IDX || >> + field->index == AIDR_EL1_IDX) { >> + /* >> + * No restriction on value that can be set for implementation ID >> + * registers fields. >> + */ >> + uint64_t max = 0; >> + if (field->length == 64) { >> + max = ~0ULL; >> + } else { >> + max = (1ULL << field->length) - 1; >> + } >> + ranges_add(ranges, 0, max); >> + } else { >> + /* >> + * After handling the special cases, other writable fields are >> + * either lower-safe or signed lower-safe. >> + */ >> + if (field->arch_vals_count) { >> + for (uint32_t i = 0; i < field->arch_vals_count; i++) { >> + uint64_t av = field->arch_vals[i].value; >> + int64_t v = is_signed ? >> + sextract64(av, 0, field->length) : (int64_t)av; >> + int64_t hv = is_signed ? >> + sextract64(host, 0, field->length) : (int64_t)host; >> + if (v <= hv) { >> + ranges_add(ranges, av, av); >> + } >> + } >> + } else { >> + g_assert(!is_signed); /* No signed field with no arch vals */ >> + ranges_add(ranges, 0, host); >> + } >> + } >> +done: >> + *value_set = g_new0(ArmFieldValueSet, 1); >> + (*value_set)->n_ranges = ranges->len; >> + (*value_set)->ranges = (ArmFieldRange *)g_array_free(ranges, false); >> +} >> + >> static bool arm_field_skip_writeback_always(const ARM64SysRegField *field) >> { >> /* >> diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h >> index 133a026036..9f15c91c4e 100644 >> --- a/target/arm/kvm_arm.h >> +++ b/target/arm/kvm_arm.h >> @@ -143,6 +143,32 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu); >> void kvm_arm_add_vcpu_properties(ARMCPU *cpu); >> >> typedef struct ARM64SysReg ARM64SysReg; >> +typedef struct ARM64SysRegField ARM64SysRegField; >> +typedef struct ARMISARegisters ARMISARegisters; >> + >> +typedef struct ArmFieldRange { >> + uint64_t min; >> + uint64_t max; >> +} ArmFieldRange; >> + >> +typedef struct ArmFieldValueSet { >> + ArmFieldRange *ranges; >> + size_t n_ranges; >> +} ArmFieldValueSet; >> + >> +/** >> + * arm_field_get_supported_values: >> + * @field: The field to get the supported values for >> + * @host_isar: The host ISAR registers >> + * @value_set: The set of supported values for the @field >> + * >> + * Will be allocated and filled in with the supported values for the @field >> + * based on the host_isar and whether the field is writable or not. >> + * The caller must free the value_set. >> + */ >> +void arm_field_get_supported_values(const ARM64SysRegField *field, >> + const ARMISARegisters *host_isar, >> + ArmFieldValueSet **value_set); >> >> /** >> * kvm_arm_steal_time_finalize: > Thanks > > Eric