diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:16:28 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:16:28 -0400 |
commit | 2ec1df4130c60d1eb49dc0fa0ed15858fede6b05 (patch) | |
tree | 97e578ba1546770eadbe84cff2dc44256f97b9d7 /arch/i386/kernel/cpu/mtrr/if.c | |
parent | ee580dc91efd83e6b55955e7261e8ad2a0e08d1a (diff) |
i386: move kernel/cpu/mtrr
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/i386/kernel/cpu/mtrr/if.c')
-rw-r--r-- | arch/i386/kernel/cpu/mtrr/if.c | 439 |
1 files changed, 0 insertions, 439 deletions
diff --git a/arch/i386/kernel/cpu/mtrr/if.c b/arch/i386/kernel/cpu/mtrr/if.c deleted file mode 100644 index c7d8f1756745..000000000000 --- a/arch/i386/kernel/cpu/mtrr/if.c +++ /dev/null | |||
@@ -1,439 +0,0 @@ | |||
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 | /* RED-PEN: this is accessed without any locking */ | ||
15 | extern unsigned int *usage_table; | ||
16 | |||
17 | |||
18 | #define FILE_FCOUNT(f) (((struct seq_file *)((f)->private_data))->private) | ||
19 | |||
20 | static const char *const mtrr_strings[MTRR_NUM_TYPES] = | ||
21 | { | ||
22 | "uncachable", /* 0 */ | ||
23 | "write-combining", /* 1 */ | ||
24 | "?", /* 2 */ | ||
25 | "?", /* 3 */ | ||
26 | "write-through", /* 4 */ | ||
27 | "write-protect", /* 5 */ | ||
28 | "write-back", /* 6 */ | ||
29 | }; | ||
30 | |||
31 | const char *mtrr_attrib_to_str(int x) | ||
32 | { | ||
33 | return (x <= 6) ? mtrr_strings[x] : "?"; | ||
34 | } | ||
35 | |||
36 | #ifdef CONFIG_PROC_FS | ||
37 | |||
38 | static int | ||
39 | mtrr_file_add(unsigned long base, unsigned long size, | ||
40 | unsigned int type, char increment, struct file *file, int page) | ||
41 | { | ||
42 | int reg, max; | ||
43 | unsigned int *fcount = FILE_FCOUNT(file); | ||
44 | |||
45 | max = num_var_ranges; | ||
46 | if (fcount == NULL) { | ||
47 | fcount = kzalloc(max * sizeof *fcount, GFP_KERNEL); | ||
48 | if (!fcount) | ||
49 | return -ENOMEM; | ||
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 | |||
64 | static int | ||
65 | mtrr_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. */ | ||
89 | static ssize_t | ||
90 | mtrr_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 | |||
152 | static long | ||
153 | mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg) | ||
154 | { | ||
155 | int err = 0; | ||
156 | mtrr_type type; | ||
157 | unsigned long size; | ||
158 | struct mtrr_sentry sentry; | ||
159 | struct mtrr_gentry gentry; | ||
160 | void __user *arg = (void __user *) __arg; | ||
161 | |||
162 | switch (cmd) { | ||
163 | case MTRRIOC_ADD_ENTRY: | ||
164 | case MTRRIOC_SET_ENTRY: | ||
165 | case MTRRIOC_DEL_ENTRY: | ||
166 | case MTRRIOC_KILL_ENTRY: | ||
167 | case MTRRIOC_ADD_PAGE_ENTRY: | ||
168 | case MTRRIOC_SET_PAGE_ENTRY: | ||
169 | case MTRRIOC_DEL_PAGE_ENTRY: | ||
170 | case MTRRIOC_KILL_PAGE_ENTRY: | ||
171 | if (copy_from_user(&sentry, arg, sizeof sentry)) | ||
172 | return -EFAULT; | ||
173 | break; | ||
174 | case MTRRIOC_GET_ENTRY: | ||
175 | case MTRRIOC_GET_PAGE_ENTRY: | ||
176 | if (copy_from_user(&gentry, arg, sizeof gentry)) | ||
177 | return -EFAULT; | ||
178 | break; | ||
179 | #ifdef CONFIG_COMPAT | ||
180 | case MTRRIOC32_ADD_ENTRY: | ||
181 | case MTRRIOC32_SET_ENTRY: | ||
182 | case MTRRIOC32_DEL_ENTRY: | ||
183 | case MTRRIOC32_KILL_ENTRY: | ||
184 | case MTRRIOC32_ADD_PAGE_ENTRY: | ||
185 | case MTRRIOC32_SET_PAGE_ENTRY: | ||
186 | case MTRRIOC32_DEL_PAGE_ENTRY: | ||
187 | case MTRRIOC32_KILL_PAGE_ENTRY: { | ||
188 | struct mtrr_sentry32 __user *s32 = (struct mtrr_sentry32 __user *)__arg; | ||
189 | err = get_user(sentry.base, &s32->base); | ||
190 | err |= get_user(sentry.size, &s32->size); | ||
191 | err |= get_user(sentry.type, &s32->type); | ||
192 | if (err) | ||
193 | return err; | ||
194 | break; | ||
195 | } | ||
196 | case MTRRIOC32_GET_ENTRY: | ||
197 | case MTRRIOC32_GET_PAGE_ENTRY: { | ||
198 | struct mtrr_gentry32 __user *g32 = (struct mtrr_gentry32 __user *)__arg; | ||
199 | err = get_user(gentry.regnum, &g32->regnum); | ||
200 | err |= get_user(gentry.base, &g32->base); | ||
201 | err |= get_user(gentry.size, &g32->size); | ||
202 | err |= get_user(gentry.type, &g32->type); | ||
203 | if (err) | ||
204 | return err; | ||
205 | break; | ||
206 | } | ||
207 | #endif | ||
208 | } | ||
209 | |||
210 | switch (cmd) { | ||
211 | default: | ||
212 | return -ENOTTY; | ||
213 | case MTRRIOC_ADD_ENTRY: | ||
214 | #ifdef CONFIG_COMPAT | ||
215 | case MTRRIOC32_ADD_ENTRY: | ||
216 | #endif | ||
217 | if (!capable(CAP_SYS_ADMIN)) | ||
218 | return -EPERM; | ||
219 | err = | ||
220 | mtrr_file_add(sentry.base, sentry.size, sentry.type, 1, | ||
221 | file, 0); | ||
222 | break; | ||
223 | case MTRRIOC_SET_ENTRY: | ||
224 | #ifdef CONFIG_COMPAT | ||
225 | case MTRRIOC32_SET_ENTRY: | ||
226 | #endif | ||
227 | if (!capable(CAP_SYS_ADMIN)) | ||
228 | return -EPERM; | ||
229 | err = mtrr_add(sentry.base, sentry.size, sentry.type, 0); | ||
230 | break; | ||
231 | case MTRRIOC_DEL_ENTRY: | ||
232 | #ifdef CONFIG_COMPAT | ||
233 | case MTRRIOC32_DEL_ENTRY: | ||
234 | #endif | ||
235 | if (!capable(CAP_SYS_ADMIN)) | ||
236 | return -EPERM; | ||
237 | err = mtrr_file_del(sentry.base, sentry.size, file, 0); | ||
238 | break; | ||
239 | case MTRRIOC_KILL_ENTRY: | ||
240 | #ifdef CONFIG_COMPAT | ||
241 | case MTRRIOC32_KILL_ENTRY: | ||
242 | #endif | ||
243 | if (!capable(CAP_SYS_ADMIN)) | ||
244 | return -EPERM; | ||
245 | err = mtrr_del(-1, sentry.base, sentry.size); | ||
246 | break; | ||
247 | case MTRRIOC_GET_ENTRY: | ||
248 | #ifdef CONFIG_COMPAT | ||
249 | case MTRRIOC32_GET_ENTRY: | ||
250 | #endif | ||
251 | if (gentry.regnum >= num_var_ranges) | ||
252 | return -EINVAL; | ||
253 | mtrr_if->get(gentry.regnum, &gentry.base, &size, &type); | ||
254 | |||
255 | /* Hide entries that go above 4GB */ | ||
256 | if (gentry.base + size - 1 >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT)) | ||
257 | || size >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT))) | ||
258 | gentry.base = gentry.size = gentry.type = 0; | ||
259 | else { | ||
260 | gentry.base <<= PAGE_SHIFT; | ||
261 | gentry.size = size << PAGE_SHIFT; | ||
262 | gentry.type = type; | ||
263 | } | ||
264 | |||
265 | break; | ||
266 | case MTRRIOC_ADD_PAGE_ENTRY: | ||
267 | #ifdef CONFIG_COMPAT | ||
268 | case MTRRIOC32_ADD_PAGE_ENTRY: | ||
269 | #endif | ||
270 | if (!capable(CAP_SYS_ADMIN)) | ||
271 | return -EPERM; | ||
272 | err = | ||
273 | mtrr_file_add(sentry.base, sentry.size, sentry.type, 1, | ||
274 | file, 1); | ||
275 | break; | ||
276 | case MTRRIOC_SET_PAGE_ENTRY: | ||
277 | #ifdef CONFIG_COMPAT | ||
278 | case MTRRIOC32_SET_PAGE_ENTRY: | ||
279 | #endif | ||
280 | if (!capable(CAP_SYS_ADMIN)) | ||
281 | return -EPERM; | ||
282 | err = mtrr_add_page(sentry.base, sentry.size, sentry.type, 0); | ||
283 | break; | ||
284 | case MTRRIOC_DEL_PAGE_ENTRY: | ||
285 | #ifdef CONFIG_COMPAT | ||
286 | case MTRRIOC32_DEL_PAGE_ENTRY: | ||
287 | #endif | ||
288 | if (!capable(CAP_SYS_ADMIN)) | ||
289 | return -EPERM; | ||
290 | err = mtrr_file_del(sentry.base, sentry.size, file, 1); | ||
291 | break; | ||
292 | case MTRRIOC_KILL_PAGE_ENTRY: | ||
293 | #ifdef CONFIG_COMPAT | ||
294 | case MTRRIOC32_KILL_PAGE_ENTRY: | ||
295 | #endif | ||
296 | if (!capable(CAP_SYS_ADMIN)) | ||
297 | return -EPERM; | ||
298 | err = mtrr_del_page(-1, sentry.base, sentry.size); | ||
299 | break; | ||
300 | case MTRRIOC_GET_PAGE_ENTRY: | ||
301 | #ifdef CONFIG_COMPAT | ||
302 | case MTRRIOC32_GET_PAGE_ENTRY: | ||
303 | #endif | ||
304 | if (gentry.regnum >= num_var_ranges) | ||
305 | return -EINVAL; | ||
306 | mtrr_if->get(gentry.regnum, &gentry.base, &size, &type); | ||
307 | /* Hide entries that would overflow */ | ||
308 | if (size != (__typeof__(gentry.size))size) | ||
309 | gentry.base = gentry.size = gentry.type = 0; | ||
310 | else { | ||
311 | gentry.size = size; | ||
312 | gentry.type = type; | ||
313 | } | ||
314 | break; | ||
315 | } | ||
316 | |||
317 | if (err) | ||
318 | return err; | ||
319 | |||
320 | switch(cmd) { | ||
321 | case MTRRIOC_GET_ENTRY: | ||
322 | case MTRRIOC_GET_PAGE_ENTRY: | ||
323 | if (copy_to_user(arg, &gentry, sizeof gentry)) | ||
324 | err = -EFAULT; | ||
325 | break; | ||
326 | #ifdef CONFIG_COMPAT | ||
327 | case MTRRIOC32_GET_ENTRY: | ||
328 | case MTRRIOC32_GET_PAGE_ENTRY: { | ||
329 | struct mtrr_gentry32 __user *g32 = (struct mtrr_gentry32 __user *)__arg; | ||
330 | err = put_user(gentry.base, &g32->base); | ||
331 | err |= put_user(gentry.size, &g32->size); | ||
332 | err |= put_user(gentry.regnum, &g32->regnum); | ||
333 | err |= put_user(gentry.type, &g32->type); | ||
334 | break; | ||
335 | } | ||
336 | #endif | ||
337 | } | ||
338 | return err; | ||
339 | } | ||
340 | |||
341 | static int | ||
342 | mtrr_close(struct inode *ino, struct file *file) | ||
343 | { | ||
344 | int i, max; | ||
345 | unsigned int *fcount = FILE_FCOUNT(file); | ||
346 | |||
347 | if (fcount != NULL) { | ||
348 | max = num_var_ranges; | ||
349 | for (i = 0; i < max; ++i) { | ||
350 | while (fcount[i] > 0) { | ||
351 | mtrr_del(i, 0, 0); | ||
352 | --fcount[i]; | ||
353 | } | ||
354 | } | ||
355 | kfree(fcount); | ||
356 | FILE_FCOUNT(file) = NULL; | ||
357 | } | ||
358 | return single_release(ino, file); | ||
359 | } | ||
360 | |||
361 | static int mtrr_seq_show(struct seq_file *seq, void *offset); | ||
362 | |||
363 | static int mtrr_open(struct inode *inode, struct file *file) | ||
364 | { | ||
365 | if (!mtrr_if) | ||
366 | return -EIO; | ||
367 | if (!mtrr_if->get) | ||
368 | return -ENXIO; | ||
369 | return single_open(file, mtrr_seq_show, NULL); | ||
370 | } | ||
371 | |||
372 | static const struct file_operations mtrr_fops = { | ||
373 | .owner = THIS_MODULE, | ||
374 | .open = mtrr_open, | ||
375 | .read = seq_read, | ||
376 | .llseek = seq_lseek, | ||
377 | .write = mtrr_write, | ||
378 | .unlocked_ioctl = mtrr_ioctl, | ||
379 | .compat_ioctl = mtrr_ioctl, | ||
380 | .release = mtrr_close, | ||
381 | }; | ||
382 | |||
383 | |||
384 | static struct proc_dir_entry *proc_root_mtrr; | ||
385 | |||
386 | |||
387 | static int mtrr_seq_show(struct seq_file *seq, void *offset) | ||
388 | { | ||
389 | char factor; | ||
390 | int i, max, len; | ||
391 | mtrr_type type; | ||
392 | unsigned long base, size; | ||
393 | |||
394 | len = 0; | ||
395 | max = num_var_ranges; | ||
396 | for (i = 0; i < max; i++) { | ||
397 | mtrr_if->get(i, &base, &size, &type); | ||
398 | if (size == 0) | ||
399 | usage_table[i] = 0; | ||
400 | else { | ||
401 | if (size < (0x100000 >> PAGE_SHIFT)) { | ||
402 | /* less than 1MB */ | ||
403 | factor = 'K'; | ||
404 | size <<= PAGE_SHIFT - 10; | ||
405 | } else { | ||
406 | factor = 'M'; | ||
407 | size >>= 20 - PAGE_SHIFT; | ||
408 | } | ||
409 | /* RED-PEN: base can be > 32bit */ | ||
410 | len += seq_printf(seq, | ||
411 | "reg%02i: base=0x%05lx000 (%4luMB), size=%4lu%cB: %s, count=%d\n", | ||
412 | i, base, base >> (20 - PAGE_SHIFT), size, factor, | ||
413 | mtrr_attrib_to_str(type), usage_table[i]); | ||
414 | } | ||
415 | } | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static int __init mtrr_if_init(void) | ||
420 | { | ||
421 | struct cpuinfo_x86 *c = &boot_cpu_data; | ||
422 | |||
423 | if ((!cpu_has(c, X86_FEATURE_MTRR)) && | ||
424 | (!cpu_has(c, X86_FEATURE_K6_MTRR)) && | ||
425 | (!cpu_has(c, X86_FEATURE_CYRIX_ARR)) && | ||
426 | (!cpu_has(c, X86_FEATURE_CENTAUR_MCR))) | ||
427 | return -ENODEV; | ||
428 | |||
429 | proc_root_mtrr = | ||
430 | create_proc_entry("mtrr", S_IWUSR | S_IRUGO, &proc_root); | ||
431 | if (proc_root_mtrr) { | ||
432 | proc_root_mtrr->owner = THIS_MODULE; | ||
433 | proc_root_mtrr->proc_fops = &mtrr_fops; | ||
434 | } | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | arch_initcall(mtrr_if_init); | ||
439 | #endif /* CONFIG_PROC_FS */ | ||