From: Kai Huang <kai.huang@intel.com>
To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org
Cc: linux-mm@kvack.org, seanjc@google.com, pbonzini@redhat.com,
dave.hansen@intel.com, dan.j.williams@intel.com,
rafael.j.wysocki@intel.com, kirill.shutemov@linux.intel.com,
reinette.chatre@intel.com, len.brown@intel.com,
tony.luck@intel.com, peterz@infradead.org, ak@linux.intel.com,
isaku.yamahata@intel.com, chao.gao@intel.com,
sathyanarayanan.kuppuswamy@linux.intel.com, bagasdotme@gmail.com,
sagis@google.com, imammedo@redhat.com, kai.huang@intel.com
Subject: [PATCH v6 04/21] x86/virt/tdx: Use all boot-time system memory as TDX memory
Date: Thu, 27 Oct 2022 12:16:03 +1300 [thread overview]
Message-ID: <2697e1aa3bf9463f09635de264380e1b35f596cf.1666824663.git.kai.huang@intel.com> (raw)
In-Reply-To: <cover.1666824663.git.kai.huang@intel.com>
TDX provides increased levels of memory confidentiality and integrity.
This requires special hardware support for features like memory
encryption and storage of memory integrity checksums. Not all memory
satisfies these requirements.
As a result, the TDX introduced the concept of a "Convertible Memory
Region" (CMR). During boot, the BIOS builds a list of all of the memory
regions which can provide the TDX security guarantees. The list of CMRs
is available to the kernel by querying the TDX module or its loader.
However those TDX-capable memory regions are not automatically usable to
the TDX module. The kernel needs to choose the memory regions that will
be used as TDX memory and pass those memory regions to the TDX module
when initializing it. Once passed to the TDX module, those TDX-usable
memory regions are fixed during the module's lifetime. The TDX module
doesn't support adding new TDX-usable memory after it gets initialized.
To keep things simple, this initial implementation chooses to use all
boot-time present memory managed by the page allocator as TDX memory.
This requires all boot-time present memory to be TDX convertible memory,
which is true in practice. If there's any boot-time memory which isn't
TDX convertible memory (which is allowed from TDX architecture's point
of view), it will be caught later during TDX module initialization and
the initialization will fail.
However one machine may support both TDX and non-TDX memory both at
machine boot time and runtime. For example, any memory hot-added at
runtime cannot be TDX memory. Also, for now NVDIMM and CXL memory are
not TDX memory, no matter whether they are present at machine boot time
or not.
This raises a problem that, if any non-TDX memory is hot-added to the
system-wide memory allocation pool, a non-TDX page may be allocated to a
TDX guest, which will result in failing to create the TDX guest, or
killing it at runtime.
The current implementation doesn't explicitly prevent adding any non-TDX
memory to system-wide memory pool, but depends on the machine owner to
make sure such operation won't happen. For example, the machine owner
should never plug any NVDIMM or CXL memory into the machine, or use kmem
driver to hot-add any to the core-mm.
This will be enhanced in the future. One solution is the kernel can be
enforced to always guarantee all pages in the page allocator are TDX
memory (i.e. by rejecting non-TDX memory in memory hotplug). Another
option is the kernel may support different memory capabilities on basis
of NUMA node. For example, the kernel can have both TDX-compatible NUMA
node and non-TDX-compatible memory NUMA node, and the userspace needs to
explicitly bind TDX guests to those TDX-compatible memory NUMA nodes.
Use memblock to get all memory regions as it is the source from where
the memory pages are freed to the page allocator. This saves the
metadata that is needed to be allocated and given to the TDX module when
initializing it in case like 'memmap' kernel command is used.
Also, don't use memblock directly when initializing the TDX module, but
make a snapshot of all memory regions in the memblock during kernel
boot. This is because:
1) Memblock is discarded after kernel boots if CONFIG_ARCH_KEEP_MEMBLOCK
is not enabled.
2) Memblock can be changed by memory hotplug (i.e. memory removal of
boot-time present memory) at runtime if it is kept.
3) The first 1MB may not be enumerated as CMR on some platforms, so the
first 1MB cannot be included as TDX memory. Looping over all TDX
memory blocks will be done couple of times when initializing the TDX
module. Instead of using memblock directly and manually excluding
the first 1MB for each loop, just do a snapshot to exclude the first
1MB once.
Reviewed-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Kai Huang <kai.huang@intel.com>
---
arch/x86/virt/vmx/tdx/tdx.c | 141 ++++++++++++++++++++++++++++++++++++
1 file changed, 141 insertions(+)
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 8d943bdc8335..c5d260e27cad 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -10,15 +10,27 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/printk.h>
+#include <linux/list.h>
+#include <linux/memblock.h>
#include <asm/msr-index.h>
#include <asm/msr.h>
#include <asm/apic.h>
#include <asm/tdx.h>
#include "tdx.h"
+struct tdx_memblock {
+ struct list_head list;
+ unsigned long start_pfn;
+ unsigned long end_pfn;
+ int nid;
+};
+
static u32 tdx_keyid_start __ro_after_init;
static u32 tdx_keyid_num __ro_after_init;
+/* All TDX-usable memory regions */
+static LIST_HEAD(tdx_memlist);
+
/*
* Detect TDX private KeyIDs to see whether TDX has been enabled by the
* BIOS. Both initializing the TDX module and running TDX guest require
@@ -66,6 +78,125 @@ static void __init clear_tdx(void)
tdx_keyid_start = tdx_keyid_num = 0;
}
+static void __init tdx_memory_destroy(void)
+{
+ while (!list_empty(&tdx_memlist)) {
+ struct tdx_memblock *tmb = list_first_entry(&tdx_memlist,
+ struct tdx_memblock, list);
+
+ list_del(&tmb->list);
+ kfree(tmb);
+ }
+}
+
+/* Add one TDX memory block after all existing TDX memory blocks */
+static int __init tdx_memory_add_block(unsigned long start_pfn,
+ unsigned long end_pfn,
+ int nid)
+{
+ struct tdx_memblock *tmb;
+
+ tmb = kmalloc(sizeof(*tmb), GFP_KERNEL);
+ if (!tmb)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&tmb->list);
+ tmb->start_pfn = start_pfn;
+ tmb->end_pfn = end_pfn;
+ tmb->nid = nid;
+
+ list_add_tail(&tmb->list, &tdx_memlist);
+
+ return 0;
+}
+
+/*
+ * TDX reports a list of "Convertible Memory Regions" (CMR) to indicate
+ * all memory regions that _can_ be used by TDX, but the kernel needs to
+ * choose the _actual_ regions that TDX can use and pass those regions
+ * to the TDX module when initializing it. After the TDX module gets
+ * initialized, no more TDX-usable memory can be hot-added to the TDX
+ * module.
+ *
+ * TDX convertible memory must be physically present during machine boot.
+ * To keep things simple, the current implementation simply chooses to
+ * use all boot-time present memory regions as TDX memory so that all
+ * pages allocated via the page allocator are TDX memory.
+ *
+ * Build all boot-time memory regions managed by memblock as TDX-usable
+ * memory regions by making a snapshot of memblock memory regions during
+ * kernel boot. Memblock is discarded when CONFIG_ARCH_KEEP_MEMBLOCK is
+ * not enabled after kernel boots. Also, memblock can be changed due to
+ * memory hotplug (i.e. memory removal from core-mm) even if it is kept.
+ *
+ * Those regions will be verified when CMRs become available when the TDX
+ * module gets initialized. At this stage, it's not possible to get CMRs
+ * during kernel boot as the core-kernel doesn't support VMXON.
+ *
+ * Note: this means the current implementation _requires_ all boot-time
+ * present memory regions are TDX convertible memory to enable TDX. This
+ * is true in practice. Also, this can be enhanced in the future when
+ * the core-kernel gets VMXON support.
+ *
+ * Important note:
+ *
+ * TDX doesn't work with physical memory hotplug, as all hot-added memory
+ * are not convertible memory.
+ *
+ * Also to keep things simple, the current implementation doesn't handle
+ * memory hotplug at all for TDX. To use TDX, it is the machine owner's
+ * responsibility to not do any operation that will hot-add any non-TDX
+ * memory to the page allocator. For example, the machine owner should
+ * not plug any non-CMR memory (such as NVDIMM and CXL memory) to the
+ * machine, or should not use kmem driver to plug any NVDIMM or CXL
+ * memory to the core-mm.
+ *
+ * This will be enhanced in the future.
+ *
+ * Note: tdx_init() is called before acpi_init(), which will scan the
+ * entire ACPI namespace and hot-add all ACPI memory devices if there
+ * are any. This belongs to the memory hotplug category as mentioned
+ * above.
+ */
+static int __init build_tdx_memory(void)
+{
+ unsigned long start_pfn, end_pfn;
+ int i, nid, ret;
+
+ /*
+ * Cannot use for_each_free_mem_range() here as some reserved
+ * memory (i.e. initrd image) will be freed to the page allocator
+ * at the late phase of kernel boot.
+ */
+ for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, &nid) {
+ /*
+ * The first 1MB is not reported as TDX convertible
+ * memory on some platforms. Manually exclude them as
+ * TDX memory. This is fine as the first 1MB is already
+ * reserved in reserve_real_mode() and won't end up to
+ * ZONE_DMA as free page anyway.
+ */
+ if (start_pfn < (SZ_1M >> PAGE_SHIFT))
+ start_pfn = (SZ_1M >> PAGE_SHIFT);
+ if (start_pfn >= end_pfn)
+ continue;
+
+ /*
+ * All TDX memory blocks must be in address ascending
+ * order when initializing the TDX module. Memblock
+ * already guarantees that.
+ */
+ ret = tdx_memory_add_block(start_pfn, end_pfn, nid);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ tdx_memory_destroy();
+ return ret;
+}
+
static int __init tdx_init(void)
{
if (detect_tdx())
@@ -92,6 +223,16 @@ static int __init tdx_init(void)
goto no_tdx;
}
+ /*
+ * Build all boot-time system memory managed in memblock as
+ * TDX-usable memory. As part of initializing the TDX module,
+ * those regions will be passed to the TDX module.
+ */
+ if (build_tdx_memory()) {
+ pr_err("Build TDX-usable memory regions failed. Disable TDX.\n");
+ goto no_tdx;
+ }
+
return 0;
no_tdx:
clear_tdx();
--
2.37.3
next prev parent reply other threads:[~2022-10-26 23:17 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-26 23:15 [PATCH v6 00/21] TDX host kernel support Kai Huang
2022-10-26 23:16 ` [PATCH v6 01/21] x86/tdx: Use enum to define page level of TDX supported page sizes Kai Huang
2022-10-27 7:08 ` Xiaoyao Li
2022-10-27 8:42 ` Huang, Kai
2022-10-27 13:51 ` Kirill A. Shutemov
2022-10-28 0:47 ` Huang, Kai
2022-10-27 22:28 ` Isaku Yamahata
2022-10-27 15:27 ` Dave Hansen
2022-10-28 0:10 ` Huang, Kai
2022-10-26 23:16 ` [PATCH v6 02/21] x86/virt/tdx: Detect TDX during kernel boot Kai Huang
2022-10-26 23:16 ` [PATCH v6 03/21] x86/virt/tdx: Disable TDX if X2APIC is not enabled Kai Huang
2022-10-26 23:16 ` Kai Huang [this message]
2022-10-26 23:16 ` [PATCH v6 05/21] x86/virt/tdx: Add skeleton to initialize TDX on demand Kai Huang
2022-10-26 23:16 ` [PATCH v6 06/21] x86/virt/tdx: Implement functions to make SEAMCALL Kai Huang
2022-10-26 23:16 ` [PATCH v6 07/21] x86/virt/tdx: Shut down TDX module in case of error Kai Huang
2022-10-26 23:16 ` [PATCH v6 08/21] x86/virt/tdx: Do TDX module global initialization Kai Huang
2022-10-26 23:16 ` [PATCH v6 09/21] x86/virt/tdx: Do logical-cpu scope TDX module initialization Kai Huang
2022-10-26 23:16 ` [PATCH v6 10/21] x86/virt/tdx: Get information about TDX module and TDX-capable memory Kai Huang
2022-10-26 23:16 ` [PATCH v6 11/21] x86/virt/tdx: Sanity check all TDX memory ranges are convertible memory Kai Huang
2022-10-26 23:16 ` [PATCH v6 12/21] x86/virt/tdx: Add placeholder to construct TDMRs to cover all TDX memory regions Kai Huang
2022-10-27 15:31 ` Andi Kleen
2022-10-28 2:21 ` Huang, Kai
2022-11-03 8:55 ` Huang, Kai
2022-11-03 15:05 ` Dave Hansen
2022-11-03 22:07 ` Huang, Kai
2022-10-26 23:16 ` [PATCH v6 13/21] x86/virt/tdx: Create " Kai Huang
2022-10-26 23:16 ` [PATCH v6 14/21] x86/virt/tdx: Allocate and set up PAMTs for TDMRs Kai Huang
2022-10-26 23:16 ` [PATCH v6 15/21] x86/virt/tdx: Set up reserved areas for all TDMRs Kai Huang
2022-10-26 23:16 ` [PATCH v6 16/21] x86/virt/tdx: Reserve TDX module global KeyID Kai Huang
2022-10-27 12:40 ` Andi Kleen
2022-10-28 1:24 ` Huang, Kai
2022-10-26 23:16 ` [PATCH v6 17/21] x86/virt/tdx: Configure TDX module with TDMRs and " Kai Huang
2022-10-26 23:16 ` [PATCH v6 18/21] x86/virt/tdx: Configure global KeyID on all packages Kai Huang
2022-10-27 12:35 ` Andi Kleen
2022-10-28 1:07 ` Huang, Kai
2022-10-26 23:16 ` [PATCH v6 19/21] x86/virt/tdx: Initialize all TDMRs Kai Huang
2022-10-27 12:30 ` Andi Kleen
2022-10-28 1:08 ` Huang, Kai
2022-10-26 23:16 ` [PATCH v6 20/21] x86/virt/tdx: Flush cache in kexec() when TDX is enabled Kai Huang
2022-10-26 23:16 ` [PATCH v6 21/21] Documentation/x86: Add documentation for TDX host support Kai Huang
2022-10-27 12:38 ` Andi Kleen
2022-10-28 2:31 ` Huang, Kai
2022-10-28 12:52 ` Bagas Sanjaya
2022-10-28 14:16 ` Dave Hansen
2022-10-26 23:26 ` [PATCH v6 00/21] TDX host kernel support Dave Hansen
2022-10-26 23:51 ` Huang, Kai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2697e1aa3bf9463f09635de264380e1b35f596cf.1666824663.git.kai.huang@intel.com \
--to=kai.huang@intel.com \
--cc=ak@linux.intel.com \
--cc=bagasdotme@gmail.com \
--cc=chao.gao@intel.com \
--cc=dan.j.williams@intel.com \
--cc=dave.hansen@intel.com \
--cc=imammedo@redhat.com \
--cc=isaku.yamahata@intel.com \
--cc=kirill.shutemov@linux.intel.com \
--cc=kvm@vger.kernel.org \
--cc=len.brown@intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=pbonzini@redhat.com \
--cc=peterz@infradead.org \
--cc=rafael.j.wysocki@intel.com \
--cc=reinette.chatre@intel.com \
--cc=sagis@google.com \
--cc=sathyanarayanan.kuppuswamy@linux.intel.com \
--cc=seanjc@google.com \
--cc=tony.luck@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®