0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023 #include <linux/module.h>
0024
0025 #include <linux/types.h>
0026 #include <linux/errno.h>
0027 #include <linux/fcntl.h>
0028 #include <linux/init.h>
0029 #include <linux/poll.h>
0030 #include <linux/smp.h>
0031 #include <linux/major.h>
0032 #include <linux/fs.h>
0033 #include <linux/device.h>
0034 #include <linux/cpu.h>
0035 #include <linux/notifier.h>
0036 #include <linux/uaccess.h>
0037 #include <linux/gfp.h>
0038 #include <linux/completion.h>
0039
0040 #include <asm/processor.h>
0041 #include <asm/msr.h>
0042
0043 static struct class *cpuid_class;
0044 static enum cpuhp_state cpuhp_cpuid_state;
0045
0046 struct cpuid_regs_done {
0047 struct cpuid_regs regs;
0048 struct completion done;
0049 };
0050
0051 static void cpuid_smp_cpuid(void *cmd_block)
0052 {
0053 struct cpuid_regs_done *cmd = cmd_block;
0054
0055 cpuid_count(cmd->regs.eax, cmd->regs.ecx,
0056 &cmd->regs.eax, &cmd->regs.ebx,
0057 &cmd->regs.ecx, &cmd->regs.edx);
0058
0059 complete(&cmd->done);
0060 }
0061
0062 static ssize_t cpuid_read(struct file *file, char __user *buf,
0063 size_t count, loff_t *ppos)
0064 {
0065 char __user *tmp = buf;
0066 struct cpuid_regs_done cmd;
0067 int cpu = iminor(file_inode(file));
0068 u64 pos = *ppos;
0069 ssize_t bytes = 0;
0070 int err = 0;
0071
0072 if (count % 16)
0073 return -EINVAL;
0074
0075 init_completion(&cmd.done);
0076 for (; count; count -= 16) {
0077 call_single_data_t csd;
0078
0079 INIT_CSD(&csd, cpuid_smp_cpuid, &cmd);
0080
0081 cmd.regs.eax = pos;
0082 cmd.regs.ecx = pos >> 32;
0083
0084 err = smp_call_function_single_async(cpu, &csd);
0085 if (err)
0086 break;
0087 wait_for_completion(&cmd.done);
0088 if (copy_to_user(tmp, &cmd.regs, 16)) {
0089 err = -EFAULT;
0090 break;
0091 }
0092 tmp += 16;
0093 bytes += 16;
0094 *ppos = ++pos;
0095 reinit_completion(&cmd.done);
0096 }
0097
0098 return bytes ? bytes : err;
0099 }
0100
0101 static int cpuid_open(struct inode *inode, struct file *file)
0102 {
0103 unsigned int cpu;
0104 struct cpuinfo_x86 *c;
0105
0106 cpu = iminor(file_inode(file));
0107 if (cpu >= nr_cpu_ids || !cpu_online(cpu))
0108 return -ENXIO;
0109
0110 c = &cpu_data(cpu);
0111 if (c->cpuid_level < 0)
0112 return -EIO;
0113
0114 return 0;
0115 }
0116
0117
0118
0119
0120 static const struct file_operations cpuid_fops = {
0121 .owner = THIS_MODULE,
0122 .llseek = no_seek_end_llseek,
0123 .read = cpuid_read,
0124 .open = cpuid_open,
0125 };
0126
0127 static int cpuid_device_create(unsigned int cpu)
0128 {
0129 struct device *dev;
0130
0131 dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL,
0132 "cpu%d", cpu);
0133 return PTR_ERR_OR_ZERO(dev);
0134 }
0135
0136 static int cpuid_device_destroy(unsigned int cpu)
0137 {
0138 device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
0139 return 0;
0140 }
0141
0142 static char *cpuid_devnode(struct device *dev, umode_t *mode)
0143 {
0144 return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
0145 }
0146
0147 static int __init cpuid_init(void)
0148 {
0149 int err;
0150
0151 if (__register_chrdev(CPUID_MAJOR, 0, NR_CPUS,
0152 "cpu/cpuid", &cpuid_fops)) {
0153 printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n",
0154 CPUID_MAJOR);
0155 return -EBUSY;
0156 }
0157 cpuid_class = class_create(THIS_MODULE, "cpuid");
0158 if (IS_ERR(cpuid_class)) {
0159 err = PTR_ERR(cpuid_class);
0160 goto out_chrdev;
0161 }
0162 cpuid_class->devnode = cpuid_devnode;
0163
0164 err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/cpuid:online",
0165 cpuid_device_create, cpuid_device_destroy);
0166 if (err < 0)
0167 goto out_class;
0168
0169 cpuhp_cpuid_state = err;
0170 return 0;
0171
0172 out_class:
0173 class_destroy(cpuid_class);
0174 out_chrdev:
0175 __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
0176 return err;
0177 }
0178 module_init(cpuid_init);
0179
0180 static void __exit cpuid_exit(void)
0181 {
0182 cpuhp_remove_state(cpuhp_cpuid_state);
0183 class_destroy(cpuid_class);
0184 __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
0185 }
0186 module_exit(cpuid_exit);
0187
0188 MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
0189 MODULE_DESCRIPTION("x86 generic CPUID driver");
0190 MODULE_LICENSE("GPL");