cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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