]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/cpuid.c
xnu-517.7.7.tar.gz
[apple/xnu.git] / osfmk / i386 / cpuid.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * @OSF_COPYRIGHT@
24 */
25
26 #include "cpuid.h"
27
28 #define min(a,b) ((a) < (b) ? (a) : (b))
29
30 /*
31 * CPU identification routines.
32 *
33 * Note that this code assumes a processor that supports the
34 * 'cpuid' instruction.
35 */
36
37 static unsigned int cpuid_maxcpuid;
38
39 static i386_cpu_info_t cpuid_cpu_info;
40
41 uint32_t cpuid_feature; /* XXX obsolescent for compat */
42
43 /*
44 * We only identify Intel CPUs here. Adding support
45 * for others would be straightforward.
46 */
47 static void set_cpu_intel(i386_cpu_info_t *);
48 static void set_cpu_unknown(i386_cpu_info_t *);
49
50 struct {
51 char *vendor;
52 void (* func)(i386_cpu_info_t *);
53 } cpu_vendors[] = {
54 {CPUID_VID_INTEL, set_cpu_intel},
55 {0, set_cpu_unknown}
56 };
57
58 void
59 cpuid_get_info(i386_cpu_info_t *info_p)
60 {
61 uint32_t cpuid_result[4];
62 int i;
63
64 bzero((void *)info_p, sizeof(i386_cpu_info_t));
65
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;
73
74 /* look up vendor */
75 for (i = 0; ; i++) {
76 if ((cpu_vendors[i].vendor == 0) ||
77 (!strcmp(cpu_vendors[i].vendor, info_p->cpuid_vendor))) {
78 cpu_vendors[i].func(info_p);
79 break;
80 }
81 }
82 }
83
84 /*
85 * A useful model name string takes some decoding.
86 */
87 char *
88 cpuid_intel_get_model_name(
89 uint8_t brand,
90 uint8_t family,
91 uint8_t model,
92 uint32_t signature)
93 {
94 /* check for brand id */
95 switch(brand) {
96 case 0:
97 /* brand ID not supported; use alternate method. */
98 switch(family) {
99 case CPUID_FAMILY_486:
100 return "486";
101 case CPUID_FAMILY_P5:
102 return "Pentium";
103 case CPUID_FAMILY_PPRO:
104 switch(model) {
105 case CPUID_MODEL_P6:
106 return "Pentium Pro";
107 case CPUID_MODEL_PII:
108 return "Pentium II";
109 case CPUID_MODEL_P65:
110 case CPUID_MODEL_P66:
111 return "Celeron";
112 case CPUID_MODEL_P67:
113 case CPUID_MODEL_P68:
114 case CPUID_MODEL_P6A:
115 case CPUID_MODEL_P6B:
116 return "Pentium III";
117 default:
118 return "Unknown P6 Family";
119 }
120 case CPUID_FAMILY_PENTIUM4:
121 return "Pentium 4";
122 default:
123 return "Unknown Family";
124 }
125 case 0x01:
126 return "Celeron";
127 case 0x02:
128 case 0x04:
129 return "Pentium III";
130 case 0x03:
131 if (signature == 0x6B1)
132 return "Celeron";
133 else
134 return "Pentium III Xeon";
135 case 0x06:
136 return "Mobile Pentium III";
137 case 0x07:
138 return "Mobile Celeron";
139 case 0x08:
140 if (signature >= 0xF20)
141 return "Genuine Intel";
142 else
143 return "Pentium 4";
144 case 0x09:
145 return "Pentium 4";
146 case 0x0b:
147 return "Xeon";
148 case 0x0e:
149 case 0x0f:
150 return "Mobile Pentium 4";
151 default:
152 return "Unknown Pentium";
153 }
154 }
155
156 /*
157 * Cache descriptor table. Each row has the form:
158 * (descriptor_value, cache, size, linesize,
159 * description)
160 * Note: the CACHE_DESC macro does not expand description text in the kernel.
161 */
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, \
230 (char *)0),
231 };
232
233 static void
234 set_cpu_intel(i386_cpu_info_t *info_p)
235 {
236 uint32_t cpuid_result[4];
237 uint32_t max_extid;
238 char str[128], *p;
239 char *model;
240 int i;
241 int j;
242
243 /* get extended cpuid results */
244 do_cpuid(0x80000000, cpuid_result);
245 max_extid = cpuid_result[0];
246
247 /* check to see if we can get brand string */
248 if (max_extid > 0x80000000) {
249 /*
250 * The brand string 48 bytes (max), guaranteed to
251 * be NUL terminated.
252 */
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;
261 }
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';
265 }
266
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];
278
279 /* decode family/model/type */
280 switch (info_p->cpuid_type) {
281 case CPUID_TYPE_OVERDRIVE:
282 strcat(info_p->model_string, "Overdrive ");
283 break;
284 case CPUID_TYPE_DUAL:
285 strcat(info_p->model_string, "Dual ");
286 break;
287 }
288 strcat(info_p->model_string,
289 cpuid_intel_get_model_name(info_p->cpuid_brand,
290 info_p->cpuid_family,
291 info_p->cpuid_model,
292 info_p->cpuid_signature));
293 info_p->model_string[sizeof(info_p->model_string)-1] = '\0';
294
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 */
299 continue;
300 ((uint32_t *) info_p->cache_info)[j] = cpuid_result[j];
301 }
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))
305 break;
306 do_cpuid(2, cpuid_result);
307 for (j = 0; j < 4; j++) {
308 if ((cpuid_result[j] >> 31) == 1)
309 continue;
310 ((uint32_t *) info_p->cache_info)[4*i+j] =
311 cpuid_result[j];
312 }
313 }
314
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];
319
320 if (desc == CPUID_CACHE_NULL)
321 continue;
322 for (descp = cpuid_cache_desc_tab;
323 descp->value != CPUID_CACHE_NULL; descp++) {
324 if (descp->value != desc)
325 continue;
326 info_p->cache_size[descp->type] = descp->size;
327 if (descp->type == L2U)
328 info_p->cache_linesize = descp->linesize;
329 break;
330 }
331 }
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;
337 }
338
339 return;
340 }
341
342 static void
343 set_cpu_unknown(i386_cpu_info_t *info_p)
344 {
345 strcat(info_p->model_string, "Unknown");
346 }
347
348
349 static struct {
350 uint32_t mask;
351 char *name;
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",},
381 {0, 0}
382 };
383
384 char *
385 cpuid_get_feature_names(uint32_t feature, char *buf, unsigned buf_len)
386 {
387 int i;
388 int len;
389 char *p = buf;
390
391 for (i = 0; feature_names[i].mask != 0; i++) {
392 if ((feature & feature_names[i].mask) == 0)
393 continue;
394 if (i > 0)
395 *p++ = ' ';
396 len = min(strlen(feature_names[i].name), (buf_len-1) - (p-buf));
397 if (len == 0)
398 break;
399 bcopy(feature_names[i].name, p, len);
400 p += len;
401 }
402 *p = '\0';
403 return buf;
404 }
405
406 void
407 cpuid_feature_display(
408 char *header,
409 int my_cpu)
410 {
411 char buf[256];
412
413 printf("%s: %s\n", header,
414 cpuid_get_feature_names(cpuid_features(), buf, sizeof(buf)));
415 }
416
417 void
418 cpuid_cpu_display(
419 char *header,
420 int my_cpu)
421 {
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);
426 }
427
428 unsigned int
429 cpuid_family(void)
430 {
431 return cpuid_cpu_info.cpuid_family;
432 }
433
434 unsigned int
435 cpuid_features(void)
436 {
437 return cpuid_cpu_info.cpuid_features;
438 }
439
440 i386_cpu_info_t *
441 cpuid_info(void)
442 {
443 return &cpuid_cpu_info;
444 }
445
446 /* XXX for temporary compatibility */
447 void
448 set_cpu_model(void)
449 {
450 cpuid_get_info(&cpuid_cpu_info);
451 cpuid_feature = cpuid_cpu_info.cpuid_features; /* XXX compat */
452 }
453