Ok. I didn't make 2.4.0 in 2000. Tough. I tried, but we had some
[davej-history.git] / arch / i386 / kernel / msr.c
blobfd8cf70883d50314619da0b4386386c804f93c0b
1 #ident"$Id$"
2 /* ----------------------------------------------------------------------- *
3 *
4 * Copyright 2000 H. Peter Anvin - All Rights Reserved
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.
12 * ----------------------------------------------------------------------- */
15 * msr.c
17 * x86 MSR access device
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.
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.
27 #include <linux/module.h>
28 #include <linux/config.h>
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>
38 #include <asm/processor.h>
39 #include <asm/msr.h>
40 #include <asm/uaccess.h>
41 #include <asm/system.h>
43 /* Note: "err" is handled in a funny way below. Otherwise one version
44 of gcc or another breaks. */
46 extern inlineintwrmsr_eio(u32 reg, u32 eax, u32 edx)
48 int err;
50 asmvolatile(
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));
64 return err;
67 extern inlineintrdmsr_eio(u32 reg, u32 *eax, u32 *edx)
69 int err;
71 asmvolatile(
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));
85 return err;
88 #ifdef CONFIG_SMP
90 struct msr_command {
91 int cpu;
92 int err;
93 u32 reg;
94 u32 data[2];
97 static voidmsr_smp_wrmsr(void*cmd_block)
99 struct msr_command *cmd = (struct msr_command *) cmd_block;
101 if( cmd->cpu ==smp_processor_id() )
102 cmd->err =wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]);
105 static voidmsr_smp_rdmsr(void*cmd_block)
107 struct msr_command *cmd = (struct msr_command *) cmd_block;
109 if( cmd->cpu ==smp_processor_id() )
110 cmd->err =rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]);
113 extern inlineintdo_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
115 struct msr_command cmd;
117 if( cpu ==smp_processor_id() ) {
118 returnwrmsr_eio(reg, eax, edx);
119 }else{
120 cmd.cpu = cpu;
121 cmd.reg = reg;
122 cmd.data[0] = eax;
123 cmd.data[1] = edx;
125 smp_call_function(msr_smp_wrmsr, &cmd,1,1);
126 return cmd.err;
130 extern inlineintdo_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
132 struct msr_command cmd;
134 if( cpu ==smp_processor_id() ) {
135 returnrdmsr_eio(reg, eax, edx);
136 }else{
137 cmd.cpu = cpu;
138 cmd.reg = reg;
140 smp_call_function(msr_smp_rdmsr, &cmd,1,1);
142 *eax = cmd.data[0];
143 *edx = cmd.data[1];
145 return cmd.err;
149 #else/* ! CONFIG_SMP */
151 extern inlineintdo_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
153 returnwrmsr_eio(reg, eax, edx);
156 extern inlineintdo_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
158 returnrdmsr_eio(reg, eax, edx);
161 #endif/* ! CONFIG_SMP */
163 static loff_t msr_seek(struct file *file, loff_t offset,int orig)
165 switch(orig) {
166 case0:
167 file->f_pos = offset;
168 return file->f_pos;
169 case1:
170 file->f_pos += offset;
171 return file->f_pos;
172 default:
173 return-EINVAL;/* SEEK_END not supported */
177 static ssize_t msr_read(struct file * file,char* buf,
178 size_t count, loff_t *ppos)
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;
187 if( count %8)
188 return-EINVAL;/* Invalid chunk size */
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;
199 return((char*)tmp) - buf;
202 static ssize_t msr_write(struct file * file,const char* buf,
203 size_t count, loff_t *ppos)
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;
212 if( count %8)
213 return-EINVAL;/* Invalid chunk size */
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;
224 return((char*)tmp) - buf;
227 static intmsr_open(struct inode *inode,struct file *file)
229 int cpu =MINOR(file->f_dentry->d_inode->i_rdev);
230 struct cpuinfo_x86 *c = &(cpu_data)[cpu];
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 */
237 return0;
241 * File operations we support
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,
251 int __init msr_init(void)
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;
259 return0;
262 void __exit msr_exit(void)
264 unregister_chrdev(MSR_MAJOR,"cpu/msr");
267 module_init(msr_init);
268 module_exit(msr_exit)
270 EXPORT_NO_SYMBOLS;
272 MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
273 MODULE_DESCRIPTION("x86 generic MSR driver");
close