Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: Binbin Wu <binbin.wu@linux.intel.com>
To: kvm@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: seanjc@google.com, pbonzini@redhat.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 v2 1/4] KVM: x86: TDX: Track supported configurable CPUID bits
Date: Thu,  4 Jun 2026 10:33:11 +0800	[thread overview]
Message-ID: <20260604023314.3907511-2-binbin.wu@linux.intel.com> (raw)
In-Reply-To: <20260604023314.3907511-1-binbin.wu@linux.intel.com>

Build an allowlist for TDX directly configurable CPUID bits that are
supported by KVM.

The TDX module reports a set of CPUID bits that the VMM can directly
configure for a TD, but KVM cannot blindly trust and expose all
module-supported bits to userspace. Certain features imply additional
architectural state (such as one or more host state clobbering MSRs)
that KVM must explicitly manage across host/guest transitions to
prevent host state corruption.

To safely manage this, track the specific subset of configurable CPUID
bits that KVM supports by initializing multi-bit fields statically and
populating individual feature bits dynamically during TDX hardware
setup.

For better readability and maintainability, define a macro
tdx_cpu_cfg_cap_init() to initialize the feature bits.

Subsequent patches will use this allowlist to consistently filter
KVM_TDX_CAPABILITIES and reject unsupported userspace input through
KVM_TDX_INIT_VM. This ensures that any newly introduced TDX configurable
CPUID bits remain hidden from userspace until KVM explicitly implements
the required virtualization support.

Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
---
 arch/x86/kvm/vmx/tdx.c | 174 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 174 insertions(+)

diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index ffe9d0db58c5..e0567088ebf5 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -52,6 +52,178 @@
 	__TDX_BUG_ON(__err, #__fn, __kvm, ", " #a1 " 0x%llx, " #a2 ", 0x%llx, " #a3 " 0x%llx", \
 		     a1, a2, a3)
 
+#define TDX_CPUID_IGNORE_INDEX	BIT(0)
+struct tdx_supported_cpuid_reg {
+	u32 function;
+	u32 index;
+	u8 flags;
+	u8 reg;
+	u32 mask;
+};
+
+/*
+ * Multi-bit fields are statically initialized, feature bits are initialized
+ * in tdx_initialize_cpu_cfg_caps().
+ */
+static struct tdx_supported_cpuid_reg tdx_kvm_supported_cpuid[] __ro_after_init = {
+	{ 0x1, 0, 0, CPUID_EAX, GENMASK_U32(27, 16) | GENMASK_U32(13, 0) },
+	{ 0x1, 0, 0, CPUID_EBX, GENMASK_U32(23, 16) },
+	{ 0x1, 0, 0, CPUID_ECX, 0 },
+	{ 0x1, 0, 0, CPUID_EDX, 0 },
+	{ 0x4, 0, TDX_CPUID_IGNORE_INDEX, CPUID_EAX, ~GENMASK_U32(13, 10) },
+	{ 0x4, 0, TDX_CPUID_IGNORE_INDEX, CPUID_EBX, GENMASK_U32(31, 12) },
+	{ 0x4, 0, TDX_CPUID_IGNORE_INDEX, CPUID_ECX, GENMASK_U32(31, 0) },
+	{ 0x4, 0, TDX_CPUID_IGNORE_INDEX, CPUID_EDX, GENMASK_U32(2, 0) },
+	{ 0x7, 0, 0, CPUID_EBX, 0 },
+	{ 0x7, 0, 0, CPUID_ECX, 0 },
+	{ 0x7, 0, 0, CPUID_EDX, 0 },
+	{ 0x7, 1, 0, CPUID_EAX, 0 },
+	{ 0x7, 1, 0, CPUID_EDX, 0 },
+	{ 0x7, 2, 0, CPUID_EDX, 0 },
+	{ 0x18, 0, TDX_CPUID_IGNORE_INDEX, CPUID_EDX, GENMASK_U32(25, 14) },
+	{ 0x1E, 1, 0, CPUID_EAX, 0 },
+	{ 0x1F, 0, TDX_CPUID_IGNORE_INDEX, CPUID_EAX, GENMASK_U32(4, 0) },
+	{ 0x1F, 0, TDX_CPUID_IGNORE_INDEX, CPUID_EBX, GENMASK_U32(15, 0) },
+	{ 0x1F, 0, TDX_CPUID_IGNORE_INDEX, CPUID_ECX, GENMASK_U32(15, 0) },
+	/* See comments in td_init_cpuid_entry2() for CPUID 0x80000008 EAX[23:16]. */
+	{ 0x80000008, 0, 0, CPUID_EAX, GENMASK_U32(23, 16) | GENMASK_U32(7, 0) },
+	{ 0x80000008, 0, 0, CPUID_EBX, 0 },
+};
+
+#define TDX_F(name)					\
+({							\
+	tdx_cpu_cfg_caps |= feature_bit(name);		\
+})
+
+#define tdx_cpu_cfg_cap_init(_func, _index, _reg, feature_initializers...)		\
+do {											\
+	u32 tdx_cpu_cfg_caps = 0;							\
+											\
+	for (int i = 0; i < ARRAY_SIZE(tdx_kvm_supported_cpuid); i++) {			\
+		struct tdx_supported_cpuid_reg *r = &tdx_kvm_supported_cpuid[i];	\
+											\
+		if (r->function == _func && r->index == _index && r->reg == _reg) {	\
+			feature_initializers						\
+			r->mask |= tdx_cpu_cfg_caps;					\
+			break;								\
+		}									\
+	}										\
+											\
+	WARN_ON_ONCE(!tdx_cpu_cfg_caps); 						\
+} while (0)
+
+/* Only for TDX directly configurable CPUID feature bits */
+static void __init tdx_initialize_cpu_cfg_caps(void)
+{
+	tdx_cpu_cfg_cap_init(0x1, 0, CPUID_ECX,
+		TDX_F(MWAIT),
+		TDX_F(TSC_DEADLINE_TIMER),
+		TDX_F(AVX),
+		TDX_F(F16C),
+	);
+
+	tdx_cpu_cfg_cap_init(0x1, 0, CPUID_EDX,
+		TDX_F(MCE),
+		TDX_F(MTRR),
+		TDX_F(MCA),
+		TDX_F(SELFSNOOP),
+	);
+
+	tdx_cpu_cfg_cap_init(0x7, 0, CPUID_EBX,
+		TDX_F(BMI1),
+		/* HLE */
+		TDX_F(BMI2),
+		TDX_F(ERMS),
+		/* RTM */
+		/* CQM */
+		/* RDT-A */
+		TDX_F(AVX512F),
+		TDX_F(AVX512DQ),
+		TDX_F(ADX),
+		TDX_F(AVX512IFMA),
+		TDX_F(AVX512PF),
+		TDX_F(AVX512ER),
+		TDX_F(AVX512CD),
+		TDX_F(AVX512BW),
+		TDX_F(AVX512VL),
+	);
+
+	tdx_cpu_cfg_cap_init(0x7, 0, CPUID_ECX,
+		/* PREFETCHWT1 */
+		TDX_F(UMIP),
+		/* WAITPKG */
+		TDX_F(AVX512_VBMI2),
+		TDX_F(GFNI),
+		TDX_F(VAES),
+		TDX_F(VPCLMULQDQ),
+		TDX_F(AVX512_VNNI),
+		TDX_F(AVX512_BITALG),
+		/* TME */
+		TDX_F(AVX512_VPOPCNTDQ),
+		TDX_F(LA57),
+		TDX_F(RDPID),
+		TDX_F(CLDEMOTE),
+	);
+
+	tdx_cpu_cfg_cap_init(0x7, 0, CPUID_EDX,
+		TDX_F(AVX512_4VNNIW),
+		TDX_F(AVX512_4FMAPS),
+		TDX_F(FSRM),
+		TDX_F(AVX512_VP2INTERSECT),
+		TDX_F(SERIALIZE),
+		TDX_F(TSXLDTRK),
+		/* PCONFIG */
+		/* IA32_CORE_CAPABILITIES */
+	);
+
+	tdx_cpu_cfg_cap_init(0x7, 1, CPUID_EAX,
+		TDX_F(SHA512),
+		TDX_F(SM3),
+		TDX_F(SM4),
+		/* RAO_INT */
+		TDX_F(AVX_VNNI),
+		TDX_F(AVX512_BF16),
+		TDX_F(CMPCCXADD),
+		/* PERFMON */
+		TDX_F(FZRM),
+		TDX_F(FSRS),
+		TDX_F(FSRC),
+		/* FRED */
+		TDX_F(LKGS),
+		TDX_F(WRMSRNS),
+		TDX_F(AMX_FP16),
+		TDX_F(AVX_IFMA),
+		TDX_F(LAM),
+		TDX_F(MOVRS),
+	);
+
+	tdx_cpu_cfg_cap_init(0x7, 1, CPUID_EDX,
+		TDX_F(AVX_VNNI_INT8),
+		TDX_F(AVX_NE_CONVERT),
+		TDX_F(AVX_VNNI_INT16),
+		TDX_F(PREFETCHITI),
+		/* UMSR */
+		/* UIRET loads UIF */
+		TDX_F(AVX10),
+	);
+
+	tdx_cpu_cfg_cap_init(0x7, 2, CPUID_EDX,
+		TDX_F(DDPD_U),
+		TDX_F(MCDT_NO),
+	);
+
+	tdx_cpu_cfg_cap_init(0x1E, 1, CPUID_EAX,
+		TDX_F(AMX_FP8),
+		/* AMX-TRANSPOSE */
+		TDX_F(AMX_TF32),
+		TDX_F(AMX_AVX512),
+		TDX_F(AMX_MOVRS),
+	);
+
+	tdx_cpu_cfg_cap_init(0x80000008, 0, CPUID_EBX,
+		TDX_F(WBNOINVD),
+	);
+}
 
 bool enable_tdx __ro_after_init;
 module_param_named(tdx, enable_tdx, bool, 0444);
@@ -3493,6 +3665,8 @@ int __init tdx_hardware_setup(void)
 		return r;
 	}
 
+	tdx_initialize_cpu_cfg_caps();
+
 	KVM_SANITY_CHECK_VM_STRUCT_SIZE(kvm_tdx);
 
 	vt_x86_ops.vm_size = max_t(unsigned int, vt_x86_ops.vm_size, sizeof(struct kvm_tdx));
-- 
2.46.0


  reply	other threads:[~2026-06-04  2:29 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-04  2:33 [RFC PATCH v2 0/4] KVM: x86: TDX: Validate directly configurable CPUID bits Binbin Wu
2026-06-04  2:33 ` Binbin Wu [this message]
2026-06-04  2:44   ` [RFC PATCH v2 1/4] KVM: x86: TDX: Track supported " sashiko-bot
2026-06-04  5:37     ` Binbin Wu
2026-06-04  2:33 ` [RFC PATCH v2 2/4] KVM: x86: TDX: Hide unsupported " Binbin Wu
2026-06-04  2:47   ` sashiko-bot
2026-06-04  2:54     ` Binbin Wu
2026-06-04  2:33 ` [RFC PATCH v2 3/4] KVM: x86: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM Binbin Wu
2026-06-04  2:49   ` sashiko-bot
2026-06-04  3:13     ` Binbin Wu
2026-06-04  2:33 ` [RFC PATCH v2 4/4] KVM: x86: TDX: Report CORE_CAPABILITIES as supported Binbin Wu
2026-06-04  2:51   ` sashiko-bot
2026-06-04  5:32     ` Binbin Wu
2026-06-04  5:40       ` Binbin Wu
2026-06-04  6:53   ` Xiaoyao Li
2026-06-04  7:20     ` 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=20260604023314.3907511-2-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=linux-kernel@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