File: /usr/src/linux/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