2 /* ----------------------------------------------------------------------- *
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 * ----------------------------------------------------------------------- */
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>
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 inline int wrmsr_eio(u32 reg
, u32 eax
, u32 edx
)
53 ".section .fixup,\"ax\"\n"
57 ".section __ex_table,\"a\"\n"
62 : "a" (eax
), "d" (edx
), "c" (reg
), "i" (-EIO
), "0" (0));
67 extern inline int rdmsr_eio(u32 reg
, u32
*eax
, u32
*edx
)
74 ".section .fixup,\"ax\"\n"
78 ".section __ex_table,\"a\"\n"
82 : "=&bDS" (err
), "=a" (*eax
), "=d" (*edx
)
83 : "c" (reg
), "i" (-EIO
), "0" (0));
97 static void msr_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 void msr_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 inline int do_wrmsr(int cpu
, u32 reg
, u32 eax
, u32 edx
)
115 struct msr_command cmd
;
117 if ( cpu
== smp_processor_id() ) {
118 return wrmsr_eio(reg
, eax
, edx
);
125 smp_call_function(msr_smp_wrmsr
, &cmd
, 1, 1);
130 extern inline int do_rdmsr(int cpu
, u32 reg
, u32
*eax
, u32
*edx
)
132 struct msr_command cmd
;
134 if ( cpu
== smp_processor_id() ) {
135 return rdmsr_eio(reg
, eax
, edx
);
140 smp_call_function(msr_smp_rdmsr
, &cmd
, 1, 1);
149 #else /* ! CONFIG_SMP */
151 extern inline int do_wrmsr(int cpu
, u32 reg
, u32 eax
, u32 edx
)
153 return wrmsr_eio(reg
, eax
, edx
);
156 extern inline int do_rdmsr(int cpu
, u32 reg
, u32
*eax
, u32
*edx
)
158 return rdmsr_eio(reg
, eax
, edx
);
161 #endif /* ! CONFIG_SMP */
163 static loff_t
msr_seek(struct file
*file
, loff_t offset
, int orig
)
167 file
->f_pos
= offset
;
170 file
->f_pos
+= offset
;
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
;
184 int cpu
= MINOR(file
->f_dentry
->d_inode
->i_rdev
);
188 return -EINVAL
; /* Invalid chunk size */
190 for ( rv
= 0 ; count
; count
-= 8 ) {
191 err
= do_rdmsr(cpu
, reg
, &data
[0], &data
[1]);
194 if ( copy_to_user(tmp
,&data
,8) )
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
;
209 int cpu
= MINOR(file
->f_dentry
->d_inode
->i_rdev
);
213 return -EINVAL
; /* Invalid chunk size */
215 for ( rv
= 0 ; count
; count
-= 8 ) {
216 if ( copy_from_user(&data
,tmp
,8) )
218 err
= do_wrmsr(cpu
, reg
, data
[0], data
[1]);
224 return ((char *)tmp
) - buf
;
227 static int msr_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 */
241 * File operations we support
243 static struct file_operations msr_fops
= {
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",
262 void __exit
msr_exit(void)
266 module_init(msr_init
);
267 module_exit(msr_exit
)
271 MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
272 MODULE_DESCRIPTION("x86 generic MSR driver");