Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f077871 authored by Jeremy Fitzhardinge's avatar Jeremy Fitzhardinge Committed by Ingo Molnar
Browse files

x86: clean up memory corruption check and add more kernel parameters



The corruption check is enabled in Kconfig by default, but disabled at runtime.

This patch adds several kernel parameters to control the corruption
check's behaviour; these are documented in kernel-parameters.txt.

Signed-off-by: default avatarJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent bb577f98
Loading
Loading
Loading
Loading
+23 −5
Original line number Diff line number Diff line
@@ -360,11 +360,6 @@ and is between 256 and 4096 characters. It is defined in the file
			Format: <io>,<irq>,<mode>
			See header of drivers/net/hamradio/baycom_ser_hdx.c.

	bios_corruption_check=0/1 [X86]
			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.

	boot_delay=	Milliseconds to delay each printk during boot.
			Values larger than 10 seconds (10000) are changed to
			no delay (0).
@@ -1233,6 +1228,29 @@ and is between 256 and 4096 characters. It is defined in the file
			         or
			         memmap=0x10000$0x18690000

	memory_corruption_check=0/1 [X86]
			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, its 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]
			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]
			By default it checks for corruption every 60
			seconds.  Use this parameter to check at some
			other rate.  0 disables periodic checking.

	memtest=	[KNL,X86] Enable memtest
			Format: <integer>
			range: 0,4 : pattern number
+23 −3
Original line number Diff line number Diff line
@@ -201,9 +201,6 @@ config X86_TRAMPOLINE
	depends on X86_SMP || (X86_VOYAGER && SMP) || (64BIT && ACPI_SLEEP)
	default y

config X86_CHECK_BIOS_CORRUPTION
        def_bool y

config KTIME_SCALAR
	def_bool X86_32
source "init/Kconfig"
@@ -1062,6 +1059,29 @@ config HIGHPTE
	  low memory.  Setting this option will put user-space page table
	  entries in high memory.

config X86_CHECK_BIOS_CORRUPTION
        bool "Check for low memory corruption"
	default y
	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/kernel-parameters.txt 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 MATH_EMULATION
	bool
	prompt "Math emulation" if X86_32
+60 −20
Original line number Diff line number Diff line
@@ -586,22 +586,71 @@ struct x86_quirks *x86_quirks __initdata = &default_x86_quirks;
 */
#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
#define MAX_SCAN_AREAS	8

static int __read_mostly memory_corruption_check = 0;
static unsigned __read_mostly corruption_check_size = 64*1024;
static unsigned __read_mostly corruption_check_period = 60; /* seconds */

static struct e820entry scan_areas[MAX_SCAN_AREAS];
static int num_scan_areas;


static int set_corruption_check(char *arg)
{
	char *end;

	memory_corruption_check = simple_strtol(arg, &end, 10);

	return (*end == 0) ? 0 : -EINVAL;
}
early_param("memory_corruption_check", set_corruption_check);

static int set_corruption_check_period(char *arg)
{
	char *end;

	corruption_check_period = simple_strtoul(arg, &end, 10);

	return (*end == 0) ? 0 : -EINVAL;
}
early_param("memory_corruption_check_period", set_corruption_check_period);

static int set_corruption_check_size(char *arg)
{
	char *end;
	unsigned size;

	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);


static void __init setup_bios_corruption_check(void)
{
	u64 addr = PAGE_SIZE;	/* assume first page is reserved anyway */

	while(addr < 0x10000 && num_scan_areas < MAX_SCAN_AREAS) {
	if (corruption_check_size == 0)
		memory_corruption_check = 0;

	if (!memory_corruption_check)
		return;

	corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);

	while(addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) {
		u64 size;
		addr = find_e820_area_size(addr, &size, PAGE_SIZE);

		if (addr == 0)
			break;

		if ((addr + size) > 0x10000)
			size = 0x10000 - addr;
		if ((addr + size) > corruption_check_size)
			size = corruption_check_size - addr;

		if (size == 0)
			break;
@@ -617,12 +666,11 @@ static void __init setup_bios_corruption_check(void)
		addr += size;
	}

	printk(KERN_INFO "scanning %d areas for BIOS corruption\n",
	printk(KERN_INFO "Scanning %d areas for low memory corruption\n",
	       num_scan_areas);
	update_e820();
}

static int __read_mostly bios_corruption_check = 1;
static struct timer_list periodic_check_timer;

void check_for_bios_corruption(void)
@@ -630,7 +678,7 @@ void check_for_bios_corruption(void)
	int i;
	int corruption = 0;

	if (!bios_corruption_check)
	if (!memory_corruption_check)
		return;

	for(i = 0; i < num_scan_areas; i++) {
@@ -647,35 +695,27 @@ void check_for_bios_corruption(void)
		}
	}

	if (corruption)
		dump_stack();
	WARN(corruption, KERN_ERR "Memory corruption detected in low memory\n");
}

static void periodic_check_for_corruption(unsigned long data)
{
	check_for_bios_corruption();
	mod_timer(&periodic_check_timer, jiffies + 60*HZ);
	mod_timer(&periodic_check_timer, jiffies + corruption_check_period*HZ);
}

void start_periodic_check_for_corruption(void)
{
	if (!bios_corruption_check)
	if (!memory_corruption_check || corruption_check_period == 0)
		return;

	printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n",
	       corruption_check_period);

	init_timer(&periodic_check_timer);
	periodic_check_timer.function = &periodic_check_for_corruption;
	periodic_check_for_corruption(0);
}

static int set_bios_corruption_check(char *arg)
{
	char *end;

	bios_corruption_check = simple_strtol(arg, &end, 10);

	return (*end == 0) ? 0 : -EINVAL;
}
early_param("bios_corruption_check", set_bios_corruption_check);
#endif

/*