Commit 9f077871ce7237e2387fc76542b3b4033cb05e49

Authored by Jeremy Fitzhardinge
Committed by Ingo Molnar
1 parent bb577f980e

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: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

Showing 3 changed files with 106 additions and 28 deletions Side-by-side Diff

Documentation/kernel-parameters.txt
... ... @@ -360,11 +360,6 @@
360 360 Format: <io>,<irq>,<mode>
361 361 See header of drivers/net/hamradio/baycom_ser_hdx.c.
362 362  
363   - bios_corruption_check=0/1 [X86]
364   - Some BIOSes seem to corrupt the first 64k of memory
365   - when doing things like suspend/resume. Setting this
366   - option will scan the memory looking for corruption.
367   -
368 363 boot_delay= Milliseconds to delay each printk during boot.
369 364 Values larger than 10 seconds (10000) are changed to
370 365 no delay (0).
... ... @@ -1232,6 +1227,29 @@
1232 1227 memmap=64K$0x18690000
1233 1228 or
1234 1229 memmap=0x10000$0x18690000
  1230 +
  1231 + memory_corruption_check=0/1 [X86]
  1232 + Some BIOSes seem to corrupt the first 64k of
  1233 + memory when doing things like suspend/resume.
  1234 + Setting this option will scan the memory
  1235 + looking for corruption. Enabling this will
  1236 + both detect corruption and prevent the kernel
  1237 + from using the memory being corrupted.
  1238 + However, its intended as a diagnostic tool; if
  1239 + repeatable BIOS-originated corruption always
  1240 + affects the same memory, you can use memmap=
  1241 + to prevent the kernel from using that memory.
  1242 +
  1243 + memory_corruption_check_size=size [X86]
  1244 + By default it checks for corruption in the low
  1245 + 64k, making this memory unavailable for normal
  1246 + use. Use this parameter to scan for
  1247 + corruption in more or less memory.
  1248 +
  1249 + memory_corruption_check_period=seconds [X86]
  1250 + By default it checks for corruption every 60
  1251 + seconds. Use this parameter to check at some
  1252 + other rate. 0 disables periodic checking.
1235 1253  
1236 1254 memtest= [KNL,X86] Enable memtest
1237 1255 Format: <integer>
... ... @@ -201,9 +201,6 @@
201 201 depends on X86_SMP || (X86_VOYAGER && SMP) || (64BIT && ACPI_SLEEP)
202 202 default y
203 203  
204   -config X86_CHECK_BIOS_CORRUPTION
205   - def_bool y
206   -
207 204 config KTIME_SCALAR
208 205 def_bool X86_32
209 206 source "init/Kconfig"
... ... @@ -1061,6 +1058,29 @@
1061 1058 For systems with a lot of RAM, this can be wasteful of precious
1062 1059 low memory. Setting this option will put user-space page table
1063 1060 entries in high memory.
  1061 +
  1062 +config X86_CHECK_BIOS_CORRUPTION
  1063 + bool "Check for low memory corruption"
  1064 + default y
  1065 + help
  1066 + Periodically check for memory corruption in low memory, which
  1067 + is suspected to be caused by BIOS. Even when enabled in the
  1068 + configuration, it is disabled at runtime. Enable it by
  1069 + setting "memory_corruption_check=1" on the kernel command
  1070 + line. By default it scans the low 64k of memory every 60
  1071 + seconds; see the memory_corruption_check_size and
  1072 + memory_corruption_check_period parameters in
  1073 + Documentation/kernel-parameters.txt to adjust this.
  1074 +
  1075 + When enabled with the default parameters, this option has
  1076 + almost no overhead, as it reserves a relatively small amount
  1077 + of memory and scans it infrequently. It both detects corruption
  1078 + and prevents it from affecting the running system.
  1079 +
  1080 + It is, however, intended as a diagnostic tool; if repeatable
  1081 + BIOS-originated corruption always affects the same memory,
  1082 + you can use memmap= to prevent the kernel from using that
  1083 + memory.
1064 1084  
1065 1085 config MATH_EMULATION
1066 1086 bool
arch/x86/kernel/setup.c
... ... @@ -586,22 +586,71 @@
586 586 */
587 587 #ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
588 588 #define MAX_SCAN_AREAS 8
  589 +
  590 +static int __read_mostly memory_corruption_check = 0;
  591 +static unsigned __read_mostly corruption_check_size = 64*1024;
  592 +static unsigned __read_mostly corruption_check_period = 60; /* seconds */
  593 +
589 594 static struct e820entry scan_areas[MAX_SCAN_AREAS];
590 595 static int num_scan_areas;
591 596  
  597 +
  598 +static int set_corruption_check(char *arg)
  599 +{
  600 + char *end;
  601 +
  602 + memory_corruption_check = simple_strtol(arg, &end, 10);
  603 +
  604 + return (*end == 0) ? 0 : -EINVAL;
  605 +}
  606 +early_param("memory_corruption_check", set_corruption_check);
  607 +
  608 +static int set_corruption_check_period(char *arg)
  609 +{
  610 + char *end;
  611 +
  612 + corruption_check_period = simple_strtoul(arg, &end, 10);
  613 +
  614 + return (*end == 0) ? 0 : -EINVAL;
  615 +}
  616 +early_param("memory_corruption_check_period", set_corruption_check_period);
  617 +
  618 +static int set_corruption_check_size(char *arg)
  619 +{
  620 + char *end;
  621 + unsigned size;
  622 +
  623 + size = memparse(arg, &end);
  624 +
  625 + if (*end == '\0')
  626 + corruption_check_size = size;
  627 +
  628 + return (size == corruption_check_size) ? 0 : -EINVAL;
  629 +}
  630 +early_param("memory_corruption_check_size", set_corruption_check_size);
  631 +
  632 +
592 633 static void __init setup_bios_corruption_check(void)
593 634 {
594 635 u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */
595 636  
596   - while(addr < 0x10000 && num_scan_areas < MAX_SCAN_AREAS) {
  637 + if (corruption_check_size == 0)
  638 + memory_corruption_check = 0;
  639 +
  640 + if (!memory_corruption_check)
  641 + return;
  642 +
  643 + corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);
  644 +
  645 + while(addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) {
597 646 u64 size;
598 647 addr = find_e820_area_size(addr, &size, PAGE_SIZE);
599 648  
600 649 if (addr == 0)
601 650 break;
602 651  
603   - if ((addr + size) > 0x10000)
604   - size = 0x10000 - addr;
  652 + if ((addr + size) > corruption_check_size)
  653 + size = corruption_check_size - addr;
605 654  
606 655 if (size == 0)
607 656 break;
608 657  
... ... @@ -617,12 +666,11 @@
617 666 addr += size;
618 667 }
619 668  
620   - printk(KERN_INFO "scanning %d areas for BIOS corruption\n",
  669 + printk(KERN_INFO "Scanning %d areas for low memory corruption\n",
621 670 num_scan_areas);
622 671 update_e820();
623 672 }
624 673  
625   -static int __read_mostly bios_corruption_check = 1;
626 674 static struct timer_list periodic_check_timer;
627 675  
628 676 void check_for_bios_corruption(void)
... ... @@ -630,7 +678,7 @@
630 678 int i;
631 679 int corruption = 0;
632 680  
633   - if (!bios_corruption_check)
  681 + if (!memory_corruption_check)
634 682 return;
635 683  
636 684 for(i = 0; i < num_scan_areas; i++) {
637 685  
638 686  
639 687  
640 688  
... ... @@ -647,35 +695,27 @@
647 695 }
648 696 }
649 697  
650   - if (corruption)
651   - dump_stack();
  698 + WARN(corruption, KERN_ERR "Memory corruption detected in low memory\n");
652 699 }
653 700  
654 701 static void periodic_check_for_corruption(unsigned long data)
655 702 {
656 703 check_for_bios_corruption();
657   - mod_timer(&periodic_check_timer, jiffies + 60*HZ);
  704 + mod_timer(&periodic_check_timer, jiffies + corruption_check_period*HZ);
658 705 }
659 706  
660 707 void start_periodic_check_for_corruption(void)
661 708 {
662   - if (!bios_corruption_check)
  709 + if (!memory_corruption_check || corruption_check_period == 0)
663 710 return;
664 711  
  712 + printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n",
  713 + corruption_check_period);
  714 +
665 715 init_timer(&periodic_check_timer);
666 716 periodic_check_timer.function = &periodic_check_for_corruption;
667 717 periodic_check_for_corruption(0);
668 718 }
669   -
670   -static int set_bios_corruption_check(char *arg)
671   -{
672   - char *end;
673   -
674   - bios_corruption_check = simple_strtol(arg, &end, 10);
675   -
676   - return (*end == 0) ? 0 : -EINVAL;
677   -}
678   -early_param("bios_corruption_check", set_bios_corruption_check);
679 719 #endif
680 720  
681 721 /*