diff options
author | Andi Kleen <ak@suse.de> | 2006-06-26 07:56:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 13:48:14 -0400 |
commit | 240cd6a80642da528bfa382ec2ae4e3cb8991ea7 (patch) | |
tree | 6c24052ea167a2fd7e0be93c167e9e7da5bddd2b /arch/i386/kernel/cpu/intel_cacheinfo.c | |
parent | faee9a5dc9d8399cc3b1b8e18b6d7ff7b17f1af1 (diff) |
[PATCH] i386/x86-64: Emulate CPUID4 on AMD
Intel systems report the cache level data from CPUID 4 in sysfs.
Add a CPUID 4 emulation for AMD CPUs to report the same
information for them. This allows programs to read this
information in a uniform way.
The AMD way to report this is less flexible so some assumptions
are hardcoded (e.g. no L3)
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel/cpu/intel_cacheinfo.c')
-rw-r--r-- | arch/i386/kernel/cpu/intel_cacheinfo.c | 113 |
1 files changed, 100 insertions, 13 deletions
diff --git a/arch/i386/kernel/cpu/intel_cacheinfo.c b/arch/i386/kernel/cpu/intel_cacheinfo.c index c8547a6fa7e6..6c37b4fd8ce2 100644 --- a/arch/i386/kernel/cpu/intel_cacheinfo.c +++ b/arch/i386/kernel/cpu/intel_cacheinfo.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * Changes: | 4 | * Changes: |
5 | * Venkatesh Pallipadi : Adding cache identification through cpuid(4) | 5 | * Venkatesh Pallipadi : Adding cache identification through cpuid(4) |
6 | * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure. | 6 | * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure. |
7 | * Andi Kleen : CPUID4 emulation on AMD. | ||
7 | */ | 8 | */ |
8 | 9 | ||
9 | #include <linux/init.h> | 10 | #include <linux/init.h> |
@@ -130,25 +131,111 @@ struct _cpuid4_info { | |||
130 | cpumask_t shared_cpu_map; | 131 | cpumask_t shared_cpu_map; |
131 | }; | 132 | }; |
132 | 133 | ||
133 | static unsigned short num_cache_leaves; | 134 | unsigned short num_cache_leaves; |
135 | |||
136 | /* AMD doesn't have CPUID4. Emulate it here to report the same | ||
137 | information to the user. This makes some assumptions about the machine: | ||
138 | No L3, L2 not shared, no SMT etc. that is currently true on AMD CPUs. | ||
139 | |||
140 | In theory the TLBs could be reported as fake type (they are in "dummy"). | ||
141 | Maybe later */ | ||
142 | union l1_cache { | ||
143 | struct { | ||
144 | unsigned line_size : 8; | ||
145 | unsigned lines_per_tag : 8; | ||
146 | unsigned assoc : 8; | ||
147 | unsigned size_in_kb : 8; | ||
148 | }; | ||
149 | unsigned val; | ||
150 | }; | ||
151 | |||
152 | union l2_cache { | ||
153 | struct { | ||
154 | unsigned line_size : 8; | ||
155 | unsigned lines_per_tag : 4; | ||
156 | unsigned assoc : 4; | ||
157 | unsigned size_in_kb : 16; | ||
158 | }; | ||
159 | unsigned val; | ||
160 | }; | ||
161 | |||
162 | static unsigned short assocs[] = { | ||
163 | [1] = 1, [2] = 2, [4] = 4, [6] = 8, | ||
164 | [8] = 16, | ||
165 | [0xf] = 0xffff // ?? | ||
166 | }; | ||
167 | static unsigned char levels[] = { 1, 1, 2 }; | ||
168 | static unsigned char types[] = { 1, 2, 3 }; | ||
169 | |||
170 | static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, | ||
171 | union _cpuid4_leaf_ebx *ebx, | ||
172 | union _cpuid4_leaf_ecx *ecx) | ||
173 | { | ||
174 | unsigned dummy; | ||
175 | unsigned line_size, lines_per_tag, assoc, size_in_kb; | ||
176 | union l1_cache l1i, l1d; | ||
177 | union l2_cache l2; | ||
178 | |||
179 | eax->full = 0; | ||
180 | ebx->full = 0; | ||
181 | ecx->full = 0; | ||
182 | |||
183 | cpuid(0x80000005, &dummy, &dummy, &l1d.val, &l1i.val); | ||
184 | cpuid(0x80000006, &dummy, &dummy, &l2.val, &dummy); | ||
185 | |||
186 | if (leaf > 2 || !l1d.val || !l1i.val || !l2.val) | ||
187 | return; | ||
188 | |||
189 | eax->split.is_self_initializing = 1; | ||
190 | eax->split.type = types[leaf]; | ||
191 | eax->split.level = levels[leaf]; | ||
192 | eax->split.num_threads_sharing = 0; | ||
193 | eax->split.num_cores_on_die = current_cpu_data.x86_max_cores - 1; | ||
194 | |||
195 | if (leaf <= 1) { | ||
196 | union l1_cache *l1 = leaf == 0 ? &l1d : &l1i; | ||
197 | assoc = l1->assoc; | ||
198 | line_size = l1->line_size; | ||
199 | lines_per_tag = l1->lines_per_tag; | ||
200 | size_in_kb = l1->size_in_kb; | ||
201 | } else { | ||
202 | assoc = l2.assoc; | ||
203 | line_size = l2.line_size; | ||
204 | lines_per_tag = l2.lines_per_tag; | ||
205 | /* cpu_data has errata corrections for K7 applied */ | ||
206 | size_in_kb = current_cpu_data.x86_cache_size; | ||
207 | } | ||
208 | |||
209 | if (assoc == 0xf) | ||
210 | eax->split.is_fully_associative = 1; | ||
211 | ebx->split.coherency_line_size = line_size - 1; | ||
212 | ebx->split.ways_of_associativity = assocs[assoc] - 1; | ||
213 | ebx->split.physical_line_partition = lines_per_tag - 1; | ||
214 | ecx->split.number_of_sets = (size_in_kb * 1024) / line_size / | ||
215 | (ebx->split.ways_of_associativity + 1) - 1; | ||
216 | } | ||
134 | 217 | ||
135 | static int __cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf) | 218 | static int __cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf) |
136 | { | 219 | { |
137 | unsigned int eax, ebx, ecx, edx; | 220 | union _cpuid4_leaf_eax eax; |
138 | union _cpuid4_leaf_eax cache_eax; | 221 | union _cpuid4_leaf_ebx ebx; |
222 | union _cpuid4_leaf_ecx ecx; | ||
223 | unsigned edx; | ||
139 | 224 | ||
140 | cpuid_count(4, index, &eax, &ebx, &ecx, &edx); | 225 | if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) |
141 | cache_eax.full = eax; | 226 | amd_cpuid4(index, &eax, &ebx, &ecx); |
142 | if (cache_eax.split.type == CACHE_TYPE_NULL) | 227 | else |
228 | cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx); | ||
229 | if (eax.split.type == CACHE_TYPE_NULL) | ||
143 | return -EIO; /* better error ? */ | 230 | return -EIO; /* better error ? */ |
144 | 231 | ||
145 | this_leaf->eax.full = eax; | 232 | this_leaf->eax = eax; |
146 | this_leaf->ebx.full = ebx; | 233 | this_leaf->ebx = ebx; |
147 | this_leaf->ecx.full = ecx; | 234 | this_leaf->ecx = ecx; |
148 | this_leaf->size = (this_leaf->ecx.split.number_of_sets + 1) * | 235 | this_leaf->size = (ecx.split.number_of_sets + 1) * |
149 | (this_leaf->ebx.split.coherency_line_size + 1) * | 236 | (ebx.split.coherency_line_size + 1) * |
150 | (this_leaf->ebx.split.physical_line_partition + 1) * | 237 | (ebx.split.physical_line_partition + 1) * |
151 | (this_leaf->ebx.split.ways_of_associativity + 1); | 238 | (ebx.split.ways_of_associativity + 1); |
152 | return 0; | 239 | return 0; |
153 | } | 240 | } |
154 | 241 | ||