[PATCH v5 3/9] mm/memory-failure: libstub: install the poisoned-memory EFI table

From: Breno Leitao

Date: Tue Sep 15 2026 - 09:20:02 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 what they agree on is the
attribute: setup_e820() takes a descriptor as RAM only if it is writeback
cacheable, and so does is_usable_memory() on arm64.

The bitmap spans from the lowest to the highest descriptor that is
write-back cacheable or unaccepted memory, as discussed with Kiryl. That
leaves out the MMIO apertures, which sit high enough to stretch it far
past the RAM it needs to describe.

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.

Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
drivers/firmware/efi/libstub/efi-stub-helper.c | 103 +++++++++++++++++++++++++
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, 112 insertions(+)

diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index 48f93f7758e9e9..9c66e06c972c5a 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -774,3 +774,106 @@ 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);
+ if (!(d->attribute & EFI_MEMORY_WB) &&
+ d->type != EFI_UNACCEPTED_MEMORY)
+ continue;
+ 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 235c9738da2d63..22a315e2814a1a 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 fd91fc15ec810b..44436869c4efe1 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 0bae0f06b6763a..3136132b9628ab 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