[PATCH v3 10/17] x86/efi: Do not rely on EFI_MEMORY_RUNTIME bit and avoid entry splitting

Ard Biesheuvel
2026-04-23T15:21:12Z

Now that efi_mem_reserve() has been updated to rely on RSRV_KERN
memblock reservations, it is no longer needed to mark memblock reserved
boot services regions as EFI_MEMORY_RUNTIME. This means that it is no
longer needed to split existing entries in the EFI memory map, removing
the need to re-allocate/copy/remap the entire EFI memory map on every
call to efi_mem_reserve().

So drop this functionality - it is no longer needed.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/x86/include/asm/efi.h | 4 -
arch/x86/platform/efi/memmap.c | 138 --------------------
arch/x86/platform/efi/quirks.c | 77 +++--------
3 files changed, 17 insertions(+), 202 deletions(-)

diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index f291845b403a..7d8f627805df 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -402,10 +402,6 @@ extern int __init efi_memmap_alloc(unsigned int num_entries,
struct efi_memory_map_data *data);

extern int __init efi_memmap_install(struct efi_memory_map_data *data);
-extern int __init efi_memmap_split_count(efi_memory_desc_t *md,
- struct range *range);
-extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap,
- void *buf, struct efi_mem_range *mem);

enum efi_secureboot_mode __x86_efi_boot_mode(void);

diff --git a/arch/x86/platform/efi/memmap.c b/arch/x86/platform/efi/memmap.c
index 697a9a26a005..da7483a33d68 100644
--- a/arch/x86/platform/efi/memmap.c
+++ b/arch/x86/platform/efi/memmap.c
@@ -107,141 +107,3 @@ int __init efi_memmap_install(struct efi_memory_map_data *data)
__efi_memmap_free(phys, size, flags);
return 0;
}
-
-/**
- * efi_memmap_split_count - Count number of additional EFI memmap entries
- * @md: EFI memory descriptor to split
- * @range: Address range (start, end) to split around
- *
- * Returns the number of additional EFI memmap entries required to
- * accommodate @range.
- */
-int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range)
-{
- u64 m_start, m_end;
- u64 start, end;
- int count = 0;
-
- start = md->phys_addr;
- end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
-
- /* modifying range */
- m_start = range->start;
- m_end = range->end;
-
- if (m_start <= start) {
- /* split into 2 parts */
- if (start < m_end && m_end < end)
- count++;
- }
-
- if (start < m_start && m_start < end) {
- /* split into 3 parts */
- if (m_end < end)
- count += 2;
- /* split into 2 parts */
- if (end <= m_end)
- count++;
- }
-
- return count;
-}
-
-/**
- * efi_memmap_insert - Insert a memory region in an EFI memmap
- * @old_memmap: The existing EFI memory map structure
- * @buf: Address of buffer to store new map
- * @mem: Memory map entry to insert
- *
- * It is suggested that you call efi_memmap_split_count() first
- * to see how large @buf needs to be.
- */
-void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf,
- struct efi_mem_range *mem)
-{
- u64 m_start, m_end, m_attr;
- efi_memory_desc_t *md;
- u64 start, end;
- void *old, *new;
-
- /* modifying range */
- m_start = mem->range.start;
- m_end = mem->range.end;
- m_attr = mem->attribute;
-
- /*
- * The EFI memory map deals with regions in EFI_PAGE_SIZE
- * units. Ensure that the region described by 'mem' is aligned
- * correctly.
- */
- if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) ||
- !IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) {
- WARN_ON(1);
- return;
- }
-
- for (old = old_memmap->map, new = buf;
- old < old_memmap->map_end;
- old += old_memmap->desc_size, new += old_memmap->desc_size) {
-
- /* copy original EFI memory descriptor */
- memcpy(new, old, old_memmap->desc_size);
- md = new;
- start = md->phys_addr;
- end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
-
- if (m_start <= start && end <= m_end)
- md->attribute |= m_attr;
-
- if (m_start <= start &&
- (start < m_end && m_end < end)) {
- /* first part */
- md->attribute |= m_attr;
- md->num_pages = (m_end - md->phys_addr + 1) >>
- EFI_PAGE_SHIFT;
- /* latter part */
- new += old_memmap->desc_size;
- memcpy(new, old, old_memmap->desc_size);
- md = new;
- md->phys_addr = m_end + 1;
- md->num_pages = (end - md->phys_addr + 1) >>
- EFI_PAGE_SHIFT;
- }
-
- if ((start < m_start && m_start < end) && m_end < end) {
- /* first part */
- md->num_pages = (m_start - md->phys_addr) >>
- EFI_PAGE_SHIFT;
- /* middle part */
- new += old_memmap->desc_size;
- memcpy(new, old, old_memmap->desc_size);
- md = new;
- md->attribute |= m_attr;
- md->phys_addr = m_start;
- md->num_pages = (m_end - m_start + 1) >>
- EFI_PAGE_SHIFT;
- /* last part */
- new += old_memmap->desc_size;
- memcpy(new, old, old_memmap->desc_size);
- md = new;
- md->phys_addr = m_end + 1;
- md->num_pages = (end - m_end) >>
- EFI_PAGE_SHIFT;
- }
-
- if ((start < m_start && m_start < end) &&
- (end <= m_end)) {
- /* first part */
- md->num_pages = (m_start - md->phys_addr) >>
- EFI_PAGE_SHIFT;
- /* latter part */
- new += old_memmap->desc_size;
- memcpy(new, old, old_memmap->desc_size);
- md = new;
- md->phys_addr = m_start;
- md->num_pages = (end - md->phys_addr + 1) >>
- EFI_PAGE_SHIFT;
- md->attribute |= m_attr;
- }
- }
-}
diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c
index ce06a388fc11..efb828b7e2ab 100644
--- a/arch/x86/platform/efi/quirks.c
+++ b/arch/x86/platform/efi/quirks.c
@@ -239,63 +239,9 @@ EXPORT_SYMBOL_GPL(efi_query_variable_store);
* buggy implementations we reserve boot services region during EFI
* init and make sure it stays executable. Then, after
* SetVirtualAddressMap(), it is discarded.
- *
- * However, some boot services regions contain data that is required
- * by drivers, so we need to track which memory ranges can never be
- * freed. This is done by tagging those regions with the
- * EFI_MEMORY_RUNTIME attribute.
- *
- * Any driver that wants to mark a region as reserved must use
- * efi_mem_reserve() which will insert a new EFI memory descriptor
- * into efi.memmap (splitting existing regions if necessary) and tag
- * it with EFI_MEMORY_RUNTIME.
*/
void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size)
{
- struct efi_memory_map_data data = { 0 };
- struct efi_mem_range mr;
- efi_memory_desc_t md;
- int num_entries;
- void *new;
-
- if (efi_mem_desc_lookup(addr, &md) ||
- md.type != EFI_BOOT_SERVICES_DATA) {
- pr_err("Failed to lookup EFI memory descriptor for %pa\n", &addr);
- return;
- }
-
- if (addr + size > md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT)) {
- pr_err("Region spans EFI memory descriptors, %pa\n", &addr);
- return;
- }
-
- size += addr % EFI_PAGE_SIZE;
- size = round_up(size, EFI_PAGE_SIZE);
- addr = round_down(addr, EFI_PAGE_SIZE);
-
- mr.range.start = addr;
- mr.range.end = addr + size - 1;
- mr.attribute = md.attribute | EFI_MEMORY_RUNTIME;
-
- num_entries = efi_memmap_split_count(&md, &mr.range);
- num_entries += efi.memmap.nr_map;
-
- if (efi_memmap_alloc(num_entries, &data) != 0) {
- pr_err("Could not allocate boot services memmap\n");
- return;
- }
-
- new = early_memremap_prot(data.phys_map, data.size,
- pgprot_val(pgprot_encrypted(FIXMAP_PAGE_NORMAL)));
- if (!new) {
- pr_err("Failed to map new boot services memmap\n");
- return;
- }
-
- efi_memmap_insert(&efi.memmap, new, &mr);
- early_memunmap(new, data.size);
-
- efi_memmap_install(&data);
e820__range_update(addr, size, E820_TYPE_RAM, E820_TYPE_RESERVED);
e820__update_table(e820_table);
}
@@ -404,7 +350,8 @@ struct efi_freeable_range {
static struct efi_freeable_range *ranges_to_free __initdata;
static int num_to_free __initdata;

-static int __init efi_add_range_to_free(u64 range_start, u64 range_end)
+static int __init efi_add_range_to_free(u64 range_start, u64 range_end,
+ bool *has_reservations)
{
struct memblock_region *region;
static int idx __initdata;
@@ -420,15 +367,18 @@ static int __init efi_add_range_to_free(u64 range_start, u64 range_end)
if (region_end < range_start)
continue;

- if (region->flags & MEMBLOCK_RSRV_KERN)
- continue;
-
start = PAGE_ALIGN(max(range_start, region->base));
end = PAGE_ALIGN_DOWN(min(range_end, region_end));

if (start >= end)
continue;

+ if ((region->flags & MEMBLOCK_RSRV_KERN) ||
+ !can_free_region(start, end - start)) {
+ *has_reservations = true;
+ continue;
+ }
+
ranges_to_free[idx].start = start;
ranges_to_free[idx].end = end;

@@ -471,6 +421,7 @@ void __init efi_unmap_boot_services(void)
for_each_efi_memory_desc(md) {
unsigned long long start = md->phys_addr;
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
+ bool has_reservations = false;

if (md->type != EFI_BOOT_SERVICES_CODE &&
md->type != EFI_BOOT_SERVICES_DATA) {
@@ -497,10 +448,13 @@ void __init efi_unmap_boot_services(void)
* memory here.
* Queue the ranges to free at a later point.
*/
- if (efi_add_range_to_free(start, start + size)) {
+ if (efi_add_range_to_free(start, start + size, &has_reservations)) {
pr_err("Failed to reallocate storage for freeable EFI regions\n");
return;
}
+
+ if (has_reservations)
+ num_entries++;
}

if (!num_entries)
@@ -526,8 +480,11 @@ void __init efi_unmap_boot_services(void)
for_each_efi_memory_desc(md) {
if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
(md->type == EFI_BOOT_SERVICES_CODE ||
- md->type == EFI_BOOT_SERVICES_DATA))
+ md->type == EFI_BOOT_SERVICES_DATA) &&
+ can_free_region(md->phys_addr,
+ md->num_pages << EFI_PAGE_SHIFT)) {
continue;
+ }

memcpy(new_md, md, efi.memmap.desc_size);
new_md += efi.memmap.desc_size;