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@xxxxxxxxx> --- 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 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 -- 2.35.3