From: Binbin Wu <binbin.wu@linux.intel.com>
To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org
Cc: seanjc@google.com, pbonzini@redhat.com,
dave.hansen@linux.intel.com, andrew.cooper3@citrix.com,
nik.borisov@suse.com, kas@kernel.org, rick.p.edgecombe@intel.com,
xiaoyao.li@intel.com, chao.gao@intel.com,
binbin.wu@linux.intel.com
Subject: [PATCH v3 3/4] KVM: TDX: Filter configurable CPUID bits
Date: Thu, 27 Aug 2026 11:18:36 +0800 [thread overview]
Message-ID: <20260827031837.2863609-4-binbin.wu@linux.intel.com> (raw)
In-Reply-To: <20260827031837.2863609-1-binbin.wu@linux.intel.com>
Filter the directly configurable CPUID bits reported through
KVM_TDX_CAPABILITIES against KVM's TDX allowlist, and drop the hardcoded
denylist based filtering.
The TDX module reports all directly configurable CPUID bits that it
supports for a TD, but KVM must not expose bits that it doesn't support,
as blindly exposing a host state clobbering feature can lead to host state
corruption. The existing denylist, which clears only TSX and WAITPKG, is
not fail-safe.
Add tdx_get_allowed_cfg_cpuid_mask() to get the mask of directly
configurable bits allowed by KVM for a given CPUID register, covering both
feature bits, which come from tdx_cpu_cfg_caps[], and non-feature bits,
which are enumerated at runtime. Apply the mask to every CPUID register
reported through KVM_TDX_CAPABILITIES.
With the allowlist in place, newly introduced TDX directly configurable
CPUID bits stay hidden from userspace until KVM explicitly opts in.
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
---
v3:
- Handle non-feature leafs at runtime. (Sean)
- Add CPUID.0x24.0.EBX[7:0] into allow list. There is a mismatch of the
description about CPUID.0x24.0.EBX[7:0], which is listed as
"XFAM & CPUID_Enabled & Native" but should be "XFAM & CPUID_Enabled &
Configured & Native".
---
arch/x86/kvm/vmx/tdx.c | 84 +++++++++++++++++++++++++++++++++---------
1 file changed, 66 insertions(+), 18 deletions(-)
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index b020518717ac..e8951353de73 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -277,34 +277,76 @@ 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)
+#define TDX_CPUID_ALL_ALLOWED_MASK GENMASK_U32(31, 0)
+
+static u32 tdx_cfg_non_feature_mask(u32 function, u32 index, int reg)
{
- if (has_tsx(entry))
- clear_tsx(entry);
+ /*
+ * For a leaf/subleaf/register that will never be repurposed to hold
+ * feature bits, it's safe to return TDX_CPUID_ALL_ALLOWED_MASK, i.e.
+ * leave the TDX module's CPUID config mask intact.
+ */
+ switch (function) {
+ case 1:
+ if (reg == CPUID_EAX || reg == CPUID_EBX)
+ return TDX_CPUID_ALL_ALLOWED_MASK;
+ return 0;
+ case 4:
+ case 0x18:
+ case 0x1f:
+ return TDX_CPUID_ALL_ALLOWED_MASK;
+ case 0x24:
+ if (index == 0 && reg == CPUID_EBX)
+ return GENMASK_U32(7, 0);
+ return 0;
+ case 0x80000008:
+ if (reg == CPUID_EAX)
+ return TDX_CPUID_ALL_ALLOWED_MASK;
+ return 0;
+ default:
+ return 0;
+ }
+}
- if (has_waitpkg(entry))
- clear_waitpkg(entry);
+static u32 tdx_cfg_feature_mask(u32 function, u32 index, int reg)
+{
+ for (int i = 0; i < NR_KVM_CPU_CAPS; i++) {
+ const struct cpuid_reg *cpuid = &reverse_cpuid[i];
+
+ if (!cpuid->function)
+ continue;
+
+ if (cpuid->function == function && cpuid->index == index &&
+ cpuid->reg == reg)
+ return tdx_cpu_cfg_caps[i];
+ }
+
+ return 0;
}
-static bool tdx_unsupported_cpuid(const struct kvm_cpuid_entry2 *entry)
+static u32 tdx_get_allowed_cfg_cpuid_mask(u32 function, u32 index, int reg)
{
- return has_tsx(entry) || has_waitpkg(entry);
+ u32 non_feature_mask = tdx_cfg_non_feature_mask(function, index, reg);
+
+ if (non_feature_mask == TDX_CPUID_ALL_ALLOWED_MASK)
+ return TDX_CPUID_ALL_ALLOWED_MASK;
+
+ /*
+ * It's possible that a CPUID register contains both feature and
+ * non-feature bits.
+ */
+ return non_feature_mask | tdx_cfg_feature_mask(function, index, reg);
}
#define KVM_TDX_CPUID_NO_SUBLEAF ((__u32)-1)
@@ -330,8 +372,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)
@@ -354,8 +394,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 allowed by KVM. */
+ e->eax &= tdx_get_allowed_cfg_cpuid_mask(e->function, e->index, CPUID_EAX);
+ e->ebx &= tdx_get_allowed_cfg_cpuid_mask(e->function, e->index, CPUID_EBX);
+ e->ecx &= tdx_get_allowed_cfg_cpuid_mask(e->function, e->index, CPUID_ECX);
+ e->edx &= tdx_get_allowed_cfg_cpuid_mask(e->function, e->index, CPUID_EDX);
+ }
return 0;
}
--
2.46.0
next prev parent reply other threads:[~2026-08-27 3:14 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-27 3:18 [PATCH v3 0/4] KVM: TDX: Validate directly configurable CPUID bits Binbin Wu
2026-08-27 3:18 ` [PATCH v3 1/4] KVM: TDX: Track configurable CPUID bits allowed by KVM Binbin Wu
2026-08-27 3:18 ` [PATCH v3 2/4] KVM: TDX: Report CORE_CAPABILITIES as configurable Binbin Wu
2026-08-27 3:18 ` Binbin Wu [this message]
2026-08-27 3:18 ` [PATCH v3 4/4] KVM: TDX: Validate userspace CPUID input for KVM_TDX_INIT_VM Binbin Wu
2026-08-27 3:24 ` sashiko-bot
2026-08-27 7:25 ` Binbin Wu
2026-08-27 19:33 ` [PATCH v3 0/4] KVM: TDX: Validate directly configurable CPUID bits Edgecombe, Rick P
2026-08-28 3:19 ` Binbin Wu
2026-08-28 16:58 ` Edgecombe, Rick P
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=20260827031837.2863609-4-binbin.wu@linux.intel.com \
--to=binbin.wu@linux.intel.com \
--cc=andrew.cooper3@citrix.com \
--cc=chao.gao@intel.com \
--cc=dave.hansen@linux.intel.com \
--cc=kas@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nik.borisov@suse.com \
--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;
as well as URLs for NNTP newsgroup(s).