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