msr.c 6.3 KB
Newer Older
L
Linus Torvalds 已提交
1
/* ----------------------------------------------------------------------- *
2 3
 *
 *   Copyright 2000-2008 H. Peter Anvin - All Rights Reserved
4
 *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
 *   USA; either version 2 of the License, or (at your option) any later
 *   version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

/*
 * x86 MSR access device
 *
 * This device is accessed by lseek() to the appropriate register number
 * and then read/write in chunks of 8 bytes.  A larger size means multiple
 * reads or writes of the same register.
 *
 * This driver uses /dev/cpu/%d/msr where %d is the minor number, and on
 * an SMP box will direct the access to CPU %d.
 */

#include <linux/module.h>

#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/smp.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cpu.h>
#include <linux/notifier.h>
38
#include <linux/uaccess.h>
39
#include <linux/gfp.h>
L
Linus Torvalds 已提交
40 41 42 43

#include <asm/processor.h>
#include <asm/msr.h>

44
static struct class *msr_class;
L
Linus Torvalds 已提交
45 46 47

static loff_t msr_seek(struct file *file, loff_t offset, int orig)
{
48 49
	loff_t ret;
	struct inode *inode = file->f_mapping->host;
L
Linus Torvalds 已提交
50

51
	mutex_lock(&inode->i_mutex);
L
Linus Torvalds 已提交
52 53 54 55 56 57 58 59
	switch (orig) {
	case 0:
		file->f_pos = offset;
		ret = file->f_pos;
		break;
	case 1:
		file->f_pos += offset;
		ret = file->f_pos;
60 61 62
		break;
	default:
		ret = -EINVAL;
L
Linus Torvalds 已提交
63
	}
64
	mutex_unlock(&inode->i_mutex);
L
Linus Torvalds 已提交
65 66 67
	return ret;
}

68 69
static ssize_t msr_read(struct file *file, char __user *buf,
			size_t count, loff_t *ppos)
L
Linus Torvalds 已提交
70 71 72 73
{
	u32 __user *tmp = (u32 __user *) buf;
	u32 data[2];
	u32 reg = *ppos;
74
	int cpu = iminor(file->f_path.dentry->d_inode);
75 76
	int err = 0;
	ssize_t bytes = 0;
L
Linus Torvalds 已提交
77 78 79 80

	if (count % 8)
		return -EINVAL;	/* Invalid chunk size */

81
	for (; count; count -= 8) {
82
		err = rdmsr_safe_on_cpu(cpu, reg, &data[0], &data[1]);
83
		if (err)
84 85 86 87
			break;
		if (copy_to_user(tmp, &data, 8)) {
			err = -EFAULT;
			break;
88
		}
L
Linus Torvalds 已提交
89
		tmp += 2;
90
		bytes += 8;
L
Linus Torvalds 已提交
91 92
	}

93
	return bytes ? bytes : err;
L
Linus Torvalds 已提交
94 95 96 97 98 99 100 101
}

static ssize_t msr_write(struct file *file, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	const u32 __user *tmp = (const u32 __user *)buf;
	u32 data[2];
	u32 reg = *ppos;
102
	int cpu = iminor(file->f_path.dentry->d_inode);
103 104
	int err = 0;
	ssize_t bytes = 0;
L
Linus Torvalds 已提交
105 106 107 108

	if (count % 8)
		return -EINVAL;	/* Invalid chunk size */

109
	for (; count; count -= 8) {
110 111 112 113
		if (copy_from_user(&data, tmp, 8)) {
			err = -EFAULT;
			break;
		}
114
		err = wrmsr_safe_on_cpu(cpu, reg, data[0], data[1]);
115
		if (err)
116
			break;
L
Linus Torvalds 已提交
117
		tmp += 2;
118
		bytes += 8;
L
Linus Torvalds 已提交
119 120
	}

121
	return bytes ? bytes : err;
L
Linus Torvalds 已提交
122 123
}

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
static long msr_ioctl(struct file *file, unsigned int ioc, unsigned long arg)
{
	u32 __user *uregs = (u32 __user *)arg;
	u32 regs[8];
	int cpu = iminor(file->f_path.dentry->d_inode);
	int err;

	switch (ioc) {
	case X86_IOC_RDMSR_REGS:
		if (!(file->f_mode & FMODE_READ)) {
			err = -EBADF;
			break;
		}
		if (copy_from_user(&regs, uregs, sizeof regs)) {
			err = -EFAULT;
			break;
		}
		err = rdmsr_safe_regs_on_cpu(cpu, regs);
		if (err)
			break;
		if (copy_to_user(uregs, &regs, sizeof regs))
			err = -EFAULT;
		break;

	case X86_IOC_WRMSR_REGS:
		if (!(file->f_mode & FMODE_WRITE)) {
			err = -EBADF;
			break;
		}
		if (copy_from_user(&regs, uregs, sizeof regs)) {
			err = -EFAULT;
			break;
		}
		err = wrmsr_safe_regs_on_cpu(cpu, regs);
		if (err)
			break;
		if (copy_to_user(uregs, &regs, sizeof regs))
			err = -EFAULT;
		break;

	default:
		err = -ENOTTY;
		break;
	}

	return err;
}

L
Linus Torvalds 已提交
172 173
static int msr_open(struct inode *inode, struct file *file)
{
174 175
	unsigned int cpu;
	struct cpuinfo_x86 *c;
L
Linus Torvalds 已提交
176

177
	cpu = iminor(file->f_path.dentry->d_inode);
178 179 180
	if (cpu >= nr_cpu_ids || !cpu_online(cpu))
		return -ENXIO;	/* No such CPU */

181 182
	c = &cpu_data(cpu);
	if (!cpu_has(c, X86_FEATURE_MSR))
183 184 185
		return -EIO;	/* MSR not supported */

	return 0;
L
Linus Torvalds 已提交
186 187 188 189 190
}

/*
 * File operations we support
 */
191
static const struct file_operations msr_fops = {
L
Linus Torvalds 已提交
192 193 194 195 196
	.owner = THIS_MODULE,
	.llseek = msr_seek,
	.read = msr_read,
	.write = msr_write,
	.open = msr_open,
197 198
	.unlocked_ioctl = msr_ioctl,
	.compat_ioctl = msr_ioctl,
L
Linus Torvalds 已提交
199 200
};

201
static int __cpuinit msr_device_create(int cpu)
L
Linus Torvalds 已提交
202
{
203
	struct device *dev;
L
Linus Torvalds 已提交
204

205 206
	dev = device_create(msr_class, NULL, MKDEV(MSR_MAJOR, cpu), NULL,
			    "msr%d", cpu);
207 208 209 210 211 212
	return IS_ERR(dev) ? PTR_ERR(dev) : 0;
}

static void msr_device_destroy(int cpu)
{
	device_destroy(msr_class, MKDEV(MSR_MAJOR, cpu));
L
Linus Torvalds 已提交
213 214
}

S
Satyam Sharma 已提交
215
static int __cpuinit msr_class_cpu_callback(struct notifier_block *nfb,
216
				unsigned long action, void *hcpu)
L
Linus Torvalds 已提交
217 218
{
	unsigned int cpu = (unsigned long)hcpu;
219
	int err = 0;
L
Linus Torvalds 已提交
220 221

	switch (action) {
222 223
	case CPU_UP_PREPARE:
		err = msr_device_create(cpu);
L
Linus Torvalds 已提交
224
		break;
225
	case CPU_UP_CANCELED:
226
	case CPU_UP_CANCELED_FROZEN:
L
Linus Torvalds 已提交
227
	case CPU_DEAD:
228
		msr_device_destroy(cpu);
L
Linus Torvalds 已提交
229 230
		break;
	}
231
	return notifier_from_errno(err);
L
Linus Torvalds 已提交
232 233
}

234
static struct notifier_block __refdata msr_class_cpu_notifier = {
L
Linus Torvalds 已提交
235 236 237
	.notifier_call = msr_class_cpu_callback,
};

238
static char *msr_devnode(struct device *dev, umode_t *mode)
239 240 241 242
{
	return kasprintf(GFP_KERNEL, "cpu/%u/msr", MINOR(dev->devt));
}

L
Linus Torvalds 已提交
243 244 245 246 247
static int __init msr_init(void)
{
	int i, err = 0;
	i = 0;

248
	if (__register_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr", &msr_fops)) {
L
Linus Torvalds 已提交
249 250 251 252 253
		printk(KERN_ERR "msr: unable to get major %d for msr\n",
		       MSR_MAJOR);
		err = -EBUSY;
		goto out;
	}
254
	msr_class = class_create(THIS_MODULE, "msr");
L
Linus Torvalds 已提交
255 256 257 258
	if (IS_ERR(msr_class)) {
		err = PTR_ERR(msr_class);
		goto out_chrdev;
	}
259
	msr_class->devnode = msr_devnode;
L
Linus Torvalds 已提交
260
	for_each_online_cpu(i) {
261
		err = msr_device_create(i);
L
Linus Torvalds 已提交
262 263 264
		if (err != 0)
			goto out_class;
	}
265
	register_hotcpu_notifier(&msr_class_cpu_notifier);
L
Linus Torvalds 已提交
266 267 268 269 270 271 272

	err = 0;
	goto out;

out_class:
	i = 0;
	for_each_online_cpu(i)
273
		msr_device_destroy(i);
274
	class_destroy(msr_class);
L
Linus Torvalds 已提交
275
out_chrdev:
276
	__unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
L
Linus Torvalds 已提交
277 278 279 280 281 282 283 284
out:
	return err;
}

static void __exit msr_exit(void)
{
	int cpu = 0;
	for_each_online_cpu(cpu)
285
		msr_device_destroy(cpu);
286
	class_destroy(msr_class);
287
	__unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
288
	unregister_hotcpu_notifier(&msr_class_cpu_notifier);
L
Linus Torvalds 已提交
289 290 291 292 293 294 295 296
}

module_init(msr_init);
module_exit(msr_exit)

MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
MODULE_DESCRIPTION("x86 generic MSR driver");
MODULE_LICENSE("GPL");