Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /* ----------------------------------------------------------------------- *
3 : : *
4 : : * Copyright 2000-2008 H. Peter Anvin - All Rights Reserved
5 : : *
6 : : * ----------------------------------------------------------------------- */
7 : :
8 : : /*
9 : : * x86 CPUID access device
10 : : *
11 : : * This device is accessed by lseek() to the appropriate CPUID level
12 : : * and then read in chunks of 16 bytes. A larger size means multiple
13 : : * reads of consecutive levels.
14 : : *
15 : : * The lower 32 bits of the file position is used as the incoming %eax,
16 : : * and the upper 32 bits of the file position as the incoming %ecx,
17 : : * the latter intended for "counting" eax levels like eax=4.
18 : : *
19 : : * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on
20 : : * an SMP box will direct the access to CPU %d.
21 : : */
22 : :
23 : : #include <linux/module.h>
24 : :
25 : : #include <linux/types.h>
26 : : #include <linux/errno.h>
27 : : #include <linux/fcntl.h>
28 : : #include <linux/init.h>
29 : : #include <linux/poll.h>
30 : : #include <linux/smp.h>
31 : : #include <linux/major.h>
32 : : #include <linux/fs.h>
33 : : #include <linux/device.h>
34 : : #include <linux/cpu.h>
35 : : #include <linux/notifier.h>
36 : : #include <linux/uaccess.h>
37 : : #include <linux/gfp.h>
38 : : #include <linux/completion.h>
39 : :
40 : : #include <asm/processor.h>
41 : : #include <asm/msr.h>
42 : :
43 : : static struct class *cpuid_class;
44 : : static enum cpuhp_state cpuhp_cpuid_state;
45 : :
46 : : struct cpuid_regs_done {
47 : : struct cpuid_regs regs;
48 : : struct completion done;
49 : : };
50 : :
51 : 0 : static void cpuid_smp_cpuid(void *cmd_block)
52 : : {
53 : 0 : struct cpuid_regs_done *cmd = cmd_block;
54 : :
55 : 0 : cpuid_count(cmd->regs.eax, cmd->regs.ecx,
56 : 0 : &cmd->regs.eax, &cmd->regs.ebx,
57 : 0 : &cmd->regs.ecx, &cmd->regs.edx);
58 : :
59 : 0 : complete(&cmd->done);
60 : 0 : }
61 : :
62 : 0 : static ssize_t cpuid_read(struct file *file, char __user *buf,
63 : : size_t count, loff_t *ppos)
64 : : {
65 : 0 : char __user *tmp = buf;
66 : 0 : struct cpuid_regs_done cmd;
67 [ # # ]: 0 : int cpu = iminor(file_inode(file));
68 : 0 : u64 pos = *ppos;
69 : 0 : ssize_t bytes = 0;
70 : 0 : int err = 0;
71 : :
72 [ # # ]: 0 : if (count % 16)
73 : : return -EINVAL; /* Invalid chunk size */
74 : :
75 : 0 : init_completion(&cmd.done);
76 [ # # ]: 0 : for (; count; count -= 16) {
77 : 0 : call_single_data_t csd = {
78 : : .func = cpuid_smp_cpuid,
79 : : .info = &cmd,
80 : : };
81 : :
82 : 0 : cmd.regs.eax = pos;
83 : 0 : cmd.regs.ecx = pos >> 32;
84 : :
85 : 0 : err = smp_call_function_single_async(cpu, &csd);
86 [ # # ]: 0 : if (err)
87 : : break;
88 : 0 : wait_for_completion(&cmd.done);
89 [ # # ]: 0 : if (copy_to_user(tmp, &cmd.regs, 16)) {
90 : : err = -EFAULT;
91 : : break;
92 : : }
93 : 0 : tmp += 16;
94 : 0 : bytes += 16;
95 : 0 : *ppos = ++pos;
96 : 0 : reinit_completion(&cmd.done);
97 : : }
98 : :
99 [ # # ]: 0 : return bytes ? bytes : err;
100 : : }
101 : :
102 : 0 : static int cpuid_open(struct inode *inode, struct file *file)
103 : : {
104 : 0 : unsigned int cpu;
105 : 0 : struct cpuinfo_x86 *c;
106 : :
107 [ # # ]: 0 : cpu = iminor(file_inode(file));
108 [ # # # # ]: 0 : if (cpu >= nr_cpu_ids || !cpu_online(cpu))
109 : 0 : return -ENXIO; /* No such CPU */
110 : :
111 : 0 : c = &cpu_data(cpu);
112 [ # # ]: 0 : if (c->cpuid_level < 0)
113 : 0 : return -EIO; /* CPUID not supported */
114 : :
115 : : return 0;
116 : : }
117 : :
118 : : /*
119 : : * File operations we support
120 : : */
121 : : static const struct file_operations cpuid_fops = {
122 : : .owner = THIS_MODULE,
123 : : .llseek = no_seek_end_llseek,
124 : : .read = cpuid_read,
125 : : .open = cpuid_open,
126 : : };
127 : :
128 : 21 : static int cpuid_device_create(unsigned int cpu)
129 : : {
130 : 21 : struct device *dev;
131 : :
132 : 21 : dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL,
133 : : "cpu%d", cpu);
134 [ - + ]: 21 : return PTR_ERR_OR_ZERO(dev);
135 : : }
136 : :
137 : 0 : static int cpuid_device_destroy(unsigned int cpu)
138 : : {
139 : 0 : device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
140 : 0 : return 0;
141 : : }
142 : :
143 : 105 : static char *cpuid_devnode(struct device *dev, umode_t *mode)
144 : : {
145 : 105 : return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
146 : : }
147 : :
148 : 21 : static int __init cpuid_init(void)
149 : : {
150 : 21 : int err;
151 : :
152 [ - + ]: 21 : if (__register_chrdev(CPUID_MAJOR, 0, NR_CPUS,
153 : : "cpu/cpuid", &cpuid_fops)) {
154 : 0 : printk(KERN_ERR "cpuid: unable to get major %d for cpuid\n",
155 : : CPUID_MAJOR);
156 : 0 : return -EBUSY;
157 : : }
158 : 21 : cpuid_class = class_create(THIS_MODULE, "cpuid");
159 [ - + ]: 21 : if (IS_ERR(cpuid_class)) {
160 : 0 : err = PTR_ERR(cpuid_class);
161 : 0 : goto out_chrdev;
162 : : }
163 : 21 : cpuid_class->devnode = cpuid_devnode;
164 : :
165 : 21 : err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/cpuid:online",
166 : : cpuid_device_create, cpuid_device_destroy);
167 [ - + ]: 21 : if (err < 0)
168 : 0 : goto out_class;
169 : :
170 : 21 : cpuhp_cpuid_state = err;
171 : 21 : return 0;
172 : :
173 : : out_class:
174 : 0 : class_destroy(cpuid_class);
175 : 0 : out_chrdev:
176 : 0 : __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
177 : 0 : return err;
178 : : }
179 : : module_init(cpuid_init);
180 : :
181 : 0 : static void __exit cpuid_exit(void)
182 : : {
183 : 0 : cpuhp_remove_state(cpuhp_cpuid_state);
184 : 0 : class_destroy(cpuid_class);
185 : 0 : __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
186 : 0 : }
187 : : module_exit(cpuid_exit);
188 : :
189 : : MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
190 : : MODULE_DESCRIPTION("x86 generic CPUID driver");
191 : : MODULE_LICENSE("GPL");
|