diff mbox series

[v5,01/22] x86/virt/tdx: Detect TDX during kernel boot

Message ID 062075b36150b119bf2d0a1262de973b0a2b11a7.1655894131.git.kai.huang@intel.com (mailing list archive)
State New, archived
Headers show
Series TDX host kernel support | expand

Commit Message

Huang, Kai June 22, 2022, 11:15 a.m. UTC
Intel Trust Domain Extensions (TDX) protects guest VMs from malicious
host and certain physical attacks.  TDX introduces a new CPU mode called
Secure Arbitration Mode (SEAM) and a new isolated range pointed by the
SEAM Ranger Register (SEAMRR).  A CPU-attested software module called
'the TDX module' runs inside the new isolated range to implement the
functionalities to manage and run protected VMs.

Pre-TDX Intel hardware has support for a memory encryption architecture
called MKTME.  The memory encryption hardware underpinning MKTME is also
used for Intel TDX.  TDX ends up "stealing" some of the physical address
space from the MKTME architecture for crypto-protection to VMs.  BIOS is
responsible for partitioning the "KeyID" space between legacy MKTME and
TDX.  The KeyIDs reserved for TDX are called 'TDX private KeyIDs' or
'TDX KeyIDs' for short.

To enable TDX, BIOS needs to configure SEAMRR (core-scope) and TDX
private KeyIDs (package-scope) consistently for all packages.  TDX
doesn't trust BIOS.  TDX ensures all BIOS configurations are correct,
and if not, refuses to enable SEAMRR on any core.  This means detecting
SEAMRR alone on BSP is enough to check whether TDX has been enabled by
BIOS.

To start to support TDX, create a new arch/x86/virt/vmx/tdx/tdx.c for
TDX host kernel support.  Add a new Kconfig option CONFIG_INTEL_TDX_HOST
to opt-in TDX host kernel support (to distinguish with TDX guest kernel
support).  So far only KVM is the only user of TDX.  Make the new config
option depend on KVM_INTEL.

Use early_initcall() to detect whether TDX is enabled by BIOS during
kernel boot, and add a function to report that.  Use a function instead
of a new CPU feature bit.  This is because the TDX module needs to be
initialized before it can be used to run any TDX guests, and the TDX
module is initialized at runtime by the caller who wants to use TDX.

Explicitly detect SEAMRR but not just only detect TDX private KeyIDs.
Theoretically, a misconfiguration of TDX private KeyIDs can result in
SEAMRR being disabled, but the BSP can still report the correct TDX
KeyIDs.  Such BIOS bug can be caught when initializing the TDX module,
but it's better to do more detection during boot to provide a more
accurate result.

Also detect the TDX KeyIDs.  This allows userspace to know how many TDX
guests the platform can run w/o needing to wait until TDX is fully
functional.

Signed-off-by: Kai Huang <kai.huang@intel.com>
---
 arch/x86/Kconfig               |  13 ++++
 arch/x86/Makefile              |   2 +
 arch/x86/include/asm/tdx.h     |   7 +++
 arch/x86/virt/Makefile         |   2 +
 arch/x86/virt/vmx/Makefile     |   2 +
 arch/x86/virt/vmx/tdx/Makefile |   2 +
 arch/x86/virt/vmx/tdx/tdx.c    | 109 +++++++++++++++++++++++++++++++++
 arch/x86/virt/vmx/tdx/tdx.h    |  47 ++++++++++++++
 8 files changed, 184 insertions(+)
 create mode 100644 arch/x86/virt/Makefile
 create mode 100644 arch/x86/virt/vmx/Makefile
 create mode 100644 arch/x86/virt/vmx/tdx/Makefile
 create mode 100644 arch/x86/virt/vmx/tdx/tdx.c
 create mode 100644 arch/x86/virt/vmx/tdx/tdx.h

Comments

Chao Gao June 23, 2022, 5:57 a.m. UTC | #1
On Wed, Jun 22, 2022 at 11:15:30PM +1200, Kai Huang wrote:
>Intel Trust Domain Extensions (TDX) protects guest VMs from malicious
>host and certain physical attacks.  TDX introduces a new CPU mode called
>Secure Arbitration Mode (SEAM) and a new isolated range pointed by the
						    ^ perhaps, range of memory

>SEAM Ranger Register (SEAMRR).  A CPU-attested software module called
>'the TDX module' runs inside the new isolated range to implement the
>functionalities to manage and run protected VMs.
>
>Pre-TDX Intel hardware has support for a memory encryption architecture
>called MKTME.  The memory encryption hardware underpinning MKTME is also
>used for Intel TDX.  TDX ends up "stealing" some of the physical address
>space from the MKTME architecture for crypto-protection to VMs.  BIOS is
>responsible for partitioning the "KeyID" space between legacy MKTME and
>TDX.  The KeyIDs reserved for TDX are called 'TDX private KeyIDs' or
>'TDX KeyIDs' for short.
>
>To enable TDX, BIOS needs to configure SEAMRR (core-scope) and TDX
>private KeyIDs (package-scope) consistently for all packages.  TDX
>doesn't trust BIOS.  TDX ensures all BIOS configurations are correct,
>and if not, refuses to enable SEAMRR on any core.  This means detecting
>SEAMRR alone on BSP is enough to check whether TDX has been enabled by
>BIOS.
>
>To start to support TDX, create a new arch/x86/virt/vmx/tdx/tdx.c for
>TDX host kernel support.  Add a new Kconfig option CONFIG_INTEL_TDX_HOST
>to opt-in TDX host kernel support (to distinguish with TDX guest kernel
>support).  So far only KVM is the only user of TDX.  Make the new config
>option depend on KVM_INTEL.
>
>Use early_initcall() to detect whether TDX is enabled by BIOS during
>kernel boot, and add a function to report that.  Use a function instead
>of a new CPU feature bit.  This is because the TDX module needs to be
>initialized before it can be used to run any TDX guests, and the TDX
>module is initialized at runtime by the caller who wants to use TDX.
>
>Explicitly detect SEAMRR but not just only detect TDX private KeyIDs.
>Theoretically, a misconfiguration of TDX private KeyIDs can result in
>SEAMRR being disabled, but the BSP can still report the correct TDX
>KeyIDs.  Such BIOS bug can be caught when initializing the TDX module,
>but it's better to do more detection during boot to provide a more
>accurate result.
>
>Also detect the TDX KeyIDs.  This allows userspace to know how many TDX
>guests the platform can run w/o needing to wait until TDX is fully
>functional.
>
>Signed-off-by: Kai Huang <kai.huang@intel.com>

Reviewed-by: Chao Gao <chao.gao@intel.com>

But some cosmetic comments below ...

>---
>+
>+static u32 tdx_keyid_start __ro_after_init;
>+static u32 tdx_keyid_num __ro_after_init;
>+
...

>+static int detect_tdx_keyids(void)
>+{
>+	u64 keyid_part;
>+
>+	rdmsrl(MSR_IA32_MKTME_KEYID_PARTITIONING, keyid_part);

how about:
	rdmsr(MSR_IA32_MKTME_KEYID_PARTITIONING, tdx_keyid_start, tdx_keyid_num);
	tdx_keyid_start++;

Then TDX_KEYID_NUM/START can be dropped.

>+
>+	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).

Maybe it is better to have a comment for why TDX/kernel guarantees
there should be at least 2 TDX keyIDs.

>+
>+/*
>+ * 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)

Can you move this bit definition to arch/x86/include/asm/msr-index.h
right after MSR_MTRRcap definition there?
Huang, Kai June 23, 2022, 9:23 a.m. UTC | #2
On Thu, 2022-06-23 at 13:57 +0800, Chao Gao wrote:
> On Wed, Jun 22, 2022 at 11:15:30PM +1200, Kai Huang wrote:
> > Intel Trust Domain Extensions (TDX) protects guest VMs from malicious
> > host and certain physical attacks.  TDX introduces a new CPU mode called
> > Secure Arbitration Mode (SEAM) and a new isolated range pointed by the
> 						    ^ perhaps, range of memory

OK.  The spec indeed says "execute out of memory defined by SEAM ranger register
(SEAMRR)".

> 
> > +static int detect_tdx_keyids(void)
> > +{
> > +	u64 keyid_part;
> > +
> > +	rdmsrl(MSR_IA32_MKTME_KEYID_PARTITIONING, keyid_part);
> 
> how about:
> 	rdmsr(MSR_IA32_MKTME_KEYID_PARTITIONING, tdx_keyid_start, tdx_keyid_num);
> 	tdx_keyid_start++;
> 
> Then TDX_KEYID_NUM/START can be dropped.

OK will do.

> 
> > +
> > +	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).
> 
> Maybe it is better to have a comment for why TDX/kernel guarantees
> there should be at least 2 TDX keyIDs.

"TDX guarantees" means it is architectural behaviour.  Perhaps I can change to
"TDX architecture guarantee" to be more explicit.

This part is currently not in the public spec, but I am working with others to
add this to the public spec.

> 
> > +
> > +/*
> > + * 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)
> 
> Can you move this bit definition to arch/x86/include/asm/msr-index.h
> right after MSR_MTRRcap definition there?

The comment at the beginning of this file says:

/*
 * CPU model specific register (MSR) numbers.
 *
 * Do not add new entries to this file unless the definitions are shared
 * between multiple compilation units.
 */

I am not sure whether adding a new bit of one MSR (which is already defined) is
adding a "new entry".  Perhaps it is not.  But I'd like to leave to maintainers.
Binbin Wu Aug. 2, 2022, 2:01 a.m. UTC | #3
On 2022/6/22 19:15, Kai Huang wrote:
> +	/*
> +	 * 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)) {
Do you think it's better to define a meaningful macro instead of the 
number here and below?
> +		tdx_keyid_start = tdx_keyid_num = 0;
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +

> +
> +/**
> + * 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;
> +}
>
>
Huang, Kai Aug. 3, 2022, 9:25 a.m. UTC | #4
On Tue, 2022-08-02 at 10:01 +0800, Wu, Binbin wrote:
> On 2022/6/22 19:15, Kai Huang wrote:
> > +	/*
> > +	 * 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)) {
> Do you think it's better to define a meaningful macro instead of the 
> number here and below?
> > 

Personally I don't think we need a macro.  The comment already said "two", so 
having a macro doesn't help readability here (and below).  But I am open on
this.
diff mbox series

Patch

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