public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
From: Binbin Wu <binbin.wu@linux.intel.com>
To: kvm@vger.kernel.org
Cc: pbonzini@redhat.com, seanjc@google.com,
	rick.p.edgecombe@intel.com, xiaoyao.li@intel.com,
	chao.gao@intel.com, kai.huang@intel.com,
	binbin.wu@linux.intel.com
Subject: [RFC PATCH 16/27] KVM: x86: Init allowed masks for basic CPUID range in paranoid mode
Date: Fri, 17 Apr 2026 15:35:59 +0800	[thread overview]
Message-ID: <20260417073610.3246316-17-binbin.wu@linux.intel.com> (raw)
In-Reply-To: <20260417073610.3246316-1-binbin.wu@linux.intel.com>

Populate the CPUID paranoid mode validation data for the basic CPUID
range (0x0 through 0x24).

For each CPUID output register, the validation follows one of three
rules:
  1. Ignored: the register is added to the ignored set and KVM skips
     validation of the userspace-provided value.
  2. Mask/value check: a new KVM-only CPUID leaf enum is defined with a
     corresponding reverse_cpuid[] entry, and an allowed mask or fixed
     value is initialized per-overlay.
  3. Zero check: for reserved registers or registers where no bits are
     supported, userspace input is checked against zero.

Add is_cpuid_subleaf_common_pattern() to map higher sub-leaf indices to
a representative sub-leaf for validation, avoiding duplicate mask
definitions for CPUID functions 4, 0xB, 0xD, 0x12, and 0x1F.

Add is_cpuid_reg_check_value() to flag registers where userspace input
must exactly match fixed values (CPUID 0x1D, 0x1E.0.EBX) rather than
being validated against a bitmask.

Notable leaf-specific handling:
 - CPUID 0x1.EDX: HT is emulated to allow userspace to set it, but
   masked when reporting supported CPUID to userspace.
 - CPUID 0x6.EAX: ARAT initialized as emulated, replacing the hardcoded
   value in __do_cpuid_func().
 - CPUID 0x6.ECX: APERFMPERF allowed for VMX/SVM (userspace may enable
   KVM_X86_DISABLE_EXITS_APERFMPERF and set it), fixed-0 for TDX.
 - CPUID 0x7.0.EDX: CORE_CAPABILITIES set for TDX to accommodate old
   TDX modules that report bit 30 as fixed-1; MSR_IA32_CORE_CAPS reads
   return 0 inside a TD as a workaround.
 - CPUID 0xD: XCR0-based masks for subleaf 0, XSS-based for subleaf 1;
   size/offset fields are ignored
 - CPUID 0x12: SGX sub-leaf masks initialized when SGX is supported,
   replacing hardcoded masks in __do_cpuid_func().
 - CPUID 0x14: PT masks initialized for VMX from intel pt_caps[].
   Override CPUID.0x14.0.{EBX, ECX} when reporting capabilities to
   userspace.
 - CPUID 0x1D: fixed values from Intel SDM, exact-match required.
 - CPUID 0x1E.0: EAX capped at 1 (max supported sub-leaf), EBX is a
   fixed value from Intel SDM.
 - CPUID 0x24: AVX10 version capped at 2, merged with vector-width bits.

Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
---
 arch/x86/include/asm/kvm_host.h |  45 +++++++-
 arch/x86/kvm/cpuid.c            | 186 +++++++++++++++++++++++++++++---
 arch/x86/kvm/reverse_cpuid.h    |  47 ++++++++
 arch/x86/kvm/vmx/tdx.c          |  10 +-
 arch/x86/kvm/vmx/vmx.c          |  14 +++
 5 files changed, 288 insertions(+), 14 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 75895ab569fb..90514791f0fd 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -794,7 +794,50 @@ enum kvm_only_cpuid_leafs {
 	CPUID_24_1_ECX,
 	/* End of the leafs tracked by per-vcpu caps. */
 	NR_KVM_CPU_CAPS,
-	NR_KVM_CPU_CAPS_PARANOID = NR_KVM_CPU_CAPS,
+	CPUID_1_EAX = NR_KVM_CPU_CAPS,
+	CPUID_2_EAX,
+	CPUID_4_0_EAX,
+	CPUID_4_0_EDX,
+	CPUID_5_EAX,
+	CPUID_5_EBX,
+	CPUID_5_ECX,
+	CPUID_6_ECX,
+	CPUID_A_EAX,
+	CPUID_A_EBX,
+	CPUID_A_ECX,
+	CPUID_A_EDX,
+	CPUID_B_0_EAX,
+	CPUID_B_0_EBX,
+	CPUID_B_0_ECX,
+	CPUID_D_0_EAX,
+	CPUID_D_0_EDX,
+	CPUID_D_1_ECX,
+	CPUID_D_2_ECX,
+	CPUID_12_0_EBX,
+	CPUID_12_0_EDX,
+	CPUID_12_1_EAX,
+	CPUID_12_1_ECX,
+	CPUID_12_1_EDX,
+	CPUID_12_2_EAX,
+	CPUID_12_2_EBX,
+	CPUID_12_2_ECX,
+	CPUID_12_2_EDX,
+	CPUID_14_0_EAX,
+	CPUID_14_0_EBX,
+	CPUID_14_0_ECX,
+	CPUID_14_1_EAX,
+	CPUID_14_1_EBX,
+	CPUID_1D_0_EAX,
+	CPUID_1D_1_EAX,
+	CPUID_1D_1_EBX,
+	CPUID_1D_1_ECX,
+	CPUID_1E_0_EAX,
+	CPUID_1E_0_EBX,
+	CPUID_1F_0_EAX,
+	CPUID_1F_0_EBX,
+	CPUID_1F_0_ECX,
+	CPUID_24_0_EAX,
+	NR_KVM_CPU_CAPS_PARANOID,
 
 	NKVMCAPINTS = NR_KVM_CPU_CAPS_PARANOID - NCAPINTS,
 };
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index e633707277f9..59f0b3166eaa 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -392,8 +392,8 @@ static int cpuid_func_emulated(struct kvm *kvm, struct kvm_cpuid_entry2 *entry,
  * - Use -1 as index_end to indicate open-ended index ranges starting from
  *   index_start.
  */
-static void __maybe_unused kvm_cpu_cap_ignore(u32 func, u32 index_start, u32 index_end,
-					      u32 reg_mask, u32 overlay_mask)
+static void kvm_cpu_cap_ignore(u32 func, u32 index_start, u32 index_end,
+			       u32 reg_mask, u32 overlay_mask)
 {
 	if (WARN_ON_ONCE(ignored_set.nr >= KVM_MAX_CPUID_ENTRIES))
 		return;
@@ -419,6 +419,35 @@ static bool __maybe_unused is_cpuid_paranoid_ignored(u32 func, u32 index, int re
 	return false;
 }
 
+static bool __maybe_unused is_cpuid_reg_check_value(u32 func, u32 index, int reg)
+{
+	switch (func) {
+	case 0x1D: return true;
+	case 0x1E: return index == 0 && reg == CPUID_EBX;
+	default: return false;
+	}
+}
+
+static bool __maybe_unused is_cpuid_subleaf_common_pattern(u32 func, u32 *index)
+{
+	switch (func) {
+	case 4:
+	case 0xB:
+	case 0x1F:
+		*index = 0;
+		return true;
+	case 0xD:
+	case 0x12:
+		if (*index >= 2) {
+			*index = 2;
+			return true;
+		}
+		return false;
+	default:
+		return false;
+	}
+}
+
 void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
 {
 	u8 cpuid_overlay = get_cpuid_overlay(vcpu->kvm);
@@ -876,6 +905,14 @@ void kvm_initialize_cpu_caps(void)
 	BUILD_BUG_ON(sizeof(kvm_cpu_caps)/NR_CPUID_OL - (NKVMCAPINTS * sizeof(**kvm_cpu_caps)) >
 		     sizeof(boot_cpu_data.x86_capability));
 
+	kvm_cpu_cap_ignore(0, 0, 0,
+			   BIT(CPUID_EAX) | BIT(CPUID_EBX) | BIT(CPUID_ECX) | BIT(CPUID_EDX),
+			   F_CPUID_DEFAULT | F_CPUID_TDX);
+
+	kvm_cpu_cap_init_mf(CPUID_1_EAX, GENMASK_U32(27, 16) | GENMASK_U32(13, 0),
+			    F_CPUID_DEFAULT | F_CPUID_TDX);
+	kvm_cpu_cap_ignore(1, 0, 0, BIT(CPUID_EBX), F_CPUID_DEFAULT | F_CPUID_TDX);
+
 	kvm_cpu_cap_init(CPUID_1_ECX,
 		F(XMM3, F_CPUID_DEFAULT | F_CPUID_TDX),
 		F(PCLMULQDQ, F_CPUID_DEFAULT | F_CPUID_TDX),
@@ -946,9 +983,40 @@ void kvm_initialize_cpu_caps(void)
 		F(XMM, F_CPUID_DEFAULT | F_CPUID_TDX),
 		F(XMM2, F_CPUID_DEFAULT | F_CPUID_TDX),
 		F(SELFSNOOP, F_CPUID_DEFAULT | F_CPUID_TDX),
-		/* HTT, TM, Reserved, PBE */
+		/* Allow userspace to set HT regardless of underlying hardware. */
+		EMULATED_F(HT, F_CPUID_DEFAULT | F_CPUID_TDX),
+		/* TM, Reserved, PBE */
+	);
+
+	/* EAX[7:0] are reserved with value 1. */
+	kvm_cpu_cap_init_mf(CPUID_2_EAX, GENMASK_U32(31, 8) | 0x01, F_CPUID_VMX | F_CPUID_TDX);
+	kvm_cpu_cap_ignore(2, 0, 0, BIT(CPUID_EBX) | BIT(CPUID_ECX) | BIT(CPUID_EDX),
+			   F_CPUID_VMX | F_CPUID_TDX);
+
+	kvm_cpu_cap_init_mf(CPUID_4_0_EAX, ~GENMASK_U32(13, 10), F_CPUID_VMX | F_CPUID_TDX);
+	kvm_cpu_cap_init_mf(CPUID_4_0_EDX, GENMASK_U32(2, 0), F_CPUID_VMX | F_CPUID_TDX);
+	kvm_cpu_cap_ignore(4, 0, -1, BIT(CPUID_EBX) | BIT(CPUID_ECX), F_CPUID_VMX | F_CPUID_TDX);
+
+	kvm_cpu_cap_init_mf(CPUID_5_EAX, GENMASK_U32(15, 0), F_CPUID_DEFAULT | F_CPUID_TDX);
+	kvm_cpu_cap_init_mf(CPUID_5_EBX, GENMASK_U32(15, 0), F_CPUID_DEFAULT | F_CPUID_TDX);
+	kvm_cpu_cap_init_mf(CPUID_5_ECX, GENMASK_U32(1, 0), F_CPUID_DEFAULT | F_CPUID_TDX);
+	kvm_cpu_cap_ignore(5, 0, 0, BIT(CPUID_EDX), F_CPUID_VMX | F_CPUID_TDX);
+
+	kvm_cpu_cap_init(CPUID_6_EAX,
+		EMULATED_F(ARAT, F_CPUID_DEFAULT | F_CPUID_TDX),
+	);
+
+	/*
+	 * KVM allows userspace to set APERFMPERF after enabling
+	 * KVM_X86_DISABLE_EXITS_APERFMPERF.
+	 * Fixed-0 for TDX.
+	 */
+	kvm_cpu_cap_init(CPUID_6_ECX,
+		F(APERFMPERF, F_CPUID_DEFAULT),
 	);
 
+	kvm_cpu_cap_ignore(7, 0, 0, BIT(CPUID_EAX), F_CPUID_DEFAULT | F_CPUID_TDX);
+
 	kvm_cpu_cap_init(CPUID_7_0_EBX,
 		F(FSGSBASE, F_CPUID_DEFAULT | F_CPUID_TDX),
 		EMULATED_F(TSC_ADJUST, F_CPUID_DEFAULT | F_CPUID_TDX),
@@ -1056,7 +1124,7 @@ void kvm_initialize_cpu_caps(void)
 		F(INTEL_STIBP, F_CPUID_DEFAULT | F_CPUID_TDX),
 		F(FLUSH_L1D, F_CPUID_DEFAULT | F_CPUID_TDX),
 		EMULATED_F(ARCH_CAPABILITIES, F_CPUID_DEFAULT | F_CPUID_TDX),
-		/* CORE_CAPABILITIES */
+		F(CORE_CAPABILITIES, F_CPUID_TDX),
 		F(SPEC_CTRL_SSBD, F_CPUID_DEFAULT | F_CPUID_TDX),
 	);
 
@@ -1120,6 +1188,30 @@ void kvm_initialize_cpu_caps(void)
 		F(MCDT_NO, F_CPUID_DEFAULT | F_CPUID_TDX),
 	);
 
+	if (enable_pmu) {
+		/* KVM doesn't support PERFMON for TDX yet. */
+		kvm_cpu_cap_init_mf(CPUID_A_EAX, GENMASK_U32(31, 0), F_CPUID_VMX);
+		kvm_cpu_cap_init_mf(CPUID_A_EBX, GENMASK_U32(12, 10) | GENMASK_U32(7, 0),
+				    F_CPUID_VMX);
+		kvm_cpu_cap_init_mf(CPUID_A_ECX, GENMASK_U32(31, 0), F_CPUID_VMX);
+		kvm_cpu_cap_init_mf(CPUID_A_EDX, GENMASK_U32(19, 15) | GENMASK_U32(12, 0),
+				    F_CPUID_VMX);
+	}
+
+	/* CPUID 0xB is derived from CPUID.0x1F for TDX, but allow userspace to set it. */
+	kvm_cpu_cap_init_mf(CPUID_B_0_EAX, GENMASK_U32(4, 0), F_CPUID_DEFAULT | F_CPUID_TDX);
+	kvm_cpu_cap_init_mf(CPUID_B_0_EBX, GENMASK_U32(15, 0), F_CPUID_DEFAULT | F_CPUID_TDX);
+	kvm_cpu_cap_init_mf(CPUID_B_0_ECX, GENMASK_U32(15, 0), F_CPUID_DEFAULT | F_CPUID_TDX);
+	kvm_cpu_cap_ignore(0xB, 0, -1, BIT(CPUID_EDX), F_CPUID_DEFAULT | F_CPUID_TDX);
+
+
+	kvm_cpu_cap_init_mf(CPUID_D_0_EAX, (u32)kvm_caps.supported_xcr0,
+			    F_CPUID_DEFAULT | F_CPUID_TDX);
+	kvm_cpu_cap_ignore(0xD, 0, 0, BIT(CPUID_EBX) | BIT(CPUID_ECX),
+			   F_CPUID_DEFAULT | F_CPUID_TDX);
+	kvm_cpu_cap_init_mf(CPUID_D_0_EDX, (u32)(kvm_caps.supported_xcr0 >> 32),
+			    F_CPUID_DEFAULT | F_CPUID_TDX);
+
 	kvm_cpu_cap_init(CPUID_D_1_EAX,
 		F(XSAVEOPT, F_CPUID_DEFAULT | F_CPUID_TDX),
 		F(XSAVEC, F_CPUID_DEFAULT | F_CPUID_TDX),
@@ -1128,6 +1220,19 @@ void kvm_initialize_cpu_caps(void)
 		X86_64_F(XFD, F_CPUID_DEFAULT | F_CPUID_TDX),
 	);
 
+	kvm_cpu_cap_ignore(0xD, 1, 1, BIT(CPUID_EBX), F_CPUID_DEFAULT | F_CPUID_TDX);
+
+	/* No bits are defined in CPUID.D.1.EDX (i.e., the upper 32 bits of XSS) yet. */
+	kvm_cpu_cap_init_mf(CPUID_D_1_ECX, (u32)kvm_caps.supported_xss,
+			    F_CPUID_DEFAULT | F_CPUID_TDX);
+
+	if ((kvm_caps.supported_xss | kvm_caps.supported_xcr0) & GENMASK_U64(62, 2)) {
+		kvm_cpu_cap_ignore(0xD, 2, 62, BIT(CPUID_EAX) | BIT(CPUID_EBX),
+				   F_CPUID_DEFAULT | F_CPUID_TDX);
+		kvm_cpu_cap_init_mf(CPUID_D_2_ECX, GENMASK_U32(2, 0),
+				    F_CPUID_DEFAULT | F_CPUID_TDX);
+	}
+
 	/* SGX related features are fixed-0 for TDX */
 	kvm_cpu_cap_init(CPUID_12_EAX,
 		SCATTERED_F(SGX1, F_CPUID_DEFAULT),
@@ -1135,6 +1240,40 @@ void kvm_initialize_cpu_caps(void)
 		SCATTERED_F(SGX_EDECCSSA, F_CPUID_DEFAULT),
 	);
 
+	if (kvm_cpu_cap_has(NULL, X86_FEATURE_SGX)) {
+		kvm_cpu_cap_check_and_init_mf(CPUID_12_0_EBX, SGX_MISC_EXINFO, F_CPUID_DEFAULT);
+		kvm_cpu_cap_init_mf(CPUID_12_0_EDX, GENMASK_U32(15, 0), F_CPUID_DEFAULT);
+
+		kvm_cpu_cap_check_and_init_mf(CPUID_12_1_EAX,
+					      SGX_ATTR_PRIV_MASK | SGX_ATTR_UNPRIV_MASK,
+					      F_CPUID_DEFAULT);
+		kvm_cpu_cap_init_mf(CPUID_12_1_ECX, (u32)kvm_caps.supported_xcr0, F_CPUID_DEFAULT);
+		kvm_cpu_cap_init_mf(CPUID_12_1_EDX, (u32)(kvm_caps.supported_xcr0 >> 32),
+				    F_CPUID_DEFAULT);
+
+		/*
+		 * SUB_LEAF_TYPE (EAX[3:0]) is valid only when it is 1. The
+		 * masks are initialized according to type 1.
+		 */
+		kvm_cpu_cap_init_mf(CPUID_12_2_EAX, GENMASK_U32(31, 12) | 0x1, F_CPUID_DEFAULT);
+		kvm_cpu_cap_init_mf(CPUID_12_2_EBX, GENMASK_U32(19, 0), F_CPUID_DEFAULT);
+		kvm_cpu_cap_init_mf(CPUID_12_2_ECX, ~GENMASK_U32(11, 4), F_CPUID_DEFAULT);
+		kvm_cpu_cap_init_mf(CPUID_12_2_EDX, GENMASK_U32(19, 0), F_CPUID_DEFAULT);
+	}
+
+	/* Hardcoded with fixed values in Intel SDM. */
+	if (kvm_cpu_cap_has(NULL, X86_FEATURE_AMX_TILE)) {
+		kvm_cpu_cap_init_mf(CPUID_1D_0_EAX, 0x00000001, F_CPUID_DEFAULT | F_CPUID_TDX);
+
+		kvm_cpu_cap_init_mf(CPUID_1D_1_EAX, 0x04002000, F_CPUID_DEFAULT | F_CPUID_TDX);
+		kvm_cpu_cap_init_mf(CPUID_1D_1_EBX, 0x00080040, F_CPUID_DEFAULT | F_CPUID_TDX);
+		kvm_cpu_cap_init_mf(CPUID_1D_1_ECX, 0x00000010, F_CPUID_DEFAULT | F_CPUID_TDX);
+
+		/* KVM limits the subleaf up to 1. */
+		kvm_cpu_cap_init_mf(CPUID_1E_0_EAX, 0x00000001, F_CPUID_DEFAULT | F_CPUID_TDX);
+		kvm_cpu_cap_init_mf(CPUID_1E_0_EBX, 0x00004010, F_CPUID_DEFAULT | F_CPUID_TDX);
+	}
+
 	kvm_cpu_cap_init(CPUID_1E_1_EAX,
 		F(AMX_INT8_ALIAS, F_CPUID_DEFAULT | F_CPUID_TDX),
 		F(AMX_BF16_ALIAS, F_CPUID_DEFAULT | F_CPUID_TDX),
@@ -1146,11 +1285,25 @@ void kvm_initialize_cpu_caps(void)
 		F(AMX_MOVRS, F_CPUID_DEFAULT | F_CPUID_TDX),
 	);
 
-	kvm_cpu_cap_init(CPUID_24_0_EBX,
-		F(AVX10_128, F_CPUID_DEFAULT | F_CPUID_TDX),
-		F(AVX10_256, F_CPUID_DEFAULT | F_CPUID_TDX),
-		F(AVX10_512, F_CPUID_DEFAULT | F_CPUID_TDX),
-	);
+	kvm_cpu_cap_init_mf(CPUID_1F_0_EAX, GENMASK_U32(4, 0), F_CPUID_VMX | F_CPUID_TDX);
+	kvm_cpu_cap_init_mf(CPUID_1F_0_EBX, GENMASK_U32(15, 0), F_CPUID_VMX | F_CPUID_TDX);
+	kvm_cpu_cap_init_mf(CPUID_1F_0_ECX, GENMASK_U32(15, 0), F_CPUID_VMX | F_CPUID_TDX);
+	kvm_cpu_cap_ignore(0x1F, 0, -1, BIT(CPUID_EDX), F_CPUID_VMX | F_CPUID_TDX);
+
+	if (kvm_cpu_cap_has(NULL, X86_FEATURE_AVX10)) {
+		/* KVM supports up to subleaf 1 */
+		kvm_cpu_cap_init_mf(CPUID_24_0_EAX, 0x00000001, F_CPUID_DEFAULT | F_CPUID_TDX);
+		/*
+		 * The allowed value for AVX10 version is 1 or 2. The version is
+		 * guaranteed to be >=1 if AVX10 is supported, and KVM supports
+		 * up to version 2. For simplicity, just allow lower 2 bits to
+		 * be set by userspace.
+		 * EBX[18:16] is reserved at 111b for all vector widths, i.e.,
+		 * AVX10_128, AVX10_256, and AVX10_512.
+		 */
+		kvm_cpu_cap_init_mf(CPUID_24_0_EBX, GENMASK_U32(18, 16) | GENMASK_U32(1, 0),
+				    F_CPUID_DEFAULT | F_CPUID_TDX);
+	}
 
 	kvm_cpu_cap_init(CPUID_24_1_ECX,
 		/* AVX10_VNNI_INT is reserved in TDX spec */
@@ -1501,6 +1654,11 @@ static inline int __do_cpuid_func(struct kvm *kvm, struct kvm_cpuid_array *array
 		break;
 	case 1:
 		cpuid_entry_override(kvm, entry, CPUID_1_EDX);
+		/*
+		 * Clear HT when reporting to userspace since it's not emulated
+		 * by KVM.
+		 */
+		entry->edx &= ~feature_bit(HT);
 		cpuid_entry_override(kvm, entry, CPUID_1_ECX);
 		break;
 	case 2:
@@ -1535,7 +1693,7 @@ static inline int __do_cpuid_func(struct kvm *kvm, struct kvm_cpuid_array *array
 		}
 		break;
 	case 6: /* Thermal management */
-		entry->eax = 0x4; /* allow ARAT */
+		cpuid_entry_override(kvm, entry, CPUID_6_EAX);
 		entry->ebx = 0;
 		entry->ecx = 0;
 		entry->edx = 0;
@@ -1674,7 +1832,7 @@ static inline int __do_cpuid_func(struct kvm *kvm, struct kvm_cpuid_array *array
 		 * feature flags), while enclave size is unrestricted.
 		 */
 		cpuid_entry_override(kvm, entry, CPUID_12_EAX);
-		entry->ebx &= SGX_MISC_EXINFO;
+		cpuid_entry_override(kvm, entry, CPUID_12_0_EBX);
 
 		entry = do_host_cpuid(array, function, 1);
 		if (!entry)
@@ -1687,7 +1845,7 @@ static inline int __do_cpuid_func(struct kvm *kvm, struct kvm_cpuid_array *array
 		 * userspace.  ATTRIBUTES.XFRM is not adjusted as userspace is
 		 * expected to derive it from supported XCR0.
 		 */
-		entry->eax &= SGX_ATTR_PRIV_MASK | SGX_ATTR_UNPRIV_MASK;
+		cpuid_entry_override(kvm, entry, CPUID_12_1_EAX);
 		entry->ebx &= 0;
 		break;
 	/* Intel PT */
@@ -1697,6 +1855,9 @@ static inline int __do_cpuid_func(struct kvm *kvm, struct kvm_cpuid_array *array
 			break;
 		}
 
+		cpuid_entry_override(kvm, entry, CPUID_14_0_EBX);
+		cpuid_entry_override(kvm, entry, CPUID_14_0_ECX);
+
 		for (i = 1, max_idx = entry->eax; i <= max_idx; ++i) {
 			if (!do_host_cpuid(array, function, i))
 				goto out;
@@ -1750,6 +1911,7 @@ static inline int __do_cpuid_func(struct kvm *kvm, struct kvm_cpuid_array *array
 		 */
 		avx10_version = min_t(u8, entry->ebx & 0xff, 2);
 		cpuid_entry_override(kvm, entry, CPUID_24_0_EBX);
+		entry->ebx &= ~GENMASK_U32(7, 0);
 		entry->ebx |= avx10_version;
 
 		entry->ecx = 0;
diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h
index 657f5f743ed9..5c7c0fbb0fec 100644
--- a/arch/x86/kvm/reverse_cpuid.h
+++ b/arch/x86/kvm/reverse_cpuid.h
@@ -76,6 +76,9 @@
 #define KVM_X86_FEATURE_TSA_SQ_NO	KVM_X86_FEATURE(CPUID_8000_0021_ECX, 1)
 #define KVM_X86_FEATURE_TSA_L1_NO	KVM_X86_FEATURE(CPUID_8000_0021_ECX, 2)
 
+/* CPUID level 0x6 (ECX) */
+#define KVM_X86_FEATURE_APERFMPERF	KVM_X86_FEATURE(CPUID_6_ECX, 0)
+
 struct cpuid_reg {
 	u32 function;
 	u32 index;
@@ -109,6 +112,49 @@ static const struct cpuid_reg reverse_cpuid[] = {
 	[CPUID_7_1_ECX]       = {         7, 1, CPUID_ECX},
 	[CPUID_1E_1_EAX]      = {      0x1e, 1, CPUID_EAX},
 	[CPUID_24_1_ECX]      = {      0x24, 1, CPUID_ECX},
+	[CPUID_1_EAX]         = {         1, 0, CPUID_EAX},
+	[CPUID_2_EAX]         = {         2, 0, CPUID_EAX},
+	[CPUID_4_0_EAX]       = {         4, 0, CPUID_EAX},
+	[CPUID_4_0_EDX]       = {         4, 0, CPUID_EDX},
+	[CPUID_5_EAX]         = {         5, 0, CPUID_EAX},
+	[CPUID_5_EBX]         = {         5, 0, CPUID_EBX},
+	[CPUID_5_ECX]         = {         5, 0, CPUID_ECX},
+	[CPUID_6_ECX]         = {         6, 0, CPUID_ECX},
+	[CPUID_A_EAX]         = {       0xa, 0, CPUID_EAX},
+	[CPUID_A_EBX]         = {       0xa, 0, CPUID_EBX},
+	[CPUID_A_ECX]         = {       0xa, 0, CPUID_ECX},
+	[CPUID_A_EDX]         = {       0xa, 0, CPUID_EDX},
+	[CPUID_B_0_EAX]       = {       0xb, 0, CPUID_EAX},
+	[CPUID_B_0_EBX]       = {       0xb, 0, CPUID_EBX},
+	[CPUID_B_0_ECX]       = {       0xb, 0, CPUID_ECX},
+	[CPUID_D_0_EAX]       = {       0xd, 0, CPUID_EAX},
+	[CPUID_D_0_EDX]       = {       0xd, 0, CPUID_EDX},
+	[CPUID_D_1_ECX]       = {       0xd, 1, CPUID_ECX},
+	[CPUID_D_2_ECX]       = {       0xd, 2, CPUID_ECX},
+	[CPUID_12_0_EBX]      = {      0x12, 0, CPUID_EBX},
+	[CPUID_12_0_EDX]      = {      0x12, 0, CPUID_EDX},
+	[CPUID_12_1_EAX]      = {      0x12, 1, CPUID_EAX},
+	[CPUID_12_1_ECX]      = {      0x12, 1, CPUID_ECX},
+	[CPUID_12_1_EDX]      = {      0x12, 1, CPUID_EDX},
+	[CPUID_12_2_EAX]      = {      0x12, 2, CPUID_EAX},
+	[CPUID_12_2_EBX]      = {      0x12, 2, CPUID_EBX},
+	[CPUID_12_2_ECX]      = {      0x12, 2, CPUID_ECX},
+	[CPUID_12_2_EDX]      = {      0x12, 2, CPUID_EDX},
+	[CPUID_14_0_EAX]      = {      0x14, 0, CPUID_EAX},
+	[CPUID_14_0_EBX]      = {      0x14, 0, CPUID_EBX},
+	[CPUID_14_0_ECX]      = {      0x14, 0, CPUID_ECX},
+	[CPUID_14_1_EAX]      = {      0x14, 1, CPUID_EAX},
+	[CPUID_14_1_EBX]      = {      0x14, 1, CPUID_EBX},
+	[CPUID_1D_0_EAX]      = {      0x1d, 0, CPUID_EAX},
+	[CPUID_1D_1_EAX]      = {      0x1d, 1, CPUID_EAX},
+	[CPUID_1D_1_EBX]      = {      0x1d, 1, CPUID_EBX},
+	[CPUID_1D_1_ECX]      = {      0x1d, 1, CPUID_ECX},
+	[CPUID_1E_0_EAX]      = {      0x1e, 0, CPUID_EAX},
+	[CPUID_1E_0_EBX]      = {      0x1e, 0, CPUID_EBX},
+	[CPUID_1F_0_EAX]      = {      0x1f, 0, CPUID_EAX},
+	[CPUID_1F_0_EBX]      = {      0x1f, 0, CPUID_EBX},
+	[CPUID_1F_0_ECX]      = {      0x1f, 0, CPUID_ECX},
+	[CPUID_24_0_EAX]      = {      0x24, 0, CPUID_EAX},
 };
 
 /*
@@ -151,6 +197,7 @@ static __always_inline u32 __feature_translate(int x86_feature)
 	KVM_X86_TRANSLATE_FEATURE(TSA_SQ_NO);
 	KVM_X86_TRANSLATE_FEATURE(TSA_L1_NO);
 	KVM_X86_TRANSLATE_FEATURE(MSR_IMM);
+	KVM_X86_TRANSLATE_FEATURE(APERFMPERF);
 	default:
 		return x86_feature;
 	}
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index 1e47c194af53..a1df89d66a84 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -2141,7 +2141,7 @@ bool tdx_has_emulated_msr(u32 index)
 static bool tdx_is_read_only_msr(u32 index)
 {
 	return  index == MSR_IA32_APICBASE || index == MSR_EFER ||
-		index == MSR_IA32_FEAT_CTL;
+		index == MSR_IA32_FEAT_CTL || index == MSR_IA32_CORE_CAPS;
 }
 
 int tdx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
@@ -2161,6 +2161,14 @@ int tdx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
 			return 1;
 		msr->data = vcpu->arch.mcg_ext_ctl;
 		return 0;
+	case MSR_IA32_CORE_CAPS:
+		/*
+		 * KVM doesn't support MSR_IA32_CORE_CAPS, however, in some old
+		 * TDX modules, CPUID.0x7.0.EDX[30] is fixed-1. As a workaround,
+		 * just return 0 for this MSR.
+		 */
+		msr->data = 0;
+		return 0;
 	default:
 		if (!tdx_has_emulated_msr(msr->index))
 			return 1;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index f772558758f7..17c9048c87f3 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8099,6 +8099,20 @@ static __init void vmx_set_cpu_caps(void)
 	if (vmx_pt_mode_is_host_guest())
 		kvm_cpu_cap_check_and_set(X86_FEATURE_INTEL_PT, F_CPUID_VMX);
 
+	if (kvm_cpu_cap_has(NULL, X86_FEATURE_INTEL_PT)) {
+		kvm_cpu_cap_init_mf(CPUID_14_0_EAX, GENMASK_U32(31, 0), F_CPUID_VMX);
+		/* Lower 9 bits are defined, however, bit 6 is not supported in intel pt_caps[] */
+		kvm_cpu_cap_check_and_init_mf(CPUID_14_0_EBX,
+					      GENMASK_U32(8, 7) | GENMASK_U32(5, 0),
+					      F_CPUID_VMX);
+		kvm_cpu_cap_check_and_init_mf(CPUID_14_0_ECX,
+					      BIT(31) | GENMASK_U32(3, 0),
+					      F_CPUID_VMX);
+
+		kvm_cpu_cap_init_mf(CPUID_14_1_EAX, ~GENMASK_U32(15, 3), F_CPUID_VMX);
+		kvm_cpu_cap_init_mf(CPUID_14_1_EBX, GENMASK_U32(31, 0), F_CPUID_VMX);
+	}
+
 	/* DS and DTES64 are fixed-1 for TDX */
 	enable_mask = vmx_pebs_supported() ? F_CPUID_TDX | F_CPUID_VMX : F_CPUID_TDX;
 	kvm_cpu_cap_check_and_set(X86_FEATURE_DS, enable_mask);
-- 
2.46.0


  parent reply	other threads:[~2026-04-17  7:32 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-17  7:35 [RFC PATCH 00/27] KVM: x86: Add a paranoid mode for CPUID verification Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 01/27] KVM: x86: Fix emulated CPUID features being applied to wrong sub-leaf Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 02/27] KVM: x86: Reorder the features for CPUID 7 Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 03/27] KVM: x86: Add definitions for CPUID overlays Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 04/27] KVM: x86: Extend F() and its variants " Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 05/27] KVM: x86: Extend kvm_cpu_cap_{set/clear}() to configure overlays Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 06/27] KVM: x86: Populate TDX CPUID overlay with supported feature bits Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 07/27] KVM: x86: Support KVM_GET_{SUPPORTED,EMULATED}_CPUID as VM scope ioctls Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 08/27] KVM: x86: Thread @kvm to KVM CPU capability helpers Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 09/27] KVM: x86: Use overlays of KVM CPU capabilities Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 10/27] KVM: x86: Use vendor-specific overlay flags instead of F_CPUID_DEFAULT Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 11/27] KVM: SVM: Drop unnecessary clears of unsupported common x86 features Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 12/27] KVM: x86: Split KVM CPU cap leafs into two parts Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 13/27] KVM: x86: Add a helper to initialize CPUID multi-bit fields Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 14/27] KVM: x86: Add a helper to init multiple feature bits based on raw CPUID Binbin Wu
2026-04-17  7:35 ` [RFC PATCH 15/27] KVM: x86: Add infrastructure to track CPUID entries ignored in paranoid mode Binbin Wu
2026-04-17  7:35 ` Binbin Wu [this message]
2026-04-17  7:36 ` [RFC PATCH 17/27] KVM: x86: Init allowed masks for extended CPUID range " Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 18/27] KVM: x86: Handle Centaur CPUID leafs " Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 19/27] KVM: x86: Track KVM PV CPUID features for " Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 20/27] KVM: x86: Add per-VM flag to track CPUID " Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 21/27] KVM: x86: Make kvm_vcpu_after_set_cpuid() return an error code Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 22/27] KVM: x86: Verify userspace CPUID inputs in paranoid mode Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 23/27] KVM: x86: Account for runtime CPUID features " Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 24/27] KVM: x86: Skip paranoid CPUID check for KVM PV leafs when base is relocated Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 25/27] KVM: x86: Add new KVM_CAP_X86_CPUID_PARANOID Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 26/27] KVM: x86: Add a helper to query the allowed CPUID mask Binbin Wu
2026-04-17  7:36 ` [RFC PATCH 27/27] KVM: TDX: Replace hardcoded CPUID filtering with the allowed mask Binbin Wu

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=20260417073610.3246316-17-binbin.wu@linux.intel.com \
    --to=binbin.wu@linux.intel.com \
    --cc=chao.gao@intel.com \
    --cc=kai.huang@intel.com \
    --cc=kvm@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=rick.p.edgecombe@intel.com \
    --cc=seanjc@google.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