xref: /linux/arch/x86/kernel/cpu/mtrr/if.c (revision 5a0e3ad6af8660be21ca98a971cd00f331318c05)
12ec1df41SThomas Gleixner #include <linux/capability.h>
22ec1df41SThomas Gleixner #include <linux/seq_file.h>
326dc67edSJaswinder Singh Rajput #include <linux/uaccess.h>
426dc67edSJaswinder Singh Rajput #include <linux/proc_fs.h>
526dc67edSJaswinder Singh Rajput #include <linux/module.h>
626dc67edSJaswinder Singh Rajput #include <linux/ctype.h>
7e7d2860bSAndré Goddard Rosa #include <linux/string.h>
8*5a0e3ad6STejun 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 
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
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) {
462ec1df41SThomas Gleixner 		fcount = kzalloc(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
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
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 	if (!capable(CAP_SYS_ADMIN))
1052ec1df41SThomas Gleixner 		return -EPERM;
10626dc67edSJaswinder Singh Rajput 
1072ec1df41SThomas Gleixner 	memset(line, 0, LINE_SIZE);
10811879ba5SArjan van de Ven 
10911879ba5SArjan van de Ven 	length = len;
11011879ba5SArjan van de Ven 	length--;
11111879ba5SArjan van de Ven 
11211879ba5SArjan van de Ven 	if (length > LINE_SIZE - 1)
11311879ba5SArjan van de Ven 		length = LINE_SIZE - 1;
11411879ba5SArjan van de Ven 
11511879ba5SArjan van de Ven 	if (length < 0)
11611879ba5SArjan van de Ven 		return -EINVAL;
11711879ba5SArjan van de Ven 
11811879ba5SArjan van de Ven 	if (copy_from_user(line, buf, length))
1192ec1df41SThomas Gleixner 		return -EFAULT;
12026dc67edSJaswinder Singh Rajput 
1212ec1df41SThomas Gleixner 	linelen = strlen(line);
1222ec1df41SThomas Gleixner 	ptr = line + linelen - 1;
1232ec1df41SThomas Gleixner 	if (linelen && *ptr == '\n')
1242ec1df41SThomas Gleixner 		*ptr = '\0';
12526dc67edSJaswinder Singh Rajput 
1262ec1df41SThomas Gleixner 	if (!strncmp(line, "disable=", 8)) {
1272ec1df41SThomas Gleixner 		reg = simple_strtoul(line + 8, &ptr, 0);
1282ec1df41SThomas Gleixner 		err = mtrr_del_page(reg, 0, 0);
1292ec1df41SThomas Gleixner 		if (err < 0)
1302ec1df41SThomas Gleixner 			return err;
1312ec1df41SThomas Gleixner 		return len;
1322ec1df41SThomas Gleixner 	}
13326dc67edSJaswinder Singh Rajput 
1342ec1df41SThomas Gleixner 	if (strncmp(line, "base=", 5))
1352ec1df41SThomas Gleixner 		return -EINVAL;
13626dc67edSJaswinder Singh Rajput 
1372ec1df41SThomas Gleixner 	base = simple_strtoull(line + 5, &ptr, 0);
138e7d2860bSAndré Goddard Rosa 	ptr = skip_spaces(ptr);
13926dc67edSJaswinder Singh Rajput 
1402ec1df41SThomas Gleixner 	if (strncmp(ptr, "size=", 5))
1412ec1df41SThomas Gleixner 		return -EINVAL;
14226dc67edSJaswinder Singh Rajput 
1432ec1df41SThomas Gleixner 	size = simple_strtoull(ptr + 5, &ptr, 0);
1442ec1df41SThomas Gleixner 	if ((base & 0xfff) || (size & 0xfff))
1452ec1df41SThomas Gleixner 		return -EINVAL;
146e7d2860bSAndré Goddard Rosa 	ptr = skip_spaces(ptr);
14726dc67edSJaswinder Singh Rajput 
1482ec1df41SThomas Gleixner 	if (strncmp(ptr, "type=", 5))
1492ec1df41SThomas Gleixner 		return -EINVAL;
150e7d2860bSAndré Goddard Rosa 	ptr = skip_spaces(ptr + 5);
15126dc67edSJaswinder Singh Rajput 
1522ec1df41SThomas Gleixner 	for (i = 0; i < MTRR_NUM_TYPES; ++i) {
1532ec1df41SThomas Gleixner 		if (strcmp(ptr, mtrr_strings[i]))
1542ec1df41SThomas Gleixner 			continue;
1552ec1df41SThomas Gleixner 		base >>= PAGE_SHIFT;
1562ec1df41SThomas Gleixner 		size >>= PAGE_SHIFT;
15726dc67edSJaswinder Singh Rajput 		err = mtrr_add_page((unsigned long)base, (unsigned long)size, i, true);
1582ec1df41SThomas Gleixner 		if (err < 0)
1592ec1df41SThomas Gleixner 			return err;
1602ec1df41SThomas Gleixner 		return len;
1612ec1df41SThomas Gleixner 	}
1622ec1df41SThomas Gleixner 	return -EINVAL;
1632ec1df41SThomas Gleixner }
1642ec1df41SThomas Gleixner 
1652ec1df41SThomas Gleixner static long
1662ec1df41SThomas Gleixner mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
1672ec1df41SThomas Gleixner {
1682ec1df41SThomas Gleixner 	int err = 0;
1692ec1df41SThomas Gleixner 	mtrr_type type;
1702ec1df41SThomas Gleixner 	unsigned long size;
1712ec1df41SThomas Gleixner 	struct mtrr_sentry sentry;
1722ec1df41SThomas Gleixner 	struct mtrr_gentry gentry;
1732ec1df41SThomas Gleixner 	void __user *arg = (void __user *) __arg;
1742ec1df41SThomas Gleixner 
1752ec1df41SThomas Gleixner 	switch (cmd) {
1762ec1df41SThomas Gleixner 	case MTRRIOC_ADD_ENTRY:
1772ec1df41SThomas Gleixner 	case MTRRIOC_SET_ENTRY:
1782ec1df41SThomas Gleixner 	case MTRRIOC_DEL_ENTRY:
1792ec1df41SThomas Gleixner 	case MTRRIOC_KILL_ENTRY:
1802ec1df41SThomas Gleixner 	case MTRRIOC_ADD_PAGE_ENTRY:
1812ec1df41SThomas Gleixner 	case MTRRIOC_SET_PAGE_ENTRY:
1822ec1df41SThomas Gleixner 	case MTRRIOC_DEL_PAGE_ENTRY:
1832ec1df41SThomas Gleixner 	case MTRRIOC_KILL_PAGE_ENTRY:
1842ec1df41SThomas Gleixner 		if (copy_from_user(&sentry, arg, sizeof sentry))
1852ec1df41SThomas Gleixner 			return -EFAULT;
1862ec1df41SThomas Gleixner 		break;
1872ec1df41SThomas Gleixner 	case MTRRIOC_GET_ENTRY:
1882ec1df41SThomas Gleixner 	case MTRRIOC_GET_PAGE_ENTRY:
1892ec1df41SThomas Gleixner 		if (copy_from_user(&gentry, arg, sizeof gentry))
1902ec1df41SThomas Gleixner 			return -EFAULT;
1912ec1df41SThomas Gleixner 		break;
1922ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
1932ec1df41SThomas Gleixner 	case MTRRIOC32_ADD_ENTRY:
1942ec1df41SThomas Gleixner 	case MTRRIOC32_SET_ENTRY:
1952ec1df41SThomas Gleixner 	case MTRRIOC32_DEL_ENTRY:
1962ec1df41SThomas Gleixner 	case MTRRIOC32_KILL_ENTRY:
1972ec1df41SThomas Gleixner 	case MTRRIOC32_ADD_PAGE_ENTRY:
1982ec1df41SThomas Gleixner 	case MTRRIOC32_SET_PAGE_ENTRY:
1992ec1df41SThomas Gleixner 	case MTRRIOC32_DEL_PAGE_ENTRY:
2002ec1df41SThomas Gleixner 	case MTRRIOC32_KILL_PAGE_ENTRY: {
20126dc67edSJaswinder Singh Rajput 		struct mtrr_sentry32 __user *s32;
20226dc67edSJaswinder Singh Rajput 
20326dc67edSJaswinder Singh Rajput 		s32 = (struct mtrr_sentry32 __user *)__arg;
2042ec1df41SThomas Gleixner 		err = get_user(sentry.base, &s32->base);
2052ec1df41SThomas Gleixner 		err |= get_user(sentry.size, &s32->size);
2062ec1df41SThomas Gleixner 		err |= get_user(sentry.type, &s32->type);
2072ec1df41SThomas Gleixner 		if (err)
2082ec1df41SThomas Gleixner 			return err;
2092ec1df41SThomas Gleixner 		break;
2102ec1df41SThomas Gleixner 	}
2112ec1df41SThomas Gleixner 	case MTRRIOC32_GET_ENTRY:
2122ec1df41SThomas Gleixner 	case MTRRIOC32_GET_PAGE_ENTRY: {
21326dc67edSJaswinder Singh Rajput 		struct mtrr_gentry32 __user *g32;
21426dc67edSJaswinder Singh Rajput 
21526dc67edSJaswinder Singh Rajput 		g32 = (struct mtrr_gentry32 __user *)__arg;
2162ec1df41SThomas Gleixner 		err = get_user(gentry.regnum, &g32->regnum);
2172ec1df41SThomas Gleixner 		err |= get_user(gentry.base, &g32->base);
2182ec1df41SThomas Gleixner 		err |= get_user(gentry.size, &g32->size);
2192ec1df41SThomas Gleixner 		err |= get_user(gentry.type, &g32->type);
2202ec1df41SThomas Gleixner 		if (err)
2212ec1df41SThomas Gleixner 			return err;
2222ec1df41SThomas Gleixner 		break;
2232ec1df41SThomas Gleixner 	}
2242ec1df41SThomas Gleixner #endif
2252ec1df41SThomas Gleixner 	}
2262ec1df41SThomas Gleixner 
2272ec1df41SThomas Gleixner 	switch (cmd) {
2282ec1df41SThomas Gleixner 	default:
2292ec1df41SThomas Gleixner 		return -ENOTTY;
2302ec1df41SThomas Gleixner 	case MTRRIOC_ADD_ENTRY:
2312ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2322ec1df41SThomas Gleixner 	case MTRRIOC32_ADD_ENTRY:
2332ec1df41SThomas Gleixner #endif
2342ec1df41SThomas Gleixner 		if (!capable(CAP_SYS_ADMIN))
2352ec1df41SThomas Gleixner 			return -EPERM;
2362ec1df41SThomas Gleixner 		err =
2372d2ee8deSPaul Jimenez 		    mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
2382ec1df41SThomas Gleixner 				  file, 0);
2392ec1df41SThomas Gleixner 		break;
2402ec1df41SThomas Gleixner 	case MTRRIOC_SET_ENTRY:
2412ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2422ec1df41SThomas Gleixner 	case MTRRIOC32_SET_ENTRY:
2432ec1df41SThomas Gleixner #endif
2442ec1df41SThomas Gleixner 		if (!capable(CAP_SYS_ADMIN))
2452ec1df41SThomas Gleixner 			return -EPERM;
2462d2ee8deSPaul Jimenez 		err = mtrr_add(sentry.base, sentry.size, sentry.type, false);
2472ec1df41SThomas Gleixner 		break;
2482ec1df41SThomas Gleixner 	case MTRRIOC_DEL_ENTRY:
2492ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2502ec1df41SThomas Gleixner 	case MTRRIOC32_DEL_ENTRY:
2512ec1df41SThomas Gleixner #endif
2522ec1df41SThomas Gleixner 		if (!capable(CAP_SYS_ADMIN))
2532ec1df41SThomas Gleixner 			return -EPERM;
2542ec1df41SThomas Gleixner 		err = mtrr_file_del(sentry.base, sentry.size, file, 0);
2552ec1df41SThomas Gleixner 		break;
2562ec1df41SThomas Gleixner 	case MTRRIOC_KILL_ENTRY:
2572ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2582ec1df41SThomas Gleixner 	case MTRRIOC32_KILL_ENTRY:
2592ec1df41SThomas Gleixner #endif
2602ec1df41SThomas Gleixner 		if (!capable(CAP_SYS_ADMIN))
2612ec1df41SThomas Gleixner 			return -EPERM;
2622ec1df41SThomas Gleixner 		err = mtrr_del(-1, sentry.base, sentry.size);
2632ec1df41SThomas Gleixner 		break;
2642ec1df41SThomas Gleixner 	case MTRRIOC_GET_ENTRY:
2652ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2662ec1df41SThomas Gleixner 	case MTRRIOC32_GET_ENTRY:
2672ec1df41SThomas Gleixner #endif
2682ec1df41SThomas Gleixner 		if (gentry.regnum >= num_var_ranges)
2692ec1df41SThomas Gleixner 			return -EINVAL;
2702ec1df41SThomas Gleixner 		mtrr_if->get(gentry.regnum, &gentry.base, &size, &type);
2712ec1df41SThomas Gleixner 
2722ec1df41SThomas Gleixner 		/* Hide entries that go above 4GB */
2732ec1df41SThomas Gleixner 		if (gentry.base + size - 1 >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT))
2742ec1df41SThomas Gleixner 		    || size >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT)))
2752ec1df41SThomas Gleixner 			gentry.base = gentry.size = gentry.type = 0;
2762ec1df41SThomas Gleixner 		else {
2772ec1df41SThomas Gleixner 			gentry.base <<= PAGE_SHIFT;
2782ec1df41SThomas Gleixner 			gentry.size = size << PAGE_SHIFT;
2792ec1df41SThomas Gleixner 			gentry.type = type;
2802ec1df41SThomas Gleixner 		}
2812ec1df41SThomas Gleixner 
2822ec1df41SThomas Gleixner 		break;
2832ec1df41SThomas Gleixner 	case MTRRIOC_ADD_PAGE_ENTRY:
2842ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2852ec1df41SThomas Gleixner 	case MTRRIOC32_ADD_PAGE_ENTRY:
2862ec1df41SThomas Gleixner #endif
2872ec1df41SThomas Gleixner 		if (!capable(CAP_SYS_ADMIN))
2882ec1df41SThomas Gleixner 			return -EPERM;
2892ec1df41SThomas Gleixner 		err =
2902d2ee8deSPaul Jimenez 		    mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
2912ec1df41SThomas Gleixner 				  file, 1);
2922ec1df41SThomas Gleixner 		break;
2932ec1df41SThomas Gleixner 	case MTRRIOC_SET_PAGE_ENTRY:
2942ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
2952ec1df41SThomas Gleixner 	case MTRRIOC32_SET_PAGE_ENTRY:
2962ec1df41SThomas Gleixner #endif
2972ec1df41SThomas Gleixner 		if (!capable(CAP_SYS_ADMIN))
2982ec1df41SThomas Gleixner 			return -EPERM;
2992d2ee8deSPaul Jimenez 		err =
3002d2ee8deSPaul Jimenez 		    mtrr_add_page(sentry.base, sentry.size, sentry.type, false);
3012ec1df41SThomas Gleixner 		break;
3022ec1df41SThomas Gleixner 	case MTRRIOC_DEL_PAGE_ENTRY:
3032ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
3042ec1df41SThomas Gleixner 	case MTRRIOC32_DEL_PAGE_ENTRY:
3052ec1df41SThomas Gleixner #endif
3062ec1df41SThomas Gleixner 		if (!capable(CAP_SYS_ADMIN))
3072ec1df41SThomas Gleixner 			return -EPERM;
3082ec1df41SThomas Gleixner 		err = mtrr_file_del(sentry.base, sentry.size, file, 1);
3092ec1df41SThomas Gleixner 		break;
3102ec1df41SThomas Gleixner 	case MTRRIOC_KILL_PAGE_ENTRY:
3112ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
3122ec1df41SThomas Gleixner 	case MTRRIOC32_KILL_PAGE_ENTRY:
3132ec1df41SThomas Gleixner #endif
3142ec1df41SThomas Gleixner 		if (!capable(CAP_SYS_ADMIN))
3152ec1df41SThomas Gleixner 			return -EPERM;
3162ec1df41SThomas Gleixner 		err = mtrr_del_page(-1, sentry.base, sentry.size);
3172ec1df41SThomas Gleixner 		break;
3182ec1df41SThomas Gleixner 	case MTRRIOC_GET_PAGE_ENTRY:
3192ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
3202ec1df41SThomas Gleixner 	case MTRRIOC32_GET_PAGE_ENTRY:
3212ec1df41SThomas Gleixner #endif
3222ec1df41SThomas Gleixner 		if (gentry.regnum >= num_var_ranges)
3232ec1df41SThomas Gleixner 			return -EINVAL;
3242ec1df41SThomas Gleixner 		mtrr_if->get(gentry.regnum, &gentry.base, &size, &type);
3252ec1df41SThomas Gleixner 		/* Hide entries that would overflow */
3262ec1df41SThomas Gleixner 		if (size != (__typeof__(gentry.size))size)
3272ec1df41SThomas Gleixner 			gentry.base = gentry.size = gentry.type = 0;
3282ec1df41SThomas Gleixner 		else {
3292ec1df41SThomas Gleixner 			gentry.size = size;
3302ec1df41SThomas Gleixner 			gentry.type = type;
3312ec1df41SThomas Gleixner 		}
3322ec1df41SThomas Gleixner 		break;
3332ec1df41SThomas Gleixner 	}
3342ec1df41SThomas Gleixner 
3352ec1df41SThomas Gleixner 	if (err)
3362ec1df41SThomas Gleixner 		return err;
3372ec1df41SThomas Gleixner 
3382ec1df41SThomas Gleixner 	switch (cmd) {
3392ec1df41SThomas Gleixner 	case MTRRIOC_GET_ENTRY:
3402ec1df41SThomas Gleixner 	case MTRRIOC_GET_PAGE_ENTRY:
3412ec1df41SThomas Gleixner 		if (copy_to_user(arg, &gentry, sizeof gentry))
3422ec1df41SThomas Gleixner 			err = -EFAULT;
3432ec1df41SThomas Gleixner 		break;
3442ec1df41SThomas Gleixner #ifdef CONFIG_COMPAT
3452ec1df41SThomas Gleixner 	case MTRRIOC32_GET_ENTRY:
3462ec1df41SThomas Gleixner 	case MTRRIOC32_GET_PAGE_ENTRY: {
34726dc67edSJaswinder Singh Rajput 		struct mtrr_gentry32 __user *g32;
34826dc67edSJaswinder Singh Rajput 
34926dc67edSJaswinder Singh Rajput 		g32 = (struct mtrr_gentry32 __user *)__arg;
3502ec1df41SThomas Gleixner 		err = put_user(gentry.base, &g32->base);
3512ec1df41SThomas Gleixner 		err |= put_user(gentry.size, &g32->size);
3522ec1df41SThomas Gleixner 		err |= put_user(gentry.regnum, &g32->regnum);
3532ec1df41SThomas Gleixner 		err |= put_user(gentry.type, &g32->type);
3542ec1df41SThomas Gleixner 		break;
3552ec1df41SThomas Gleixner 	}
3562ec1df41SThomas Gleixner #endif
3572ec1df41SThomas Gleixner 	}
3582ec1df41SThomas Gleixner 	return err;
3592ec1df41SThomas Gleixner }
3602ec1df41SThomas Gleixner 
36126dc67edSJaswinder Singh Rajput static int mtrr_close(struct inode *ino, struct file *file)
3622ec1df41SThomas Gleixner {
3632ec1df41SThomas Gleixner 	unsigned int *fcount = FILE_FCOUNT(file);
36426dc67edSJaswinder Singh Rajput 	int i, max;
3652ec1df41SThomas Gleixner 
3662ec1df41SThomas Gleixner 	if (fcount != NULL) {
3672ec1df41SThomas Gleixner 		max = num_var_ranges;
3682ec1df41SThomas Gleixner 		for (i = 0; i < max; ++i) {
3692ec1df41SThomas Gleixner 			while (fcount[i] > 0) {
3702ec1df41SThomas Gleixner 				mtrr_del(i, 0, 0);
3712ec1df41SThomas Gleixner 				--fcount[i];
3722ec1df41SThomas Gleixner 			}
3732ec1df41SThomas Gleixner 		}
3742ec1df41SThomas Gleixner 		kfree(fcount);
3752ec1df41SThomas Gleixner 		FILE_FCOUNT(file) = NULL;
3762ec1df41SThomas Gleixner 	}
3772ec1df41SThomas Gleixner 	return single_release(ino, file);
3782ec1df41SThomas Gleixner }
3792ec1df41SThomas Gleixner 
3802ec1df41SThomas Gleixner static int mtrr_seq_show(struct seq_file *seq, void *offset);
3812ec1df41SThomas Gleixner 
3822ec1df41SThomas Gleixner static int mtrr_open(struct inode *inode, struct file *file)
3832ec1df41SThomas Gleixner {
3842ec1df41SThomas Gleixner 	if (!mtrr_if)
3852ec1df41SThomas Gleixner 		return -EIO;
3862ec1df41SThomas Gleixner 	if (!mtrr_if->get)
3872ec1df41SThomas Gleixner 		return -ENXIO;
3882ec1df41SThomas Gleixner 	return single_open(file, mtrr_seq_show, NULL);
3892ec1df41SThomas Gleixner }
3902ec1df41SThomas Gleixner 
3912ec1df41SThomas Gleixner static const struct file_operations mtrr_fops = {
3922ec1df41SThomas Gleixner 	.owner			= THIS_MODULE,
3932ec1df41SThomas Gleixner 	.open			= mtrr_open,
3942ec1df41SThomas Gleixner 	.read			= seq_read,
3952ec1df41SThomas Gleixner 	.llseek			= seq_lseek,
3962ec1df41SThomas Gleixner 	.write			= mtrr_write,
3972ec1df41SThomas Gleixner 	.unlocked_ioctl		= mtrr_ioctl,
3982ec1df41SThomas Gleixner 	.compat_ioctl		= mtrr_ioctl,
3992ec1df41SThomas Gleixner 	.release		= mtrr_close,
4002ec1df41SThomas Gleixner };
4012ec1df41SThomas Gleixner 
4022ec1df41SThomas Gleixner static int mtrr_seq_show(struct seq_file *seq, void *offset)
4032ec1df41SThomas Gleixner {
4042ec1df41SThomas Gleixner 	char factor;
4052ec1df41SThomas Gleixner 	int i, max, len;
4062ec1df41SThomas Gleixner 	mtrr_type type;
4072ec1df41SThomas Gleixner 	unsigned long base, size;
4082ec1df41SThomas Gleixner 
4092ec1df41SThomas Gleixner 	len = 0;
4102ec1df41SThomas Gleixner 	max = num_var_ranges;
4112ec1df41SThomas Gleixner 	for (i = 0; i < max; i++) {
4122ec1df41SThomas Gleixner 		mtrr_if->get(i, &base, &size, &type);
41326dc67edSJaswinder Singh Rajput 		if (size == 0) {
41499fc8d42SJesse Barnes 			mtrr_usage_table[i] = 0;
41526dc67edSJaswinder Singh Rajput 			continue;
41626dc67edSJaswinder Singh Rajput 		}
4172ec1df41SThomas Gleixner 		if (size < (0x100000 >> PAGE_SHIFT)) {
4182ec1df41SThomas Gleixner 			/* less than 1MB */
4192ec1df41SThomas Gleixner 			factor = 'K';
4202ec1df41SThomas Gleixner 			size <<= PAGE_SHIFT - 10;
4212ec1df41SThomas Gleixner 		} else {
4222ec1df41SThomas Gleixner 			factor = 'M';
4232ec1df41SThomas Gleixner 			size >>= 20 - PAGE_SHIFT;
4242ec1df41SThomas Gleixner 		}
42526dc67edSJaswinder Singh Rajput 		/* Base can be > 32bit */
42626dc67edSJaswinder Singh Rajput 		len += seq_printf(seq, "reg%02i: base=0x%06lx000 "
42726dc67edSJaswinder Singh Rajput 			"(%5luMB), size=%5lu%cB, count=%d: %s\n",
42826dc67edSJaswinder Singh Rajput 			i, base, base >> (20 - PAGE_SHIFT), size,
42926dc67edSJaswinder Singh Rajput 			factor, mtrr_usage_table[i],
43026dc67edSJaswinder Singh Rajput 			mtrr_attrib_to_str(type));
4312ec1df41SThomas Gleixner 	}
4322ec1df41SThomas Gleixner 	return 0;
4332ec1df41SThomas Gleixner }
4342ec1df41SThomas Gleixner 
4352ec1df41SThomas Gleixner static int __init mtrr_if_init(void)
4362ec1df41SThomas Gleixner {
4372ec1df41SThomas Gleixner 	struct cpuinfo_x86 *c = &boot_cpu_data;
4382ec1df41SThomas Gleixner 
4392ec1df41SThomas Gleixner 	if ((!cpu_has(c, X86_FEATURE_MTRR)) &&
4402ec1df41SThomas Gleixner 	    (!cpu_has(c, X86_FEATURE_K6_MTRR)) &&
4412ec1df41SThomas Gleixner 	    (!cpu_has(c, X86_FEATURE_CYRIX_ARR)) &&
4422ec1df41SThomas Gleixner 	    (!cpu_has(c, X86_FEATURE_CENTAUR_MCR)))
4432ec1df41SThomas Gleixner 		return -ENODEV;
4442ec1df41SThomas Gleixner 
445c74c120aSAlexey Dobriyan 	proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops);
4462ec1df41SThomas Gleixner 	return 0;
4472ec1df41SThomas Gleixner }
4482ec1df41SThomas Gleixner arch_initcall(mtrr_if_init);
4492ec1df41SThomas Gleixner #endif			/*  CONFIG_PROC_FS  */
450