blob: 55eed1752b43d554a0ed6112d5c682312acbeecc [file] [log] [blame]
Arjan van de Ven6784f7d2008-10-05 11:33:42 -07001#include <linux/module.h>
2#include <linux/sched.h>
Arjan van de Ven304e6292008-10-05 12:09:03 -07003#include <linux/kthread.h>
4#include <linux/workqueue.h>
Arjan van de Ven6784f7d2008-10-05 11:33:42 -07005#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
17static int __read_mostly memory_corruption_check = -1;
18
19static unsigned __read_mostly corruption_check_size = 64*1024;
20static unsigned __read_mostly corruption_check_period = 60; /* seconds */
21
22static struct e820entry scan_areas[MAX_SCAN_AREAS];
23static int num_scan_areas;
24
25
26static 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}
34early_param("memory_corruption_check", set_corruption_check);
35
36static 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}
44early_param("memory_corruption_check_period", set_corruption_check_period);
45
46static 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}
58early_param("memory_corruption_check_size", set_corruption_check_size);
59
60
61void __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
Arjan van de Ven6784f7d2008-10-05 11:33:42 -0700112
113void check_for_bios_corruption(void)
114{
115 int i;
116 int corruption = 0;
117
Arjan van de Ven304e6292008-10-05 12:09:03 -0700118 printk("dot\n");
119
Arjan van de Ven6784f7d2008-10-05 11:33:42 -0700120 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
Arjan van de Ven304e6292008-10-05 12:09:03 -0700140static void check_corruption(struct work_struct *dummy);
141static DECLARE_DELAYED_WORK(bios_check_work, check_corruption);
142
143static void check_corruption(struct work_struct *dummy)
Arjan van de Ven6784f7d2008-10-05 11:33:42 -0700144{
145 check_for_bios_corruption();
Arjan van de Ven304e6292008-10-05 12:09:03 -0700146 schedule_delayed_work(&bios_check_work,
147 round_jiffies_relative(corruption_check_period*HZ));
Arjan van de Ven6784f7d2008-10-05 11:33:42 -0700148}
149
Arjan van de Ven304e6292008-10-05 12:09:03 -0700150static int start_periodic_check_for_corruption(void)
Arjan van de Ven6784f7d2008-10-05 11:33:42 -0700151{
152 if (!memory_corruption_check || corruption_check_period == 0)
Arjan van de Ven304e6292008-10-05 12:09:03 -0700153 return 0;
Arjan van de Ven6784f7d2008-10-05 11:33:42 -0700154
155 printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n",
156 corruption_check_period);
157
Arjan van de Ven304e6292008-10-05 12:09:03 -0700158 /* First time we run the checks right away */
159 schedule_delayed_work(&bios_check_work, 0);
160 return 0;
Arjan van de Ven6784f7d2008-10-05 11:33:42 -0700161}
Arjan van de Ven304e6292008-10-05 12:09:03 -0700162
163module_init(start_periodic_check_for_corruption);
Arjan van de Ven6784f7d2008-10-05 11:33:42 -0700164#endif
165