linux/arch/x86/kernel/check.c
<<
>>
Prefs
   1#include <linux/module.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        char *end;
  31
  32        memory_corruption_check = simple_strtol(arg, &end, 10);
  33
  34        return (*end == 0) ? 0 : -EINVAL;
  35}
  36early_param("memory_corruption_check", set_corruption_check);
  37
  38static __init int set_corruption_check_period(char *arg)
  39{
  40        char *end;
  41
  42        corruption_check_period = simple_strtoul(arg, &end, 10);
  43
  44        return (*end == 0) ? 0 : -EINVAL;
  45}
  46early_param("memory_corruption_check_period", set_corruption_check_period);
  47
  48static __init int set_corruption_check_size(char *arg)
  49{
  50        char *end;
  51        unsigned size;
  52
  53        size = memparse(arg, &end);
  54
  55        if (*end == '\0')
  56                corruption_check_size = size;
  57
  58        return (size == corruption_check_size) ? 0 : -EINVAL;
  59}
  60early_param("memory_corruption_check_size", set_corruption_check_size);
  61
  62
  63void __init setup_bios_corruption_check(void)
  64{
  65        u64 addr = PAGE_SIZE;   /* assume first page is reserved anyway */
  66
  67        if (memory_corruption_check == -1) {
  68                memory_corruption_check =
  69#ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
  70                        1
  71#else
  72                        0
  73#endif
  74                        ;
  75        }
  76
  77        if (corruption_check_size == 0)
  78                memory_corruption_check = 0;
  79
  80        if (!memory_corruption_check)
  81                return;
  82
  83        corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);
  84
  85        while (addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) {
  86                u64 size;
  87                addr = memblock_x86_find_in_range_size(addr, &size, PAGE_SIZE);
  88
  89                if (addr == MEMBLOCK_ERROR)
  90                        break;
  91
  92                if (addr >= corruption_check_size)
  93                        break;
  94
  95                if ((addr + size) > corruption_check_size)
  96                        size = corruption_check_size - addr;
  97
  98                memblock_x86_reserve_range(addr, addr + size, "SCAN RAM");
  99                scan_areas[num_scan_areas].addr = addr;
 100                scan_areas[num_scan_areas].size = size;
 101                num_scan_areas++;
 102
 103                /* Assume we've already mapped this early memory */
 104                memset(__va(addr), 0, size);
 105
 106                addr += size;
 107        }
 108
 109        if (num_scan_areas)
 110                printk(KERN_INFO "Scanning %d areas for low memory corruption\n", num_scan_areas);
 111}
 112
 113
 114void check_for_bios_corruption(void)
 115{
 116        int i;
 117        int corruption = 0;
 118
 119        if (!memory_corruption_check)
 120                return;
 121
 122        for (i = 0; i < num_scan_areas; i++) {
 123                unsigned long *addr = __va(scan_areas[i].addr);
 124                unsigned long size = scan_areas[i].size;
 125
 126                for (; size; addr++, size -= sizeof(unsigned long)) {
 127                        if (!*addr)
 128                                continue;
 129                        printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n",
 130                               addr, __pa(addr), *addr);
 131                        corruption = 1;
 132                        *addr = 0;
 133                }
 134        }
 135
 136        WARN_ONCE(corruption, KERN_ERR "Memory corruption detected in low memory\n");
 137}
 138
 139static void check_corruption(struct work_struct *dummy);
 140static DECLARE_DELAYED_WORK(bios_check_work, check_corruption);
 141
 142static void check_corruption(struct work_struct *dummy)
 143{
 144        check_for_bios_corruption();
 145        schedule_delayed_work(&bios_check_work,
 146                round_jiffies_relative(corruption_check_period*HZ));
 147}
 148
 149static int start_periodic_check_for_corruption(void)
 150{
 151        if (!num_scan_areas || !memory_corruption_check || corruption_check_period == 0)
 152                return 0;
 153
 154        printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n",
 155               corruption_check_period);
 156
 157        /* First time we run the checks right away */
 158        schedule_delayed_work(&bios_check_work, 0);
 159        return 0;
 160}
 161
 162module_init(start_periodic_check_for_corruption);
 163
 164