xref: /linux/arch/x86/kernel/cpu/mtrr/if.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
22ec1df41SThomas Gleixner #include <linux/capability.h>
32ec1df41SThomas Gleixner #include <linux/seq_file.h>
426dc67edSJaswinder Singh Rajput #include <linux/uaccess.h>
526dc67edSJaswinder Singh Rajput #include <linux/proc_fs.h>
626dc67edSJaswinder Singh Rajput #include <linux/ctype.h>
7e7d2860bSAndré Goddard Rosa #include <linux/string.h>
85a0e3ad6STejun Heo #include <linux/slab.h>
926dc67edSJaswinder Singh Rajput #include <linux/init.h>
102ec1df41SThomas Gleixner 
112ec1df41SThomas Gleixner #define LINE_SIZE 80
122ec1df41SThomas Gleixner 
132ec1df41SThomas Gleixner #include <asm/mtrr.h>
1426dc67edSJaswinder Singh Rajput 
152ec1df41SThomas Gleixner #include "mtrr.h"
162ec1df41SThomas Gleixner 
172ec1df41SThomas Gleixner #define FILE_FCOUNT(f) (((struct seq_file *)((f)->private_data))->private)
182ec1df41SThomas Gleixner 
192ec1df41SThomas Gleixner static const char *const mtrr_strings[MTRR_NUM_TYPES] =
202ec1df41SThomas Gleixner {
212ec1df41SThomas Gleixner 	"uncachable",		/* 0 */
222ec1df41SThomas Gleixner 	"write-combining",	/* 1 */
232ec1df41SThomas Gleixner 	"?",			/* 2 */
242ec1df41SThomas Gleixner 	"?",			/* 3 */
252ec1df41SThomas Gleixner 	"write-through",	/* 4 */
262ec1df41SThomas Gleixner 	"write-protect",	/* 5 */
272ec1df41SThomas Gleixner 	"write-back",		/* 6 */
282ec1df41SThomas Gleixner };
292ec1df41SThomas Gleixner 
mtrr_attrib_to_str(int x)302ec1df41SThomas Gleixner const char *mtrr_attrib_to_str(int x)
312ec1df41SThomas Gleixner {
322ec1df41SThomas Gleixner 	return (x <= 6) ? mtrr_strings[x] : "?";
332ec1df41SThomas Gleixner }
342ec1df41SThomas Gleixner 
352ec1df41SThomas Gleixner #ifdef CONFIG_PROC_FS
362ec1df41SThomas Gleixner 
372ec1df41SThomas Gleixner static int
mtrr_file_add(unsigned long base,unsigned long size,unsigned int type,bool increment,struct file * file,int page)382ec1df41SThomas Gleixner mtrr_file_add(unsigned long base, unsigned long size,
392d2ee8deSPaul Jimenez 	      unsigned int type, bool increment, struct file *file, int page)
402ec1df41SThomas Gleixner {
412ec1df41SThomas Gleixner 	unsigned int *fcount = FILE_FCOUNT(file);
4226dc67edSJaswinder Singh Rajput 	int reg, max;
432ec1df41SThomas Gleixner 
442ec1df41SThomas Gleixner 	max = num_var_ranges;
452ec1df41SThomas Gleixner 	if (fcount == NULL) {
466396bb22SKees Cook 		fcount = kcalloc(max, sizeof(*fcount), GFP_KERNEL);
472ec1df41SThomas Gleixner 		if (!fcount)
482ec1df41SThomas Gleixner 			return -ENOMEM;
492ec1df41SThomas Gleixner 		FILE_FCOUNT(file) = fcount;
502ec1df41SThomas Gleixner 	}
512ec1df41SThomas Gleixner 	if (!page) {
522ec1df41SThomas Gleixner 		if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
532ec1df41SThomas Gleixner 			return -EINVAL;
542ec1df41SThomas Gleixner 		base >>= PAGE_SHIFT;
552ec1df41SThomas Gleixner 		size >>= PAGE_SHIFT;
562ec1df41SThomas Gleixner 	}
572d2ee8deSPaul Jimenez 	reg = mtrr_add_page(base, size, type, true);
582ec1df41SThomas Gleixner 	if (reg >= 0)
592ec1df41SThomas Gleixner 		++fcount[reg];
602ec1df41SThomas Gleixner 	return reg;
612ec1df41SThomas Gleixner }
622ec1df41SThomas Gleixner 
632ec1df41SThomas Gleixner static int
mtrr_file_del(unsigned long base,unsigned long size,struct file * file,int page)642ec1df41SThomas Gleixner mtrr_file_del(unsigned long base, unsigned long size,
652ec1df41SThomas Gleixner 	      struct file *file, int page)
662ec1df41SThomas Gleixner {
672ec1df41SThomas Gleixner 	unsigned int *fcount = FILE_FCOUNT(file);
6826dc67edSJaswinder Singh Rajput 	int reg;
692ec1df41SThomas Gleixner 
702ec1df41SThomas Gleixner 	if (!page) {
712ec1df41SThomas Gleixner 		if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
722ec1df41SThomas Gleixner 			return -EINVAL;
732ec1df41SThomas Gleixner 		base >>= PAGE_SHIFT;
742ec1df41SThomas Gleixner 		size >>= PAGE_SHIFT;
752ec1df41SThomas Gleixner 	}
762ec1df41SThomas Gleixner 	reg = mtrr_del_page(-1, base, size);
772ec1df41SThomas Gleixner 	if (reg < 0)
782ec1df41SThomas Gleixner 		return reg;
792ec1df41SThomas Gleixner 	if (fcount == NULL)
802ec1df41SThomas Gleixner 		return reg;
812ec1df41SThomas Gleixner 	if (fcount[reg] < 1)
822ec1df41SThomas Gleixner 		return -EINVAL;
832ec1df41SThomas Gleixner 	--fcount[reg];
842ec1df41SThomas Gleixner 	return reg;
852ec1df41SThomas Gleixner }
862ec1df41SThomas Gleixner 
8726dc67edSJaswinder Singh Rajput /*
8826dc67edSJaswinder Singh Rajput  * seq_file can seek but we ignore it.
8926dc67edSJaswinder Singh Rajput  *
9026dc67edSJaswinder Singh Rajput  * Format of control line:
9126dc67edSJaswinder Singh Rajput  *    "base=%Lx size=%Lx type=%s" or "disable=%d"
9226dc67edSJaswinder Singh Rajput  */
932ec1df41SThomas Gleixner static ssize_t
mtrr_write(struct file * file,const char __user * buf,size_t len,loff_t * ppos)942ec1df41SThomas Gleixner mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
952ec1df41SThomas Gleixner {
962ec1df41SThomas Gleixner 	int i, err;
972ec1df41SThomas Gleixner 	unsigned long reg;
982ec1df41SThomas Gleixner 	unsigned long long base, size;
992ec1df41SThomas Gleixner 	char *ptr;
1002ec1df41SThomas Gleixner 	char line[LINE_SIZE];
10111879ba5SArjan van de Ven 	int length;
1022ec1df41SThomas Gleixner 	size_t linelen;
1032ec1df41SThomas Gleixner 
1042ec1df41SThomas Gleixner 	memset(line, 0, LINE_SIZE);
10511879ba5SArjan van de Ven 
10615279df6SJann Horn 	len = min_t(size_t, len, LINE_SIZE - 1);
10715279df6SJann Horn 	length = strncpy_from_user(line, buf, len);
10811879ba5SArjan van de Ven 	if (length < 0)
1097f8ec5a4SAndy Shevchenko 		return length;
11026dc67edSJaswinder Singh Rajput 
1112ec1df41SThomas Gleixner 	linelen = strlen(line);
1122ec1df41SThomas Gleixner 	ptr = line + linelen - 1;
1132ec1df41SThomas Gleixner 	if (linelen && *ptr == '\n')
1142ec1df41SThomas Gleixner 		*ptr = '\0';
11526dc67edSJaswinder Singh Rajput 
1162ec1df41SThomas Gleixner 	if (!strncmp(line, "disable=", 8)) {
1172ec1df41SThomas Gleixner 		reg = simple_strtoul(line + 8, &ptr, 0);
1182ec1df41SThomas Gleixner 		err = mtrr_del_page(reg, 0, 0);
1192ec1df41SThomas Gleixner 		if (err < 0)
1202ec1df41SThomas Gleixner 			return err;
1212ec1df41SThomas Gleixner 		return len;
1222ec1df41SThomas Gleixner 	}
12326dc67edSJaswinder Singh Rajput 
1242ec1df41SThomas Gleixner 	if (strncmp(line, "base=", 5))
1252ec1df41SThomas Gleixner 		return -EINVAL;
12626dc67edSJaswinder Singh Rajput 
1272ec1df41SThomas Gleixner 	base = simple_strtoull(line + 5, &ptr, 0);
128e7d2860bSAndré Goddard Rosa 	ptr = skip_spaces(ptr);
12926dc67edSJaswinder Singh Rajput 
1302ec1df41SThomas Gleixner 	if (strncmp(ptr, "size=", 5))
1312ec1df41SThomas Gleixner 		return -EINVAL;
13226dc67edSJaswinder Singh Rajput 
1332ec1df41SThomas Gleixner 	size = simple_strtoull(ptr + 5, &ptr, 0);
1342ec1df41SThomas Gleixner 	if ((base & 0xfff) || (size & 0xfff))
1352ec1df41SThomas Gleixner 		return -EINVAL;
136e7d2860bSAndré Goddard Rosa 	ptr = skip_spaces(ptr);
13726dc67edSJaswinder Singh Rajput 
1382ec1df41SThomas Gleixner 	if (strncmp(ptr, "type=", 5))
1392ec1df41SThomas Gleixner 		return -EINVAL;
140e7d2860bSAndré Goddard Rosa 	ptr = skip_spaces(ptr + 5);
14126dc67edSJaswinder Singh Rajput 
14213a4db9dSAndy Shevchenko 	i = match_string(mtrr_strings, MTRR_NUM_TYPES, ptr);
14313a4db9dSAndy Shevchenko 	if (i < 0)
14413a4db9dSAndy Shevchenko 		return i;
14513a4db9dSAndy Shevchenko 
1462ec1df41SThomas Gleixner 	base >>= PAGE_SHIFT;
1472ec1df41SThomas Gleixner 	size >>= PAGE_SHIFT;
14826dc67edSJaswinder Singh Rajput 	err = mtrr_add_page((unsigned long)base, (unsigned long)size, i, true);
1492ec1df41SThomas Gleixner 	if (err < 0)
1502ec1df41SThomas Gleixner 		return err;
1512ec1df41SThomas Gleixner 	return len;
1522ec1df41SThomas Gleixner }
1532ec1df41SThomas Gleixner 
1542ec1df41SThomas Gleixner static long
mtrr_ioctl(struct file * file,unsigned int cmd,unsigned long __arg)1552ec1df41SThomas Gleixner mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
1562ec1df41SThomas Gleixner {
1572ec1df41SThomas Gleixner 	int err = 0;
1582ec1df41SThomas Gleixner 	mtrr_type type;
159b263b31eSH. Peter Anvin 	unsigned long base;
1602ec1df41SThomas Gleixner 	unsigned long size;
1612ec1df41SThomas Gleixner 	struct mtrr_sentry sentry;
1622ec1df41SThomas Gleixner 	struct mtrr_gentry gentry;
1632ec1df41SThomas Gleixner 	void __user *arg = (void __user *) __arg;
1642ec1df41SThomas Gleixner 
16532043fa0SColin Ian King 	memset(&gentry, 0, sizeof(gentry));
16632043fa0SColin Ian King 
1672ec1df41SThomas Gleixner 	switch (cmd) {
1682ec1df41SThomas Gleixner 	case MTRRIOC_ADD_ENTRY:
1692ec1df41SThomas Gleixner 	case MTRRIOC_SET_ENTRY:
1702ec1df41SThomas Gleixner 	case MTRRIOC_DEL_ENTRY:
1712ec1df41SThomas Gleixner 	case MTRRIOC_KILL_ENTRY:
1722ec1df41SThomas Gleixner 	case MTRRIOC_ADD_PAGE_ENTRY:
1732ec1df41SThomas Gleixner 	case MTRRIOC_SET_PAGE_ENTRY:
1742ec1df41SThomas Gleixner 	case MTRRIOC_DEL_PAGE_ENTRY:
1752ec1df41SThomas Gleixner 	case MTRRIOC_KILL_PAGE_ENTRY:
1760e96f31eSJordan Borgner 		if (copy_from_user(&sentry, arg, sizeof(sentry)))
1772ec1df41SThomas Gleixner 			return -EFAULT;
1782ec1df41SThomas Gleixner 		break;
1792ec1df41SThomas Gleixner 	case MTRRIOC_GET_ENTRY:
1802ec1df41SThomas Gleixner 	case MTRRIOC_GET_PAGE_ENTRY:
1810e96f31eSJordan Borgner 		if (copy_from_user(&gentry, arg, sizeof(gentry)))
1822ec1df41SThomas Gleixner 			return -EFAULT;
1832ec1df41SThomas Gleixner 		break;
1842ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
1852ec1df41SThomas Gleixner 	case MTRRIOC32_ADD_ENTRY:
1862ec1df41SThomas Gleixner 	case MTRRIOC32_SET_ENTRY:
1872ec1df41SThomas Gleixner 	case MTRRIOC32_DEL_ENTRY:
1882ec1df41SThomas Gleixner 	case MTRRIOC32_KILL_ENTRY:
1892ec1df41SThomas Gleixner 	case MTRRIOC32_ADD_PAGE_ENTRY:
1902ec1df41SThomas Gleixner 	case MTRRIOC32_SET_PAGE_ENTRY:
1912ec1df41SThomas Gleixner 	case MTRRIOC32_DEL_PAGE_ENTRY:
1922ec1df41SThomas Gleixner 	case MTRRIOC32_KILL_PAGE_ENTRY: {
19326dc67edSJaswinder Singh Rajput 		struct mtrr_sentry32 __user *s32;
19426dc67edSJaswinder Singh Rajput 
19526dc67edSJaswinder Singh Rajput 		s32 = (struct mtrr_sentry32 __user *)__arg;
1962ec1df41SThomas Gleixner 		err = get_user(sentry.base, &s32->base);
1972ec1df41SThomas Gleixner 		err |= get_user(sentry.size, &s32->size);
1982ec1df41SThomas Gleixner 		err |= get_user(sentry.type, &s32->type);
1992ec1df41SThomas Gleixner 		if (err)
2002ec1df41SThomas Gleixner 			return err;
2012ec1df41SThomas Gleixner 		break;
2022ec1df41SThomas Gleixner 	}
2032ec1df41SThomas Gleixner 	case MTRRIOC32_GET_ENTRY:
2042ec1df41SThomas Gleixner 	case MTRRIOC32_GET_PAGE_ENTRY: {
20526dc67edSJaswinder Singh Rajput 		struct mtrr_gentry32 __user *g32;
20626dc67edSJaswinder Singh Rajput 
20726dc67edSJaswinder Singh Rajput 		g32 = (struct mtrr_gentry32 __user *)__arg;
2082ec1df41SThomas Gleixner 		err = get_user(gentry.regnum, &g32->regnum);
2092ec1df41SThomas Gleixner 		err |= get_user(gentry.base, &g32->base);
2102ec1df41SThomas Gleixner 		err |= get_user(gentry.size, &g32->size);
2112ec1df41SThomas Gleixner 		err |= get_user(gentry.type, &g32->type);
2122ec1df41SThomas Gleixner 		if (err)
2132ec1df41SThomas Gleixner 			return err;
2142ec1df41SThomas Gleixner 		break;
2152ec1df41SThomas Gleixner 	}
2162ec1df41SThomas Gleixner #endif
2172ec1df41SThomas Gleixner 	}
2182ec1df41SThomas Gleixner 
2192ec1df41SThomas Gleixner 	switch (cmd) {
2202ec1df41SThomas Gleixner 	default:
2212ec1df41SThomas Gleixner 		return -ENOTTY;
2222ec1df41SThomas Gleixner 	case MTRRIOC_ADD_ENTRY:
2232ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2242ec1df41SThomas Gleixner 	case MTRRIOC32_ADD_ENTRY:
2252ec1df41SThomas Gleixner #endif
2262ec1df41SThomas Gleixner 		err =
2272d2ee8deSPaul Jimenez 		    mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
2282ec1df41SThomas Gleixner 				  file, 0);
2292ec1df41SThomas Gleixner 		break;
2302ec1df41SThomas Gleixner 	case MTRRIOC_SET_ENTRY:
2312ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2322ec1df41SThomas Gleixner 	case MTRRIOC32_SET_ENTRY:
2332ec1df41SThomas Gleixner #endif
2342d2ee8deSPaul Jimenez 		err = mtrr_add(sentry.base, sentry.size, sentry.type, false);
2352ec1df41SThomas Gleixner 		break;
2362ec1df41SThomas Gleixner 	case MTRRIOC_DEL_ENTRY:
2372ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2382ec1df41SThomas Gleixner 	case MTRRIOC32_DEL_ENTRY:
2392ec1df41SThomas Gleixner #endif
2402ec1df41SThomas Gleixner 		err = mtrr_file_del(sentry.base, sentry.size, file, 0);
2412ec1df41SThomas Gleixner 		break;
2422ec1df41SThomas Gleixner 	case MTRRIOC_KILL_ENTRY:
2432ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2442ec1df41SThomas Gleixner 	case MTRRIOC32_KILL_ENTRY:
2452ec1df41SThomas Gleixner #endif
2462ec1df41SThomas Gleixner 		err = mtrr_del(-1, sentry.base, sentry.size);
2472ec1df41SThomas Gleixner 		break;
2482ec1df41SThomas Gleixner 	case MTRRIOC_GET_ENTRY:
2492ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2502ec1df41SThomas Gleixner 	case MTRRIOC32_GET_ENTRY:
2512ec1df41SThomas Gleixner #endif
2522ec1df41SThomas Gleixner 		if (gentry.regnum >= num_var_ranges)
2532ec1df41SThomas Gleixner 			return -EINVAL;
254b263b31eSH. Peter Anvin 		mtrr_if->get(gentry.regnum, &base, &size, &type);
2552ec1df41SThomas Gleixner 
2562ec1df41SThomas Gleixner 		/* Hide entries that go above 4GB */
257b263b31eSH. Peter Anvin 		if (base + size - 1 >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT))
2582ec1df41SThomas Gleixner 		    || size >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT)))
2592ec1df41SThomas Gleixner 			gentry.base = gentry.size = gentry.type = 0;
2602ec1df41SThomas Gleixner 		else {
261b263b31eSH. Peter Anvin 			gentry.base = base << PAGE_SHIFT;
2622ec1df41SThomas Gleixner 			gentry.size = size << PAGE_SHIFT;
2632ec1df41SThomas Gleixner 			gentry.type = type;
2642ec1df41SThomas Gleixner 		}
2652ec1df41SThomas Gleixner 
2662ec1df41SThomas Gleixner 		break;
2672ec1df41SThomas Gleixner 	case MTRRIOC_ADD_PAGE_ENTRY:
2682ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2692ec1df41SThomas Gleixner 	case MTRRIOC32_ADD_PAGE_ENTRY:
2702ec1df41SThomas Gleixner #endif
2712ec1df41SThomas Gleixner 		err =
2722d2ee8deSPaul Jimenez 		    mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
2732ec1df41SThomas Gleixner 				  file, 1);
2742ec1df41SThomas Gleixner 		break;
2752ec1df41SThomas Gleixner 	case MTRRIOC_SET_PAGE_ENTRY:
2762ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2772ec1df41SThomas Gleixner 	case MTRRIOC32_SET_PAGE_ENTRY:
2782ec1df41SThomas Gleixner #endif
2792d2ee8deSPaul Jimenez 		err =
2802d2ee8deSPaul Jimenez 		    mtrr_add_page(sentry.base, sentry.size, sentry.type, false);
2812ec1df41SThomas Gleixner 		break;
2822ec1df41SThomas Gleixner 	case MTRRIOC_DEL_PAGE_ENTRY:
2832ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2842ec1df41SThomas Gleixner 	case MTRRIOC32_DEL_PAGE_ENTRY:
2852ec1df41SThomas Gleixner #endif
2862ec1df41SThomas Gleixner 		err = mtrr_file_del(sentry.base, sentry.size, file, 1);
2872ec1df41SThomas Gleixner 		break;
2882ec1df41SThomas Gleixner 	case MTRRIOC_KILL_PAGE_ENTRY:
2892ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2902ec1df41SThomas Gleixner 	case MTRRIOC32_KILL_PAGE_ENTRY:
2912ec1df41SThomas Gleixner #endif
2922ec1df41SThomas Gleixner 		err = mtrr_del_page(-1, sentry.base, sentry.size);
2932ec1df41SThomas Gleixner 		break;
2942ec1df41SThomas Gleixner 	case MTRRIOC_GET_PAGE_ENTRY:
2952ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2962ec1df41SThomas Gleixner 	case MTRRIOC32_GET_PAGE_ENTRY:
2972ec1df41SThomas Gleixner #endif
2982ec1df41SThomas Gleixner 		if (gentry.regnum >= num_var_ranges)
2992ec1df41SThomas Gleixner 			return -EINVAL;
300b263b31eSH. Peter Anvin 		mtrr_if->get(gentry.regnum, &base, &size, &type);
3012ec1df41SThomas Gleixner 		/* Hide entries that would overflow */
3022ec1df41SThomas Gleixner 		if (size != (__typeof__(gentry.size))size)
3032ec1df41SThomas Gleixner 			gentry.base = gentry.size = gentry.type = 0;
3042ec1df41SThomas Gleixner 		else {
305b263b31eSH. Peter Anvin 			gentry.base = base;
3062ec1df41SThomas Gleixner 			gentry.size = size;
3072ec1df41SThomas Gleixner 			gentry.type = type;
3082ec1df41SThomas Gleixner 		}
3092ec1df41SThomas Gleixner 		break;
3102ec1df41SThomas Gleixner 	}
3112ec1df41SThomas Gleixner 
3122ec1df41SThomas Gleixner 	if (err)
3132ec1df41SThomas Gleixner 		return err;
3142ec1df41SThomas Gleixner 
3152ec1df41SThomas Gleixner 	switch (cmd) {
3162ec1df41SThomas Gleixner 	case MTRRIOC_GET_ENTRY:
3172ec1df41SThomas Gleixner 	case MTRRIOC_GET_PAGE_ENTRY:
3180e96f31eSJordan Borgner 		if (copy_to_user(arg, &gentry, sizeof(gentry)))
3192ec1df41SThomas Gleixner 			err = -EFAULT;
3202ec1df41SThomas Gleixner 		break;
3212ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
3222ec1df41SThomas Gleixner 	case MTRRIOC32_GET_ENTRY:
3232ec1df41SThomas Gleixner 	case MTRRIOC32_GET_PAGE_ENTRY: {
32426dc67edSJaswinder Singh Rajput 		struct mtrr_gentry32 __user *g32;
32526dc67edSJaswinder Singh Rajput 
32626dc67edSJaswinder Singh Rajput 		g32 = (struct mtrr_gentry32 __user *)__arg;
3272ec1df41SThomas Gleixner 		err = put_user(gentry.base, &g32->base);
3282ec1df41SThomas Gleixner 		err |= put_user(gentry.size, &g32->size);
3292ec1df41SThomas Gleixner 		err |= put_user(gentry.regnum, &g32->regnum);
3302ec1df41SThomas Gleixner 		err |= put_user(gentry.type, &g32->type);
3312ec1df41SThomas Gleixner 		break;
3322ec1df41SThomas Gleixner 	}
3332ec1df41SThomas Gleixner #endif
3342ec1df41SThomas Gleixner 	}
3352ec1df41SThomas Gleixner 	return err;
3362ec1df41SThomas Gleixner }
3372ec1df41SThomas Gleixner 
mtrr_close(struct inode * ino,struct file * file)33826dc67edSJaswinder Singh Rajput static int mtrr_close(struct inode *ino, struct file *file)
3392ec1df41SThomas Gleixner {
3402ec1df41SThomas Gleixner 	unsigned int *fcount = FILE_FCOUNT(file);
34126dc67edSJaswinder Singh Rajput 	int i, max;
3422ec1df41SThomas Gleixner 
3432ec1df41SThomas Gleixner 	if (fcount != NULL) {
3442ec1df41SThomas Gleixner 		max = num_var_ranges;
3452ec1df41SThomas Gleixner 		for (i = 0; i < max; ++i) {
3462ec1df41SThomas Gleixner 			while (fcount[i] > 0) {
3472ec1df41SThomas Gleixner 				mtrr_del(i, 0, 0);
3482ec1df41SThomas Gleixner 				--fcount[i];
3492ec1df41SThomas Gleixner 			}
3502ec1df41SThomas Gleixner 		}
3512ec1df41SThomas Gleixner 		kfree(fcount);
3522ec1df41SThomas Gleixner 		FILE_FCOUNT(file) = NULL;
3532ec1df41SThomas Gleixner 	}
3542ec1df41SThomas Gleixner 	return single_release(ino, file);
3552ec1df41SThomas Gleixner }
3562ec1df41SThomas Gleixner 
mtrr_seq_show(struct seq_file * seq,void * offset)3572ec1df41SThomas Gleixner static int mtrr_seq_show(struct seq_file *seq, void *offset)
3582ec1df41SThomas Gleixner {
3592ec1df41SThomas Gleixner 	char factor;
3603ac62bc0SJoe Perches 	int i, max;
3612ec1df41SThomas Gleixner 	mtrr_type type;
3622ec1df41SThomas Gleixner 	unsigned long base, size;
3632ec1df41SThomas Gleixner 
3642ec1df41SThomas Gleixner 	max = num_var_ranges;
3652ec1df41SThomas Gleixner 	for (i = 0; i < max; i++) {
3662ec1df41SThomas Gleixner 		mtrr_if->get(i, &base, &size, &type);
36726dc67edSJaswinder Singh Rajput 		if (size == 0) {
36899fc8d42SJesse Barnes 			mtrr_usage_table[i] = 0;
36926dc67edSJaswinder Singh Rajput 			continue;
37026dc67edSJaswinder Singh Rajput 		}
3712ec1df41SThomas Gleixner 		if (size < (0x100000 >> PAGE_SHIFT)) {
3722ec1df41SThomas Gleixner 			/* less than 1MB */
3732ec1df41SThomas Gleixner 			factor = 'K';
3742ec1df41SThomas Gleixner 			size <<= PAGE_SHIFT - 10;
3752ec1df41SThomas Gleixner 		} else {
3762ec1df41SThomas Gleixner 			factor = 'M';
3772ec1df41SThomas Gleixner 			size >>= 20 - PAGE_SHIFT;
3782ec1df41SThomas Gleixner 		}
37926dc67edSJaswinder Singh Rajput 		/* Base can be > 32bit */
3803ac62bc0SJoe Perches 		seq_printf(seq, "reg%02i: base=0x%06lx000 (%5luMB), size=%5lu%cB, count=%d: %s\n",
3813ac62bc0SJoe Perches 			   i, base, base >> (20 - PAGE_SHIFT),
3823ac62bc0SJoe Perches 			   size, factor,
3833ac62bc0SJoe Perches 			   mtrr_usage_table[i], mtrr_attrib_to_str(type));
3842ec1df41SThomas Gleixner 	}
3852ec1df41SThomas Gleixner 	return 0;
3862ec1df41SThomas Gleixner }
3872ec1df41SThomas Gleixner 
mtrr_open(struct inode * inode,struct file * file)3882e30dd9eSBorislav Petkov static int mtrr_open(struct inode *inode, struct file *file)
3892e30dd9eSBorislav Petkov {
3902e30dd9eSBorislav Petkov 	if (!mtrr_if)
3912e30dd9eSBorislav Petkov 		return -EIO;
3922e30dd9eSBorislav Petkov 	if (!mtrr_if->get)
3932e30dd9eSBorislav Petkov 		return -ENXIO;
3944fc265a9SKees Cook 	if (!capable(CAP_SYS_ADMIN))
3954fc265a9SKees Cook 		return -EPERM;
3962e30dd9eSBorislav Petkov 	return single_open(file, mtrr_seq_show, NULL);
3972e30dd9eSBorislav Petkov }
3982e30dd9eSBorislav Petkov 
399*97a32539SAlexey Dobriyan static const struct proc_ops mtrr_proc_ops = {
400*97a32539SAlexey Dobriyan 	.proc_open		= mtrr_open,
401*97a32539SAlexey Dobriyan 	.proc_read		= seq_read,
402*97a32539SAlexey Dobriyan 	.proc_lseek		= seq_lseek,
403*97a32539SAlexey Dobriyan 	.proc_write		= mtrr_write,
404*97a32539SAlexey Dobriyan 	.proc_ioctl		= mtrr_ioctl,
405*97a32539SAlexey Dobriyan #ifdef CONFIG_COMPAT
406*97a32539SAlexey Dobriyan 	.proc_compat_ioctl	= mtrr_ioctl,
407*97a32539SAlexey Dobriyan #endif
408*97a32539SAlexey Dobriyan 	.proc_release		= mtrr_close,
4092e30dd9eSBorislav Petkov };
4102e30dd9eSBorislav Petkov 
mtrr_if_init(void)4112ec1df41SThomas Gleixner static int __init mtrr_if_init(void)
4122ec1df41SThomas Gleixner {
4132ec1df41SThomas Gleixner 	struct cpuinfo_x86 *c = &boot_cpu_data;
4142ec1df41SThomas Gleixner 
4152ec1df41SThomas Gleixner 	if ((!cpu_has(c, X86_FEATURE_MTRR)) &&
4162ec1df41SThomas Gleixner 	    (!cpu_has(c, X86_FEATURE_K6_MTRR)) &&
4172ec1df41SThomas Gleixner 	    (!cpu_has(c, X86_FEATURE_CYRIX_ARR)) &&
4182ec1df41SThomas Gleixner 	    (!cpu_has(c, X86_FEATURE_CENTAUR_MCR)))
4192ec1df41SThomas Gleixner 		return -ENODEV;
4202ec1df41SThomas Gleixner 
421*97a32539SAlexey Dobriyan 	proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_proc_ops);
4222ec1df41SThomas Gleixner 	return 0;
4232ec1df41SThomas Gleixner }
4242ec1df41SThomas Gleixner arch_initcall(mtrr_if_init);
4252ec1df41SThomas Gleixner #endif			/*  CONFIG_PROC_FS  */
426