Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/virt/tdx: Detect TDX during kernel boot

Intel Trust Domain Extensions (TDX) protects guest VMs from malicious
host and certain physical attacks. A CPU-attested software module
called 'the TDX module' runs inside a new isolated memory range as a
trusted hypervisor 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. The
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.

During machine boot, TDX microcode verifies that the BIOS programmed TDX
private KeyIDs consistently and correctly programmed across all CPU
packages. The MSRs are locked in this state after verification. This
is why MSR_IA32_MKTME_KEYID_PARTITIONING gets used for TDX enumeration:
it indicates not just that the hardware supports TDX, but that all the
boot-time security checks passed.

The TDX module is expected to be loaded by the BIOS when it enables TDX,
but the kernel needs to properly initialize it before it can be used to
create and run any TDX guests. The TDX module will be initialized by
the KVM subsystem when KVM wants to use TDX.

Detect platform TDX support by detecting TDX private KeyIDs.

The TDX module itself requires one TDX KeyID as the 'TDX global KeyID'
to protect its metadata. Each TDX guest also needs a TDX KeyID for its
own protection. Just use the first TDX KeyID as the global KeyID and
leave the rest for TDX guests. If no TDX KeyID is left for TDX guests,
disable TDX as initializing the TDX module alone is useless.

[ dhansen: add X86_FEATURE, replace helper function ]

Signed-off-by: Kai Huang <kai.huang@intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reviewed-by: Isaku Yamahata <isaku.yamahata@intel.com>
Reviewed-by: David Hildenbrand <david@redhat.com>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Link: https://lore.kernel.org/all/20231208170740.53979-1-dave.hansen%40intel.com

authored by

Kai Huang and committed by
Dave Hansen
765a0542 33cc938e

+91 -1
+1
arch/x86/include/asm/cpufeatures.h
··· 198 198 #define X86_FEATURE_CAT_L3 ( 7*32+ 4) /* Cache Allocation Technology L3 */ 199 199 #define X86_FEATURE_CAT_L2 ( 7*32+ 5) /* Cache Allocation Technology L2 */ 200 200 #define X86_FEATURE_CDP_L3 ( 7*32+ 6) /* Code and Data Prioritization L3 */ 201 + #define X86_FEATURE_TDX_HOST_PLATFORM ( 7*32+ 7) /* Platform supports being a TDX host */ 201 202 #define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */ 202 203 #define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */ 203 204 #define X86_FEATURE_XCOMPACTED ( 7*32+10) /* "" Use compacted XSTATE (XSAVES or XSAVEC) */
+3
arch/x86/include/asm/msr-index.h
··· 536 536 #define MSR_RELOAD_PMC0 0x000014c1 537 537 #define MSR_RELOAD_FIXED_CTR0 0x00001309 538 538 539 + /* KeyID partitioning between MKTME and TDX */ 540 + #define MSR_IA32_MKTME_KEYID_PARTITIONING 0x00000087 541 + 539 542 /* 540 543 * AMD64 MSRs. Not complete. See the architecture manual for a more 541 544 * complete list.
+3
arch/x86/include/asm/tdx.h
··· 83 83 u64 __seamcall(u64 fn, struct tdx_module_args *args); 84 84 u64 __seamcall_ret(u64 fn, struct tdx_module_args *args); 85 85 u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args); 86 + void tdx_init(void); 87 + #else 88 + static inline void tdx_init(void) { } 86 89 #endif /* CONFIG_INTEL_TDX_HOST */ 87 90 88 91 #endif /* !__ASSEMBLY__ */
+2
arch/x86/kernel/cpu/common.c
··· 66 66 #include <asm/set_memory.h> 67 67 #include <asm/traps.h> 68 68 #include <asm/sev.h> 69 + #include <asm/tdx.h> 69 70 70 71 #include "cpu.h" 71 72 ··· 1988 1987 setup_cr_pinning(); 1989 1988 1990 1989 tsx_init(); 1990 + tdx_init(); 1991 1991 lkgs_init(); 1992 1992 } 1993 1993
+1 -1
arch/x86/virt/vmx/tdx/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 - obj-y += seamcall.o 2 + obj-y += seamcall.o tdx.o
+81
arch/x86/virt/vmx/tdx/tdx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright(c) 2023 Intel Corporation. 4 + * 5 + * Intel Trusted Domain Extensions (TDX) support 6 + */ 7 + 8 + #define pr_fmt(fmt) "virt/tdx: " fmt 9 + 10 + #include <linux/types.h> 11 + #include <linux/cache.h> 12 + #include <linux/init.h> 13 + #include <linux/errno.h> 14 + #include <linux/printk.h> 15 + #include <asm/msr-index.h> 16 + #include <asm/msr.h> 17 + #include <asm/cpufeature.h> 18 + #include <asm/tdx.h> 19 + 20 + static u32 tdx_global_keyid __ro_after_init; 21 + static u32 tdx_guest_keyid_start __ro_after_init; 22 + static u32 tdx_nr_guest_keyids __ro_after_init; 23 + 24 + static __init int record_keyid_partitioning(u32 *tdx_keyid_start, 25 + u32 *nr_tdx_keyids) 26 + { 27 + u32 _nr_mktme_keyids, _tdx_keyid_start, _nr_tdx_keyids; 28 + int ret; 29 + 30 + /* 31 + * IA32_MKTME_KEYID_PARTIONING: 32 + * Bit [31:0]: Number of MKTME KeyIDs. 33 + * Bit [63:32]: Number of TDX private KeyIDs. 34 + */ 35 + ret = rdmsr_safe(MSR_IA32_MKTME_KEYID_PARTITIONING, &_nr_mktme_keyids, 36 + &_nr_tdx_keyids); 37 + if (ret || !_nr_tdx_keyids) 38 + return -EINVAL; 39 + 40 + /* TDX KeyIDs start after the last MKTME KeyID. */ 41 + _tdx_keyid_start = _nr_mktme_keyids + 1; 42 + 43 + *tdx_keyid_start = _tdx_keyid_start; 44 + *nr_tdx_keyids = _nr_tdx_keyids; 45 + 46 + return 0; 47 + } 48 + 49 + void __init tdx_init(void) 50 + { 51 + u32 tdx_keyid_start, nr_tdx_keyids; 52 + int err; 53 + 54 + err = record_keyid_partitioning(&tdx_keyid_start, &nr_tdx_keyids); 55 + if (err) 56 + return; 57 + 58 + pr_info("BIOS enabled: private KeyID range [%u, %u)\n", 59 + tdx_keyid_start, tdx_keyid_start + nr_tdx_keyids); 60 + 61 + /* 62 + * The TDX module itself requires one 'global KeyID' to protect 63 + * its metadata. If there's only one TDX KeyID, there won't be 64 + * any left for TDX guests thus there's no point to enable TDX 65 + * at all. 66 + */ 67 + if (nr_tdx_keyids < 2) { 68 + pr_err("initialization failed: too few private KeyIDs available.\n"); 69 + return; 70 + } 71 + 72 + /* 73 + * Just use the first TDX KeyID as the 'global KeyID' and 74 + * leave the rest for TDX guests. 75 + */ 76 + tdx_global_keyid = tdx_keyid_start; 77 + tdx_guest_keyid_start = tdx_keyid_start + 1; 78 + tdx_nr_guest_keyids = nr_tdx_keyids - 1; 79 + 80 + setup_force_cpu_cap(X86_FEATURE_TDX_HOST_PLATFORM); 81 + }