public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: Paolo Bonzini <pbonzini@redhat.com>,
	Sean Christopherson <seanjc@google.com>,
	 Vitaly Kuznetsov <vkuznets@redhat.com>,
	Jarkko Sakkinen <jarkko@kernel.org>
Cc: kvm@vger.kernel.org, linux-sgx@vger.kernel.org,
	 linux-kernel@vger.kernel.org,
	Maxim Levitsky <mlevitsk@redhat.com>,
	 Hou Wenlong <houwenlong.hwl@antgroup.com>,
	Xiaoyao Li <xiaoyao.li@intel.com>,
	 Kechen Lu <kechenl@nvidia.com>,
	Oliver Upton <oliver.upton@linux.dev>,
	 Binbin Wu <binbin.wu@linux.intel.com>,
	Yang Weijiang <weijiang.yang@intel.com>,
	 Robert Hoo <robert.hoo.linux@gmail.com>
Subject: [PATCH v3 41/57] KVM: x86: Initialize guest cpu_caps based on guest CPUID
Date: Wed, 27 Nov 2024 17:34:08 -0800	[thread overview]
Message-ID: <20241128013424.4096668-42-seanjc@google.com> (raw)
In-Reply-To: <20241128013424.4096668-1-seanjc@google.com>

Initialize a vCPU's capabilities based on the guest CPUID provided by
userspace instead of simply zeroing the entire array.  This is the first
step toward using cpu_caps to query *all* CPUID-based guest capabilities,
i.e. will allow converting all usage of guest_cpuid_has() to
guest_cpu_cap_has().

Zeroing the array was the logical choice when using cpu_caps was opt-in,
e.g. "unsupported" was generally a safer default, and the whole point of
governed features is that KVM would need to check host and guest support,
i.e. making everything unsupported by default didn't require more code.

But requiring KVM to manually "enable" every CPUID-based feature in
cpu_caps would require an absurd amount of boilerplate code.

Follow existing CPUID/kvm_cpu_caps nomenclature where possible, e.g. for
the change() and clear() APIs.  Replace check_and_set() with constrain()
to try and capture that KVM is constraining userspace's desired guest
feature set based on KVM's capabilities.

This is intended to be gigantic nop, i.e. should not have any impact on
guest or KVM functionality.

This is also an intermediate step; a future commit will also incorporate
KVM support into the vCPU's cpu_caps before converting guest_cpuid_has()
to guest_cpu_cap_has().

Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/cpuid.c   | 46 ++++++++++++++++++++++++++++++++++++++++--
 arch/x86/kvm/cpuid.h   | 24 +++++++++++++++++++---
 arch/x86/kvm/svm/svm.c | 28 +++++++++++++------------
 arch/x86/kvm/vmx/vmx.c |  8 +++++---
 4 files changed, 85 insertions(+), 21 deletions(-)

diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index f0721ad84a18..803d89577e6f 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -333,13 +333,56 @@ static bool guest_cpuid_is_amd_or_hygon(struct kvm_vcpu *vcpu)
 	       is_guest_vendor_hygon(entry->ebx, entry->ecx, entry->edx);
 }
 
+/*
+ * This isn't truly "unsafe", but except for the cpu_caps initialization code,
+ * all register lookups should use __cpuid_entry_get_reg(), which provides
+ * compile-time validation of the input.
+ */
+static u32 cpuid_get_reg_unsafe(struct kvm_cpuid_entry2 *entry, u32 reg)
+{
+	switch (reg) {
+	case CPUID_EAX:
+		return entry->eax;
+	case CPUID_EBX:
+		return entry->ebx;
+	case CPUID_ECX:
+		return entry->ecx;
+	case CPUID_EDX:
+		return entry->edx;
+	default:
+		WARN_ON_ONCE(1);
+		return 0;
+	}
+}
+
 void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 {
 	struct kvm_lapic *apic = vcpu->arch.apic;
 	struct kvm_cpuid_entry2 *best;
+	struct kvm_cpuid_entry2 *entry;
 	bool allow_gbpages;
+	int i;
 
 	memset(vcpu->arch.cpu_caps, 0, sizeof(vcpu->arch.cpu_caps));
+	BUILD_BUG_ON(ARRAY_SIZE(reverse_cpuid) != NR_KVM_CPU_CAPS);
+
+	/*
+	 * Reset guest capabilities to userspace's guest CPUID definition, i.e.
+	 * honor userspace's definition for features that don't require KVM or
+	 * hardware management/support (or that KVM simply doesn't care about).
+	 */
+	for (i = 0; i < NR_KVM_CPU_CAPS; i++) {
+		const struct cpuid_reg cpuid = reverse_cpuid[i];
+
+		if (!cpuid.function)
+			continue;
+
+		entry = kvm_find_cpuid_entry_index(vcpu, cpuid.function, cpuid.index);
+		if (!entry)
+			continue;
+
+		vcpu->arch.cpu_caps[i] = cpuid_get_reg_unsafe(entry, cpuid.reg);
+	}
 
 	kvm_update_cpuid_runtime(vcpu);
 
@@ -356,8 +399,7 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 	 */
 	allow_gbpages = tdp_enabled ? boot_cpu_has(X86_FEATURE_GBPAGES) :
 				      guest_cpuid_has(vcpu, X86_FEATURE_GBPAGES);
-	if (allow_gbpages)
-		guest_cpu_cap_set(vcpu, X86_FEATURE_GBPAGES);
+	guest_cpu_cap_change(vcpu, X86_FEATURE_GBPAGES, allow_gbpages);
 
 	best = kvm_find_cpuid_entry(vcpu, 1);
 	if (best && apic) {
diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h
index 0a9c3086539b..8c9d6be8cb58 100644
--- a/arch/x86/kvm/cpuid.h
+++ b/arch/x86/kvm/cpuid.h
@@ -245,11 +245,29 @@ static __always_inline void guest_cpu_cap_set(struct kvm_vcpu *vcpu,
 	vcpu->arch.cpu_caps[x86_leaf] |= __feature_bit(x86_feature);
 }
 
-static __always_inline void guest_cpu_cap_check_and_set(struct kvm_vcpu *vcpu,
-							unsigned int x86_feature)
+static __always_inline void guest_cpu_cap_clear(struct kvm_vcpu *vcpu,
+						unsigned int x86_feature)
 {
-	if (kvm_cpu_cap_has(x86_feature) && guest_cpuid_has(vcpu, x86_feature))
+	unsigned int x86_leaf = __feature_leaf(x86_feature);
+
+	vcpu->arch.cpu_caps[x86_leaf] &= ~__feature_bit(x86_feature);
+}
+
+static __always_inline void guest_cpu_cap_change(struct kvm_vcpu *vcpu,
+						 unsigned int x86_feature,
+						 bool guest_has_cap)
+{
+	if (guest_has_cap)
 		guest_cpu_cap_set(vcpu, x86_feature);
+	else
+		guest_cpu_cap_clear(vcpu, x86_feature);
+}
+
+static __always_inline void guest_cpu_cap_constrain(struct kvm_vcpu *vcpu,
+						    unsigned int x86_feature)
+{
+	if (!kvm_cpu_cap_has(x86_feature))
+		guest_cpu_cap_clear(vcpu, x86_feature);
 }
 
 static __always_inline bool guest_cpu_cap_has(struct kvm_vcpu *vcpu,
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index f96c62a9d2c2..3b94cb6c2b7a 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -4401,27 +4401,29 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 	 * XSS on VM-Enter/VM-Exit.  Failure to do so would effectively give
 	 * the guest read/write access to the host's XSS.
 	 */
-	if (boot_cpu_has(X86_FEATURE_XSAVE) &&
-	    boot_cpu_has(X86_FEATURE_XSAVES) &&
-	    guest_cpuid_has(vcpu, X86_FEATURE_XSAVE))
-		guest_cpu_cap_set(vcpu, X86_FEATURE_XSAVES);
+	guest_cpu_cap_change(vcpu, X86_FEATURE_XSAVES,
+			     boot_cpu_has(X86_FEATURE_XSAVE) &&
+			     boot_cpu_has(X86_FEATURE_XSAVES) &&
+			     guest_cpuid_has(vcpu, X86_FEATURE_XSAVE));
 
-	guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_NRIPS);
-	guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_TSCRATEMSR);
-	guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_LBRV);
+	guest_cpu_cap_constrain(vcpu, X86_FEATURE_NRIPS);
+	guest_cpu_cap_constrain(vcpu, X86_FEATURE_TSCRATEMSR);
+	guest_cpu_cap_constrain(vcpu, X86_FEATURE_LBRV);
 
 	/*
 	 * Intercept VMLOAD if the vCPU model is Intel in order to emulate that
 	 * VMLOAD drops bits 63:32 of SYSENTER (ignoring the fact that exposing
 	 * SVM on Intel is bonkers and extremely unlikely to work).
 	 */
-	if (!guest_cpuid_is_intel_compatible(vcpu))
-		guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD);
+	if (guest_cpuid_is_intel_compatible(vcpu))
+		guest_cpu_cap_clear(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD);
+	else
+		guest_cpu_cap_constrain(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD);
 
-	guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_PAUSEFILTER);
-	guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_PFTHRESHOLD);
-	guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_VGIF);
-	guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_VNMI);
+	guest_cpu_cap_constrain(vcpu, X86_FEATURE_PAUSEFILTER);
+	guest_cpu_cap_constrain(vcpu, X86_FEATURE_PFTHRESHOLD);
+	guest_cpu_cap_constrain(vcpu, X86_FEATURE_VGIF);
+	guest_cpu_cap_constrain(vcpu, X86_FEATURE_VNMI);
 
 	svm_recalc_instruction_intercepts(vcpu, svm);
 
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index ccba522246c3..8b95ba323a17 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -7830,10 +7830,12 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 	 */
 	if (boot_cpu_has(X86_FEATURE_XSAVE) &&
 	    guest_cpuid_has(vcpu, X86_FEATURE_XSAVE))
-		guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_XSAVES);
+		guest_cpu_cap_constrain(vcpu, X86_FEATURE_XSAVES);
+	else
+		guest_cpu_cap_clear(vcpu, X86_FEATURE_XSAVES);
 
-	guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_VMX);
-	guest_cpu_cap_check_and_set(vcpu, X86_FEATURE_LAM);
+	guest_cpu_cap_constrain(vcpu, X86_FEATURE_VMX);
+	guest_cpu_cap_constrain(vcpu, X86_FEATURE_LAM);
 
 	vmx_setup_uret_msrs(vmx);
 
-- 
2.47.0.338.g60cca15819-goog


  parent reply	other threads:[~2024-11-28  1:35 UTC|newest]

Thread overview: 65+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-28  1:33 [PATCH v3 00/57] KVM: x86: CPUID overhaul, fixes, and caching Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 01/57] KVM: x86: Use feature_bit() to clear CONSTANT_TSC when emulating CPUID Sean Christopherson
2024-12-13 10:53   ` Vitaly Kuznetsov
2024-11-28  1:33 ` [PATCH v3 02/57] KVM: x86: Limit use of F() and SF() to kvm_cpu_cap_{mask,init_kvm_defined}() Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 03/57] KVM: x86: Do all post-set CPUID processing during vCPU creation Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 04/57] KVM: x86: Explicitly do runtime CPUID updates "after" initial setup Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 05/57] KVM: x86: Account for KVM-reserved CR4 bits when passing through CR4 on VMX Sean Christopherson
2024-12-13  1:30   ` Chao Gao
2024-11-28  1:33 ` [PATCH v3 06/57] KVM: selftests: Update x86's set_sregs_test to match KVM's CPUID enforcement Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 07/57] KVM: selftests: Assert that vcpu->cpuid is non-NULL when getting CPUID entries Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 08/57] KVM: selftests: Refresh vCPU CPUID cache in __vcpu_get_cpuid_entry() Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 09/57] KVM: selftests: Verify KVM stuffs runtime CPUID OS bits on CR4 writes Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 10/57] KVM: x86: Move __kvm_is_valid_cr4() definition to x86.h Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 11/57] KVM: x86/pmu: Drop now-redundant refresh() during init() Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 12/57] KVM: x86: Drop now-redundant MAXPHYADDR and GPA rsvd bits from vCPU creation Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 13/57] KVM: x86: Disallow KVM_CAP_X86_DISABLE_EXITS after " Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 14/57] KVM: x86: Reject disabling of MWAIT/HLT interception when not allowed Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 15/57] KVM: x86: Drop the now unused KVM_X86_DISABLE_VALID_EXITS Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 16/57] KVM: selftests: Fix a bad TEST_REQUIRE() in x86's KVM PV test Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 17/57] KVM: selftests: Update x86's KVM PV test to match KVM's disabling exits behavior Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 18/57] KVM: x86: Zero out PV features cache when the CPUID leaf is not present Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 19/57] KVM: x86: Don't update PV features caches when enabling enforcement capability Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 20/57] KVM: x86: Do reverse CPUID sanity checks in __feature_leaf() Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 21/57] KVM: x86: Account for max supported CPUID leaf when getting raw host CPUID Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 22/57] KVM: x86: Unpack F() CPUID feature flag macros to one flag per line of code Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 23/57] KVM: x86: Rename kvm_cpu_cap_mask() to kvm_cpu_cap_init() Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 24/57] KVM: x86: Add a macro to init CPUID features that are 64-bit only Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 25/57] KVM: x86: Add a macro to precisely handle aliased 0x1.EDX CPUID features Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 26/57] KVM: x86: Handle kernel- and KVM-defined CPUID words in a single helper Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 27/57] KVM: x86: #undef SPEC_CTRL_SSBD in cpuid.c to avoid macro collisions Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 28/57] KVM: x86: Harden CPU capabilities processing against out-of-scope features Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 29/57] KVM: x86: Add a macro to init CPUID features that ignore host kernel support Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 30/57] KVM: x86: Add a macro to init CPUID features that KVM emulates in software Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 31/57] KVM: x86: Swap incoming guest CPUID into vCPU before massaging in KVM_SET_CPUID2 Sean Christopherson
2024-11-28  1:33 ` [PATCH v3 32/57] KVM: x86: Clear PV_UNHALT for !HLT-exiting only when userspace sets CPUID Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 33/57] KVM: x86: Remove unnecessary caching of KVM's PV CPUID base Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 34/57] KVM: x86: Always operate on kvm_vcpu data in cpuid_entry2_find() Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 35/57] KVM: x86: Move kvm_find_cpuid_entry{,_index}() up near cpuid_entry2_find() Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 36/57] KVM: x86: Remove all direct usage of cpuid_entry2_find() Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 37/57] KVM: x86: Advertise TSC_DEADLINE_TIMER in KVM_GET_SUPPORTED_CPUID Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 38/57] KVM: x86: Advertise HYPERVISOR " Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 39/57] KVM: x86: Rename "governed features" helpers to use "guest_cpu_cap" Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 40/57] KVM: x86: Replace guts of "governed" features with comprehensive cpu_caps Sean Christopherson
2024-11-28  1:34 ` Sean Christopherson [this message]
2024-11-28  1:34 ` [PATCH v3 42/57] KVM: x86: Extract code for generating per-entry emulated CPUID information Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 43/57] KVM: x86: Treat MONTIOR/MWAIT as a "partially emulated" feature Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 44/57] KVM: x86: Initialize guest cpu_caps based on KVM support Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 45/57] KVM: x86: Avoid double CPUID lookup when updating MWAIT at runtime Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 46/57] KVM: x86: Drop unnecessary check that cpuid_entry2_find() returns right leaf Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 47/57] KVM: x86: Update OS{XSAVE,PKE} bits in guest CPUID irrespective of host support Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 48/57] KVM: x86: Update guest cpu_caps at runtime for dynamic CPUID-based features Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 49/57] KVM: x86: Shuffle code to prepare for dropping guest_cpuid_has() Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 50/57] KVM: x86: Replace (almost) all guest CPUID feature queries with cpu_caps Sean Christopherson
2024-12-13  2:14   ` Chao Gao
2024-12-17  0:05     ` Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 51/57] KVM: x86: Drop superfluous host XSAVE check when adjusting guest XSAVES caps Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 52/57] KVM: x86: Add a macro for features that are synthesized into boot_cpu_data Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 53/57] KVM: x86: Pull CPUID capabilities from boot_cpu_data only as needed Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 54/57] KVM: x86: Rename "SF" macro to "SCATTERED_F" Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 55/57] KVM: x86: Explicitly track feature flags that require vendor enabling Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 56/57] KVM: x86: Explicitly track feature flags that are enabled at runtime Sean Christopherson
2024-11-28  1:34 ` [PATCH v3 57/57] KVM: x86: Use only local variables (no bitmask) to init kvm_cpu_caps Sean Christopherson
2024-12-18  1:15   ` Maxim Levitsky
2024-12-18  1:13 ` [PATCH v3 00/57] KVM: x86: CPUID overhaul, fixes, and caching Maxim Levitsky
2024-12-19  2:40 ` Sean Christopherson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241128013424.4096668-42-seanjc@google.com \
    --to=seanjc@google.com \
    --cc=binbin.wu@linux.intel.com \
    --cc=houwenlong.hwl@antgroup.com \
    --cc=jarkko@kernel.org \
    --cc=kechenl@nvidia.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sgx@vger.kernel.org \
    --cc=mlevitsk@redhat.com \
    --cc=oliver.upton@linux.dev \
    --cc=pbonzini@redhat.com \
    --cc=robert.hoo.linux@gmail.com \
    --cc=vkuznets@redhat.com \
    --cc=weijiang.yang@intel.com \
    --cc=xiaoyao.li@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox