#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/cpufeature.h>
 
 static DEFINE_PER_CPU(bool, tdx_lp_initialized);
 
+static struct tdmr_info_list tdx_tdmr_list;
+
 static enum tdx_module_status_t tdx_module_status;
 static DEFINE_MUTEX(tdx_module_lock);
 
        return 0;
 }
 
+/* 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;
        if (ret)
                goto err_free_tdxmem;
 
+       /* Allocate enough space for constructing TDMRs */
+       ret = alloc_tdmr_list(&tdx_tdmr_list, &tdmr_sysinfo);
+       if (ret)
+               goto err_free_tdxmem;
+
+       /* Cover all TDX-usable memory regions in TDMRs */
+       ret = construct_tdmrs(&tdx_memlist, &tdx_tdmr_list, &tdmr_sysinfo);
+       if (ret)
+               goto err_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.
         */
        ret = -EINVAL;
        if (ret)
-               goto err_free_tdxmem;
+               goto err_free_tdmrs;
 out_put_tdxmem:
        /*
         * @tdx_memlist is written here and read at memory hotplug time.
        put_online_mems();
        return ret;
 
+err_free_tdmrs:
+       free_tdmr_list(&tdx_tdmr_list);
 err_free_tdxmem:
        free_tdx_memlist(&tdx_memlist);
        goto out_put_tdxmem;
 
 
 #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!
        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