Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/capability.h>
0003 #include <linux/seq_file.h>
0004 #include <linux/uaccess.h>
0005 #include <linux/proc_fs.h>
0006 #include <linux/ctype.h>
0007 #include <linux/string.h>
0008 #include <linux/slab.h>
0009 #include <linux/init.h>
0010 
0011 #define LINE_SIZE 80
0012 
0013 #include <asm/mtrr.h>
0014 
0015 #include "mtrr.h"
0016 
0017 #define FILE_FCOUNT(f) (((struct seq_file *)((f)->private_data))->private)
0018 
0019 static const char *const mtrr_strings[MTRR_NUM_TYPES] =
0020 {
0021     "uncachable",       /* 0 */
0022     "write-combining",  /* 1 */
0023     "?",            /* 2 */
0024     "?",            /* 3 */
0025     "write-through",    /* 4 */
0026     "write-protect",    /* 5 */
0027     "write-back",       /* 6 */
0028 };
0029 
0030 const char *mtrr_attrib_to_str(int x)
0031 {
0032     return (x <= 6) ? mtrr_strings[x] : "?";
0033 }
0034 
0035 #ifdef CONFIG_PROC_FS
0036 
0037 static int
0038 mtrr_file_add(unsigned long base, unsigned long size,
0039           unsigned int type, bool increment, struct file *file, int page)
0040 {
0041     unsigned int *fcount = FILE_FCOUNT(file);
0042     int reg, max;
0043 
0044     max = num_var_ranges;
0045     if (fcount == NULL) {
0046         fcount = kcalloc(max, sizeof(*fcount), GFP_KERNEL);
0047         if (!fcount)
0048             return -ENOMEM;
0049         FILE_FCOUNT(file) = fcount;
0050     }
0051     if (!page) {
0052         if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
0053             return -EINVAL;
0054         base >>= PAGE_SHIFT;
0055         size >>= PAGE_SHIFT;
0056     }
0057     reg = mtrr_add_page(base, size, type, true);
0058     if (reg >= 0)
0059         ++fcount[reg];
0060     return reg;
0061 }
0062 
0063 static int
0064 mtrr_file_del(unsigned long base, unsigned long size,
0065           struct file *file, int page)
0066 {
0067     unsigned int *fcount = FILE_FCOUNT(file);
0068     int reg;
0069 
0070     if (!page) {
0071         if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
0072             return -EINVAL;
0073         base >>= PAGE_SHIFT;
0074         size >>= PAGE_SHIFT;
0075     }
0076     reg = mtrr_del_page(-1, base, size);
0077     if (reg < 0)
0078         return reg;
0079     if (fcount == NULL)
0080         return reg;
0081     if (fcount[reg] < 1)
0082         return -EINVAL;
0083     --fcount[reg];
0084     return reg;
0085 }
0086 
0087 /*
0088  * seq_file can seek but we ignore it.
0089  *
0090  * Format of control line:
0091  *    "base=%Lx size=%Lx type=%s" or "disable=%d"
0092  */
0093 static ssize_t
0094 mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
0095 {
0096     int i, err;
0097     unsigned long reg;
0098     unsigned long long base, size;
0099     char *ptr;
0100     char line[LINE_SIZE];
0101     int length;
0102     size_t linelen;
0103 
0104     memset(line, 0, LINE_SIZE);
0105 
0106     len = min_t(size_t, len, LINE_SIZE - 1);
0107     length = strncpy_from_user(line, buf, len);
0108     if (length < 0)
0109         return length;
0110 
0111     linelen = strlen(line);
0112     ptr = line + linelen - 1;
0113     if (linelen && *ptr == '\n')
0114         *ptr = '\0';
0115 
0116     if (!strncmp(line, "disable=", 8)) {
0117         reg = simple_strtoul(line + 8, &ptr, 0);
0118         err = mtrr_del_page(reg, 0, 0);
0119         if (err < 0)
0120             return err;
0121         return len;
0122     }
0123 
0124     if (strncmp(line, "base=", 5))
0125         return -EINVAL;
0126 
0127     base = simple_strtoull(line + 5, &ptr, 0);
0128     ptr = skip_spaces(ptr);
0129 
0130     if (strncmp(ptr, "size=", 5))
0131         return -EINVAL;
0132 
0133     size = simple_strtoull(ptr + 5, &ptr, 0);
0134     if ((base & 0xfff) || (size & 0xfff))
0135         return -EINVAL;
0136     ptr = skip_spaces(ptr);
0137 
0138     if (strncmp(ptr, "type=", 5))
0139         return -EINVAL;
0140     ptr = skip_spaces(ptr + 5);
0141 
0142     i = match_string(mtrr_strings, MTRR_NUM_TYPES, ptr);
0143     if (i < 0)
0144         return i;
0145 
0146     base >>= PAGE_SHIFT;
0147     size >>= PAGE_SHIFT;
0148     err = mtrr_add_page((unsigned long)base, (unsigned long)size, i, true);
0149     if (err < 0)
0150         return err;
0151     return len;
0152 }
0153 
0154 static long
0155 mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
0156 {
0157     int err = 0;
0158     mtrr_type type;
0159     unsigned long base;
0160     unsigned long size;
0161     struct mtrr_sentry sentry;
0162     struct mtrr_gentry gentry;
0163     void __user *arg = (void __user *) __arg;
0164 
0165     memset(&gentry, 0, sizeof(gentry));
0166 
0167     switch (cmd) {
0168     case MTRRIOC_ADD_ENTRY:
0169     case MTRRIOC_SET_ENTRY:
0170     case MTRRIOC_DEL_ENTRY:
0171     case MTRRIOC_KILL_ENTRY:
0172     case MTRRIOC_ADD_PAGE_ENTRY:
0173     case MTRRIOC_SET_PAGE_ENTRY:
0174     case MTRRIOC_DEL_PAGE_ENTRY:
0175     case MTRRIOC_KILL_PAGE_ENTRY:
0176         if (copy_from_user(&sentry, arg, sizeof(sentry)))
0177             return -EFAULT;
0178         break;
0179     case MTRRIOC_GET_ENTRY:
0180     case MTRRIOC_GET_PAGE_ENTRY:
0181         if (copy_from_user(&gentry, arg, sizeof(gentry)))
0182             return -EFAULT;
0183         break;
0184 #ifdef CONFIG_COMPAT
0185     case MTRRIOC32_ADD_ENTRY:
0186     case MTRRIOC32_SET_ENTRY:
0187     case MTRRIOC32_DEL_ENTRY:
0188     case MTRRIOC32_KILL_ENTRY:
0189     case MTRRIOC32_ADD_PAGE_ENTRY:
0190     case MTRRIOC32_SET_PAGE_ENTRY:
0191     case MTRRIOC32_DEL_PAGE_ENTRY:
0192     case MTRRIOC32_KILL_PAGE_ENTRY: {
0193         struct mtrr_sentry32 __user *s32;
0194 
0195         s32 = (struct mtrr_sentry32 __user *)__arg;
0196         err = get_user(sentry.base, &s32->base);
0197         err |= get_user(sentry.size, &s32->size);
0198         err |= get_user(sentry.type, &s32->type);
0199         if (err)
0200             return err;
0201         break;
0202     }
0203     case MTRRIOC32_GET_ENTRY:
0204     case MTRRIOC32_GET_PAGE_ENTRY: {
0205         struct mtrr_gentry32 __user *g32;
0206 
0207         g32 = (struct mtrr_gentry32 __user *)__arg;
0208         err = get_user(gentry.regnum, &g32->regnum);
0209         err |= get_user(gentry.base, &g32->base);
0210         err |= get_user(gentry.size, &g32->size);
0211         err |= get_user(gentry.type, &g32->type);
0212         if (err)
0213             return err;
0214         break;
0215     }
0216 #endif
0217     }
0218 
0219     switch (cmd) {
0220     default:
0221         return -ENOTTY;
0222     case MTRRIOC_ADD_ENTRY:
0223 #ifdef CONFIG_COMPAT
0224     case MTRRIOC32_ADD_ENTRY:
0225 #endif
0226         err =
0227             mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
0228                   file, 0);
0229         break;
0230     case MTRRIOC_SET_ENTRY:
0231 #ifdef CONFIG_COMPAT
0232     case MTRRIOC32_SET_ENTRY:
0233 #endif
0234         err = mtrr_add(sentry.base, sentry.size, sentry.type, false);
0235         break;
0236     case MTRRIOC_DEL_ENTRY:
0237 #ifdef CONFIG_COMPAT
0238     case MTRRIOC32_DEL_ENTRY:
0239 #endif
0240         err = mtrr_file_del(sentry.base, sentry.size, file, 0);
0241         break;
0242     case MTRRIOC_KILL_ENTRY:
0243 #ifdef CONFIG_COMPAT
0244     case MTRRIOC32_KILL_ENTRY:
0245 #endif
0246         err = mtrr_del(-1, sentry.base, sentry.size);
0247         break;
0248     case MTRRIOC_GET_ENTRY:
0249 #ifdef CONFIG_COMPAT
0250     case MTRRIOC32_GET_ENTRY:
0251 #endif
0252         if (gentry.regnum >= num_var_ranges)
0253             return -EINVAL;
0254         mtrr_if->get(gentry.regnum, &base, &size, &type);
0255 
0256         /* Hide entries that go above 4GB */
0257         if (base + size - 1 >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT))
0258             || size >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT)))
0259             gentry.base = gentry.size = gentry.type = 0;
0260         else {
0261             gentry.base = base << PAGE_SHIFT;
0262             gentry.size = size << PAGE_SHIFT;
0263             gentry.type = type;
0264         }
0265 
0266         break;
0267     case MTRRIOC_ADD_PAGE_ENTRY:
0268 #ifdef CONFIG_COMPAT
0269     case MTRRIOC32_ADD_PAGE_ENTRY:
0270 #endif
0271         err =
0272             mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
0273                   file, 1);
0274         break;
0275     case MTRRIOC_SET_PAGE_ENTRY:
0276 #ifdef CONFIG_COMPAT
0277     case MTRRIOC32_SET_PAGE_ENTRY:
0278 #endif
0279         err =
0280             mtrr_add_page(sentry.base, sentry.size, sentry.type, false);
0281         break;
0282     case MTRRIOC_DEL_PAGE_ENTRY:
0283 #ifdef CONFIG_COMPAT
0284     case MTRRIOC32_DEL_PAGE_ENTRY:
0285 #endif
0286         err = mtrr_file_del(sentry.base, sentry.size, file, 1);
0287         break;
0288     case MTRRIOC_KILL_PAGE_ENTRY:
0289 #ifdef CONFIG_COMPAT
0290     case MTRRIOC32_KILL_PAGE_ENTRY:
0291 #endif
0292         err = mtrr_del_page(-1, sentry.base, sentry.size);
0293         break;
0294     case MTRRIOC_GET_PAGE_ENTRY:
0295 #ifdef CONFIG_COMPAT
0296     case MTRRIOC32_GET_PAGE_ENTRY:
0297 #endif
0298         if (gentry.regnum >= num_var_ranges)
0299             return -EINVAL;
0300         mtrr_if->get(gentry.regnum, &base, &size, &type);
0301         /* Hide entries that would overflow */
0302         if (size != (__typeof__(gentry.size))size)
0303             gentry.base = gentry.size = gentry.type = 0;
0304         else {
0305             gentry.base = base;
0306             gentry.size = size;
0307             gentry.type = type;
0308         }
0309         break;
0310     }
0311 
0312     if (err)
0313         return err;
0314 
0315     switch (cmd) {
0316     case MTRRIOC_GET_ENTRY:
0317     case MTRRIOC_GET_PAGE_ENTRY:
0318         if (copy_to_user(arg, &gentry, sizeof(gentry)))
0319             err = -EFAULT;
0320         break;
0321 #ifdef CONFIG_COMPAT
0322     case MTRRIOC32_GET_ENTRY:
0323     case MTRRIOC32_GET_PAGE_ENTRY: {
0324         struct mtrr_gentry32 __user *g32;
0325 
0326         g32 = (struct mtrr_gentry32 __user *)__arg;
0327         err = put_user(gentry.base, &g32->base);
0328         err |= put_user(gentry.size, &g32->size);
0329         err |= put_user(gentry.regnum, &g32->regnum);
0330         err |= put_user(gentry.type, &g32->type);
0331         break;
0332     }
0333 #endif
0334     }
0335     return err;
0336 }
0337 
0338 static int mtrr_close(struct inode *ino, struct file *file)
0339 {
0340     unsigned int *fcount = FILE_FCOUNT(file);
0341     int i, max;
0342 
0343     if (fcount != NULL) {
0344         max = num_var_ranges;
0345         for (i = 0; i < max; ++i) {
0346             while (fcount[i] > 0) {
0347                 mtrr_del(i, 0, 0);
0348                 --fcount[i];
0349             }
0350         }
0351         kfree(fcount);
0352         FILE_FCOUNT(file) = NULL;
0353     }
0354     return single_release(ino, file);
0355 }
0356 
0357 static int mtrr_seq_show(struct seq_file *seq, void *offset)
0358 {
0359     char factor;
0360     int i, max;
0361     mtrr_type type;
0362     unsigned long base, size;
0363 
0364     max = num_var_ranges;
0365     for (i = 0; i < max; i++) {
0366         mtrr_if->get(i, &base, &size, &type);
0367         if (size == 0) {
0368             mtrr_usage_table[i] = 0;
0369             continue;
0370         }
0371         if (size < (0x100000 >> PAGE_SHIFT)) {
0372             /* less than 1MB */
0373             factor = 'K';
0374             size <<= PAGE_SHIFT - 10;
0375         } else {
0376             factor = 'M';
0377             size >>= 20 - PAGE_SHIFT;
0378         }
0379         /* Base can be > 32bit */
0380         seq_printf(seq, "reg%02i: base=0x%06lx000 (%5luMB), size=%5lu%cB, count=%d: %s\n",
0381                i, base, base >> (20 - PAGE_SHIFT),
0382                size, factor,
0383                mtrr_usage_table[i], mtrr_attrib_to_str(type));
0384     }
0385     return 0;
0386 }
0387 
0388 static int mtrr_open(struct inode *inode, struct file *file)
0389 {
0390     if (!mtrr_if)
0391         return -EIO;
0392     if (!mtrr_if->get)
0393         return -ENXIO;
0394     if (!capable(CAP_SYS_ADMIN))
0395         return -EPERM;
0396     return single_open(file, mtrr_seq_show, NULL);
0397 }
0398 
0399 static const struct proc_ops mtrr_proc_ops = {
0400     .proc_open      = mtrr_open,
0401     .proc_read      = seq_read,
0402     .proc_lseek     = seq_lseek,
0403     .proc_write     = mtrr_write,
0404     .proc_ioctl     = mtrr_ioctl,
0405 #ifdef CONFIG_COMPAT
0406     .proc_compat_ioctl  = mtrr_ioctl,
0407 #endif
0408     .proc_release       = mtrr_close,
0409 };
0410 
0411 static int __init mtrr_if_init(void)
0412 {
0413     struct cpuinfo_x86 *c = &boot_cpu_data;
0414 
0415     if ((!cpu_has(c, X86_FEATURE_MTRR)) &&
0416         (!cpu_has(c, X86_FEATURE_K6_MTRR)) &&
0417         (!cpu_has(c, X86_FEATURE_CYRIX_ARR)) &&
0418         (!cpu_has(c, X86_FEATURE_CENTAUR_MCR)))
0419         return -ENODEV;
0420 
0421     proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_proc_ops);
0422     return 0;
0423 }
0424 arch_initcall(mtrr_if_init);
0425 #endif          /*  CONFIG_PROC_FS  */