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