linux/arch/x86/kernel/cpu/mtrr/if.c
<<
>>
Prefs
   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
  16static 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
  27const char *mtrr_attrib_to_str(int x)
  28{
  29        return (x <= 6) ? mtrr_strings[x] : "?";
  30}
  31
  32#ifdef CONFIG_PROC_FS
  33
  34static int
  35mtrr_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
  60static int
  61mtrr_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. */
  85static ssize_t
  86mtrr_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
 148static long
 149mtrr_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
 338static int
 339mtrr_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
 358static int mtrr_seq_show(struct seq_file *seq, void *offset);
 359
 360static 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
 369static 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
 381static struct proc_dir_entry *proc_root_mtrr;
 382
 383
 384static 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
 416static 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            create_proc_entry("mtrr", S_IWUSR | S_IRUGO, &proc_root);
 428        if (proc_root_mtrr) {
 429                proc_root_mtrr->owner = THIS_MODULE;
 430                proc_root_mtrr->proc_fops = &mtrr_fops;
 431        }
 432        return 0;
 433}
 434
 435arch_initcall(mtrr_if_init);
 436#endif                  /*  CONFIG_PROC_FS  */
 437
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.