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 : : * Copyright 2009 Intel Corporation; author: H. Peter Anvin
6 : : *
7 : : * ----------------------------------------------------------------------- */
8 : :
9 : : /*
10 : : * x86 MSR access device
11 : : *
12 : : * This device is accessed by lseek() to the appropriate register number
13 : : * and then read/write in chunks of 8 bytes. A larger size means multiple
14 : : * reads or writes of the same register.
15 : : *
16 : : * This driver uses /dev/cpu/%d/msr where %d is the minor number, and on
17 : : * an SMP box will direct the access to CPU %d.
18 : : */
19 : :
20 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21 : :
22 : : #include <linux/module.h>
23 : :
24 : : #include <linux/types.h>
25 : : #include <linux/errno.h>
26 : : #include <linux/fcntl.h>
27 : : #include <linux/init.h>
28 : : #include <linux/poll.h>
29 : : #include <linux/smp.h>
30 : : #include <linux/major.h>
31 : : #include <linux/fs.h>
32 : : #include <linux/device.h>
33 : : #include <linux/cpu.h>
34 : : #include <linux/notifier.h>
35 : : #include <linux/uaccess.h>
36 : : #include <linux/gfp.h>
37 : : #include <linux/security.h>
38 : :
39 : : #include <asm/cpufeature.h>
40 : : #include <asm/msr.h>
41 : :
42 : : static struct class *msr_class;
43 : : static enum cpuhp_state cpuhp_msr_state;
44 : :
45 : 0 : static ssize_t msr_read(struct file *file, char __user *buf,
46 : : size_t count, loff_t *ppos)
47 : : {
48 : 0 : u32 __user *tmp = (u32 __user *) buf;
49 : 0 : u32 data[2];
50 : 0 : u32 reg = *ppos;
51 [ # # ]: 0 : int cpu = iminor(file_inode(file));
52 : 0 : int err = 0;
53 : 0 : ssize_t bytes = 0;
54 : :
55 [ # # ]: 0 : if (count % 8)
56 : : return -EINVAL; /* Invalid chunk size */
57 : :
58 [ # # ]: 0 : for (; count; count -= 8) {
59 : 0 : err = rdmsr_safe_on_cpu(cpu, reg, &data[0], &data[1]);
60 [ # # ]: 0 : if (err)
61 : : break;
62 [ # # ]: 0 : if (copy_to_user(tmp, &data, 8)) {
63 : : err = -EFAULT;
64 : : break;
65 : : }
66 : 0 : tmp += 2;
67 : 0 : bytes += 8;
68 : : }
69 : :
70 [ # # ]: 0 : return bytes ? bytes : err;
71 : : }
72 : :
73 : 0 : static ssize_t msr_write(struct file *file, const char __user *buf,
74 : : size_t count, loff_t *ppos)
75 : : {
76 : 0 : const u32 __user *tmp = (const u32 __user *)buf;
77 : 0 : u32 data[2];
78 : 0 : u32 reg = *ppos;
79 : 0 : int cpu = iminor(file_inode(file));
80 : 0 : int err = 0;
81 : 0 : ssize_t bytes = 0;
82 : :
83 : 0 : err = security_locked_down(LOCKDOWN_MSR);
84 [ # # ]: 0 : if (err)
85 : 0 : return err;
86 : :
87 [ # # ]: 0 : if (count % 8)
88 : : return -EINVAL; /* Invalid chunk size */
89 : :
90 [ # # ]: 0 : for (; count; count -= 8) {
91 [ # # ]: 0 : if (copy_from_user(&data, tmp, 8)) {
92 : : err = -EFAULT;
93 : : break;
94 : : }
95 : 0 : err = wrmsr_safe_on_cpu(cpu, reg, data[0], data[1]);
96 [ # # ]: 0 : if (err)
97 : : break;
98 : 0 : tmp += 2;
99 : 0 : bytes += 8;
100 : : }
101 : :
102 [ # # ]: 0 : return bytes ? bytes : err;
103 : : }
104 : :
105 : 0 : static long msr_ioctl(struct file *file, unsigned int ioc, unsigned long arg)
106 : : {
107 : 0 : u32 __user *uregs = (u32 __user *)arg;
108 : 0 : u32 regs[8];
109 [ # # # ]: 0 : int cpu = iminor(file_inode(file));
110 : 0 : int err;
111 : :
112 [ # # # ]: 0 : switch (ioc) {
113 : 0 : case X86_IOC_RDMSR_REGS:
114 [ # # ]: 0 : if (!(file->f_mode & FMODE_READ)) {
115 : : err = -EBADF;
116 : : break;
117 : : }
118 [ # # ]: 0 : if (copy_from_user(®s, uregs, sizeof(regs))) {
119 : : err = -EFAULT;
120 : : break;
121 : : }
122 : 0 : err = rdmsr_safe_regs_on_cpu(cpu, regs);
123 [ # # ]: 0 : if (err)
124 : : break;
125 [ # # ]: 0 : if (copy_to_user(uregs, ®s, sizeof(regs)))
126 : 0 : err = -EFAULT;
127 : : break;
128 : :
129 : 0 : case X86_IOC_WRMSR_REGS:
130 [ # # ]: 0 : if (!(file->f_mode & FMODE_WRITE)) {
131 : : err = -EBADF;
132 : : break;
133 : : }
134 [ # # ]: 0 : if (copy_from_user(®s, uregs, sizeof(regs))) {
135 : : err = -EFAULT;
136 : : break;
137 : : }
138 : 0 : err = security_locked_down(LOCKDOWN_MSR);
139 [ # # ]: 0 : if (err)
140 : : break;
141 : 0 : err = wrmsr_safe_regs_on_cpu(cpu, regs);
142 [ # # ]: 0 : if (err)
143 : : break;
144 [ # # ]: 0 : if (copy_to_user(uregs, ®s, sizeof(regs)))
145 : 0 : err = -EFAULT;
146 : : break;
147 : :
148 : : default:
149 : : err = -ENOTTY;
150 : : break;
151 : : }
152 : :
153 : 0 : return err;
154 : : }
155 : :
156 : 0 : static int msr_open(struct inode *inode, struct file *file)
157 : : {
158 : 0 : unsigned int cpu = iminor(file_inode(file));
159 : 0 : struct cpuinfo_x86 *c;
160 : :
161 [ # # ]: 0 : if (!capable(CAP_SYS_RAWIO))
162 : : return -EPERM;
163 : :
164 [ # # # # ]: 0 : if (cpu >= nr_cpu_ids || !cpu_online(cpu))
165 : 0 : return -ENXIO; /* No such CPU */
166 : :
167 : 0 : c = &cpu_data(cpu);
168 : 0 : if (!cpu_has(c, X86_FEATURE_MSR))
169 : : return -EIO; /* MSR not supported */
170 : :
171 : 0 : return 0;
172 : : }
173 : :
174 : : /*
175 : : * File operations we support
176 : : */
177 : : static const struct file_operations msr_fops = {
178 : : .owner = THIS_MODULE,
179 : : .llseek = no_seek_end_llseek,
180 : : .read = msr_read,
181 : : .write = msr_write,
182 : : .open = msr_open,
183 : : .unlocked_ioctl = msr_ioctl,
184 : : .compat_ioctl = msr_ioctl,
185 : : };
186 : :
187 : 21 : static int msr_device_create(unsigned int cpu)
188 : : {
189 : 21 : struct device *dev;
190 : :
191 : 21 : dev = device_create(msr_class, NULL, MKDEV(MSR_MAJOR, cpu), NULL,
192 : : "msr%d", cpu);
193 [ - + ]: 21 : return PTR_ERR_OR_ZERO(dev);
194 : : }
195 : :
196 : 0 : static int msr_device_destroy(unsigned int cpu)
197 : : {
198 : 0 : device_destroy(msr_class, MKDEV(MSR_MAJOR, cpu));
199 : 0 : return 0;
200 : : }
201 : :
202 : 105 : static char *msr_devnode(struct device *dev, umode_t *mode)
203 : : {
204 : 105 : return kasprintf(GFP_KERNEL, "cpu/%u/msr", MINOR(dev->devt));
205 : : }
206 : :
207 : 21 : static int __init msr_init(void)
208 : : {
209 : 21 : int err;
210 : :
211 [ - + ]: 21 : if (__register_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr", &msr_fops)) {
212 : 0 : pr_err("unable to get major %d for msr\n", MSR_MAJOR);
213 : 0 : return -EBUSY;
214 : : }
215 : 21 : msr_class = class_create(THIS_MODULE, "msr");
216 [ - + ]: 21 : if (IS_ERR(msr_class)) {
217 : 0 : err = PTR_ERR(msr_class);
218 : 0 : goto out_chrdev;
219 : : }
220 : 21 : msr_class->devnode = msr_devnode;
221 : :
222 : 21 : err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/msr:online",
223 : : msr_device_create, msr_device_destroy);
224 [ - + ]: 21 : if (err < 0)
225 : 0 : goto out_class;
226 : 21 : cpuhp_msr_state = err;
227 : 21 : return 0;
228 : :
229 : : out_class:
230 : 0 : class_destroy(msr_class);
231 : 0 : out_chrdev:
232 : 0 : __unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
233 : 0 : return err;
234 : : }
235 : : module_init(msr_init);
236 : :
237 : 0 : static void __exit msr_exit(void)
238 : : {
239 : 0 : cpuhp_remove_state(cpuhp_msr_state);
240 : 0 : class_destroy(msr_class);
241 : 0 : __unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
242 : 0 : }
243 : : module_exit(msr_exit)
244 : :
245 : : MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
246 : : MODULE_DESCRIPTION("x86 generic MSR driver");
247 : : MODULE_LICENSE("GPL");
|