0001
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",
0022 "write-combining",
0023 "?",
0024 "?",
0025 "write-through",
0026 "write-protect",
0027 "write-back",
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
0089
0090
0091
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
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
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
0373 factor = 'K';
0374 size <<= PAGE_SHIFT - 10;
0375 } else {
0376 factor = 'M';
0377 size >>= 20 - PAGE_SHIFT;
0378 }
0379
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