linux/arch/x86/kernel/cpuid.c
<<
>>
Prefs
   1/* ----------------------------------------------------------------------- *
   2 *
   3 *   Copyright 2000-2008 H. Peter Anvin - All Rights Reserved
   4 *
   5 *   This program is free software; you can redistribute it and/or modify
   6 *   it under the terms of the GNU General Public License as published by
   7 *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
   8 *   USA; either version 2 of the License, or (at your option) any later
   9 *   version; incorporated herein by reference.
  10 *
  11 * ----------------------------------------------------------------------- */
  12
  13/*
  14 * x86 CPUID access device
  15 *
  16 * This device is accessed by lseek() to the appropriate CPUID level
  17 * and then read in chunks of 16 bytes.  A larger size means multiple
  18 * reads of consecutive levels.
  19 *
  20 * The lower 32 bits of the file position is used as the incoming %eax,
  21 * and the upper 32 bits of the file position as the incoming %ecx,
  22 * the latter intended for "counting" eax levels like eax=4.
  23 *
  24 * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on
  25 * an SMP box will direct the access to CPU %d.
  26 */
  27
  28#include <linux/module.h>
  29
  30#include <linux/types.h>
  31#include <linux/errno.h>
  32#include <linux/fcntl.h>
  33#include <linux/init.h>
  34#include <linux/poll.h>
  35#include <linux/smp.h>
  36#include <linux/major.h>
  37#include <linux/fs.h>
  38#include <linux/device.h>
  39#include <linux/cpu.h>
  40#include <linux/notifier.h>
  41#include <linux/uaccess.h>
  42#include <linux/gfp.h>
  43
  44#include <asm/processor.h>
  45#include <asm/msr.h>
  46
  47static struct class *cpuid_class;
  48
  49struct cpuid_regs {
  50        u32 eax, ebx, ecx, edx;
  51};
  52
  53static void cpuid_smp_cpuid(void *cmd_block)
  54{
  55        struct cpuid_regs *cmd = (struct cpuid_regs *)cmd_block;
  56
  57        cpuid_count(cmd->eax, cmd->ecx,
  58                    &cmd->eax, &cmd->ebx, &cmd->ecx, &cmd->edx);
  59}
  60
  61static loff_t cpuid_seek(struct file *file, loff_t offset, int orig)
  62{
  63        loff_t ret;
  64        struct inode *inode = file->f_mapping->host;
  65
  66        mutex_lock(&inode->i_mutex);
  67        switch (orig) {
  68        case 0:
  69                file->f_pos = offset;
  70                ret = file->f_pos;
  71                break;
  72        case 1:
  73                file->f_pos += offset;
  74                ret = file->f_pos;
  75                break;
  76        default:
  77                ret = -EINVAL;
  78        }
  79        mutex_unlock(&inode->i_mutex);
  80        return ret;
  81}
  82
  83static ssize_t cpuid_read(struct file *file, char __user *buf,
  84                          size_t count, loff_t *ppos)
  85{
  86        char __user *tmp = buf;
  87        struct cpuid_regs cmd;
  88        int cpu = iminor(file_inode(file));
  89        u64 pos = *ppos;
  90        ssize_t bytes = 0;
  91        int err = 0;
  92
  93        if (count % 16)
  94                return -EINVAL; /* Invalid chunk size */
  95
  96        for (; count; count -= 16) {
  97                cmd.eax = pos;
  98                cmd.ecx = pos >> 32;
  99                err = smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1);
 100                if (err)
 101                        break;
 102                if (copy_to_user(tmp, &cmd, 16)) {
 103                        err = -EFAULT;
 104                        break;
 105                }
 106                tmp += 16;
 107                bytes += 16;
 108                *ppos = ++pos;
 109        }
 110
 111        return bytes ? bytes : err;
 112}
 113
 114static int cpuid_open(struct inode *inode, struct file *file)
 115{
 116        unsigned int cpu;
 117        struct cpuinfo_x86 *c;
 118
 119        cpu = iminor(file_inode(file));
 120        if (cpu >= nr_cpu_ids || !cpu_online(cpu))
 121                return -ENXIO;  /* No such CPU */
 122
 123        c = &cpu_data(cpu);
 124        if (c->cpuid_level < 0)
 125                return -EIO;    /* CPUID not supported */
 126
 127        return 0;
 128}
 129
 130/*
 131 * File operations we support
 132 */
 133static const struct file_operations cpuid_fops = {
 134        .owner = THIS_MODULE,
 135        .llseek = cpuid_seek,
 136        .read = cpuid_read,
 137        .open = cpuid_open,
 138};
 139
 140static int cpuid_device_create(int cpu)
 141{
 142        struct device *dev;
 143
 144        dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL,
 145                            "cpu%d", cpu);
 146        return IS_ERR(dev) ? PTR_ERR(dev) : 0;
 147}
 148
 149static void cpuid_device_destroy(int cpu)
 150{
 151        device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
 152}
 153
 154static int cpuid_class_cpu_callback(struct notifier_block *nfb,
 155                                    unsigned long action, void *hcpu)
 156{
 157        unsigned int cpu = (unsigned long)hcpu;
 158        int err = 0;
 159
 160        switch (action) {
 161        case CPU_UP_PREPARE:
 162                err = cpuid_device_create(cpu);
 163                break;
 164        case CPU_UP_CANCELED:
 165        case CPU_UP_CANCELED_FROZEN:
 166        case CPU_DEAD:
 167                cpuid_device_destroy(cpu);
 168                break;
 169        }
 170        return notifier_from_errno(err);
 171}
 172
 173static struct notifier_block __refdata cpuid_class_cpu_notifier =
 174{
 175        .notifier_call = cpuid_class_cpu_callback,
 176};
 177
 178static char *cpuid_devnode(struct device *dev, umode_t *mode)
 179{
 180        return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
 181}
 182
 183static int __init cpuid_init(void)
 184{
 185        int i, err = 0;
 186        i = 0;
 187
 188        if (__register_chrdev(CPUID_MAJOR, 0, NR_CPUS,
 189                              "cpu/cpuid", &cpuid_fops)) {
 190                printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n",
 191                       CPUID_MAJOR);
 192                err = -EBUSY;
 193                goto out;
 194        }
 195        cpuid_class = class_create(THIS_MODULE, "cpuid");
 196        if (IS_ERR(cpuid_class)) {
 197                err = PTR_ERR(cpuid_class);
 198                goto out_chrdev;
 199        }
 200        cpuid_class->devnode = cpuid_devnode;
 201        get_online_cpus();
 202        for_each_online_cpu(i) {
 203                err = cpuid_device_create(i);
 204                if (err != 0)
 205                        goto out_class;
 206        }
 207        register_hotcpu_notifier(&cpuid_class_cpu_notifier);
 208        put_online_cpus();
 209
 210        err = 0;
 211        goto out;
 212
 213out_class:
 214        i = 0;
 215        for_each_online_cpu(i) {
 216                cpuid_device_destroy(i);
 217        }
 218        put_online_cpus();
 219        class_destroy(cpuid_class);
 220out_chrdev:
 221        __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
 222out:
 223        return err;
 224}
 225
 226static void __exit cpuid_exit(void)
 227{
 228        int cpu = 0;
 229
 230        get_online_cpus();
 231        for_each_online_cpu(cpu)
 232                cpuid_device_destroy(cpu);
 233        class_destroy(cpuid_class);
 234        __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
 235        unregister_hotcpu_notifier(&cpuid_class_cpu_notifier);
 236        put_online_cpus();
 237}
 238
 239module_init(cpuid_init);
 240module_exit(cpuid_exit);
 241
 242MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
 243MODULE_DESCRIPTION("x86 generic CPUID driver");
 244MODULE_LICENSE("GPL");
 245