root/arch/x86/kernel/cpu/mtrr/if.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mtrr_attrib_to_str
  2. mtrr_file_add
  3. mtrr_file_del
  4. mtrr_write
  5. mtrr_ioctl
  6. mtrr_close
  7. mtrr_open
  8. mtrr_seq_show
  9. mtrr_if_init

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

/* [<][>][^][v][top][bottom][index][help] */