diff options
Diffstat (limited to 'arch/x86/kernel/setup.c')
| -rw-r--r-- | arch/x86/kernel/setup.c | 196 |
1 files changed, 194 insertions, 2 deletions
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 46c98efbbf8d..21b8e0a59780 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c | |||
| @@ -582,6 +582,190 @@ static struct x86_quirks default_x86_quirks __initdata; | |||
| 582 | struct x86_quirks *x86_quirks __initdata = &default_x86_quirks; | 582 | struct x86_quirks *x86_quirks __initdata = &default_x86_quirks; |
| 583 | 583 | ||
| 584 | /* | 584 | /* |
| 585 | * Some BIOSes seem to corrupt the low 64k of memory during events | ||
| 586 | * like suspend/resume and unplugging an HDMI cable. Reserve all | ||
| 587 | * remaining free memory in that area and fill it with a distinct | ||
| 588 | * pattern. | ||
| 589 | */ | ||
| 590 | #ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION | ||
| 591 | #define MAX_SCAN_AREAS 8 | ||
| 592 | |||
| 593 | static int __read_mostly memory_corruption_check = -1; | ||
| 594 | |||
| 595 | static unsigned __read_mostly corruption_check_size = 64*1024; | ||
| 596 | static unsigned __read_mostly corruption_check_period = 60; /* seconds */ | ||
| 597 | |||
| 598 | static struct e820entry scan_areas[MAX_SCAN_AREAS]; | ||
| 599 | static int num_scan_areas; | ||
| 600 | |||
| 601 | |||
| 602 | static int set_corruption_check(char *arg) | ||
| 603 | { | ||
| 604 | char *end; | ||
| 605 | |||
| 606 | memory_corruption_check = simple_strtol(arg, &end, 10); | ||
| 607 | |||
| 608 | return (*end == 0) ? 0 : -EINVAL; | ||
| 609 | } | ||
| 610 | early_param("memory_corruption_check", set_corruption_check); | ||
| 611 | |||
| 612 | static int set_corruption_check_period(char *arg) | ||
| 613 | { | ||
| 614 | char *end; | ||
| 615 | |||
| 616 | corruption_check_period = simple_strtoul(arg, &end, 10); | ||
| 617 | |||
| 618 | return (*end == 0) ? 0 : -EINVAL; | ||
| 619 | } | ||
| 620 | early_param("memory_corruption_check_period", set_corruption_check_period); | ||
| 621 | |||
| 622 | static int set_corruption_check_size(char *arg) | ||
| 623 | { | ||
| 624 | char *end; | ||
| 625 | unsigned size; | ||
| 626 | |||
| 627 | size = memparse(arg, &end); | ||
| 628 | |||
| 629 | if (*end == '\0') | ||
| 630 | corruption_check_size = size; | ||
| 631 | |||
| 632 | return (size == corruption_check_size) ? 0 : -EINVAL; | ||
| 633 | } | ||
| 634 | early_param("memory_corruption_check_size", set_corruption_check_size); | ||
| 635 | |||
| 636 | |||
| 637 | static void __init setup_bios_corruption_check(void) | ||
| 638 | { | ||
| 639 | u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */ | ||
| 640 | |||
| 641 | if (memory_corruption_check == -1) { | ||
| 642 | memory_corruption_check = | ||
| 643 | #ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK | ||
| 644 | 1 | ||
| 645 | #else | ||
| 646 | 0 | ||
| 647 | #endif | ||
| 648 | ; | ||
| 649 | } | ||
| 650 | |||
| 651 | if (corruption_check_size == 0) | ||
| 652 | memory_corruption_check = 0; | ||
| 653 | |||
| 654 | if (!memory_corruption_check) | ||
| 655 | return; | ||
| 656 | |||
| 657 | corruption_check_size = round_up(corruption_check_size, PAGE_SIZE); | ||
| 658 | |||
| 659 | while(addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) { | ||
| 660 | u64 size; | ||
| 661 | addr = find_e820_area_size(addr, &size, PAGE_SIZE); | ||
| 662 | |||
| 663 | if (addr == 0) | ||
| 664 | break; | ||
| 665 | |||
| 666 | if ((addr + size) > corruption_check_size) | ||
| 667 | size = corruption_check_size - addr; | ||
| 668 | |||
| 669 | if (size == 0) | ||
| 670 | break; | ||
| 671 | |||
| 672 | e820_update_range(addr, size, E820_RAM, E820_RESERVED); | ||
| 673 | scan_areas[num_scan_areas].addr = addr; | ||
| 674 | scan_areas[num_scan_areas].size = size; | ||
| 675 | num_scan_areas++; | ||
| 676 | |||
| 677 | /* Assume we've already mapped this early memory */ | ||
| 678 | memset(__va(addr), 0, size); | ||
| 679 | |||
| 680 | addr += size; | ||
| 681 | } | ||
| 682 | |||
| 683 | printk(KERN_INFO "Scanning %d areas for low memory corruption\n", | ||
| 684 | num_scan_areas); | ||
| 685 | update_e820(); | ||
| 686 | } | ||
| 687 | |||
| 688 | static struct timer_list periodic_check_timer; | ||
| 689 | |||
| 690 | void check_for_bios_corruption(void) | ||
| 691 | { | ||
| 692 | int i; | ||
| 693 | int corruption = 0; | ||
| 694 | |||
| 695 | if (!memory_corruption_check) | ||
| 696 | return; | ||
| 697 | |||
| 698 | for(i = 0; i < num_scan_areas; i++) { | ||
| 699 | unsigned long *addr = __va(scan_areas[i].addr); | ||
| 700 | unsigned long size = scan_areas[i].size; | ||
| 701 | |||
| 702 | for(; size; addr++, size -= sizeof(unsigned long)) { | ||
| 703 | if (!*addr) | ||
| 704 | continue; | ||
| 705 | printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n", | ||
| 706 | addr, __pa(addr), *addr); | ||
| 707 | corruption = 1; | ||
| 708 | *addr = 0; | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | WARN(corruption, KERN_ERR "Memory corruption detected in low memory\n"); | ||
| 713 | } | ||
| 714 | |||
| 715 | static void periodic_check_for_corruption(unsigned long data) | ||
| 716 | { | ||
| 717 | check_for_bios_corruption(); | ||
| 718 | mod_timer(&periodic_check_timer, round_jiffies(jiffies + corruption_check_period*HZ)); | ||
| 719 | } | ||
| 720 | |||
| 721 | void start_periodic_check_for_corruption(void) | ||
| 722 | { | ||
| 723 | if (!memory_corruption_check || corruption_check_period == 0) | ||
| 724 | return; | ||
| 725 | |||
| 726 | printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n", | ||
| 727 | corruption_check_period); | ||
| 728 | |||
| 729 | init_timer(&periodic_check_timer); | ||
| 730 | periodic_check_timer.function = &periodic_check_for_corruption; | ||
| 731 | periodic_check_for_corruption(0); | ||
| 732 | } | ||
| 733 | #endif | ||
| 734 | |||
| 735 | static int __init dmi_low_memory_corruption(const struct dmi_system_id *d) | ||
| 736 | { | ||
| 737 | printk(KERN_NOTICE | ||
| 738 | "%s detected: BIOS may corrupt low RAM, working it around.\n", | ||
| 739 | d->ident); | ||
| 740 | |||
| 741 | e820_update_range(0, 0x10000, E820_RAM, E820_RESERVED); | ||
| 742 | sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); | ||
| 743 | |||
| 744 | return 0; | ||
| 745 | } | ||
| 746 | |||
| 747 | /* List of systems that have known low memory corruption BIOS problems */ | ||
| 748 | static struct dmi_system_id __initdata bad_bios_dmi_table[] = { | ||
| 749 | #ifdef CONFIG_X86_RESERVE_LOW_64K | ||
| 750 | { | ||
| 751 | .callback = dmi_low_memory_corruption, | ||
| 752 | .ident = "AMI BIOS", | ||
| 753 | .matches = { | ||
| 754 | DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), | ||
| 755 | }, | ||
| 756 | }, | ||
| 757 | { | ||
| 758 | .callback = dmi_low_memory_corruption, | ||
| 759 | .ident = "Phoenix BIOS", | ||
| 760 | .matches = { | ||
| 761 | DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies, LTD"), | ||
| 762 | }, | ||
| 763 | }, | ||
| 764 | #endif | ||
| 765 | {} | ||
| 766 | }; | ||
| 767 | |||
| 768 | /* | ||
| 585 | * Determine if we were loaded by an EFI loader. If so, then we have also been | 769 | * Determine if we were loaded by an EFI loader. If so, then we have also been |
| 586 | * passed the efi memmap, systab, etc., so we should use these data structures | 770 | * passed the efi memmap, systab, etc., so we should use these data structures |
| 587 | * for initialization. Note, the efi init code path is determined by the | 771 | * for initialization. Note, the efi init code path is determined by the |
| @@ -715,6 +899,10 @@ void __init setup_arch(char **cmdline_p) | |||
| 715 | 899 | ||
| 716 | finish_e820_parsing(); | 900 | finish_e820_parsing(); |
| 717 | 901 | ||
| 902 | dmi_scan_machine(); | ||
| 903 | |||
| 904 | dmi_check_system(bad_bios_dmi_table); | ||
| 905 | |||
| 718 | #ifdef CONFIG_X86_32 | 906 | #ifdef CONFIG_X86_32 |
| 719 | probe_roms(); | 907 | probe_roms(); |
| 720 | #endif | 908 | #endif |
| @@ -771,6 +959,10 @@ void __init setup_arch(char **cmdline_p) | |||
| 771 | high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1; | 959 | high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1; |
| 772 | #endif | 960 | #endif |
| 773 | 961 | ||
| 962 | #ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION | ||
| 963 | setup_bios_corruption_check(); | ||
| 964 | #endif | ||
| 965 | |||
| 774 | /* max_pfn_mapped is updated here */ | 966 | /* max_pfn_mapped is updated here */ |
| 775 | max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT); | 967 | max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT); |
| 776 | max_pfn_mapped = max_low_pfn_mapped; | 968 | max_pfn_mapped = max_low_pfn_mapped; |
| @@ -799,8 +991,6 @@ void __init setup_arch(char **cmdline_p) | |||
| 799 | vsmp_init(); | 991 | vsmp_init(); |
| 800 | #endif | 992 | #endif |
| 801 | 993 | ||
| 802 | dmi_scan_machine(); | ||
| 803 | |||
| 804 | io_delay_init(); | 994 | io_delay_init(); |
| 805 | 995 | ||
| 806 | /* | 996 | /* |
| @@ -903,3 +1093,5 @@ void __init setup_arch(char **cmdline_p) | |||
| 903 | #endif | 1093 | #endif |
| 904 | #endif | 1094 | #endif |
| 905 | } | 1095 | } |
| 1096 | |||
| 1097 | |||
