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

if.c (9642B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/capability.h>
      3#include <linux/seq_file.h>
      4#include <linux/uaccess.h>
      5#include <linux/proc_fs.h>
      6#include <linux/ctype.h>
      7#include <linux/string.h>
      8#include <linux/slab.h>
      9#include <linux/init.h>
     10
     11#define LINE_SIZE 80
     12
     13#include <asm/mtrr.h>
     14
     15#include "mtrr.h"
     16
     17#define FILE_FCOUNT(f) (((struct seq_file *)((f)->private_data))->private)
     18
     19static const char *const mtrr_strings[MTRR_NUM_TYPES] =
     20{
     21	"uncachable",		/* 0 */
     22	"write-combining",	/* 1 */
     23	"?",			/* 2 */
     24	"?",			/* 3 */
     25	"write-through",	/* 4 */
     26	"write-protect",	/* 5 */
     27	"write-back",		/* 6 */
     28};
     29
     30const char *mtrr_attrib_to_str(int x)
     31{
     32	return (x <= 6) ? mtrr_strings[x] : "?";
     33}
     34
     35#ifdef CONFIG_PROC_FS
     36
     37static int
     38mtrr_file_add(unsigned long base, unsigned long size,
     39	      unsigned int type, bool increment, struct file *file, int page)
     40{
     41	unsigned int *fcount = FILE_FCOUNT(file);
     42	int reg, max;
     43
     44	max = num_var_ranges;
     45	if (fcount == NULL) {
     46		fcount = kcalloc(max, sizeof(*fcount), GFP_KERNEL);
     47		if (!fcount)
     48			return -ENOMEM;
     49		FILE_FCOUNT(file) = fcount;
     50	}
     51	if (!page) {
     52		if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
     53			return -EINVAL;
     54		base >>= PAGE_SHIFT;
     55		size >>= PAGE_SHIFT;
     56	}
     57	reg = mtrr_add_page(base, size, type, true);
     58	if (reg >= 0)
     59		++fcount[reg];
     60	return reg;
     61}
     62
     63static int
     64mtrr_file_del(unsigned long base, unsigned long size,
     65	      struct file *file, int page)
     66{
     67	unsigned int *fcount = FILE_FCOUNT(file);
     68	int reg;
     69
     70	if (!page) {
     71		if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
     72			return -EINVAL;
     73		base >>= PAGE_SHIFT;
     74		size >>= PAGE_SHIFT;
     75	}
     76	reg = mtrr_del_page(-1, base, size);
     77	if (reg < 0)
     78		return reg;
     79	if (fcount == NULL)
     80		return reg;
     81	if (fcount[reg] < 1)
     82		return -EINVAL;
     83	--fcount[reg];
     84	return reg;
     85}
     86
     87/*
     88 * seq_file can seek but we ignore it.
     89 *
     90 * Format of control line:
     91 *    "base=%Lx size=%Lx type=%s" or "disable=%d"
     92 */
     93static ssize_t
     94mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
     95{
     96	int i, err;
     97	unsigned long reg;
     98	unsigned long long base, size;
     99	char *ptr;
    100	char line[LINE_SIZE];
    101	int length;
    102	size_t linelen;
    103
    104	memset(line, 0, LINE_SIZE);
    105
    106	len = min_t(size_t, len, LINE_SIZE - 1);
    107	length = strncpy_from_user(line, buf, len);
    108	if (length < 0)
    109		return length;
    110
    111	linelen = strlen(line);
    112	ptr = line + linelen - 1;
    113	if (linelen && *ptr == '\n')
    114		*ptr = '\0';
    115
    116	if (!strncmp(line, "disable=", 8)) {
    117		reg = simple_strtoul(line + 8, &ptr, 0);
    118		err = mtrr_del_page(reg, 0, 0);
    119		if (err < 0)
    120			return err;
    121		return len;
    122	}
    123
    124	if (strncmp(line, "base=", 5))
    125		return -EINVAL;
    126
    127	base = simple_strtoull(line + 5, &ptr, 0);
    128	ptr = skip_spaces(ptr);
    129
    130	if (strncmp(ptr, "size=", 5))
    131		return -EINVAL;
    132
    133	size = simple_strtoull(ptr + 5, &ptr, 0);
    134	if ((base & 0xfff) || (size & 0xfff))
    135		return -EINVAL;
    136	ptr = skip_spaces(ptr);
    137
    138	if (strncmp(ptr, "type=", 5))
    139		return -EINVAL;
    140	ptr = skip_spaces(ptr + 5);
    141
    142	i = match_string(mtrr_strings, MTRR_NUM_TYPES, ptr);
    143	if (i < 0)
    144		return i;
    145
    146	base >>= PAGE_SHIFT;
    147	size >>= PAGE_SHIFT;
    148	err = mtrr_add_page((unsigned long)base, (unsigned long)size, i, true);
    149	if (err < 0)
    150		return err;
    151	return len;
    152}
    153
    154static long
    155mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
    156{
    157	int err = 0;
    158	mtrr_type type;
    159	unsigned long base;
    160	unsigned long size;
    161	struct mtrr_sentry sentry;
    162	struct mtrr_gentry gentry;
    163	void __user *arg = (void __user *) __arg;
    164
    165	memset(&gentry, 0, sizeof(gentry));
    166
    167	switch (cmd) {
    168	case MTRRIOC_ADD_ENTRY:
    169	case MTRRIOC_SET_ENTRY:
    170	case MTRRIOC_DEL_ENTRY:
    171	case MTRRIOC_KILL_ENTRY:
    172	case MTRRIOC_ADD_PAGE_ENTRY:
    173	case MTRRIOC_SET_PAGE_ENTRY:
    174	case MTRRIOC_DEL_PAGE_ENTRY:
    175	case MTRRIOC_KILL_PAGE_ENTRY:
    176		if (copy_from_user(&sentry, arg, sizeof(sentry)))
    177			return -EFAULT;
    178		break;
    179	case MTRRIOC_GET_ENTRY:
    180	case MTRRIOC_GET_PAGE_ENTRY:
    181		if (copy_from_user(&gentry, arg, sizeof(gentry)))
    182			return -EFAULT;
    183		break;
    184#ifdef CONFIG_COMPAT
    185	case MTRRIOC32_ADD_ENTRY:
    186	case MTRRIOC32_SET_ENTRY:
    187	case MTRRIOC32_DEL_ENTRY:
    188	case MTRRIOC32_KILL_ENTRY:
    189	case MTRRIOC32_ADD_PAGE_ENTRY:
    190	case MTRRIOC32_SET_PAGE_ENTRY:
    191	case MTRRIOC32_DEL_PAGE_ENTRY:
    192	case MTRRIOC32_KILL_PAGE_ENTRY: {
    193		struct mtrr_sentry32 __user *s32;
    194
    195		s32 = (struct mtrr_sentry32 __user *)__arg;
    196		err = get_user(sentry.base, &s32->base);
    197		err |= get_user(sentry.size, &s32->size);
    198		err |= get_user(sentry.type, &s32->type);
    199		if (err)
    200			return err;
    201		break;
    202	}
    203	case MTRRIOC32_GET_ENTRY:
    204	case MTRRIOC32_GET_PAGE_ENTRY: {
    205		struct mtrr_gentry32 __user *g32;
    206
    207		g32 = (struct mtrr_gentry32 __user *)__arg;
    208		err = get_user(gentry.regnum, &g32->regnum);
    209		err |= get_user(gentry.base, &g32->base);
    210		err |= get_user(gentry.size, &g32->size);
    211		err |= get_user(gentry.type, &g32->type);
    212		if (err)
    213			return err;
    214		break;
    215	}
    216#endif
    217	}
    218
    219	switch (cmd) {
    220	default:
    221		return -ENOTTY;
    222	case MTRRIOC_ADD_ENTRY:
    223#ifdef CONFIG_COMPAT
    224	case MTRRIOC32_ADD_ENTRY:
    225#endif
    226		err =
    227		    mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
    228				  file, 0);
    229		break;
    230	case MTRRIOC_SET_ENTRY:
    231#ifdef CONFIG_COMPAT
    232	case MTRRIOC32_SET_ENTRY:
    233#endif
    234		err = mtrr_add(sentry.base, sentry.size, sentry.type, false);
    235		break;
    236	case MTRRIOC_DEL_ENTRY:
    237#ifdef CONFIG_COMPAT
    238	case MTRRIOC32_DEL_ENTRY:
    239#endif
    240		err = mtrr_file_del(sentry.base, sentry.size, file, 0);
    241		break;
    242	case MTRRIOC_KILL_ENTRY:
    243#ifdef CONFIG_COMPAT
    244	case MTRRIOC32_KILL_ENTRY:
    245#endif
    246		err = mtrr_del(-1, sentry.base, sentry.size);
    247		break;
    248	case MTRRIOC_GET_ENTRY:
    249#ifdef CONFIG_COMPAT
    250	case MTRRIOC32_GET_ENTRY:
    251#endif
    252		if (gentry.regnum >= num_var_ranges)
    253			return -EINVAL;
    254		mtrr_if->get(gentry.regnum, &base, &size, &type);
    255
    256		/* Hide entries that go above 4GB */
    257		if (base + size - 1 >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT))
    258		    || size >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT)))
    259			gentry.base = gentry.size = gentry.type = 0;
    260		else {
    261			gentry.base = base << PAGE_SHIFT;
    262			gentry.size = size << PAGE_SHIFT;
    263			gentry.type = type;
    264		}
    265
    266		break;
    267	case MTRRIOC_ADD_PAGE_ENTRY:
    268#ifdef CONFIG_COMPAT
    269	case MTRRIOC32_ADD_PAGE_ENTRY:
    270#endif
    271		err =
    272		    mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
    273				  file, 1);
    274		break;
    275	case MTRRIOC_SET_PAGE_ENTRY:
    276#ifdef CONFIG_COMPAT
    277	case MTRRIOC32_SET_PAGE_ENTRY:
    278#endif
    279		err =
    280		    mtrr_add_page(sentry.base, sentry.size, sentry.type, false);
    281		break;
    282	case MTRRIOC_DEL_PAGE_ENTRY:
    283#ifdef CONFIG_COMPAT
    284	case MTRRIOC32_DEL_PAGE_ENTRY:
    285#endif
    286		err = mtrr_file_del(sentry.base, sentry.size, file, 1);
    287		break;
    288	case MTRRIOC_KILL_PAGE_ENTRY:
    289#ifdef CONFIG_COMPAT
    290	case MTRRIOC32_KILL_PAGE_ENTRY:
    291#endif
    292		err = mtrr_del_page(-1, sentry.base, sentry.size);
    293		break;
    294	case MTRRIOC_GET_PAGE_ENTRY:
    295#ifdef CONFIG_COMPAT
    296	case MTRRIOC32_GET_PAGE_ENTRY:
    297#endif
    298		if (gentry.regnum >= num_var_ranges)
    299			return -EINVAL;
    300		mtrr_if->get(gentry.regnum, &base, &size, &type);
    301		/* Hide entries that would overflow */
    302		if (size != (__typeof__(gentry.size))size)
    303			gentry.base = gentry.size = gentry.type = 0;
    304		else {
    305			gentry.base = base;
    306			gentry.size = size;
    307			gentry.type = type;
    308		}
    309		break;
    310	}
    311
    312	if (err)
    313		return err;
    314
    315	switch (cmd) {
    316	case MTRRIOC_GET_ENTRY:
    317	case MTRRIOC_GET_PAGE_ENTRY:
    318		if (copy_to_user(arg, &gentry, sizeof(gentry)))
    319			err = -EFAULT;
    320		break;
    321#ifdef CONFIG_COMPAT
    322	case MTRRIOC32_GET_ENTRY:
    323	case MTRRIOC32_GET_PAGE_ENTRY: {
    324		struct mtrr_gentry32 __user *g32;
    325
    326		g32 = (struct mtrr_gentry32 __user *)__arg;
    327		err = put_user(gentry.base, &g32->base);
    328		err |= put_user(gentry.size, &g32->size);
    329		err |= put_user(gentry.regnum, &g32->regnum);
    330		err |= put_user(gentry.type, &g32->type);
    331		break;
    332	}
    333#endif
    334	}
    335	return err;
    336}
    337
    338static int mtrr_close(struct inode *ino, struct file *file)
    339{
    340	unsigned int *fcount = FILE_FCOUNT(file);
    341	int i, max;
    342
    343	if (fcount != NULL) {
    344		max = num_var_ranges;
    345		for (i = 0; i < max; ++i) {
    346			while (fcount[i] > 0) {
    347				mtrr_del(i, 0, 0);
    348				--fcount[i];
    349			}
    350		}
    351		kfree(fcount);
    352		FILE_FCOUNT(file) = NULL;
    353	}
    354	return single_release(ino, file);
    355}
    356
    357static int mtrr_seq_show(struct seq_file *seq, void *offset)
    358{
    359	char factor;
    360	int i, max;
    361	mtrr_type type;
    362	unsigned long base, size;
    363
    364	max = num_var_ranges;
    365	for (i = 0; i < max; i++) {
    366		mtrr_if->get(i, &base, &size, &type);
    367		if (size == 0) {
    368			mtrr_usage_table[i] = 0;
    369			continue;
    370		}
    371		if (size < (0x100000 >> PAGE_SHIFT)) {
    372			/* less than 1MB */
    373			factor = 'K';
    374			size <<= PAGE_SHIFT - 10;
    375		} else {
    376			factor = 'M';
    377			size >>= 20 - PAGE_SHIFT;
    378		}
    379		/* Base can be > 32bit */
    380		seq_printf(seq, "reg%02i: base=0x%06lx000 (%5luMB), size=%5lu%cB, count=%d: %s\n",
    381			   i, base, base >> (20 - PAGE_SHIFT),
    382			   size, factor,
    383			   mtrr_usage_table[i], mtrr_attrib_to_str(type));
    384	}
    385	return 0;
    386}
    387
    388static int mtrr_open(struct inode *inode, struct file *file)
    389{
    390	if (!mtrr_if)
    391		return -EIO;
    392	if (!mtrr_if->get)
    393		return -ENXIO;
    394	if (!capable(CAP_SYS_ADMIN))
    395		return -EPERM;
    396	return single_open(file, mtrr_seq_show, NULL);
    397}
    398
    399static const struct proc_ops mtrr_proc_ops = {
    400	.proc_open		= mtrr_open,
    401	.proc_read		= seq_read,
    402	.proc_lseek		= seq_lseek,
    403	.proc_write		= mtrr_write,
    404	.proc_ioctl		= mtrr_ioctl,
    405#ifdef CONFIG_COMPAT
    406	.proc_compat_ioctl	= mtrr_ioctl,
    407#endif
    408	.proc_release		= mtrr_close,
    409};
    410
    411static int __init mtrr_if_init(void)
    412{
    413	struct cpuinfo_x86 *c = &boot_cpu_data;
    414
    415	if ((!cpu_has(c, X86_FEATURE_MTRR)) &&
    416	    (!cpu_has(c, X86_FEATURE_K6_MTRR)) &&
    417	    (!cpu_has(c, X86_FEATURE_CYRIX_ARR)) &&
    418	    (!cpu_has(c, X86_FEATURE_CENTAUR_MCR)))
    419		return -ENODEV;
    420
    421	proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_proc_ops);
    422	return 0;
    423}
    424arch_initcall(mtrr_if_init);
    425#endif			/*  CONFIG_PROC_FS  */