]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
d7e50217 | 6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
1c79356b | 7 | * |
d7e50217 A |
8 | * This file contains Original Code and/or Modifications of Original Code |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14 | * | |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
d7e50217 A |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
1c79356b A |
22 | * |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* | |
26 | * @OSF_COPYRIGHT@ | |
27 | */ | |
28 | ||
d7e50217 | 29 | #include "cpuid.h" |
1c79356b | 30 | |
d7e50217 | 31 | #define min(a,b) ((a) < (b) ? (a) : (b)) |
1c79356b A |
32 | |
33 | /* | |
d7e50217 A |
34 | * CPU identification routines. |
35 | * | |
36 | * Note that this code assumes a processor that supports the | |
37 | * 'cpuid' instruction. | |
1c79356b | 38 | */ |
1c79356b | 39 | |
d7e50217 | 40 | static unsigned int cpuid_maxcpuid; |
1c79356b | 41 | |
d7e50217 | 42 | static i386_cpu_info_t cpuid_cpu_info; |
1c79356b | 43 | |
d7e50217 | 44 | uint32_t cpuid_feature; /* XXX obsolescent for compat */ |
1c79356b A |
45 | |
46 | /* | |
d7e50217 A |
47 | * We only identify Intel CPUs here. Adding support |
48 | * for others would be straightforward. | |
1c79356b | 49 | */ |
d7e50217 A |
50 | static void set_cpu_intel(i386_cpu_info_t *); |
51 | static void set_cpu_unknown(i386_cpu_info_t *); | |
52 | ||
53 | struct { | |
54 | char *vendor; | |
55 | void (* func)(i386_cpu_info_t *); | |
56 | } cpu_vendors[] = { | |
57 | {CPUID_VID_INTEL, set_cpu_intel}, | |
58 | {0, set_cpu_unknown} | |
1c79356b A |
59 | }; |
60 | ||
d7e50217 A |
61 | void |
62 | cpuid_get_info(i386_cpu_info_t *info_p) | |
63 | { | |
64 | uint32_t cpuid_result[4]; | |
65 | int i; | |
66 | ||
67 | bzero((void *)info_p, sizeof(i386_cpu_info_t)); | |
68 | ||
69 | /* do cpuid 0 to get vendor */ | |
70 | do_cpuid(0, cpuid_result); | |
71 | cpuid_maxcpuid = cpuid_result[0]; | |
72 | bcopy((char *)&cpuid_result[1], &info_p->cpuid_vendor[0], 4); /* ugh */ | |
73 | bcopy((char *)&cpuid_result[2], &info_p->cpuid_vendor[8], 4); | |
74 | bcopy((char *)&cpuid_result[3], &info_p->cpuid_vendor[4], 4); | |
75 | info_p->cpuid_vendor[12] = 0; | |
76 | ||
77 | /* look up vendor */ | |
78 | for (i = 0; ; i++) { | |
79 | if ((cpu_vendors[i].vendor == 0) || | |
80 | (!strcmp(cpu_vendors[i].vendor, info_p->cpuid_vendor))) { | |
81 | cpu_vendors[i].func(info_p); | |
82 | break; | |
83 | } | |
84 | } | |
85 | } | |
86 | ||
1c79356b | 87 | /* |
d7e50217 | 88 | * A useful model name string takes some decoding. |
1c79356b | 89 | */ |
d7e50217 A |
90 | char * |
91 | cpuid_intel_get_model_name( | |
92 | uint8_t brand, | |
93 | uint8_t family, | |
94 | uint8_t model, | |
95 | uint32_t signature) | |
96 | { | |
97 | /* check for brand id */ | |
98 | switch(brand) { | |
99 | case 0: | |
100 | /* brand ID not supported; use alternate method. */ | |
101 | switch(family) { | |
102 | case CPUID_FAMILY_486: | |
103 | return "486"; | |
104 | case CPUID_FAMILY_P5: | |
105 | return "Pentium"; | |
106 | case CPUID_FAMILY_PPRO: | |
107 | switch(model) { | |
108 | case CPUID_MODEL_P6: | |
109 | return "Pentium Pro"; | |
110 | case CPUID_MODEL_PII: | |
111 | return "Pentium II"; | |
112 | case CPUID_MODEL_P65: | |
113 | case CPUID_MODEL_P66: | |
114 | return "Celeron"; | |
115 | case CPUID_MODEL_P67: | |
116 | case CPUID_MODEL_P68: | |
117 | case CPUID_MODEL_P6A: | |
118 | case CPUID_MODEL_P6B: | |
119 | return "Pentium III"; | |
120 | default: | |
121 | return "Unknown P6 Family"; | |
122 | } | |
123 | case CPUID_FAMILY_PENTIUM4: | |
124 | return "Pentium 4"; | |
125 | default: | |
126 | return "Unknown Family"; | |
127 | } | |
128 | case 0x01: | |
129 | return "Celeron"; | |
130 | case 0x02: | |
131 | case 0x04: | |
132 | return "Pentium III"; | |
133 | case 0x03: | |
134 | if (signature == 0x6B1) | |
135 | return "Celeron"; | |
136 | else | |
137 | return "Pentium III Xeon"; | |
138 | case 0x06: | |
139 | return "Mobile Pentium III"; | |
140 | case 0x07: | |
141 | return "Mobile Celeron"; | |
142 | case 0x08: | |
143 | if (signature >= 0xF20) | |
144 | return "Genuine Intel"; | |
145 | else | |
146 | return "Pentium 4"; | |
147 | case 0x09: | |
148 | return "Pentium 4"; | |
149 | case 0x0b: | |
150 | return "Xeon"; | |
151 | case 0x0e: | |
152 | case 0x0f: | |
153 | return "Mobile Pentium 4"; | |
154 | default: | |
155 | return "Unknown Pentium"; | |
156 | } | |
157 | } | |
1c79356b A |
158 | |
159 | /* | |
d7e50217 A |
160 | * Cache descriptor table. Each row has the form: |
161 | * (descriptor_value, cache, size, linesize, | |
162 | * description) | |
163 | * Note: the CACHE_DESC macro does not expand description text in the kernel. | |
1c79356b | 164 | */ |
d7e50217 A |
165 | static cpuid_cache_desc_t cpuid_cache_desc_tab[] = { |
166 | CACHE_DESC(CPUID_CACHE_ITLB_4K, Lnone, 0, 0, \ | |
167 | "Instruction TLB, 4K, pages 4-way set associative, 64 entries"), | |
168 | CACHE_DESC(CPUID_CACHE_ITLB_4M, Lnone, 0, 0, \ | |
169 | "Instruction TLB, 4M, pages 4-way set associative, 4 entries"), | |
170 | CACHE_DESC(CPUID_CACHE_DTLB_4K, Lnone, 0, 0, \ | |
171 | "Data TLB, 4K pages, 4-way set associative, 64 entries"), | |
172 | CACHE_DESC(CPUID_CACHE_DTLB_4M, Lnone, 0, 0, \ | |
173 | "Data TLB, 4M pages, 4-way set associative, 4 entries"), | |
174 | CACHE_DESC(CPUID_CACHE_ITLB_64, Lnone, 0, 0, \ | |
175 | "Instruction TLB, 4K and 2M or 4M pages, 64 entries"), | |
176 | CACHE_DESC(CPUID_CACHE_ITLB_128, Lnone, 0, 0, \ | |
177 | "Instruction TLB, 4K and 2M or 4M pages, 128 entries"), | |
178 | CACHE_DESC(CPUID_CACHE_ITLB_256, Lnone, 0, 0, \ | |
179 | "Instruction TLB, 4K and 2M or 4M pages, 256 entries"), | |
180 | CACHE_DESC(CPUID_CACHE_DTLB_64, Lnone, 0, 0, \ | |
181 | "Data TLB, 4K and 4M pages, 64 entries"), | |
182 | CACHE_DESC(CPUID_CACHE_DTLB_128, Lnone, 0, 0, \ | |
183 | "Data TLB, 4K and 4M pages, 128 entries"), | |
184 | CACHE_DESC(CPUID_CACHE_DTLB_256, Lnone, 0, 0, \ | |
185 | "Data TLB, 4K and 4M pages, 256 entries"), | |
186 | CACHE_DESC(CPUID_CACHE_ICACHE_8K, L1I, 8*1024, 32, \ | |
187 | "Instruction L1 cache, 8K, 4-way set associative, 32byte line size"), | |
188 | CACHE_DESC(CPUID_CACHE_DCACHE_8K, L1D, 8*1024, 32, \ | |
189 | "Data L1 cache, 8K, 2-way set associative, 32byte line size"), | |
190 | CACHE_DESC(CPUID_CACHE_ICACHE_16K, L1I, 16*1024, 32, \ | |
191 | "Instruction L1 cache, 16K, 4-way set associative, 32byte line size"), | |
192 | CACHE_DESC(CPUID_CACHE_DCACHE_16K, L1D, 16*1024, 32, \ | |
193 | "Data L1 cache, 16K, 4-way set associative, 32byte line size"), | |
194 | CACHE_DESC(CPUID_CACHE_DCACHE_8K_64, L1D, 8*1024, 64, \ | |
195 | "Data L1 cache, 8K, 4-way set associative, 64byte line size"), | |
196 | CACHE_DESC(CPUID_CACHE_DCACHE_16K_64, L1D, 16*1024, 64, \ | |
197 | "Data L1 cache, 16K, 4-way set associative, 64byte line size"), | |
198 | CACHE_DESC(CPUID_CACHE_DCACHE_32K_64, L1D, 32*1024, 64, \ | |
199 | "Data L1 cache, 32K, 4-way set associative, 64byte line size"), | |
200 | CACHE_DESC(CPUID_CACHE_TRACE_12K, L1I, 12*1024, 64, \ | |
201 | "Trace cache, 12K-uop, 8-way set associative"), | |
202 | CACHE_DESC(CPUID_CACHE_TRACE_12K, L1I, 16*1024, 64, \ | |
203 | "Trace cache, 16K-uop, 8-way set associative"), | |
204 | CACHE_DESC(CPUID_CACHE_TRACE_12K, L1I, 32*1024, 64, \ | |
205 | "Trace cache, 32K-uop, 8-way set associative"), | |
206 | CACHE_DESC(CPUID_CACHE_UCACHE_128K, L2U, 128*1024, 32, \ | |
207 | "Unified L2 cache, 128K, 4-way set associative, 32byte line size"), | |
208 | CACHE_DESC(CPUID_CACHE_UCACHE_256K, L2U, 128*1024, 32, \ | |
209 | "Unified L2 cache, 256K, 4-way set associative, 32byte line size"), | |
210 | CACHE_DESC(CPUID_CACHE_UCACHE_512K, L2U, 512*1024, 32, \ | |
211 | "Unified L2 cache, 512K, 4-way set associative, 32byte line size"), | |
212 | CACHE_DESC(CPUID_CACHE_UCACHE_1M, L2U, 1*1024*1024, 32, \ | |
213 | "Unified L2 cache, 1M, 4-way set associative, 32byte line size"), | |
214 | CACHE_DESC(CPUID_CACHE_UCACHE_2M, L2U, 2*1024*1024, 32, \ | |
215 | "Unified L2 cache, 2M, 4-way set associative, 32byte line size"), | |
216 | CACHE_DESC(CPUID_CACHE_UCACHE_128K_64, L2U, 128*1024, 64, \ | |
217 | "Unified L2 cache, 128K, 8-way set associative, 64byte line size"), | |
218 | CACHE_DESC(CPUID_CACHE_UCACHE_256K_64, L2U, 256*1024, 64, \ | |
219 | "Unified L2 cache, 256K, 8-way set associative, 64byte line size"), | |
220 | CACHE_DESC(CPUID_CACHE_UCACHE_512K_64, L2U, 512*1024, 64, \ | |
221 | "Unified L2 cache, 512K, 8-way set associative, 64byte line size"), | |
222 | CACHE_DESC(CPUID_CACHE_UCACHE_1M_64, L2U, 1*1024*1024, 64, \ | |
223 | "Unified L2 cache, 1M, 8-way set associative, 64byte line size"), | |
224 | CACHE_DESC(CPUID_CACHE_UCACHE_256K_32, L2U, 256*1024, 32, \ | |
225 | "Unified L2 cache, 256K, 8-way set associative, 32byte line size"), | |
226 | CACHE_DESC(CPUID_CACHE_UCACHE_512K_32, L2U, 512*1024, 32, \ | |
227 | "Unified L2 cache, 512K, 8-way set associative, 32byte line size"), | |
228 | CACHE_DESC(CPUID_CACHE_UCACHE_1M_32, L2U, 1*1024*1024, 32, \ | |
229 | "Unified L2 cache, 1M, 8-way set associative, 32byte line size"), | |
230 | CACHE_DESC(CPUID_CACHE_UCACHE_2M_32, L2U, 2*1024*1024, 32, \ | |
231 | "Unified L2 cache, 2M, 8-way set associative, 32byte line size"), | |
232 | CACHE_DESC(CPUID_CACHE_NULL, Lnone, 0, 0, \ | |
233 | (char *)0), | |
1c79356b | 234 | }; |
d7e50217 A |
235 | |
236 | static void | |
237 | set_cpu_intel(i386_cpu_info_t *info_p) | |
238 | { | |
239 | uint32_t cpuid_result[4]; | |
240 | uint32_t max_extid; | |
241 | char str[128], *p; | |
242 | char *model; | |
243 | int i; | |
244 | int j; | |
245 | ||
246 | /* get extended cpuid results */ | |
247 | do_cpuid(0x80000000, cpuid_result); | |
248 | max_extid = cpuid_result[0]; | |
249 | ||
250 | /* check to see if we can get brand string */ | |
251 | if (max_extid > 0x80000000) { | |
252 | /* | |
253 | * The brand string 48 bytes (max), guaranteed to | |
254 | * be NUL terminated. | |
255 | */ | |
256 | do_cpuid(0x80000002, cpuid_result); | |
257 | bcopy((char *)cpuid_result, &str[0], 16); | |
258 | do_cpuid(0x80000003, cpuid_result); | |
259 | bcopy((char *)cpuid_result, &str[16], 16); | |
260 | do_cpuid(0x80000004, cpuid_result); | |
261 | bcopy((char *)cpuid_result, &str[32], 16); | |
262 | for (p = str; *p != '\0'; p++) { | |
263 | if (*p != ' ') break; | |
264 | } | |
265 | strncpy(info_p->cpuid_brand_string, | |
266 | p, sizeof(info_p->cpuid_brand_string)-1); | |
267 | info_p->cpuid_brand_string[sizeof(info_p->cpuid_brand_string)-1] = '\0'; | |
268 | } | |
1c79356b | 269 | |
d7e50217 A |
270 | /* get processor signature and decode */ |
271 | do_cpuid(1, cpuid_result); | |
272 | info_p->cpuid_signature = cpuid_result[0]; | |
273 | info_p->cpuid_stepping = cpuid_result[0] & 0x0f; | |
274 | info_p->cpuid_model = (cpuid_result[0] >> 4) & 0x0f; | |
275 | info_p->cpuid_family = (cpuid_result[0] >> 8) & 0x0f; | |
276 | info_p->cpuid_type = (cpuid_result[0] >> 12) & 0x03; | |
277 | info_p->cpuid_extmodel = (cpuid_result[0] >> 16) & 0x0f; | |
278 | info_p->cpuid_extfamily = (cpuid_result[0] >> 20) & 0xff; | |
279 | info_p->cpuid_brand = cpuid_result[1] & 0xff; | |
280 | info_p->cpuid_features = cpuid_result[3]; | |
1c79356b | 281 | |
d7e50217 A |
282 | /* decode family/model/type */ |
283 | switch (info_p->cpuid_type) { | |
284 | case CPUID_TYPE_OVERDRIVE: | |
285 | strcat(info_p->model_string, "Overdrive "); | |
286 | break; | |
287 | case CPUID_TYPE_DUAL: | |
288 | strcat(info_p->model_string, "Dual "); | |
289 | break; | |
290 | } | |
291 | strcat(info_p->model_string, | |
292 | cpuid_intel_get_model_name(info_p->cpuid_brand, | |
293 | info_p->cpuid_family, | |
294 | info_p->cpuid_model, | |
295 | info_p->cpuid_signature)); | |
296 | info_p->model_string[sizeof(info_p->model_string)-1] = '\0'; | |
297 | ||
298 | /* get processor cache descriptor info */ | |
299 | do_cpuid(2, cpuid_result); | |
300 | for (j = 0; j < 4; j++) { | |
301 | if ((cpuid_result[j] >> 31) == 1) /* bit31 is validity */ | |
302 | continue; | |
303 | ((uint32_t *) info_p->cache_info)[j] = cpuid_result[j]; | |
304 | } | |
305 | /* first byte gives number of cpuid calls to get all descriptors */ | |
306 | for (i = 1; i < info_p->cache_info[0]; i++) { | |
307 | if (i*16 > sizeof(info_p->cache_info)) | |
308 | break; | |
309 | do_cpuid(2, cpuid_result); | |
310 | for (j = 0; j < 4; j++) { | |
311 | if ((cpuid_result[j] >> 31) == 1) | |
312 | continue; | |
313 | ((uint32_t *) info_p->cache_info)[4*i+j] = | |
314 | cpuid_result[j]; | |
315 | } | |
316 | } | |
317 | ||
318 | /* decode the descriptors looking for L1/L2/L3 size info */ | |
319 | for (i = 1; i < sizeof(info_p->cache_info); i++) { | |
320 | cpuid_cache_desc_t *descp; | |
321 | uint8_t desc = info_p->cache_info[i]; | |
322 | ||
323 | if (desc == CPUID_CACHE_NULL) | |
324 | continue; | |
325 | for (descp = cpuid_cache_desc_tab; | |
326 | descp->value != CPUID_CACHE_NULL; descp++) { | |
327 | if (descp->value != desc) | |
328 | continue; | |
329 | info_p->cache_size[descp->type] = descp->size; | |
330 | if (descp->type == L2U) | |
331 | info_p->cache_linesize = descp->linesize; | |
332 | break; | |
333 | } | |
334 | } | |
335 | /* For P-IIIs, L2 could be 256k or 512k but we can't tell */ | |
336 | if (info_p->cache_size[L2U] == 0 && | |
337 | info_p->cpuid_family == 0x6 && info_p->cpuid_model == 0xb) { | |
338 | info_p->cache_size[L2U] = 256*1024; | |
339 | info_p->cache_linesize = 32; | |
340 | } | |
341 | ||
342 | return; | |
343 | } | |
344 | ||
345 | static void | |
346 | set_cpu_unknown(i386_cpu_info_t *info_p) | |
1c79356b | 347 | { |
d7e50217 A |
348 | strcat(info_p->model_string, "Unknown"); |
349 | } | |
350 | ||
351 | ||
352 | static struct { | |
353 | uint32_t mask; | |
354 | char *name; | |
355 | } feature_names[] = { | |
356 | {CPUID_FEATURE_FPU, "FPU",}, | |
357 | {CPUID_FEATURE_VME, "VME",}, | |
358 | {CPUID_FEATURE_DE, "DE",}, | |
359 | {CPUID_FEATURE_PSE, "PSE",}, | |
360 | {CPUID_FEATURE_TSC, "TSC",}, | |
361 | {CPUID_FEATURE_MSR, "MSR",}, | |
362 | {CPUID_FEATURE_PAE, "PAE",}, | |
363 | {CPUID_FEATURE_MCE, "MCE",}, | |
364 | {CPUID_FEATURE_CX8, "CX8",}, | |
365 | {CPUID_FEATURE_APIC, "APIC",}, | |
366 | {CPUID_FEATURE_SEP, "SEP",}, | |
367 | {CPUID_FEATURE_MTRR, "MTRR",}, | |
368 | {CPUID_FEATURE_PGE, "PGE",}, | |
369 | {CPUID_FEATURE_MCA, "MCA",}, | |
370 | {CPUID_FEATURE_CMOV, "CMOV",}, | |
371 | {CPUID_FEATURE_PAT, "PAT",}, | |
372 | {CPUID_FEATURE_PSE36, "PSE36",}, | |
373 | {CPUID_FEATURE_PSN, "PSN",}, | |
374 | {CPUID_FEATURE_CLFSH, "CLFSH",}, | |
375 | {CPUID_FEATURE_DS, "DS",}, | |
376 | {CPUID_FEATURE_ACPI, "ACPI",}, | |
377 | {CPUID_FEATURE_MMX, "MMX",}, | |
378 | {CPUID_FEATURE_FXSR, "FXSR",}, | |
379 | {CPUID_FEATURE_SSE, "SSE",}, | |
380 | {CPUID_FEATURE_SSE2, "SSE2",}, | |
381 | {CPUID_FEATURE_SS, "SS",}, | |
382 | {CPUID_FEATURE_HTT, "HTT",}, | |
383 | {CPUID_FEATURE_TM, "TM",}, | |
384 | {0, 0} | |
385 | }; | |
386 | ||
387 | char * | |
388 | cpuid_get_feature_names(uint32_t feature, char *buf, unsigned buf_len) | |
389 | { | |
390 | int i; | |
391 | int len; | |
392 | char *p = buf; | |
393 | ||
394 | for (i = 0; feature_names[i].mask != 0; i++) { | |
395 | if ((feature & feature_names[i].mask) == 0) | |
396 | continue; | |
397 | if (i > 0) | |
398 | *p++ = ' '; | |
399 | len = min(strlen(feature_names[i].name), (buf_len-1) - (p-buf)); | |
400 | if (len == 0) | |
401 | break; | |
402 | bcopy(feature_names[i].name, p, len); | |
403 | p += len; | |
404 | } | |
405 | *p = '\0'; | |
406 | return buf; | |
407 | } | |
408 | ||
409 | void | |
410 | cpuid_feature_display( | |
411 | char *header, | |
412 | int my_cpu) | |
413 | { | |
414 | char buf[256]; | |
415 | ||
416 | printf("%s: %s\n", header, | |
417 | cpuid_get_feature_names(cpuid_features(), buf, sizeof(buf))); | |
1c79356b A |
418 | } |
419 | ||
1c79356b A |
420 | void |
421 | cpuid_cpu_display( | |
d7e50217 A |
422 | char *header, |
423 | int my_cpu) | |
1c79356b | 424 | { |
d7e50217 A |
425 | printf("%s: %s\n", header, |
426 | (cpuid_cpu_info.cpuid_brand_string[0] != '\0') ? | |
427 | cpuid_cpu_info.cpuid_brand_string : | |
428 | cpuid_cpu_info.model_string); | |
1c79356b A |
429 | } |
430 | ||
d7e50217 A |
431 | unsigned int |
432 | cpuid_family(void) | |
433 | { | |
434 | return cpuid_cpu_info.cpuid_family; | |
435 | } | |
436 | ||
437 | unsigned int | |
438 | cpuid_features(void) | |
439 | { | |
440 | return cpuid_cpu_info.cpuid_features; | |
441 | } | |
442 | ||
443 | i386_cpu_info_t * | |
444 | cpuid_info(void) | |
445 | { | |
446 | return &cpuid_cpu_info; | |
447 | } | |
448 | ||
449 | /* XXX for temporary compatibility */ | |
1c79356b | 450 | void |
d7e50217 | 451 | set_cpu_model(void) |
1c79356b | 452 | { |
d7e50217 A |
453 | cpuid_get_info(&cpuid_cpu_info); |
454 | cpuid_feature = cpuid_cpu_info.cpuid_features; /* XXX compat */ | |
1c79356b | 455 | } |
d7e50217 | 456 |