aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/cpu/mtrr/if.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/i386/kernel/cpu/mtrr/if.c')
-rw-r--r--arch/i386/kernel/cpu/mtrr/if.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/arch/i386/kernel/cpu/mtrr/if.c b/arch/i386/kernel/cpu/mtrr/if.c
new file mode 100644
index 000000000000..1923e0aed26a
--- /dev/null
+++ b/arch/i386/kernel/cpu/mtrr/if.c
@@ -0,0 +1,374 @@
1#include <linux/init.h>
2#include <linux/proc_fs.h>
3#include <linux/ctype.h>
4#include <linux/module.h>
5#include <linux/seq_file.h>
6#include <asm/uaccess.h>
7
8#define LINE_SIZE 80
9
10#include <asm/mtrr.h>
11#include "mtrr.h"
12
13/* RED-PEN: this is accessed without any locking */
14extern unsigned int *usage_table;
15
16
17#define FILE_FCOUNT(f) (((struct seq_file *)((f)->private_data))->private)
18
19static char *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
30char *mtrr_attrib_to_str(int x)
31{
32 return (x <= 6) ? mtrr_strings[x] : "?";
33}
34
35#ifdef CONFIG_PROC_FS
36
37static int
38mtrr_file_add(unsigned long base, unsigned long size,
39 unsigned int type, char increment, struct file *file, int page)
40{
41 int reg, max;
42 unsigned int *fcount = FILE_FCOUNT(file);
43
44 max = num_var_ranges;
45 if (fcount == NULL) {
46 fcount = kmalloc(max * sizeof *fcount, GFP_KERNEL);
47 if (!fcount)
48 return -ENOMEM;
49 memset(fcount, 0, max * sizeof *fcount);
50 FILE_FCOUNT(file) = fcount;
51 }
52 if (!page) {
53 if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
54 return -EINVAL;
55 base >>= PAGE_SHIFT;
56 size >>= PAGE_SHIFT;
57 }
58 reg = mtrr_add_page(base, size, type, 1);
59 if (reg >= 0)
60 ++fcount[reg];
61 return reg;
62}
63
64static int
65mtrr_file_del(unsigned long base, unsigned long size,
66 struct file *file, int page)
67{
68 int reg;
69 unsigned int *fcount = FILE_FCOUNT(file);
70
71 if (!page) {
72 if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
73 return -EINVAL;
74 base >>= PAGE_SHIFT;
75 size >>= PAGE_SHIFT;
76 }
77 reg = mtrr_del_page(-1, base, size);
78 if (reg < 0)
79 return reg;
80 if (fcount == NULL)
81 return reg;
82 if (fcount[reg] < 1)
83 return -EINVAL;
84 --fcount[reg];
85 return reg;
86}
87
88/* RED-PEN: seq_file can seek now. this is ignored. */
89static ssize_t
90mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
91/* Format of control line:
92 "base=%Lx size=%Lx type=%s" OR:
93 "disable=%d"
94*/
95{
96 int i, err;
97 unsigned long reg;
98 unsigned long long base, size;
99 char *ptr;
100 char line[LINE_SIZE];
101 size_t linelen;
102
103 if (!capable(CAP_SYS_ADMIN))
104 return -EPERM;
105 if (!len)
106 return -EINVAL;
107 memset(line, 0, LINE_SIZE);
108 if (len > LINE_SIZE)
109 len = LINE_SIZE;
110 if (copy_from_user(line, buf, len - 1))
111 return -EFAULT;
112 linelen = strlen(line);
113 ptr = line + linelen - 1;
114 if (linelen && *ptr == '\n')
115 *ptr = '\0';
116 if (!strncmp(line, "disable=", 8)) {
117 reg = simple_strtoul(line + 8, &ptr, 0);
118 err = mtrr_del_page(reg, 0, 0);
119 if (err < 0)
120 return err;
121 return len;
122 }
123 if (strncmp(line, "base=", 5))
124 return -EINVAL;
125 base = simple_strtoull(line + 5, &ptr, 0);
126 for (; isspace(*ptr); ++ptr) ;
127 if (strncmp(ptr, "size=", 5))
128 return -EINVAL;
129 size = simple_strtoull(ptr + 5, &ptr, 0);
130 if ((base & 0xfff) || (size & 0xfff))
131 return -EINVAL;
132 for (; isspace(*ptr); ++ptr) ;
133 if (strncmp(ptr, "type=", 5))
134 return -EINVAL;
135 ptr += 5;
136 for (; isspace(*ptr); ++ptr) ;
137 for (i = 0; i < MTRR_NUM_TYPES; ++i) {
138 if (strcmp(ptr, mtrr_strings[i]))
139 continue;
140 base >>= PAGE_SHIFT;
141 size >>= PAGE_SHIFT;
142 err =
143 mtrr_add_page((unsigned long) base, (unsigned long) size, i,
144 1);
145 if (err < 0)
146 return err;
147 return len;
148 }
149 return -EINVAL;
150}
151
152static int
153mtrr_ioctl(struct inode *inode, struct file *file,
154 unsigned int cmd, unsigned long __arg)
155{
156 int err;
157 mtrr_type type;
158 struct mtrr_sentry sentry;
159 struct mtrr_gentry gentry;
160 void __user *arg = (void __user *) __arg;
161
162 switch (cmd) {
163 default:
164 return -ENOTTY;
165 case MTRRIOC_ADD_ENTRY:
166 if (!capable(CAP_SYS_ADMIN))
167 return -EPERM;
168 if (copy_from_user(&sentry, arg, sizeof sentry))
169 return -EFAULT;
170 err =
171 mtrr_file_add(sentry.base, sentry.size, sentry.type, 1,
172 file, 0);
173 if (err < 0)
174 return err;
175 break;
176 case MTRRIOC_SET_ENTRY:
177 if (!capable(CAP_SYS_ADMIN))
178 return -EPERM;
179 if (copy_from_user(&sentry, arg, sizeof sentry))
180 return -EFAULT;
181 err = mtrr_add(sentry.base, sentry.size, sentry.type, 0);
182 if (err < 0)
183 return err;
184 break;
185 case MTRRIOC_DEL_ENTRY:
186 if (!capable(CAP_SYS_ADMIN))
187 return -EPERM;
188 if (copy_from_user(&sentry, arg, sizeof sentry))
189 return -EFAULT;
190 err = mtrr_file_del(sentry.base, sentry.size, file, 0);
191 if (err < 0)
192 return err;
193 break;
194 case MTRRIOC_KILL_ENTRY:
195 if (!capable(CAP_SYS_ADMIN))
196 return -EPERM;
197 if (copy_from_user(&sentry, arg, sizeof sentry))
198 return -EFAULT;
199 err = mtrr_del(-1, sentry.base, sentry.size);
200 if (err < 0)
201 return err;
202 break;
203 case MTRRIOC_GET_ENTRY:
204 if (copy_from_user(&gentry, arg, sizeof gentry))
205 return -EFAULT;
206 if (gentry.regnum >= num_var_ranges)
207 return -EINVAL;
208 mtrr_if->get(gentry.regnum, &gentry.base, &gentry.size, &type);
209
210 /* Hide entries that go above 4GB */
211 if (gentry.base + gentry.size > 0x100000
212 || gentry.size == 0x100000)
213 gentry.base = gentry.size = gentry.type = 0;
214 else {
215 gentry.base <<= PAGE_SHIFT;
216 gentry.size <<= PAGE_SHIFT;
217 gentry.type = type;
218 }
219
220 if (copy_to_user(arg, &gentry, sizeof gentry))
221 return -EFAULT;
222 break;
223 case MTRRIOC_ADD_PAGE_ENTRY:
224 if (!capable(CAP_SYS_ADMIN))
225 return -EPERM;
226 if (copy_from_user(&sentry, arg, sizeof sentry))
227 return -EFAULT;
228 err =
229 mtrr_file_add(sentry.base, sentry.size, sentry.type, 1,
230 file, 1);
231 if (err < 0)
232 return err;
233 break;
234 case MTRRIOC_SET_PAGE_ENTRY:
235 if (!capable(CAP_SYS_ADMIN))
236 return -EPERM;
237 if (copy_from_user(&sentry, arg, sizeof sentry))
238 return -EFAULT;
239 err = mtrr_add_page(sentry.base, sentry.size, sentry.type, 0);
240 if (err < 0)
241 return err;
242 break;
243 case MTRRIOC_DEL_PAGE_ENTRY:
244 if (!capable(CAP_SYS_ADMIN))
245 return -EPERM;
246 if (copy_from_user(&sentry, arg, sizeof sentry))
247 return -EFAULT;
248 err = mtrr_file_del(sentry.base, sentry.size, file, 1);
249 if (err < 0)
250 return err;
251 break;
252 case MTRRIOC_KILL_PAGE_ENTRY:
253 if (!capable(CAP_SYS_ADMIN))
254 return -EPERM;
255 if (copy_from_user(&sentry, arg, sizeof sentry))
256 return -EFAULT;
257 err = mtrr_del_page(-1, sentry.base, sentry.size);
258 if (err < 0)
259 return err;
260 break;
261 case MTRRIOC_GET_PAGE_ENTRY:
262 if (copy_from_user(&gentry, arg, sizeof gentry))
263 return -EFAULT;
264 if (gentry.regnum >= num_var_ranges)
265 return -EINVAL;
266 mtrr_if->get(gentry.regnum, &gentry.base, &gentry.size, &type);
267 gentry.type = type;
268
269 if (copy_to_user(arg, &gentry, sizeof gentry))
270 return -EFAULT;
271 break;
272 }
273 return 0;
274}
275
276static int
277mtrr_close(struct inode *ino, struct file *file)
278{
279 int i, max;
280 unsigned int *fcount = FILE_FCOUNT(file);
281
282 if (fcount != NULL) {
283 max = num_var_ranges;
284 for (i = 0; i < max; ++i) {
285 while (fcount[i] > 0) {
286 mtrr_del(i, 0, 0);
287 --fcount[i];
288 }
289 }
290 kfree(fcount);
291 FILE_FCOUNT(file) = NULL;
292 }
293 return single_release(ino, file);
294}
295
296static int mtrr_seq_show(struct seq_file *seq, void *offset);
297
298static int mtrr_open(struct inode *inode, struct file *file)
299{
300 if (!mtrr_if)
301 return -EIO;
302 if (!mtrr_if->get)
303 return -ENXIO;
304 return single_open(file, mtrr_seq_show, NULL);
305}
306
307static struct file_operations mtrr_fops = {
308 .owner = THIS_MODULE,
309 .open = mtrr_open,
310 .read = seq_read,
311 .llseek = seq_lseek,
312 .write = mtrr_write,
313 .ioctl = mtrr_ioctl,
314 .release = mtrr_close,
315};
316
317
318static struct proc_dir_entry *proc_root_mtrr;
319
320
321static int mtrr_seq_show(struct seq_file *seq, void *offset)
322{
323 char factor;
324 int i, max, len;
325 mtrr_type type;
326 unsigned long base;
327 unsigned int size;
328
329 len = 0;
330 max = num_var_ranges;
331 for (i = 0; i < max; i++) {
332 mtrr_if->get(i, &base, &size, &type);
333 if (size == 0)
334 usage_table[i] = 0;
335 else {
336 if (size < (0x100000 >> PAGE_SHIFT)) {
337 /* less than 1MB */
338 factor = 'K';
339 size <<= PAGE_SHIFT - 10;
340 } else {
341 factor = 'M';
342 size >>= 20 - PAGE_SHIFT;
343 }
344 /* RED-PEN: base can be > 32bit */
345 len += seq_printf(seq,
346 "reg%02i: base=0x%05lx000 (%4liMB), size=%4i%cB: %s, count=%d\n",
347 i, base, base >> (20 - PAGE_SHIFT), size, factor,
348 mtrr_attrib_to_str(type), usage_table[i]);
349 }
350 }
351 return 0;
352}
353
354static int __init mtrr_if_init(void)
355{
356 struct cpuinfo_x86 *c = &boot_cpu_data;
357
358 if ((!cpu_has(c, X86_FEATURE_MTRR)) &&
359 (!cpu_has(c, X86_FEATURE_K6_MTRR)) &&
360 (!cpu_has(c, X86_FEATURE_CYRIX_ARR)) &&
361 (!cpu_has(c, X86_FEATURE_CENTAUR_MCR)))
362 return -ENODEV;
363
364 proc_root_mtrr =
365 create_proc_entry("mtrr", S_IWUSR | S_IRUGO, &proc_root);
366 if (proc_root_mtrr) {
367 proc_root_mtrr->owner = THIS_MODULE;
368 proc_root_mtrr->proc_fops = &mtrr_fops;
369 }
370 return 0;
371}
372
373arch_initcall(mtrr_if_init);
374#endif /* CONFIG_PROC_FS */