>From 92b813355251d61ab7941a8df500ab35c0a34193 Mon Sep 17 00:00:00 2001 From: Khushit Shah <[email protected]> Date: Mon, 25 May 2026 17:51:54 +0530 Subject: [PATCH] target/arm/kvm: cache host ID register views with and without EL2
kvm_arm_get_host_cpu_features() probes the host by spinning up a scratch vCPU and reading its ID registers. When the host kernel advertises nested virtualization, the scratch was always created with KVM_ARM_VCPU_HAS_EL2, which makes the kernel run limit_nv_id_reg() on that vCPU and force several ID register fields (e.g. ID_AA64MMFR4_EL1.NV_frac, parts of ID_AA64DFR0_EL1, etc.) to values that match KVM's nested-virt emulation rather than the raw host capability. Split the cache into two slots indexed by with_el2 (lazily populated) and plumb the selector through kvm_arm_set_cpu_features_from_host() and kvm_arm_get_host_isar(). Callers pick the view that matches their intent. Signed-off-by: Khushit Shah <[email protected]> --- Patch 1, part of RFC v2 for named models for arm. will not cleanly apply but for the gist of the solution. --- target/arm/arm-cpu-models.c | 5 +++-- target/arm/arm-qmp-cmds.c | 4 ++-- target/arm/cpu64.c | 2 +- target/arm/kvm-stub.c | 7 ++++++- target/arm/kvm.c | 37 +++++++++++++++++++++---------------- target/arm/kvm_arm.h | 5 +++-- 6 files changed, 36 insertions(+), 24 deletions(-) diff --git a/target/arm/arm-cpu-models.c b/target/arm/arm-cpu-models.c index 9265bb776a..31765be55b 100644 --- a/target/arm/arm-cpu-models.c +++ b/target/arm/arm-cpu-models.c @@ -549,7 +549,8 @@ static void arm_named_cpu_initfn(Object *obj) return; } - kvm_arm_set_cpu_features_from_host(cpu); + /* For now let's not support nested virt for named models */ + kvm_arm_set_cpu_features_from_host(cpu, false); if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { return; } @@ -592,7 +593,7 @@ void arm_cpu_class_check_missing_features(ARMCPUClass *acc, strList **blockers) } } - arm_idregs_get_blockers(cpu, kvm_arm_get_host_isar(), blockers); + arm_idregs_get_blockers(cpu, kvm_arm_get_host_isar(false), blockers); out: object_unref(obj); } diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c index 84341a2105..453442ab93 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -82,7 +82,7 @@ CpuPropSupportedValues *qmp_query_cpu_props_supported_values(Error **errp) QList *sv = qlist_new(); arm_prop_append_supported_values(pdesc, - kvm_arm_get_host_isar(), sv); + kvm_arm_get_host_isar(false), sv); qdict_put_obj(props, pdesc->name, QOBJECT(sv)); } @@ -119,7 +119,7 @@ CpuPropInfoList *qmp_query_arm_cpu_named_model_prop_info(Error **errp) sv = qlist_new(); arm_prop_append_supported_values(pdesc, - kvm_arm_get_host_isar(), sv); + kvm_arm_get_host_isar(false), sv); prop_info->supported_values = QOBJECT(sv); prop_info->composite = NULL; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 109cead3c8..16b4d87dc1 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -875,7 +875,7 @@ static void aarch64_host_initfn(Object *obj) #if defined(CONFIG_KVM) kvm_arm_set_cpreg_mig_tolerances(cpu); - kvm_arm_set_cpu_features_from_host(cpu); + kvm_arm_set_cpu_features_from_host(cpu, true); aarch64_add_sve_properties(obj); arm_add_cpu_props(obj); #elif defined(CONFIG_HVF) diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c index 88cbe8d85c..98f41818ba 100644 --- a/target/arm/kvm-stub.c +++ b/target/arm/kvm-stub.c @@ -45,7 +45,7 @@ bool kvm_arm_el2_supported(void) /* * These functions should never actually be called without KVM support. */ -void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) +void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu, bool with_el2) { g_assert_not_reached(); } @@ -119,3 +119,8 @@ char *kvm_print_register_name(uint64_t regidx) { g_assert_not_reached(); } + +const ARMISARegisters *kvm_arm_get_host_isar(bool with_el2) +{ + return NULL; +} diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 9f8368712f..c26ed485a3 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -65,7 +65,7 @@ typedef struct ARMHostCPUFeatures { const char *dtb_compatible; } ARMHostCPUFeatures; -static ARMHostCPUFeatures arm_host_cpu_features; +static ARMHostCPUFeatures arm_host_cpu_features[2]; #define DEF(NAME, OP0, OP1, CRN, CRM, OP2) [NAME##_IDX] = #NAME, const char * const sysreg_names[NUM_ID_IDX] = { @@ -349,7 +349,7 @@ static uint32_t kvm_arm_sve_get_vls(int fd) return vls[0] & MAKE_64BIT_MASK(0, ARM_MAX_VQ); } -static void kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) +static void kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf, bool with_el2) { /* Identify the feature bits corresponding to the host CPU, and * fill out the ARMHostCPUClass fields accordingly. To do this @@ -389,7 +389,7 @@ static void kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * Ask for EL2 if supported. */ el2_supported = kvm_arm_el2_supported(); - if (el2_supported) { + if (el2_supported && with_el2) { init.features[0] |= 1 << KVM_ARM_VCPU_HAS_EL2; } @@ -528,7 +528,7 @@ static void kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err |= get_host_cpu_reg(fd, ahcf, ID_AA64ZFR0_EL1_IDX); /* Read the set of supported vector lengths. */ - arm_host_cpu_features.sve_vq_supported = kvm_arm_sve_get_vls(fd); + ahcf->sve_vq_supported = kvm_arm_sve_get_vls(fd); } } /* @@ -586,15 +586,15 @@ static void kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ahcf->features = features; } -void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) +void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu, bool with_el2) { CPUARMState *env = &cpu->env; - if (!arm_host_cpu_features.dtb_compatible) { - kvm_arm_get_host_cpu_features(&arm_host_cpu_features); + if (!arm_host_cpu_features[with_el2].dtb_compatible) { + kvm_arm_get_host_cpu_features(&arm_host_cpu_features[with_el2], with_el2); } - cpu->kvm_target = arm_host_cpu_features.target; + cpu->kvm_target = arm_host_cpu_features[with_el2].target; if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) { /* @@ -605,15 +605,18 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) return; } - cpu->dtb_compatible = arm_host_cpu_features.dtb_compatible; - cpu->isar = arm_host_cpu_features.isar; - cpu->sve_vq.supported = arm_host_cpu_features.sve_vq_supported; - env->features = arm_host_cpu_features.features; + cpu->dtb_compatible = arm_host_cpu_features[with_el2].dtb_compatible; + cpu->isar = arm_host_cpu_features[with_el2].isar; + cpu->sve_vq.supported = arm_host_cpu_features[with_el2].sve_vq_supported; + env->features = arm_host_cpu_features[with_el2].features; } -const ARMISARegisters *kvm_arm_get_host_isar(void) +const ARMISARegisters *kvm_arm_get_host_isar(bool with_el2) { - return &arm_host_cpu_features.isar; + if (!arm_host_cpu_features[with_el2].dtb_compatible) { + kvm_arm_get_host_cpu_features(&arm_host_cpu_features[with_el2], with_el2); + } + return &arm_host_cpu_features[with_el2].isar; } static bool kvm_no_adjvtime_get(Object *obj, Error **errp) @@ -2331,9 +2334,11 @@ int kvm_arch_init_vcpu(CPUState *cs) " host=0x%016" PRIx64 "\n", _i, sysreg_names[_i] ? sysreg_names[_i] : "(null)", cpu->isar.idregs[_i], - arm_host_cpu_features.isar.idregs[_i]); + arm_host_cpu_features[cpu->has_el2].isar.idregs[_i]); } - if (!arm_idregs_validate_safe_rule(cpu, &arm_host_cpu_features.isar, &err)) { + + /* 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); return -EINVAL; } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 750dbe1ef4..538f87e69e 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -129,11 +129,12 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray); /** * kvm_arm_set_cpu_features_from_host: * @cpu: ARMCPU to set the features for + * @with_el2: probe the host with the EL2-enabled scratch vCPU view * * Set up the ARMCPU struct fields up to match the information probed * from the host CPU. */ -void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu); +void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu, bool with_el2); /** * kvm_arm_add_vcpu_properties: @@ -231,7 +232,7 @@ void kvm_arm_enable_mte(Object *cpuobj, Error **errp); void arm_cpu_kvm_set_irq(void *arm_cpu, int irq, int level); -const ARMISARegisters *kvm_arm_get_host_isar(void); +const ARMISARegisters *kvm_arm_get_host_isar(bool with_el2); void arm_gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3); -- 2.52.0
