diff options
-rw-r--r-- | Documentation/kernel-parameters.txt | 5 | ||||
-rw-r--r-- | arch/x86/Kconfig | 3 | ||||
-rw-r--r-- | arch/x86/kernel/setup.c | 87 | ||||
-rw-r--r-- | arch/x86/mm/fault.c | 2 | ||||
-rw-r--r-- | drivers/base/power/main.c | 1 | ||||
-rw-r--r-- | include/linux/kernel.h | 12 |
6 files changed, 110 insertions, 0 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 1150444a21ab..df48af505d15 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
@@ -360,6 +360,11 @@ and is between 256 and 4096 characters. It is defined in the file | |||
360 | Format: <io>,<irq>,<mode> | 360 | Format: <io>,<irq>,<mode> |
361 | See header of drivers/net/hamradio/baycom_ser_hdx.c. | 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 | |||
363 | boot_delay= Milliseconds to delay each printk during boot. | 368 | boot_delay= Milliseconds to delay each printk during boot. |
364 | Values larger than 10 seconds (10000) are changed to | 369 | Values larger than 10 seconds (10000) are changed to |
365 | no delay (0). | 370 | no delay (0). |
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ed92864d1325..1bb52e2ca02e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -201,6 +201,9 @@ config X86_TRAMPOLINE | |||
201 | depends on X86_SMP || (X86_VOYAGER && SMP) || (64BIT && ACPI_SLEEP) | 201 | depends on X86_SMP || (X86_VOYAGER && SMP) || (64BIT && ACPI_SLEEP) |
202 | default y | 202 | default y |
203 | 203 | ||
204 | config X86_CHECK_BIOS_CORRUPTION | ||
205 | def_bool y | ||
206 | |||
204 | config KTIME_SCALAR | 207 | config KTIME_SCALAR |
205 | def_bool X86_32 | 208 | def_bool X86_32 |
206 | source "init/Kconfig" | 209 | source "init/Kconfig" |
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 362d4e7f2d38..ee89ebc5aabc 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c | |||
@@ -579,6 +579,89 @@ static struct x86_quirks default_x86_quirks __initdata; | |||
579 | struct x86_quirks *x86_quirks __initdata = &default_x86_quirks; | 579 | struct x86_quirks *x86_quirks __initdata = &default_x86_quirks; |
580 | 580 | ||
581 | /* | 581 | /* |
582 | * Some BIOSes seem to corrupt the low 64k of memory during events | ||
583 | * like suspend/resume and unplugging an HDMI cable. Reserve all | ||
584 | * remaining free memory in that area and fill it with a distinct | ||
585 | * pattern. | ||
586 | */ | ||
587 | #ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION | ||
588 | #define MAX_SCAN_AREAS 8 | ||
589 | static struct e820entry scan_areas[MAX_SCAN_AREAS]; | ||
590 | static int num_scan_areas; | ||
591 | |||
592 | static void __init setup_bios_corruption_check(void) | ||
593 | { | ||
594 | u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */ | ||
595 | |||
596 | while(addr < 0x10000 && num_scan_areas < MAX_SCAN_AREAS) { | ||
597 | u64 size; | ||
598 | addr = find_e820_area_size(addr, &size, PAGE_SIZE); | ||
599 | |||
600 | if (addr == 0) | ||
601 | break; | ||
602 | |||
603 | if ((addr + size) > 0x10000) | ||
604 | size = 0x10000 - addr; | ||
605 | |||
606 | if (size == 0) | ||
607 | break; | ||
608 | |||
609 | e820_update_range(addr, size, E820_RAM, E820_RESERVED); | ||
610 | scan_areas[num_scan_areas].addr = addr; | ||
611 | scan_areas[num_scan_areas].size = size; | ||
612 | num_scan_areas++; | ||
613 | |||
614 | /* Assume we've already mapped this early memory */ | ||
615 | memset(__va(addr), 0, size); | ||
616 | |||
617 | addr += size; | ||
618 | } | ||
619 | |||
620 | printk(KERN_INFO "scanning %d areas for BIOS corruption\n", | ||
621 | num_scan_areas); | ||
622 | update_e820(); | ||
623 | } | ||
624 | |||
625 | static int __read_mostly bios_corruption_check = 1; | ||
626 | |||
627 | void check_for_bios_corruption(void) | ||
628 | { | ||
629 | int i; | ||
630 | int corruption = 0; | ||
631 | |||
632 | if (!bios_corruption_check) | ||
633 | return; | ||
634 | |||
635 | for(i = 0; i < num_scan_areas; i++) { | ||
636 | unsigned long *addr = __va(scan_areas[i].addr); | ||
637 | unsigned long size = scan_areas[i].size; | ||
638 | |||
639 | for(; size; addr++, size -= sizeof(unsigned long)) { | ||
640 | if (!*addr) | ||
641 | continue; | ||
642 | printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n", | ||
643 | addr, __pa(addr), *addr); | ||
644 | corruption = 1; | ||
645 | *addr = 0; | ||
646 | } | ||
647 | } | ||
648 | |||
649 | if (corruption) | ||
650 | dump_stack(); | ||
651 | } | ||
652 | |||
653 | static int set_bios_corruption_check(char *arg) | ||
654 | { | ||
655 | char *end; | ||
656 | |||
657 | bios_corruption_check = simple_strtol(arg, &end, 10); | ||
658 | |||
659 | return (*end == 0) ? 0 : -EINVAL; | ||
660 | } | ||
661 | early_param("bios_corruption_check", set_bios_corruption_check); | ||
662 | #endif | ||
663 | |||
664 | /* | ||
582 | * Determine if we were loaded by an EFI loader. If so, then we have also been | 665 | * Determine if we were loaded by an EFI loader. If so, then we have also been |
583 | * passed the efi memmap, systab, etc., so we should use these data structures | 666 | * passed the efi memmap, systab, etc., so we should use these data structures |
584 | * for initialization. Note, the efi init code path is determined by the | 667 | * for initialization. Note, the efi init code path is determined by the |
@@ -750,6 +833,10 @@ void __init setup_arch(char **cmdline_p) | |||
750 | high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1; | 833 | high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1; |
751 | #endif | 834 | #endif |
752 | 835 | ||
836 | #ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION | ||
837 | setup_bios_corruption_check(); | ||
838 | #endif | ||
839 | |||
753 | /* max_pfn_mapped is updated here */ | 840 | /* max_pfn_mapped is updated here */ |
754 | max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT); | 841 | max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT); |
755 | max_pfn_mapped = max_low_pfn_mapped; | 842 | max_pfn_mapped = max_low_pfn_mapped; |
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 455f3fe67b42..5140bdf03020 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -848,6 +848,8 @@ no_context: | |||
848 | * Oops. The kernel tried to access some bad page. We'll have to | 848 | * Oops. The kernel tried to access some bad page. We'll have to |
849 | * terminate things with extreme prejudice. | 849 | * terminate things with extreme prejudice. |
850 | */ | 850 | */ |
851 | check_for_bios_corruption(); | ||
852 | |||
851 | #ifdef CONFIG_X86_32 | 853 | #ifdef CONFIG_X86_32 |
852 | bust_spinlocks(1); | 854 | bust_spinlocks(1); |
853 | #else | 855 | #else |
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 273a944d4040..bf6d3554e506 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -254,6 +254,7 @@ static char *pm_verb(int event) | |||
254 | 254 | ||
255 | static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info) | 255 | static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info) |
256 | { | 256 | { |
257 | check_for_bios_corruption(); | ||
257 | dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event), | 258 | dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event), |
258 | ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ? | 259 | ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ? |
259 | ", may wakeup" : ""); | 260 | ", may wakeup" : ""); |
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 2651f805ba6d..8017129e6b63 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
@@ -240,6 +240,18 @@ extern const char *print_tainted(void); | |||
240 | extern void add_taint(unsigned); | 240 | extern void add_taint(unsigned); |
241 | extern int root_mountflags; | 241 | extern int root_mountflags; |
242 | 242 | ||
243 | #ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION | ||
244 | /* | ||
245 | * This is obviously not a great place for this, but we want to be | ||
246 | * able to scatter it around anywhere in the kernel. | ||
247 | */ | ||
248 | void check_for_bios_corruption(void); | ||
249 | #else | ||
250 | static inline void check_for_bios_corruption(void) | ||
251 | { | ||
252 | } | ||
253 | #endif | ||
254 | |||
243 | /* Values used for system_state */ | 255 | /* Values used for system_state */ |
244 | extern enum system_states { | 256 | extern enum system_states { |
245 | SYSTEM_BOOTING, | 257 | SYSTEM_BOOTING, |