LCOV - code coverage report
Current view: top level - arch/x86/kernel/cpu/mtrr - if.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 5 200 2.5 %
Date: 2021-04-22 12:43:58 Functions: 1 9 11.1 %

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

Generated by: LCOV version 1.14