Blame view

arch/x86/kernel/cpuid.c 5.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /* ----------------------------------------------------------------------- *
2347d933b   H. Peter Anvin   x86: cpuid: allow...
2
3
   *
   *   Copyright 2000-2008 H. Peter Anvin - All Rights Reserved
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
   *   USA; either version 2 of the License, or (at your option) any later
   *   version; incorporated herein by reference.
   *
   * ----------------------------------------------------------------------- */
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
18
19
   * x86 CPUID access device
   *
   * This device is accessed by lseek() to the appropriate CPUID level
   * and then read in chunks of 16 bytes.  A larger size means multiple
   * reads of consecutive levels.
   *
2347d933b   H. Peter Anvin   x86: cpuid: allow...
20
21
22
23
   * The lower 32 bits of the file position is used as the incoming %eax,
   * and the upper 32 bits of the file position as the incoming %ecx,
   * the latter intended for "counting" eax levels like eax=4.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
   * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on
   * an SMP box will direct the access to CPU %d.
   */
  
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
36
37
  
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/fcntl.h>
  #include <linux/init.h>
  #include <linux/poll.h>
  #include <linux/smp.h>
  #include <linux/major.h>
  #include <linux/fs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
  #include <linux/device.h>
  #include <linux/cpu.h>
  #include <linux/notifier.h>
f634fa941   Jaswinder Singh Rajput   x86: cpuid.c fix ...
41
  #include <linux/uaccess.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
42
  #include <linux/gfp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
  
  #include <asm/processor.h>
  #include <asm/msr.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
  #include <asm/system.h>
8874b414f   Greg Kroah-Hartman   [PATCH] class: co...
47
  static struct class *cpuid_class;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48

2347d933b   H. Peter Anvin   x86: cpuid: allow...
49
50
  struct cpuid_regs {
  	u32 eax, ebx, ecx, edx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
  };
  
  static void cpuid_smp_cpuid(void *cmd_block)
  {
2347d933b   H. Peter Anvin   x86: cpuid: allow...
55
  	struct cpuid_regs *cmd = (struct cpuid_regs *)cmd_block;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56

2347d933b   H. Peter Anvin   x86: cpuid: allow...
57
58
  	cpuid_count(cmd->eax, cmd->ecx,
  		    &cmd->eax, &cmd->ebx, &cmd->ecx, &cmd->edx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
  static loff_t cpuid_seek(struct file *file, loff_t offset, int orig)
  {
  	loff_t ret;
2b06ac867   H. Peter Anvin   x86: cpuid, msr: ...
63
  	struct inode *inode = file->f_mapping->host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64

2b06ac867   H. Peter Anvin   x86: cpuid, msr: ...
65
  	mutex_lock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
68
69
70
71
72
73
74
75
76
77
  	switch (orig) {
  	case 0:
  		file->f_pos = offset;
  		ret = file->f_pos;
  		break;
  	case 1:
  		file->f_pos += offset;
  		ret = file->f_pos;
  		break;
  	default:
  		ret = -EINVAL;
  	}
2b06ac867   H. Peter Anvin   x86: cpuid, msr: ...
78
  	mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
81
82
  	return ret;
  }
  
  static ssize_t cpuid_read(struct file *file, char __user *buf,
f634fa941   Jaswinder Singh Rajput   x86: cpuid.c fix ...
83
  			  size_t count, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
  {
  	char __user *tmp = buf;
2347d933b   H. Peter Anvin   x86: cpuid: allow...
86
  	struct cpuid_regs cmd;
aab4c5a51   Josef "Jeff" Sipek   [PATCH] i386: cha...
87
  	int cpu = iminor(file->f_path.dentry->d_inode);
2347d933b   H. Peter Anvin   x86: cpuid: allow...
88
  	u64 pos = *ppos;
9ea2b82ed   H. Peter Anvin   x86: cpuid: corre...
89
90
  	ssize_t bytes = 0;
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
  
  	if (count % 16)
  		return -EINVAL;	/* Invalid chunk size */
6b7f430ee   Daniel Marjamaki   [PATCH] arch/i386...
94
  	for (; count; count -= 16) {
2347d933b   H. Peter Anvin   x86: cpuid: allow...
95
96
  		cmd.eax = pos;
  		cmd.ecx = pos >> 32;
4b46ca701   H. Peter Anvin   x86: cpuid: propa...
97
98
  		err = smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1);
  		if (err)
9ea2b82ed   H. Peter Anvin   x86: cpuid: corre...
99
100
101
102
103
  			break;
  		if (copy_to_user(tmp, &cmd, 16)) {
  			err = -EFAULT;
  			break;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
  		tmp += 16;
9ea2b82ed   H. Peter Anvin   x86: cpuid: corre...
105
  		bytes += 16;
2347d933b   H. Peter Anvin   x86: cpuid: allow...
106
  		*ppos = ++pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
  	}
9ea2b82ed   H. Peter Anvin   x86: cpuid: corre...
108
  	return bytes ? bytes : err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
  }
  
  static int cpuid_open(struct inode *inode, struct file *file)
  {
5119e92ef   Jonathan Corbet   x86: cdev lock_ke...
113
114
  	unsigned int cpu;
  	struct cpuinfo_x86 *c;
5119e92ef   Jonathan Corbet   x86: cdev lock_ke...
115
116
  
  	cpu = iminor(file->f_path.dentry->d_inode);
5a943617e   John Kacur   x86, cpuid: Simpl...
117
118
  	if (cpu >= nr_cpu_ids || !cpu_online(cpu))
  		return -ENXIO;	/* No such CPU */
5119e92ef   Jonathan Corbet   x86: cdev lock_ke...
119
  	c = &cpu_data(cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
  	if (c->cpuid_level < 0)
5a943617e   John Kacur   x86, cpuid: Simpl...
121
122
123
  		return -EIO;	/* CPUID not supported */
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
  }
  
  /*
   * File operations we support
   */
5dfe4c964   Arjan van de Ven   [PATCH] mark stru...
129
  static const struct file_operations cpuid_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
134
  	.owner = THIS_MODULE,
  	.llseek = cpuid_seek,
  	.read = cpuid_read,
  	.open = cpuid_open,
  };
1f503e774   Akinobu Mita   i386: do cpuid_de...
135
  static __cpuinit int cpuid_device_create(int cpu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  {
07accdc18   Greg Kroah-Hartman   Driver core: conv...
137
  	struct device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138

a9b12619f   Greg Kroah-Hartman   device create: mi...
139
140
  	dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL,
  			    "cpu%d", cpu);
1f503e774   Akinobu Mita   i386: do cpuid_de...
141
142
143
144
145
146
  	return IS_ERR(dev) ? PTR_ERR(dev) : 0;
  }
  
  static void cpuid_device_destroy(int cpu)
  {
  	device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
  }
1e32b073f   Satyam Sharma   i386: misc cpuini...
148
149
150
  static int __cpuinit cpuid_class_cpu_callback(struct notifier_block *nfb,
  					      unsigned long action,
  					      void *hcpu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
  {
  	unsigned int cpu = (unsigned long)hcpu;
1f503e774   Akinobu Mita   i386: do cpuid_de...
153
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
  
  	switch (action) {
1f503e774   Akinobu Mita   i386: do cpuid_de...
156
  	case CPU_UP_PREPARE:
1f503e774   Akinobu Mita   i386: do cpuid_de...
157
  		err = cpuid_device_create(cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  		break;
1f503e774   Akinobu Mita   i386: do cpuid_de...
159
  	case CPU_UP_CANCELED:
b844eba29   Rafael J. Wysocki   PM: Remove destro...
160
  	case CPU_UP_CANCELED_FROZEN:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  	case CPU_DEAD:
1f503e774   Akinobu Mita   i386: do cpuid_de...
162
  		cpuid_device_destroy(cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
  		break;
  	}
a94247e7f   Akinobu Mita   x86: convert cpu ...
165
  	return notifier_from_errno(err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  }
c72258c7c   Sam Ravnborg   x86: fix section ...
167
  static struct notifier_block __refdata cpuid_class_cpu_notifier =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
  {
  	.notifier_call = cpuid_class_cpu_callback,
  };
2c9ede55e   Al Viro   switch device_get...
171
  static char *cpuid_devnode(struct device *dev, umode_t *mode)
07e9bb8ee   Kay Sievers   Driver Core: x86:...
172
173
174
  {
  	return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt));
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
  static int __init cpuid_init(void)
  {
  	int i, err = 0;
  	i = 0;
0b962d473   H. Peter Anvin   x86, msr/cpuid: R...
179
180
  	if (__register_chrdev(CPUID_MAJOR, 0, NR_CPUS,
  			      "cpu/cpuid", &cpuid_fops)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
184
185
186
  		printk(KERN_ERR "cpuid: unable to get major %d for cpuid
  ",
  		       CPUID_MAJOR);
  		err = -EBUSY;
  		goto out;
  	}
8874b414f   Greg Kroah-Hartman   [PATCH] class: co...
187
  	cpuid_class = class_create(THIS_MODULE, "cpuid");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
  	if (IS_ERR(cpuid_class)) {
  		err = PTR_ERR(cpuid_class);
  		goto out_chrdev;
  	}
e454cea20   Kay Sievers   Driver-Core: exte...
192
  	cpuid_class->devnode = cpuid_devnode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  	for_each_online_cpu(i) {
07accdc18   Greg Kroah-Hartman   Driver core: conv...
194
  		err = cpuid_device_create(i);
2347d933b   H. Peter Anvin   x86: cpuid: allow...
195
  		if (err != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
  			goto out_class;
  	}
b6a7c79a5   OGAWA Hirofumi   [PATCH] Fix modul...
198
  	register_hotcpu_notifier(&cpuid_class_cpu_notifier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
203
204
205
  
  	err = 0;
  	goto out;
  
  out_class:
  	i = 0;
  	for_each_online_cpu(i) {
1f503e774   Akinobu Mita   i386: do cpuid_de...
206
  		cpuid_device_destroy(i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  	}
8874b414f   Greg Kroah-Hartman   [PATCH] class: co...
208
  	class_destroy(cpuid_class);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  out_chrdev:
0b962d473   H. Peter Anvin   x86, msr/cpuid: R...
210
  	__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
219
  out:
  	return err;
  }
  
  static void __exit cpuid_exit(void)
  {
  	int cpu = 0;
  
  	for_each_online_cpu(cpu)
1f503e774   Akinobu Mita   i386: do cpuid_de...
220
  		cpuid_device_destroy(cpu);
8874b414f   Greg Kroah-Hartman   [PATCH] class: co...
221
  	class_destroy(cpuid_class);
da482474b   Russ Anderson   x86, msr/cpuid: P...
222
  	__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
b6a7c79a5   OGAWA Hirofumi   [PATCH] Fix modul...
223
  	unregister_hotcpu_notifier(&cpuid_class_cpu_notifier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
228
229
230
231
  }
  
  module_init(cpuid_init);
  module_exit(cpuid_exit);
  
  MODULE_AUTHOR("H. Peter Anvin <hpa@zytor.com>");
  MODULE_DESCRIPTION("x86 generic CPUID driver");
  MODULE_LICENSE("GPL");