2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
28 #define min(a,b) ((a) < (b) ? (a) : (b))
31 * CPU identification routines.
33 * Note that this code assumes a processor that supports the
34 * 'cpuid' instruction.
37 static unsigned int cpuid_maxcpuid
;
39 static i386_cpu_info_t cpuid_cpu_info
;
41 uint32_t cpuid_feature
; /* XXX obsolescent for compat */
44 * We only identify Intel CPUs here. Adding support
45 * for others would be straightforward.
47 static void set_cpu_intel(i386_cpu_info_t
*);
48 static void set_cpu_unknown(i386_cpu_info_t
*);
52 void (* func
)(i386_cpu_info_t
*);
54 {CPUID_VID_INTEL
, set_cpu_intel
},
59 cpuid_get_info(i386_cpu_info_t
*info_p
)
61 uint32_t cpuid_result
[4];
64 bzero((void *)info_p
, sizeof(i386_cpu_info_t
));
66 /* do cpuid 0 to get vendor */
67 do_cpuid(0, cpuid_result
);
68 cpuid_maxcpuid
= cpuid_result
[0];
69 bcopy((char *)&cpuid_result
[1], &info_p
->cpuid_vendor
[0], 4); /* ugh */
70 bcopy((char *)&cpuid_result
[2], &info_p
->cpuid_vendor
[8], 4);
71 bcopy((char *)&cpuid_result
[3], &info_p
->cpuid_vendor
[4], 4);
72 info_p
->cpuid_vendor
[12] = 0;
76 if ((cpu_vendors
[i
].vendor
== 0) ||
77 (!strcmp(cpu_vendors
[i
].vendor
, info_p
->cpuid_vendor
))) {
78 cpu_vendors
[i
].func(info_p
);
85 * A useful model name string takes some decoding.
88 cpuid_intel_get_model_name(
94 /* check for brand id */
97 /* brand ID not supported; use alternate method. */
99 case CPUID_FAMILY_486
:
101 case CPUID_FAMILY_P5
:
103 case CPUID_FAMILY_PPRO
:
106 return "Pentium Pro";
107 case CPUID_MODEL_PII
:
109 case CPUID_MODEL_P65
:
110 case CPUID_MODEL_P66
:
112 case CPUID_MODEL_P67
:
113 case CPUID_MODEL_P68
:
114 case CPUID_MODEL_P6A
:
115 case CPUID_MODEL_P6B
:
116 return "Pentium III";
118 return "Unknown P6 Family";
120 case CPUID_FAMILY_PENTIUM4
:
123 return "Unknown Family";
129 return "Pentium III";
131 if (signature
== 0x6B1)
134 return "Pentium III Xeon";
136 return "Mobile Pentium III";
138 return "Mobile Celeron";
140 if (signature
>= 0xF20)
141 return "Genuine Intel";
150 return "Mobile Pentium 4";
152 return "Unknown Pentium";
157 * Cache descriptor table. Each row has the form:
158 * (descriptor_value, cache, size, linesize,
160 * Note: the CACHE_DESC macro does not expand description text in the kernel.
162 static cpuid_cache_desc_t cpuid_cache_desc_tab
[] = {
163 CACHE_DESC(CPUID_CACHE_ITLB_4K
, Lnone
, 0, 0, \
164 "Instruction TLB, 4K, pages 4-way set associative, 64 entries"),
165 CACHE_DESC(CPUID_CACHE_ITLB_4M
, Lnone
, 0, 0, \
166 "Instruction TLB, 4M, pages 4-way set associative, 4 entries"),
167 CACHE_DESC(CPUID_CACHE_DTLB_4K
, Lnone
, 0, 0, \
168 "Data TLB, 4K pages, 4-way set associative, 64 entries"),
169 CACHE_DESC(CPUID_CACHE_DTLB_4M
, Lnone
, 0, 0, \
170 "Data TLB, 4M pages, 4-way set associative, 4 entries"),
171 CACHE_DESC(CPUID_CACHE_ITLB_64
, Lnone
, 0, 0, \
172 "Instruction TLB, 4K and 2M or 4M pages, 64 entries"),
173 CACHE_DESC(CPUID_CACHE_ITLB_128
, Lnone
, 0, 0, \
174 "Instruction TLB, 4K and 2M or 4M pages, 128 entries"),
175 CACHE_DESC(CPUID_CACHE_ITLB_256
, Lnone
, 0, 0, \
176 "Instruction TLB, 4K and 2M or 4M pages, 256 entries"),
177 CACHE_DESC(CPUID_CACHE_DTLB_64
, Lnone
, 0, 0, \
178 "Data TLB, 4K and 4M pages, 64 entries"),
179 CACHE_DESC(CPUID_CACHE_DTLB_128
, Lnone
, 0, 0, \
180 "Data TLB, 4K and 4M pages, 128 entries"),
181 CACHE_DESC(CPUID_CACHE_DTLB_256
, Lnone
, 0, 0, \
182 "Data TLB, 4K and 4M pages, 256 entries"),
183 CACHE_DESC(CPUID_CACHE_ICACHE_8K
, L1I
, 8*1024, 32, \
184 "Instruction L1 cache, 8K, 4-way set associative, 32byte line size"),
185 CACHE_DESC(CPUID_CACHE_DCACHE_8K
, L1D
, 8*1024, 32, \
186 "Data L1 cache, 8K, 2-way set associative, 32byte line size"),
187 CACHE_DESC(CPUID_CACHE_ICACHE_16K
, L1I
, 16*1024, 32, \
188 "Instruction L1 cache, 16K, 4-way set associative, 32byte line size"),
189 CACHE_DESC(CPUID_CACHE_DCACHE_16K
, L1D
, 16*1024, 32, \
190 "Data L1 cache, 16K, 4-way set associative, 32byte line size"),
191 CACHE_DESC(CPUID_CACHE_DCACHE_8K_64
, L1D
, 8*1024, 64, \
192 "Data L1 cache, 8K, 4-way set associative, 64byte line size"),
193 CACHE_DESC(CPUID_CACHE_DCACHE_16K_64
, L1D
, 16*1024, 64, \
194 "Data L1 cache, 16K, 4-way set associative, 64byte line size"),
195 CACHE_DESC(CPUID_CACHE_DCACHE_32K_64
, L1D
, 32*1024, 64, \
196 "Data L1 cache, 32K, 4-way set associative, 64byte line size"),
197 CACHE_DESC(CPUID_CACHE_TRACE_12K
, L1I
, 12*1024, 64, \
198 "Trace cache, 12K-uop, 8-way set associative"),
199 CACHE_DESC(CPUID_CACHE_TRACE_12K
, L1I
, 16*1024, 64, \
200 "Trace cache, 16K-uop, 8-way set associative"),
201 CACHE_DESC(CPUID_CACHE_TRACE_12K
, L1I
, 32*1024, 64, \
202 "Trace cache, 32K-uop, 8-way set associative"),
203 CACHE_DESC(CPUID_CACHE_UCACHE_128K
, L2U
, 128*1024, 32, \
204 "Unified L2 cache, 128K, 4-way set associative, 32byte line size"),
205 CACHE_DESC(CPUID_CACHE_UCACHE_256K
, L2U
, 128*1024, 32, \
206 "Unified L2 cache, 256K, 4-way set associative, 32byte line size"),
207 CACHE_DESC(CPUID_CACHE_UCACHE_512K
, L2U
, 512*1024, 32, \
208 "Unified L2 cache, 512K, 4-way set associative, 32byte line size"),
209 CACHE_DESC(CPUID_CACHE_UCACHE_1M
, L2U
, 1*1024*1024, 32, \
210 "Unified L2 cache, 1M, 4-way set associative, 32byte line size"),
211 CACHE_DESC(CPUID_CACHE_UCACHE_2M
, L2U
, 2*1024*1024, 32, \
212 "Unified L2 cache, 2M, 4-way set associative, 32byte line size"),
213 CACHE_DESC(CPUID_CACHE_UCACHE_128K_64
, L2U
, 128*1024, 64, \
214 "Unified L2 cache, 128K, 8-way set associative, 64byte line size"),
215 CACHE_DESC(CPUID_CACHE_UCACHE_256K_64
, L2U
, 256*1024, 64, \
216 "Unified L2 cache, 256K, 8-way set associative, 64byte line size"),
217 CACHE_DESC(CPUID_CACHE_UCACHE_512K_64
, L2U
, 512*1024, 64, \
218 "Unified L2 cache, 512K, 8-way set associative, 64byte line size"),
219 CACHE_DESC(CPUID_CACHE_UCACHE_1M_64
, L2U
, 1*1024*1024, 64, \
220 "Unified L2 cache, 1M, 8-way set associative, 64byte line size"),
221 CACHE_DESC(CPUID_CACHE_UCACHE_256K_32
, L2U
, 256*1024, 32, \
222 "Unified L2 cache, 256K, 8-way set associative, 32byte line size"),
223 CACHE_DESC(CPUID_CACHE_UCACHE_512K_32
, L2U
, 512*1024, 32, \
224 "Unified L2 cache, 512K, 8-way set associative, 32byte line size"),
225 CACHE_DESC(CPUID_CACHE_UCACHE_1M_32
, L2U
, 1*1024*1024, 32, \
226 "Unified L2 cache, 1M, 8-way set associative, 32byte line size"),
227 CACHE_DESC(CPUID_CACHE_UCACHE_2M_32
, L2U
, 2*1024*1024, 32, \
228 "Unified L2 cache, 2M, 8-way set associative, 32byte line size"),
229 CACHE_DESC(CPUID_CACHE_NULL
, Lnone
, 0, 0, \
234 set_cpu_intel(i386_cpu_info_t
*info_p
)
236 uint32_t cpuid_result
[4];
243 /* get extended cpuid results */
244 do_cpuid(0x80000000, cpuid_result
);
245 max_extid
= cpuid_result
[0];
247 /* check to see if we can get brand string */
248 if (max_extid
> 0x80000000) {
250 * The brand string 48 bytes (max), guaranteed to
253 do_cpuid(0x80000002, cpuid_result
);
254 bcopy((char *)cpuid_result
, &str
[0], 16);
255 do_cpuid(0x80000003, cpuid_result
);
256 bcopy((char *)cpuid_result
, &str
[16], 16);
257 do_cpuid(0x80000004, cpuid_result
);
258 bcopy((char *)cpuid_result
, &str
[32], 16);
259 for (p
= str
; *p
!= '\0'; p
++) {
260 if (*p
!= ' ') break;
262 strncpy(info_p
->cpuid_brand_string
,
263 p
, sizeof(info_p
->cpuid_brand_string
)-1);
264 info_p
->cpuid_brand_string
[sizeof(info_p
->cpuid_brand_string
)-1] = '\0';
267 /* get processor signature and decode */
268 do_cpuid(1, cpuid_result
);
269 info_p
->cpuid_signature
= cpuid_result
[0];
270 info_p
->cpuid_stepping
= cpuid_result
[0] & 0x0f;
271 info_p
->cpuid_model
= (cpuid_result
[0] >> 4) & 0x0f;
272 info_p
->cpuid_family
= (cpuid_result
[0] >> 8) & 0x0f;
273 info_p
->cpuid_type
= (cpuid_result
[0] >> 12) & 0x03;
274 info_p
->cpuid_extmodel
= (cpuid_result
[0] >> 16) & 0x0f;
275 info_p
->cpuid_extfamily
= (cpuid_result
[0] >> 20) & 0xff;
276 info_p
->cpuid_brand
= cpuid_result
[1] & 0xff;
277 info_p
->cpuid_features
= cpuid_result
[3];
279 /* decode family/model/type */
280 switch (info_p
->cpuid_type
) {
281 case CPUID_TYPE_OVERDRIVE
:
282 strcat(info_p
->model_string
, "Overdrive ");
284 case CPUID_TYPE_DUAL
:
285 strcat(info_p
->model_string
, "Dual ");
288 strcat(info_p
->model_string
,
289 cpuid_intel_get_model_name(info_p
->cpuid_brand
,
290 info_p
->cpuid_family
,
292 info_p
->cpuid_signature
));
293 info_p
->model_string
[sizeof(info_p
->model_string
)-1] = '\0';
295 /* get processor cache descriptor info */
296 do_cpuid(2, cpuid_result
);
297 for (j
= 0; j
< 4; j
++) {
298 if ((cpuid_result
[j
] >> 31) == 1) /* bit31 is validity */
300 ((uint32_t *) info_p
->cache_info
)[j
] = cpuid_result
[j
];
302 /* first byte gives number of cpuid calls to get all descriptors */
303 for (i
= 1; i
< info_p
->cache_info
[0]; i
++) {
304 if (i
*16 > sizeof(info_p
->cache_info
))
306 do_cpuid(2, cpuid_result
);
307 for (j
= 0; j
< 4; j
++) {
308 if ((cpuid_result
[j
] >> 31) == 1)
310 ((uint32_t *) info_p
->cache_info
)[4*i
+j
] =
315 /* decode the descriptors looking for L1/L2/L3 size info */
316 for (i
= 1; i
< sizeof(info_p
->cache_info
); i
++) {
317 cpuid_cache_desc_t
*descp
;
318 uint8_t desc
= info_p
->cache_info
[i
];
320 if (desc
== CPUID_CACHE_NULL
)
322 for (descp
= cpuid_cache_desc_tab
;
323 descp
->value
!= CPUID_CACHE_NULL
; descp
++) {
324 if (descp
->value
!= desc
)
326 info_p
->cache_size
[descp
->type
] = descp
->size
;
327 if (descp
->type
== L2U
)
328 info_p
->cache_linesize
= descp
->linesize
;
332 /* For P-IIIs, L2 could be 256k or 512k but we can't tell */
333 if (info_p
->cache_size
[L2U
] == 0 &&
334 info_p
->cpuid_family
== 0x6 && info_p
->cpuid_model
== 0xb) {
335 info_p
->cache_size
[L2U
] = 256*1024;
336 info_p
->cache_linesize
= 32;
343 set_cpu_unknown(i386_cpu_info_t
*info_p
)
345 strcat(info_p
->model_string
, "Unknown");
352 } feature_names
[] = {
353 {CPUID_FEATURE_FPU
, "FPU",},
354 {CPUID_FEATURE_VME
, "VME",},
355 {CPUID_FEATURE_DE
, "DE",},
356 {CPUID_FEATURE_PSE
, "PSE",},
357 {CPUID_FEATURE_TSC
, "TSC",},
358 {CPUID_FEATURE_MSR
, "MSR",},
359 {CPUID_FEATURE_PAE
, "PAE",},
360 {CPUID_FEATURE_MCE
, "MCE",},
361 {CPUID_FEATURE_CX8
, "CX8",},
362 {CPUID_FEATURE_APIC
, "APIC",},
363 {CPUID_FEATURE_SEP
, "SEP",},
364 {CPUID_FEATURE_MTRR
, "MTRR",},
365 {CPUID_FEATURE_PGE
, "PGE",},
366 {CPUID_FEATURE_MCA
, "MCA",},
367 {CPUID_FEATURE_CMOV
, "CMOV",},
368 {CPUID_FEATURE_PAT
, "PAT",},
369 {CPUID_FEATURE_PSE36
, "PSE36",},
370 {CPUID_FEATURE_PSN
, "PSN",},
371 {CPUID_FEATURE_CLFSH
, "CLFSH",},
372 {CPUID_FEATURE_DS
, "DS",},
373 {CPUID_FEATURE_ACPI
, "ACPI",},
374 {CPUID_FEATURE_MMX
, "MMX",},
375 {CPUID_FEATURE_FXSR
, "FXSR",},
376 {CPUID_FEATURE_SSE
, "SSE",},
377 {CPUID_FEATURE_SSE2
, "SSE2",},
378 {CPUID_FEATURE_SS
, "SS",},
379 {CPUID_FEATURE_HTT
, "HTT",},
380 {CPUID_FEATURE_TM
, "TM",},
385 cpuid_get_feature_names(uint32_t feature
, char *buf
, unsigned buf_len
)
391 for (i
= 0; feature_names
[i
].mask
!= 0; i
++) {
392 if ((feature
& feature_names
[i
].mask
) == 0)
396 len
= min(strlen(feature_names
[i
].name
), (buf_len
-1) - (p
-buf
));
399 bcopy(feature_names
[i
].name
, p
, len
);
407 cpuid_feature_display(
413 printf("%s: %s\n", header
,
414 cpuid_get_feature_names(cpuid_features(), buf
, sizeof(buf
)));
422 printf("%s: %s\n", header
,
423 (cpuid_cpu_info
.cpuid_brand_string
[0] != '\0') ?
424 cpuid_cpu_info
.cpuid_brand_string
:
425 cpuid_cpu_info
.model_string
);
431 return cpuid_cpu_info
.cpuid_family
;
437 return cpuid_cpu_info
.cpuid_features
;
443 return &cpuid_cpu_info
;
446 /* XXX for temporary compatibility */
450 cpuid_get_info(&cpuid_cpu_info
);
451 cpuid_feature
= cpuid_cpu_info
.cpuid_features
; /* XXX compat */