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