From patchwork Tue Aug 2 07:47:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Xiaoyao Li X-Patchwork-Id: 12934307 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C4EC4C3F6B0 for ; Tue, 2 Aug 2022 07:49:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236230AbiHBHtO (ORCPT ); Tue, 2 Aug 2022 03:49:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54710 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236235AbiHBHsy (ORCPT ); Tue, 2 Aug 2022 03:48:54 -0400 Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B2FFC4D4D2 for ; Tue, 2 Aug 2022 00:48:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1659426527; x=1690962527; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=2UpUGlnIoZBfsVq1PYm2rNQFoN9MB+P4Gck+oH7AHhA=; b=I9GMfeDH7vBDeE/fe1POOOb+puQen2OJCMLrbqZDbArewXRtsGtVlIJS 2UwaM+t8icMFbsjLptF/LTn8Hbb34YOqgO4A1VULfiH2VLnlmCKT8fvMe xipxHHLsa7hrwXo9+E5o/B3JY1k4+SW3nt3TAIDBY+ANPnHTsE1bTxUYI Thf+OLM5rHk3xUc5g4HIew0eGCEoR6K14DMegCWsdHpN/30ZyA0oBQhe8 GSTmyQlCY+yRk7XkWU+jgGvnyJN1JU5O0cEjTc3pNrIcbVuzsoRXEoRvT swE11MXbJHxM5zbYKcCAX+e9pG74EWxk5BMrw8naIWaY4Z399ZMNDFFqW g==; X-IronPort-AV: E=McAfee;i="6400,9594,10426"; a="286908551" X-IronPort-AV: E=Sophos;i="5.93,210,1654585200"; d="scan'208";a="286908551" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Aug 2022 00:48:47 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.93,210,1654585200"; d="scan'208";a="630603961" Received: from lxy-dell.sh.intel.com ([10.239.48.38]) by orsmga008.jf.intel.com with ESMTP; 02 Aug 2022 00:48:42 -0700 From: Xiaoyao Li To: Paolo Bonzini , Isaku Yamahata , Gerd Hoffmann , =?utf-8?q?Daniel_P_=2E_Berrang=C3=A9?= , =?utf-8?q?Phi?= =?utf-8?q?lippe_Mathieu-Daud=C3=A9?= , Richard Henderson , "Michael S . Tsirkin" , Marcel Apfelbaum , Cornelia Huck , Marcelo Tosatti , Laszlo Ersek , Eric Blake Cc: Connor Kuehl , erdemaktas@google.com, kvm@vger.kernel.org, qemu-devel@nongnu.org, seanjc@google.com, xiaoyao.li@intel.com Subject: [PATCH v1 12/40] i386/kvm: Move architectural CPUID leaf generation to separate helper Date: Tue, 2 Aug 2022 15:47:22 +0800 Message-Id: <20220802074750.2581308-13-xiaoyao.li@intel.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20220802074750.2581308-1-xiaoyao.li@intel.com> References: <20220802074750.2581308-1-xiaoyao.li@intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Sean Christopherson Move the architectural (for lack of a better term) CPUID leaf generation to a separate helper so that the generation code can be reused by TDX, which needs to generate a canonical VM-scoped configuration. Signed-off-by: Sean Christopherson Signed-off-by: Xiaoyao Li --- target/i386/kvm/kvm.c | 220 +++++++++++++++++++------------------ target/i386/kvm/kvm_i386.h | 3 + 2 files changed, 118 insertions(+), 105 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 9930902ae890..9c0d5be5cc23 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1728,115 +1728,21 @@ static void kvm_init_xsave(CPUX86State *env) env->xsave_buf_len); } -int kvm_arch_init_vcpu(CPUState *cs) +uint32_t kvm_x86_arch_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries, + uint32_t cpuid_i) { - struct { - struct kvm_cpuid2 cpuid; - struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; - } cpuid_data; - /* - * The kernel defines these structs with padding fields so there - * should be no extra padding in our cpuid_data struct. - */ - QEMU_BUILD_BUG_ON(sizeof(cpuid_data) != - sizeof(struct kvm_cpuid2) + - sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); - - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - uint32_t limit, i, j, cpuid_i; + uint32_t limit, i, j; uint32_t unused; struct kvm_cpuid_entry2 *c; - uint32_t signature[3]; - int kvm_base = KVM_CPUID_SIGNATURE; - int max_nested_state_len; - int r; - Error *local_err = NULL; - - memset(&cpuid_data, 0, sizeof(cpuid_data)); - - cpuid_i = 0; - - has_xsave2 = kvm_check_extension(cs->kvm_state, KVM_CAP_XSAVE2); - - r = kvm_arch_set_tsc_khz(cs); - if (r < 0) { - return r; - } - - /* vcpu's TSC frequency is either specified by user, or following - * the value used by KVM if the former is not present. In the - * latter case, we query it from KVM and record in env->tsc_khz, - * so that vcpu's TSC frequency can be migrated later via this field. - */ - if (!env->tsc_khz) { - r = kvm_check_extension(cs->kvm_state, KVM_CAP_GET_TSC_KHZ) ? - kvm_vcpu_ioctl(cs, KVM_GET_TSC_KHZ) : - -ENOTSUP; - if (r > 0) { - env->tsc_khz = r; - } - } - - env->apic_bus_freq = KVM_APIC_BUS_FREQUENCY; - - /* - * kvm_hyperv_expand_features() is called here for the second time in case - * KVM_CAP_SYS_HYPERV_CPUID is not supported. While we can't possibly handle - * 'query-cpu-model-expansion' in this case as we don't have a KVM vCPU to - * check which Hyper-V enlightenments are supported and which are not, we - * can still proceed and check/expand Hyper-V enlightenments here so legacy - * behavior is preserved. - */ - if (!kvm_hyperv_expand_features(cpu, &local_err)) { - error_report_err(local_err); - return -ENOSYS; - } - - if (hyperv_enabled(cpu)) { - r = hyperv_init_vcpu(cpu); - if (r) { - return r; - } - - cpuid_i = hyperv_fill_cpuids(cs, cpuid_data.entries); - kvm_base = KVM_CPUID_SIGNATURE_NEXT; - has_msr_hv_hypercall = true; - } - - if (cpu->expose_kvm) { - memcpy(signature, "KVMKVMKVM\0\0\0", 12); - c = &cpuid_data.entries[cpuid_i++]; - c->function = KVM_CPUID_SIGNATURE | kvm_base; - c->eax = KVM_CPUID_FEATURES | kvm_base; - c->ebx = signature[0]; - c->ecx = signature[1]; - c->edx = signature[2]; - - c = &cpuid_data.entries[cpuid_i++]; - c->function = KVM_CPUID_FEATURES | kvm_base; - c->eax = env->features[FEAT_KVM]; - c->edx = env->features[FEAT_KVM_HINTS]; - } cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); - if (cpu->kvm_pv_enforce_cpuid) { - r = kvm_vcpu_enable_cap(cs, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 0, 1); - if (r < 0) { - fprintf(stderr, - "failed to enable KVM_CAP_ENFORCE_PV_FEATURE_CPUID: %s", - strerror(-r)); - abort(); - } - } - for (i = 0; i <= limit; i++) { if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { fprintf(stderr, "unsupported level value: 0x%x\n", limit); abort(); } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; switch (i) { case 2: { @@ -1855,7 +1761,7 @@ int kvm_arch_init_vcpu(CPUState *cs) "cpuid(eax:2):eax & 0xf = 0x%x\n", times); abort(); } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; c->function = i; c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC; cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); @@ -1901,7 +1807,7 @@ int kvm_arch_init_vcpu(CPUState *cs) "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); abort(); } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; } break; case 0x7: @@ -1921,7 +1827,7 @@ int kvm_arch_init_vcpu(CPUState *cs) "cpuid(eax:0x12,ecx:0x%x)\n", j); abort(); } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; } break; case 0x14: @@ -1941,7 +1847,7 @@ int kvm_arch_init_vcpu(CPUState *cs) "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); abort(); } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; c->function = i; c->index = j; c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; @@ -1998,7 +1904,7 @@ int kvm_arch_init_vcpu(CPUState *cs) fprintf(stderr, "unsupported xlevel value: 0x%x\n", limit); abort(); } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; switch (i) { case 0x8000001d: @@ -2017,7 +1923,7 @@ int kvm_arch_init_vcpu(CPUState *cs) "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); abort(); } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; } break; default: @@ -2044,7 +1950,7 @@ int kvm_arch_init_vcpu(CPUState *cs) fprintf(stderr, "unsupported xlevel2 value: 0x%x\n", limit); abort(); } - c = &cpuid_data.entries[cpuid_i++]; + c = &entries[cpuid_i++]; c->function = i; c->flags = 0; @@ -2052,6 +1958,110 @@ int kvm_arch_init_vcpu(CPUState *cs) } } + return cpuid_i; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + struct { + struct kvm_cpuid2 cpuid; + struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; + } cpuid_data; + /* + * The kernel defines these structs with padding fields so there + * should be no extra padding in our cpuid_data struct. + */ + QEMU_BUILD_BUG_ON(sizeof(cpuid_data) != + sizeof(struct kvm_cpuid2) + + sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); + + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + uint32_t cpuid_i; + struct kvm_cpuid_entry2 *c; + uint32_t signature[3]; + int kvm_base = KVM_CPUID_SIGNATURE; + int max_nested_state_len; + int r; + Error *local_err = NULL; + + memset(&cpuid_data, 0, sizeof(cpuid_data)); + + cpuid_i = 0; + + has_xsave2 = kvm_check_extension(cs->kvm_state, KVM_CAP_XSAVE2); + + r = kvm_arch_set_tsc_khz(cs); + if (r < 0) { + return r; + } + + /* vcpu's TSC frequency is either specified by user, or following + * the value used by KVM if the former is not present. In the + * latter case, we query it from KVM and record in env->tsc_khz, + * so that vcpu's TSC frequency can be migrated later via this field. + */ + if (!env->tsc_khz) { + r = kvm_check_extension(cs->kvm_state, KVM_CAP_GET_TSC_KHZ) ? + kvm_vcpu_ioctl(cs, KVM_GET_TSC_KHZ) : + -ENOTSUP; + if (r > 0) { + env->tsc_khz = r; + } + } + + env->apic_bus_freq = KVM_APIC_BUS_FREQUENCY; + + /* + * kvm_hyperv_expand_features() is called here for the second time in case + * KVM_CAP_SYS_HYPERV_CPUID is not supported. While we can't possibly handle + * 'query-cpu-model-expansion' in this case as we don't have a KVM vCPU to + * check which Hyper-V enlightenments are supported and which are not, we + * can still proceed and check/expand Hyper-V enlightenments here so legacy + * behavior is preserved. + */ + if (!kvm_hyperv_expand_features(cpu, &local_err)) { + error_report_err(local_err); + return -ENOSYS; + } + + if (hyperv_enabled(cpu)) { + r = hyperv_init_vcpu(cpu); + if (r) { + return r; + } + + cpuid_i = hyperv_fill_cpuids(cs, cpuid_data.entries); + kvm_base = KVM_CPUID_SIGNATURE_NEXT; + has_msr_hv_hypercall = true; + } + + if (cpu->expose_kvm) { + memcpy(signature, "KVMKVMKVM\0\0\0", 12); + c = &cpuid_data.entries[cpuid_i++]; + c->function = KVM_CPUID_SIGNATURE | kvm_base; + c->eax = KVM_CPUID_FEATURES | kvm_base; + c->ebx = signature[0]; + c->ecx = signature[1]; + c->edx = signature[2]; + + c = &cpuid_data.entries[cpuid_i++]; + c->function = KVM_CPUID_FEATURES | kvm_base; + c->eax = env->features[FEAT_KVM]; + c->edx = env->features[FEAT_KVM_HINTS]; + } + + if (cpu->kvm_pv_enforce_cpuid) { + r = kvm_vcpu_enable_cap(cs, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 0, 1); + if (r < 0) { + fprintf(stderr, + "failed to enable KVM_CAP_ENFORCE_PV_FEATURE_CPUID: %s", + strerror(-r)); + abort(); + } + } + + cpuid_i = kvm_x86_arch_cpuid(env, cpuid_data.entries, cpuid_i); cpuid_data.cpuid.nent = cpuid_i; if (((env->cpuid_version >> 8)&0xF) >= 6 diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 6b24ab2a7813..c77dd7a95a7c 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -26,6 +26,9 @@ #define kvm_ioapic_in_kernel() \ (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split()) +uint32_t kvm_x86_arch_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries, + uint32_t cpuid_i); + #else #define kvm_pit_in_kernel() 0