xref: /haiku/src/bin/sysinfo.cpp (revision 93e30a47bed879ad448b3e2d9e10333d3f2e60ae)
1 /*
2  * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright (c) 2002, Carlos Hasan, for Haiku.
4  *
5  * Distributed under the terms of the MIT license.
6  */
7 
8 
9 #include <OS.h>
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <cpu_type.h>
16 
17 
18 // ToDo: -disable_cpu_sn option is not yet implemented
19 // ToDo: most of this file should go into an architecture dependent source file
20 #ifdef __INTEL__
21 
22 struct cache_description {
23 	uint8		code;
24 	const char	*description;
25 } static sIntelCacheDescriptions[] = {
26 	{0x01, "Instruction TLB: 4k-byte pages, 4-way set associative, 32 entries"},
27 	{0x02, "Instruction TLB: 4M-byte pages, fully associative, 2 entries"},
28 	{0x03, "Data TLB: 4k-byte pages, 4-way set associative, 64 entries"},
29 	{0x04, "Data TLB: 4M-byte pages, 4-way set associative, 8 entries"},
30 	{0x05, "Data TLB: 4M-byte pages, 4-way set associative, 32 entries"},
31 	{0x06, "L1 inst cache: 8 KB, 4-way set associative, 32 bytes/line"},
32 	{0x08, "L1 inst cache: 16 KB, 4-way set associative, 32 bytes/line"},
33 	{0x0A, "L1 data cache: 8 KB, 2-way set associative, 32 bytes/line"},
34 	{0x0C, "L1 data cache: 16 KB, 4-way set associative, 32 bytes/line"},
35 	{0x0D, "L1 data cache: 16 KB, 4-way set associative, 64-bytes/line, ECC"},
36 	{0x0E, "L1 data cache, 24 KB, 6-way set associative, 64-bytes/line"},
37 	{0x10, /* IA-64 */ "L1 data cache: 16 KB, 4-way set associative, 32 bytes/line"},
38 	{0x15, /* IA-64 */ "L1 inst cache: 16 KB, 4-way set associative, 32 bytes/line"},
39 	{0x1A, /* IA-64 */ "L2 cache: 96 KB, 6-way set associative, 64 bytes/line"},
40 	{0x21, "L2 cache: 256 KB (MLC), 8-way set associative, 64-bytes/line"},
41 	{0x22, "L3 cache: 512 KB, 4-way set associative (!), 64 bytes/line, dual-sectored"},
42 	{0x23, "L3 cache: 1 MB, 8-way set associative, 64 bytes/line, dual-sectored"},
43 	{0x25, "L3 cache: 2 MB, 8-way set associative, 64 bytes/line, dual-sectored"},
44 	{0x29, "L3 cache: 4 MB, 8-way set associative, 64 bytes/line, dual-sectored"},
45 	{0x2c, "L1 data cache: 32 KB, 8-way set associative, 64 bytes/line"},
46 	{0x30, "L1 inst cache: 32 KB, 8-way set associative, 64 bytes/line"},
47 	{0x39, "L2 cache: 128 KB, 4-way set associative, 64 bytes/line, sectored"},
48 	{0x3A, "L2 cache: 192 KB, 4-way set associative, 64 bytes/line, sectored"},
49 	{0x3B, "L2 cache: 128 KB, 2-way set associative, 64 bytes/line, sectored"},
50 	{0x3C, "L2 cache: 256 KB, 4-way set associative, 64 bytes/line, sectored"},
51 	{0x3D, "L2 cache: 384 KB, 6-way set associative, 64 bytes/line, sectored"},
52 	{0x3E, "L2 cache: 512 KB, 4-way set associative, 64 bytes/line, sectored"},
53 	{0x40, NULL /*"No integrated L2 cache (P6 core) or L3 cache (P4 core)"*/},
54 		// this one is separately handled
55 	{0x41, "L2 cache: 128 KB, 4-way set associative, 32 bytes/line"},
56 	{0x42, "L2 cache: 256 KB, 4-way set associative, 32 bytes/line"},
57 	{0x43, "L2 cache: 512 KB, 4-way set associative, 32 bytes/line"},
58 	{0x44, "L2 cache: 1024 KB, 4-way set associative, 32 bytes/line"},
59 	{0x45, "L2 cache: 2048 KB, 4-way set associative, 32 bytes/line"},
60 	{0x46, "L3 cache: 4096 KB, 4-way set associative, 64 bytes/line"},
61 	{0x47, "L3 cache: 8192 KB, 8-way set associative, 64 bytes/line"},
62 	{0x48, "L2 cache: 3072 KB, 12-way set associative, 64 bytes/line, unified on-die"},
63 	// 0x49 requires special handling, either 4M L3 (Xeon MP, 0F06; otherwise 4M L2
64 	{0x4A, "L3 cache: 6144 KB, 12-way set associative, 64 bytes/line"},
65 	{0x4B, "L3 cache: 8192 KB, 16-way set associative, 64 bytes/line"},
66 	{0x4C, "L3 cache: 12288 KB, 12-way set associative, 64 bytes/line"},
67 	{0x4D, "L3 cache: 16384 KB, 16-way set associative, 64 bytes/line"},
68 	{0x4E, "L2 cache: 6144 KB, 24-way set associative, 64 bytes/line"},
69 	{0x4F, "Inst TLB, 4K-bytes pages, 32 entries"},
70 	{0x50, "Inst TLB: 4K/4M/2M-bytes pages, fully associative, 64 entries"},
71 	{0x51, "Inst TLB: 4K/4M/2M-bytes pages, fully associative, 128 entries"},
72 	{0x52, "Inst TLB: 4K/4M/2M-bytes pages, fully associative, 256 entries"},
73 	{0x55, "Inst TLB: 2M/4M-bytes pages, fully associative, 7 entries"},
74 	{0x56, "L1 Data TLB: 4M-bytes pages, 4-way set associative, 16 entries"},
75 	{0x57, "L1 Data TLB: 4K-bytes pages, 4-way set associative, 16 entries"},
76 	{0x59, "L0 Data TLB, 4K-bytes pages, fully associative, 16 entries"},
77 	{0x5A, "Data TLB: 2M/4M-bytes pages, 4-way set associative, 32 entries"},
78 	{0x5B, "Data TLB: 4K/4M-bytes pages, fully associative, 64 entries"},
79 	{0x5C, "Data TLB: 4K/4M-bytes pages, fully associative, 128 entries"},
80 	{0x5D, "Data TLB: 4K/4M-bytes pages, fully associative, 256 entries"},
81 	{0x66, "L1 data cache: 8 KB, 4-way set associative, 64 bytes/line, sectored"},
82 	{0x67, "L1 data cache: 16 KB, 4-way set associative, 64 bytes/line, sectored"},
83 	{0x68, "L1 data cache: 32 KB, 4-way set associative, 64 bytes/line, sectored"},
84 	{0x70, "Inst trace cache: 12K µOPs, 8-way set associative"},
85 	{0x71, "Inst trace cache: 16K µOPs, 8-way set associative"},
86 	{0x72, "Inst trace cache: 32K µOPs, 8-way set associative"},
87 	{0x77, /* IA-64 */ "L1 inst cache: 16 KB, 4-way set associative, 64 bytes/line, sectored"},
88 	{0x79, "L2 cache: 128 KB, 8-way set associative, 64 bytes/line, dual-sectored"},
89 	{0x7A, "L2 cache: 256 KB, 8-way set associative, 64 bytes/line, dual-sectored"},
90 	{0x7B, "L2 cache: 512 KB, 8-way set associative, 64 bytes/line, dual-sectored"},
91 	{0x7C, "L2 cache: 1024 KB, 8-way set associative, 64 bytes/line, dual-sectored"},
92 	{0x7D, "L2 cache: 2048 KB, 8-way set associative, 64 bytes/line"},
93 	{0x7E, /* IA-64 */ "L2 cache: 256 KB, 8-way set associative, 128 bytes/line, sectored"},
94 	{0x7F, "L2 cache: 512 KB, 2-way set associative, 64 bytes/line"},
95 	{0x81, "L2 cache: 128 KB, 8-way set associative, 32 bytes/line"},
96 	{0x82, "L2 cache: 256 KB, 8-way set associative, 32 bytes/line"},
97 	{0x83, "L2 cache: 512 KB, 8-way set associative, 32 bytes/line"},
98 	{0x84, "L2 cache: 1024 KB, 8-way set associative, 32 bytes/line"},
99 	{0x85, "L2 cache: 2048 KB, 8-way set associative, 32 bytes/line"},
100 	{0x86, "L2 cache: 512 KB, 4-way set associative, 64 bytes/line"},
101 	{0x87, "L2 cache: 1024 KB, 8-way set associative, 64 bytes/line"},
102 	{0x88, /* IA-64 */ "L3 cache: 2 MB, 4-way set associative, 64 bytes/line"},
103 	{0x89, /* IA-64 */ "L3 cache: 4 MB, 4-way set associative, 64 bytes/line"},
104 	{0x8A, /* IA-64 */ "L3 cache: 8 MB, 4-way set associative, 64 bytes/line"},
105 	{0x8D, /* IA-64 */ "L3 cache: 3 MB, 12-way set associative, 128 bytes/line"},
106 	{0x90, /* IA-64 */ "Inst TLB: 4K-256Mbytes pages, fully associative, 64 entries"},
107 	{0x96, /* IA-64 */ "L1 data TLB: 4K-256M bytes pages, fully associative, 32 entries"},
108 	{0x9B, /* IA-64 */ "L2 data TLB: 4K-256M bytes pages, fully associative, 96 entries"},
109 //	{0x70, "Cyrix specific: Code and data TLB: 4k-bytes pages, 4-way set associative, 32 entries"},
110 //	{0x74, "Cyrix specific: ???"},
111 //	{0x77, "Cyrix specific: ???"},
112 	{0x80, /* Cyrix specific */ "L1 cache: 16 KB, 4-way set associative, 16 bytes/line"},
113 //	{0x82, "Cyrix specific: ???"},
114 //	{0x84, "Cyrix specific: ???"},
115 	{0xB0, "Inst TLB: 4K-bytes pages, 4-way set associative, 128 entries"},
116 	{0xB1, "Inst TLB: 2M-bytes pages, 4-way set associative, 8 entries OR 4M, 4-way, 4 entries"},
117 		// Intel doesn't give any details how to determine which of the two options is the case
118 		// as per Intel Application Note 485, November 2008.
119 	{0xB2, "Inst TLB: 4K-bytes pages, 4-way set associative, 64 entries"},
120 	{0xB3, "Data TLB: 4K-bytes pages, 4-way set associative, 128 entries"},
121 	{0xB4, "Data TLB: 4K-bytes pages, 4-way set associative, 256 entries"},
122 	{0xBA, "Data TLB, 4K-bytes pages, 4-way set associative, 64 entries"},
123 	{0xC0, "Data TLB, 4K-4M bytes pages, 4-way set associative, 8 entries"},
124 	{0xCA, "Shared 2nd-level TLB: 4K, 4-way set associative, 512 entries"},
125 	{0xD0, "L3 cache: 512 KB, 4-way set associative, 64-bytes/line"},
126 	{0xD1, "L3 cache: 1024 KB, 4-way set associative, 64-bytes/line"},
127 	{0xD2, "L3 cache: 2048 KB, 4-way set associative, 64-bytes/line"},
128 	{0xD6, "L3 cache: 1024 KB, 8-way set associative, 64-bytes/line"},
129 	{0xD7, "L3 cache: 2048 KB, 8-way set associative, 64-bytes/line"},
130 	{0xD8, "L3 cache: 4096 KB, 8-way set associative, 64-bytes/line"},
131 	{0xDC, "L3 cache: 2048 KB, 12-way set associative, 64-bytes/line"},
132 	{0xDD, "L3 cache: 4096 KB, 12-way set associative, 64-bytes/line"},
133 	{0xDE, "L3 cache: 8192 KB, 12-way set associative, 64-bytes/line"},
134 	{0xE2, "L3 cache: 2048 KB, 16-way set associative, 64-bytes/line"},
135 	{0xE3, "L3 cache: 4096 KB, 16-way set associative, 64-bytes/line"},
136 	{0xE4, "L3 cache: 8192 KB, 16-way set associative, 64-bytes/line"},
137 	{0xF0, "64-byte Prefetching"},
138 	{0xF1, "128-byte Prefetching"},
139 	{0, NULL}
140 };
141 
142 
143 static void
144 print_intel_cache_descriptors(enum cpu_types type, cpuid_info *info)
145 {
146 	uint8 cacheDescriptors[15];	// Max
147 
148 	int maxDesc = 0;
149 	int i = 0;
150 
151 	// put valid values into array
152 	if ((info->regs.eax & 0x80000000) == 0) {
153 		// eax is valid, include values
154 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
155 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
156 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
157 	} else {
158 		i += 3;
159 	}
160 	if ((info->regs.ebx & 0x80000000) == 0) {
161 		// ebx is valid, include values
162 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
163 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
164 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
165 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
166 	} else {
167 		i += 4;
168 	}
169 	if ((info->regs.edx & 0x80000000) == 0) {
170 		// edx is valid, include values
171 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
172 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
173 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
174 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
175 	} else {
176 		i += 4;
177 	}
178 	if ((info->regs.ecx & 0x80000000) == 0) {
179 		// ecx is valid, include values
180 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
181 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
182 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
183 		cacheDescriptors[maxDesc++] = info->eax_2.cache_descriptors[i++];
184 	}
185 
186 	putchar('\n');
187 
188 	for (int i = 0; i < maxDesc; i++) {
189 		// ignore NULL descriptors
190 		if (cacheDescriptors[i] == 0)
191 			continue;
192 
193 		int j;
194 		for (j = 0; sIntelCacheDescriptions[j].code; j++) {
195 			if (cacheDescriptors[i] == sIntelCacheDescriptions[j].code) {
196 				if (cacheDescriptors[i] == 0x40) {
197 					printf("\tNo integrated L%u cache\n",
198 						type >= B_CPU_INTEL_PENTIUM_IV
199 						&& (type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86 ?
200 							3 : 2);
201 				} else
202 					printf("\t%s\n", sIntelCacheDescriptions[j].description);
203 				break;
204 			}
205 		}
206 
207 		// Reached the end without finding a descriptor
208 		if (sIntelCacheDescriptions[j].code == 0)
209 			printf("\tUnknown cache descriptor 0x%02x\n", cacheDescriptors[i]);
210 	}
211 }
212 
213 #endif	// __INTEL__
214 
215 
216 static void
217 print_TLB(uint32 reg, const char *pages)
218 {
219 	int entries[2];
220 	int ways[2];
221 	const char *name[2] = { "Inst TLB", "Data TLB" };
222 
223 	entries[0] = (reg & 0xff);
224 	ways[0] = ((reg >> 8) & 0xff);
225 	entries[1] = ((reg >> 16) & 0xff);
226 	ways[1] = ((reg >> 24) & 0xff);
227 
228 	for (int num = 0; num < 2; num++) {
229 		printf("\t%s: %s%s%u entries, ", name[num],
230 			pages ? pages : "", pages ? " pages, " : "", entries[num]);
231 
232 		if (ways[num] == 0xff)
233 			printf("fully associative\n");
234 		else
235 			printf("%u-way set associative\n", ways[num]);
236 	}
237 }
238 
239 
240 static void
241 print_level2_cache(uint32 reg, const char *name)
242 {
243 	uint32 size = (reg >> 16) & 0xffff;
244 	uint32 ways = (reg >> 12) & 0xf;
245 	uint32 linesPerTag = (reg >> 8) & 0xf;		// intel does not define this
246 	uint32 lineSize = reg & 0xff;
247 
248 	printf("\t%s: %lu KB, ", name, size);
249 	if (ways == 0xf)
250 		printf("fully associative, ");
251 	else if (ways == 0x1)
252 		printf("direct-mapped, ");
253 	else
254 		printf("%lu-way set associative, ", 1UL << (ways / 2));
255 	printf("%lu lines/tag, %lu bytes/line\n", linesPerTag, lineSize);
256 }
257 
258 
259 static void
260 print_level1_cache(uint32 reg, const char *name)
261 {
262 	uint32 size = (reg >> 24) & 0xff;
263 	uint32 ways = (reg >> 16) & 0xff;
264 	uint32 linesPerTag = (reg >> 8) & 0xff;
265 	uint32 lineSize = reg & 0xff;
266 
267 	printf("\t%s: %lu KB, ", name, size);
268 	if (ways == 0xff)
269 		printf("fully associative, ");
270 	else
271 		printf("%lu-way set associative, ", ways);
272 	printf("%lu lines/tag, %lu bytes/line\n", linesPerTag, lineSize);
273 }
274 
275 
276 #ifdef __INTEL__
277 
278 static void
279 print_cache_desc(int32 cpu)
280 {
281 	cpuid_info info;
282 	get_cpuid(&info, 0x80000005, cpu);
283 
284 	putchar('\n');
285 
286 	if (info.regs.eax)
287 		print_TLB(info.regs.eax, info.regs.ebx ? "2M/4M-byte" : NULL);
288 	if (info.regs.ebx)
289 		print_TLB(info.regs.ebx, info.regs.eax ? "4K-byte" : NULL);
290 
291 	print_level1_cache(info.regs.ecx, "L1 inst cache");
292 	print_level1_cache(info.regs.edx, "L1 data cache");
293 
294 	get_cpuid(&info, 0x80000006, cpu);
295 	print_level2_cache(info.regs.ecx, "L2 cache");
296 }
297 
298 static void
299 print_intel_cache_desc(int32 cpu)
300 {
301 	cpuid_info info;
302 
303 	// A second parameters needs to be passed to CPUID which determines the cache level to query
304 	get_cpuid(&info, 0x00000004, cpu);
305 
306 	putchar('\n');
307 
308 	uint32 type = info.regs.eax & 0xf;
309 	uint32 level = (info.regs.eax & 0x70) >> 4;
310 	bool isFullyAssoc = info.regs.eax & 0x100;
311 
312 	uint32 lineSize = (info.regs.ebx & 0xfff) + 1;
313 	uint32 linesPerTag = ((info.regs.ebx & 0x3ff000) >> 12) + 1;
314 	uint32 ways = ((info.regs.ebx & 0xffc00000) >> 22) + 1;
315 
316 	uint32 sets = info.regs.ecx;
317 
318 	printf("\tL%ld ",level);
319 
320 	switch (type) {
321 		case 1: printf("Data cache "); break;
322 		case 2: printf("Inst cache "); break;
323 		case 3: printf("Unified cache "); break;
324 		default: break;
325 	}
326 
327 	if (isFullyAssoc)
328 		printf("fully associative, ");
329 	else
330 		printf("%lu-way set associative, ", ways);
331 	printf("%lu lines/tag, %lu bytes/line\n", linesPerTag, lineSize);
332 
333 	get_cpuid(&info, 0x80000006, cpu);
334 	print_level2_cache(sets, "L2 cache");
335 }
336 
337 
338 static void
339 print_transmeta_features(uint32 features)
340 {
341 	if (features & (1 << 16))
342 		printf("\t\tFCMOV\n");
343 }
344 
345 #endif	// __INTEL__
346 
347 
348 static void
349 print_amd_power_management_features(uint32 features)
350 {
351 	static const char *kFeatures[6] = {
352 		"TS", "FID", "VID", "TTP", "TM", "STC",
353 	};
354 	int32 found = 4;
355 
356 	printf("\tPower Management Features:");
357 
358 	for (int32 i = 0; i < 6; i++) {
359 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
360 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
361 			found++;
362 			if (found > 0 && (found % 16) == 0) {
363 				putchar('\n');
364 				found = 0;
365 			}
366 		}
367 	}
368 
369 	if (found != 0)
370 		putchar('\n');
371 }
372 
373 
374 static void
375 print_amd_features(uint32 features)
376 {
377 	static const char *kFeatures[32] = {
378 		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
379 		NULL, NULL, NULL, "SCE", NULL, NULL, NULL, NULL,
380 		NULL, NULL, NULL, NULL, "NX", NULL, "AMD-MMX", NULL,
381 		NULL, "FFXSTR", NULL, "RDTSCP", NULL, "64", "3DNow+", "3DNow!"
382 	};
383 	int32 found = 0;
384 
385 	for (int32 i = 0; i < 32; i++) {
386 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
387 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
388 			found++;
389 			if (found > 0 && (found % 16) == 0) {
390 				putchar('\n');
391 				found = 0;
392 			}
393 		}
394 	}
395 
396 	if (found != 0)
397 		putchar('\n');
398 }
399 
400 
401 static void
402 print_extended_features(uint32 features)
403 {
404 	static const char *kFeatures[32] = {
405 		"SSE3", "PCLMULDQ", "DTES64", "MONITOR", "DS-CPL", "VMX", "SMX", "EST",
406 		"TM2", "SSSE3", "CNTXT-ID", NULL, NULL, "CX16", "xTPR", "PDCM",
407 		NULL, NULL, "DCA", "SSE4.1", "SSE4.2", "x2APIC", "MOVEB", "POPCNT",
408 		NULL, "AES", "XSAVE", "OSXSAVE", NULL, NULL, NULL, NULL
409 	};
410 	int32 found = 0;
411 
412 	for (int32 i = 0; i < 32; i++) {
413 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
414 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
415 			found++;
416 			if (found > 0 && (found % 16) == 0) {
417 				putchar('\n');
418 				found = 0;
419 			}
420 		}
421 	}
422 
423 	if (found != 0)
424 		putchar('\n');
425 }
426 
427 
428 static void
429 print_features(uint32 features)
430 {
431 	static const char *kFeatures[32] = {
432 		"FPU", "VME", "DE", "PSE",
433 		"TSC", "MSR", "PAE", "MCE",
434 		"CX8", "APIC", NULL, "SEP",
435 		"MTRR", "PGE", "MCA", "CMOV",
436 		"PAT", "PSE36", "PSN", "CFLUSH",
437 		NULL, "DS", "ACPI", "MMX",
438 		"FXSTR", "SSE", "SSE2", "SS",
439 		"HTT", "TM", NULL, "PBE",
440 	};
441 	int32 found = 0;
442 
443 	for (int32 i = 0; i < 32; i++) {
444 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
445 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
446 			found++;
447 			if (found > 0 && (found % 16) == 0) {
448 				putchar('\n');
449 				found = 0;
450 			}
451 		}
452 	}
453 
454 	if (found != 0)
455 		putchar('\n');
456 }
457 
458 
459 #ifdef __INTEL__
460 
461 static void
462 print_processor_signature(system_info *sys_info, cpuid_info *info, const char *prefix)
463 {
464 
465 	if ((sys_info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_AMD_x86) {
466 		printf("\t%s%sype %lu, family %lu, model %u, stepping %lu, features 0x%08lx\n",
467 			prefix ? prefix : "", prefix && prefix[0] ? "t" : "T",
468 			info->eax_1.type,
469 			info->eax_1.family + (info->eax_1.family == 0xf ? info->eax_1.extended_family : 0),
470 			info->eax_1.model + (info->eax_1.model == 0xf ? info->eax_1.extended_model << 4 : 0),
471 			info->eax_1.stepping,
472 			info->eax_1.features);
473 	} else if ((sys_info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86) {
474 		// model calculation is different for INTEL
475 		printf("\t%s%sype %lu, family %lu, model %u, stepping %lu, features 0x%08lx\n",
476 			prefix ? prefix : "", prefix && prefix[0] ? "t" : "T",
477 			info->eax_1.type,
478 			info->eax_1.family + (info->eax_1.family == 0xf ? info->eax_1.extended_family : 0),
479 			info->eax_1.model + ((info->eax_1.family == 0xf || info->eax_1.family == 0x6) ? info->eax_1.extended_model << 4 : 0),
480 			info->eax_1.stepping,
481 			info->eax_1.features);
482 	}
483 }
484 
485 #endif	// __INTEL__
486 
487 
488 static void
489 dump_platform(system_info *info)
490 {
491 	printf("%s\n",
492 		info->platform_type == B_AT_CLONE_PLATFORM ? "IntelArchitecture" :
493 		info->platform_type == B_MAC_PLATFORM      ? "Macintosh" :
494 		info->platform_type == B_BEBOX_PLATFORM    ? "BeBox" : "unknown");
495 }
496 
497 
498 #ifdef __INTEL__
499 
500 static void
501 dump_cpu(system_info *info, int32 cpu)
502 {
503 	/* references:
504 	 *
505 	 * http://grafi.ii.pw.edu.pl/gbm/x86/cpuid.html
506 	 * http://www.sandpile.org/ia32/cpuid.htm
507 	 * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/TN13.pdf (Duron erratum)
508 	 */
509 
510 	cpuid_info baseInfo;
511 	if (get_cpuid(&baseInfo, 0, cpu) != B_OK) {
512 		// this CPU doesn't support cpuid
513 		return;
514 	}
515 
516 	int32 maxStandardFunction = baseInfo.eax_0.max_eax;
517 	if (maxStandardFunction >= 500)
518 		maxStandardFunction = 0; /* old Pentium sample chips has cpu signature here */
519 
520 	/* Extended cpuid */
521 
522 	cpuid_info cpuInfo;
523 	get_cpuid(&cpuInfo, 0x80000000, cpu);
524 
525 	// extended cpuid is only supported if max_eax is greater than the service id
526 	int32 maxExtendedFunction = 0;
527 	if (cpuInfo.eax_0.max_eax > 0x80000000)
528 		maxExtendedFunction = cpuInfo.eax_0.max_eax & 0xff;
529 
530 	if (maxExtendedFunction >=4 ) {
531 		char buffer[49];
532 		char *name = buffer;
533 
534 		memset(buffer, 0, sizeof(buffer));
535 
536 		for (int32 i = 0; i < 3; i++) {
537 			cpuid_info nameInfo;
538 			get_cpuid(&nameInfo, 0x80000002 + i, cpu);
539 
540 			memcpy(name, &nameInfo.regs.eax, 4);
541 			memcpy(name + 4, &nameInfo.regs.ebx, 4);
542 			memcpy(name + 8, &nameInfo.regs.ecx, 4);
543 			memcpy(name + 12, &nameInfo.regs.edx, 4);
544 			name += 16;
545 		}
546 
547 		// cut off leading spaces (names are right aligned)
548 		name = buffer;
549 		while (name[0] == ' ')
550 			name++;
551 
552 		// the BIOS may not have set the processor name
553 		if (name[0])
554 			printf("CPU #%ld: \"%s\"\n", cpu, name);
555 		else {
556 			// Intel CPUs don't seem to have the genuine vendor field
557 			printf("CPU #%ld: %.12s\n", cpu,
558 				(info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86 ?
559 					baseInfo.eax_0.vendor_id : cpuInfo.eax_0.vendor_id);
560 		}
561 	} else {
562 		printf("CPU #%ld: %.12s\n", cpu, baseInfo.eax_0.vendor_id);
563 		if (maxStandardFunction == 0)
564 			return;
565 	}
566 
567 	get_cpuid(&cpuInfo, 1, cpu);
568 	print_processor_signature(info, &cpuInfo, NULL);
569 	print_features(cpuInfo.eax_1.features);
570 
571 	if (maxStandardFunction >= 1) {
572 		/* Extended features */
573 		printf("\tExtended Intel: 0x%08lx\n", cpuInfo.eax_1.extended_features);
574 		print_extended_features(cpuInfo.eax_1.extended_features);
575 	}
576 
577 	/* Extended CPUID */
578 	if (maxExtendedFunction >= 1) {
579 		get_cpuid(&cpuInfo, 0x80000001, cpu);
580 		print_processor_signature(info, &cpuInfo, "Extended AMD: ");
581 
582 		if ((info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_AMD_x86
583 			|| (info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86) {
584 			print_amd_features(cpuInfo.regs.edx);
585 			if (maxExtendedFunction >= 7) {
586 				get_cpuid(&cpuInfo, 0x80000007, cpu);
587 				print_amd_power_management_features(cpuInfo.regs.edx);
588 			}
589 		} else if ((info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_TRANSMETA_x86)
590 			print_transmeta_features(cpuInfo.regs.edx);
591 	}
592 
593 	/* Cache/TLB descriptors */
594 	if (maxExtendedFunction >= 5) {
595 		if (!strncmp(baseInfo.eax_0.vendor_id, "CyrixInstead", 12)) {
596 			get_cpuid(&cpuInfo, 0x00000002, cpu);
597 			print_intel_cache_descriptors(info->cpu_type, &cpuInfo);
598 		} else if ((info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86) {
599 			// Intel does not support extended function 5 (but it does 6 hmm)
600 			print_intel_cache_desc(cpu);
601 		} else {
602 			print_cache_desc(cpu);
603 		}
604 	}
605 
606 	if (maxStandardFunction >= 2) {
607 		do {
608 			get_cpuid(&cpuInfo, 2, cpu);
609 
610 			if (cpuInfo.eax_2.call_num > 0)
611 				print_intel_cache_descriptors(info->cpu_type, &cpuInfo);
612 		} while (cpuInfo.eax_2.call_num > 1);
613 	}
614 
615 	/* Serial number */
616 	if (maxStandardFunction >= 3) {
617 		cpuid_info flagsInfo;
618 		get_cpuid(&flagsInfo, 1, cpu);
619 
620 		if (flagsInfo.eax_1.features & (1UL << 18)) {
621 			get_cpuid(&cpuInfo, 3, cpu);
622 			printf("Serial number: %04lx-%04lx-%04lx-%04lx-%04lx-%04lx\n",
623 				flagsInfo.eax_1.features >> 16, flagsInfo.eax_1.features & 0xffff,
624 				cpuInfo.regs.edx >> 16, cpuInfo.regs.edx & 0xffff,
625 				cpuInfo.regs.ecx >> 16, cpuInfo.regs.edx & 0xffff);
626 		}
627 	}
628 
629 	putchar('\n');
630 }
631 
632 #endif	// __INTEL__
633 
634 
635 static void
636 dump_cpus(system_info *info)
637 {
638 	const char *vendor = get_cpu_vendor_string(info->cpu_type);
639 	const char *model = get_cpu_model_string(info);
640 	char modelString[32];
641 
642 	if (model == NULL && vendor == NULL)
643 		model = "(Unknown)";
644 	else if (model == NULL) {
645 		model = modelString;
646 		snprintf(modelString, 32, "(Unknown %x)", info->cpu_type);
647 	}
648 
649 	printf("%ld %s%s%s, revision %04lx running at %LdMHz (ID: 0x%08lx 0x%08lx)\n\n",
650 		info->cpu_count,
651 		vendor ? vendor : "", vendor ? " " : "", model,
652 		info->cpu_revision,
653 		info->cpu_clock_speed / 1000000,
654 		info->id[0], info->id[1]);
655 
656 #ifdef __INTEL__
657 	for (int32 cpu = 0; cpu < info->cpu_count; cpu++)
658 		dump_cpu(info, cpu);
659 #endif	// __INTEL__
660 }
661 
662 
663 static void
664 dump_mem(system_info *info)
665 {
666 	printf("%10lu bytes free      (used/max %10lu / %10lu)\n",
667 		B_PAGE_SIZE * (uint32)(info->max_pages - info->used_pages),
668 		B_PAGE_SIZE * (uint32)info->used_pages,
669 		B_PAGE_SIZE * (uint32)info->max_pages);
670 	printf("                           (cached   %10lu)\n",
671 		B_PAGE_SIZE * (uint32)info->cached_pages);
672 }
673 
674 
675 static void
676 dump_sem(system_info *info)
677 {
678 	printf("%10ld semaphores free (used/max %10ld / %10ld)\n",
679 		info->max_sems - info->used_sems,
680 		info->used_sems, info->max_sems);
681 }
682 
683 
684 static void
685 dump_ports(system_info *info)
686 {
687 	printf("%10ld ports free      (used/max %10ld / %10ld)\n",
688 		info->max_ports - info->used_ports,
689 		info->used_ports, info->max_ports);
690 }
691 
692 
693 static void
694 dump_thread(system_info *info)
695 {
696 	printf("%10ld threads free    (used/max %10ld / %10ld)\n",
697 		info->max_threads - info->used_threads,
698 		info->used_threads, info->max_threads);
699 }
700 
701 
702 static void
703 dump_team(system_info *info)
704 {
705 	printf("%10ld teams free      (used/max %10ld / %10ld)\n",
706 		info->max_teams - info->used_teams,
707 		info->used_teams, info->max_teams);
708 }
709 
710 
711 static void
712 dump_kinfo(system_info *info)
713 {
714 	printf("Kernel name: %s built on: %s %s version 0x%Lx\n",
715 		info->kernel_name,
716 		info->kernel_build_date, info->kernel_build_time,
717 		info->kernel_version );
718 }
719 
720 
721 static void
722 dump_system_info(system_info *info)
723 {
724 	dump_kinfo(info);
725 	dump_cpus(info);
726 	dump_mem(info);
727 	dump_sem(info);
728 	dump_ports(info);
729 	dump_thread(info);
730 	dump_team(info);
731 }
732 
733 
734 int
735 main(int argc, char *argv[])
736 {
737 	if (!is_computer_on()) {
738 		printf("The computer is not on! No info available\n");
739 		exit(EXIT_FAILURE);
740 	}
741 
742 	system_info info;
743 	if (get_system_info(&info) != B_OK) {
744 		printf("Error getting system information!\n");
745 		return 1;
746 	}
747 
748 	if (argc <= 1) {
749 		dump_system_info(&info);
750 	} else {
751 		for (int i = 1; i < argc; i++) {
752 			const char *opt = argv[i];
753 			if (strncmp(opt, "-id", strlen(opt)) == 0) {
754 				/* note: the original also assumes this option on "sysinfo -" */
755 				printf("0x%.8lx 0x%.8lx\n", info.id[0], info.id[1]);
756 			} else if (strncmp(opt, "-cpu", strlen(opt)) == 0) {
757 				dump_cpus(&info);
758 			} else if (strncmp(opt, "-mem", strlen(opt)) == 0) {
759 				dump_mem(&info);
760 			} else if (strncmp(opt, "-semaphores", strlen(opt)) == 0) {
761 				dump_sem(&info);
762 			} else if (strncmp(opt, "-ports", strlen(opt)) == 0) {
763 				dump_ports(&info);
764 			} else if (strncmp(opt, "-threads", strlen(opt)) == 0) {
765 				dump_thread(&info);
766 			} else if (strncmp(opt, "-teams", strlen(opt)) == 0) {
767 				dump_team(&info);
768 			} else if (strncmp(opt, "-kinfo", strlen(opt)) == 0) {
769 				dump_kinfo(&info);
770 			} else if (strncmp(opt, "-platform", strlen(opt)) == 0) {
771 				dump_platform(&info);
772 			} else if (strncmp(opt, "-disable_cpu_sn", strlen(opt)) == 0) {
773 				/* TODO: printf("CPU #%d serial number:  old state: %s,  new state: %s\n", ... ); */
774 				fprintf(stderr, "Sorry, not yet implemented\n");
775 			} else {
776 				const char *name = strrchr(argv[0], '/');
777 				if (name == NULL)
778 					name = argv[0];
779 				else
780 					name++;
781 
782 				fprintf(stderr, "Usage:\n");
783 				fprintf(stderr, "  %s [-id|-cpu|-mem|-semaphore|-ports|-threads|-teams|-platform|-disable_cpu_sn|-kinfo]\n", name);
784 				return 0;
785 			}
786 		}
787 	}
788 	return 0;
789 }
790