[PATCH v2 2/6] efi: add the LINUX_EFI_POISONED_MEMORY configuration table
From: Breno Leitao
Date: Fri Aug 21 2026 - 06:46:20 EST
Hardware-poisoned page frames are tracked only in the running kernel's
data structures, so a kexec loses them and the next kernel doesn't have
this information, thus, tripping into them again.
Add an EFI configuration table to carry that information across kexec.
It is a bitmap with one bit per EFI_POISON_UNIT_SIZE (2MiB) of physical
memory, modeled on the LINUX_EFI_UNACCEPTED_MEMORY table, and it rides
the EFI system table to every kernel in the chain.
Suggested-by: Kiryl Shutsemau <kas@xxxxxxxxxx>
Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
drivers/firmware/efi/Kconfig | 10 ++++++++++
drivers/firmware/efi/efi.c | 6 ++++++
include/linux/efi.h | 13 +++++++++++++
3 files changed, 29 insertions(+)
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 29e0729299f5b..69c0dc02bc112 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 && 64BIT
+ 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..111e60479211a 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -55,6 +55,9 @@ struct efi __read_mostly efi = {
#ifdef CONFIG_UNACCEPTED_MEMORY
.unaccepted = EFI_INVALID_TABLE_ADDR,
#endif
+#ifdef CONFIG_EFI_POISONED_MEMORY
+ .poisoned_memory = EFI_INVALID_TABLE_ADDR,
+#endif
};
EXPORT_SYMBOL(efi);
@@ -646,6 +649,9 @@ static const efi_config_table_type_t common_tables[] __initconst = {
#ifdef CONFIG_UNACCEPTED_MEMORY
{LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID, &efi.unaccepted, "Unaccepted" },
#endif
+#ifdef CONFIG_EFI_POISONED_MEMORY
+ {LINUX_EFI_POISONED_MEMORY_TABLE_GUID, &efi.poisoned_memory, "POISON" },
+#endif
#ifdef CONFIG_EFI_GENERIC_STUB
{LINUX_EFI_PRIMARY_DISPLAY_TABLE_GUID, &primary_display_table },
#endif
diff --git a/include/linux/efi.h b/include/linux/efi.h
index b3c83516593d1..ce0980a5bb81b 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -23,6 +23,7 @@
#include <linux/pstore.h>
#include <linux/range.h>
#include <linux/reboot.h>
+#include <linux/sizes.h>
#include <linux/uuid.h>
#include <asm/page.h>
@@ -422,6 +423,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 +652,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 +1274,16 @@ struct linux_efi_memreserve {
#define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memreserve)) \
/ sizeof_field(struct linux_efi_memreserve, entry[0]))
+struct linux_efi_poisoned_memory {
+ u32 version;
+ u32 unit_size; /* bytes of phys space per bitmap bit */
+ u64 phys_base; /* phys address covered by bit 0 */
+ u64 size; /* bitmap size in bytes */
+ unsigned long bitmap[];
+};
+
+#define EFI_POISON_UNIT_SIZE SZ_2M
+
void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size);
/*
--
2.53.0-Meta