Blame view
arch/x86/kernel/cpuid.c
5.4 KB
1da177e4c Linux-2.6.12-rc2 |
1 |
/* ----------------------------------------------------------------------- * |
2347d933b x86: cpuid: allow... |
2 3 |
* * Copyright 2000-2008 H. Peter Anvin - All Rights Reserved |
1da177e4c 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 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 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 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 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 Linux-2.6.12-rc2 |
38 39 40 |
#include <linux/device.h> #include <linux/cpu.h> #include <linux/notifier.h> |
f634fa941 x86: cpuid.c fix ... |
41 |
#include <linux/uaccess.h> |
5a0e3ad6a include cleanup: ... |
42 |
#include <linux/gfp.h> |
1da177e4c Linux-2.6.12-rc2 |
43 44 45 |
#include <asm/processor.h> #include <asm/msr.h> |
1da177e4c Linux-2.6.12-rc2 |
46 |
#include <asm/system.h> |
8874b414f [PATCH] class: co... |
47 |
static struct class *cpuid_class; |
1da177e4c Linux-2.6.12-rc2 |
48 |
|
2347d933b x86: cpuid: allow... |
49 50 |
struct cpuid_regs { u32 eax, ebx, ecx, edx; |
1da177e4c Linux-2.6.12-rc2 |
51 52 53 54 |
}; static void cpuid_smp_cpuid(void *cmd_block) { |
2347d933b x86: cpuid: allow... |
55 |
struct cpuid_regs *cmd = (struct cpuid_regs *)cmd_block; |
1da177e4c Linux-2.6.12-rc2 |
56 |
|
2347d933b x86: cpuid: allow... |
57 58 |
cpuid_count(cmd->eax, cmd->ecx, &cmd->eax, &cmd->ebx, &cmd->ecx, &cmd->edx); |
1da177e4c Linux-2.6.12-rc2 |
59 |
} |
1da177e4c 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 x86: cpuid, msr: ... |
63 |
struct inode *inode = file->f_mapping->host; |
1da177e4c Linux-2.6.12-rc2 |
64 |
|
2b06ac867 x86: cpuid, msr: ... |
65 |
mutex_lock(&inode->i_mutex); |
1da177e4c 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 x86: cpuid, msr: ... |
78 |
mutex_unlock(&inode->i_mutex); |
1da177e4c Linux-2.6.12-rc2 |
79 80 81 82 |
return ret; } static ssize_t cpuid_read(struct file *file, char __user *buf, |
f634fa941 x86: cpuid.c fix ... |
83 |
size_t count, loff_t *ppos) |
1da177e4c Linux-2.6.12-rc2 |
84 85 |
{ char __user *tmp = buf; |
2347d933b x86: cpuid: allow... |
86 |
struct cpuid_regs cmd; |
aab4c5a51 [PATCH] i386: cha... |
87 |
int cpu = iminor(file->f_path.dentry->d_inode); |
2347d933b x86: cpuid: allow... |
88 |
u64 pos = *ppos; |
9ea2b82ed x86: cpuid: corre... |
89 90 |
ssize_t bytes = 0; int err = 0; |
1da177e4c Linux-2.6.12-rc2 |
91 92 93 |
if (count % 16) return -EINVAL; /* Invalid chunk size */ |
6b7f430ee [PATCH] arch/i386... |
94 |
for (; count; count -= 16) { |
2347d933b x86: cpuid: allow... |
95 96 |
cmd.eax = pos; cmd.ecx = pos >> 32; |
4b46ca701 x86: cpuid: propa... |
97 98 |
err = smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1); if (err) |
9ea2b82ed x86: cpuid: corre... |
99 100 101 102 103 |
break; if (copy_to_user(tmp, &cmd, 16)) { err = -EFAULT; break; } |
1da177e4c Linux-2.6.12-rc2 |
104 |
tmp += 16; |
9ea2b82ed x86: cpuid: corre... |
105 |
bytes += 16; |
2347d933b x86: cpuid: allow... |
106 |
*ppos = ++pos; |
1da177e4c Linux-2.6.12-rc2 |
107 |
} |
9ea2b82ed x86: cpuid: corre... |
108 |
return bytes ? bytes : err; |
1da177e4c Linux-2.6.12-rc2 |
109 110 111 112 |
} static int cpuid_open(struct inode *inode, struct file *file) { |
5119e92ef x86: cdev lock_ke... |
113 114 |
unsigned int cpu; struct cpuinfo_x86 *c; |
5119e92ef x86: cdev lock_ke... |
115 116 |
cpu = iminor(file->f_path.dentry->d_inode); |
5a943617e x86, cpuid: Simpl... |
117 118 |
if (cpu >= nr_cpu_ids || !cpu_online(cpu)) return -ENXIO; /* No such CPU */ |
5119e92ef x86: cdev lock_ke... |
119 |
c = &cpu_data(cpu); |
1da177e4c Linux-2.6.12-rc2 |
120 |
if (c->cpuid_level < 0) |
5a943617e x86, cpuid: Simpl... |
121 122 123 |
return -EIO; /* CPUID not supported */ return 0; |
1da177e4c Linux-2.6.12-rc2 |
124 125 126 127 128 |
} /* * File operations we support */ |
5dfe4c964 [PATCH] mark stru... |
129 |
static const struct file_operations cpuid_fops = { |
1da177e4c Linux-2.6.12-rc2 |
130 131 132 133 134 |
.owner = THIS_MODULE, .llseek = cpuid_seek, .read = cpuid_read, .open = cpuid_open, }; |
1f503e774 i386: do cpuid_de... |
135 |
static __cpuinit int cpuid_device_create(int cpu) |
1da177e4c Linux-2.6.12-rc2 |
136 |
{ |
07accdc18 Driver core: conv... |
137 |
struct device *dev; |
1da177e4c Linux-2.6.12-rc2 |
138 |
|
a9b12619f device create: mi... |
139 140 |
dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL, "cpu%d", cpu); |
1f503e774 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 Linux-2.6.12-rc2 |
147 |
} |
1e32b073f i386: misc cpuini... |
148 149 150 |
static int __cpuinit cpuid_class_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) |
1da177e4c Linux-2.6.12-rc2 |
151 152 |
{ unsigned int cpu = (unsigned long)hcpu; |
1f503e774 i386: do cpuid_de... |
153 |
int err = 0; |
1da177e4c Linux-2.6.12-rc2 |
154 155 |
switch (action) { |
1f503e774 i386: do cpuid_de... |
156 |
case CPU_UP_PREPARE: |
1f503e774 i386: do cpuid_de... |
157 |
err = cpuid_device_create(cpu); |
1da177e4c Linux-2.6.12-rc2 |
158 |
break; |
1f503e774 i386: do cpuid_de... |
159 |
case CPU_UP_CANCELED: |
b844eba29 PM: Remove destro... |
160 |
case CPU_UP_CANCELED_FROZEN: |
1da177e4c Linux-2.6.12-rc2 |
161 |
case CPU_DEAD: |
1f503e774 i386: do cpuid_de... |
162 |
cpuid_device_destroy(cpu); |
1da177e4c Linux-2.6.12-rc2 |
163 164 |
break; } |
a94247e7f x86: convert cpu ... |
165 |
return notifier_from_errno(err); |
1da177e4c Linux-2.6.12-rc2 |
166 |
} |
c72258c7c x86: fix section ... |
167 |
static struct notifier_block __refdata cpuid_class_cpu_notifier = |
1da177e4c Linux-2.6.12-rc2 |
168 169 170 |
{ .notifier_call = cpuid_class_cpu_callback, }; |
2c9ede55e switch device_get... |
171 |
static char *cpuid_devnode(struct device *dev, umode_t *mode) |
07e9bb8ee Driver Core: x86:... |
172 173 174 |
{ return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt)); } |
1da177e4c Linux-2.6.12-rc2 |
175 176 177 178 |
static int __init cpuid_init(void) { int i, err = 0; i = 0; |
0b962d473 x86, msr/cpuid: R... |
179 180 |
if (__register_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid", &cpuid_fops)) { |
1da177e4c 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 [PATCH] class: co... |
187 |
cpuid_class = class_create(THIS_MODULE, "cpuid"); |
1da177e4c Linux-2.6.12-rc2 |
188 189 190 191 |
if (IS_ERR(cpuid_class)) { err = PTR_ERR(cpuid_class); goto out_chrdev; } |
e454cea20 Driver-Core: exte... |
192 |
cpuid_class->devnode = cpuid_devnode; |
1da177e4c Linux-2.6.12-rc2 |
193 |
for_each_online_cpu(i) { |
07accdc18 Driver core: conv... |
194 |
err = cpuid_device_create(i); |
2347d933b x86: cpuid: allow... |
195 |
if (err != 0) |
1da177e4c Linux-2.6.12-rc2 |
196 197 |
goto out_class; } |
b6a7c79a5 [PATCH] Fix modul... |
198 |
register_hotcpu_notifier(&cpuid_class_cpu_notifier); |
1da177e4c 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 i386: do cpuid_de... |
206 |
cpuid_device_destroy(i); |
1da177e4c Linux-2.6.12-rc2 |
207 |
} |
8874b414f [PATCH] class: co... |
208 |
class_destroy(cpuid_class); |
1da177e4c Linux-2.6.12-rc2 |
209 |
out_chrdev: |
0b962d473 x86, msr/cpuid: R... |
210 |
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid"); |
1da177e4c 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 i386: do cpuid_de... |
220 |
cpuid_device_destroy(cpu); |
8874b414f [PATCH] class: co... |
221 |
class_destroy(cpuid_class); |
da482474b x86, msr/cpuid: P... |
222 |
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid"); |
b6a7c79a5 [PATCH] Fix modul... |
223 |
unregister_hotcpu_notifier(&cpuid_class_cpu_notifier); |
1da177e4c 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"); |