@@ -22,6 +22,7 @@
#include <linux/minmax.h>
#include <linux/sizes.h>
#include <linux/pfn.h>
+#include <linux/align.h>
#include <asm/msr-index.h>
#include <asm/msr.h>
#include <asm/tdx.h>
@@ -301,9 +302,84 @@ static int get_tdx_tdmr_sysinfo(struct tdx_tdmr_sysinfo *tdmr_sysinfo)
&tdmr_sysinfo->pamt_entry_size[TDX_PS_1G]);
}
+/* Calculate the actual TDMR size */
+static int tdmr_size_single(u16 max_reserved_per_tdmr)
+{
+ int tdmr_sz;
+
+ /*
+ * The actual size of TDMR depends on the maximum
+ * number of reserved areas.
+ */
+ tdmr_sz = sizeof(struct tdmr_info);
+ tdmr_sz += sizeof(struct tdmr_reserved_area) * max_reserved_per_tdmr;
+
+ return ALIGN(tdmr_sz, TDMR_INFO_ALIGNMENT);
+}
+
+static int alloc_tdmr_list(struct tdmr_info_list *tdmr_list,
+ struct tdx_tdmr_sysinfo *tdmr_sysinfo)
+{
+ size_t tdmr_sz, tdmr_array_sz;
+ void *tdmr_array;
+
+ tdmr_sz = tdmr_size_single(tdmr_sysinfo->max_reserved_per_tdmr);
+ tdmr_array_sz = tdmr_sz * tdmr_sysinfo->max_tdmrs;
+
+ /*
+ * To keep things simple, allocate all TDMRs together.
+ * The buffer needs to be physically contiguous to make
+ * sure each TDMR is physically contiguous.
+ */
+ tdmr_array = alloc_pages_exact(tdmr_array_sz,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!tdmr_array)
+ return -ENOMEM;
+
+ tdmr_list->tdmrs = tdmr_array;
+
+ /*
+ * Keep the size of TDMR to find the target TDMR
+ * at a given index in the TDMR list.
+ */
+ tdmr_list->tdmr_sz = tdmr_sz;
+ tdmr_list->max_tdmrs = tdmr_sysinfo->max_tdmrs;
+ tdmr_list->nr_consumed_tdmrs = 0;
+
+ return 0;
+}
+
+static void free_tdmr_list(struct tdmr_info_list *tdmr_list)
+{
+ free_pages_exact(tdmr_list->tdmrs,
+ tdmr_list->max_tdmrs * tdmr_list->tdmr_sz);
+}
+
+/*
+ * Construct a list of TDMRs on the preallocated space in @tdmr_list
+ * to cover all TDX memory regions in @tmb_list based on the TDX module
+ * TDMR global information in @tdmr_sysinfo.
+ */
+static int construct_tdmrs(struct list_head *tmb_list,
+ struct tdmr_info_list *tdmr_list,
+ struct tdx_tdmr_sysinfo *tdmr_sysinfo)
+{
+ /*
+ * TODO:
+ *
+ * - Fill out TDMRs to cover all TDX memory regions.
+ * - Allocate and set up PAMTs for each TDMR.
+ * - Designate reserved areas for each TDMR.
+ *
+ * Return -EINVAL until constructing TDMRs is done
+ */
+ return -EINVAL;
+}
+
static int init_tdx_module(void)
{
struct tdx_tdmr_sysinfo tdmr_sysinfo;
+ struct tdmr_info_list tdmr_list;
int ret;
/*
@@ -326,11 +402,19 @@ static int init_tdx_module(void)
if (ret)
goto out_free_tdxmem;
+ /* Allocate enough space for constructing TDMRs */
+ ret = alloc_tdmr_list(&tdmr_list, &tdmr_sysinfo);
+ if (ret)
+ goto out_free_tdxmem;
+
+ /* Cover all TDX-usable memory regions in TDMRs */
+ ret = construct_tdmrs(&tdx_memlist, &tdmr_list, &tdmr_sysinfo);
+ if (ret)
+ goto out_free_tdmrs;
+
/*
* TODO:
*
- * - Construct a list of TDMRs to cover all TDX-usable memory
- * regions.
* - Configure the TDMRs and the global KeyID to the TDX module.
* - Configure the global KeyID on all packages.
* - Initialize all TDMRs.
@@ -338,6 +422,12 @@ static int init_tdx_module(void)
* Return error before all steps are done.
*/
ret = -EINVAL;
+out_free_tdmrs:
+ /*
+ * Always free the buffer of TDMRs as they are only used during
+ * module initialization.
+ */
+ free_tdmr_list(&tdmr_list);
out_free_tdxmem:
if (ret)
free_tdx_memlist(&tdx_memlist);
@@ -47,6 +47,30 @@
#define MD_FIELD_ID_ELE_SIZE_16BIT 1
+struct tdmr_reserved_area {
+ u64 offset;
+ u64 size;
+} __packed;
+
+#define TDMR_INFO_ALIGNMENT 512
+
+struct tdmr_info {
+ u64 base;
+ u64 size;
+ u64 pamt_1g_base;
+ u64 pamt_1g_size;
+ u64 pamt_2m_base;
+ u64 pamt_2m_size;
+ u64 pamt_4k_base;
+ u64 pamt_4k_size;
+ /*
+ * The actual number of reserved areas depends on the value of
+ * field MD_FIELD_ID_MAX_RESERVED_PER_TDMR in the TDX module
+ * global metadata.
+ */
+ DECLARE_FLEX_ARRAY(struct tdmr_reserved_area, reserved_areas);
+} __packed __aligned(TDMR_INFO_ALIGNMENT);
+
/*
* Do not put any hardware-defined TDX structure representations below
* this comment!
@@ -72,4 +96,13 @@ struct tdx_tdmr_sysinfo {
u16 pamt_entry_size[TDX_PS_NR];
};
+struct tdmr_info_list {
+ void *tdmrs; /* Flexible array to hold 'tdmr_info's */
+ int nr_consumed_tdmrs; /* How many 'tdmr_info's are in use */
+
+ /* Metadata for finding target 'tdmr_info' and freeing @tdmrs */
+ int tdmr_sz; /* Size of one 'tdmr_info' */
+ int max_tdmrs; /* How many 'tdmr_info's are allocated */
+};
+
#endif