>From 842f2adb6e4c64d285863f582d5c91862126e1fe Mon Sep 17 00:00:00 2001 From: Khushit Shah <[email protected]> Date: Mon, 25 May 2026 17:52:40 +0530 Subject: [PATCH] target/arm/kvm: rebase -cpu host idregs to the EL2-off host view
When the host kernel supports nested virtualization, the scratch vCPU used to seed cpu->isar for -cpu host is created with KVM_ARM_VCPU_HAS_EL2. limit_nv_id_reg() then forces several ID register fields on that scratch (e.g. ID_AA64MMFR4_EL1.NV_frac, parts of ID_AA64DFR0_EL1) to values that match KVM's nested-virt emulation rather than the raw host capability. If the actual -cpu host vCPU is later created without EL2 (for example under -machine virt,virtualization=off) limit_nv_id_reg() does not run on it. cpu->isar still intialized on top of the scratch vCPU with EL2. This is obivously wrong and leads to error while writeback. To solve this rebase the idregs fields of cpu->isar.idregs[] on top of the scratch vCPU isar without EL2. If the field in cpu->isar.idregs[] is unchanged, it get's set to the scratch vCPU's field value without EL2. Named models are unaffected: their cpu->isar is rebuilt by arm_idregs_reset_to_defaults() which resets the idregs view. Signed-off-by: Khushit Shah <[email protected]> --- And then this. There is also another way to do this, We can track which properties are set by user/model and then try to enforce those on top of the new_isar. This is more concrete than rebasing but did not get into that complexity for v2. I think this is good enough for now. --- target/arm/kvm.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index c26ed485a3..26d20799b4 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -2207,6 +2207,52 @@ bool kvm_arm_mte_supported(void) return kvm_check_extension(kvm_state, KVM_CAP_ARM_MTE); } +/* + * Rebase cpu->isar.idregs[] on top of new_isar->isar. + * + * For fields in cpu->isar.idregs[] that are same as initial_isar->isar, update the + * field with the new_isar->isar values. + * + * cpu->isar.idregs[] is initialized with initial_isar->isar, CPU props + * changes fields values on top of that. + * + * But, initial_isar->isar always enables all the features that are supported + * by host, -cpu host might not enable the same (for example, virtualization/has_el2) + * So the idregs[] view for the actual -cpu host vCPU differs from initial_isar->isar + * This function rebases cpu->isar.idregs[] on top of new_isar->isar. + */ +static void kvm_arm_rebase_host_idregs(ARMCPU *cpu, + const ARMISARegisters *new_isar, + const ARMISARegisters *initial_isar) +{ + for (int i = 0; i < NUM_ID_IDX; i++) { + ArmIdReg *idreg = &arm_idregs[i]; + + for (int j = 0; j < idreg->fields_count; j++) { + ArmIdRegField *field = &idreg->fields[j]; + uint64_t idreg_val; + uint64_t initial_val; + uint64_t new_val; + + arm_idreg_field_read(&cpu->isar, field->idx, &idreg_val); + arm_idreg_field_read(initial_isar, field->idx, &initial_val); + arm_idreg_field_read(new_isar, field->idx, &new_val); + + if (idreg_val == initial_val && new_val != idreg_val) { + /* The cpu->isar.idregs[]'s field val is same as + * ahcf->isar.idregs[]'s field val, which was used to + * initialise it. + * Hence, override it with the cpregs[] value. + */ + arm_idreg_field_write(cpu, field->idx, new_val, &error_abort); + + warn_report("KVM ID %s.%s value changed from 0x%016" PRIx64 " to 0x%016" PRIx64, + idreg->name,field->name, idreg_val, new_val); + } + } + } +} + QEMU_BUILD_BUG_ON(KVM_ARM64_SVE_VQ_MIN != 1); static int kvm_arm_sve_set_vls(ARMCPU *cpu) @@ -2337,6 +2383,15 @@ int kvm_arch_init_vcpu(CPUState *cs) arm_host_cpu_features[cpu->has_el2].isar.idregs[_i]); } + /* For -cpu host/max, rebase the idregs[] on top of new_isar->isar */ + if ((object_dynamic_cast(OBJECT(cpu), TYPE_ARM_HOST_CPU) + || object_dynamic_cast(OBJECT(cpu), TYPE_ARM_MAX_CPU)) + && !cpu->has_el2 + && kvm_arm_el2_supported()) { + /* scratch vCPU is created with EL2-enabled for -cpu host */ + kvm_arm_rebase_host_idregs(cpu, kvm_arm_get_host_isar(false), kvm_arm_get_host_isar(true)); + } + /* validate the cpu->isar.idregs[] against the ahcf->isar.idregs[], select ahcf based on the cpu->has_el2 */ if (!arm_idregs_validate_safe_rule(cpu, &arm_host_cpu_features[cpu->has_el2].isar, &err)) { error_report_err(err); -- 2.52.0
