* [PATCH v2 1/6] efi/libstub: add a helper for the top of usable RAM
2026-08-21 10:06 [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec Breno Leitao
@ 2026-08-21 10:06 ` Breno Leitao
2026-08-21 11:04 ` Kiryl Shutsemau
2026-08-21 10:06 ` [PATCH v2 2/6] efi: add the LINUX_EFI_POISONED_MEMORY configuration table Breno Leitao
` (5 subsequent siblings)
6 siblings, 1 reply; 16+ messages in thread
From: Breno Leitao @ 2026-08-21 10:06 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton
Cc: linux-efi, linux-kernel, linux-mm, rmikey, kas, riel, kexec,
Breno Leitao, kernel-team
The stub has no way to ask how far RAM reaches, which is useful to
create a bitmap that covers the whole memory.
max_pfn answers that for the kernel, but it is only set in
setup_arch(), long after the stub has handed off, so, unfortunately we
cannot use it here..
Add efi_get_ram_top(), which returns the highest address reached by the
entry types that become usable RAM. Unaccepted memory is counted as
well, as suggested by Kiryl.
Memory the firmware hot-adds later is *not* in the memory map, so it is
not covered; that matches max_pfn, which is likewise the top of the
memory present at boot.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/firmware/efi/libstub/efistub.h | 2 ++
drivers/firmware/efi/libstub/mem.c | 53 ++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index fd91fc15ec810..77ba576779aca 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -1094,6 +1094,8 @@ char *efi_convert_cmdline(efi_loaded_image_t *image);
efi_status_t efi_get_memory_map(struct efi_boot_memmap **map,
bool install_cfg_tbl);
+efi_status_t efi_get_ram_top(u64 *top);
+
efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
unsigned long max);
diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c
index 59f3f83de50c2..777f1f180c66e 100644
--- a/drivers/firmware/efi/libstub/mem.c
+++ b/drivers/firmware/efi/libstub/mem.c
@@ -64,6 +64,59 @@ efi_status_t efi_get_memory_map(struct efi_boot_memmap **map,
return EFI_SUCCESS;
}
+/**
+ * efi_get_ram_top() - find the top of usable RAM
+ * @top: on return, the end of the highest memory map entry that becomes
+ * usable RAM
+ *
+ * Walk the UEFI memory map for the entry types that become usable RAM, the set
+ * setup_e820() maps to E820_TYPE_RAM, and return the highest address any of
+ * them reaches. Memory a confidential guest has not accepted yet counts as
+ * well, since it becomes RAM once accepted. This is what the stub has in place
+ * of max_pfn, which is only set once the kernel proper is up.
+ *
+ * Memory the firmware hot-adds later is not described by the memory map and so
+ * is not accounted for here.
+ *
+ * Return: status code
+ */
+efi_status_t efi_get_ram_top(u64 *top)
+{
+ struct efi_boot_memmap *map __free(efi_pool) = NULL;
+ efi_status_t status;
+ u64 ram_top = 0;
+ 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);
+ switch (d->type) {
+ case EFI_LOADER_CODE:
+ case EFI_LOADER_DATA:
+ case EFI_BOOT_SERVICES_CODE:
+ case EFI_BOOT_SERVICES_DATA:
+ case EFI_CONVENTIONAL_MEMORY:
+ case EFI_UNACCEPTED_MEMORY:
+ ram_top = max(ram_top,
+ d->phys_addr + d->num_pages * EFI_PAGE_SIZE);
+ break;
+ default:
+ break;
+ }
+ }
+ if (!ram_top)
+ return EFI_NOT_FOUND;
+
+ *top = ram_top;
+ return EFI_SUCCESS;
+}
+
/**
* efi_allocate_pages() - Allocate memory pages
* @size: minimum number of bytes to allocate
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 1/6] efi/libstub: add a helper for the top of usable RAM
2026-08-21 10:06 ` [PATCH v2 1/6] efi/libstub: add a helper for the top of usable RAM Breno Leitao
@ 2026-08-21 11:04 ` Kiryl Shutsemau
0 siblings, 0 replies; 16+ messages in thread
From: Kiryl Shutsemau @ 2026-08-21 11:04 UTC (permalink / raw)
To: Breno Leitao
Cc: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton, linux-efi, linux-kernel, linux-mm, rmikey, riel,
kexec, kernel-team
On Fri, Aug 21, 2026 at 03:06:01AM -0700, Breno Leitao wrote:
> diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c
> index 59f3f83de50c2..777f1f180c66e 100644
> --- a/drivers/firmware/efi/libstub/mem.c
> +++ b/drivers/firmware/efi/libstub/mem.c
> @@ -64,6 +64,59 @@ efi_status_t efi_get_memory_map(struct efi_boot_memmap **map,
> return EFI_SUCCESS;
> }
>
> +/**
> + * efi_get_ram_top() - find the top of usable RAM
> + * @top: on return, the end of the highest memory map entry that becomes
> + * usable RAM
> + *
> + * Walk the UEFI memory map for the entry types that become usable RAM, the set
> + * setup_e820() maps to E820_TYPE_RAM, and return the highest address any of
> + * them reaches. Memory a confidential guest has not accepted yet counts as
> + * well, since it becomes RAM once accepted. This is what the stub has in place
> + * of max_pfn, which is only set once the kernel proper is up.
> + *
> + * Memory the firmware hot-adds later is not described by the memory map and so
> + * is not accounted for here.
> + *
> + * Return: status code
> + */
> +efi_status_t efi_get_ram_top(u64 *top)
> +{
> + struct efi_boot_memmap *map __free(efi_pool) = NULL;
> + efi_status_t status;
> + u64 ram_top = 0;
> + 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);
> + switch (d->type) {
> + case EFI_LOADER_CODE:
> + case EFI_LOADER_DATA:
> + case EFI_BOOT_SERVICES_CODE:
> + case EFI_BOOT_SERVICES_DATA:
> + case EFI_CONVENTIONAL_MEMORY:
> + case EFI_UNACCEPTED_MEMORY:
> + ram_top = max(ram_top,
> + d->phys_addr + d->num_pages * EFI_PAGE_SIZE);
> + break;
> + default:
> + break;
> + }
> + }
You model it after setup_e820() which is x86-specific.
On ARM is_memory() adds anything with WB/WT/WC, and is_usable_memory()
also counts EFI_ACPI_RECLAIM_MEMORY and EFI_PERSISTENT_MEMORY.
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 2/6] efi: add the LINUX_EFI_POISONED_MEMORY configuration table
2026-08-21 10:06 [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec Breno Leitao
2026-08-21 10:06 ` [PATCH v2 1/6] efi/libstub: add a helper for the top of usable RAM Breno Leitao
@ 2026-08-21 10:06 ` Breno Leitao
2026-08-21 11:51 ` Kiryl Shutsemau
2026-08-21 10:06 ` [PATCH v2 3/6] efi/libstub: add the poisoned-memory EFI table Breno Leitao
` (4 subsequent siblings)
6 siblings, 1 reply; 16+ messages in thread
From: Breno Leitao @ 2026-08-21 10:06 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton
Cc: linux-efi, linux-kernel, linux-mm, rmikey, kas, riel, kexec,
Breno Leitao, kernel-team
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@kernel.org>
Signed-off-by: Breno Leitao <leitao@debian.org>
---
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
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 2/6] efi: add the LINUX_EFI_POISONED_MEMORY configuration table
2026-08-21 10:06 ` [PATCH v2 2/6] efi: add the LINUX_EFI_POISONED_MEMORY configuration table Breno Leitao
@ 2026-08-21 11:51 ` Kiryl Shutsemau
2026-08-21 12:03 ` Pratyush Yadav
2026-08-21 14:04 ` Breno Leitao
0 siblings, 2 replies; 16+ messages in thread
From: Kiryl Shutsemau @ 2026-08-21 11:51 UTC (permalink / raw)
To: Breno Leitao
Cc: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton, linux-efi, linux-kernel, linux-mm, rmikey, riel,
kexec, kernel-team
On Fri, Aug 21, 2026 at 03:06:02AM -0700, Breno Leitao wrote:
> 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.
> +
Do we want to make it conditional? Maybe just for everyone with EFI_STUB
&& MEMORY_FAILURE?
Where 64BIT limitation comes from?
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/6] efi: add the LINUX_EFI_POISONED_MEMORY configuration table
2026-08-21 11:51 ` Kiryl Shutsemau
@ 2026-08-21 12:03 ` Pratyush Yadav
2026-08-21 14:11 ` Breno Leitao
2026-08-21 14:04 ` Breno Leitao
1 sibling, 1 reply; 16+ messages in thread
From: Pratyush Yadav @ 2026-08-21 12:03 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: Breno Leitao, Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin,
Naoya Horiguchi, Andrew Morton, linux-efi, linux-kernel, linux-mm,
rmikey, riel, kexec, kernel-team
On Fri, Aug 21 2026, Kiryl Shutsemau wrote:
> On Fri, Aug 21, 2026 at 03:06:02AM -0700, Breno Leitao wrote:
>> 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.
>> +
>
> Do we want to make it conditional? Maybe just for everyone with EFI_STUB
> && MEMORY_FAILURE?
+1. Each config we add is a decision someone has to make when compiling
the kernel. If they rely on the default, they will miss out on this
fairly useful feature.
So, is there any downside to just doing this by default without any
config?
--
Regards,
Pratyush Yadav
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/6] efi: add the LINUX_EFI_POISONED_MEMORY configuration table
2026-08-21 12:03 ` Pratyush Yadav
@ 2026-08-21 14:11 ` Breno Leitao
0 siblings, 0 replies; 16+ messages in thread
From: Breno Leitao @ 2026-08-21 14:11 UTC (permalink / raw)
To: Pratyush Yadav
Cc: Kiryl Shutsemau, Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin,
Naoya Horiguchi, Andrew Morton, linux-efi, linux-kernel, linux-mm,
rmikey, riel, kexec, kernel-team
Hello Pratyush,
On Fri, Aug 21, 2026 at 02:03:30PM +0200, Pratyush Yadav wrote:
> >> +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.
> >> +
> >
> > Do we want to make it conditional? Maybe just for everyone with EFI_STUB
> > && MEMORY_FAILURE?
>
> +1. Each config we add is a decision someone has to make when compiling
> the kernel. If they rely on the default, they will miss out on this
> fairly useful feature.
>
> So, is there any downside to just doing this by default without any
> config?
The only downside I see is that this extra config table gets carved
out of main memory for every MEMORY_FAILURE user.
That is roughly 64 KiB per TiB of RAM, though, so maybe it is small
enough to just enable by default?!
Thanks for the review,
--breno
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/6] efi: add the LINUX_EFI_POISONED_MEMORY configuration table
2026-08-21 11:51 ` Kiryl Shutsemau
2026-08-21 12:03 ` Pratyush Yadav
@ 2026-08-21 14:04 ` Breno Leitao
1 sibling, 0 replies; 16+ messages in thread
From: Breno Leitao @ 2026-08-21 14:04 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton, linux-efi, linux-kernel, linux-mm, rmikey, riel,
kexec, kernel-team
On Fri, Aug 21, 2026 at 12:51:34PM +0100, Kiryl Shutsemau wrote:
> On Fri, Aug 21, 2026 at 03:06:02AM -0700, Breno Leitao wrote:
> > 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.
> > +
>
> Do we want to make it conditional? Maybe just for everyone with EFI_STUB
> && MEMORY_FAILURE?
>
> Where 64BIT limitation comes from?
That simplifies a bit the maths for memory operations, and
make them more digestible.
Do we really care about it on non 64 bits platform?
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 3/6] efi/libstub: add the poisoned-memory EFI table
2026-08-21 10:06 [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec Breno Leitao
2026-08-21 10:06 ` [PATCH v2 1/6] efi/libstub: add a helper for the top of usable RAM Breno Leitao
2026-08-21 10:06 ` [PATCH v2 2/6] efi: add the LINUX_EFI_POISONED_MEMORY configuration table Breno Leitao
@ 2026-08-21 10:06 ` Breno Leitao
2026-08-21 10:06 ` [PATCH v2 4/6] efi/libstub: install the poisoned-memory table from the stub Breno Leitao
` (3 subsequent siblings)
6 siblings, 0 replies; 16+ messages in thread
From: Breno Leitao @ 2026-08-21 10:06 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton
Cc: linux-efi, linux-kernel, linux-mm, rmikey, kas, riel, kexec,
Breno Leitao, kernel-team
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@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
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 4/6] efi/libstub: install the poisoned-memory table from the stub
2026-08-21 10:06 [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec Breno Leitao
` (2 preceding siblings ...)
2026-08-21 10:06 ` [PATCH v2 3/6] efi/libstub: add the poisoned-memory EFI table Breno Leitao
@ 2026-08-21 10:06 ` Breno Leitao
2026-08-21 10:06 ` [PATCH v2 5/6] efi: record hardware-poisoned frames into the poisoned-memory table Breno Leitao
` (2 subsequent siblings)
6 siblings, 0 replies; 16+ messages in thread
From: Breno Leitao @ 2026-08-21 10:06 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton
Cc: linux-efi, linux-kernel, linux-mm, rmikey, kas, riel, kexec,
Breno Leitao, kernel-team
Call install_poisoned_memory_table() while boot services are still up,
so the table exists before the kernel that will flip bits in it runs.
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@debian.org>
---
drivers/firmware/efi/libstub/efi-stub.c | 1 +
drivers/firmware/efi/libstub/x86-stub.c | 2 ++
2 files changed, 3 insertions(+)
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/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");
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 5/6] efi: record hardware-poisoned frames into the poisoned-memory table
2026-08-21 10:06 [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec Breno Leitao
` (3 preceding siblings ...)
2026-08-21 10:06 ` [PATCH v2 4/6] efi/libstub: install the poisoned-memory table from the stub Breno Leitao
@ 2026-08-21 10:06 ` Breno Leitao
2026-08-21 10:06 ` [PATCH v2 6/6] efi: respect the poisoned pages coming from previous kernel Breno Leitao
2026-08-21 10:29 ` [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec Kiryl Shutsemau
6 siblings, 0 replies; 16+ messages in thread
From: Breno Leitao @ 2026-08-21 10:06 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton
Cc: linux-efi, linux-kernel, linux-mm, rmikey, kas, riel, kexec,
Breno Leitao, kernel-team
action_result() is where memory_failure() reports the outcome of a hard
offline, so hook it to set the frame's bit in the
LINUX_EFI_POISONED_MEMORY bitmap.
Soft-offlined pages reach num_poisoned_pages_inc() through
page_handle_poison() and are deliberately left out: they are still
functional and were offlined predictively, so recording them would turn
a prediction into a permanent loss for every kernel further down the
kexec chain.
A bit is only ever set, never cleared, given that multiple pages can set
the same bit, and it is not trivial to decide if the bit should be unset
when a page is unrecorded.
Unpoisoning a frame therefore does not hand its unit back to the next
kernel. That is a known limitation.
memory_failure() has already taken the frame out of this kernel's
allocator, so only the cross-kexec record happens here.
Suggested-by: Kiryl Shutsemau <kas@kernel.org>
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/firmware/efi/Makefile | 1 +
drivers/firmware/efi/poison.c | 130 ++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 6 ++
mm/memory-failure.c | 3 +
4 files changed, 140 insertions(+)
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index 8efbcf699e4ff..05d0a490923e5 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -43,4 +43,5 @@ obj-$(CONFIG_EFI_EARLYCON) += earlycon.o
obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o
obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o
obj-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o
+obj-$(CONFIG_EFI_POISONED_MEMORY) += poison.o
obj-$(CONFIG_TEE_STMM_EFI) += stmm/tee_stmm_efi.o
diff --git a/drivers/firmware/efi/poison.c b/drivers/firmware/efi/poison.c
new file mode 100644
index 0000000000000..2f47293a4e45f
--- /dev/null
+++ b/drivers/firmware/efi/poison.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Runtime handling for the LINUX_EFI_POISONED_MEMORY configuration table: a
+ * bitmap with one bit per EFI_POISON_UNIT_SIZE of physical memory that records
+ * hardware-poisoned frames so the next kexec kernel can keep them out of its
+ * allocator. The stub installs the (empty) bitmap; this kernel sets bits at
+ * runtime.
+ *
+ * Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
+ * Copyright (c) 2026 Breno Leitao <leitao@debian.org>
+ */
+
+#define pr_fmt(fmt) "efi: " fmt
+
+#include <linux/bitmap.h>
+#include <linux/efi.h>
+#include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/mm.h>
+#include <linux/overflow.h>
+
+static struct linux_efi_poisoned_memory *efi_poison __ro_after_init;
+
+/* A non-empty bitmap on a power-of-2 grid that phys_base actually sits on. */
+static bool __init
+efi_poison_geometry_valid(const struct linux_efi_poisoned_memory *pm)
+{
+ if (!pm->size)
+ return false;
+ if (pm->unit_size < PAGE_SIZE || !is_power_of_2(pm->unit_size))
+ return false;
+
+ return IS_ALIGNED(pm->phys_base, pm->unit_size);
+}
+
+/* The range the bitmap claims to describe has to fit in a u64. */
+static bool __init
+efi_poison_range_valid(const struct linux_efi_poisoned_memory *pm)
+{
+ u64 nbits, span;
+
+ if (check_mul_overflow(pm->size, (u64)BITS_PER_BYTE, &nbits))
+ return false;
+ if (check_mul_overflow(nbits, (u64)pm->unit_size, &span))
+ return false;
+
+ return !check_add_overflow(pm->phys_base, span, &span);
+}
+
+/*
+ * The table may have been installed by an earlier kernel in the kexec chain,
+ * so check its geometry before doing any arithmetic with it.
+ */
+static bool __init
+efi_poison_table_valid(const struct linux_efi_poisoned_memory *pm)
+{
+ if (pm->version != 1) {
+ pr_warn("Ignoring poisoned-memory table with version %u\n",
+ pm->version);
+ return false;
+ }
+
+ if (!efi_poison_geometry_valid(pm) || !efi_poison_range_valid(pm)) {
+ pr_warn("Ignoring malformed poisoned-memory table\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int __init efi_poison_init(void)
+{
+ struct linux_efi_poisoned_memory *pm;
+ u64 size;
+
+ if (efi.poisoned_memory == EFI_INVALID_TABLE_ADDR)
+ return 0;
+
+ /* Map the header to learn the bitmap size, then map the whole table. */
+ pm = memremap(efi.poisoned_memory, sizeof(*pm), MEMREMAP_WB);
+ if (WARN_ON_ONCE(!pm))
+ return 0;
+ if (!efi_poison_table_valid(pm)) {
+ memunmap(pm);
+ return 0;
+ }
+ size = pm->size;
+ memunmap(pm);
+
+ efi_poison = memremap(efi.poisoned_memory, sizeof(*pm) + size,
+ MEMREMAP_WB);
+ WARN_ON_ONCE(!efi_poison);
+ return 0;
+}
+early_initcall(efi_poison_init);
+
+/* Bitmap unit covering @pfn, or -1 if the pfn falls outside the table. */
+static long efi_poison_unit(unsigned long pfn)
+{
+ phys_addr_t addr = PFN_PHYS(pfn);
+ u64 unit;
+
+ if (addr < efi_poison->phys_base)
+ return -1;
+ unit = (addr - efi_poison->phys_base) / efi_poison->unit_size;
+ if (unit >= (u64)efi_poison->size * BITS_PER_BYTE)
+ return -1;
+ return unit;
+}
+
+/*
+ * Record a hardware-poisoned frame so the next kernel keeps its unit out of the
+ * allocator. memory_failure() has already removed the frame from this kernel.
+ *
+ * A bit is never cleared: one bit stands for a whole EFI_POISON_UNIT_SIZE, so
+ * an unpoison cannot tell whether the unit as a whole is good again.
+ */
+void efi_hwpoison_record_pfn(unsigned long pfn)
+{
+ long unit;
+
+ if (!efi_poison)
+ return;
+
+ unit = efi_poison_unit(pfn);
+ if (unit < 0)
+ return;
+
+ set_bit(unit, efi_poison->bitmap);
+}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index ce0980a5bb81b..f03b1bb576133 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1284,6 +1284,12 @@ struct linux_efi_poisoned_memory {
#define EFI_POISON_UNIT_SIZE SZ_2M
+#ifdef CONFIG_EFI_POISONED_MEMORY
+void efi_hwpoison_record_pfn(unsigned long pfn);
+#else
+static inline void efi_hwpoison_record_pfn(unsigned long pfn) { }
+#endif
+
void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size);
/*
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index aaf14608b30e2..357a72ffda625 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -43,6 +43,7 @@
#include <linux/sched/signal.h>
#include <linux/sched/task.h>
#include <linux/dax.h>
+#include <linux/efi.h>
#include <linux/ksm.h>
#include <linux/rmap.h>
#include <linux/export.h>
@@ -1286,6 +1287,8 @@ static int action_result(unsigned long pfn, enum mf_action_page_type type,
if (type != MF_MSG_ALREADY_POISONED && type != MF_MSG_PFN_MAP) {
num_poisoned_pages_inc(pfn);
update_per_node_mf_stats(pfn, result);
+ /* Only hard offlines are carried over to the next kernel. */
+ efi_hwpoison_record_pfn(pfn);
}
pr_err("%#lx: recovery action for %s: %s\n",
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 6/6] efi: respect the poisoned pages coming from previous kernel
2026-08-21 10:06 [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec Breno Leitao
` (4 preceding siblings ...)
2026-08-21 10:06 ` [PATCH v2 5/6] efi: record hardware-poisoned frames into the poisoned-memory table Breno Leitao
@ 2026-08-21 10:06 ` Breno Leitao
2026-08-21 12:13 ` Kiryl Shutsemau
2026-08-21 10:29 ` [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec Kiryl Shutsemau
6 siblings, 1 reply; 16+ messages in thread
From: Breno Leitao @ 2026-08-21 10:06 UTC (permalink / raw)
To: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton
Cc: linux-efi, linux-kernel, linux-mm, rmikey, kas, riel, kexec,
Breno Leitao, kernel-team
On kexec next kernel, walk the LINUX_EFI_POISONED_MEMORY bitmap during
EFI init -- before memblock hands memory to the buddy allocator -- and
memblock_reserve() every unit whose bit is set. So a frame poisoned
under a previous kernel is never handed back out across a kexec.
This runs from efi_config_parse_tables(), early enough to keep the
frames out of memblock and the buddy allocator.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/firmware/efi/efi.c | 2 ++
drivers/firmware/efi/poison.c | 71 +++++++++++++++++++++++++++++++++++++++++--
include/linux/efi.h | 2 ++
3 files changed, 73 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 111e60479211a..7474eeebb0add 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -812,6 +812,8 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
}
}
+ efi_reserve_poisoned_memory();
+
if (rt_prop != EFI_INVALID_TABLE_ADDR) {
efi_rt_properties_table_t *tbl;
diff --git a/drivers/firmware/efi/poison.c b/drivers/firmware/efi/poison.c
index 2f47293a4e45f..e418c6b3aab81 100644
--- a/drivers/firmware/efi/poison.c
+++ b/drivers/firmware/efi/poison.c
@@ -3,8 +3,9 @@
* Runtime handling for the LINUX_EFI_POISONED_MEMORY configuration table: a
* bitmap with one bit per EFI_POISON_UNIT_SIZE of physical memory that records
* hardware-poisoned frames so the next kexec kernel can keep them out of its
- * allocator. The stub installs the (empty) bitmap; this kernel sets bits at
- * runtime.
+ * allocator. The stub allocates and installs the (empty) bitmap; this kernel
+ * sets bits at runtime; the next kernel reserves the set units before the
+ * allocator comes up.
*
* Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
* Copyright (c) 2026 Breno Leitao <leitao@debian.org>
@@ -16,6 +17,8 @@
#include <linux/efi.h>
#include <linux/io.h>
#include <linux/log2.h>
+#include <linux/memblock.h>
+#include <linux/minmax.h>
#include <linux/mm.h>
#include <linux/overflow.h>
@@ -128,3 +131,67 @@ void efi_hwpoison_record_pfn(unsigned long pfn)
set_bit(unit, efi_poison->bitmap);
}
+
+void __init efi_reserve_poisoned_memory(void)
+{
+ u64 ppm = efi.poisoned_memory, phys_base, unit_size, bitmap_size, off;
+ struct linux_efi_poisoned_memory *pm;
+ unsigned int nr_units = 0;
+
+ if (ppm == EFI_INVALID_TABLE_ADDR)
+ return;
+
+ pm = early_memremap(ppm, sizeof(*pm));
+ if (!pm) {
+ pr_warn("Could not map poisoned-memory table\n");
+ return;
+ }
+
+ if (!efi_poison_table_valid(pm)) {
+ /* Keep the runtime side off a table this pass rejected. */
+ efi.poisoned_memory = EFI_INVALID_TABLE_ADDR;
+ early_memunmap(pm, sizeof(*pm));
+ return;
+ }
+
+ phys_base = pm->phys_base;
+ unit_size = pm->unit_size;
+ bitmap_size = pm->size;
+
+ early_memunmap(pm, sizeof(*pm));
+
+ /* Reserve the table itself so it survives a further kexec. */
+ memblock_reserve(PAGE_ALIGN_DOWN(ppm),
+ PAGE_ALIGN(ppm + sizeof(*pm) + bitmap_size) -
+ PAGE_ALIGN_DOWN(ppm));
+
+ /*
+ * Walk the bitmap a page at a time and reserve each poisoned unit.
+ * memblock.memory is not populated this early, so memblock_remove()
+ * and memblock_mark_nomap() would be no-ops; a reservation is what
+ * keeps the units away from the allocator.
+ */
+ for (off = 0; off < bitmap_size; off += PAGE_SIZE) {
+ u64 chunk = min_t(u64, PAGE_SIZE, bitmap_size - off);
+ unsigned long bit, nbits = chunk * BITS_PER_BYTE;
+ unsigned long *map;
+
+ map = early_memremap(ppm + offsetof(struct linux_efi_poisoned_memory,
+ bitmap) + off, chunk);
+ if (!map) {
+ pr_warn("Could not map poisoned-memory bitmap\n");
+ return;
+ }
+ for_each_set_bit(bit, map, nbits) {
+ u64 unit = off * BITS_PER_BYTE + bit;
+
+ memblock_reserve(phys_base + unit * unit_size, unit_size);
+ nr_units++;
+ }
+ early_memunmap(map, chunk);
+ }
+
+ if (nr_units)
+ pr_info("reserved %u poisoned unit(s) (%lluK each) inherited across kexec\n",
+ nr_units, unit_size >> 10);
+}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index f03b1bb576133..350a3cb0babdd 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1285,8 +1285,10 @@ struct linux_efi_poisoned_memory {
#define EFI_POISON_UNIT_SIZE SZ_2M
#ifdef CONFIG_EFI_POISONED_MEMORY
+void efi_reserve_poisoned_memory(void);
void efi_hwpoison_record_pfn(unsigned long pfn);
#else
+static inline void efi_reserve_poisoned_memory(void) { }
static inline void efi_hwpoison_record_pfn(unsigned long pfn) { }
#endif
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 6/6] efi: respect the poisoned pages coming from previous kernel
2026-08-21 10:06 ` [PATCH v2 6/6] efi: respect the poisoned pages coming from previous kernel Breno Leitao
@ 2026-08-21 12:13 ` Kiryl Shutsemau
2026-08-21 14:03 ` Breno Leitao
0 siblings, 1 reply; 16+ messages in thread
From: Kiryl Shutsemau @ 2026-08-21 12:13 UTC (permalink / raw)
To: Breno Leitao
Cc: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton, linux-efi, linux-kernel, linux-mm, rmikey, riel,
kexec, kernel-team
On Fri, Aug 21, 2026 at 03:06:06AM -0700, Breno Leitao wrote:
> + /* Reserve the table itself so it survives a further kexec. */
> + memblock_reserve(PAGE_ALIGN_DOWN(ppm),
> + PAGE_ALIGN(ppm + sizeof(*pm) + bitmap_size) -
> + PAGE_ALIGN_DOWN(ppm));
Hm. I don't think it is enough.
On x86, kernel doesn't keep memblock around after boot (see
CONFIG_ARCH_KEEP_MEMBLOCK). Reserving in memblock exclude the memory
from page allocator. But kexec can place the image there.
For !CONFIG_ARCH_KEEP_MEMBLOCK, kexec uses walk_system_ram_res() that
looks into iomem_resource. And memblock does nothing to exclude the
memory from iomem_resource.
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 6/6] efi: respect the poisoned pages coming from previous kernel
2026-08-21 12:13 ` Kiryl Shutsemau
@ 2026-08-21 14:03 ` Breno Leitao
2026-08-21 14:53 ` Kiryl Shutsemau
0 siblings, 1 reply; 16+ messages in thread
From: Breno Leitao @ 2026-08-21 14:03 UTC (permalink / raw)
To: Kiryl Shutsemau
Cc: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton, linux-efi, linux-kernel, linux-mm, rmikey, riel,
kexec, kernel-team
On Fri, Aug 21, 2026 at 01:13:55PM +0100, Kiryl Shutsemau wrote:
> On Fri, Aug 21, 2026 at 03:06:06AM -0700, Breno Leitao wrote:
> > + /* Reserve the table itself so it survives a further kexec. */
> > + memblock_reserve(PAGE_ALIGN_DOWN(ppm),
> > + PAGE_ALIGN(ppm + sizeof(*pm) + bitmap_size) -
> > + PAGE_ALIGN_DOWN(ppm));
>
> Hm. I don't think it is enough.
>
> On x86, kernel doesn't keep memblock around after boot (see
> CONFIG_ARCH_KEEP_MEMBLOCK). Reserving in memblock exclude the memory
> from page allocator. But kexec can place the image there.
You mean the third kexec?
1) Kernel A hits an ECC error and marks page X poisoned.
2) Kernel A kexecs into kernel B, which won't use that page since
it's in EFI_POISONED_PAGE and memblock-reserved.
3) Kernel B kexecs into kernel C, which doesn't respect
EFI_POISONED_PAGE.
Is this the scenario you mean?
If so, the config table, once installed, persists forever, right?
So kernel C will see the poisoned pages, but could still step into
one during the kexec itself?
> For !CONFIG_ARCH_KEEP_MEMBLOCK, kexec uses walk_system_ram_res() that
> looks into iomem_resource. And memblock does nothing to exclude the
> memory from iomem_resource.
Maybe we need something similar to "efi: mm/memory-failure: keep
hardware-poisoned pages out of the next kexec"[1], but checking
EFI_POISONED_PAGE instead?
Link: https://lore.kernel.org/all/20260812-kexec_posioned-v6-0-e477887086f0@debian.org/ [1]
Thanks for the review,
--breno
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 6/6] efi: respect the poisoned pages coming from previous kernel
2026-08-21 14:03 ` Breno Leitao
@ 2026-08-21 14:53 ` Kiryl Shutsemau
0 siblings, 0 replies; 16+ messages in thread
From: Kiryl Shutsemau @ 2026-08-21 14:53 UTC (permalink / raw)
To: Breno Leitao
Cc: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton, linux-efi, linux-kernel, linux-mm, rmikey, riel,
kexec, kernel-team
On Fri, Aug 21, 2026 at 07:03:06AM -0700, Breno Leitao wrote:
> On Fri, Aug 21, 2026 at 01:13:55PM +0100, Kiryl Shutsemau wrote:
> > On Fri, Aug 21, 2026 at 03:06:06AM -0700, Breno Leitao wrote:
> > > + /* Reserve the table itself so it survives a further kexec. */
> > > + memblock_reserve(PAGE_ALIGN_DOWN(ppm),
> > > + PAGE_ALIGN(ppm + sizeof(*pm) + bitmap_size) -
> > > + PAGE_ALIGN_DOWN(ppm));
> >
> > Hm. I don't think it is enough.
> >
> > On x86, kernel doesn't keep memblock around after boot (see
> > CONFIG_ARCH_KEEP_MEMBLOCK). Reserving in memblock exclude the memory
> > from page allocator. But kexec can place the image there.
>
> You mean the third kexec?
>
> 1) Kernel A hits an ECC error and marks page X poisoned.
> 2) Kernel A kexecs into kernel B, which won't use that page since
> it's in EFI_POISONED_PAGE and memblock-reserved.
> 3) Kernel B kexecs into kernel C, which doesn't respect
> EFI_POISONED_PAGE.
>
> Is this the scenario you mean?
No. I think nothing prevents kernel B from putting kernel C image into
the reserved space in !CONFIG_ARCH_KEEP_MEMBLOCK case.
Kernel B excludes poisoned memory from buddy allocator, but kexec
doesn't care about this when look for placement for the next image.
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec
2026-08-21 10:06 [PATCH v2 0/6] efi: mm/memory-failure: keep hardware-poisoned pages out of the next kexec Breno Leitao
` (5 preceding siblings ...)
2026-08-21 10:06 ` [PATCH v2 6/6] efi: respect the poisoned pages coming from previous kernel Breno Leitao
@ 2026-08-21 10:29 ` Kiryl Shutsemau
6 siblings, 0 replies; 16+ messages in thread
From: Kiryl Shutsemau @ 2026-08-21 10:29 UTC (permalink / raw)
To: Breno Leitao
Cc: Ard Biesheuvel, Ilias Apalodimas, Miaohe Lin, Naoya Horiguchi,
Andrew Morton, linux-efi, linux-kernel, linux-mm, rmikey, riel,
kexec, kernel-team
On Fri, Aug 21, 2026 at 03:06:00AM -0700, Breno Leitao wrote:
> The allocation is capped at 1MB, which covers 16TB at 2MB per bit. Past
> that the stub doubles the unit rather than growing the table, so the
> table stays bounded on any machine.
For unaccepted memory I didn't capped it. On x86, architectural maximum
is 4PB which would cost 256MB in the bitmap. It should be okay for the
scale.
> Memory hot-added after boot is not covered either: the bitmap spans the
> RAM the EFI memory map describes, and a frame outside it is silently not
> recorded.
We might update it if/when it gets solved for unaccepted memory.
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply [flat|nested] 16+ messages in thread