[PATCH v3 11/17] efi: Use nr_map not map_end to find the last valid memory map entry

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

Currently, the efi.memmap struct keeps track of the start and the end of
the EFI memory map in memory, as well as the number of entries.

Let's repaint the nr_map field as the number of *valid* entries, and
update all the iterators and other memory map traversal routines
accordingly.

This allows pruning of invalid or unneeded entries by moving the
remaining entries to the start of the map, without the need for
freeing/reallocating or unmapping and remapping. Now that entries are
never added, but only removed, it is possible to retain the same
allocation throughout the boot process, and free the part that is no
longer in use afterwards.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/x86/platform/efi/efi.c | 18 ++++++++++++------
arch/x86/platform/efi/memmap.c | 2 +-
arch/x86/platform/efi/quirks.c | 2 +-
arch/x86/platform/efi/runtime-map.c | 4 ++--
drivers/firmware/efi/arm-runtime.c | 2 +-
drivers/firmware/efi/memmap.c | 8 +++-----
drivers/firmware/efi/riscv-runtime.c | 2 +-
include/linux/efi.h | 8 ++++----
8 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index c0195b5eab21..edbf6efe3947 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -222,7 +222,7 @@ int __init efi_memblock_x86_reserve_range(void)
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
efi.memmap.desc_version);

- memblock_reserve(pmap, efi.memmap.nr_map * efi.memmap.desc_size);
+ memblock_reserve(pmap, efi.memmap.num_valid_entries * efi.memmap.desc_size);
set_bit(EFI_PRESERVE_BS_REGIONS, &efi.flags);

return 0;
@@ -289,7 +289,7 @@ static void __init efi_clean_memmap(void)
.phys_map = efi.memmap.phys_map,
.desc_version = efi.memmap.desc_version,
.desc_size = efi.memmap.desc_size,
- .size = efi.memmap.desc_size * (efi.memmap.nr_map - n_removal),
+ .size = efi.memmap.desc_size * (efi.memmap.num_valid_entries - n_removal),
.flags = 0,
};

@@ -533,7 +533,8 @@ static inline void *efi_map_next_entry_reverse(void *entry)
{
/* Initial call */
if (!entry)
- return efi.memmap.map_end - efi.memmap.desc_size;
+ return efi_memdesc_ptr(efi.memmap.map, efi.memmap.desc_size,
+ efi.memmap.num_valid_entries - 1);

entry -= efi.memmap.desc_size;
if (entry < efi.memmap.map)
@@ -555,6 +556,9 @@ static inline void *efi_map_next_entry_reverse(void *entry)
*/
static void *efi_map_next_entry(void *entry)
{
+ if (!efi.memmap.num_valid_entries)
+ return NULL;
+
if (efi_enabled(EFI_64BIT)) {
/*
* Starting in UEFI v2.5 the EFI_PROPERTIES_TABLE
@@ -581,7 +585,9 @@ static void *efi_map_next_entry(void *entry)
return efi.memmap.map;

entry += efi.memmap.desc_size;
- if (entry >= efi.memmap.map_end)
+ if (entry >= (void *)efi_memdesc_ptr(efi.memmap.map,
+ efi.memmap.desc_size,
+ efi.memmap.num_valid_entries))
return NULL;

return entry;
@@ -712,13 +718,13 @@ static void __init kexec_enter_virtual_mode(void)
efi_memmap_unmap();

if (efi_memmap_init_late(efi.memmap.phys_map,
- efi.memmap.desc_size * efi.memmap.nr_map)) {
+ efi.memmap.desc_size * efi.memmap.num_valid_entries)) {
pr_err("Failed to remap late EFI memory map\n");
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
return;
}

- num_pages = ALIGN(efi.memmap.nr_map * efi.memmap.desc_size, PAGE_SIZE);
+ num_pages = ALIGN(efi.memmap.num_valid_entries * efi.memmap.desc_size, PAGE_SIZE);
num_pages >>= PAGE_SHIFT;

if (efi_setup_page_tables(efi.memmap.phys_map, num_pages)) {
diff --git a/arch/x86/platform/efi/memmap.c b/arch/x86/platform/efi/memmap.c
index da7483a33d68..524e9f2ef276 100644
--- a/arch/x86/platform/efi/memmap.c
+++ b/arch/x86/platform/efi/memmap.c
@@ -90,7 +90,7 @@ int __init efi_memmap_alloc(unsigned int num_entries,
*/
int __init efi_memmap_install(struct efi_memory_map_data *data)
{
- unsigned long size = efi.memmap.desc_size * efi.memmap.nr_map;
+ unsigned long size = efi.memmap.map_end - efi.memmap.map;
unsigned long flags = efi.memmap.flags;
u64 phys = efi.memmap.phys_map;
int ret;
diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c
index efb828b7e2ab..eb00130bcb66 100644
--- a/arch/x86/platform/efi/quirks.c
+++ b/arch/x86/platform/efi/quirks.c
@@ -410,7 +410,7 @@ void __init efi_unmap_boot_services(void)
if (efi_enabled(EFI_DBG))
return;

- num_to_free = efi.memmap.nr_map;
+ num_to_free = efi.memmap.num_valid_entries;
ranges_to_free = kmalloc_array(num_to_free, sizeof(ranges_to_free[0]),
GFP_KERNEL);
if (!ranges_to_free) {
diff --git a/arch/x86/platform/efi/runtime-map.c b/arch/x86/platform/efi/runtime-map.c
index 053ff161eb9a..fc8ca1974730 100644
--- a/arch/x86/platform/efi/runtime-map.c
+++ b/arch/x86/platform/efi/runtime-map.c
@@ -138,7 +138,7 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr,

int efi_get_runtime_map_size(void)
{
- return efi.memmap.nr_map * efi.memmap.desc_size;
+ return efi.memmap.num_valid_entries * efi.memmap.desc_size;
}

int efi_get_runtime_map_desc_size(void)
@@ -166,7 +166,7 @@ static int __init efi_runtime_map_init(void)
if (!efi_enabled(EFI_MEMMAP) || !efi_kobj)
return 0;

- map_entries = kzalloc_objs(entry, efi.memmap.nr_map);
+ map_entries = kzalloc_objs(entry, efi.memmap.num_valid_entries);
if (!map_entries) {
ret = -ENOMEM;
goto out;
diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
index 3167cab62014..e19997c09175 100644
--- a/drivers/firmware/efi/arm-runtime.c
+++ b/drivers/firmware/efi/arm-runtime.c
@@ -96,7 +96,7 @@ static int __init arm_enable_runtime_services(void)

efi_memmap_unmap();

- mapsize = efi.memmap.desc_size * efi.memmap.nr_map;
+ mapsize = efi.memmap.desc_size * efi.memmap.num_valid_entries;

if (efi_memmap_init_late(efi.memmap.phys_map, mapsize)) {
pr_err("Failed to remap EFI memory map\n");
diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c
index f1c04d7cfd71..035089791c93 100644
--- a/drivers/firmware/efi/memmap.c
+++ b/drivers/firmware/efi/memmap.c
@@ -49,7 +49,7 @@ int __init __efi_memmap_init(struct efi_memory_map_data *data)
}

map.phys_map = data->phys_map;
- map.nr_map = data->size / data->desc_size;
+ map.num_valid_entries = data->size / data->desc_size;
map.map_end = map.map + data->size;

map.desc_version = data->desc_version;
@@ -87,10 +87,8 @@ void __init efi_memmap_unmap(void)
return;

if (!(efi.memmap.flags & EFI_MEMMAP_LATE)) {
- unsigned long size;
-
- size = efi.memmap.desc_size * efi.memmap.nr_map;
- early_memunmap(efi.memmap.map, size);
+ early_memunmap(efi.memmap.map,
+ efi.memmap.map_end - efi.memmap.map);
} else {
memunmap(efi.memmap.map);
}
diff --git a/drivers/firmware/efi/riscv-runtime.c b/drivers/firmware/efi/riscv-runtime.c
index 60cdf7bf141f..087a7f8a74e6 100644
--- a/drivers/firmware/efi/riscv-runtime.c
+++ b/drivers/firmware/efi/riscv-runtime.c
@@ -66,7 +66,7 @@ static int __init riscv_enable_runtime_services(void)

efi_memmap_unmap();

- mapsize = efi.memmap.desc_size * efi.memmap.nr_map;
+ mapsize = efi.memmap.desc_size * efi.memmap.num_valid_entries;

if (efi_memmap_init_late(efi.memmap.phys_map, mapsize)) {
pr_err("Failed to remap EFI memory map\n");
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 72e76ec54641..a8406ca92332 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -568,7 +568,7 @@ struct efi_memory_map {
phys_addr_t phys_map;
void *map;
void *map_end;
- int nr_map;
+ int num_valid_entries;
unsigned long desc_version;
unsigned long desc_size;
#define EFI_MEMMAP_LATE (1UL << 0)
@@ -803,9 +803,9 @@ extern int efi_memattr_apply_permissions(struct mm_struct *mm,

/* Iterate through an efi_memory_map */
#define for_each_efi_memory_desc_in_map(m, md) \
- for ((md) = (m)->map; \
- (md) && ((void *)(md) + (m)->desc_size) <= (m)->map_end; \
- (md) = (void *)(md) + (m)->desc_size)
+ for (int __idx = 0; \
+ (md) = efi_memdesc_ptr((m)->map, (m)->desc_size, __idx), \
+ __idx < (m)->num_valid_entries; ++__idx)

/**
* for_each_efi_memory_desc - iterate over descriptors in efi.memmap