diff --git a/lib/asm-generic/page.h b/lib/asm-generic/page.h
index 5ed086129657..56f539ac45eb 100644
--- a/lib/asm-generic/page.h
+++ b/lib/asm-generic/page.h
@@ -12,8 +12,11 @@
 #include <linux/const.h>
 
 #define PAGE_SHIFT		12
-#define PAGE_SIZE		(_AC(1,UL) << PAGE_SHIFT)
-#define PAGE_MASK		(~(PAGE_SIZE-1))
+#define PAGE_SIZE		(_AC(1, UL) << PAGE_SHIFT)
+#define PAGE_MASK		(~(PAGE_SIZE - 1))
+#define PMD_SHIFT		21
+#define PMD_SIZE		(_AC(1, UL) << PMD_SHIFT)
+#define PMD_MASK		(~(PMD_SIZE - 1))
 
 #ifndef __ASSEMBLY__
 
diff --git a/lib/linux/efi.h b/lib/linux/efi.h
index 455625aa155d..df9fa7974d87 100644
--- a/lib/linux/efi.h
+++ b/lib/linux/efi.h
@@ -96,7 +96,8 @@ typedef	struct {
 #define EFI_MEMORY_MAPPED_IO_PORT_SPACE	12
 #define EFI_PAL_CODE			13
 #define EFI_PERSISTENT_MEMORY		14
-#define EFI_MAX_MEMORY_TYPE		15
+#define EFI_UNACCEPTED_MEMORY		15
+#define EFI_MAX_MEMORY_TYPE		16
 
 /* Attribute values: */
 #define EFI_MEMORY_UC		((u64)0x0000000000000001ULL)	/* uncached */
@@ -416,6 +417,26 @@ struct efi_boot_memmap {
 	unsigned long           *buff_size;
 };
 
+/*
+ * efi_memdesc_ptr - get the n-th EFI memmap descriptor
+ * @map: the start of efi memmap
+ * @desc_size: the size of space for each EFI memmap descriptor
+ * @n: the index of efi memmap descriptor
+ *
+ * EFI boot service provides the GetMemoryMap() function to get a copy of the
+ * current memory map which is an array of memory descriptors, each of
+ * which describes a contiguous block of memory. It also gets the size of the
+ * map, and the size of each descriptor, etc.
+ *
+ * Note that per section 6.2 of UEFI Spec 2.6 Errata A, the returned size of
+ * each descriptor might not be equal to sizeof(efi_memory_memdesc_t),
+ * since efi_memory_memdesc_t may be extended in the future. Thus the OS
+ * MUST use the returned size of the descriptor to find the start of each
+ * efi_memory_memdesc_t in the memory map array.
+ */
+#define efi_memdesc_ptr(map, desc_size, n)			\
+	((efi_memory_desc_t *)((void *)(map) + ((n) * (desc_size))))
+
 #define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__)
 #define efi_rs_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__)
 
diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index e834fdfd290c..29202478ae0d 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -288,7 +288,7 @@ efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
 	 * TDVF support partial memory accept, accept remaining memory
 	 * early so memory allocator can use it.
 	 */
-	status = setup_tdx();
+	status = setup_tdx(efi_bootinfo);
 	if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) {
 		printf("INTEL TDX setup failed, error = 0x%lx\n", status);
 		return status;
diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c
index 2b2e3164be33..b74c697353d9 100644
--- a/lib/x86/tdx.c
+++ b/lib/x86/tdx.c
@@ -10,9 +10,11 @@
  */
 
 #include "tdx.h"
+#include "errno.h"
 #include "bitops.h"
 #include "x86/processor.h"
 #include "x86/smp.h"
+#include "asm/page.h"
 
 #define VE_IS_IO_OUT(exit_qual)		(((exit_qual) & 8) ? 0 : 1)
 #define VE_GET_IO_SIZE(exit_qual)	(((exit_qual) & 7) + 1)
@@ -124,6 +126,42 @@ done:
 	return !!tdx_guest;
 }
 
+static struct {
+	unsigned int gpa_width;
+	unsigned long attributes;
+} td_info;
+
+/* The highest bit of a guest physical address is the "sharing" bit */
+phys_addr_t tdx_shared_mask(void)
+{
+	return 1ULL << (td_info.gpa_width - 1);
+}
+
+static void tdx_get_info(void)
+{
+	struct tdx_module_output out;
+	u64 ret;
+
+	/*
+	 * TDINFO TDX Module call is used to get the TD
+	 * execution environment information like GPA
+	 * width, number of available vcpus, debug mode
+	 * information, etc. More details about the ABI
+	 * can be found in TDX Guest-Host-Communication
+	 * Interface (GHCI), sec 2.4.2 TDCALL [TDG.VP.INFO].
+	 */
+	ret = __tdx_module_call(TDX_GET_INFO, 0, 0, 0, 0, &out);
+
+	/*
+	 * Non zero return means buggy TDX module (which is
+	 * fatal). So raise a BUG().
+	 */
+	BUG_ON(ret);
+
+	td_info.gpa_width = out.rcx & GENMASK(5, 0);
+	td_info.attributes = out.rdx;
+}
+
 /*
  * Wrapper for standard use of __tdx_hypercall with BUG_ON() check
  * for TDCALL error.
@@ -393,11 +431,110 @@ static void tdx_handle_ve(struct ex_regs *regs)
 	tdx_handle_virtualization_exception(regs, &ve);
 }
 
-efi_status_t setup_tdx(void)
+static u64 tdx_accept_page(phys_addr_t gpa, bool page_2mb)
+{
+	/*
+	 * Pass the page physical address and size (4KB|2MB) to the
+	 * TDX module to accept the pending, private page. More info
+	 * about ABI can be found in TDX Guest-Host-Communication
+	 * Interface (GHCI), sec 2.4.7.
+	 */
+
+	if (page_2mb)
+		gpa |= 1;
+
+	return __tdx_module_call(TDX_ACCEPT_PAGE, gpa, 0, 0, 0, NULL);
+}
+
+/*
+ * Inform the VMM of the guest's intent for this physical page:
+ * shared with the VMM or private to the guest.  The VMM is
+ * expected to change its mapping of the page in response.
+ */
+int tdx_hcall_gpa_intent(phys_addr_t start, phys_addr_t end,
+			 enum tdx_map_type map_type)
+{
+	u64 ret = 0;
+
+	if (map_type == TDX_MAP_SHARED) {
+		start |= tdx_shared_mask();
+		end |= tdx_shared_mask();
+	}
+
+	/*
+	 * Notify VMM about page mapping conversion. More info
+	 * about ABI can be found in TDX Guest-Host-Communication
+	 * Interface (GHCI), sec 3.2.
+	 */
+	ret = _tdx_hypercall(TDVMCALL_MAP_GPA, start, end - start, 0, 0,
+			     NULL);
+	if (ret)
+		ret = -EIO;
+
+	if (ret || map_type == TDX_MAP_SHARED)
+		return ret;
+	/*
+	 * For shared->private conversion, accept the page using
+	 * TDX_ACCEPT_PAGE TDX module call.
+	 */
+	while (start < end) {
+		/* Try 2M page accept first if possible */
+		if (!(start & ~PMD_MASK) && end - start >= PMD_SIZE &&
+				!tdx_accept_page(start, true)) {
+			start += PMD_SIZE;
+			continue;
+		}
+
+		if (tdx_accept_page(start, false))
+			return -EIO;
+		start += PAGE_SIZE;
+	}
+
+	return 0;
+}
+
+bool tdx_accept_memory(phys_addr_t start, phys_addr_t end)
+{
+	if (tdx_hcall_gpa_intent(start, end, TDX_MAP_PRIVATE)) {
+		tdx_printf("Accepting memory failed\n");
+		return false;
+	}
+	return true;
+}
+
+static bool tdx_accept_memory_regions(struct efi_boot_memmap *mem_map)
+{
+	unsigned long i, nr_desc = *mem_map->map_size / *mem_map->desc_size;
+	efi_memory_desc_t *d;
+
+	for (i = 0; i < nr_desc; i++) {
+		d = efi_memdesc_ptr(*mem_map->map, *mem_map->desc_size, i);
+
+		if (d->type == EFI_UNACCEPTED_MEMORY) {
+			if (d->phys_addr & ~PAGE_MASK) {
+				tdx_printf("WARN: EFI: unaligned base %lx\n",
+					   d->phys_addr);
+				d->phys_addr &= PAGE_MASK;
+			}
+			if (!tdx_accept_memory(d->phys_addr, d->phys_addr +
+					       PAGE_SIZE * d->num_pages))
+				return false;
+
+			d->type = EFI_CONVENTIONAL_MEMORY;
+		}
+	}
+	return true;
+}
+
+efi_status_t setup_tdx(efi_bootinfo_t *efi_bootinfo)
 {
 	if (!is_tdx_guest())
 		return EFI_UNSUPPORTED;
 
+	tdx_get_info();
+	if (!tdx_accept_memory_regions(&efi_bootinfo->mem_map))
+		return EFI_OUT_OF_RESOURCES;
+
 	handle_exception(20, tdx_handle_ve);
 
 	printf("Initialized TDX.\n");
diff --git a/lib/x86/tdx.h b/lib/x86/tdx.h
index 68ddc136d1d9..2f938038dc45 100644
--- a/lib/x86/tdx.h
+++ b/lib/x86/tdx.h
@@ -30,7 +30,12 @@
 #define EXIT_REASON_MSR_WRITE           32
 
 /* TDX Module call Leaf IDs */
+#define TDX_GET_INFO			1
 #define TDX_GET_VEINFO			3
+#define TDX_ACCEPT_PAGE			6
+
+/* TDX hypercall Leaf IDs */
+#define TDVMCALL_MAP_GPA		0x10001
 
 /*
  * Used in __tdx_module_call() helper function to gather the
@@ -76,8 +81,21 @@ struct ve_info {
 	u32 instr_info;
 };
 
+/*
+ * Page mapping type enum. This is software construct not
+ * part of any hardware or VMM ABI.
+ */
+enum tdx_map_type {
+	TDX_MAP_PRIVATE,
+	TDX_MAP_SHARED,
+};
+
 bool is_tdx_guest(void);
-efi_status_t setup_tdx(void);
+phys_addr_t tdx_shared_mask(void);
+int tdx_hcall_gpa_intent(phys_addr_t start, phys_addr_t end,
+			 enum tdx_map_type map_type);
+bool tdx_accept_memory(phys_addr_t start, phys_addr_t end);
+efi_status_t setup_tdx(efi_bootinfo_t *efi_bootinfo);
 
 /* Helper function used to communicate with the TDX module */
 u64 __tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
