diff options
Diffstat (limited to 'arch/i386/kernel/cpu/mtrr/if.c')
-rw-r--r-- | arch/i386/kernel/cpu/mtrr/if.c | 374 |
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 */ | ||
14 | extern unsigned int *usage_table; | ||
15 | |||
16 | |||
17 | #define FILE_FCOUNT(f) (((struct seq_file *)((f)->private_data))->private) | ||
18 | |||
19 | static 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 | |||
30 | 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, 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 | |||
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 int | ||
153 | mtrr_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 | |||
276 | static int | ||
277 | mtrr_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 | |||
296 | static int mtrr_seq_show(struct seq_file *seq, void *offset); | ||
297 | |||
298 | static 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 | |||
307 | static 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 | |||
318 | static struct proc_dir_entry *proc_root_mtrr; | ||
319 | |||
320 | |||
321 | static 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 | |||
354 | static 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 | |||
373 | arch_initcall(mtrr_if_init); | ||
374 | #endif /* CONFIG_PROC_FS */ | ||