Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* ----------------------------------------------------------------------- *
0003  *
0004  *   Copyright 2000-2008 H. Peter Anvin - All Rights Reserved
0005  *
0006  * ----------------------------------------------------------------------- */
0007 
0008 /*
0009  * x86 CPUID access device
0010  *
0011  * This device is accessed by lseek() to the appropriate CPUID level
0012  * and then read in chunks of 16 bytes.  A larger size means multiple
0013  * reads of consecutive levels.
0014  *
0015  * The lower 32 bits of the file position is used as the incoming %eax,
0016  * and the upper 32 bits of the file position as the incoming %ecx,
0017  * the latter intended for "counting" eax levels like eax=4.
0018  *
0019  * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on
0020  * an SMP box will direct the access to CPU %d.
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; /* Invalid chunk size */
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;  /* No such CPU */
0109 
0110     c = &cpu_data(cpu);
0111     if (c->cpuid_level < 0)
0112         return -EIO;    /* CPUID not supported */
0113 
0114     return 0;
0115 }
0116 
0117 /*
0118  * File operations we support
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");