Re: [PATCH v2] x86: remove the BIOS memory corruption check feature
From: H. Peter Anvin
Date: Mon Jul 20 2026 - 17:00:31 EST
On July 20, 2026 1:41:05 PM PDT, Luiz Capitulino <luizcap@xxxxxxxxxx> wrote:
>By default, the BIOS memory corruption check works by reserving the
>first 64 KiB physical area from memblock, zeroing it and then scanning it
>periodically.
>
>However, this functionality has been broken since v5.13 by two commits:
>
>- Commit a799c2bd29d1 ("x86/setup: Consolidate early memory reservations").
> Moved trim_low_memory_range() into early_reserve_memory(), which runs
> earlier in setup_arch(), causing the first 64 KiB to be reserved (when
> CONFIG_X86_RESERVE_LOW=64) before setup_bios_corruption_check() runs
>
>- Commit f1d4d47c5851 ("x86/setup: Always reserve the first 1M of RAM").
> Hardcodes the reservation of the first 64 KiB early in
> early_reserve_memory()
>
>It might be possible to get this feature to work by allowing
>setup_bios_corruption_check() to run first, but it's just not worth it
>given that the kernel will never access this memory area anyways.
>
>Signed-off-by: Luiz Capitulino <luizcap@xxxxxxxxxx>
>---
>
>Changelog
>=========
>
>v2
>- Rebase against latest Linus tree (v7.2-rc4)
>- Improve changelog
>
> .../admin-guide/kernel-parameters.txt | 23 ---
> arch/x86/Kconfig | 30 ---
> arch/x86/configs/i386_defconfig | 1 -
> arch/x86/configs/x86_64_defconfig | 1 -
> arch/x86/include/asm/bios_ebda.h | 17 --
> arch/x86/include/asm/setup.h | 1 -
> arch/x86/kernel/Makefile | 2 -
> arch/x86/kernel/check.c | 187 ------------------
> arch/x86/kernel/setup.c | 4 -
> 9 files changed, 266 deletions(-)
> delete mode 100644 arch/x86/kernel/check.c
>
>diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
>index b5493a7f8f22..314a2a0dc9cd 100644
>--- a/Documentation/admin-guide/kernel-parameters.txt
>+++ b/Documentation/admin-guide/kernel-parameters.txt
>@@ -3935,29 +3935,6 @@ Kernel parameters
> specified as e820 types, e.g., 1 = RAM, 2 = reserved,
> 3 = ACPI, 12 = PRAM.
> - memory_corruption_check=0/1 [X86,EARLY]
>- Some BIOSes seem to corrupt the first 64k of
>- memory when doing things like suspend/resume.
>- Setting this option will scan the memory
>- looking for corruption. Enabling this will
>- both detect corruption and prevent the kernel
>- from using the memory being corrupted.
>- However, it's intended as a diagnostic tool; if
>- repeatable BIOS-originated corruption always
>- affects the same memory, you can use memmap=
>- to prevent the kernel from using that memory.
>-
>- memory_corruption_check_size=size [X86,EARLY]
>- By default it checks for corruption in the low
>- 64k, making this memory unavailable for normal
>- use. Use this parameter to scan for
>- corruption in more or less memory.
>-
>- memory_corruption_check_period=seconds [X86,EARLY]
>- By default it checks for corruption every 60
>- seconds. Use this parameter to check at some
>- other rate. 0 disables periodic checking.
>-
> memory_hotplug.memmap_on_memory
> [KNL,X86,ARM] Boolean flag to enable this feature.
> Format: {on | off (default)}
>diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
>index bdad90f210e4..cce558ca5c6c 100644
>--- a/arch/x86/Kconfig
>+++ b/arch/x86/Kconfig
>@@ -1601,36 +1601,6 @@ config X86_PMEM_LEGACY
> Say Y if unsure.
> -config X86_CHECK_BIOS_CORRUPTION
>- bool "Check for low memory corruption"
>- help
>- Periodically check for memory corruption in low memory, which
>- is suspected to be caused by BIOS. Even when enabled in the
>- configuration, it is disabled at runtime. Enable it by
>- setting "memory_corruption_check=1" on the kernel command
>- line. By default it scans the low 64k of memory every 60
>- seconds; see the memory_corruption_check_size and
>- memory_corruption_check_period parameters in
>- Documentation/admin-guide/kernel-parameters.rst to adjust this.
>-
>- When enabled with the default parameters, this option has
>- almost no overhead, as it reserves a relatively small amount
>- of memory and scans it infrequently. It both detects corruption
>- and prevents it from affecting the running system.
>-
>- It is, however, intended as a diagnostic tool; if repeatable
>- BIOS-originated corruption always affects the same memory,
>- you can use memmap= to prevent the kernel from using that
>- memory.
>-
>-config X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
>- bool "Set the default setting of memory_corruption_check"
>- depends on X86_CHECK_BIOS_CORRUPTION
>- default y
>- help
>- Set whether the default state of memory_corruption_check is
>- on or off.
>-
> config MTRR
> def_bool y
> prompt "MTRR (Memory Type Range Register) support" if EXPERT
>diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig
>index 79fa38ca954d..848c2d924668 100644
>--- a/arch/x86/configs/i386_defconfig
>+++ b/arch/x86/configs/i386_defconfig
>@@ -36,7 +36,6 @@ CONFIG_PARAVIRT=y
> CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y
> CONFIG_X86_MSR=y
> CONFIG_X86_CPUID=y
>-CONFIG_X86_CHECK_BIOS_CORRUPTION=y
> # CONFIG_MTRR_SANITIZER is not set
> CONFIG_EFI=y
> CONFIG_EFI_STUB=y
>diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig
>index 269f7d808be4..e05d67f708cb 100644
>--- a/arch/x86/configs/x86_64_defconfig
>+++ b/arch/x86/configs/x86_64_defconfig
>@@ -35,7 +35,6 @@ CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y
> CONFIG_X86_MSR=y
> CONFIG_X86_CPUID=y
> CONFIG_NUMA=y
>-CONFIG_X86_CHECK_BIOS_CORRUPTION=y
> # CONFIG_MTRR_SANITIZER is not set
> CONFIG_EFI=y
> CONFIG_EFI_STUB=y
>diff --git a/arch/x86/include/asm/bios_ebda.h b/arch/x86/include/asm/bios_ebda.h
>index 4d5a17e2febe..2f441fd9ebf8 100644
>--- a/arch/x86/include/asm/bios_ebda.h
>+++ b/arch/x86/include/asm/bios_ebda.h
>@@ -20,21 +20,4 @@ static inline unsigned int get_bios_ebda(void)
> void reserve_bios_regions(void);
> -#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
>-/*
>- * This is obviously not a great place for this, but we want to be
>- * able to scatter it around anywhere in the kernel.
>- */
>-void check_for_bios_corruption(void);
>-void start_periodic_check_for_corruption(void);
>-#else
>-static inline void check_for_bios_corruption(void)
>-{
>-}
>-
>-static inline void start_periodic_check_for_corruption(void)
>-{
>-}
>-#endif
>-
> #endif /* _ASM_X86_BIOS_EBDA_H */
>diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h
>index 914eb32581c7..1f58361eb038 100644
>--- a/arch/x86/include/asm/setup.h
>+++ b/arch/x86/include/asm/setup.h
>@@ -42,7 +42,6 @@ static inline void vsmp_init(void) { }
> struct pt_regs;
> -void setup_bios_corruption_check(void);
> void early_platform_quirks(void);
> extern unsigned long saved_video_mode;
>diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
>index 31f46fd00527..702536938433 100644
>--- a/arch/x86/kernel/Makefile
>+++ b/arch/x86/kernel/Makefile
>@@ -149,8 +149,6 @@ obj-$(CONFIG_JAILHOUSE_GUEST) += jailhouse.o
> obj-$(CONFIG_EISA) += eisa.o
> obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o
> -obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o
>-
> obj-$(CONFIG_OF) += devicetree.o
> obj-$(CONFIG_UPROBES) += uprobes.o
> diff --git a/arch/x86/kernel/check.c b/arch/x86/kernel/check.c
>deleted file mode 100644
>index 5136e6818da8..000000000000
>--- a/arch/x86/kernel/check.c
>+++ /dev/null
>@@ -1,187 +0,0 @@
>-// SPDX-License-Identifier: GPL-2.0
>-
>-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>-
>-#include <linux/init.h>
>-#include <linux/sched.h>
>-#include <linux/kthread.h>
>-#include <linux/workqueue.h>
>-#include <linux/memblock.h>
>-
>-#include <asm/proto.h>
>-#include <asm/setup.h>
>-
>-/*
>- * Some BIOSes seem to corrupt the low 64k of memory during events
>- * like suspend/resume and unplugging an HDMI cable. Reserve all
>- * remaining free memory in that area and fill it with a distinct
>- * pattern.
>- */
>-#define MAX_SCAN_AREAS 8
>-
>-static int __read_mostly memory_corruption_check = -1;
>-
>-static unsigned __read_mostly corruption_check_size = 64*1024;
>-static unsigned __read_mostly corruption_check_period = 60; /* seconds */
>-
>-static struct scan_area {
>- u64 addr;
>- u64 size;
>-} scan_areas[MAX_SCAN_AREAS];
>-static int num_scan_areas;
>-
>-static __init int set_corruption_check(char *arg)
>-{
>- ssize_t ret;
>- unsigned long val;
>-
>- if (!arg) {
>- pr_err("memory_corruption_check config string not provided\n");
>- return -EINVAL;
>- }
>-
>- ret = kstrtoul(arg, 10, &val);
>- if (ret)
>- return ret;
>-
>- memory_corruption_check = val;
>-
>- return 0;
>-}
>-early_param("memory_corruption_check", set_corruption_check);
>-
>-static __init int set_corruption_check_period(char *arg)
>-{
>- ssize_t ret;
>- unsigned long val;
>-
>- if (!arg) {
>- pr_err("memory_corruption_check_period config string not provided\n");
>- return -EINVAL;
>- }
>-
>- ret = kstrtoul(arg, 10, &val);
>- if (ret)
>- return ret;
>-
>- corruption_check_period = val;
>- return 0;
>-}
>-early_param("memory_corruption_check_period", set_corruption_check_period);
>-
>-static __init int set_corruption_check_size(char *arg)
>-{
>- char *end;
>- unsigned size;
>-
>- if (!arg) {
>- pr_err("memory_corruption_check_size config string not provided\n");
>- return -EINVAL;
>- }
>-
>- size = memparse(arg, &end);
>-
>- if (*end == '\0')
>- corruption_check_size = size;
>-
>- return (size == corruption_check_size) ? 0 : -EINVAL;
>-}
>-early_param("memory_corruption_check_size", set_corruption_check_size);
>-
>-
>-void __init setup_bios_corruption_check(void)
>-{
>- phys_addr_t start, end;
>- u64 i;
>-
>- if (memory_corruption_check == -1) {
>- memory_corruption_check =
>-#ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
>- 1
>-#else
>- 0
>-#endif
>- ;
>- }
>-
>- if (corruption_check_size == 0)
>- memory_corruption_check = 0;
>-
>- if (!memory_corruption_check)
>- return;
>-
>- corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);
>-
>- for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end,
>- NULL) {
>- start = clamp_t(phys_addr_t, round_up(start, PAGE_SIZE),
>- PAGE_SIZE, corruption_check_size);
>- end = clamp_t(phys_addr_t, round_down(end, PAGE_SIZE),
>- PAGE_SIZE, corruption_check_size);
>- if (start >= end)
>- continue;
>-
>- memblock_reserve(start, end - start);
>- scan_areas[num_scan_areas].addr = start;
>- scan_areas[num_scan_areas].size = end - start;
>-
>- /* Assume we've already mapped this early memory */
>- memset(__va(start), 0, end - start);
>-
>- if (++num_scan_areas >= MAX_SCAN_AREAS)
>- break;
>- }
>-
>- if (num_scan_areas)
>- pr_info("Scanning %d areas for low memory corruption\n", num_scan_areas);
>-}
>-
>-
>-static void check_for_bios_corruption(void)
>-{
>- int i;
>- int corruption = 0;
>-
>- if (!memory_corruption_check)
>- return;
>-
>- for (i = 0; i < num_scan_areas; i++) {
>- unsigned long *addr = __va(scan_areas[i].addr);
>- unsigned long size = scan_areas[i].size;
>-
>- for (; size; addr++, size -= sizeof(unsigned long)) {
>- if (!*addr)
>- continue;
>- pr_err("Corrupted low memory at %p (%lx phys) = %08lx\n", addr, __pa(addr), *addr);
>- corruption = 1;
>- *addr = 0;
>- }
>- }
>-
>- WARN_ONCE(corruption, KERN_ERR "Memory corruption detected in low memory\n");
>-}
>-
>-static void check_corruption(struct work_struct *dummy);
>-static DECLARE_DELAYED_WORK(bios_check_work, check_corruption);
>-
>-static void check_corruption(struct work_struct *dummy)
>-{
>- check_for_bios_corruption();
>- schedule_delayed_work(&bios_check_work,
>- round_jiffies_relative(corruption_check_period*HZ));
>-}
>-
>-static int start_periodic_check_for_corruption(void)
>-{
>- if (!num_scan_areas || !memory_corruption_check || corruption_check_period == 0)
>- return 0;
>-
>- pr_info("Scanning for low memory corruption every %d seconds\n", corruption_check_period);
>-
>- /* First time we run the checks right away */
>- schedule_delayed_work(&bios_check_work, 0);
>-
>- return 0;
>-}
>-device_initcall(start_periodic_check_for_corruption);
>-
>diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
>index 46882ce79c3a..ec2cbe3f800d 100644
>--- a/arch/x86/kernel/setup.c
>+++ b/arch/x86/kernel/setup.c
>@@ -1096,10 +1096,6 @@ void __init setup_arch(char **cmdline_p)
> /* preallocate 4k for mptable mpc */
> e820__memblock_alloc_reserved_mpc_new();
> -#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
>- setup_bios_corruption_check();
>-#endif
>-
> #ifdef CONFIG_X86_32
> printk(KERN_DEBUG "initial memory mapped: [mem 0x00000000-%#010lx]\n",
> (max_pfn_mapped<<PAGE_SHIFT) - 1);
Acked-by: H. Peter Anvin <hpa@xxxxxxxxx>