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