linux-old/arch/i386/kernel/msr.c
<<
>>
Prefs
   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
  46static 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
  67static 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
  90struct msr_command {
  91  int cpu;
  92  int err;
  93  u32 reg;
  94  u32 data[2];
  95};
  96
  97static 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
 105static 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
 113static 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
 130static 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
 151static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
 152{
 153  return wrmsr_eio(reg, eax, edx);
 154}
 155
 156static 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
 163static 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
 177static 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
 202static 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
 227static 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 */
 243static 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
 251int __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
 262void __exit msr_exit(void)
 263{
 264  unregister_chrdev(MSR_MAJOR, "cpu/msr");
 265}
 266
 267module_init(msr_init);
 268module_exit(msr_exit)
 269
 270EXPORT_NO_SYMBOLS;
 271
 272MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
 273MODULE_DESCRIPTION("x86 generic MSR driver");
 274MODULE_LICENSE("GPL");
 275
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.