--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/efi.h>
+#include <asm/efi.h>
+#include "efistub.h"
+
+struct efi_unaccepted_memory *unaccepted_table;
+
+efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc,
+                                       struct efi_boot_memmap *map)
+{
+       efi_guid_t unaccepted_table_guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID;
+       u64 unaccepted_start = ULLONG_MAX, unaccepted_end = 0, bitmap_size;
+       efi_status_t status;
+       int i;
+
+       /* Check if the table is already installed */
+       unaccepted_table = get_efi_config_table(unaccepted_table_guid);
+       if (unaccepted_table) {
+               if (unaccepted_table->version != 1) {
+                       efi_err("Unknown version of unaccepted memory table\n");
+                       return EFI_UNSUPPORTED;
+               }
+               return EFI_SUCCESS;
+       }
+
+       /* Check if there's any unaccepted memory and find the max address */
+       for (i = 0; i < nr_desc; i++) {
+               efi_memory_desc_t *d;
+               unsigned long m = (unsigned long)map->map;
+
+               d = efi_early_memdesc_ptr(m, map->desc_size, i);
+               if (d->type != EFI_UNACCEPTED_MEMORY)
+                       continue;
+
+               unaccepted_start = min(unaccepted_start, d->phys_addr);
+               unaccepted_end = max(unaccepted_end,
+                                    d->phys_addr + d->num_pages * PAGE_SIZE);
+       }
+
+       if (unaccepted_start == ULLONG_MAX)
+               return EFI_SUCCESS;
+
+       unaccepted_start = round_down(unaccepted_start,
+                                     EFI_UNACCEPTED_UNIT_SIZE);
+       unaccepted_end = round_up(unaccepted_end, EFI_UNACCEPTED_UNIT_SIZE);
+
+       /*
+        * If unaccepted memory is present, allocate a bitmap to track what
+        * memory has to be accepted before access.
+        *
+        * One bit in the bitmap represents 2MiB in the address space:
+        * A 4k bitmap can track 64GiB of physical address space.
+        *
+        * In the worst case scenario -- a huge hole in the middle of the
+        * address space -- It needs 256MiB to handle 4PiB of the address
+        * space.
+        *
+        * The bitmap will be populated in setup_e820() according to the memory
+        * map after efi_exit_boot_services().
+        */
+       bitmap_size = DIV_ROUND_UP(unaccepted_end - unaccepted_start,
+                                  EFI_UNACCEPTED_UNIT_SIZE * BITS_PER_BYTE);
+
+       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
+                            sizeof(*unaccepted_table) + bitmap_size,
+                            (void **)&unaccepted_table);
+       if (status != EFI_SUCCESS) {
+               efi_err("Failed to allocate unaccepted memory config table\n");
+               return status;
+       }
+
+       unaccepted_table->version = 1;
+       unaccepted_table->unit_size = EFI_UNACCEPTED_UNIT_SIZE;
+       unaccepted_table->phys_base = unaccepted_start;
+       unaccepted_table->size = bitmap_size;
+       memset(unaccepted_table->bitmap, 0, bitmap_size);
+
+       status = efi_bs_call(install_configuration_table,
+                            &unaccepted_table_guid, unaccepted_table);
+       if (status != EFI_SUCCESS) {
+               efi_bs_call(free_pool, unaccepted_table);
+               efi_err("Failed to install unaccepted memory config table!\n");
+       }
+
+       return status;
+}
+
+/*
+ * The accepted memory bitmap only works at unit_size granularity.  Take
+ * unaligned start/end addresses and either:
+ *  1. Accepts the memory immediately and in its entirety
+ *  2. Accepts unaligned parts, and marks *some* aligned part unaccepted
+ *
+ * The function will never reach the bitmap_set() with zero bits to set.
+ */
+void process_unaccepted_memory(u64 start, u64 end)
+{
+       u64 unit_size = unaccepted_table->unit_size;
+       u64 unit_mask = unaccepted_table->unit_size - 1;
+       u64 bitmap_size = unaccepted_table->size;
+
+       /*
+        * Ensure that at least one bit will be set in the bitmap by
+        * immediately accepting all regions under 2*unit_size.  This is
+        * imprecise and may immediately accept some areas that could
+        * have been represented in the bitmap.  But, results in simpler
+        * code below
+        *
+        * Consider case like this (assuming unit_size == 2MB):
+        *
+        * | 4k | 2044k |    2048k   |
+        * ^ 0x0        ^ 2MB        ^ 4MB
+        *
+        * Only the first 4k has been accepted. The 0MB->2MB region can not be
+        * represented in the bitmap. The 2MB->4MB region can be represented in
+        * the bitmap. But, the 0MB->4MB region is <2*unit_size and will be
+        * immediately accepted in its entirety.
+        */
+       if (end - start < 2 * unit_size) {
+               arch_accept_memory(start, end);
+               return;
+       }
+
+       /*
+        * No matter how the start and end are aligned, at least one unaccepted
+        * unit_size area will remain to be marked in the bitmap.
+        */
+
+       /* Immediately accept a <unit_size piece at the start: */
+       if (start & unit_mask) {
+               arch_accept_memory(start, round_up(start, unit_size));
+               start = round_up(start, unit_size);
+       }
+
+       /* Immediately accept a <unit_size piece at the end: */
+       if (end & unit_mask) {
+               arch_accept_memory(round_down(end, unit_size), end);
+               end = round_down(end, unit_size);
+       }
+
+       /*
+        * Accept part of the range that before phys_base and cannot be recorded
+        * into the bitmap.
+        */
+       if (start < unaccepted_table->phys_base) {
+               arch_accept_memory(start,
+                                  min(unaccepted_table->phys_base, end));
+               start = unaccepted_table->phys_base;
+       }
+
+       /* Nothing to record */
+       if (end < unaccepted_table->phys_base)
+               return;
+
+       /* Translate to offsets from the beginning of the bitmap */
+       start -= unaccepted_table->phys_base;
+       end -= unaccepted_table->phys_base;
+
+       /* Accept memory that doesn't fit into bitmap */
+       if (end > bitmap_size * unit_size * BITS_PER_BYTE) {
+               unsigned long phys_start, phys_end;
+
+               phys_start = bitmap_size * unit_size * BITS_PER_BYTE +
+                            unaccepted_table->phys_base;
+               phys_end = end + unaccepted_table->phys_base;
+
+               arch_accept_memory(phys_start, phys_end);
+               end = bitmap_size * unit_size * BITS_PER_BYTE;
+       }
+
+       /*
+        * 'start' and 'end' are now both unit_size-aligned.
+        * Record the range as being unaccepted:
+        */
+       bitmap_set(unaccepted_table->bitmap,
+                  start / unit_size, (end - start) / unit_size);
+}
+
+void accept_memory(phys_addr_t start, phys_addr_t end)
+{
+       unsigned long range_start, range_end;
+       unsigned long bitmap_size;
+       u64 unit_size;
+
+       if (!unaccepted_table)
+               return;
+
+       unit_size = unaccepted_table->unit_size;
+
+       /*
+        * Only care for the part of the range that is represented
+        * in the bitmap.
+        */
+       if (start < unaccepted_table->phys_base)
+               start = unaccepted_table->phys_base;
+       if (end < unaccepted_table->phys_base)
+               return;
+
+       /* Translate to offsets from the beginning of the bitmap */
+       start -= unaccepted_table->phys_base;
+       end -= unaccepted_table->phys_base;
+
+       /* Make sure not to overrun the bitmap */
+       if (end > unaccepted_table->size * unit_size * BITS_PER_BYTE)
+               end = unaccepted_table->size * unit_size * BITS_PER_BYTE;
+
+       range_start = start / unit_size;
+       bitmap_size = DIV_ROUND_UP(end, unit_size);
+
+       for_each_set_bitrange_from(range_start, range_end,
+                                  unaccepted_table->bitmap, bitmap_size) {
+               unsigned long phys_start, phys_end;
+
+               phys_start = range_start * unit_size + unaccepted_table->phys_base;
+               phys_end = range_end * unit_size + unaccepted_table->phys_base;
+
+               arch_accept_memory(phys_start, phys_end);
+               bitmap_clear(unaccepted_table->bitmap,
+                            range_start, range_end - range_start);
+       }
+}
 
 #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 */
 #define LINUX_EFI_MOK_VARIABLE_TABLE_GUID      EFI_GUID(0xc451ed2b, 0x9694, 0x45d3,  0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89)
 #define LINUX_EFI_COCO_SECRET_AREA_GUID                EFI_GUID(0xadf956ad, 0xe98c, 0x484c,  0xae, 0x11, 0xb5, 0x1c, 0x7d, 0x33, 0x64, 0x47)
 #define LINUX_EFI_BOOT_MEMMAP_GUID             EFI_GUID(0x800f683f, 0xd08b, 0x423a,  0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4)
+#define LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID    EFI_GUID(0xd5d1de3c, 0x105c, 0x44f9,  0x9e, 0xa9, 0xbc, 0xef, 0x98, 0x12, 0x00, 0x31)
 
 #define RISCV_EFI_BOOT_PROTOCOL_GUID           EFI_GUID(0xccd15fec, 0x6f73, 0x4eec,  0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf)
 
        efi_memory_desc_t       map[];
 };
 
+struct efi_unaccepted_memory {
+       u32 version;
+       u32 unit_size;
+       u64 phys_base;
+       u64 size;
+       unsigned long bitmap[];
+};
+
 /*
  * Architecture independent structure for describing a memory map for the
  * benefit of efi_memmap_init_early(), and for passing context between