[PATCH v4 2/5] mm/memory-failure: libstub: install the poisoned-memory EFI table
From: Breno Leitao
Date: Wed Sep 09 2026 - 09:13:19 EST
A EFI config table can only be installed while boot services are still
up, so the stub has to create it; the running kernel can only flip bits
in a table that already exists.
Size the bitmap from the span the UEFI memory map describes, which
efi_get_ram_range() walks since the stub has no max_pfn. Bit 0 covers
the bottom of that span, recorded in phys_base, so a machine whose RAM
starts high does not pay for the hole below it. Memory the firmware
hot-adds later sits outside the span and is not carried across a kexec.
One table has to serve every architecture, and they do not agree on what
becomes RAM: x86 decides by descriptor type, arm64 by attribute. So
efi_get_ram_range() does not filter at all and spans every descriptor in
the map. Sizing wide only costs bitmap bytes; sizing narrow silently
drops the records for every frame outside the span.
At one bit per 2M that is 64K per TiB, and 256M at the 4PB x86
architectural maximum. The 2M granule is called "unit" here, and the
table carries it so the granule can change later without breaking the
kernels already reading it.
Allocate it as EFI_ACPI_RECLAIM_MEMORY so the next kernel does not take
it for free RAM, and install it empty.
A table installed by an earlier boot rides the system table across kexec
and is reused as-is.
x86 does not go through efi_stub_common(), so the generic stub and the
x86 stub each need the call; on x86 it has to come before exit_boot(),
which is the last point a configuration table can be installed.
Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
drivers/firmware/efi/libstub/efi-stub-helper.c | 100 +++++++++++++++++++++++++
drivers/firmware/efi/libstub/efi-stub.c | 1 +
drivers/firmware/efi/libstub/efistub.h | 6 ++
drivers/firmware/efi/libstub/x86-stub.c | 2 +
4 files changed, 109 insertions(+)
diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index 48f93f7758e9e..5cbe675491333 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -774,3 +774,103 @@ void efi_remap_image(unsigned long image_base, unsigned alloc_size,
efi_warn("Failed to remap data region non-executable\n");
}
}
+
+#ifdef CONFIG_EFI_POISONED_MEMORY
+/*
+ * Find the base and top of the memory, so, we can create the bitmap for
+ * the full range.
+ */
+static efi_status_t efi_get_ram_range(u64 *base, u64 *top)
+{
+ struct efi_boot_memmap *map __free(efi_pool) = NULL;
+ u64 ram_base = ULLONG_MAX, ram_top = 0;
+ efi_status_t status;
+ int i, nr_desc;
+
+ status = efi_get_memory_map(&map, false);
+ if (status != EFI_SUCCESS)
+ return status;
+
+ nr_desc = map->map_size / map->desc_size;
+ for (i = 0; i < nr_desc; i++) {
+ efi_memory_desc_t *d;
+
+ d = efi_memdesc_ptr((unsigned long)map->map, map->desc_size, i);
+ ram_base = min(ram_base, d->phys_addr);
+ ram_top = max(ram_top,
+ d->phys_addr + d->num_pages * EFI_PAGE_SIZE);
+ }
+ if (!ram_top || ram_base == ULLONG_MAX)
+ return EFI_NOT_FOUND;
+
+ *base = round_down(ram_base, EFI_POISON_UNIT_SIZE);
+ *top = round_up(ram_top, EFI_POISON_UNIT_SIZE);
+
+ return EFI_SUCCESS;
+}
+
+/* The size of the bitmap */
+static u64 efi_poison_bitmap_size(u64 span)
+{
+ u64 bytes = DIV_ROUND_UP(DIV_ROUND_UP(span, EFI_POISON_UNIT_SIZE),
+ BITS_PER_BYTE);
+
+ return round_up(bytes, sizeof(unsigned long));
+}
+
+static struct linux_efi_poisoned_memory *efi_poison_alloc(u64 phys_base,
+ u64 bitmap_size)
+{
+ struct linux_efi_poisoned_memory *pm;
+ efi_status_t status;
+
+ status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
+ sizeof(*pm) + bitmap_size, (void **)&pm);
+ if (status != EFI_SUCCESS)
+ return NULL;
+
+ pm->version = 1;
+ pm->unit_size = EFI_POISON_UNIT_SIZE;
+ pm->phys_base = phys_base;
+ pm->size = bitmap_size;
+ memset(pm->bitmap, 0, bitmap_size);
+
+ return pm;
+}
+
+/* This needs to be done while boot service is still active */
+void install_poisoned_memory_table(void)
+{
+ efi_guid_t poisoned_memory_table_guid = LINUX_EFI_POISONED_MEMORY_TABLE_GUID;
+ struct linux_efi_poisoned_memory *pm;
+ u64 ram_base, ram_top, bitmap_size;
+ efi_status_t status;
+
+ /* A table installed by an earlier boot rides the system table across kexec. */
+ pm = get_efi_config_table(poisoned_memory_table_guid);
+ if (pm) {
+ if (pm->version != 1)
+ efi_err("Unknown version of poisoned-memory table\n");
+ return;
+ }
+
+ if (efi_get_ram_range(&ram_base, &ram_top) != EFI_SUCCESS) {
+ efi_err("Failed to size the poisoned-memory table!\n");
+ return;
+ }
+
+ bitmap_size = efi_poison_bitmap_size(ram_top - ram_base);
+ pm = efi_poison_alloc(ram_base, bitmap_size);
+ if (!pm) {
+ efi_err("Failed to allocate poisoned-memory table!\n");
+ return;
+ }
+
+ status = efi_bs_call(install_configuration_table,
+ &poisoned_memory_table_guid, pm);
+ if (status != EFI_SUCCESS) {
+ efi_bs_call(free_pool, pm);
+ efi_err("Failed to install poisoned-memory config table!\n");
+ }
+}
+#endif
diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
index 235c9738da2d6..22a315e2814a1 100644
--- a/drivers/firmware/efi/libstub/efi-stub.c
+++ b/drivers/firmware/efi/libstub/efi-stub.c
@@ -179,6 +179,7 @@ efi_status_t efi_stub_common(efi_handle_t handle,
EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP);
install_memreserve_table();
+ install_poisoned_memory_table();
status = efi_boot_kernel(handle, image, image_addr, cmdline_ptr);
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index fd91fc15ec810..44436869c4efe 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -1169,6 +1169,12 @@ efi_enable_reset_attack_mitigation(void) { }
void efi_retrieve_eventlog(void);
+#ifdef CONFIG_EFI_POISONED_MEMORY
+void install_poisoned_memory_table(void);
+#else
+static inline void install_poisoned_memory_table(void) { }
+#endif
+
struct sysfb_display_info *alloc_primary_display(void);
struct sysfb_display_info *__alloc_primary_display(void);
void free_primary_display(struct sysfb_display_info *dpy);
diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index 0bae0f06b6763..3136132b9628a 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -1024,6 +1024,8 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
setup_unaccepted_memory();
+ install_poisoned_memory_table();
+
status = exit_boot(boot_params, handle);
if (status != EFI_SUCCESS) {
efi_err("exit_boot() failed!\n");
--
2.53.0-Meta