x86: clean up memory corruption check and add more kernel parameters
authorJeremy Fitzhardinge <jeremy@goop.org>
Sun, 7 Sep 2008 08:51:34 +0000 (01:51 -0700)
committerIngo Molnar <mingo@elte.hu>
Sun, 7 Sep 2008 15:40:01 +0000 (17:40 +0200)
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: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Documentation/kernel-parameters.txt
arch/x86/Kconfig
arch/x86/kernel/setup.c

index df48af505d15466b9f2312cda9feb7135ad5155c..6a2629d00598989182d523f1701de6beb90c7401 100644 (file)
@@ -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
index 1bb52e2ca02e710c3604d7531c4931d14a08afcb..cbee4199689ce4e94c4418216aca57f7290d682e 100644 (file)
@@ -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
index c239b378097395d3270eab23d64a08b92474375f..27ae912888555b559caeeaad02bc98a92ee38ee0 100644 (file)
@@ -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
 
 /*