Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/x86/kernel/cpu/mtrr/if.c
10775 views
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 size;
171
struct mtrr_sentry sentry;
172
struct mtrr_gentry gentry;
173
void __user *arg = (void __user *) __arg;
174
175
switch (cmd) {
176
case MTRRIOC_ADD_ENTRY:
177
case MTRRIOC_SET_ENTRY:
178
case MTRRIOC_DEL_ENTRY:
179
case MTRRIOC_KILL_ENTRY:
180
case MTRRIOC_ADD_PAGE_ENTRY:
181
case MTRRIOC_SET_PAGE_ENTRY:
182
case MTRRIOC_DEL_PAGE_ENTRY:
183
case MTRRIOC_KILL_PAGE_ENTRY:
184
if (copy_from_user(&sentry, arg, sizeof sentry))
185
return -EFAULT;
186
break;
187
case MTRRIOC_GET_ENTRY:
188
case MTRRIOC_GET_PAGE_ENTRY:
189
if (copy_from_user(&gentry, arg, sizeof gentry))
190
return -EFAULT;
191
break;
192
#ifdef CONFIG_COMPAT
193
case MTRRIOC32_ADD_ENTRY:
194
case MTRRIOC32_SET_ENTRY:
195
case MTRRIOC32_DEL_ENTRY:
196
case MTRRIOC32_KILL_ENTRY:
197
case MTRRIOC32_ADD_PAGE_ENTRY:
198
case MTRRIOC32_SET_PAGE_ENTRY:
199
case MTRRIOC32_DEL_PAGE_ENTRY:
200
case MTRRIOC32_KILL_PAGE_ENTRY: {
201
struct mtrr_sentry32 __user *s32;
202
203
s32 = (struct mtrr_sentry32 __user *)__arg;
204
err = get_user(sentry.base, &s32->base);
205
err |= get_user(sentry.size, &s32->size);
206
err |= get_user(sentry.type, &s32->type);
207
if (err)
208
return err;
209
break;
210
}
211
case MTRRIOC32_GET_ENTRY:
212
case MTRRIOC32_GET_PAGE_ENTRY: {
213
struct mtrr_gentry32 __user *g32;
214
215
g32 = (struct mtrr_gentry32 __user *)__arg;
216
err = get_user(gentry.regnum, &g32->regnum);
217
err |= get_user(gentry.base, &g32->base);
218
err |= get_user(gentry.size, &g32->size);
219
err |= get_user(gentry.type, &g32->type);
220
if (err)
221
return err;
222
break;
223
}
224
#endif
225
}
226
227
switch (cmd) {
228
default:
229
return -ENOTTY;
230
case MTRRIOC_ADD_ENTRY:
231
#ifdef CONFIG_COMPAT
232
case MTRRIOC32_ADD_ENTRY:
233
#endif
234
if (!capable(CAP_SYS_ADMIN))
235
return -EPERM;
236
err =
237
mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
238
file, 0);
239
break;
240
case MTRRIOC_SET_ENTRY:
241
#ifdef CONFIG_COMPAT
242
case MTRRIOC32_SET_ENTRY:
243
#endif
244
if (!capable(CAP_SYS_ADMIN))
245
return -EPERM;
246
err = mtrr_add(sentry.base, sentry.size, sentry.type, false);
247
break;
248
case MTRRIOC_DEL_ENTRY:
249
#ifdef CONFIG_COMPAT
250
case MTRRIOC32_DEL_ENTRY:
251
#endif
252
if (!capable(CAP_SYS_ADMIN))
253
return -EPERM;
254
err = mtrr_file_del(sentry.base, sentry.size, file, 0);
255
break;
256
case MTRRIOC_KILL_ENTRY:
257
#ifdef CONFIG_COMPAT
258
case MTRRIOC32_KILL_ENTRY:
259
#endif
260
if (!capable(CAP_SYS_ADMIN))
261
return -EPERM;
262
err = mtrr_del(-1, sentry.base, sentry.size);
263
break;
264
case MTRRIOC_GET_ENTRY:
265
#ifdef CONFIG_COMPAT
266
case MTRRIOC32_GET_ENTRY:
267
#endif
268
if (gentry.regnum >= num_var_ranges)
269
return -EINVAL;
270
mtrr_if->get(gentry.regnum, &gentry.base, &size, &type);
271
272
/* Hide entries that go above 4GB */
273
if (gentry.base + size - 1 >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT))
274
|| size >= (1UL << (8 * sizeof(gentry.size) - PAGE_SHIFT)))
275
gentry.base = gentry.size = gentry.type = 0;
276
else {
277
gentry.base <<= PAGE_SHIFT;
278
gentry.size = size << PAGE_SHIFT;
279
gentry.type = type;
280
}
281
282
break;
283
case MTRRIOC_ADD_PAGE_ENTRY:
284
#ifdef CONFIG_COMPAT
285
case MTRRIOC32_ADD_PAGE_ENTRY:
286
#endif
287
if (!capable(CAP_SYS_ADMIN))
288
return -EPERM;
289
err =
290
mtrr_file_add(sentry.base, sentry.size, sentry.type, true,
291
file, 1);
292
break;
293
case MTRRIOC_SET_PAGE_ENTRY:
294
#ifdef CONFIG_COMPAT
295
case MTRRIOC32_SET_PAGE_ENTRY:
296
#endif
297
if (!capable(CAP_SYS_ADMIN))
298
return -EPERM;
299
err =
300
mtrr_add_page(sentry.base, sentry.size, sentry.type, false);
301
break;
302
case MTRRIOC_DEL_PAGE_ENTRY:
303
#ifdef CONFIG_COMPAT
304
case MTRRIOC32_DEL_PAGE_ENTRY:
305
#endif
306
if (!capable(CAP_SYS_ADMIN))
307
return -EPERM;
308
err = mtrr_file_del(sentry.base, sentry.size, file, 1);
309
break;
310
case MTRRIOC_KILL_PAGE_ENTRY:
311
#ifdef CONFIG_COMPAT
312
case MTRRIOC32_KILL_PAGE_ENTRY:
313
#endif
314
if (!capable(CAP_SYS_ADMIN))
315
return -EPERM;
316
err = mtrr_del_page(-1, sentry.base, sentry.size);
317
break;
318
case MTRRIOC_GET_PAGE_ENTRY:
319
#ifdef CONFIG_COMPAT
320
case MTRRIOC32_GET_PAGE_ENTRY:
321
#endif
322
if (gentry.regnum >= num_var_ranges)
323
return -EINVAL;
324
mtrr_if->get(gentry.regnum, &gentry.base, &size, &type);
325
/* Hide entries that would overflow */
326
if (size != (__typeof__(gentry.size))size)
327
gentry.base = gentry.size = gentry.type = 0;
328
else {
329
gentry.size = size;
330
gentry.type = type;
331
}
332
break;
333
}
334
335
if (err)
336
return err;
337
338
switch (cmd) {
339
case MTRRIOC_GET_ENTRY:
340
case MTRRIOC_GET_PAGE_ENTRY:
341
if (copy_to_user(arg, &gentry, sizeof gentry))
342
err = -EFAULT;
343
break;
344
#ifdef CONFIG_COMPAT
345
case MTRRIOC32_GET_ENTRY:
346
case MTRRIOC32_GET_PAGE_ENTRY: {
347
struct mtrr_gentry32 __user *g32;
348
349
g32 = (struct mtrr_gentry32 __user *)__arg;
350
err = put_user(gentry.base, &g32->base);
351
err |= put_user(gentry.size, &g32->size);
352
err |= put_user(gentry.regnum, &g32->regnum);
353
err |= put_user(gentry.type, &g32->type);
354
break;
355
}
356
#endif
357
}
358
return err;
359
}
360
361
static int mtrr_close(struct inode *ino, struct file *file)
362
{
363
unsigned int *fcount = FILE_FCOUNT(file);
364
int i, max;
365
366
if (fcount != NULL) {
367
max = num_var_ranges;
368
for (i = 0; i < max; ++i) {
369
while (fcount[i] > 0) {
370
mtrr_del(i, 0, 0);
371
--fcount[i];
372
}
373
}
374
kfree(fcount);
375
FILE_FCOUNT(file) = NULL;
376
}
377
return single_release(ino, file);
378
}
379
380
static int mtrr_seq_show(struct seq_file *seq, void *offset);
381
382
static int mtrr_open(struct inode *inode, struct file *file)
383
{
384
if (!mtrr_if)
385
return -EIO;
386
if (!mtrr_if->get)
387
return -ENXIO;
388
return single_open(file, mtrr_seq_show, NULL);
389
}
390
391
static const struct file_operations mtrr_fops = {
392
.owner = THIS_MODULE,
393
.open = mtrr_open,
394
.read = seq_read,
395
.llseek = seq_lseek,
396
.write = mtrr_write,
397
.unlocked_ioctl = mtrr_ioctl,
398
.compat_ioctl = mtrr_ioctl,
399
.release = mtrr_close,
400
};
401
402
static int mtrr_seq_show(struct seq_file *seq, void *offset)
403
{
404
char factor;
405
int i, max, len;
406
mtrr_type type;
407
unsigned long base, size;
408
409
len = 0;
410
max = num_var_ranges;
411
for (i = 0; i < max; i++) {
412
mtrr_if->get(i, &base, &size, &type);
413
if (size == 0) {
414
mtrr_usage_table[i] = 0;
415
continue;
416
}
417
if (size < (0x100000 >> PAGE_SHIFT)) {
418
/* less than 1MB */
419
factor = 'K';
420
size <<= PAGE_SHIFT - 10;
421
} else {
422
factor = 'M';
423
size >>= 20 - PAGE_SHIFT;
424
}
425
/* Base can be > 32bit */
426
len += seq_printf(seq, "reg%02i: base=0x%06lx000 "
427
"(%5luMB), size=%5lu%cB, count=%d: %s\n",
428
i, base, base >> (20 - PAGE_SHIFT), size,
429
factor, mtrr_usage_table[i],
430
mtrr_attrib_to_str(type));
431
}
432
return 0;
433
}
434
435
static int __init mtrr_if_init(void)
436
{
437
struct cpuinfo_x86 *c = &boot_cpu_data;
438
439
if ((!cpu_has(c, X86_FEATURE_MTRR)) &&
440
(!cpu_has(c, X86_FEATURE_K6_MTRR)) &&
441
(!cpu_has(c, X86_FEATURE_CYRIX_ARR)) &&
442
(!cpu_has(c, X86_FEATURE_CENTAUR_MCR)))
443
return -ENODEV;
444
445
proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops);
446
return 0;
447
}
448
arch_initcall(mtrr_if_init);
449
#endif /* CONFIG_PROC_FS */
450
451