1 #include <linux/module.h> 2 #include <linux/sched.h> 3 #include <linux/kthread.h> 4 #include <linux/workqueue.h> 5 #include <asm/e820.h> 6 #include <asm/proto.h> 7 8 /* 9 * Some BIOSes seem to corrupt the low 64k of memory during events 10 * like suspend/resume and unplugging an HDMI cable. Reserve all 11 * remaining free memory in that area and fill it with a distinct 12 * pattern. 13 */ 14 #ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION 15 #define MAX_SCAN_AREAS 8 16 17 static int __read_mostly memory_corruption_check = -1; 18 19 static unsigned __read_mostly corruption_check_size = 64*1024; 20 static unsigned __read_mostly corruption_check_period = 60; /* seconds */ 21 22 static struct e820entry scan_areas[MAX_SCAN_AREAS]; 23 static int num_scan_areas; 24 25 26 static int set_corruption_check(char *arg) 27 { 28 char *end; 29 30 memory_corruption_check = simple_strtol(arg, &end, 10); 31 32 return (*end == 0) ? 0 : -EINVAL; 33 } 34 early_param("memory_corruption_check", set_corruption_check); 35 36 static int set_corruption_check_period(char *arg) 37 { 38 char *end; 39 40 corruption_check_period = simple_strtoul(arg, &end, 10); 41 42 return (*end == 0) ? 0 : -EINVAL; 43 } 44 early_param("memory_corruption_check_period", set_corruption_check_period); 45 46 static int set_corruption_check_size(char *arg) 47 { 48 char *end; 49 unsigned size; 50 51 size = memparse(arg, &end); 52 53 if (*end == '\0') 54 corruption_check_size = size; 55 56 return (size == corruption_check_size) ? 0 : -EINVAL; 57 } 58 early_param("memory_corruption_check_size", set_corruption_check_size); 59 60 61 void __init setup_bios_corruption_check(void) 62 { 63 u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */ 64 65 if (memory_corruption_check == -1) { 66 memory_corruption_check = 67 #ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK 68 1 69 #else 70 0 71 #endif 72 ; 73 } 74 75 if (corruption_check_size == 0) 76 memory_corruption_check = 0; 77 78 if (!memory_corruption_check) 79 return; 80 81 corruption_check_size = round_up(corruption_check_size, PAGE_SIZE); 82 83 while (addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) { 84 u64 size; 85 addr = find_e820_area_size(addr, &size, PAGE_SIZE); 86 87 if (addr == 0) 88 break; 89 90 if ((addr + size) > corruption_check_size) 91 size = corruption_check_size - addr; 92 93 if (size == 0) 94 break; 95 96 e820_update_range(addr, size, E820_RAM, E820_RESERVED); 97 scan_areas[num_scan_areas].addr = addr; 98 scan_areas[num_scan_areas].size = size; 99 num_scan_areas++; 100 101 /* Assume we've already mapped this early memory */ 102 memset(__va(addr), 0, size); 103 104 addr += size; 105 } 106 107 printk(KERN_INFO "Scanning %d areas for low memory corruption\n", 108 num_scan_areas); 109 update_e820(); 110 } 111 112 113 void check_for_bios_corruption(void) 114 { 115 int i; 116 int corruption = 0; 117 118 printk("dot\n"); 119 120 if (!memory_corruption_check) 121 return; 122 123 for (i = 0; i < num_scan_areas; i++) { 124 unsigned long *addr = __va(scan_areas[i].addr); 125 unsigned long size = scan_areas[i].size; 126 127 for (; size; addr++, size -= sizeof(unsigned long)) { 128 if (!*addr) 129 continue; 130 printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n", 131 addr, __pa(addr), *addr); 132 corruption = 1; 133 *addr = 0; 134 } 135 } 136 137 WARN(corruption, KERN_ERR "Memory corruption detected in low memory\n"); 138 } 139 140 static void check_corruption(struct work_struct *dummy); 141 static DECLARE_DELAYED_WORK(bios_check_work, check_corruption); 142 143 static void check_corruption(struct work_struct *dummy) 144 { 145 check_for_bios_corruption(); 146 schedule_delayed_work(&bios_check_work, 147 round_jiffies_relative(corruption_check_period*HZ)); 148 } 149 150 static int start_periodic_check_for_corruption(void) 151 { 152 if (!memory_corruption_check || corruption_check_period == 0) 153 return 0; 154 155 printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n", 156 corruption_check_period); 157 158 /* First time we run the checks right away */ 159 schedule_delayed_work(&bios_check_work, 0); 160 return 0; 161 } 162 163 module_init(start_periodic_check_for_corruption); 164 #endif 165 166