lkml.org 
[lkml]   [2023]   [Oct]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v14 01/23] x86/virt/tdx: Detect TDX during kernel boot
    Date
    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.

    Add a new early_initcall(tdx_init) to detect the TDX by detecting TDX
    private KeyIDs. Also add a function to report whether TDX is enabled by
    the BIOS. Similar to AMD SME, kexec() will use it to determine whether
    cache flush is needed.

    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.

    Signed-off-by: Kai Huang <kai.huang@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>
    ---

    v13 -> v14:
    - "tdx:" -> "virt/tdx:" (internal)
    - Add Dave's tag

    ---
    arch/x86/include/asm/msr-index.h | 3 ++
    arch/x86/include/asm/tdx.h | 4 ++
    arch/x86/virt/vmx/tdx/Makefile | 2 +-
    arch/x86/virt/vmx/tdx/tdx.c | 90 ++++++++++++++++++++++++++++++++
    4 files changed, 98 insertions(+), 1 deletion(-)
    create mode 100644 arch/x86/virt/vmx/tdx/tdx.c

    diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
    index 1d111350197f..7a44cac70e9f 100644
    --- a/arch/x86/include/asm/msr-index.h
    +++ b/arch/x86/include/asm/msr-index.h
    @@ -535,6 +535,9 @@
    #define MSR_RELOAD_PMC0 0x000014c1
    #define MSR_RELOAD_FIXED_CTR0 0x00001309

    +/* KeyID partitioning between MKTME and TDX */
    +#define MSR_IA32_MKTME_KEYID_PARTITIONING 0x00000087
    +
    /*
    * AMD64 MSRs. Not complete. See the architecture manual for a more
    * complete list.
    diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
    index adcbe3f1de30..a252328734c7 100644
    --- a/arch/x86/include/asm/tdx.h
    +++ b/arch/x86/include/asm/tdx.h
    @@ -81,6 +81,10 @@ static inline long tdx_kvm_hypercall(unsigned int nr, unsigned long p1,
    u64 __seamcall(u64 fn, struct tdx_module_args *args);
    u64 __seamcall_ret(u64 fn, struct tdx_module_args *args);
    u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
    +
    +bool platform_tdx_enabled(void);
    +#else
    +static inline bool platform_tdx_enabled(void) { return false; }
    #endif /* CONFIG_INTEL_TDX_HOST */

    #endif /* !__ASSEMBLY__ */
    diff --git a/arch/x86/virt/vmx/tdx/Makefile b/arch/x86/virt/vmx/tdx/Makefile
    index 46ef8f73aebb..90da47eb85ee 100644
    --- a/arch/x86/virt/vmx/tdx/Makefile
    +++ b/arch/x86/virt/vmx/tdx/Makefile
    @@ -1,2 +1,2 @@
    # SPDX-License-Identifier: GPL-2.0-only
    -obj-y += seamcall.o
    +obj-y += seamcall.o 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..13d22ea2e2d9
    --- /dev/null
    +++ b/arch/x86/virt/vmx/tdx/tdx.c
    @@ -0,0 +1,90 @@
    +// SPDX-License-Identifier: GPL-2.0
    +/*
    + * Copyright(c) 2023 Intel Corporation.
    + *
    + * Intel Trusted Domain Extensions (TDX) support
    + */
    +
    +#define pr_fmt(fmt) "virt/tdx: " fmt
    +
    +#include <linux/types.h>
    +#include <linux/cache.h>
    +#include <linux/init.h>
    +#include <linux/errno.h>
    +#include <linux/printk.h>
    +#include <asm/msr-index.h>
    +#include <asm/msr.h>
    +#include <asm/tdx.h>
    +
    +static u32 tdx_global_keyid __ro_after_init;
    +static u32 tdx_guest_keyid_start __ro_after_init;
    +static u32 tdx_nr_guest_keyids __ro_after_init;
    +
    +static int __init record_keyid_partitioning(u32 *tdx_keyid_start,
    + u32 *nr_tdx_keyids)
    +{
    + u32 _nr_mktme_keyids, _tdx_keyid_start, _nr_tdx_keyids;
    + int ret;
    +
    + /*
    + * IA32_MKTME_KEYID_PARTIONING:
    + * Bit [31:0]: Number of MKTME KeyIDs.
    + * Bit [63:32]: Number of TDX private KeyIDs.
    + */
    + ret = rdmsr_safe(MSR_IA32_MKTME_KEYID_PARTITIONING, &_nr_mktme_keyids,
    + &_nr_tdx_keyids);
    + if (ret)
    + return -ENODEV;
    +
    + if (!_nr_tdx_keyids)
    + return -ENODEV;
    +
    + /* TDX KeyIDs start after the last MKTME KeyID. */
    + _tdx_keyid_start = _nr_mktme_keyids + 1;
    +
    + *tdx_keyid_start = _tdx_keyid_start;
    + *nr_tdx_keyids = _nr_tdx_keyids;
    +
    + return 0;
    +}
    +
    +static int __init tdx_init(void)
    +{
    + u32 tdx_keyid_start, nr_tdx_keyids;
    + int err;
    +
    + err = record_keyid_partitioning(&tdx_keyid_start, &nr_tdx_keyids);
    + if (err)
    + return err;
    +
    + pr_info("BIOS enabled: private KeyID range [%u, %u)\n",
    + tdx_keyid_start, tdx_keyid_start + nr_tdx_keyids);
    +
    + /*
    + * The TDX module itself requires one 'global KeyID' to protect
    + * its metadata. If there's only one TDX KeyID, there won't be
    + * any left for TDX guests thus there's no point to enable TDX
    + * at all.
    + */
    + if (nr_tdx_keyids < 2) {
    + pr_err("initialization failed: too few private KeyIDs available.\n");
    + return -ENODEV;
    + }
    +
    + /*
    + * Just use the first TDX KeyID as the 'global KeyID' and
    + * leave the rest for TDX guests.
    + */
    + tdx_global_keyid = tdx_keyid_start;
    + tdx_guest_keyid_start = tdx_keyid_start + 1;
    + tdx_nr_guest_keyids = nr_tdx_keyids - 1;
    +
    + return 0;
    +}
    +early_initcall(tdx_init);
    +
    +/* Return whether the BIOS has enabled TDX */
    +bool platform_tdx_enabled(void)
    +{
    + return !!tdx_global_keyid;
    +}
    --
    2.41.0
    \
     
     \ /
      Last update: 2023-10-17 12:15    [W:4.754 / U:0.096 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site