linux/arch/i386/kernel/cpuid.c
<<
>>
Prefs
   1/* ----------------------------------------------------------------------- *
   2 *   
   3 *   Copyright 2000 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 * cpuid.c
  15 *
  16 * x86 CPUID access device
  17 *
  18 * This device is accessed by lseek() to the appropriate CPUID level
  19 * and then read in chunks of 16 bytes.  A larger size means multiple
  20 * reads of consecutive levels.
  21 *
  22 * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on
  23 * an SMP box will direct the access to CPU %d.
  24 */
  25
  26#include <linux/module.h>
  27
  28#include <linux/types.h>
  29#include <linux/errno.h>
  30#include <linux/fcntl.h>
  31#include <linux/init.h>
  32#include <linux/poll.h>
  33#include <linux/smp.h>
  34#include <linux/major.h>
  35#include <linux/fs.h>
  36#include <linux/smp_lock.h>
  37#include <linux/device.h>
  38#include <linux/cpu.h>
  39#include <linux/notifier.h>
  40
  41#include <asm/processor.h>
  42#include <asm/msr.h>
  43#include <asm/uaccess.h>
  44#include <asm/system.h>
  45
  46static struct class *cpuid_class;
  47
  48#ifdef CONFIG_SMP
  49
  50struct cpuid_command {
  51        u32 reg;
  52        u32 *data;
  53};
  54
  55static void cpuid_smp_cpuid(void *cmd_block)
  56{
  57        struct cpuid_command *cmd = (struct cpuid_command *)cmd_block;
  58
  59        cpuid(cmd->reg, &cmd->data[0], &cmd->data[1], &cmd->data[2],
  60                      &cmd->data[3]);
  61}
  62
  63static inline void do_cpuid(int cpu, u32 reg, u32 * data)
  64{
  65        struct cpuid_command cmd;
  66
  67        preempt_disable();
  68        if (cpu == smp_processor_id()) {
  69                cpuid(reg, &data[0], &data[1], &data[2], &data[3]);
  70        } else {
  71                cmd.reg = reg;
  72                cmd.data = data;
  73
  74                smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1, 1);
  75        }
  76        preempt_enable();
  77}
  78#else                           /* ! CONFIG_SMP */
  79
  80static inline void do_cpuid(int cpu, u32 reg, u32 * data)
  81{
  82        cpuid(reg, &data[0], &data[1], &data[2], &data[3]);
  83}
  84
  85#endif                          /* ! CONFIG_SMP */
  86
  87static loff_t cpuid_seek(struct file *file, loff_t offset, int orig)
  88{
  89        loff_t ret;
  90
  91        lock_kernel();
  92
  93        switch (orig) {
  94        case 0:
  95                file->f_pos = offset;
  96                ret = file->f_pos;
  97                break;
  98        case 1:
  99                file->f_pos += offset;
 100                ret = file->f_pos;
 101                break;
 102        default:
 103                ret = -EINVAL;
 104        }
 105
 106        unlock_kernel();
 107        return ret;
 108}
 109
 110static ssize_t cpuid_read(struct file *file, char __user *buf,
 111                          size_t count, loff_t * ppos)
 112{
 113        char __user *tmp = buf;
 114        u32 data[4];
 115        u32 reg = *ppos;
 116        int cpu = iminor(file->f_path.dentry->d_inode);
 117
 118        if (count % 16)
 119                return -EINVAL; /* Invalid chunk size */
 120
 121        for (; count; count -= 16) {
 122                do_cpuid(cpu, reg, data);
 123                if (copy_to_user(tmp, &data, 16))
 124                        return -EFAULT;
 125                tmp += 16;
 126                *ppos = reg++;
 127        }
 128
 129        return tmp - buf;
 130}
 131
 132static int cpuid_open(struct inode *inode, struct file *file)
 133{
 134        unsigned int cpu = iminor(file->f_path.dentry->d_inode);
 135        struct cpuinfo_x86 *c = &(cpu_data)[cpu];
 136
 137        if (cpu >= NR_CPUS || !cpu_online(cpu))
 138                return -ENXIO;  /* No such CPU */
 139        if (c->cpuid_level < 0)
 140                return -EIO;    /* CPUID not supported */
 141
 142        return 0;
 143}
 144
 145/*
 146 * File operations we support
 147 */
 148static const struct file_operations cpuid_fops = {
 149        .owner = THIS_MODULE,
 150        .llseek = cpuid_seek,
 151        .read = cpuid_read,
 152        .open = cpuid_open,
 153};
 154
 155static int cpuid_device_create(int i)
 156{
 157        int err = 0;
 158        struct device *dev;
 159
 160        dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, i), "cpu%d",i);
 161        if (IS_ERR(dev))
 162                err = PTR_ERR(dev);
 163        return err;
 164}
 165
 166static int cpuid_class_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
 167{
 168        unsigned int cpu = (unsigned long)hcpu;
 169
 170        switch (action) {
 171        case CPU_ONLINE:
 172        case CPU_ONLINE_FROZEN:
 173                cpuid_device_create(cpu);
 174                break;
 175        case CPU_DEAD:
 176        case CPU_DEAD_FROZEN:
 177                device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
 178                break;
 179        }
 180        return NOTIFY_OK;
 181}
 182
 183static struct notifier_block __cpuinitdata cpuid_class_cpu_notifier =
 184{
 185        .notifier_call = cpuid_class_cpu_callback,
 186};
 187
 188static int __init cpuid_init(void)
 189{
 190        int i, err = 0;
 191        i = 0;
 192
 193        if (register_chrdev(CPUID_MAJOR, "cpu/cpuid", &cpuid_fops)) {
 194                printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n",
 195                       CPUID_MAJOR);
 196                err = -EBUSY;
 197                goto out;
 198        }
 199        cpuid_class = class_create(THIS_MODULE, "cpuid");
 200        if (IS_ERR(cpuid_class)) {
 201                err = PTR_ERR(cpuid_class);
 202                goto out_chrdev;
 203        }
 204        for_each_online_cpu(i) {
 205                err = cpuid_device_create(i);
 206                if (err != 0) 
 207                        goto out_class;
 208        }
 209        register_hotcpu_notifier(&cpuid_class_cpu_notifier);
 210
 211        err = 0;
 212        goto out;
 213
 214out_class:
 215        i = 0;
 216        for_each_online_cpu(i) {
 217                device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, i));
 218        }
 219        class_destroy(cpuid_class);
 220out_chrdev:
 221        unregister_chrdev(CPUID_MAJOR, "cpu/cpuid");    
 222out:
 223        return err;
 224}
 225
 226static void __exit cpuid_exit(void)
 227{
 228        int cpu = 0;
 229
 230        for_each_online_cpu(cpu)
 231                device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
 232        class_destroy(cpuid_class);
 233        unregister_chrdev(CPUID_MAJOR, "cpu/cpuid");
 234        unregister_hotcpu_notifier(&cpuid_class_cpu_notifier);
 235}
 236
 237module_init(cpuid_init);
 238module_exit(cpuid_exit);
 239
 240MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
 241MODULE_DESCRIPTION("x86 generic CPUID driver");
 242MODULE_LICENSE("GPL");
 243
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.