[PATCH v2 3/6] efi/libstub: add the poisoned-memory EFI table

Breno Leitao leitao at debian.org
Fri Aug 21 03:06:03 PDT 2026


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 efi_get_ram_top(): bit N covers unit N counting
from address 0, up to the top of usable RAM, so the table tracks
max_pfn. Memory the firmware hot-adds later sits above it and is not
carried across a kexec.

At one bit per 2M that is 64K per TiB. The 2M granule is called "unit"
here.

Cap the bitmap at 1M to make sure this doesn't get too big.

The next kernel reads unit_size back out of the table, so a wider unit
only costs precision: more memory withheld per poisoned frame, nothing
lost.

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.

Nothing calls it yet; the stub entry paths pick it up next.

Signed-off-by: Breno Leitao <leitao at debian.org>
---
 drivers/firmware/efi/libstub/efi-stub-helper.c | 89 ++++++++++++++++++++++++++
 drivers/firmware/efi/libstub/efistub.h         |  6 ++
 2 files changed, 95 insertions(+)

diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index f27f2e1f00199..7258ecdf1f7c2 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -774,3 +774,92 @@ 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
+/* Cap on the bitmap; a span too wide to fit gets a coarser unit instead. */
+#define EFI_POISON_MAX_TABLE_SIZE	SZ_1M
+#define EFI_POISON_MAX_UNIT_SIZE	SZ_1G
+
+/*
+ * Bitmap geometry for a given top of RAM. The bitmap starts at address 0, so
+ * bit N covers unit N.
+ * In the following code, a "unit" is granule of physical address space that one
+ * bitmap bit covers.
+ */
+static u32 efi_poison_geometry(u64 ram_top, u64 *bitmap_size)
+{
+	u64 nr_units = DIV_ROUND_UP(ram_top, EFI_POISON_UNIT_SIZE);
+	u32 unit_size = EFI_POISON_UNIT_SIZE;
+
+	while (DIV_ROUND_UP(nr_units, BITS_PER_BYTE) > EFI_POISON_MAX_TABLE_SIZE &&
+	       unit_size < EFI_POISON_MAX_UNIT_SIZE) {
+		nr_units = DIV_ROUND_UP(nr_units, 2);
+		unit_size *= 2;
+	}
+
+	*bitmap_size = DIV_ROUND_UP(nr_units, BITS_PER_BYTE);
+	return unit_size;
+}
+
+/* ACPI reclaim memory, so the next kernel does not treat it as free RAM. */
+static struct linux_efi_poisoned_memory *efi_poison_alloc(u64 bitmap_size,
+							  u32 unit_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 = unit_size;
+	pm->phys_base = 0;
+	pm->size = bitmap_size;
+	memset(pm->bitmap, 0, bitmap_size);
+
+	return pm;
+}
+
+/*
+ * Allocate and install the poisoned-memory bitmap while boot services are
+ * available
+ */
+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_top, bitmap_size;
+	efi_status_t status;
+	u32 unit_size;
+
+	/* 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_top(&ram_top) != EFI_SUCCESS) {
+		efi_err("Failed to size the poisoned-memory table!\n");
+		return;
+	}
+
+	unit_size = efi_poison_geometry(ram_top, &bitmap_size);
+
+	pm = efi_poison_alloc(bitmap_size, unit_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/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index 77ba576779aca..b5e19ba50ddae 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -1171,6 +1171,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);

-- 
2.53.0-Meta




More information about the kexec mailing list