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