From nobody Fri Sep 25 19:20:31 2026 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 02ED355D882; Wed, 9 Sep 2026 13:05:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959162; cv=none; b=mKL1GDC0m88w76lIlF/zo6SzDPe1igSf2nu9uosDFDLTG2U5FpHXtpmqB/Wt9GrqWqfWzYqGlLHCjUIMdlxKZh/pqjUtRZkO98nCzvgaxSXiclp1vCiidpmRNGA9Me8Ik2RAGIMYX5+I11T7CyRr/Ni/2pl+0JMUqpyVAkMKJTE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959162; c=relaxed/simple; bh=ctiHNkWke05Q0iaiTEl4WUQNecfiVhJWf2TuHJC4Tno=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=h/59vp2c+R/x7GiKqHes2Ovh3Mk5AlGOyWYlpjjn28c96WAcIE6ZTv2qUY+B/iuS6A52/sobwGOOPAsZaJLvvJ+2EjrnX5Z6IR5aJphs8TQUdG8pHojJGxMJnscFzAvhLmIJBT7lzWj72v52ocT/OQed3zs25YLc7im3OUQWeUA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=DYC+Xp5c; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="DYC+Xp5c" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=fLp0/6osqzYu1K79RxqaODeJezxTORDI/HZWm8RTcnE=; b=DYC+Xp5c43ihxjxqgyBzUWuGvH OvrZVuEMqQGQgQaImD+gdQeUa310L9Q5m3jEu6y3giBi+QYcNJerc3cSPP/EXUXH2aL3FHh1g1vu7 nXF0SVYaltmLCxKobKfzZPIXB2MkY+opTT/GjLak9UQodGHA06dmqWww7s9HBInF/2iLj9IRiRk5i rPk1CFQ2JItFsGKL6o1cc+iZhw0n2hXEc2j6IBjMzdc/3pJhdN/DDqH6RHEGlHVFCp01gIChxSla3 pgDo4yFsvC5V8KnQsJ+KGz29tvzDm7/UszGGAJSPzGy9TyKnU2D3hb1nkfTdRH/IddFY8+T0r752W LYudlCfg==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1x4Hzh-0034dQ-32; Wed, 09 Sep 2026 13:05:42 +0000 From: Breno Leitao Date: Wed, 09 Sep 2026 06:05:20 -0700 Subject: [PATCH v4 1/5] mm/memory-failure: efi: add the LINUX_EFI_POISONED_MEMORY configuration table Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-hwpoison-kho-v4-1-359313564495@debian.org> References: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> In-Reply-To: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> To: Ard Biesheuvel , Ilias Apalodimas , Miaohe Lin , Naoya Horiguchi , Andrew Morton , kas@kernel.org, kexec@lists.infradead.org, David Hildenbrand , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Brendan Jackman , Johannes Weiner , Zi Yan Cc: linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rmikey@meta.com, riel@surriel.com, Breno Leitao , harry@kernel.org, kernel-team@meta.com X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=5629; i=leitao@debian.org; h=from:subject:message-id; bh=ctiHNkWke05Q0iaiTEl4WUQNecfiVhJWf2TuHJC4Tno=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqoVmYv6/F2rp5R0pqzLMUowHz37sQM9qWNR5x2 UuJzfxpaNuJAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCaqFZmAAKCRA1o5Of/Hh3 bfNHD/0apJ2TYY/PkRtgB2eQI58aaL6LZZSdIvSHzd5ZQG3H/NgtttpYjBPyQ5g3FNs0bGo7NEM 9+zRqvaatJOs8qOLNakhUerv/PJNYpZKHji0Nm/qok+8Qk8eSVwyC1iCg6oNwYN09a4TbMb8wc7 3kZgwEQYBtEylUQ42+a4P0TXQgCzIFk55ZH1vVM/5vcLrTENg5qa8n6OnbbCyDVwZumfiLd2/3F Rd7olMYEJKE92ZCWRP1cGXohvSGxUfJA1DplypSNwcusEsFSwV7YVc+rlBKpOvby/KYt7b5Jcw5 JvF2ewSM2f576xh6R4pTenjcZVcBfBji8d086jQ7EFqkLCIDQsU6QAo15LQZYCJvHuTD4CbYFuo 9ykGUChQq9HfUxGzVyuZEcZZK1DcVjWv8uJHTK8W5I63pLBt2RdSdrRhT8rYg8wMjdJ2NPyu7rN 8qSHc5s1e/adnS6JBLkK0We5xHNaFlber0z3gULBMD5D2W1b7xn81PwynMwtrrNIRsdcKPcCOmi IL9oFYTZRoU7WOIkNLwRPrphx3cpsL9L7zReLWaVzt2uXigRk+SajgHza5wMMt9akA3ljDpFcvR llSWuV9hAWlbz0k7nbB1c0MGCa6qo31FFQLEKKmrkbFnZBp1N7E2XzF5grQOcVnEIBud5sNjtjk ZhRCZ8I8nSt2Wpw== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao 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 starting at phys_base, modeled on the LINUX_EFI_UNACCEPTED_MEMORY table, and it rides the EFI system table to every kernel in the chain. Basing the bitmap keeps a machine whose RAM starts high from paying for the hole below it. List the table in the x86 efi_tables[] too, so an SME host maps it unencrypted like every other EFI table. The Kconfig symbol has no prompt. There is nothing for a user to decide, so it is on wherever it can be, and it only costs 64K per TiB of RAM on a kernel that already has the EFI stub and MEMORY_FAILURE. It is restricted to 64BIT because the unit arithmetic would need div_u64() on 32-bit, and there is no 32-bit EFI configuration with MEMORY_FAILURE to test that on. This is a similar approach as used as unaccepted memory. Suggested-by: Kiryl Shutsemau Signed-off-by: Breno Leitao --- arch/x86/platform/efi/efi.c | 3 +++ drivers/firmware/efi/Kconfig | 8 ++++++++ drivers/firmware/efi/efi.c | 6 ++++++ include/linux/efi.h | 14 ++++++++++++++ 4 files changed, 31 insertions(+) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 0c39adb96b912..2b37b96a36e09 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -93,6 +93,9 @@ static const unsigned long * const efi_tables[] =3D { #ifdef CONFIG_UNACCEPTED_MEMORY &efi.unaccepted, #endif +#ifdef CONFIG_EFI_POISONED_MEMORY + &efi.poisoned_memory, +#endif }; =20 u64 efi_setup; /* efi setup_data physical address */ diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 29e0729299f5b..aafcd41bc0063 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -263,6 +263,14 @@ config EFI_COCO_SECRET virt/coco/efi_secret module to access the secrets, which in turn allows userspace programs to access the injected secrets. =20 +config EFI_POISONED_MEMORY + def_bool y + 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. + 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 6d987d7f97781..af1fa443839c4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -55,6 +55,9 @@ struct efi __read_mostly efi =3D { #ifdef CONFIG_UNACCEPTED_MEMORY .unaccepted =3D EFI_INVALID_TABLE_ADDR, #endif +#ifdef CONFIG_EFI_POISONED_MEMORY + .poisoned_memory =3D EFI_INVALID_TABLE_ADDR, +#endif }; EXPORT_SYMBOL(efi); =20 @@ -677,6 +680,9 @@ static const efi_config_table_type_t common_tables[] __= initconst =3D { #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 c35446a0b66fa..efaf63f9a54ed 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -23,6 +23,7 @@ #include #include #include +#include #include =20 #include @@ -422,6 +423,7 @@ void efi_native_runtime_setup(void); #define LINUX_EFI_COCO_SECRET_AREA_GUID EFI_GUID(0xadf956ad, 0xe98c, 0x48= 4c, 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, 0= x44f9, 0x9e, 0xa9, 0xbc, 0xef, 0x98, 0x12, 0x00, 0x31) +#define LINUX_EFI_POISONED_MEMORY_TABLE_GUID EFI_GUID(0xaf828a15, 0x0ef4, = 0x439a, 0xb8, 0x6a, 0xd6, 0xd6, 0x9e, 0xaf, 0xba, 0xfa) =20 #define RISCV_EFI_BOOT_PROTOCOL_GUID EFI_GUID(0xccd15fec, 0x6f73, 0x4eec,= 0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf) =20 @@ -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 */ =20 efi_get_time_t *get_time; efi_set_time_t *set_time; @@ -1272,6 +1275,17 @@ struct linux_efi_memreserve { #define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memr= eserve)) \ / sizeof_field(struct linux_efi_memreserve, entry[0])) =20 +/* Bit N covers the unit at @phys_base + N * @unit_size. */ +struct linux_efi_poisoned_memory { + u32 version; + u32 unit_size; /* bytes of phys space per bitmap bit */ + u64 phys_base; /* address the first bit covers */ + 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); =20 /* --=20 2.53.0-Meta From nobody Fri Sep 25 19:20:31 2026 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 073E3242D65; Wed, 9 Sep 2026 13:05:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959164; cv=none; b=j/MXdJJTu7zmEamDRYVdn8QJhGARHlALyBeidA4Ks+MzqMoMWDpWHW7qjfR0R2JgYlTdUL74AuXlMLbqTsVs0OWWj+nRufyJy8xlMJVNw/2qnIcKoirJ+xUgvsoZsxEKTVO7yDI82va80zosWlNE1HXB3mNJdAwCN9x3t+SgVoo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959164; c=relaxed/simple; bh=05MyTSGHE/o1BoN7JT8ZUU1a9DWMs95W/eAkbNG9wbw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FzRY7PZAxfv1Z9aU4g1yhd5s0wWqVyT8RFNAmKrmr/mzUKb+VAyBWYxnk8U+sRppxne0YvEI1d3vynrSkQyKHYQ0WzCWGIDaaX2K+zd6S1dlMF2omeMwQlCexOD7lnpJLDbgQze3bgSCKZbwVDUHx66ZgclBwooo8XDnNGwW48I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=tnpVQ2I/; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="tnpVQ2I/" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=g8SaUqrPyh2Bv1Oc+EsXzswvobS611/PIEK4DTAvYjU=; b=tnpVQ2I/ZDmLTviYSnpv64WTuk 5iOP2Gea5kpJUxZwHvdX2chpJH/neq3K+2apfI2vQtBs0dcb69DDGW4GEaSzLoYkpI3WgxIr5hQWk kFZvQ+iIsKz7YmY4TY7cO1nNRTBicm5AHWl0ip8S08SRfE5qBr/jEwLoXIDmqts1GESuJg89S2PZj klWg+s1MeAnkb+L9km/Qc98OFKA8lombXdQG/GGR4HJO3/WyNv8SXAKwLzkRtVPm6yge5fFa7mdE+ mTzf8ad3ClkTE1gveUm6FcxVYjuTx03KNVHhZltnA3sj3W+/dNjicltZaJs858d84VZNRljsIOsUW UOISwFzw==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1x4Hzo-0034dV-28; Wed, 09 Sep 2026 13:05:49 +0000 From: Breno Leitao Date: Wed, 09 Sep 2026 06:05:21 -0700 Subject: [PATCH v4 2/5] mm/memory-failure: libstub: install the poisoned-memory EFI table Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-hwpoison-kho-v4-2-359313564495@debian.org> References: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> In-Reply-To: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> To: Ard Biesheuvel , Ilias Apalodimas , Miaohe Lin , Naoya Horiguchi , Andrew Morton , kas@kernel.org, kexec@lists.infradead.org, David Hildenbrand , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Brendan Jackman , Johannes Weiner , Zi Yan Cc: linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rmikey@meta.com, riel@surriel.com, Breno Leitao , harry@kernel.org, kernel-team@meta.com X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=6886; i=leitao@debian.org; h=from:subject:message-id; bh=05MyTSGHE/o1BoN7JT8ZUU1a9DWMs95W/eAkbNG9wbw=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqoVmYWfxLsAK34UQi8yLrAWOt4rdGV5oD4DhJ3 IcnxPiESsiJAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCaqFZmAAKCRA1o5Of/Hh3 bSimEACahq1zxDH2xwtJpY6LTrQ/oIuZKPmJl8R1Vk+GerIev1DJU2oHC1f41ZRsxGGeLUaQ0gu kffzmDr96qzvZdMzEIr9USYRZiblIw4iC44rUB1gLhAJ2n75gh1MeVoDHHAvHHzuxbb2WyW6Urp wNVsqRaeGTPydnPjhI9kEXhNtqI9APjXvJnKaQHJrr54siJvNTnRMxXFG77dz6/4yZFLUMxgeMy eZVvcV1BqbhV4rpdH7xMmo7VbQjl40slz2HUaMXmHYrJkFfpFSsHstWDqIEG0MTy0oziVttbtBE ZqNdsP4tp8w1zUekqITA/wTimxCoaCEy3+HhjeDOgIpjUSlrxTLv8Y7GEVnQvdXKvOhKT6eFkee da5bl6HXPvDmDJc48UhYoEcd9ezb2WLn/YLBnEmyPu7JJrYMPChMxWRJj38+gvZ4yDKXAqgR6Ta X/5g+oBLHUTqqOKKceE5QQWbaFg9OawPCAUsN4fzr4TM8FUTQ+tQ9OcBBj9mCs3UmOxlKy3lvr3 zquWHz69oOstdGIiFevVvMJygRRgYi1P2bfD+ETLcpWU3jikpFvTkH7UM3bqapNGOJd8uInGXZ4 QlicDmR0xMkF4E/0O8gU6Kti+92Pk1ZGLGJqHgKC+eOvNhNYgFMiY16RnayZ/QiYJas8vxjS+sO JSb9+lYP0FlVjfQ== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao 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 they do not agree on what becomes RAM: x86 decides by descriptor type, arm64 by attribute. So efi_get_ram_range() does not filter at all and spans every descriptor in the map. Sizing wide only costs bitmap bytes; sizing narrow silently drops the records for every frame outside the span. 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. 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 --- drivers/firmware/efi/libstub/efi-stub-helper.c | 100 +++++++++++++++++++++= ++++ 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, 109 insertions(+) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmw= are/efi/libstub/efi-stub-helper.c index 48f93f7758e9e..5cbe675491333 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -774,3 +774,103 @@ void efi_remap_image(unsigned long image_base, unsign= ed 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) =3D NULL; + u64 ram_base =3D ULLONG_MAX, ram_top =3D 0; + efi_status_t status; + int i, nr_desc; + + status =3D efi_get_memory_map(&map, false); + if (status !=3D EFI_SUCCESS) + return status; + + nr_desc =3D map->map_size / map->desc_size; + for (i =3D 0; i < nr_desc; i++) { + efi_memory_desc_t *d; + + d =3D efi_memdesc_ptr((unsigned long)map->map, map->desc_size, i); + ram_base =3D min(ram_base, d->phys_addr); + ram_top =3D max(ram_top, + d->phys_addr + d->num_pages * EFI_PAGE_SIZE); + } + if (!ram_top || ram_base =3D=3D ULLONG_MAX) + return EFI_NOT_FOUND; + + *base =3D round_down(ram_base, EFI_POISON_UNIT_SIZE); + *top =3D 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 =3D 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 =3D efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, + sizeof(*pm) + bitmap_size, (void **)&pm); + if (status !=3D EFI_SUCCESS) + return NULL; + + pm->version =3D 1; + pm->unit_size =3D EFI_POISON_UNIT_SIZE; + pm->phys_base =3D phys_base; + pm->size =3D 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 =3D 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 kex= ec. */ + pm =3D get_efi_config_table(poisoned_memory_table_guid); + if (pm) { + if (pm->version !=3D 1) + efi_err("Unknown version of poisoned-memory table\n"); + return; + } + + if (efi_get_ram_range(&ram_base, &ram_top) !=3D EFI_SUCCESS) { + efi_err("Failed to size the poisoned-memory table!\n"); + return; + } + + bitmap_size =3D efi_poison_bitmap_size(ram_top - ram_base); + pm =3D efi_poison_alloc(ram_base, bitmap_size); + if (!pm) { + efi_err("Failed to allocate poisoned-memory table!\n"); + return; + } + + status =3D efi_bs_call(install_configuration_table, + &poisoned_memory_table_guid, pm); + if (status !=3D 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 235c9738da2d6..22a315e2814a1 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); =20 install_memreserve_table(); + install_poisoned_memory_table(); =20 status =3D efi_boot_kernel(handle, image, image_addr, cmdline_ptr); =20 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) { } =20 void efi_retrieve_eventlog(void); =20 +#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 0bae0f06b6763..3136132b9628a 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, =20 setup_unaccepted_memory(); =20 + install_poisoned_memory_table(); + status =3D exit_boot(boot_params, handle); if (status !=3D EFI_SUCCESS) { efi_err("exit_boot() failed!\n"); --=20 2.53.0-Meta From nobody Fri Sep 25 19:20:31 2026 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1D32C559CA7; Wed, 9 Sep 2026 13:06:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959167; cv=none; b=GitWdaOu+lfhH0qwC0cJYSloDy3EsT6+pZD72B2hf/Nr4F/2OzNvTp9tBrCZdyHywEIB9K9qaDLGV8WtoHF5FJA9BflINfnDQsxOhxd3czq1I52K/hwjhOtO4FdYYAj5LhYwGEkZOvbUVjdUJKH/u1aoC5em7r4YjlViiyk2Rqk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959167; c=relaxed/simple; bh=QzxY6swGj4uClWFDi/b7LYNR3LsUHGQKrkxuPAwiI/A=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HqAGhcsk5QNzLVidxHDOrRVlpM3i1+aFyS7bTiWonVOyVlwLjgIFnmnxMRBQ3nXRme6GjjPfyAy+I/4nGWXVnDKWA9j9YZJyM8cvh+52X9yreRfzqvX+XSwBAE/40ch/Z/py/vcgmifKZrJOPQ2lQZGuHzbL5Fp8Q7tHSoTF/rQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=h4aPQMF0; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="h4aPQMF0" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=PemFBMviso9wUk51x5UQAGvSeZzXgVpZ1oI2Ix7beok=; b=h4aPQMF0TgBykrGoPNAWU/sfP1 HKgkZpOnAJ1rQeQWmsaRyU1t9hzIN30Pm7NOJJxu2QtTGxvj4Su+CvgJ5S7Vhq0H9wozAegm3QIBL YL+kR/j2+zqdwQ2tfHCcNJYQ8kon7SuAfHiTxxVUzxepPvCY56NhyPHIUABjwM29ngLq/icWz8lCq XtMgnUoteYawUU1GZ7TqtXnka0m/T2g+72a5Kqnxyclcw6UUaVIl0dnPGaXexjGd2cUnnQRs50GYm RcDEM0lZ/XZHXajn8KbIqujRqrSspBlIfLhhLT3btevmvVoyaKUS4dOhY/wRz4B6sNHzNXPSqKrmc aQ2JlOXw==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1x4Hzv-0034da-0I; Wed, 09 Sep 2026 13:05:55 +0000 From: Breno Leitao Date: Wed, 09 Sep 2026 06:05:22 -0700 Subject: [PATCH v4 3/5] mm/memory-failure: efi: record hardware-poisoned frames into the poisoned-memory table Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-hwpoison-kho-v4-3-359313564495@debian.org> References: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> In-Reply-To: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> To: Ard Biesheuvel , Ilias Apalodimas , Miaohe Lin , Naoya Horiguchi , Andrew Morton , kas@kernel.org, kexec@lists.infradead.org, David Hildenbrand , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Brendan Jackman , Johannes Weiner , Zi Yan Cc: linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rmikey@meta.com, riel@surriel.com, Breno Leitao , harry@kernel.org, kernel-team@meta.com X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=7468; i=leitao@debian.org; h=from:subject:message-id; bh=QzxY6swGj4uClWFDi/b7LYNR3LsUHGQKrkxuPAwiI/A=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqoVmYxYrjNedwZha81oWfKnvUlWIrUR+ttXbdT l5XHuOQ65KJAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCaqFZmAAKCRA1o5Of/Hh3 bQIiD/47XBTdySzJJtvFna4DTxdL7fCIOx+d+ZkNqTDRMWLNAkMtNH9hyKBgmwpCEUXbmuDW57i 7z5iZz3wMWHsnim7Shcm5b75pz4sKnBSN/YenYNaa+WjyYj11hyTCMB4+ZqAQpABLU93O1fBgis Mydvnr+Md7jm7iHdBRsAObubUES2rX0s/h20ymBfdnz7DuonN1WctKkEe3JwKmY7YIIfk0Z2k8R HR3qOKtzWE9G/ZweGOGrIRGspOXrt5uRSVhLf7ej8NRQKURmbzTF68LoEcYTu7KZG/+PiOSBGfC JT6A8W+LdXIqLP5n5BvQzF5cwjCBxf4M9sF5zzQccNbtzk/x5rF5I1kc949gpsvVNuL693ENllb QAZDuboAsYoW8PuxrxGP+8m8CwICLtrlyrgcm0PUI59jq7FpcRDhG9va5FoynqT+FM5ObtPZnEf 81wXL6ZTltDpi56YaqzeFXjTCTw4tBL+5un5LXX8gu3lpk1BoHK2YfJFYVAftUGOBFJrI+59fR2 63x1A5FmxRmHzjfJLbZctxjIYh/m+z7bbV6eRJLfkyBDw5p4TwZcAsmioHUB1tTYnw1hgDv52c9 SSwivX1YNiKoopoAPgwYMZN4tjrabbItkpMi3cIcq0ZXpeBJ9HsujSYKherKxP+x4sIHNJJdJQq mxF94JC+1ZEEA3Q== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao 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. The table is EFI ACPI reclaim memory, which becomes E820_TYPE_ACPI and so reaches neither memblock nor the direct map; touching it then faults. Hand its pages to memblock from efi_config_parse_tables() the way the unaccepted memory table already does, and vet the inherited header in the same pass, so everything afterwards can reach a table it can trust with phys_to_virt(). memory_failure() has already taken the frame out of this kernel's allocator, so only the cross-kexec record happens here. Signed-off-by: Breno Leitao --- drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/efi.c | 2 + drivers/firmware/efi/poison.c | 115 ++++++++++++++++++++++++++++++++++++++= ++++ include/linux/efi.h | 8 +++ mm/memory-failure.c | 3 ++ 5 files changed, 129 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) +=3D earlycon.o obj-$(CONFIG_UEFI_CPER_ARM) +=3D cper-arm.o obj-$(CONFIG_UEFI_CPER_X86) +=3D cper-x86.o obj-$(CONFIG_UNACCEPTED_MEMORY) +=3D unaccepted_memory.o +obj-$(CONFIG_EFI_POISONED_MEMORY) +=3D poison.o obj-$(CONFIG_TEE_STMM_EFI) +=3D stmm/tee_stmm_efi.o diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index af1fa443839c4..55b2ee53fc268 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -883,6 +883,8 @@ int __init efi_config_parse_tables(const efi_config_tab= le_t *config_tables, } } =20 + efi_poisoned_memory_reserve(); + return 0; } =20 diff --git a/drivers/firmware/efi/poison.c b/drivers/firmware/efi/poison.c new file mode 100644 index 0000000000000..c18edf111c710 --- /dev/null +++ b/drivers/firmware/efi/poison.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Runtime side of the LINUX_EFI_POISONED_MEMORY table: one bit per + * EFI_POISON_UNIT_SIZE, set here as frames go bad, honored by the next ke= rnel. + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * Copyright (c) 2026 Breno Leitao + */ + +#define pr_fmt(fmt) "efi: " fmt + +#include +#include +#include +#include +#include +#include +#include + +static bool __init +efi_poison_geometry_valid(const struct linux_efi_poisoned_memory *pm) +{ + u64 nbits; + + /* Whole words, and a bit count that can be taken without wrapping. */ + if (!pm->size || !IS_ALIGNED(pm->size, sizeof(unsigned long)) || + check_mul_overflow(pm->size, (u64)BITS_PER_BYTE, &nbits)) + 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 table may come from an earlier kernel, so vet it before using it. */ +static bool __init +efi_poison_table_valid(const struct linux_efi_poisoned_memory *pm) +{ + if (pm->version !=3D 1) { + pr_warn("Ignoring poisoned-memory table with version %u\n", + pm->version); + return false; + } + + if (!efi_poison_geometry_valid(pm)) { + pr_warn("Ignoring malformed poisoned-memory table\n"); + return false; + } + + return true; +} + +/* + * Vet the inherited table and hand its pages to memblock, the way the + * unaccepted memory table is handled. It is EFI ACPI reclaim memory, which + * becomes E820_TYPE_ACPI and would otherwise stay out of the direct map, = and + * touching it then faults. Called from efi_config_parse_tables(), so + * everything later can reach it with efi_poisoned_memory(). + */ +void __init efi_poisoned_memory_reserve(void) +{ + struct linux_efi_poisoned_memory *pm; + phys_addr_t start, end; + + if (efi.poisoned_memory =3D=3D EFI_INVALID_TABLE_ADDR) + return; + + pm =3D early_memremap(efi.poisoned_memory, sizeof(*pm)); + if (!pm) { + pr_warn("Could not map poisoned-memory table\n"); + efi.poisoned_memory =3D EFI_INVALID_TABLE_ADDR; + return; + } + + if (!efi_poison_table_valid(pm)) { + efi.poisoned_memory =3D EFI_INVALID_TABLE_ADDR; + early_memunmap(pm, sizeof(*pm)); + return; + } + + start =3D PAGE_ALIGN_DOWN(efi.poisoned_memory); + end =3D PAGE_ALIGN(efi.poisoned_memory + sizeof(*pm) + pm->size); + early_memunmap(pm, sizeof(*pm)); + + memblock_add(start, end - start); + memblock_reserve(start, end - start); +} + +/* The table, vetted at parse time, or NULL if this boot has none. */ +static struct linux_efi_poisoned_memory *efi_poisoned_memory(void) +{ + if (efi.poisoned_memory =3D=3D EFI_INVALID_TABLE_ADDR) + return NULL; + + return phys_to_virt(efi.poisoned_memory); +} + +/* + * A bit is never cleared: it 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) +{ + struct linux_efi_poisoned_memory *pm =3D efi_poisoned_memory(); + phys_addr_t addr =3D PFN_PHYS(pfn); + u64 unit; + + if (!pm || addr < pm->phys_base) + return; + + unit =3D (addr - pm->phys_base) / pm->unit_size; + if (unit < pm->size * BITS_PER_BYTE) + set_bit(unit, pm->bitmap); +} diff --git a/include/linux/efi.h b/include/linux/efi.h index efaf63f9a54ed..56402fdccd114 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1286,6 +1286,14 @@ struct linux_efi_poisoned_memory { =20 #define EFI_POISON_UNIT_SIZE SZ_2M =20 +#ifdef CONFIG_EFI_POISONED_MEMORY +void __init efi_poisoned_memory_reserve(void); +void efi_hwpoison_record_pfn(unsigned long pfn); +#else +static inline void efi_poisoned_memory_reserve(void) { } +static inline void efi_hwpoison_record_pfn(unsigned long pfn) { } +#endif + void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size); =20 /* diff --git a/mm/memory-failure.c b/mm/memory-failure.c index a2ca8df501cae..d9b8be696aac3 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -1326,6 +1327,8 @@ static int action_result(unsigned long pfn, enum mf_a= ction_page_type type, if (type !=3D MF_MSG_ALREADY_POISONED && type !=3D 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); } =20 pr_err("%#lx: recovery action for %s: %s\n", --=20 2.53.0-Meta From nobody Fri Sep 25 19:20:31 2026 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 19DB655C300; Wed, 9 Sep 2026 13:06:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959172; cv=none; b=bWHumFcWAGPMfQ6be+PpPJ2BG2/RowUhSq4rqySyYcZ5jKqi83Hhyebu5LHjitTjAP+MPBPt+aYXKuuzjOZv+L0p1/GvIzWPG19oSRttmPYzN/SfSjcZzuoxHnzI1gOF4Wa1XT9UjqmOtv2tCzJoHS+Bk8dkUCbblUu/+dlHp+k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959172; c=relaxed/simple; bh=MZnll+uvZ+yY/oMfw6DSCIEir7AmYXLDgRkRphbIGv4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YPDZf983PNJ6bDGz42ezQuUG3vHzwEh0tFGY+XbwjLs+pJhU4FtwjKHO2SN+KI7UAakC7t0TV9C3w96Ll3fPGOxckW0zTyu9FpncaefPLYQjo5Lz3U7F5d+nAqirbaOopyIx8stTr/KKHLsPmHpFoFJswghAgWNOftGPHcZ9Dvk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=tHwGSDg5; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="tHwGSDg5" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=1pT0yQbwDkGLyzj6/qFjpw66FqU9lPeWqHePe181JIU=; b=tHwGSDg54ByEfThjAj+t4YPGlr oZmW25qQh71QWoYRoNFnzPGiZ7q1hBwUaG+br9xCsqnjXZgWt9jDLMbCznjOdgY/aUJtWObxG19tc 57pIOs3bOeCLjdjYp7mt1vGNJqYKLnTr4bvNjJ4qSI5cOHCjAfJV0TTzXw0QuSuwhvUEanuY/EO0Z gIty+yaHouxyyAAef0FkMQqQmyrgMIkV3J150bH8PsLwLDqD69gBr5kcANXmYdvuoq3/calpWgVbg Ph5CZerJW2HE44JYcZe/99Zq3hCIIn98LrF0r23FwAoWI90mngX1Sw0jW3dyS/h+irw0xsHwRE3eu Yuvt+Xuw==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1x4I01-0034e0-0s; Wed, 09 Sep 2026 13:06:01 +0000 From: Breno Leitao Date: Wed, 09 Sep 2026 06:05:23 -0700 Subject: [PATCH v4 4/5] mm/memory-failure: efi: answer whether a range is poisoned Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-hwpoison-kho-v4-4-359313564495@debian.org> References: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> In-Reply-To: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> To: Ard Biesheuvel , Ilias Apalodimas , Miaohe Lin , Naoya Horiguchi , Andrew Morton , kas@kernel.org, kexec@lists.infradead.org, David Hildenbrand , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Brendan Jackman , Johannes Weiner , Zi Yan Cc: linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rmikey@meta.com, riel@surriel.com, Breno Leitao , harry@kernel.org, kernel-team@meta.com X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=2349; i=leitao@debian.org; h=from:subject:message-id; bh=MZnll+uvZ+yY/oMfw6DSCIEir7AmYXLDgRkRphbIGv4=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqoVmYSlBarC3RkQk9eIxE1kitJWwdSYMD89hTK 8h7doIqSa+JAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCaqFZmAAKCRA1o5Of/Hh3 bY5bD/9F7YKH2OjzTN+a/S5ygVJPXHtamJZIQ+TnnkmEvwQmdlVoyS4CRJSxclq4Js03mxJyva0 qhkZaMKfbHM0I5I9MER8lgxRzomsl+gzHs4/0WCQuJNH0izWGCn3nSFQsfguePpLlyMnXCzwHdj OE4IIsvyIgy0jn0aqNW4FzYrHy/zw27sAi7SQoMkQT/4sVSucXjhAHgY1vDHrFiKcruHugMDjx1 o2ai+946IwYpayMnLy82CHJqnOuvKU9mJNF1jgscp5PMnlWGFe5xaQ8Ctf2RoBsmmc75qEDTqVp shQ9UHRely46WbP95/46g2Nqtjb4rO0KudGUsNOnHJLefz5wXo8yHfC1dWjWpfhxa67AWwzQCG7 Poy+diIt1+VZgMuAsYIu35h0o1YFdJmebQ4ve2XTHtMZlpHNvk7PiTcLrtXMBA8mQ7QLQTz8rdo 3PE8mA5cpqkbWm9Ue7ItkSJVgVDhZEVuKOvZ3+kx6l78/rJ6mRE3DTJRx4lAEc6d9OtS1P4XwgG X6E0a07eLOzD7PipCfzttdg7aeJtDOsiG5PuhdRrO1f+bCZYKtHMjvWEeQKg2v4zaJ6G8v26V1U ruxQhN7Zw+LCBdD6EcL9Ch73V4T8fMVuDXXBpIfncOP01zdbAtYOkWCs1H9GyH6crGJgAlKAl+H GVLmgr7mIpZIA0w== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao The bitmap an earlier kernel filled in gets in this one through EFI, but nothing reads it back yet. Introduce range_contains_poisoned_memory(), which the page allocator will use to ask about a block before handing it out. Signed-off-by: Breno Leitao --- drivers/firmware/efi/poison.c | 26 ++++++++++++++++++++++++++ include/linux/mm.h | 14 ++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/drivers/firmware/efi/poison.c b/drivers/firmware/efi/poison.c index c18edf111c710..e16d43f4438ee 100644 --- a/drivers/firmware/efi/poison.c +++ b/drivers/firmware/efi/poison.c @@ -96,6 +96,32 @@ static struct linux_efi_poisoned_memory *efi_poisoned_me= mory(void) return phys_to_virt(efi.poisoned_memory); } =20 +/* Does the range cover a unit an earlier kernel recorded as bad? */ +bool range_contains_poisoned_memory(phys_addr_t start, unsigned long size) +{ + struct linux_efi_poisoned_memory *pm =3D efi_poisoned_memory(); + u64 first, last, nbits; + + if (!pm) + return false; + + nbits =3D pm->size * BITS_PER_BYTE; + + if (start + size <=3D pm->phys_base) + return false; + if (start < pm->phys_base) + start =3D pm->phys_base; + + first =3D (start - pm->phys_base) / pm->unit_size; + if (first >=3D nbits) + return false; + + last =3D (start + size - 1 - pm->phys_base) / pm->unit_size; + last =3D min(last, nbits - 1); + + return find_next_bit(pm->bitmap, last + 1, first) <=3D last; +} + /* * A bit is never cleared: it stands for a whole EFI_POISON_UNIT_SIZE, so = an * unpoison cannot tell whether the unit as a whole is good again. diff --git a/include/linux/mm.h b/include/linux/mm.h index 274fa880077c5..b68824fcfbef1 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -5387,6 +5387,20 @@ static inline bool pfn_is_unaccepted_memory(unsigned= long pfn) return range_contains_unaccepted_memory(pfn << PAGE_SHIFT, PAGE_SIZE); } =20 +#ifdef CONFIG_EFI_POISONED_MEMORY + +bool range_contains_poisoned_memory(phys_addr_t start, unsigned long size); + +#else + +static inline bool range_contains_poisoned_memory(phys_addr_t start, + unsigned long size) +{ + return false; +} + +#endif + void vma_pgtable_walk_begin(struct vm_area_struct *vma); void vma_pgtable_walk_end(struct vm_area_struct *vma); =20 --=20 2.53.0-Meta From nobody Fri Sep 25 19:20:31 2026 Received: from stravinsky.debian.org (stravinsky.debian.org [82.195.75.108]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1B77654B1CB; Wed, 9 Sep 2026 13:06:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=82.195.75.108 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959176; cv=none; b=pz4YU1EXbu6WNOkd4+/g0GeNmu4YKKGBvTuy2zyi9gbCJlnnQ4+7Tg+zpaKfcqyW/ROgBCmDfMxd3urtZs64xEUQKlLiN5szIKDaSMlav4TQAzPTYFnXG0HV3MdjDIviSMuk3HTSpP2vx4LmEJiT5xB/TBAsKGx1oYVoPOW4S1E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788959176; c=relaxed/simple; bh=drOYIVpoY7AQaUepXy25YegitABVihsyPIT5xNKQkkg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=TmcB3lajkyPyGK0fU2LBHcCedI/mpSuMlBwfpjTPaqQrMjeyb9Pi2eiki0DqE4J+11l+zpxtdCul0K05iebrzF/wE4mnj1JP9VGf9WZe/BNJKvKWffohddlCAbhwFfG1e9hk7FNdyKWHxrHoRiUifI1QYy1qGE5ySBUVkmxwAyk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org; spf=pass smtp.mailfrom=debian.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b=Emhkd0c+; arc=none smtp.client-ip=82.195.75.108 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=debian.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=debian.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=debian.org header.i=@debian.org header.b="Emhkd0c+" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debian.org; s=smtpauto.stravinsky; h=X-Debian-User:Cc:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description; bh=S6tS1Yb/K4/AvrXAKqpEiR5JcZF8qOSHzE/V/IVZb4Y=; b=Emhkd0c+jSvCqWB2hHCtjCVesP UhfV/ilBH7u6HeVo0QjEBjWeAvWtB/am1I2Jjzvp5kmD0JgYOiuty/biWlGwPK3JjqvNwcBieI1gA cMWauvnYu0O6U0YiCAm0GrwtzfVJpG7CL4YlS8GaCBmvb3XLbL0W8jufZc6w0mOOFioSmPHk6K2KL 51FnXQUStPA2WHw044vrjr5Be+QO6o+eDCsadF8z0qyZuZLDBZzdTfDR9VgA/uzeibE+bgEoTbj5U +PGhdfjNDrKihMdF6WQdc5wMDt+gvq47Mhi+Jwl+/N5IoQWOASSemJ6jJ7sp3IU5365R+yW0Mxvx4 plHr9DKw==; Received: from authenticated-user by stravinsky.debian.org with esmtpsa (TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.96) (envelope-from ) id 1x4I07-0034eB-1Y; Wed, 09 Sep 2026 13:06:07 +0000 From: Breno Leitao Date: Wed, 09 Sep 2026 06:05:24 -0700 Subject: [PATCH v4 5/5] mm/memory-failure: keep inherited poisoned frames out of the buddy allocator Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-hwpoison-kho-v4-5-359313564495@debian.org> References: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> In-Reply-To: <20260909-hwpoison-kho-v4-0-359313564495@debian.org> To: Ard Biesheuvel , Ilias Apalodimas , Miaohe Lin , Naoya Horiguchi , Andrew Morton , kas@kernel.org, kexec@lists.infradead.org, David Hildenbrand , Lorenzo Stoakes , "Liam R. Howlett" , Vlastimil Babka , Mike Rapoport , Suren Baghdasaryan , Michal Hocko , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Brendan Jackman , Johannes Weiner , Zi Yan Cc: linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, rmikey@meta.com, riel@surriel.com, Breno Leitao , harry@kernel.org, kernel-team@meta.com X-Mailer: b4 0.16-dev-f8e9d X-Developer-Signature: v=1; a=openpgp-sha256; l=4020; i=leitao@debian.org; h=from:subject:message-id; bh=drOYIVpoY7AQaUepXy25YegitABVihsyPIT5xNKQkkg=; b=owEBbQKS/ZANAwAIATWjk5/8eHdtAcsmYgBqoVmYmVxCxsLeTjYv3u9z+EL2a6qkRIZ1SrEGK HBAjZjn6WSJAjMEAAEIAB0WIQSshTmm6PRnAspKQ5s1o5Of/Hh3bQUCaqFZmAAKCRA1o5Of/Hh3 bRI/D/91oPLXxFDp9Rr/sQrlT/YiQl/S/8FOn4S2vYWZTtNRBuE6K3TFP9w3UvAoN+cWuKRQDda XWJAC9s+Z8+75ZltnjS7OgNk33KsMJ0svQnxXhH7BTbLvwFaQxY2+FAOdrKdvJCBfmJqhuF6afv mOhJAzwB/G2S7STW7sOEWTGbF5x4oHQOMZfYR4LJ1BuDtIBRopdYsVmdAbdqaHQqMJrycH6hUo8 rLuI4EY9jtzNm/iLF8pjAa6nQTud8KwEpVgCWFHJyJeBCVYUEXeXpOXhWvIQOwIpP2qIDiWKvTo 1orI/+xNunPwlAZoZBYFk7HUOvb8grxKRsqiI/yxrgLE8O8CoHwXYtRweyJYszYoBC0Kq7FV7+X 8gDlNuvn+sfnUV/MkvQUyHFGULb35erkJr3A0GWPAMlfBZKrXajCasv6QugF96KhusrEHAVNXTn Ev+TQ3mv+U10TiZC0p1wGD65m6Qj04HciA1gXgcOOYq4Rt7g3MV6p25u5xAFkNQadTcVdZQWnpe KGz3bVI3BbSxBn5F0ApC92q6tOsBUy3h9zGcv13nEFo/L+BZ77T7rMbmuiXkFO1h3ssVgaJWpw+ eoFti/Uk+3h3UnYR1x13WahcQzdkvtfrcb+mLQPSkI6up7nKhavfmYRZYN4eehwnv4YW+3leRYd pfRrIgOdOLAQ4YQ== X-Developer-Key: i=leitao@debian.org; a=openpgp; fpr=AC8539A6E8F46702CA4A439B35A3939FFC78776D X-Debian-User: leitao When the pages are being given to the allocator, check if they are poisoned, and mark them as such. Similar to unaccepted memory, hook it in __free_pages_core(), and thus the frames never enter the allocator, rather than being taken back out of it. hwpoison_boot_page() leaves a frame in the state a frame poisoned by this kernel would be in, so everything that already understands PG_hwpoison covers it, the kexec segment placement check included. Suggested-by: Kiryl Shutsemau Signed-off-by: Breno Leitao --- include/linux/mm.h | 5 +++++ mm/memory-failure.c | 15 +++++++++++++++ mm/page_alloc.c | 25 +++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index b68824fcfbef1..9d9f2e8fdc136 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -5225,6 +5225,7 @@ extern const struct attribute_group memory_failure_at= tr_group; extern void memory_failure_queue(unsigned long pfn, int flags); void num_poisoned_pages_inc(unsigned long pfn); void num_poisoned_pages_sub(unsigned long pfn, long i); +void __meminit hwpoison_boot_page(struct page *page); phys_addr_t range_first_hwpoison(phys_addr_t start, unsigned long size); phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size); #else @@ -5232,6 +5233,10 @@ static inline void memory_failure_queue(unsigned lon= g pfn, int flags) { } =20 +static inline void hwpoison_boot_page(struct page *page) +{ +} + static inline void num_poisoned_pages_inc(unsigned long pfn) { } diff --git a/mm/memory-failure.c b/mm/memory-failure.c index d9b8be696aac3..f6afdb2a89a94 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -137,6 +137,21 @@ phys_addr_t range_last_hwpoison(phys_addr_t start, uns= igned long size) return range_hwpoison(start, size, false); } =20 +static void update_per_node_mf_stats(unsigned long pfn, enum mf_result res= ult); + +/* Not num_poisoned_pages_inc(): its per block half divides by zero this e= arly. */ +void __meminit hwpoison_boot_page(struct page *page) +{ + if (PageHWPoison(page)) + return; + + SetPageHWPoison(page); + set_page_count(page, 1); + /* The page has been completely isolated =3D=3D MF_RECOVERED */ + update_per_node_mf_stats(page_to_pfn(page), MF_RECOVERED); + atomic_long_inc(&num_poisoned_pages); +} + /** * MF_ATTR_RO - Create sysfs entry for each memory failure statistics. * @_name: name of the file in the per NUMA sysfs directory. diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 404896b53003e..9e2ce833fd409 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1579,6 +1579,19 @@ static void __free_pages_ok(struct page *page, unsig= ned int order, free_one_page(zone, page, pfn, order, fpi_flags); } =20 +/* Flag the frames an earlier kernel recorded as bad. */ +static void __meminit poison_block(struct page *page, unsigned int order) +{ + unsigned long i, nr_pages =3D 1UL << order; + + for (i =3D 0; i < nr_pages; i++) { + struct page *p =3D page + i; + + if (range_contains_poisoned_memory(page_to_phys(p), PAGE_SIZE)) + hwpoison_boot_page(p); + } +} + void __meminit __free_pages_core(struct page *page, unsigned int order, enum meminit_context context) { @@ -1613,6 +1626,18 @@ void __meminit __free_pages_core(struct page *page, = unsigned int order, atomic_long_add(nr_pages, &page_zone(page)->managed_pages); } =20 + /* First: a block parked by __free_unaccepted() never returns here. */ + if (range_contains_poisoned_memory(page_to_phys(page), + PAGE_SIZE << order)) { + poison_block(page, order); + /* + * TODO: free the frames in the block that are not poisoned. + * They stay out of the allocator and still count in + * managed_pages, so a unit costs up to a block. + */ + return; + } + if (page_contains_unaccepted(page, order)) { if (order =3D=3D MAX_PAGE_ORDER && __free_unaccepted(page)) return; --=20 2.53.0-Meta