* [RFC PATCH v2 0/4] KVM: x86: TDX: Validate directly configurable CPUID bits
@ 2026-06-04 2:33 Binbin Wu
2026-06-04 2:33 ` [RFC PATCH v2 1/4] KVM: x86: TDX: Track supported " Binbin Wu
` (3 more replies)
0 siblings, 4 replies; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 2:33 UTC (permalink / raw)
To: kvm, linux-kernel
Cc: seanjc, pbonzini, rick.p.edgecombe, xiaoyao.li, chao.gao,
kai.huang, binbin.wu
Hi,
A host state clobbering feature on new TDX modules/platforms can lead
to host state corruption if KVM does not explicitly save and restore
the related MSR(s) during host/guest transitions. If such a feature is
blindly exposed to and used by TDs, it will result in unexpected behavior
on the host.
The v1 RFC [1] attempted to solve this by introducing a comprehensive
CPUID paranoid verification framework across VMX, SVM, and TDX. However,
as Sean pointed out in [2] and the discussion in the PUCK meeting, this
approach was overly complex and bled too many TDX-specific details into
common KVM code, creating an unnecessary maintenance burden.
This v2 takes a significantly simpler, TDX-contained approach. It strictly
validates only the TDX directly configurable CPUID bits—those reported by
the TDX module in CPUID_CONFIG fields that the VMM can configure for a TD.
This is sufficient to address the host clobbering issue, as no new host
state clobbering features will be fixed-1. All filtering and validation
logic is entirely isolated within TDX code.
Feedback is highly appreciated, particularly on whether this contained
approach strikes an acceptable balance regarding complexity.
Specifically, this series builds a KVM-side allowlist of supported TDX
directly configurable CPUID bits to:
- Filter KVM_TDX_CAPABILITIES:
Replace the hardcoded denylist to only report configurable bits that
KVM explicitly supports.
- Validate KVM_TDX_INIT_VM:
Reject any configurable bit that the TDX module allows but KVM does
not yet support.
With this allowlist, newly added TDX configurable CPUID bits will not be
exposed to userspace until KVM explicitly opts-in after fulfilling the
necessary virtualization requirements.
Open:
- This series doesn't implement validation for KVM_SET_CPUID2.
TDX has two interfaces for userspace to set CPUID bits: KVM_TDX_INIT_VM
and KVM_SET_CPUID2. A malicious userspace VMM could lie to KVM through
KVM_SET_CPUID2 by setting a TDX directly configurable CPUID bit to a
different value than what it set via KVM_TDX_INIT_VM. KVM does not
currently use its own view of vCPU capabilities to manage host clobbering
features for TDs. The consistency check is not a must have action so
far. It could be added later if KVM really relies on its own view
to make decisions to manage host clobbering features for TDX.
Changes from v1:
- Dropped the overarching CPUID paranoid verification framework across
VMX/SVM/TDX and the opt-in interface. (Sean)
- Shifted focus entirely to isolating and validating TDX directly
configurable CPUID bits.
[1] https://lore.kernel.org/kvm/20260417073610.3246316-1-binbin.wu@linux.intel.com/
[2] https://lore.kernel.org/kvm/agsiQGikhZA0CGTY@google.com/
Binbin Wu (4):
KVM: x86: TDX: Track supported configurable CPUID bits
KVM: x86: TDX: Hide unsupported configurable CPUID bits
KVM: x86: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM
KVM: x86: TDX: Report CORE_CAPABILITIES as supported
arch/x86/kvm/vmx/tdx.c | 251 +++++++++++++++++++++++++++++++++++------
1 file changed, 214 insertions(+), 37 deletions(-)
base-commit: d4bfaa66fa171089b9b9fb2dc17af9245f2b9b34
--
2.46.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [RFC PATCH v2 1/4] KVM: x86: TDX: Track supported configurable CPUID bits
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
2026-06-04 2:44 ` sashiko-bot
2026-06-04 2:33 ` [RFC PATCH v2 2/4] KVM: x86: TDX: Hide unsupported " Binbin Wu
` (2 subsequent siblings)
3 siblings, 1 reply; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 2:33 UTC (permalink / raw)
To: kvm, linux-kernel
Cc: seanjc, pbonzini, rick.p.edgecombe, xiaoyao.li, chao.gao,
kai.huang, binbin.wu
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
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH v2 2/4] KVM: x86: TDX: Hide unsupported configurable CPUID bits
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 ` [RFC PATCH v2 1/4] KVM: x86: TDX: Track supported " Binbin Wu
@ 2026-06-04 2:33 ` Binbin Wu
2026-06-04 2:47 ` sashiko-bot
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:33 ` [RFC PATCH v2 4/4] KVM: x86: TDX: Report CORE_CAPABILITIES as supported Binbin Wu
3 siblings, 1 reply; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 2:33 UTC (permalink / raw)
To: kvm, linux-kernel
Cc: seanjc, pbonzini, rick.p.edgecombe, xiaoyao.li, chao.gao,
kai.huang, binbin.wu
Filter the CPUID capabilities reported by KVM_TDX_CAPABILITIES through
KVM's supported TDX configurable CPUID allowlist.
The TDX module reports all configurable CPUID bits it supports for a TD,
but KVM must not expose bits to userspace that it doesn't support.
Blindly exposing unsupported features could lead to host state corruption.
Add a helper get_supported_cfg_cpuid() to retrieve KVM's supported TDX
configurable CPUID bit mask for a given leaf, subleaf, and register.
Additionally, add a comment explicitly noting that the allowlist array
must remain sorted by CPUID function. This allows the helper's linear
search to safely terminate early once it iterates past the target leaf,
optimizing the lookup process.
Replace the existing hardcoded denylist with a secure-by-default approach.
By using the get_supported_cfg_cpuid() helper, KVM now strictly reports
only the configurable CPUID bits it explicitly supports. This ensures
any newly introduced configurable CPUID bits are automatically hidden from
userspace until KVM specifically opts in.
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
---
arch/x86/kvm/vmx/tdx.c | 45 +++++++++++++++++++++++-------------------
1 file changed, 25 insertions(+), 20 deletions(-)
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index e0567088ebf5..e6bfec87a484 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -64,6 +64,8 @@ struct tdx_supported_cpuid_reg {
/*
* Multi-bit fields are statically initialized, feature bits are initialized
* in tdx_initialize_cpu_cfg_caps().
+ *
+ * Keep the list sorted by CPUID function.
*/
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) },
@@ -300,34 +302,31 @@ static bool has_tsx(const struct kvm_cpuid_entry2 *entry)
(entry->ebx & TDX_FEATURE_TSX);
}
-static void clear_tsx(struct kvm_cpuid_entry2 *entry)
-{
- entry->ebx &= ~TDX_FEATURE_TSX;
-}
-
static bool has_waitpkg(const struct kvm_cpuid_entry2 *entry)
{
return entry->function == 7 && entry->index == 0 &&
(entry->ecx & __feature_bit(X86_FEATURE_WAITPKG));
}
-static void clear_waitpkg(struct kvm_cpuid_entry2 *entry)
+static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *entry)
{
- entry->ecx &= ~__feature_bit(X86_FEATURE_WAITPKG);
+ return has_tsx(entry) || has_waitpkg(entry);
}
-static void tdx_clear_unsupported_cpuid(struct kvm_cpuid_entry2 *entry)
+static u32 get_supported_cfg_cpuid(u32 function, u32 index, u8 reg)
{
- if (has_tsx(entry))
- clear_tsx(entry);
+ for (int i = 0; i < ARRAY_SIZE(tdx_kvm_supported_cpuid); i++) {
+ struct tdx_supported_cpuid_reg *r = &tdx_kvm_supported_cpuid[i];
- if (has_waitpkg(entry))
- clear_waitpkg(entry);
-}
+ if (r->function > function)
+ break;
-static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *entry)
-{
- return has_tsx(entry) || has_waitpkg(entry);
+ if (r->function == function && r->reg == reg &&
+ (r->index == index || (r->flags & TDX_CPUID_IGNORE_INDEX)))
+ return r->mask;
+ }
+
+ return 0;
}
#define KVM_TDX_CPUID_NO_SUBLEAF ((__u32)-1)
@@ -353,8 +352,6 @@ static void td_init_cpuid_entry2(struct kvm_cpuid_entry2 *entry, unsigned char i
*/
if (entry->function == 0x80000008)
entry->eax = tdx_set_guest_phys_addr_bits(entry->eax, 0xff);
-
- tdx_clear_unsupported_cpuid(entry);
}
#define TDVMCALLINFO_SETUP_EVENT_NOTIFY_INTERRUPT BIT(1)
@@ -377,8 +374,16 @@ static int init_kvm_tdx_caps(const struct tdx_sys_info_td_conf *td_conf,
caps->user_tdvmcallinfo_1_r11 =
TDVMCALLINFO_SETUP_EVENT_NOTIFY_INTERRUPT;
- for (i = 0; i < td_conf->num_cpuid_config; i++)
- td_init_cpuid_entry2(&caps->cpuid.entries[i], i);
+ for (i = 0; i < td_conf->num_cpuid_config; i++) {
+ struct kvm_cpuid_entry2 *e = &caps->cpuid.entries[i];
+
+ td_init_cpuid_entry2(e, i);
+ /* Only report the configurable bits supported by KVM. */
+ e->eax &= get_supported_cfg_cpuid(e->function, e->index, CPUID_EAX);
+ e->ebx &= get_supported_cfg_cpuid(e->function, e->index, CPUID_EBX);
+ e->ecx &= get_supported_cfg_cpuid(e->function, e->index, CPUID_ECX);
+ e->edx &= get_supported_cfg_cpuid(e->function, e->index, CPUID_EDX);
+ }
return 0;
}
--
2.46.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH v2 3/4] KVM: x86: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM
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 ` [RFC PATCH v2 1/4] KVM: x86: TDX: Track supported " Binbin Wu
2026-06-04 2:33 ` [RFC PATCH v2 2/4] KVM: x86: TDX: Hide unsupported " Binbin Wu
@ 2026-06-04 2:33 ` Binbin Wu
2026-06-04 2:49 ` sashiko-bot
2026-06-04 2:33 ` [RFC PATCH v2 4/4] KVM: x86: TDX: Report CORE_CAPABILITIES as supported Binbin Wu
3 siblings, 1 reply; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 2:33 UTC (permalink / raw)
To: kvm, linux-kernel
Cc: seanjc, pbonzini, rick.p.edgecombe, xiaoyao.li, chao.gao,
kai.huang, binbin.wu
Reject unsupported TDX configurable CPUID bits provided by userspace
during KVM_TDX_INIT_VM.
While the TDX module allows the VMM to configure certain CPUID
features for a TD during initialization, KVM must strictly govern
which features are actually enabled. Allowing userspace to blindly
enable features that KVM does not yet support—particularly those
involving host state clobbering MSRs—could lead to host state
corruption, as KVM is not prepared to manage the associated
architectural state across host/guest transitions.
Replace the hardcoded denylist with a robust validation mechanism. By
leveraging the get_supported_cfg_cpuid() helper, KVM now explicitly
rejects the input if userspace attempts to set any TDX configurable bit
that is not present in KVM's allowlist.
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
---
arch/x86/kvm/vmx/tdx.c | 30 ++++++++++--------------------
1 file changed, 10 insertions(+), 20 deletions(-)
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index e6bfec87a484..e44a862c6219 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -294,25 +294,6 @@ static u32 tdx_set_guest_phys_addr_bits(const u32 eax, int addr_bits)
return (eax & ~GENMASK(23, 16)) | (addr_bits & 0xff) << 16;
}
-#define TDX_FEATURE_TSX (__feature_bit(X86_FEATURE_HLE) | __feature_bit(X86_FEATURE_RTM))
-
-static bool has_tsx(const struct kvm_cpuid_entry2 *entry)
-{
- return entry->function == 7 && entry->index == 0 &&
- (entry->ebx & TDX_FEATURE_TSX);
-}
-
-static bool has_waitpkg(const struct kvm_cpuid_entry2 *entry)
-{
- return entry->function == 7 && entry->index == 0 &&
- (entry->ecx & __feature_bit(X86_FEATURE_WAITPKG));
-}
-
-static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *entry)
-{
- return has_tsx(entry) || has_waitpkg(entry);
-}
-
static u32 get_supported_cfg_cpuid(u32 function, u32 index, u8 reg)
{
for (int i = 0; i < ARRAY_SIZE(tdx_kvm_supported_cpuid); i++) {
@@ -2526,6 +2507,15 @@ static int setup_tdparams_eptp_controls(struct kvm_cpuid2 *cpuid,
return 0;
}
+static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *e,
+ const struct kvm_cpuid_entry2 *mask)
+{
+ return ((e->eax & mask->eax & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EAX))) ||
+ (e->ebx & mask->ebx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EBX))) ||
+ (e->ecx & mask->ecx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_ECX))) ||
+ (e->edx & mask->edx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EDX))));
+}
+
static int setup_tdparams_cpuids(struct kvm_cpuid2 *cpuid,
struct td_params *td_params)
{
@@ -2549,7 +2539,7 @@ static int setup_tdparams_cpuids(struct kvm_cpuid2 *cpuid,
if (!entry)
continue;
- if (tdx_unsupported_cpuid(entry))
+ if (tdx_unsupported_cpuid(entry, &tmp))
return -EINVAL;
copy_cnt++;
--
2.46.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [RFC PATCH v2 4/4] KVM: x86: TDX: Report CORE_CAPABILITIES as supported
2026-06-04 2:33 [RFC PATCH v2 0/4] KVM: x86: TDX: Validate directly configurable CPUID bits Binbin Wu
` (2 preceding siblings ...)
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:33 ` Binbin Wu
2026-06-04 2:51 ` sashiko-bot
2026-06-04 6:53 ` Xiaoyao Li
3 siblings, 2 replies; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 2:33 UTC (permalink / raw)
To: kvm, linux-kernel
Cc: seanjc, pbonzini, rick.p.edgecombe, xiaoyao.li, chao.gao,
kai.huang, binbin.wu
Add CORE_CAPABILITIES (CPUID.0x7.0.EDX[30]) to the TDX configurable
CPUID allowlist to accommodate legacy TDX module behavior.
KVM doesn't support MSR_IA32_CORE_CAPS, however, some older TDX specs
define CORE_CAPABILITIES CPUID bit as fixed-1. As a result, userspace
may expect this bit to be enabled in the TDX module for TDs. When the
CPUID bit becomes a directly configurable without reporting to the
userspace, it can not be enabled. To avoid confusing userspace, report
CORE_CAPABILITIES to userspace via KVM_TDX_CAPABILITIES.
Although KVM could determine the real CPUID setting by reading the
metadata via SEAMCALL after KVM_TDX_INIT_VM, doing so is overkill to
cover such a corner case. If CORE_CAPABILITIES is exposed to a TDX
guest, and the guest reads it, simply return 0.
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
---
arch/x86/kvm/vmx/tdx.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index e44a862c6219..58647bb70708 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -175,7 +175,7 @@ static void __init tdx_initialize_cpu_cfg_caps(void)
TDX_F(SERIALIZE),
TDX_F(TSXLDTRK),
/* PCONFIG */
- /* IA32_CORE_CAPABILITIES */
+ TDX_F(CORE_CAPABILITIES),
);
tdx_cpu_cfg_cap_init(0x7, 1, CPUID_EAX,
@@ -2401,6 +2401,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;
--
2.46.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 1/4] KVM: x86: TDX: Track supported configurable CPUID bits
2026-06-04 2:33 ` [RFC PATCH v2 1/4] KVM: x86: TDX: Track supported " Binbin Wu
@ 2026-06-04 2:44 ` sashiko-bot
2026-06-04 5:37 ` Binbin Wu
0 siblings, 1 reply; 16+ messages in thread
From: sashiko-bot @ 2026-06-04 2:44 UTC (permalink / raw)
To: Binbin Wu; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The `tdx_cpu_cfg_cap_init` macro is highly fragile and relies on the caller providing a mandatory trailing comma, which will cause obscure syntax errors if omitted.
--
commit 8aa0677abade944ac536d7acccef89b12ace7386
Author: Binbin Wu <binbin.wu@linux.intel.com>
KVM: x86: TDX: Track supported configurable CPUID bits
This patch introduces an allowlist for TDX directly configurable CPUID bits
supported by KVM. It tracks the supported bits by statically initializing
multi-bit fields and dynamically populating feature bits during hardware
setup using a new initialization macro.
> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> index b8c3d3d8bbfe5..028d54c76b81e 100644
> --- a/arch/x86/kvm/vmx/tdx.c
> +++ b/arch/x86/kvm/vmx/tdx.c
[ ... ]
> @@ -52,6 +52,178 @@
> +#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; \
[Severity: Low]
Is this macro design relying on the caller providing a mandatory trailing
comma?
Because feature_initializers expands to a list of statement expressions
like ({ tdx_cpu_cfg_caps |= feature_bit(name); }), omitting the trailing
comma in the macro invocation would result in a syntax error immediately
before r->mask |= tdx_cpu_cfg_caps.
Could TDX_F be redesigned to return the bitmask directly so they can be
combined using bitwise-OR, avoiding the strict trailing comma dependency?
> + 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),
> + );
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260604023314.3907511-1-binbin.wu@linux.intel.com?part=1
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 2/4] KVM: x86: TDX: Hide unsupported configurable CPUID bits
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
0 siblings, 1 reply; 16+ messages in thread
From: sashiko-bot @ 2026-06-04 2:47 UTC (permalink / raw)
To: Binbin Wu; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The secure-by-default CPUID allowlist is applied to KVM capability reporting but missing from TD initialization validation. This allows a malicious userspace VMM to bypass capability enumeration and supply unsupported configurable CPUID bits directly to the TDX module, potentially causing host state corruption.
--
commit e4dd30c1f72bbeeb74f3ba5a865ab30e0c17bcf6
Author: Binbin Wu <binbin.wu@linux.intel.com>
KVM: x86: TDX: Hide unsupported configurable CPUID bits
This commit introduces get_supported_cfg_cpuid() to filter the CPUID
capabilities reported by KVM_TDX_CAPABILITIES. It replaces a hardcoded
denylist with a secure-by-default allowlist approach to prevent exposing
unsupported features that could lead to host state corruption.
> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> index 028d54c76b81e..befda6bc17b1e 100644
> --- a/arch/x86/kvm/vmx/tdx.c
> +++ b/arch/x86/kvm/vmx/tdx.c
[ ... ]
> -static void clear_waitpkg(struct kvm_cpuid_entry2 *entry)
> +static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *entry)
> {
> - entry->ecx &= ~__feature_bit(X86_FEATURE_WAITPKG);
> + return has_tsx(entry) || has_waitpkg(entry);
> }
[Severity: High]
This is a pre-existing issue, but by keeping this legacy denylist active, is
there a risk that userspace can bypass the new secure-by-default allowlist
during TD initialization?
While this patch updates capability reporting to explicitly mask unsupported
features, the configuration path in setup_tdparams_cpuids() still appears to
use this older denylist function to validate user input:
arch/x86/kvm/vmx/tdx.c:setup_tdparams_cpuids() {
...
if (tdx_unsupported_cpuid(entry))
return -EINVAL;
value = &td_params->cpuid_values[i];
value->eax = entry->eax;
value->ebx = entry->ebx;
value->ecx = entry->ecx;
value->edx = entry->edx;
...
}
Because these user-provided CPUID values are copied directly into the hardware
initialization parameters without being masked against the newly introduced
get_supported_cfg_cpuid() allowlist, could a VMM explicitly request and pass
unsupported configurable CPUID bits directly to the TDX module?
Does this still leave the door open to the host state corruption mentioned
in the commit message if the TDX module only enforces hardware-level support
rather than KVM's software constraints?
>
> -static void tdx_clear_unsupported_cpuid(struct kvm_cpuid_entry2 *entry)
> +static u32 get_supported_cfg_cpuid(u32 function, u32 index, u8 reg)
> {
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260604023314.3907511-1-binbin.wu@linux.intel.com?part=2
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 3/4] KVM: x86: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM
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
0 siblings, 1 reply; 16+ messages in thread
From: sashiko-bot @ 2026-06-04 2:49 UTC (permalink / raw)
To: Binbin Wu; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] `tdx_unsupported_cpuid()` incorrectly uses the userspace-provided CPUID index instead of the canonical TDX configurable index for validation, allowing feature allowlist bypasses.
--
commit c2adba56353483cd6dcf40b3fe14141409c0ce54
Author: Binbin Wu <binbin.wu@linux.intel.com>
KVM: x86: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM
This commit replaces the hardcoded CPUID denylist with a validation mechanism
that checks userspace input against KVM's supported allowlist. It aims to
prevent userspace from enabling unsupported TDX features during VM init.
> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> --- a/arch/x86/kvm/vmx/tdx.c
> +++ b/arch/x86/kvm/vmx/tdx.c
[ ... ]
> +static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *e,
> + const struct kvm_cpuid_entry2 *mask)
> +{
> + return ((e->eax & mask->eax & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EAX))) ||
> + (e->ebx & mask->ebx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EBX))) ||
> + (e->ecx & mask->ecx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_ECX))) ||
> + (e->edx & mask->edx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EDX))));
> +}
[Severity: High]
Does this validation query the allowlist using the correct index?
If userspace provides a CPUID entry with the KVM_CPUID_FLAG_SIGNIFCANT_INDEX
flag cleared, kvm_find_cpuid_entry2() matches based solely on the function.
This means the returned entry 'e' could contain an arbitrary, user-controlled
index.
Because get_supported_cfg_cpuid() is queried using e->index instead of the
canonical target subleaf mask->index, userspace could spoof e->index to
point to a more permissive subleaf in KVM's allowlist. The TDX module would
then blindly apply those validated bits to the original, narrower target
subleaf, bypassing the intended feature restrictions.
Should this use mask->index instead of e->index when querying the allowlist?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260604023314.3907511-1-binbin.wu@linux.intel.com?part=3
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 4/4] KVM: x86: TDX: Report CORE_CAPABILITIES as supported
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 6:53 ` Xiaoyao Li
1 sibling, 1 reply; 16+ messages in thread
From: sashiko-bot @ 2026-06-04 2:51 UTC (permalink / raw)
To: Binbin Wu; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing handling of MSR_IA32_CORE_CAPS in tdx_set_msr() causes KVM_SET_MSRS to fail, breaking VM initialization and live migration.
--
commit 217a78047d73d2be68ef27e206470a6c237db79a
Author: Binbin Wu <binbin.wu@linux.intel.com>
KVM: x86: TDX: Report CORE_CAPABILITIES as supported
This commit adds CORE_CAPABILITIES to the TDX configurable CPUID allowlist
to accommodate legacy TDX module behavior. It also modifies tdx_get_msr()
to return 0 for MSR_IA32_CORE_CAPS.
> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
> index 26a5ad59a5694..b7661ba98a8d4 100644
> --- a/arch/x86/kvm/vmx/tdx.c
> +++ b/arch/x86/kvm/vmx/tdx.c
[ ... ]
> @@ -2319,6 +2319,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;
[Severity: High]
Since MSR_IA32_CORE_CAPS is now exposed to userspace via tdx_get_msr(), does
tdx_set_msr() need a corresponding update to accept host-initiated writes?
During VM migration or state restore, userspace typically reads MSRs on the
source and writes them to the destination. If tdx_set_msr() doesn't handle
MSR_IA32_CORE_CAPS, the KVM_SET_MSRS ioctl will fall through to the default
case.
Because tdx_has_emulated_msr() does not include MSR_IA32_CORE_CAPS, won't
tdx_set_msr() return an error and immediately abort the migration?
> default:
> if (!tdx_has_emulated_msr(msr->index))
> return 1;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260604023314.3907511-1-binbin.wu@linux.intel.com?part=4
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 2/4] KVM: x86: TDX: Hide unsupported configurable CPUID bits
2026-06-04 2:47 ` sashiko-bot
@ 2026-06-04 2:54 ` Binbin Wu
0 siblings, 0 replies; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 2:54 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 6/4/2026 10:47 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] The secure-by-default CPUID allowlist is applied to KVM capability reporting but missing from TD initialization validation. This allows a malicious userspace VMM to bypass capability enumeration and supply unsupported configurable CPUID bits directly to the TDX module, potentially causing host state corruption.
> --
>
> commit e4dd30c1f72bbeeb74f3ba5a865ab30e0c17bcf6
> Author: Binbin Wu <binbin.wu@linux.intel.com>
>
> KVM: x86: TDX: Hide unsupported configurable CPUID bits
>
> This commit introduces get_supported_cfg_cpuid() to filter the CPUID
> capabilities reported by KVM_TDX_CAPABILITIES. It replaces a hardcoded
> denylist with a secure-by-default allowlist approach to prevent exposing
> unsupported features that could lead to host state corruption.
>
>> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
>> index 028d54c76b81e..befda6bc17b1e 100644
>> --- a/arch/x86/kvm/vmx/tdx.c
>> +++ b/arch/x86/kvm/vmx/tdx.c
>
> [ ... ]
>
>> -static void clear_waitpkg(struct kvm_cpuid_entry2 *entry)
>> +static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *entry)
>> {
>> - entry->ecx &= ~__feature_bit(X86_FEATURE_WAITPKG);
>> + return has_tsx(entry) || has_waitpkg(entry);
>> }
>
> [Severity: High]
> This is a pre-existing issue, but by keeping this legacy denylist active, is
> there a risk that userspace can bypass the new secure-by-default allowlist
> during TD initialization?
>
> While this patch updates capability reporting to explicitly mask unsupported
> features, the configuration path in setup_tdparams_cpuids() still appears to
> use this older denylist function to validate user input:
>
> arch/x86/kvm/vmx/tdx.c:setup_tdparams_cpuids() {
> ...
> if (tdx_unsupported_cpuid(entry))
> return -EINVAL;
>
> value = &td_params->cpuid_values[i];
> value->eax = entry->eax;
> value->ebx = entry->ebx;
> value->ecx = entry->ecx;
> value->edx = entry->edx;
> ...
> }
>
> Because these user-provided CPUID values are copied directly into the hardware
> initialization parameters without being masked against the newly introduced
> get_supported_cfg_cpuid() allowlist, could a VMM explicitly request and pass
> unsupported configurable CPUID bits directly to the TDX module?
>
> Does this still leave the door open to the host state corruption mentioned
> in the commit message if the TDX module only enforces hardware-level support
> rather than KVM's software constraints?
This is addressed by the next patch.
>
>>
>> -static void tdx_clear_unsupported_cpuid(struct kvm_cpuid_entry2 *entry)
>> +static u32 get_supported_cfg_cpuid(u32 function, u32 index, u8 reg)
>> {
>
> [ ... ]
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 3/4] KVM: x86: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM
2026-06-04 2:49 ` sashiko-bot
@ 2026-06-04 3:13 ` Binbin Wu
0 siblings, 0 replies; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 3:13 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 6/4/2026 10:49 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] `tdx_unsupported_cpuid()` incorrectly uses the userspace-provided CPUID index instead of the canonical TDX configurable index for validation, allowing feature allowlist bypasses.
> --
>
> commit c2adba56353483cd6dcf40b3fe14141409c0ce54
> Author: Binbin Wu <binbin.wu@linux.intel.com>
>
> KVM: x86: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM
>
> This commit replaces the hardcoded CPUID denylist with a validation mechanism
> that checks userspace input against KVM's supported allowlist. It aims to
> prevent userspace from enabling unsupported TDX features during VM init.
>
>> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
>> --- a/arch/x86/kvm/vmx/tdx.c
>> +++ b/arch/x86/kvm/vmx/tdx.c
> [ ... ]
>> +static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *e,
>> + const struct kvm_cpuid_entry2 *mask)
>> +{
>> + return ((e->eax & mask->eax & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EAX))) ||
>> + (e->ebx & mask->ebx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EBX))) ||
>> + (e->ecx & mask->ecx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_ECX))) ||
>> + (e->edx & mask->edx & (~get_supported_cfg_cpuid(e->function, e->index, CPUID_EDX))));
>> +}
>
> [Severity: High]
> Does this validation query the allowlist using the correct index?
>
> If userspace provides a CPUID entry with the KVM_CPUID_FLAG_SIGNIFCANT_INDEX
> flag cleared, kvm_find_cpuid_entry2() matches based solely on the function.
> This means the returned entry 'e' could contain an arbitrary, user-controlled
> index.
>
> Because get_supported_cfg_cpuid() is queried using e->index instead of the
> canonical target subleaf mask->index, userspace could spoof e->index to
> point to a more permissive subleaf in KVM's allowlist. The TDX module would
> then blindly apply those validated bits to the original, narrower target
> subleaf, bypassing the intended feature restrictions.
>
> Should this use mask->index instead of e->index when querying the allowlist?
>
Right, it should use mask->index.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 4/4] KVM: x86: TDX: Report CORE_CAPABILITIES as supported
2026-06-04 2:51 ` sashiko-bot
@ 2026-06-04 5:32 ` Binbin Wu
2026-06-04 5:40 ` Binbin Wu
0 siblings, 1 reply; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 5:32 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm, Xiaoyao Li
On 6/4/2026 10:51 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Missing handling of MSR_IA32_CORE_CAPS in tdx_set_msr() causes KVM_SET_MSRS to fail, breaking VM initialization and live migration.
> --
>
> commit 217a78047d73d2be68ef27e206470a6c237db79a
> Author: Binbin Wu <binbin.wu@linux.intel.com>
>
> KVM: x86: TDX: Report CORE_CAPABILITIES as supported
>
> This commit adds CORE_CAPABILITIES to the TDX configurable CPUID allowlist
> to accommodate legacy TDX module behavior. It also modifies tdx_get_msr()
> to return 0 for MSR_IA32_CORE_CAPS.
>
>> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
>> index 26a5ad59a5694..b7661ba98a8d4 100644
>> --- a/arch/x86/kvm/vmx/tdx.c
>> +++ b/arch/x86/kvm/vmx/tdx.c
>
> [ ... ]
>
>> @@ -2319,6 +2319,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;
>
> [Severity: High]
> Since MSR_IA32_CORE_CAPS is now exposed to userspace via tdx_get_msr(), does
MSR_IA32_CORE_CAPS is not exposed to userspace via KVM_GET_MSR_INDEX_LIST.
I suppose userspace should retrieve the supported MSRs via KVM_GET_MSR_INDEX_LIST
before KVM_GET_MSRS/KVM_SET_MSRS?
Had a second thought, this MSR code could be dropped.
In Linux kernel, MSR_IA32_CORE_CAPS is either read by the safe version or skipped
when it's running in virtualized environment. Also, there was no complain about
the access of this MSR in any guest so far.
We can just report CORE_CAPABILITIES as a TDX configurable CPUID bit to userspace.
Userspace can do some change to remove any bit that reported as configurable from
fixed1 bits. Since KVM doesn't support CORE_CAPABILITIES via KVM_GET_CPUID2,
userspace then should not enable CORE_CAPABILITIES bit for TDX guests.
> tdx_set_msr() need a corresponding update to accept host-initiated writes?
>
> During VM migration or state restore, userspace typically reads MSRs on the
> source and writes them to the destination. If tdx_set_msr() doesn't handle
> MSR_IA32_CORE_CAPS, the KVM_SET_MSRS ioctl will fall through to the default
> case.
>
> Because tdx_has_emulated_msr() does not include MSR_IA32_CORE_CAPS, won't
> tdx_set_msr() return an error and immediately abort the migration?
>
>> default:
>> if (!tdx_has_emulated_msr(msr->index))
>> return 1;
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 1/4] KVM: x86: TDX: Track supported configurable CPUID bits
2026-06-04 2:44 ` sashiko-bot
@ 2026-06-04 5:37 ` Binbin Wu
0 siblings, 0 replies; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 5:37 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm
On 6/4/2026 10:44 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Low] The `tdx_cpu_cfg_cap_init` macro is highly fragile and relies on the caller providing a mandatory trailing comma, which will cause obscure syntax errors if omitted.
> --
>
> commit 8aa0677abade944ac536d7acccef89b12ace7386
> Author: Binbin Wu <binbin.wu@linux.intel.com>
>
> KVM: x86: TDX: Track supported configurable CPUID bits
>
> This patch introduces an allowlist for TDX directly configurable CPUID bits
> supported by KVM. It tracks the supported bits by statically initializing
> multi-bit fields and dynamically populating feature bits during hardware
> setup using a new initialization macro.
>
>> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
>> index b8c3d3d8bbfe5..028d54c76b81e 100644
>> --- a/arch/x86/kvm/vmx/tdx.c
>> +++ b/arch/x86/kvm/vmx/tdx.c
> [ ... ]
>> @@ -52,6 +52,178 @@
>> +#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; \
>
> [Severity: Low]
> Is this macro design relying on the caller providing a mandatory trailing
> comma?
Yes.
It adopts the same style as kvm_cpu_cap_init() in cpuid.c
>
> Because feature_initializers expands to a list of statement expressions
> like ({ tdx_cpu_cfg_caps |= feature_bit(name); }), omitting the trailing
> comma in the macro invocation would result in a syntax error immediately
> before r->mask |= tdx_cpu_cfg_caps.
>
> Could TDX_F be redesigned to return the bitmask directly so they can be
> combined using bitwise-OR, avoiding the strict trailing comma dependency?
>
>> + 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),
>> + );
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 4/4] KVM: x86: TDX: Report CORE_CAPABILITIES as supported
2026-06-04 5:32 ` Binbin Wu
@ 2026-06-04 5:40 ` Binbin Wu
0 siblings, 0 replies; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 5:40 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvm, Xiaoyao Li
On 6/4/2026 1:32 PM, Binbin Wu wrote:
>
>
> On 6/4/2026 10:51 AM, sashiko-bot@kernel.org wrote:
>> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>> - [High] Missing handling of MSR_IA32_CORE_CAPS in tdx_set_msr() causes KVM_SET_MSRS to fail, breaking VM initialization and live migration.
>> --
>>
>> commit 217a78047d73d2be68ef27e206470a6c237db79a
>> Author: Binbin Wu <binbin.wu@linux.intel.com>
>>
>> KVM: x86: TDX: Report CORE_CAPABILITIES as supported
>>
>> This commit adds CORE_CAPABILITIES to the TDX configurable CPUID allowlist
>> to accommodate legacy TDX module behavior. It also modifies tdx_get_msr()
>> to return 0 for MSR_IA32_CORE_CAPS.
>>
>>> diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
>>> index 26a5ad59a5694..b7661ba98a8d4 100644
>>> --- a/arch/x86/kvm/vmx/tdx.c
>>> +++ b/arch/x86/kvm/vmx/tdx.c
>>
>> [ ... ]
>>
>>> @@ -2319,6 +2319,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;
>>
>> [Severity: High]
>> Since MSR_IA32_CORE_CAPS is now exposed to userspace via tdx_get_msr(), does
> MSR_IA32_CORE_CAPS is not exposed to userspace via KVM_GET_MSR_INDEX_LIST.
> I suppose userspace should retrieve the supported MSRs via KVM_GET_MSR_INDEX_LIST
or KVM_GET_MSR_FEATURE_INDEX_LIST
> before KVM_GET_MSRS/KVM_SET_MSRS?
>
>
> Had a second thought, this MSR code could be dropped.
> In Linux kernel, MSR_IA32_CORE_CAPS is either read by the safe version or skipped
> when it's running in virtualized environment. Also, there was no complain about
> the access of this MSR in any guest so far.
>
> We can just report CORE_CAPABILITIES as a TDX configurable CPUID bit to userspace.
> Userspace can do some change to remove any bit that reported as configurable from
> fixed1 bits. Since KVM doesn't support CORE_CAPABILITIES via KVM_GET_CPUID2,
> userspace then should not enable CORE_CAPABILITIES bit for TDX guests.
>
>
>> tdx_set_msr() need a corresponding update to accept host-initiated writes?
>>
>> During VM migration or state restore, userspace typically reads MSRs on the
>> source and writes them to the destination. If tdx_set_msr() doesn't handle
>> MSR_IA32_CORE_CAPS, the KVM_SET_MSRS ioctl will fall through to the default
>> case.
>>
>> Because tdx_has_emulated_msr() does not include MSR_IA32_CORE_CAPS, won't
>> tdx_set_msr() return an error and immediately abort the migration?
>>
>>> default:
>>> if (!tdx_has_emulated_msr(msr->index))
>>> return 1;
>>
>
>
>
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 4/4] KVM: x86: TDX: Report CORE_CAPABILITIES as supported
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 6:53 ` Xiaoyao Li
2026-06-04 7:20 ` Binbin Wu
1 sibling, 1 reply; 16+ messages in thread
From: Xiaoyao Li @ 2026-06-04 6:53 UTC (permalink / raw)
To: Binbin Wu, kvm, linux-kernel
Cc: seanjc, pbonzini, rick.p.edgecombe, chao.gao, kai.huang
On 6/4/2026 10:33 AM, Binbin Wu wrote:
> Add CORE_CAPABILITIES (CPUID.0x7.0.EDX[30]) to the TDX configurable
> CPUID allowlist to accommodate legacy TDX module behavior.
>
> KVM doesn't support MSR_IA32_CORE_CAPS, however, some older TDX specs
> define CORE_CAPABILITIES CPUID bit as fixed-1. As a result, userspace
> may expect this bit to be enabled in the TDX module for TDs. When the
> CPUID bit becomes a directly configurable without reporting to the
> userspace, it can not be enabled. To avoid confusing userspace, report
> CORE_CAPABILITIES to userspace via KVM_TDX_CAPABILITIES.
>
> Although KVM could determine the real CPUID setting by reading the
> metadata via SEAMCALL after KVM_TDX_INIT_VM, doing so is overkill to
> cover such a corner case. If CORE_CAPABILITIES is exposed to a TDX
> guest, and the guest reads it, simply return 0.
shouldn't this patch be put as patch 02 instead of 04?
Patch 02 and 03 in this series break the old QEMU and then patch 04
fixes the broken. This is not friendly to the bisect.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [RFC PATCH v2 4/4] KVM: x86: TDX: Report CORE_CAPABILITIES as supported
2026-06-04 6:53 ` Xiaoyao Li
@ 2026-06-04 7:20 ` Binbin Wu
0 siblings, 0 replies; 16+ messages in thread
From: Binbin Wu @ 2026-06-04 7:20 UTC (permalink / raw)
To: Xiaoyao Li
Cc: kvm, linux-kernel, seanjc, pbonzini, rick.p.edgecombe, chao.gao,
kai.huang
On 6/4/2026 2:53 PM, Xiaoyao Li wrote:
> On 6/4/2026 10:33 AM, Binbin Wu wrote:
>> Add CORE_CAPABILITIES (CPUID.0x7.0.EDX[30]) to the TDX configurable
>> CPUID allowlist to accommodate legacy TDX module behavior.
>>
>> KVM doesn't support MSR_IA32_CORE_CAPS, however, some older TDX specs
>> define CORE_CAPABILITIES CPUID bit as fixed-1. As a result, userspace
>> may expect this bit to be enabled in the TDX module for TDs. When the
>> CPUID bit becomes a directly configurable without reporting to the
>> userspace, it can not be enabled. To avoid confusing userspace, report
>> CORE_CAPABILITIES to userspace via KVM_TDX_CAPABILITIES.
>>
>> Although KVM could determine the real CPUID setting by reading the
>> metadata via SEAMCALL after KVM_TDX_INIT_VM, doing so is overkill to
>> cover such a corner case. If CORE_CAPABILITIES is exposed to a TDX
>> guest, and the guest reads it, simply return 0.
>
> shouldn't this patch be put as patch 02 instead of 04?
>
> Patch 02 and 03 in this series break the old QEMU and then patch 04 fixes the broken. This is not friendly to the bisect.
Good point.
I will reorder the patch in the later version if the direction of this
patch series is acked by maintainers.
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-06-04 7:20 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [RFC PATCH v2 1/4] KVM: x86: TDX: Track supported " Binbin Wu
2026-06-04 2:44 ` 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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox