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
  49static void cpuid_smp_cpuid(void *cmd_block)
  50{
  51        struct cpuid_regs *cmd = (struct cpuid_regs *)cmd_block;
  52
  53        cpuid_count(cmd->eax, cmd->ecx,
  54                    &cmd->eax, &cmd->ebx, &cmd->ecx, &cmd->edx);
  55}
  56
  57static ssize_t cpuid_read(struct file *file, char __user *buf,
  58                          size_t count, loff_t *ppos)
  59{
  60        char __user *tmp = buf;
  61        struct cpuid_regs cmd;
  62        int cpu = iminor(file_inode(file));
  63        u64 pos = *ppos;
  64        ssize_t bytes = 0;
  65        int err = 0;
  66
  67        if (count % 16)
  68                return -EINVAL; /* Invalid chunk size */
  69
  70        for (; count; count -= 16) {
  71                cmd.eax = pos;
  72                cmd.ecx = pos >> 32;
  73                err = smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1);
  74                if (err)
  75                        break;
  76                if (copy_to_user(tmp, &cmd, 16)) {
  77                        err = -EFAULT;
  78                        break;
  79                }
  80                tmp += 16;
  81                bytes += 16;
  82                *ppos = ++pos;
  83        }
  84
  85        return bytes ? bytes : err;
  86}
  87
  88static int cpuid_open(struct inode *inode, struct file *file)
  89{
  90        unsigned int cpu;
  91        struct cpuinfo_x86 *c;
  92
  93        cpu = iminor(file_inode(file));
  94        if (cpu >= nr_cpu_ids || !cpu_online(cpu))
  95                return -ENXIO;  /* No such CPU */
  96
  97        c = &cpu_data(cpu);
  98        if (c->cpuid_level < 0)
  99                return -EIO;    /* CPUID not supported */
 100
 101        return 0;
 102}
 103
 104/*
 105 * File operations we support
 106 */
 107static const struct file_operations cpuid_fops = {
 108        .owner = THIS_MODULE,
 109        .llseek = no_seek_end_llseek,
 110        .read = cpuid_read,
 111        .open = cpuid_open,
 112};
 113
 114static int cpuid_device_create(int cpu)
 115{
 116        struct device *dev;
 117
 118        dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL,
 119                            "cpu%d", cpu);
 120        return IS_ERR(dev) ? PTR_ERR(dev) : 0;
 121}
 122
 123static void cpuid_device_destroy(int cpu)
 124{
 125        device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
 126}
 127
 128static int cpuid_class_cpu_callback(struct notifier_block *nfb,
 129                                    unsigned long action, void *hcpu)
 130{
 131        unsigned int cpu = (unsigned long)hcpu;
 132        int err = 0;
 133
 134        switch (action) {
 135        case CPU_UP_PREPARE:
 136                err = cpuid_device_create(cpu);
 137                break;
 138        case CPU_UP_CANCELED:
 139        case CPU_UP_CANCELED_FROZEN:
 140        case CPU_DEAD:
 141                cpuid_device_destroy(cpu);
 142                break;
 143        }
 144        return notifier_from_errno(err);
 145}
 146
 147static struct notifier_block __refdata cpuid_class_cpu_notifier =
 148{
 149        .notifier_call = cpuid_class_cpu_callback,
 150};
 151
 152static char *cpuid_devnode(struct device *dev, umode_t *mode)
 153{
 154        return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
 155}
 156
 157static int __init cpuid_init(void)
 158{
 159        int i, err = 0;
 160        i = 0;
 161
 162        if (__register_chrdev(CPUID_MAJOR, 0, NR_CPUS,
 163                              "cpu/cpuid", &cpuid_fops)) {
 164                printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n",
 165                       CPUID_MAJOR);
 166                err = -EBUSY;
 167                goto out;
 168        }
 169        cpuid_class = class_create(THIS_MODULE, "cpuid");
 170        if (IS_ERR(cpuid_class)) {
 171                err = PTR_ERR(cpuid_class);
 172                goto out_chrdev;
 173        }
 174        cpuid_class->devnode = cpuid_devnode;
 175
 176        cpu_notifier_register_begin();
 177        for_each_online_cpu(i) {
 178                err = cpuid_device_create(i);
 179                if (err != 0)
 180                        goto out_class;
 181        }
 182        __register_hotcpu_notifier(&cpuid_class_cpu_notifier);
 183        cpu_notifier_register_done();
 184
 185        err = 0;
 186        goto out;
 187
 188out_class:
 189        i = 0;
 190        for_each_online_cpu(i) {
 191                cpuid_device_destroy(i);
 192        }
 193        cpu_notifier_register_done();
 194        class_destroy(cpuid_class);
 195out_chrdev:
 196        __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
 197out:
 198        return err;
 199}
 200
 201static void __exit cpuid_exit(void)
 202{
 203        int cpu = 0;
 204
 205        cpu_notifier_register_begin();
 206        for_each_online_cpu(cpu)
 207                cpuid_device_destroy(cpu);
 208        class_destroy(cpuid_class);
 209        __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
 210        __unregister_hotcpu_notifier(&cpuid_class_cpu_notifier);
 211        cpu_notifier_register_done();
 212}
 213
 214module_init(cpuid_init);
 215module_exit(cpuid_exit);
 216
 217MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
 218MODULE_DESCRIPTION("x86 generic CPUID driver");
 219MODULE_LICENSE("GPL");
 220