import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / i386 / kernel / msr.c
1 #ident "$Id$"
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/major.h>
37
38 #include <asm/processor.h>
39 #include <asm/msr.h>
40 #include <asm/uaccess.h>
41 #include <asm/system.h>
42
43 /* Note: "err" is handled in a funny way below.  Otherwise one version
44    of gcc or another breaks. */
45
46 static inline int wrmsr_eio(u32 reg, u32 eax, u32 edx)
47 {
48   int err;
49
50   asm volatile(
51                "1:      wrmsr\n"
52                "2:\n"
53                ".section .fixup,\"ax\"\n"
54                "3:      movl %4,%0\n"
55                "        jmp 2b\n"
56                ".previous\n"
57                ".section __ex_table,\"a\"\n"
58                "        .align 4\n"
59                "        .long 1b,3b\n"
60                ".previous"
61                : "=&bDS" (err)
62                : "a" (eax), "d" (edx), "c" (reg), "i" (-EIO), "0" (0));
63
64   return err;
65 }
66
67 static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx)
68 {
69   int err;
70
71   asm volatile(
72                "1:      rdmsr\n"
73                "2:\n"
74                ".section .fixup,\"ax\"\n"
75                "3:      movl %4,%0\n"
76                "        jmp 2b\n"
77                ".previous\n"
78                ".section __ex_table,\"a\"\n"
79                "        .align 4\n"
80                "        .long 1b,3b\n"
81                ".previous"
82                : "=&bDS" (err), "=a" (*eax), "=d" (*edx)
83                : "c" (reg), "i" (-EIO), "0" (0));
84
85   return err;
86 }
87
88 #ifdef CONFIG_SMP
89
90 struct msr_command {
91   int cpu;
92   int err;
93   u32 reg;
94   u32 data[2];
95 };
96
97 static void msr_smp_wrmsr(void *cmd_block)
98 {
99   struct msr_command *cmd = (struct msr_command *) cmd_block;
100   
101   if ( cmd->cpu == smp_processor_id() )
102     cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]);
103 }
104
105 static void msr_smp_rdmsr(void *cmd_block)
106 {
107   struct msr_command *cmd = (struct msr_command *) cmd_block;
108   
109   if ( cmd->cpu == smp_processor_id() )
110     cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]);
111 }
112
113 static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
114 {
115   struct msr_command cmd;
116
117   if ( cpu == smp_processor_id() ) {
118     return wrmsr_eio(reg, eax, edx);
119   } else {
120     cmd.cpu = cpu;
121     cmd.reg = reg;
122     cmd.data[0] = eax;
123     cmd.data[1] = edx;
124     
125     smp_call_function(msr_smp_wrmsr, &cmd, 1, 1);
126     return cmd.err;
127   }
128 }
129
130 static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
131 {
132   struct msr_command cmd;
133
134   if ( cpu == smp_processor_id() ) {
135     return rdmsr_eio(reg, eax, edx);
136   } else {
137     cmd.cpu = cpu;
138     cmd.reg = reg;
139
140     smp_call_function(msr_smp_rdmsr, &cmd, 1, 1);
141     
142     *eax = cmd.data[0];
143     *edx = cmd.data[1];
144
145     return cmd.err;
146   }
147 }
148
149 #else /* ! CONFIG_SMP */
150
151 static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
152 {
153   return wrmsr_eio(reg, eax, edx);
154 }
155
156 static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
157 {
158   return rdmsr_eio(reg, eax, edx);
159 }
160
161 #endif /* ! CONFIG_SMP */
162
163 static loff_t msr_seek(struct file *file, loff_t offset, int orig)
164 {
165   switch (orig) {
166   case 0:
167     file->f_pos = offset;
168     return file->f_pos;
169   case 1:
170     file->f_pos += offset;
171     return file->f_pos;
172   default:
173     return -EINVAL;     /* SEEK_END not supported */
174   }
175 }
176
177 static ssize_t msr_read(struct file * file, char * buf,
178                         size_t count, loff_t *ppos)
179 {
180   u32 *tmp = (u32 *)buf;
181   u32 data[2];
182   size_t rv;
183   u32 reg = *ppos;
184   int cpu = MINOR(file->f_dentry->d_inode->i_rdev);
185   int err;
186
187   if ( count % 8 )
188     return -EINVAL; /* Invalid chunk size */
189   
190   for ( rv = 0 ; count ; count -= 8 ) {
191     err = do_rdmsr(cpu, reg, &data[0], &data[1]);
192     if ( err )
193       return err;
194     if ( copy_to_user(tmp,&data,8) )
195       return -EFAULT;
196     tmp += 2;
197   }
198
199   return ((char *)tmp) - buf;
200 }
201
202 static ssize_t msr_write(struct file * file, const char * buf,
203                          size_t count, loff_t *ppos)
204 {
205   const u32 *tmp = (const u32 *)buf;
206   u32 data[2];
207   size_t rv;
208   u32 reg = *ppos;
209   int cpu = MINOR(file->f_dentry->d_inode->i_rdev);
210   int err;
211
212   if ( count % 8 )
213     return -EINVAL; /* Invalid chunk size */
214   
215   for ( rv = 0 ; count ; count -= 8 ) {
216     if ( copy_from_user(&data,tmp,8) )
217       return -EFAULT;
218     err = do_wrmsr(cpu, reg, data[0], data[1]);
219     if ( err )
220       return err;
221     tmp += 2;
222   }
223
224   return ((char *)tmp) - buf;
225 }
226
227 static int msr_open(struct inode *inode, struct file *file)
228 {
229   int cpu = MINOR(file->f_dentry->d_inode->i_rdev);
230   struct cpuinfo_x86 *c = &(cpu_data)[cpu];
231   
232   if ( !(cpu_online_map & (1UL << cpu)) )
233     return -ENXIO;              /* No such CPU */
234   if ( !test_bit(X86_FEATURE_MSR, &c->x86_capability) )
235     return -EIO;                /* MSR not supported */
236   
237   return 0;
238 }
239
240 /*
241  * File operations we support
242  */
243 static struct file_operations msr_fops = {
244   owner:        THIS_MODULE,
245   llseek:       msr_seek,
246   read:         msr_read,
247   write:        msr_write,
248   open:         msr_open,
249 };
250
251 int __init msr_init(void)
252 {
253   if (register_chrdev(MSR_MAJOR, "cpu/msr", &msr_fops)) {
254     printk(KERN_ERR "msr: unable to get major %d for msr\n",
255            MSR_MAJOR);
256     return -EBUSY;
257   }
258   
259   return 0;
260 }
261
262 void __exit msr_exit(void)
263 {
264   unregister_chrdev(MSR_MAJOR, "cpu/msr");
265 }
266
267 module_init(msr_init);
268 module_exit(msr_exit)
269
270 EXPORT_NO_SYMBOLS;
271
272 MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
273 MODULE_DESCRIPTION("x86 generic MSR driver");
274 MODULE_LICENSE("GPL");