>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


Reply via email to