[PATCH v3 13/17] x86/efi: Update the runtime map in place

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

When creating the EFI runtime map, a copy is created containing only the
entries that will be mapped on behalf of the firmware, but the
assignment of the virtual address field is applied to both copies.

Subsequently, the copy is installed as the new EFI memory map, and the
old one is just leaked.

This means that there is no reason whatsoever to allocate and install
the copy, and it is much easier to just update the existing memory map in
place to set the virtual addresses and suppress unused entries.

So reuse the filter function used by efi_clean_memmap() to drop all
entries that are irrelevant, and then apply the existing logic to assign
the virtual addresses and create the mappings in the EFI page tables.

Note that x86_64 and i386 traverse the memory map in opposite order, so
this part remains a separate pass as before. This logic will be further
simplified in subsequent patch.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/x86/platform/efi/efi.c | 82 ++++----------------
1 file changed, 16 insertions(+), 66 deletions(-)

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 459b5c13167a..f67ea6d4cad0 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -498,27 +498,6 @@ void __init efi_init(void)
efi_print_memmap();
}

-static void *realloc_pages(void *old_memmap, int old_shift)
-{
- void *ret;
-
- ret = (void *)__get_free_pages(GFP_KERNEL, old_shift + 1);
- if (!ret)
- goto out;
-
- /*
- * A first-time allocation doesn't have anything to copy.
- */
- if (!old_memmap)
- return ret;
-
- memcpy(ret, old_memmap, PAGE_SIZE << old_shift);
-
-out:
- free_pages((unsigned long)old_memmap, old_shift);
- return ret;
-}
-
/*
* Iterate the EFI memory map in reverse order because the regions
* will be mapped top-down. The end result is the same as if we had
@@ -589,7 +568,7 @@ static void *efi_map_next_entry(void *entry)
return entry;
}

-static bool should_map_region(efi_memory_desc_t *md)
+static bool should_map_region(const efi_memory_desc_t *md, int unused)
{
/*
* Runtime regions always require runtime mappings (obviously).
@@ -642,40 +621,14 @@ static bool should_map_region(efi_memory_desc_t *md)
* Map the efi memory ranges of the runtime services and update new_mmap with
* virtual addresses.
*/
-static void * __init efi_map_regions(int *count, int *pg_shift)
+static void __init efi_map_regions(void)
{
- void *p, *new_memmap = NULL;
- unsigned long left = 0;
- unsigned long desc_size;
- efi_memory_desc_t *md;
-
- desc_size = efi.memmap.desc_size;
-
- p = NULL;
- while ((p = efi_map_next_entry(p))) {
- md = p;
+ efi_memory_desc_t *md = NULL;

- if (!should_map_region(md))
- continue;
+ efi_memmap_filter_entries(should_map_region);

+ while ((md = efi_map_next_entry(md)))
efi_map_region(md);
-
- if (left < desc_size) {
- new_memmap = realloc_pages(new_memmap, *pg_shift);
- if (!new_memmap)
- return NULL;
-
- left += PAGE_SIZE << *pg_shift;
- (*pg_shift)++;
- }
-
- memcpy(new_memmap + (*count * desc_size), md, desc_size);
-
- left -= desc_size;
- (*count)++;
- }
-
- return new_memmap;
}

static void __init kexec_enter_virtual_mode(void)
@@ -752,9 +705,8 @@ static void __init kexec_enter_virtual_mode(void)
*/
static void __init __efi_enter_virtual_mode(void)
{
- int count = 0, pg_shift = 0;
- void *new_memmap = NULL;
efi_status_t status;
+ unsigned long size;
unsigned long pa;

if (efi_alloc_page_tables()) {
@@ -762,15 +714,6 @@ static void __init __efi_enter_virtual_mode(void)
goto err;
}

- efi_merge_regions();
- new_memmap = efi_map_regions(&count, &pg_shift);
- if (!new_memmap) {
- pr_err("Error reallocating memory, EFI runtime non-functional!\n");
- goto err;
- }
-
- pa = __pa(new_memmap);
-
/*
* Unregister the early EFI memmap from efi_init() and install
* the new EFI memory map that we are about to pass to the
@@ -778,22 +721,29 @@ static void __init __efi_enter_virtual_mode(void)
*/
efi_memmap_unmap();

- if (efi_memmap_init_late(pa, efi.memmap.desc_size * count)) {
+ size = efi.memmap.desc_size * efi.memmap.num_valid_entries;
+ if (efi_memmap_init_late(efi.memmap.phys_map, size)) {
pr_err("Failed to remap late EFI memory map\n");
goto err;
}

+ efi_merge_regions();
+ efi_map_regions();
+
if (efi_enabled(EFI_DBG)) {
pr_info("EFI runtime memory map:\n");
efi_print_memmap();
}

- if (efi_setup_page_tables(pa, 1 << pg_shift))
+ size = efi.memmap.desc_size * efi.memmap.num_valid_entries;
+ if (efi_setup_page_tables(efi.memmap.phys_map,
+ DIV_ROUND_UP(size, PAGE_SIZE)))
goto err;

efi_sync_low_kernel_mappings();

- status = efi_set_virtual_address_map(efi.memmap.desc_size * count,
+ pa = efi.memmap.phys_map;
+ status = efi_set_virtual_address_map(size,
efi.memmap.desc_size,
efi.memmap.desc_version,
(efi_memory_desc_t *)pa,