On 9/21/26 2:32 PM, Khushit Shah wrote:
> Now that all ID register fields are exposed as properties, refactor
> kvm_arm_writable_idregs_to_cpreg_list() as
> kvm_arm_write_idregs_to_cpreg_list() and process each field
> individually. kvm_arm_writable_idregs_to_cpreg_list() assumed that
this does not exist anymore in v9. this later implementation should ease
the handling
Eric
> only writable fields were exposed as SYSREG_ properties.
>
> Copy only fields advertised as writable by KVM instead of replacing the
> whole register. For registers absent from the KVM cpreg list, compare
> the requested value against the host value captured during probing.
>
> Return an error when a non-writable field differs from the host value,
> failing vCPU init.
>
> Signed-off-by: Khushit Shah <[email protected]>
> ---
> target/arm/cpu-idregs.c | 13 +++++++
> target/arm/cpu-idregs.h | 2 ++
> target/arm/kvm.c | 79 +++++++++++++++++++++++++++++++++--------
> target/arm/trace-events | 2 +-
> 4 files changed, 80 insertions(+), 16 deletions(-)
>
> diff --git a/target/arm/cpu-idregs.c b/target/arm/cpu-idregs.c
> index 199d593153..d58b8ed063 100644
> --- a/target/arm/cpu-idregs.c
> +++ b/target/arm/cpu-idregs.c
> @@ -7,6 +7,7 @@
> * SPDX-License-Identifier: GPL-2.0-or-later
> */
> #include "qemu/osdep.h"
> +#include "qemu/bitops.h"
> #include "qemu/error-report.h"
> #include "qapi/error.h"
> #include "cpu.h"
> @@ -94,3 +95,15 @@ ARM64SysReg arm64_id_regs[NUM_ID_IDX] = {
> #include "cpu-idregs.h.inc"
> };
>
> +uint64_t arm_get_field_mask(const ARM64SysRegField *field)
> +{
> + return MAKE_64BIT_MASK(field->shift, field->length);
> +}
> +
> +bool arm_field_is_writable(const ARM64SysRegField *field)
> +{
> + uint64_t field_mask = arm_get_field_mask(field);
> +
> + return (arm64_id_regs[field->index].writable_mask & field_mask)
> + == field_mask;
> +}
> diff --git a/target/arm/cpu-idregs.h b/target/arm/cpu-idregs.h
> index 98c7a7518f..aab1a79db1 100644
> --- a/target/arm/cpu-idregs.h
> +++ b/target/arm/cpu-idregs.h
> @@ -33,4 +33,6 @@ typedef struct ARM64SysReg {
> */
> extern ARM64SysReg arm64_id_regs[NUM_ID_IDX];
>
> +uint64_t arm_get_field_mask(const ARM64SysRegField *field);
> +bool arm_field_is_writable(const ARM64SysRegField *field);
> #endif
> diff --git a/target/arm/kvm.c b/target/arm/kvm.c
> index 07df6626d4..2d94b3a884 100644
> --- a/target/arm/kvm.c
> +++ b/target/arm/kvm.c
> @@ -1006,6 +1006,16 @@ static uint64_t *kvm_arm_get_cpreg_ptr(ARMCPU *cpu,
> uint64_t regidx)
> return &cpu->cpreg_values[res - cpu->cpreg_indexes];
> }
>
> +/* Like kvm_arm_get_cpreg_ptr, but returns NULL if the register is not
> found. */
> +static uint64_t *kvm_arm_find_cpreg_ptr(ARMCPU *cpu, uint64_t regidx)
> +{
> + uint64_t *res;
> + res = bsearch(®idx, cpu->cpreg_indexes, cpu->cpreg_array_len,
> + sizeof(uint64_t), compare_u64);
> +
> + return res ? &cpu->cpreg_values[res - cpu->cpreg_indexes] : NULL;
> +}
> +
> /**
> * kvm_arm_reg_syncs_via_cpreg_list:
> * @regidx: KVM register index
> @@ -1308,31 +1318,67 @@ bool kvm_arm_cpu_post_load(ARMCPU *cpu)
> }
>
> /*
> - * Copy writable ID regs from isar.idregs[] to cpreg_list
> - * in case their value differs from the original init cpreg value
> + * Copy writable ID reg fields from isar.idregs[] into the KVM cpreg list,
> + * so the subsequent write_list_to_kvmstate() pushes them to KVM.
> + * Only writable fields are copied.
> + * Returns -1 if any vCPU's ID reg value differs from the host value and the
> + * field is not writable.
> */
> -static void kvm_arm_writable_idregs_to_cpreg_list(ARMCPU *cpu)
> +static int kvm_arm_write_idregs_to_cpreg_list(ARMCPU *cpu)
> {
> for (int i = 0; i < NUM_ID_IDX; i++) {
> ARM64SysReg *sysregdesc = &arm64_id_regs[i];
> ARMSysRegs sysreg = id_register_sysreg[i];
> - uint64_t previous, new;
> + uint64_t writable_mask = sysregdesc->writable_mask;
> + uint64_t desired = cpu->isar.idregs[i];
> + uint64_t previous, updated;
> uint64_t *cpreg;
>
> - if (!sysregdesc->writable_mask) {
> + cpreg = kvm_arm_find_cpreg_ptr(cpu,
> idregs_sysreg_to_kvm_reg(sysreg));
> + /*
> + * Registers such as DCZID_EL0 are not exposed through the cpreg
> + * list and are therefore not writable. Use the host value
> + * snapshotted at probe time as the reference to check the vCPU ID
> + * regs have the same value.
> + */
> + previous = cpreg ? *cpreg : arm_host_cpu_features.isar.idregs[i];
> +
> + if (previous == desired) {
> continue;
> }
>
> - cpreg = kvm_arm_get_cpreg_ptr(cpu, idregs_sysreg_to_kvm_reg(sysreg));
> - previous = *cpreg;
> - new = cpu->isar.idregs[i];
> + for (int j = 0; j < sysregdesc->fields_count; j++) {
> + const ARM64SysRegField *field = &sysregdesc->fields[j];
> + uint64_t field_mask = arm_get_field_mask(field);
> + uint64_t prev_val = previous & field_mask;
> + uint64_t new_val = desired & field_mask;
> +
> + if (prev_val == new_val) {
> + continue;
> + }
> +
> + if (!arm_field_is_writable(field)) {
> + error_report("%s.%s is not writable: host=0x%" PRIx64
> + ", requested=0x%" PRIx64,
> + sysregdesc->name, field->name,
> + prev_val >> field->shift, new_val >>
> field->shift);
> + return -1;
> + }
> + }
> +
> + if (!cpreg || !writable_mask) {
> + continue;
> + }
>
> - if (previous != new) {
> - *cpreg = new;
> - trace_kvm_arm_writable_idregs_to_cpreg_list(sysregdesc->name,
> - previous, new);
> - }
> + updated = (previous & ~writable_mask) | (desired & writable_mask);
> + if (updated != previous) {
> + *cpreg = updated;
> + trace_kvm_arm_write_idregs_to_cpreg_list(sysregdesc->name,
> + previous, updated);
> + }
> }
> +
> + return 0;
> }
>
> void kvm_arm_reset_vcpu(ARMCPU *cpu)
> @@ -2293,8 +2339,11 @@ int kvm_arch_init_vcpu(CPUState *cs)
> if (ret) {
> return ret;
> }
> - /* overwrite writable ID regs with their updated property values */
> - kvm_arm_writable_idregs_to_cpreg_list(cpu);
> + /* overwrite ID reg fields with their updated property values */
> + ret = kvm_arm_write_idregs_to_cpreg_list(cpu);
> + if (ret) {
> + return ret;
> + }
> ret = write_list_to_kvmstate(cpu, KVM_PUT_FULL_STATE);
> if (!ret) {
> return -1;
> diff --git a/target/arm/trace-events b/target/arm/trace-events
> index 2bb7766f7f..7b54e34ad9 100644
> --- a/target/arm/trace-events
> +++ b/target/arm/trace-events
> @@ -15,7 +15,7 @@ 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
> get_host_cpu_idreg_not_found(const char *name) "host %s register not
> readable from KVM"
> -kvm_arm_writable_idregs_to_cpreg_list(const char *name, uint64_t previous,
> uint64_t new) "%s overwrite default 0x%"PRIx64" with 0x%"PRIx64
> +kvm_arm_write_idregs_to_cpreg_list(const char *name, uint64_t previous,
> uint64_t new) "%s overwrite default 0x%"PRIx64" with 0x%"PRIx64
> kvm_idreg_write_scratch_vcpu(const char *name, uint64_t oldfv, uint64_t
> newfv, uint64_t actualfv) "successful dry-run for %s old=0x%"PRIx64"
> new=0x%"PRIx64" actual=0x%"PRIx64
>
> # cpu64.c