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