LCOV - code coverage report
Current view: top level - arch/x86/kernel/cpu/mtrr - if.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 7 186 3.8 %
Date: 2022-04-01 14:58:12 Functions: 2 8 25.0 %
Branches: 2 107 1.9 %

           Branch data     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                 :         15 : const char *mtrr_attrib_to_str(int x)
      31                 :            : {
      32         [ +  - ]:         15 :         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                 :            : mtrr_file_del(unsigned long base, unsigned long size,
      65                 :            :               struct file *file, int page)
      66                 :            : {
      67                 :            :         unsigned int *fcount = FILE_FCOUNT(file);
      68                 :            :         int reg;
      69                 :            : 
      70                 :            :         if (!page) {
      71                 :            :                 if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
      72                 :            :                         return -EINVAL;
      73                 :            :                 base >>= PAGE_SHIFT;
      74                 :            :                 size >>= PAGE_SHIFT;
      75                 :            :         }
      76                 :            :         reg = mtrr_del_page(-1, base, size);
      77                 :            :         if (reg < 0)
      78                 :            :                 return reg;
      79                 :            :         if (fcount == NULL)
      80                 :            :                 return reg;
      81                 :            :         if (fcount[reg] < 1)
      82                 :            :                 return -EINVAL;
      83                 :            :         --fcount[reg];
      84                 :            :         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                 :          3 : static int __init mtrr_if_init(void)
     412                 :            : {
     413                 :          3 :         struct cpuinfo_x86 *c = &boot_cpu_data;
     414                 :            : 
     415   [ -  +  -  - ]:          3 :         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                 :            :             (!cpu_has(c, X86_FEATURE_CENTAUR_MCR)))
     419                 :            :                 return -ENODEV;
     420                 :            : 
     421                 :          3 :         proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_proc_ops);
     422                 :          3 :         return 0;
     423                 :            : }
     424                 :            : arch_initcall(mtrr_if_init);
     425                 :            : #endif                  /*  CONFIG_PROC_FS  */

Generated by: LCOV version 1.14