[PATCH] x86_64 merge: arch + asm
[opensuse:kernel.git] / arch / x86_64 / kernel / msr.c
1 #ident "$Id: msr.c,v 1.6 2001/10/24 23:58:53 ak Exp $"
2 /* ----------------------------------------------------------------------- *
3  *   
4  *   Copyright 2000 H. Peter Anvin - All Rights Reserved
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
9  *   USA; either version 2 of the License, or (at your option) any later
10  *   version; incorporated herein by reference.
11  *
12  * ----------------------------------------------------------------------- */
13
14 /*
15  * msr.c
16  *
17  * x86 MSR access device
18  *
19  * This device is accessed by lseek() to the appropriate register number
20  * and then read/write in chunks of 8 bytes.  A larger size means multiple
21  * reads or writes of the same register.
22  *
23  * This driver uses /dev/cpu/%d/msr where %d is the minor number, and on
24  * an SMP box will direct the access to CPU %d.
25  */
26
27 #include <linux/module.h>
28 #include <linux/config.h>
29
30 #include <linux/types.h>
31 #include <linux/errno.h>
32 #include <linux/fcntl.h>
33 #include <linux/init.h>
34 #include <linux/poll.h>
35 #include <linux/smp.h>
36 #include <linux/smp_lock.h>
37 #include <linux/major.h>
38 #include <linux/fs.h>
39
40 #include <asm/processor.h>
41 #include <asm/msr.h>
42 #include <asm/uaccess.h>
43 #include <asm/system.h>
44
45 /* Note: "err" is handled in a funny way below.  Otherwise one version
46    of gcc or another breaks. */
47
48 static inline int wrmsr_eio(u32 reg, u32 eax, u32 edx)
49 {
50   int err;
51
52   asm volatile(
53                "1:      wrmsr\n"
54                "2:\n"
55                ".section .fixup,\"ax\"\n"
56                "3:      movl %4,%0\n"
57                "        jmp 2b\n"
58                ".previous\n"
59                ".section __ex_table,\"a\"\n"
60                "        .align 4\n"
61                "        .quad 1b,3b\n"
62                ".previous"
63                : "=&bDS" (err)
64                : "a" (eax), "d" (edx), "c" (reg), "i" (-EIO), "0" (0));
65
66   return err;
67 }
68
69 static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx)
70 {
71   int err;
72
73   asm volatile(
74                "1:      rdmsr\n"
75                "2:\n"
76                ".section .fixup,\"ax\"\n"
77                "3:      movl %4,%0\n"
78                "        jmp 2b\n"
79                ".previous\n"
80                ".section __ex_table,\"a\"\n"
81                "        .align 4\n"
82                "        .quad 1b,3b\n"
83                ".previous"
84                : "=&bDS" (err), "=a" (*eax), "=d" (*edx)
85                : "c" (reg), "i" (-EIO), "0" (0));
86
87   return err;
88 }
89
90 #ifdef CONFIG_SMP
91
92 struct msr_command {
93   int cpu;
94   int err;
95   u32 reg;
96   u32 data[2];
97 };
98
99 static void msr_smp_wrmsr(void *cmd_block)
100 {
101   struct msr_command *cmd = (struct msr_command *) cmd_block;
102   
103   if ( cmd->cpu == smp_processor_id() )
104     cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]);
105 }
106
107 static void msr_smp_rdmsr(void *cmd_block)
108 {
109   struct msr_command *cmd = (struct msr_command *) cmd_block;
110   
111   if ( cmd->cpu == smp_processor_id() )
112     cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]);
113 }
114
115 static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
116 {
117   struct msr_command cmd;
118
119   if ( cpu == smp_processor_id() ) {
120     return wrmsr_eio(reg, eax, edx);
121   } else {
122     cmd.cpu = cpu;
123     cmd.reg = reg;
124     cmd.data[0] = eax;
125     cmd.data[1] = edx;
126     
127     smp_call_function(msr_smp_wrmsr, &cmd, 1, 1);
128     return cmd.err;
129   }
130 }
131
132 static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
133 {
134   struct msr_command cmd;
135
136   if ( cpu == smp_processor_id() ) {
137     return rdmsr_eio(reg, eax, edx);
138   } else {
139     cmd.cpu = cpu;
140     cmd.reg = reg;
141
142     smp_call_function(msr_smp_rdmsr, &cmd, 1, 1);
143     
144     *eax = cmd.data[0];
145     *edx = cmd.data[1];
146
147     return cmd.err;
148   }
149 }
150
151 #else /* ! CONFIG_SMP */
152
153 static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
154 {
155   return wrmsr_eio(reg, eax, edx);
156 }
157
158 static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
159 {
160   return rdmsr_eio(reg, eax, edx);
161 }
162
163 #endif /* ! CONFIG_SMP */
164
165 static loff_t msr_seek(struct file *file, loff_t offset, int orig)
166 {
167   loff_t ret = -EINVAL;
168   lock_kernel();
169   switch (orig) {
170   case 0:
171     file->f_pos = offset;
172     ret = file->f_pos;
173     break;
174   case 1:
175     file->f_pos += offset;
176     ret = file->f_pos;
177   }
178   unlock_kernel();
179   return ret;
180 }
181
182 static ssize_t msr_read(struct file * file, char * buf,
183                         size_t count, loff_t *ppos)
184 {
185   u32 *tmp = (u32 *)buf;
186   u32 data[2];
187   size_t rv;
188   u32 reg = *ppos;
189   int cpu = minor(file->f_dentry->d_inode->i_rdev);
190   int err;
191
192   if ( count % 8 )
193     return -EINVAL; /* Invalid chunk size */
194   
195   for ( rv = 0 ; count ; count -= 8 ) {
196     err = do_rdmsr(cpu, reg, &data[0], &data[1]);
197     if ( err )
198       return err;
199     if ( copy_to_user(tmp,&data,8) )
200       return -EFAULT;
201     tmp += 2;
202   }
203
204   return ((char *)tmp) - buf;
205 }
206
207 static ssize_t msr_write(struct file * file, const char * buf,
208                          size_t count, loff_t *ppos)
209 {
210   const u32 *tmp = (const u32 *)buf;
211   u32 data[2];
212   size_t rv;
213   u32 reg = *ppos;
214   int cpu = minor(file->f_dentry->d_inode->i_rdev);
215   int err;
216
217   if ( count % 8 )
218     return -EINVAL; /* Invalid chunk size */
219   
220   for ( rv = 0 ; count ; count -= 8 ) {
221     if ( copy_from_user(&data,tmp,8) )
222       return -EFAULT;
223     err = do_wrmsr(cpu, reg, data[0], data[1]);
224     if ( err )
225       return err;
226     tmp += 2;
227   }
228
229   return ((char *)tmp) - buf;
230 }
231
232 static int msr_open(struct inode *inode, struct file *file)
233 {
234   int cpu = minor(file->f_dentry->d_inode->i_rdev);
235   struct cpuinfo_x86 *c = &(cpu_data)[cpu];
236   
237   if ( !(cpu_online_map & (1UL << cpu)) )
238     return -ENXIO;              /* No such CPU */
239   if ( !test_bit(X86_FEATURE_MSR, &c->x86_capability) )
240     return -EIO;                /* MSR not supported */
241   
242   return 0;
243 }
244
245 /*
246  * File operations we support
247  */
248 static struct file_operations msr_fops = {
249   owner:        THIS_MODULE,
250   llseek:       msr_seek,
251   read:         msr_read,
252   write:        msr_write,
253   open:         msr_open,
254 };
255
256 int __init msr_init(void)
257 {
258   if (register_chrdev(MSR_MAJOR, "cpu/msr", &msr_fops)) {
259     printk(KERN_ERR "msr: unable to get major %d for msr\n",
260            MSR_MAJOR);
261     return -EBUSY;
262   }
263   
264   return 0;
265 }
266
267 void __exit msr_exit(void)
268 {
269   unregister_chrdev(MSR_MAJOR, "cpu/msr");
270 }
271
272 module_init(msr_init);
273 module_exit(msr_exit)
274
275 EXPORT_NO_SYMBOLS;
276
277 MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
278 MODULE_DESCRIPTION("x86 generic MSR driver");
279 MODULE_LICENSE("GPL");