[PATCH RFC 1/3] efi: add the LINUX_EFI_POISONED_MEMORY configuration table
Breno Leitao
leitao at debian.org
Fri Jul 17 07:03:03 PDT 2026
Hardware-poisoned page frames are tracked only in the running kernel's data
structures, so a kexec loses them and the next kernel hands the known-bad RAM
back out.
Add an EFI configuration table to carry that list to the next kernel. It uses
the same growable linked-list layout as LINUX_EFI_MEMRESERVE and, like it, is
installed empty by the stub while boot services are still available -- a
running kernel can only append to the list, not install a new config table --
and rides the EFI system table across kexec.
Define the table and install the empty root here; the record and reserve
paths follow.
Signed-off-by: Breno Leitao <leitao at debian.org>
---
drivers/firmware/efi/Kconfig | 10 +++++++++
drivers/firmware/efi/efi.c | 2 ++
drivers/firmware/efi/libstub/efi-stub-helper.c | 30 ++++++++++++++++++++++++++
drivers/firmware/efi/libstub/efi-stub.c | 1 +
drivers/firmware/efi/libstub/efistub.h | 6 ++++++
drivers/firmware/efi/libstub/x86-stub.c | 2 ++
include/linux/efi.h | 21 ++++++++++++++++++
7 files changed, 72 insertions(+)
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 29e0729299f5b..0e4ccfd968b87 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -263,6 +263,16 @@ config EFI_COCO_SECRET
virt/coco/efi_secret module to access the secrets, which in turn
allows userspace programs to access the injected secrets.
+config EFI_POISONED_MEMORY
+ bool "Carry hardware-poisoned pages across kexec"
+ depends on EFI_STUB && MEMORY_FAILURE
+ help
+ Record page frames that are hardware-poisoned while this kernel runs
+ into an EFI configuration table, and honor that table early on the
+ next kernel so a kexec does not hand known-bad RAM back out.
+
+ If unsure, say N.
+
config OVMF_DEBUG_LOG
bool "Expose OVMF firmware debug log via sysfs"
depends on EFI
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 0327a39d31fa5..f3b799930be44 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -55,6 +55,7 @@ struct efi __read_mostly efi = {
#ifdef CONFIG_UNACCEPTED_MEMORY
.unaccepted = EFI_INVALID_TABLE_ADDR,
#endif
+ .poisoned_memory = EFI_INVALID_TABLE_ADDR,
};
EXPORT_SYMBOL(efi);
@@ -629,6 +630,7 @@ static const efi_config_table_type_t common_tables[] __initconst = {
{EFI_TCG2_FINAL_EVENTS_TABLE_GUID, &efi.tpm_final_log, "TPMFinalLog" },
{EFI_CC_FINAL_EVENTS_TABLE_GUID, &efi.tpm_final_log, "CCFinalLog" },
{LINUX_EFI_MEMRESERVE_TABLE_GUID, &mem_reserve, "MEMRESERVE" },
+ {LINUX_EFI_POISONED_MEMORY_TABLE_GUID, &efi.poisoned_memory, "POISON" },
{LINUX_EFI_INITRD_MEDIA_GUID, &initrd, "INITRD" },
{EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" },
#ifdef CONFIG_OVMF_DEBUG_LOG
diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index f27f2e1f00199..2d873bccac481 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -774,3 +774,33 @@ void efi_remap_image(unsigned long image_base, unsigned alloc_size,
efi_warn("Failed to remap data region non-executable\n");
}
}
+
+/*
+ * Install an empty root for the poisoned-memory table now, while boot services
+ * are available; the running kernel can only append to it later, not install a
+ * new config table. Shared by all stubs (x86 and the generic stub).
+ */
+#ifdef CONFIG_EFI_POISONED_MEMORY
+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;
+ efi_status_t status;
+
+ status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*pm),
+ (void **)&pm);
+ if (status != EFI_SUCCESS) {
+ efi_err("Failed to allocate poisoned-memory entry!\n");
+ return;
+ }
+
+ pm->next = 0;
+ pm->size = 0;
+ atomic_set(&pm->count, 0);
+
+ status = efi_bs_call(install_configuration_table,
+ &poisoned_memory_table_guid, pm);
+ if (status != EFI_SUCCESS)
+ 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 42d6073bcd062..008635eb5027a 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 cef32e2c82d8f..f90de11bc8855 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -1023,6 +1023,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");
diff --git a/include/linux/efi.h b/include/linux/efi.h
index b3c83516593d1..7787eb8d4e4c1 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -422,6 +422,7 @@ void efi_native_runtime_setup(void);
#define LINUX_EFI_COCO_SECRET_AREA_GUID EFI_GUID(0xadf956ad, 0xe98c, 0x484c, 0xae, 0x11, 0xb5, 0x1c, 0x7d, 0x33, 0x64, 0x47)
#define LINUX_EFI_BOOT_MEMMAP_GUID EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4)
#define LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID EFI_GUID(0xd5d1de3c, 0x105c, 0x44f9, 0x9e, 0xa9, 0xbc, 0xef, 0x98, 0x12, 0x00, 0x31)
+#define LINUX_EFI_POISONED_MEMORY_TABLE_GUID EFI_GUID(0x78a5bf07, 0x7d2a, 0x2889, 0x16, 0x31, 0x63, 0xd4, 0x56, 0xf2, 0x83, 0x50)
#define RISCV_EFI_BOOT_PROTOCOL_GUID EFI_GUID(0xccd15fec, 0x6f73, 0x4eec, 0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf)
@@ -650,6 +651,7 @@ extern struct efi {
unsigned long mokvar_table; /* MOK variable config table */
unsigned long coco_secret; /* Confidential computing secret table */
unsigned long unaccepted; /* Unaccepted memory table */
+ unsigned long poisoned_memory; /* Hardware-poisoned memory table */
efi_get_time_t *get_time;
efi_set_time_t *set_time;
@@ -1271,6 +1273,25 @@ struct linux_efi_memreserve {
#define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memreserve)) \
/ sizeof_field(struct linux_efi_memreserve, entry[0]))
+/*
+ * Frames hardware-poisoned while a kernel runs are recorded here so a kexec'd
+ * kernel can keep them out of its allocator. Same growable linked-list shape
+ * as linux_efi_memreserve; carried across kexec via the EFI system table.
+ */
+struct linux_efi_poisoned_memory {
+ int size; // allocated size of the array
+ atomic_t count; // number of entries used
+ phys_addr_t next; // pa of next struct instance
+ struct {
+ phys_addr_t base;
+ phys_addr_t size;
+ } entry[];
+};
+
+#define EFI_POISONED_MEMORY_COUNT(size) \
+ (((size) - sizeof(struct linux_efi_poisoned_memory)) \
+ / sizeof_field(struct linux_efi_poisoned_memory, entry[0]))
+
void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size);
/*
--
2.53.0-Meta
More information about the kexec
mailing list