Re: [RFC PATCH v3 04/19] target/arm: expose all ID regs fields as properties
Eric Auger <[email protected]>
| Newsgroups | org.nongnu.qemu-arm,dev.linux.lists.kvmarm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
On 8/18/26 11:46 AM, Khushit Shah wrote: > >> On 14 Aug 2026, at 7:24 PM, Eric Auger <[email protected]> wrote: >> >> !-------------------------------------------------------------------| >> CAUTION: External Email >> >> |-------------------------------------------------------------------! >> >> >> >> On 8/3/26 5:05 PM, Khushit Shah wrote: >>>> On 21 Jul 2026, at 8:14 PM, Eric Auger <[email protected]> wrote: >>>> >>>> !-------------------------------------------------------------------| >>>> CAUTION: External Email >>>> >>>> |-------------------------------------------------------------------! >>>> >>>> Hi Khushit, >>>> >>>> On 7/16/26 11:38 PM, Khushit Shah wrote: >>>>> Upcoming named cpu models layer requires exposing all the ID register >>>>> fields as properties. >>>>> >>>>> While doing so also move the code to cpu64.c to make it >>>>> accelerator-agnostic, and rename the function to >>>>> `aarch64_add_idregs_properties()`. >>>> Better to split that patch into 2 to ease the review: >>>> 1) update the code in kvm to expose all ID regs including those which >>>> are not writable and adaptation in set_sysreg_prop to handle RAZ, >>>> RES0/1, ... >>>> 2) Move to cpu64.c >>> This makes more sense, thanks, will do that in v4. >>> >>>> To me it is still arguable whether it makes sense to expose settings >>>> which cannot be really set. To me It pollutes the qmp introspection >>>> output and I don't know whether there is any case history. >>>> Why can't we: >>>> - only expose writable id regs as props >>>> - other non writable settings you need to check could be registered in a >>>> hash table during named cpu model definition in props. >>>> in arm_apply_model_props() when you enumerate all props you can check if >>>> there is an existing prop for this name, in which case you really call >>>> >>>> object_property_set_bool(), otherwise, you go through another path, which is not visible by the end user where you check the KVM value matches your "prop" value. No need to pretend this is writable. >>> Please refer to my below text. >>> >>>> Also I am doubtful about RES/RAZ. >>>> >>>> + MODEL_PROP("SYSREG_CTR_EL0_RES1_31", NUM, 1), >>>> >>>> does not make sense to me >>>> >>>> Why can't you enumate all ID regs and go through those fields and check the KVM value matches. Why would we need to explicitly set a RAS/RES0/1 field. Why can't that be checked transparently. >>> Why do we need non-writable field props?: >>> - Assume some non-writable field mismatches for the model and >>> host, why do we want to stop users from explicitly overriding >>> the model's field value to host value? >>> - Even with cpu -host, how will upper layer know, if they can >>> migrate between host A and host B? (Without actually doing >>> the migration) >>> -> Only if all non-writable fields in both host matches >>> and writable fields in both host are “compatible”. >> I do agree with you we need to check non writable fields do match. What >> I am challenging is the fact you want to expose non writable idregs >> through qemu properties. If it is not writable why would we pretend the >> end user can change it through the cmd line. You can set the expected >> field value in the vcpu model description through DEFINEs or whatever >> method. Then on init, the model will read the host value and fails the >> init if they don't match. The fact no prop is available for this field >> would teach the end user the field is not writable. >> >> The above MODEL_PROP() could be a wrapper that either sets the actual >> property if it exists (for a writable field) or just silently populate a >> table of expected non writable field values that are checked on init, no? > Okay, I think here the confusion lies in perspective of writability. > > From my perspective, a field is only host non-writable, i.e. every guest > on host should have the same field value. > > But this should not restrict model’s field writability. Take the > “pauth” example as explained earlier. A field being non-writable does > not mean we cannot change model’s value, it only means we cannot change > host value, right? Yes that's what I meant. You need to be able to tell I want this field to be equal to x. If it is not host writable, the model will just read host value and check this latter and x do match, otherwise fail the init. However I advocate for not exposing the field as a true qemu property as it is not host writable. Eric > >>> Why would we need to explicitly set a RAS/RES0/1 field? >>> - Here my argument is a little weaker but, what if RES1 field >>> is repurposed in the future? We still want existing models >>> to show 1. >> AARCHMRS description tells you which fields are RESx. You can read the >> host value and fails the init in case of mismatch. >> > I agree, will remove RAS/RES props. > > Warm Regards, > Khushit > >> Thanks >> >> Eric >>> RES0 does not need any special handling, only RES1 field needs >>> this special handling. >>> >>> >>> Does the above arguments make sense? >>> >>> Warm Regards, >>> Khushit >>> >>>> Thanks >>>> >>>> Eric >>>> >>>>> This makes porting this solution to other accelerators easy in future. >>>>> >>>>> Signed-off-by: Khushit Shah <[email protected]> >>>>> --- >>>>> target/arm/cpu64.c | 116 +++++++++++++++++++++++++++++- >>>>> target/arm/internals.h | 3 + >>>>> target/arm/kvm.c | 151 ---------------------------------------- >>>>> target/arm/kvm_arm.h | 8 --- >>>>> target/arm/trace-events | 4 +- >>>>> 5 files changed, 119 insertions(+), 163 deletions(-) >>>>> >>>>> diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c >>>>> index 92a01d4ce7..cdad0fc46d 100644 >>>>> --- a/target/arm/cpu64.c >>>>> +++ b/target/arm/cpu64.c >>>>> @@ -38,6 +38,7 @@ >>>>> #include "internals.h" >>>>> #include "cpu-features.h" >>>>> #include "cpu-idregs.h" >>>>> +#include "trace.h" >>>>> >>>>> /* convert between <register>_IDX and SYS_<register> */ >>>>> #define DEF(NAME, OP0, OP1, CRN, CRM, OP2) \ >>>>> @@ -644,6 +645,113 @@ static const Property arm_cpu_pauth_qarma3_property = >>>>> static Property arm_cpu_pauth_qarma5_property = >>>>> DEFINE_PROP_BOOL("pauth-qarma5", ARMCPU, prop_pauth_qarma5, false); >>>>> >>>>> +#ifndef CONFIG_USER_ONLY >>>>> +static void set_sysreg_prop(Object *obj, Visitor *v, >>>>> + const char *name, void *opaque, >>>>> + Error **errp) >>>>> +{ >>>>> + ARM64SysRegField *field = (ARM64SysRegField *)opaque; >>>>> + ARMCPU *cpu = ARM_CPU(obj); >>>>> + uint64_t *idregs = cpu->isar.idregs; >>>>> + uint64_t old, value, mask; >>>>> + int lower = field->shift; >>>>> + int length = field->length; >>>>> + int index = field->index; >>>>> + >>>>> + if (!visit_type_uint64(v, name, &value, errp)) { >>>>> + return; >>>>> + } >>>>> + >>>>> + if (length < 64 && value > ((1ULL << length) - 1)) { >>>>> + error_setg(errp, >>>>> + "idreg %s set value (0x%lx) exceeds length of field (%d)!", >>>>> + name, value, length); >>>>> + return; >>>>> + } >>>>> + >>>>> + if (field->arch_vals) { >>>>> + /* this field has some enum values */ >>>>> + for (int i = 0; i < field->arch_vals_count; i++) { >>>>> + if (value == field->arch_vals[i].value) { >>>>> + goto valid; >>>>> + } >>>>> + } >>>>> + error_setg(errp, >>>>> + "idreg %s set value (0x%lx) does not match any " >>>>> + "arch valid enum value!", name, value); >>>>> + return; >>>>> + } >>>>> + >>>>> + /* If the field is RESx or RAZ, it should not be written anything else */ >>>>> + if (strstr(field->name, "RES0") && value != 0) { >>>>> + error_setg(errp, "idreg %s is RES0 field and cannot write %ld", >>>>> + name, value); >>>>> + return; >>>>> + } else if (strstr(field->name, "RAZ") && value != 0) { >>>>> + error_setg(errp, "idreg %s is RAZ field and cannot write %ld", >>>>> + name, value); >>>>> + return; >>>>> + } else if (strstr(field->name, "RES1") && value != 1) { >>>>> + error_setg(errp, "idreg %s is RES1 field and cannot write %ld", >>>>> + name, value); >>>>> + return; >>>>> + } >>>>> + >>>>> +valid: >>>>> + mask = MAKE_64BIT_MASK(lower, length); >>>>> + value = value << lower; >>>>> + old = idregs[index]; >>>>> + idregs[index] = old & ~mask; >>>>> + idregs[index] |= value; >>>>> + trace_set_sysreg_prop(name, old, mask, value, idregs[index]); >>>>> +} >>>>> + >>>>> +static void get_sysreg_prop(Object *obj, Visitor *v, >>>>> + const char *name, void *opaque, >>>>> + Error **errp) >>>>> +{ >>>>> + ARM64SysRegField *field = (ARM64SysRegField *)opaque; >>>>> + ARMCPU *cpu = ARM_CPU(obj); >>>>> + uint64_t *idregs = cpu->isar.idregs; >>>>> + uint64_t value, mask; >>>>> + int lower = field->shift; >>>>> + int length = field->length; >>>>> + int index = field->index; >>>>> + >>>>> + mask = MAKE_64BIT_MASK(lower, length); >>>>> + value = (idregs[index] & mask) >> lower; >>>>> + visit_type_uint64(v, name, &value, errp); >>>>> + trace_get_sysreg_prop(name, value); >>>>> +} >>>>> + >>>>> +static int >>>>> +aarch64_add_idreg_properties(Object *obj, ARM64SysReg *reg) >>>>> +{ >>>>> + int nb_sysreg_props = 0; >>>>> + >>>>> + for (int i = 0; i < reg->fields_count; i++) { >>>>> + ARM64SysRegField *field = ®->fields[i]; >>>>> + char *prop_name = g_strdup_printf("SYSREG_%s_%s", reg->name, >>>>> + field->name); >>>>> + object_property_add(obj, prop_name, "uint64", >>>>> + get_sysreg_prop, set_sysreg_prop, NULL, field); >>>>> + g_free(prop_name); >>>>> + nb_sysreg_props++; >>>>> + } >>>>> + >>>>> + trace_nb_sysreg_props(reg->name, nb_sysreg_props); >>>>> + return 0; >>>>> +} >>>>> + >>>>> +void aarch64_add_idregs_properties(Object *obj) >>>>> +{ >>>>> + for (int i = 0; i < NUM_ID_IDX; i++) { >>>>> + ARM64SysReg *reg = &arm64_id_regs[i]; >>>>> + aarch64_add_idreg_properties(obj, reg); >>>>> + } >>>>> +} >>>>> +#endif >>>>> + >>>>> void aarch64_add_pauth_properties(Object *obj) >>>>> { >>>>> ARMCPU *cpu = ARM_CPU(obj); >>>>> @@ -865,8 +973,12 @@ static void aarch64_host_initfn(Object *obj) >>>>> kvm_arm_set_cpu_features_from_host(cpu); >>>>> aarch64_add_sve_properties(obj); >>>>> >>>>> - /* generate SYSREG properties according to writable masks */ >>>>> - kvm_arm_expose_idreg_properties(cpu, arm64_id_regs); >>>>> +#ifndef CONFIG_USER_ONLY >>>>> + /* generate SYSREG properties */ >>>>> + if (kvm_enabled()) { >>>>> + aarch64_add_idregs_properties(obj); >>>>> + } >>>>> +#endif >>>>> >>>>> #elif defined(CONFIG_HVF) >>>>> hvf_arm_set_cpu_features_from_host(cpu); >>>>> diff --git a/target/arm/internals.h b/target/arm/internals.h >>>>> index a632584a4e..7502b6db8a 100644 >>>>> --- a/target/arm/internals.h >>>>> +++ b/target/arm/internals.h >>>>> @@ -1754,6 +1754,9 @@ void aarch64_max_tcg_initfn(Object *obj); >>>>> void aarch64_add_pauth_properties(Object *obj); >>>>> void aarch64_add_sve_properties(Object *obj); >>>>> void aarch64_add_sme_properties(Object *obj); >>>>> +#ifndef CONFIG_USER_ONLY >>>>> +void aarch64_add_idregs_properties(Object *obj); >>>>> +#endif >>>>> >>>>> /* Return true if the gdbstub is presenting an AArch64 CPU */ >>>>> static inline bool arm_gdbstub_is_aarch64(ARMCPU *cpu) >>>>> diff --git a/target/arm/kvm.c b/target/arm/kvm.c >>>>> index 892433a612..76583db3f0 100644 >>>>> --- a/target/arm/kvm.c >>>>> +++ b/target/arm/kvm.c >>>>> @@ -327,157 +327,6 @@ static int get_host_cpu_idregs(ARMCPU *cpu, int fd, ARMHostCPUFeatures *ahcf) >>>>> return err; >>>>> } >>>>> >>>>> -static ARM64SysRegField *get_field(int i, ARM64SysReg *reg) >>>>> -{ >>>>> - for (int f = 0; f < reg->fields_count; f++) { >>>>> - struct ARM64SysRegField *field = ®->fields[f]; >>>>> - int upper = field->shift + field->length - 1; >>>>> - >>>>> - if (i >= field->shift && i <= upper) { >>>>> - return field; >>>>> - } >>>>> - } >>>>> - return NULL; >>>>> -} >>>>> - >>>>> -static void set_sysreg_prop(Object *obj, Visitor *v, >>>>> - const char *name, void *opaque, >>>>> - Error **errp) >>>>> -{ >>>>> - ARM64SysRegField *field = (ARM64SysRegField *)opaque; >>>>> - ARMCPU *cpu = ARM_CPU(obj); >>>>> - uint64_t *idregs = cpu->isar.idregs; >>>>> - uint64_t old, value, mask; >>>>> - int lower = field->shift; >>>>> - int length = field->length; >>>>> - int index = field->index; >>>>> - >>>>> - if (!visit_type_uint64(v, name, &value, errp)) { >>>>> - return; >>>>> - } >>>>> - >>>>> - if (length < 64 && value > ((1 << length) - 1)) { >>>>> - error_setg(errp, >>>>> - "idreg %s set value (0x%lx) exceeds length of field (%d)!", >>>>> - name, value, length); >>>>> - return; >>>>> - } >>>>> - >>>>> - if (field->arch_vals) { >>>>> - /* this field has some enum values */ >>>>> - for (int i = 0; i < field->arch_vals_count; i++) { >>>>> - if (value == field->arch_vals[i].value) { >>>>> - goto valid; >>>>> - } >>>>> - } >>>>> - error_setg(errp, >>>>> - "idreg %s set value (0x%lx) does not match any " >>>>> - "arch valid enum value!", name, value); >>>>> - return; >>>>> - } >>>>> - >>>>> -valid: >>>>> - >>>>> - mask = MAKE_64BIT_MASK(lower, length); >>>>> - value = value << lower; >>>>> - old = idregs[index]; >>>>> - idregs[index] = old & ~mask; >>>>> - idregs[index] |= value; >>>>> - trace_set_sysreg_prop(name, old, mask, value, idregs[index]); >>>>> -} >>>>> - >>>>> -static void get_sysreg_prop(Object *obj, Visitor *v, >>>>> - const char *name, void *opaque, >>>>> - Error **errp) >>>>> -{ >>>>> - ARM64SysRegField *field = (ARM64SysRegField *)opaque; >>>>> - ARMCPU *cpu = ARM_CPU(obj); >>>>> - uint64_t *idregs = cpu->isar.idregs; >>>>> - uint64_t value, mask; >>>>> - int lower = field->shift; >>>>> - int length = field->length; >>>>> - int index = field->index; >>>>> - >>>>> - mask = MAKE_64BIT_MASK(lower, length); >>>>> - value = (idregs[index] & mask) >> lower; >>>>> - visit_type_uint64(v, name, &value, errp); >>>>> - trace_get_sysreg_prop(name, value); >>>>> -} >>>>> - >>>>> -/* >>>>> - * decode_idreg_writemap: Generate props for writable fields >>>>> - * >>>>> - * @obj: CPU object >>>>> - * @reg: description of the sysreg >>>>> - */ >>>>> -static int >>>>> -decode_idreg_writemap(Object *obj, ARM64SysReg *reg) >>>>> -{ >>>>> - uint64_t map = reg->writable_mask; >>>>> - int i = ctz64(map); >>>>> - int nb_sysreg_props = 0; >>>>> - >>>>> - while (map) { >>>>> - ARM64SysRegField *field = get_field(i, reg); >>>>> - int lower, upper; >>>>> - char *prop_name; >>>>> - uint64_t field_mask; >>>>> - >>>>> - if (!field) { >>>>> - warn_report("%s bit %d of %s is writable but no named field " >>>>> - "in target/arm/cpu-idregs.h.inc", >>>>> - __func__, i, reg->name); >>>>> - warn_report("%s is target/arm/cpu-idregs.h.inc up-to-date?", __func__); >>>>> - map = map & ~BIT_ULL(i); >>>>> - i = ctz64(map); >>>>> - continue; >>>>> - } >>>>> - lower = field->shift; >>>>> - upper = field->shift + field->length - 1; >>>>> - >>>>> - /* Sanity check the field is not a reserved field */ >>>>> - if (strstr(field->name, "RES0") || strstr(field->name, "RES1") || >>>>> - strstr(field->name, "RAZ")) { >>>>> - trace_unexpected_writable_reserved_field(reg->name, field->name, >>>>> - lower, upper); >>>>> - continue; >>>>> - } >>>>> - prop_name = g_strdup_printf("SYSREG_%s_%s", reg->name, field->name); >>>>> - trace_decode_idreg_writemap(field->name, lower, upper, prop_name); >>>>> - object_property_add(obj, prop_name, "uint64", >>>>> - get_sysreg_prop, set_sysreg_prop, NULL, field); >>>>> - g_free(prop_name); >>>>> - nb_sysreg_props++; >>>>> - >>>>> - field_mask = MAKE_64BIT_MASK(lower, field->length); >>>>> - map = map & ~field_mask; >>>>> - i = ctz64(map); >>>>> - } >>>>> - trace_nb_sysreg_props(reg->name, nb_sysreg_props); >>>>> - return 0; >>>>> -} >>>>> - >>>>> -/* analyze the writable mask and generate properties for writable fields */ >>>>> -void kvm_arm_expose_idreg_properties(ARMCPU *cpu, ARM64SysReg *regs) >>>>> -{ >>>>> - Object *obj = OBJECT(cpu); >>>>> - >>>>> - for (int i = 0; i < NUM_ID_IDX; i++) { >>>>> - ARM64SysReg *sysregdesc = ®s[i]; >>>>> - >>>>> - if (sysregdesc->writable_mask) { >>>>> - /* >>>>> - * special case REVIDR_EL1 and AIDR_EL1 which are writable but >>>>> - * do not expose named fields. They will need to be handled >>>>> - * separately >>>>> - */ >>>>> - if (strcmp(sysregdesc->name, "REVIDR_EL1") && >>>>> - strcmp(sysregdesc->name, "AIDR_EL1")) { >>>>> - decode_idreg_writemap(obj, sysregdesc); >>>>> - } >>>>> - } >>>>> - } >>>>> -} >>>>> >>>>> static void >>>>> kvm_arm_get_host_cpu_features(ARMCPU *cpu, ARMHostCPUFeatures *ahcf) >>>>> diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h >>>>> index 2b3474cc36..133a026036 100644 >>>>> --- a/target/arm/kvm_arm.h >>>>> +++ b/target/arm/kvm_arm.h >>>>> @@ -143,14 +143,6 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu); >>>>> void kvm_arm_add_vcpu_properties(ARMCPU *cpu); >>>>> >>>>> typedef struct ARM64SysReg ARM64SysReg; >>>>> -/** >>>>> - * kvm_arm_expose_idreg_properties: >>>>> - * @cpu: The CPU object to generate the properties for >>>>> - * @reg: registers from the host >>>>> - * >>>>> - * analyze the writable mask and generate properties for writable fields >>>>> - */ >>>>> -void kvm_arm_expose_idreg_properties(ARMCPU *cpu, ARM64SysReg *regs); >>>>> >>>>> /** >>>>> * kvm_arm_steal_time_finalize: >>>>> diff --git a/target/arm/trace-events b/target/arm/trace-events >>>>> index e67730de6b..f33b0d821d 100644 >>>>> --- a/target/arm/trace-events >>>>> +++ b/target/arm/trace-events >>>>> @@ -15,11 +15,11 @@ arm_gt_update_irq(int timer, int irqstate) "gt_update_irq: timer %d irqstate %d" >>>>> kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64 >>>>> get_host_cpu_idregs(const char *name, uint64_t value) "scratch vcpu host value for %s is 0x%"PRIx64 >>>>> kvm_arm_writable_idregs_to_cpreg_list(const char *name, uint64_t previous, uint64_t new) "%s overwrite default 0x%"PRIx64" with 0x%"PRIx64 >>>>> -decode_idreg_writemap(const char* name, int lower, int upper, char *prop_name) "%s [%d:%d] is writable (prop %s)" >>>>> + >>>>> +# cpu64.c >>>>> get_sysreg_prop(const char *name, uint64_t value) "%s 0x%"PRIx64 >>>>> set_sysreg_prop(const char *name, uint64_t old, uint64_t mask, uint64_t field_value, uint64_t new) "%s old reg value=0x%"PRIx64" mask=0x%"PRIx64" new field value=0x%"PRIx64" new reg value=0x%"PRIx64 >>>>> nb_sysreg_props(const char *name, int count) "%s: %d SYSREG properties" >>>>> -unexpected_writable_reserved_field(const char *reg_name, const char *field_name, int lower, int upper) "Unexpected writable reserved field: %s.%s [%d,%d], skip it ..." >>>>> >>>>> # cpu.c >>>>> arm_cpu_reset(uint64_t mp_aff) "cpu %" PRIu64