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