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