2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
23 * @APPLE_LICENSE_HEADER_END@
31 #define min(a,b) ((a) < (b) ? (a) : (b))
34 * CPU identification routines.
36 * Note that this code assumes a processor that supports the
37 * 'cpuid' instruction.
40 static unsigned int cpuid_maxcpuid
;
42 static i386_cpu_info_t cpuid_cpu_info
;
44 uint32_t cpuid_feature
; /* XXX obsolescent for compat */
47 * We only identify Intel CPUs here. Adding support
48 * for others would be straightforward.
50 static void set_cpu_intel(i386_cpu_info_t
*);
51 static void set_cpu_unknown(i386_cpu_info_t
*);
55 void (* func
)(i386_cpu_info_t
*);
57 {CPUID_VID_INTEL
, set_cpu_intel
},
62 cpuid_get_info(i386_cpu_info_t
*info_p
)
64 uint32_t cpuid_result
[4];
67 bzero((void *)info_p
, sizeof(i386_cpu_info_t
));
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;
79 if ((cpu_vendors
[i
].vendor
== 0) ||
80 (!strcmp(cpu_vendors
[i
].vendor
, info_p
->cpuid_vendor
))) {
81 cpu_vendors
[i
].func(info_p
);
88 * A useful model name string takes some decoding.
91 cpuid_intel_get_model_name(
97 /* check for brand id */
100 /* brand ID not supported; use alternate method. */
102 case CPUID_FAMILY_486
:
104 case CPUID_FAMILY_P5
:
106 case CPUID_FAMILY_PPRO
:
109 return "Pentium Pro";
110 case CPUID_MODEL_PII
:
112 case CPUID_MODEL_P65
:
113 case CPUID_MODEL_P66
:
115 case CPUID_MODEL_P67
:
116 case CPUID_MODEL_P68
:
117 case CPUID_MODEL_P6A
:
118 case CPUID_MODEL_P6B
:
119 return "Pentium III";
121 return "Unknown P6 Family";
123 case CPUID_FAMILY_PENTIUM4
:
126 return "Unknown Family";
132 return "Pentium III";
134 if (signature
== 0x6B1)
137 return "Pentium III Xeon";
139 return "Mobile Pentium III";
141 return "Mobile Celeron";
143 if (signature
>= 0xF20)
144 return "Genuine Intel";
153 return "Mobile Pentium 4";
155 return "Unknown Pentium";
160 * Cache descriptor table. Each row has the form:
161 * (descriptor_value, cache, size, linesize,
163 * Note: the CACHE_DESC macro does not expand description text in the kernel.
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, \
237 set_cpu_intel(i386_cpu_info_t
*info_p
)
239 uint32_t cpuid_result
[4];
246 /* get extended cpuid results */
247 do_cpuid(0x80000000, cpuid_result
);
248 max_extid
= cpuid_result
[0];
250 /* check to see if we can get brand string */
251 if (max_extid
> 0x80000000) {
253 * The brand string 48 bytes (max), guaranteed to
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;
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';
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];
282 /* decode family/model/type */
283 switch (info_p
->cpuid_type
) {
284 case CPUID_TYPE_OVERDRIVE
:
285 strcat(info_p
->model_string
, "Overdrive ");
287 case CPUID_TYPE_DUAL
:
288 strcat(info_p
->model_string
, "Dual ");
291 strcat(info_p
->model_string
,
292 cpuid_intel_get_model_name(info_p
->cpuid_brand
,
293 info_p
->cpuid_family
,
295 info_p
->cpuid_signature
));
296 info_p
->model_string
[sizeof(info_p
->model_string
)-1] = '\0';
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 */
303 ((uint32_t *) info_p
->cache_info
)[j
] = cpuid_result
[j
];
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
))
309 do_cpuid(2, cpuid_result
);
310 for (j
= 0; j
< 4; j
++) {
311 if ((cpuid_result
[j
] >> 31) == 1)
313 ((uint32_t *) info_p
->cache_info
)[4*i
+j
] =
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
];
323 if (desc
== CPUID_CACHE_NULL
)
325 for (descp
= cpuid_cache_desc_tab
;
326 descp
->value
!= CPUID_CACHE_NULL
; descp
++) {
327 if (descp
->value
!= desc
)
329 info_p
->cache_size
[descp
->type
] = descp
->size
;
330 if (descp
->type
== L2U
)
331 info_p
->cache_linesize
= descp
->linesize
;
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;
346 set_cpu_unknown(i386_cpu_info_t
*info_p
)
348 strcat(info_p
->model_string
, "Unknown");
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",},
388 cpuid_get_feature_names(uint32_t feature
, char *buf
, unsigned buf_len
)
394 for (i
= 0; feature_names
[i
].mask
!= 0; i
++) {
395 if ((feature
& feature_names
[i
].mask
) == 0)
399 len
= min(strlen(feature_names
[i
].name
), (buf_len
-1) - (p
-buf
));
402 bcopy(feature_names
[i
].name
, p
, len
);
410 cpuid_feature_display(
416 printf("%s: %s\n", header
,
417 cpuid_get_feature_names(cpuid_features(), buf
, sizeof(buf
)));
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
);
434 return cpuid_cpu_info
.cpuid_family
;
440 return cpuid_cpu_info
.cpuid_features
;
446 return &cpuid_cpu_info
;
449 /* XXX for temporary compatibility */
453 cpuid_get_info(&cpuid_cpu_info
);
454 cpuid_feature
= cpuid_cpu_info
.cpuid_features
; /* XXX compat */