diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 7021ec725dd3..23f21aa3a5c4 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1967,6 +1967,19 @@ config X86_SGX
 
 	  If unsure, say N.
 
+config INTEL_TDX_HOST
+	bool "Intel Trust Domain Extensions (TDX) host support"
+	default n
+	depends on CPU_SUP_INTEL
+	depends on X86_64
+	depends on KVM_INTEL
+	help
+	  Intel Trust Domain Extensions (TDX) protects guest VMs from malicious
+	  host and certain physical attacks.  This option enables necessary TDX
+	  support in host kernel to run protected VMs.
+
+	  If unsure, say N.
+
 config EFI
 	bool "EFI runtime service support"
 	depends on ACPI
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 63d50f65b828..2ca3a2a36dc5 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -234,6 +234,8 @@ head-y += arch/x86/kernel/platform-quirks.o
 
 libs-y  += arch/x86/lib/
 
+core-y += arch/x86/virt/
+
 # drivers-y are linked after core-y
 drivers-$(CONFIG_MATH_EMULATION) += arch/x86/math-emu/
 drivers-$(CONFIG_PCI)            += arch/x86/pci/
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 020c81a7c729..97511b76c1ac 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -87,5 +87,12 @@ static inline long tdx_kvm_hypercall(unsigned int nr, unsigned long p1,
 	return -ENODEV;
 }
 #endif /* CONFIG_INTEL_TDX_GUEST && CONFIG_KVM_GUEST */
+
+#ifdef CONFIG_INTEL_TDX_HOST
+bool platform_tdx_enabled(void);
+#else	/* !CONFIG_INTEL_TDX_HOST */
+static inline bool platform_tdx_enabled(void) { return false; }
+#endif	/* CONFIG_INTEL_TDX_HOST */
+
 #endif /* !__ASSEMBLY__ */
 #endif /* _ASM_X86_TDX_H */
diff --git a/arch/x86/virt/Makefile b/arch/x86/virt/Makefile
new file mode 100644
index 000000000000..1e36502cd738
--- /dev/null
+++ b/arch/x86/virt/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y	+= vmx/
diff --git a/arch/x86/virt/vmx/Makefile b/arch/x86/virt/vmx/Makefile
new file mode 100644
index 000000000000..feebda21d793
--- /dev/null
+++ b/arch/x86/virt/vmx/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_INTEL_TDX_HOST)	+= tdx/
diff --git a/arch/x86/virt/vmx/tdx/Makefile b/arch/x86/virt/vmx/tdx/Makefile
new file mode 100644
index 000000000000..1bd688684716
--- /dev/null
+++ b/arch/x86/virt/vmx/tdx/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_INTEL_TDX_HOST)	+= tdx.o
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
new file mode 100644
index 000000000000..8275007702e6
--- /dev/null
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(c) 2022 Intel Corporation.
+ *
+ * Intel Trusted Domain Extensions (TDX) support
+ */
+
+#define pr_fmt(fmt)	"tdx: " fmt
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/printk.h>
+#include <asm/cpufeatures.h>
+#include <asm/cpufeature.h>
+#include <asm/msr-index.h>
+#include <asm/msr.h>
+#include <asm/tdx.h>
+#include "tdx.h"
+
+static u32 tdx_keyid_start __ro_after_init;
+static u32 tdx_keyid_num __ro_after_init;
+
+/* Detect whether CPU supports SEAM */
+static int detect_seam(void)
+{
+	u64 mtrrcap, mask;
+
+	/* SEAMRR is reported via MTRRcap */
+	if (!boot_cpu_has(X86_FEATURE_MTRR))
+		return -ENODEV;
+
+	rdmsrl(MSR_MTRRcap, mtrrcap);
+	if (!(mtrrcap & MTRR_CAP_SEAMRR))
+		return -ENODEV;
+
+	/* The MASK MSR reports whether SEAMRR is enabled */
+	rdmsrl(MSR_IA32_SEAMRR_PHYS_MASK, mask);
+	if ((mask & SEAMRR_ENABLED_BITS) != SEAMRR_ENABLED_BITS)
+		return -ENODEV;
+
+	pr_info("SEAMRR enabled.\n");
+	return 0;
+}
+
+static int detect_tdx_keyids(void)
+{
+	u64 keyid_part;
+
+	rdmsrl(MSR_IA32_MKTME_KEYID_PARTITIONING, keyid_part);
+
+	tdx_keyid_num = TDX_KEYID_NUM(keyid_part);
+	tdx_keyid_start = TDX_KEYID_START(keyid_part);
+
+	pr_info("TDX private KeyID range: [%u, %u).\n",
+			tdx_keyid_start, tdx_keyid_start + tdx_keyid_num);
+
+	/*
+	 * TDX guarantees at least two TDX KeyIDs are configured by
+	 * BIOS, otherwise SEAMRR is disabled.  Invalid TDX private
+	 * range means kernel bug (TDX is broken).
+	 */
+	if (WARN_ON(!tdx_keyid_start || tdx_keyid_num < 2)) {
+		tdx_keyid_start = tdx_keyid_num = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Detect TDX via detecting SEAMRR during kernel boot.
+ *
+ * To enable TDX, BIOS must configure SEAMRR consistently across all
+ * CPU cores.  TDX doesn't trust BIOS.  Instead, MCHECK verifies all
+ * configurations from BIOS are correct, and if not, it disables TDX
+ * (SEAMRR is disabled on all cores).  This means detecting SEAMRR on
+ * BSP is enough to determine whether TDX has been enabled by BIOS.
+ */
+static int __init tdx_early_detect(void)
+{
+	int ret;
+
+	ret = detect_seam();
+	if (ret)
+		return ret;
+
+	/*
+	 * TDX private KeyIDs is only accessible by SEAM software.
+	 * Only detect TDX KeyIDs when SEAMRR is enabled.
+	 */
+	ret = detect_tdx_keyids();
+	if (ret)
+		return ret;
+
+	pr_info("TDX enabled by BIOS.\n");
+	return 0;
+}
+early_initcall(tdx_early_detect);
+
+/**
+ * platform_tdx_enabled() - Return whether BIOS has enabled TDX
+ *
+ * Return whether BIOS has enabled TDX regardless whether the TDX module
+ * has been loaded or not.
+ */
+bool platform_tdx_enabled(void)
+{
+	return tdx_keyid_num >= 2;
+}
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
new file mode 100644
index 000000000000..f16055cc25f4
--- /dev/null
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _X86_VIRT_TDX_H
+#define _X86_VIRT_TDX_H
+
+#include <linux/bits.h>
+
+/*
+ * This file contains both macros and data structures defined by the TDX
+ * architecture and Linux defined software data structures and functions.
+ * The two should not be mixed together for better readability.  The
+ * architectural definitions come first.
+ */
+
+/*
+ * Intel Trusted Domain CPU Architecture Extension spec:
+ *
+ * IA32_MTRRCAP:
+ *   Bit 15:	The support of SEAMRR
+ *
+ * IA32_SEAMRR_PHYS_MASK (core-scope):
+ *   Bit 10:	Lock bit
+ *   Bit 11:	Enable bit
+ */
+#define MTRR_CAP_SEAMRR			BIT_ULL(15)
+
+#define MSR_IA32_SEAMRR_PHYS_MASK	0x00001401
+
+#define SEAMRR_PHYS_MASK_ENABLED	BIT_ULL(11)
+#define SEAMRR_PHYS_MASK_LOCKED		BIT_ULL(10)
+#define SEAMRR_ENABLED_BITS	\
+	(SEAMRR_PHYS_MASK_ENABLED | SEAMRR_PHYS_MASK_LOCKED)
+
+/*
+ * IA32_MKTME_KEYID_PARTIONING:
+ *   Bit [31:0]:	Number of MKTME KeyIDs.
+ *   Bit [63:32]:	Number of TDX private KeyIDs.
+ *
+ * MKTME KeyIDs start from KeyID 1. TDX private KeyIDs start
+ * after the last MKTME KeyID.
+ */
+#define MSR_IA32_MKTME_KEYID_PARTITIONING	0x00000087
+
+#define TDX_KEYID_START(_keyid_part)	\
+		((u32)(((_keyid_part) & 0xffffffffull) + 1))
+#define TDX_KEYID_NUM(_keyid_part)	((u32)((_keyid_part) >> 32))
+
+#endif
