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