xref: /openbmc/linux/arch/x86/kernel/check.c (revision e3d786a3)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4 
5 #include <linux/init.h>
6 #include <linux/sched.h>
7 #include <linux/kthread.h>
8 #include <linux/workqueue.h>
9 #include <linux/memblock.h>
10 
11 #include <asm/proto.h>
12 
13 /*
14  * Some BIOSes seem to corrupt the low 64k of memory during events
15  * like suspend/resume and unplugging an HDMI cable.  Reserve all
16  * remaining free memory in that area and fill it with a distinct
17  * pattern.
18  */
19 #define MAX_SCAN_AREAS	8
20 
21 static int __read_mostly memory_corruption_check = -1;
22 
23 static unsigned __read_mostly corruption_check_size = 64*1024;
24 static unsigned __read_mostly corruption_check_period = 60; /* seconds */
25 
26 static struct scan_area {
27 	u64 addr;
28 	u64 size;
29 } scan_areas[MAX_SCAN_AREAS];
30 static int num_scan_areas;
31 
32 static __init int set_corruption_check(char *arg)
33 {
34 	ssize_t ret;
35 	unsigned long val;
36 
37 	if (!arg) {
38 		pr_err("memory_corruption_check config string not provided\n");
39 		return -EINVAL;
40 	}
41 
42 	ret = kstrtoul(arg, 10, &val);
43 	if (ret)
44 		return ret;
45 
46 	memory_corruption_check = val;
47 
48 	return 0;
49 }
50 early_param("memory_corruption_check", set_corruption_check);
51 
52 static __init int set_corruption_check_period(char *arg)
53 {
54 	ssize_t ret;
55 	unsigned long val;
56 
57 	if (!arg) {
58 		pr_err("memory_corruption_check_period config string not provided\n");
59 		return -EINVAL;
60 	}
61 
62 	ret = kstrtoul(arg, 10, &val);
63 	if (ret)
64 		return ret;
65 
66 	corruption_check_period = val;
67 	return 0;
68 }
69 early_param("memory_corruption_check_period", set_corruption_check_period);
70 
71 static __init int set_corruption_check_size(char *arg)
72 {
73 	char *end;
74 	unsigned size;
75 
76 	if (!arg) {
77 		pr_err("memory_corruption_check_size config string not provided\n");
78 		return -EINVAL;
79 	}
80 
81 	size = memparse(arg, &end);
82 
83 	if (*end == '\0')
84 		corruption_check_size = size;
85 
86 	return (size == corruption_check_size) ? 0 : -EINVAL;
87 }
88 early_param("memory_corruption_check_size", set_corruption_check_size);
89 
90 
91 void __init setup_bios_corruption_check(void)
92 {
93 	phys_addr_t start, end;
94 	u64 i;
95 
96 	if (memory_corruption_check == -1) {
97 		memory_corruption_check =
98 #ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
99 			1
100 #else
101 			0
102 #endif
103 			;
104 	}
105 
106 	if (corruption_check_size == 0)
107 		memory_corruption_check = 0;
108 
109 	if (!memory_corruption_check)
110 		return;
111 
112 	corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);
113 
114 	for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end,
115 				NULL) {
116 		start = clamp_t(phys_addr_t, round_up(start, PAGE_SIZE),
117 				PAGE_SIZE, corruption_check_size);
118 		end = clamp_t(phys_addr_t, round_down(end, PAGE_SIZE),
119 			      PAGE_SIZE, corruption_check_size);
120 		if (start >= end)
121 			continue;
122 
123 		memblock_reserve(start, end - start);
124 		scan_areas[num_scan_areas].addr = start;
125 		scan_areas[num_scan_areas].size = end - start;
126 
127 		/* Assume we've already mapped this early memory */
128 		memset(__va(start), 0, end - start);
129 
130 		if (++num_scan_areas >= MAX_SCAN_AREAS)
131 			break;
132 	}
133 
134 	if (num_scan_areas)
135 		pr_info("Scanning %d areas for low memory corruption\n", num_scan_areas);
136 }
137 
138 
139 void check_for_bios_corruption(void)
140 {
141 	int i;
142 	int corruption = 0;
143 
144 	if (!memory_corruption_check)
145 		return;
146 
147 	for (i = 0; i < num_scan_areas; i++) {
148 		unsigned long *addr = __va(scan_areas[i].addr);
149 		unsigned long size = scan_areas[i].size;
150 
151 		for (; size; addr++, size -= sizeof(unsigned long)) {
152 			if (!*addr)
153 				continue;
154 			pr_err("Corrupted low memory at %p (%lx phys) = %08lx\n", addr, __pa(addr), *addr);
155 			corruption = 1;
156 			*addr = 0;
157 		}
158 	}
159 
160 	WARN_ONCE(corruption, KERN_ERR "Memory corruption detected in low memory\n");
161 }
162 
163 static void check_corruption(struct work_struct *dummy);
164 static DECLARE_DELAYED_WORK(bios_check_work, check_corruption);
165 
166 static void check_corruption(struct work_struct *dummy)
167 {
168 	check_for_bios_corruption();
169 	schedule_delayed_work(&bios_check_work,
170 		round_jiffies_relative(corruption_check_period*HZ));
171 }
172 
173 static int start_periodic_check_for_corruption(void)
174 {
175 	if (!num_scan_areas || !memory_corruption_check || corruption_check_period == 0)
176 		return 0;
177 
178 	pr_info("Scanning for low memory corruption every %d seconds\n", corruption_check_period);
179 
180 	/* First time we run the checks right away */
181 	schedule_delayed_work(&bios_check_work, 0);
182 
183 	return 0;
184 }
185 device_initcall(start_periodic_check_for_corruption);
186 
187