diff options
author | H. Peter Anvin <hpa@zytor.com> | 2008-02-04 10:47:59 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-02-04 10:47:59 -0500 |
commit | 2347d933b158932cf2b8aeebae3e5cc16b200bd1 (patch) | |
tree | 09eeeb7da4a431f07bc7b4afb146279ff6b78d9d /arch/x86/kernel | |
parent | 71713eeed0c90bb05c509388609223555575f558 (diff) |
x86: cpuid: allow querying %ecx-sensitive CPUID levels
After /dev/*/cpuid was introduced, Intel changed the semantics of the
CPUID instruction to be sentitive to %ecx as well as %eax. This patch
allows querying of %ecx-sensitive levels by placing the %ecx value in
the upper 32 bits of the file position (lower 32 bits always were used
for the %eax value.)
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r-- | arch/x86/kernel/cpuid.c | 45 |
1 files changed, 20 insertions, 25 deletions
diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index a63432d800f9..c10ebc4b8e4b 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* ----------------------------------------------------------------------- * | 1 | /* ----------------------------------------------------------------------- * |
2 | * | 2 | * |
3 | * Copyright 2000 H. Peter Anvin - All Rights Reserved | 3 | * Copyright 2000-2008 H. Peter Anvin - All Rights Reserved |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by |
@@ -17,6 +17,10 @@ | |||
17 | * and then read in chunks of 16 bytes. A larger size means multiple | 17 | * and then read in chunks of 16 bytes. A larger size means multiple |
18 | * reads of consecutive levels. | 18 | * reads of consecutive levels. |
19 | * | 19 | * |
20 | * The lower 32 bits of the file position is used as the incoming %eax, | ||
21 | * and the upper 32 bits of the file position as the incoming %ecx, | ||
22 | * the latter intended for "counting" eax levels like eax=4. | ||
23 | * | ||
20 | * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on | 24 | * This driver uses /dev/cpu/%d/cpuid where %d is the minor number, and on |
21 | * an SMP box will direct the access to CPU %d. | 25 | * an SMP box will direct the access to CPU %d. |
22 | */ | 26 | */ |
@@ -43,27 +47,16 @@ | |||
43 | 47 | ||
44 | static struct class *cpuid_class; | 48 | static struct class *cpuid_class; |
45 | 49 | ||
46 | struct cpuid_command { | 50 | struct cpuid_regs { |
47 | u32 reg; | 51 | u32 eax, ebx, ecx, edx; |
48 | u32 *data; | ||
49 | }; | 52 | }; |
50 | 53 | ||
51 | static void cpuid_smp_cpuid(void *cmd_block) | 54 | static void cpuid_smp_cpuid(void *cmd_block) |
52 | { | 55 | { |
53 | struct cpuid_command *cmd = cmd_block; | 56 | struct cpuid_regs *cmd = (struct cpuid_regs *)cmd_block; |
54 | |||
55 | cpuid(cmd->reg, &cmd->data[0], &cmd->data[1], &cmd->data[2], | ||
56 | &cmd->data[3]); | ||
57 | } | ||
58 | |||
59 | static inline void do_cpuid(int cpu, u32 reg, u32 * data) | ||
60 | { | ||
61 | struct cpuid_command cmd; | ||
62 | 57 | ||
63 | cmd.reg = reg; | 58 | cpuid_count(cmd->eax, cmd->ecx, |
64 | cmd.data = data; | 59 | &cmd->eax, &cmd->ebx, &cmd->ecx, &cmd->edx); |
65 | |||
66 | smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1, 1); | ||
67 | } | 60 | } |
68 | 61 | ||
69 | static loff_t cpuid_seek(struct file *file, loff_t offset, int orig) | 62 | static loff_t cpuid_seek(struct file *file, loff_t offset, int orig) |
@@ -93,19 +86,21 @@ static ssize_t cpuid_read(struct file *file, char __user *buf, | |||
93 | size_t count, loff_t * ppos) | 86 | size_t count, loff_t * ppos) |
94 | { | 87 | { |
95 | char __user *tmp = buf; | 88 | char __user *tmp = buf; |
96 | u32 data[4]; | 89 | struct cpuid_regs cmd; |
97 | u32 reg = *ppos; | ||
98 | int cpu = iminor(file->f_path.dentry->d_inode); | 90 | int cpu = iminor(file->f_path.dentry->d_inode); |
91 | u64 pos = *ppos; | ||
99 | 92 | ||
100 | if (count % 16) | 93 | if (count % 16) |
101 | return -EINVAL; /* Invalid chunk size */ | 94 | return -EINVAL; /* Invalid chunk size */ |
102 | 95 | ||
103 | for (; count; count -= 16) { | 96 | for (; count; count -= 16) { |
104 | do_cpuid(cpu, reg, data); | 97 | cmd.eax = pos; |
105 | if (copy_to_user(tmp, &data, 16)) | 98 | cmd.ecx = pos >> 32; |
99 | smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1, 1); | ||
100 | if (copy_to_user(tmp, &cmd, 16)) | ||
106 | return -EFAULT; | 101 | return -EFAULT; |
107 | tmp += 16; | 102 | tmp += 16; |
108 | *ppos = reg++; | 103 | *ppos = ++pos; |
109 | } | 104 | } |
110 | 105 | ||
111 | return tmp - buf; | 106 | return tmp - buf; |
@@ -193,7 +188,7 @@ static int __init cpuid_init(void) | |||
193 | } | 188 | } |
194 | for_each_online_cpu(i) { | 189 | for_each_online_cpu(i) { |
195 | err = cpuid_device_create(i); | 190 | err = cpuid_device_create(i); |
196 | if (err != 0) | 191 | if (err != 0) |
197 | goto out_class; | 192 | goto out_class; |
198 | } | 193 | } |
199 | register_hotcpu_notifier(&cpuid_class_cpu_notifier); | 194 | register_hotcpu_notifier(&cpuid_class_cpu_notifier); |
@@ -208,7 +203,7 @@ out_class: | |||
208 | } | 203 | } |
209 | class_destroy(cpuid_class); | 204 | class_destroy(cpuid_class); |
210 | out_chrdev: | 205 | out_chrdev: |
211 | unregister_chrdev(CPUID_MAJOR, "cpu/cpuid"); | 206 | unregister_chrdev(CPUID_MAJOR, "cpu/cpuid"); |
212 | out: | 207 | out: |
213 | return err; | 208 | return err; |
214 | } | 209 | } |