xref: /haiku/src/bin/sysinfo.cpp (revision 49d7857e32a5c34fe63a11e46a41a774aa1b2728)
1 /*
2  * Copyright 2004-2012, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2002, Carlos Hasan.
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;
246 		// intel does not define this
247 	uint32 lineSize = reg & 0xff;
248 
249 	printf("\t%s: %lu KB, ", name, size);
250 	if (ways == 0xf)
251 		printf("fully associative, ");
252 	else if (ways == 0x1)
253 		printf("direct-mapped, ");
254 	else
255 		printf("%lu-way set associative, ", 1UL << (ways / 2));
256 	printf("%lu lines/tag, %lu bytes/line\n", linesPerTag, lineSize);
257 }
258 
259 
260 static void
261 print_level1_cache(uint32 reg, const char *name)
262 {
263 	uint32 size = (reg >> 24) & 0xff;
264 	uint32 ways = (reg >> 16) & 0xff;
265 	uint32 linesPerTag = (reg >> 8) & 0xff;
266 	uint32 lineSize = reg & 0xff;
267 
268 	printf("\t%s: %lu KB, ", name, size);
269 	if (ways == 0xff)
270 		printf("fully associative, ");
271 	else
272 		printf("%lu-way set associative, ", ways);
273 	printf("%lu lines/tag, %lu bytes/line\n", linesPerTag, lineSize);
274 }
275 
276 
277 #ifdef __INTEL__
278 
279 static void
280 print_cache_desc(int32 cpu)
281 {
282 	cpuid_info info;
283 	get_cpuid(&info, 0x80000005, cpu);
284 
285 	putchar('\n');
286 
287 	if (info.regs.eax)
288 		print_TLB(info.regs.eax, info.regs.ebx ? "2M/4M-byte" : NULL);
289 	if (info.regs.ebx)
290 		print_TLB(info.regs.ebx, info.regs.eax ? "4K-byte" : NULL);
291 
292 	print_level1_cache(info.regs.ecx, "L1 inst cache");
293 	print_level1_cache(info.regs.edx, "L1 data cache");
294 
295 	get_cpuid(&info, 0x80000006, cpu);
296 	print_level2_cache(info.regs.ecx, "L2 cache");
297 }
298 
299 
300 static void
301 print_intel_cache_desc(int32 cpu)
302 {
303 	cpuid_info info;
304 
305 	// A second parameters needs to be passed to CPUID which determines the
306 	// cache level to query
307 	get_cpuid(&info, 0x00000004, cpu);
308 
309 	putchar('\n');
310 
311 	uint32 type = info.regs.eax & 0xf;
312 	uint32 level = (info.regs.eax & 0x70) >> 4;
313 	bool isFullyAssoc = info.regs.eax & 0x100;
314 
315 	uint32 lineSize = (info.regs.ebx & 0xfff) + 1;
316 	uint32 linesPerTag = ((info.regs.ebx & 0x3ff000) >> 12) + 1;
317 	uint32 ways = ((info.regs.ebx & 0xffc00000) >> 22) + 1;
318 
319 	uint32 sets = info.regs.ecx;
320 
321 	printf("\tL%ld ",level);
322 
323 	switch (type) {
324 		case 1: printf("Data cache "); break;
325 		case 2: printf("Inst cache "); break;
326 		case 3: printf("Unified cache "); break;
327 		default: break;
328 	}
329 
330 	if (isFullyAssoc)
331 		printf("fully associative, ");
332 	else
333 		printf("%lu-way set associative, ", ways);
334 	printf("%lu lines/tag, %lu bytes/line\n", linesPerTag, lineSize);
335 
336 	get_cpuid(&info, 0x80000006, cpu);
337 	print_level2_cache(sets, "L2 cache");
338 }
339 
340 
341 static void
342 print_transmeta_features(uint32 features)
343 {
344 	if (features & (1 << 16))
345 		printf("\t\tFCMOV\n");
346 }
347 
348 #endif	// __INTEL__
349 
350 
351 static void
352 print_amd_power_management_features(uint32 features)
353 {
354 	static const char *kFeatures[6] = {
355 		"TS", "FID", "VID", "TTP", "TM", "STC",
356 	};
357 	int32 found = 4;
358 
359 	printf("\tPower Management Features:");
360 
361 	for (int32 i = 0; i < 6; i++) {
362 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
363 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
364 			found++;
365 			if (found > 0 && (found % 16) == 0) {
366 				putchar('\n');
367 				found = 0;
368 			}
369 		}
370 	}
371 
372 	if (found != 0)
373 		putchar('\n');
374 }
375 
376 
377 static void
378 print_amd_features(uint32 features)
379 {
380 	static const char *kFeatures[32] = {
381 		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
382 		NULL, NULL, NULL, "SCE", NULL, NULL, NULL, NULL,
383 		NULL, NULL, NULL, NULL, "NX", NULL, "AMD-MMX", NULL,
384 		NULL, "FFXSTR", NULL, "RDTSCP", NULL, "64", "3DNow+", "3DNow!"
385 	};
386 	int32 found = 0;
387 
388 	for (int32 i = 0; i < 32; i++) {
389 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
390 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
391 			found++;
392 			if (found > 0 && (found % 16) == 0) {
393 				putchar('\n');
394 				found = 0;
395 			}
396 		}
397 	}
398 
399 	if (found != 0)
400 		putchar('\n');
401 }
402 
403 
404 static void
405 print_extended_features(uint32 features)
406 {
407 	static const char *kFeatures[32] = {
408 		"SSE3", "PCLMULDQ", "DTES64", "MONITOR", "DS-CPL", "VMX", "SMX", "EST",
409 		"TM2", "SSSE3", "CNTXT-ID", NULL, NULL, "CX16", "xTPR", "PDCM",
410 		NULL, NULL, "DCA", "SSE4.1", "SSE4.2", "x2APIC", "MOVEB", "POPCNT",
411 		NULL, "AES", "XSAVE", "OSXSAVE", NULL, NULL, NULL, NULL
412 	};
413 	int32 found = 0;
414 
415 	for (int32 i = 0; i < 32; i++) {
416 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
417 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
418 			found++;
419 			if (found > 0 && (found % 16) == 0) {
420 				putchar('\n');
421 				found = 0;
422 			}
423 		}
424 	}
425 
426 	if (found != 0)
427 		putchar('\n');
428 }
429 
430 
431 static void
432 print_features(uint32 features)
433 {
434 	static const char *kFeatures[32] = {
435 		"FPU", "VME", "DE", "PSE",
436 		"TSC", "MSR", "PAE", "MCE",
437 		"CX8", "APIC", NULL, "SEP",
438 		"MTRR", "PGE", "MCA", "CMOV",
439 		"PAT", "PSE36", "PSN", "CFLUSH",
440 		NULL, "DS", "ACPI", "MMX",
441 		"FXSTR", "SSE", "SSE2", "SS",
442 		"HTT", "TM", NULL, "PBE",
443 	};
444 	int32 found = 0;
445 
446 	for (int32 i = 0; i < 32; i++) {
447 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
448 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
449 			found++;
450 			if (found > 0 && (found % 16) == 0) {
451 				putchar('\n');
452 				found = 0;
453 			}
454 		}
455 	}
456 
457 	if (found != 0)
458 		putchar('\n');
459 }
460 
461 
462 #ifdef __INTEL__
463 
464 static void
465 print_processor_signature(system_info *sys_info, cpuid_info *info,
466 	const char *prefix)
467 {
468 
469 	if ((sys_info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_AMD_x86) {
470 		printf("\t%s%sype %lu, family %lu, model %lu, stepping %lu, features "
471 			"0x%08lx\n", prefix ? prefix : "", prefix && prefix[0] ? "t" : "T",
472 			info->eax_1.type,
473 			info->eax_1.family + (info->eax_1.family == 0xf
474 				? info->eax_1.extended_family : 0),
475 			info->eax_1.model + (info->eax_1.model == 0xf
476 				? info->eax_1.extended_model << 4 : 0),
477 			info->eax_1.stepping,
478 			info->eax_1.features);
479 	} else if ((sys_info->cpu_type & B_CPU_x86_VENDOR_MASK)
480 			== B_CPU_INTEL_x86) {
481 		// model calculation is different for INTEL
482 		printf("\t%s%sype %lu, family %lu, model %lu, stepping %lu, features "
483 			"0x%08lx\n", prefix ? prefix : "", prefix && prefix[0] ? "t" : "T",
484 			info->eax_1.type,
485 			info->eax_1.family + (info->eax_1.family == 0xf
486 				? info->eax_1.extended_family : 0),
487 			info->eax_1.model
488 				+ ((info->eax_1.family == 0xf || info->eax_1.family == 0x6)
489 					? info->eax_1.extended_model << 4 : 0),
490 			info->eax_1.stepping,
491 			info->eax_1.features);
492 	}
493 }
494 
495 #endif	// __INTEL__
496 
497 
498 static void
499 dump_platform(system_info *info)
500 {
501 	printf("%s\n",
502 		info->platform_type == B_AT_CLONE_PLATFORM ? "IntelArchitecture" :
503 		info->platform_type == B_MAC_PLATFORM      ? "Macintosh" :
504 		info->platform_type == B_BEBOX_PLATFORM    ? "BeBox" : "unknown");
505 }
506 
507 
508 #ifdef __INTEL__
509 
510 static void
511 dump_cpu(system_info *info, int32 cpu)
512 {
513 	// References:
514 	// http://grafi.ii.pw.edu.pl/gbm/x86/cpuid.html
515 	// http://www.sandpile.org/ia32/cpuid.htm
516 	// http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/TN13.pdf (Duron erratum)
517 
518 	cpuid_info baseInfo;
519 	if (get_cpuid(&baseInfo, 0, cpu) != B_OK) {
520 		// this CPU doesn't support cpuid
521 		return;
522 	}
523 
524 	int32 maxStandardFunction = baseInfo.eax_0.max_eax;
525 	if (maxStandardFunction >= 500) {
526 		// old Pentium sample chips has cpu signature here
527 		maxStandardFunction = 0;
528 	}
529 
530 	// Extended cpuid
531 
532 	cpuid_info cpuInfo;
533 	get_cpuid(&cpuInfo, 0x80000000, cpu);
534 
535 	// Extended cpuid is only supported if max_eax is greater than the
536 	// service id
537 	int32 maxExtendedFunction = 0;
538 	if (cpuInfo.eax_0.max_eax > 0x80000000)
539 		maxExtendedFunction = cpuInfo.eax_0.max_eax & 0xff;
540 
541 	if (maxExtendedFunction >=4 ) {
542 		char buffer[49];
543 		char *name = buffer;
544 
545 		memset(buffer, 0, sizeof(buffer));
546 
547 		for (int32 i = 0; i < 3; i++) {
548 			cpuid_info nameInfo;
549 			get_cpuid(&nameInfo, 0x80000002 + i, cpu);
550 
551 			memcpy(name, &nameInfo.regs.eax, 4);
552 			memcpy(name + 4, &nameInfo.regs.ebx, 4);
553 			memcpy(name + 8, &nameInfo.regs.ecx, 4);
554 			memcpy(name + 12, &nameInfo.regs.edx, 4);
555 			name += 16;
556 		}
557 
558 		// cut off leading spaces (names are right aligned)
559 		name = buffer;
560 		while (name[0] == ' ')
561 			name++;
562 
563 		// the BIOS may not have set the processor name
564 		if (name[0])
565 			printf("CPU #%ld: \"%s\"\n", cpu, name);
566 		else {
567 			// Intel CPUs don't seem to have the genuine vendor field
568 			printf("CPU #%ld: %.12s\n", cpu,
569 				(info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86 ?
570 					baseInfo.eax_0.vendor_id : cpuInfo.eax_0.vendor_id);
571 		}
572 	} else {
573 		printf("CPU #%ld: %.12s\n", cpu, baseInfo.eax_0.vendor_id);
574 		if (maxStandardFunction == 0)
575 			return;
576 	}
577 
578 	get_cpuid(&cpuInfo, 1, cpu);
579 	print_processor_signature(info, &cpuInfo, NULL);
580 	print_features(cpuInfo.eax_1.features);
581 
582 	if (maxStandardFunction >= 1) {
583 		/* Extended features */
584 		printf("\tExtended Intel: 0x%08lx\n", cpuInfo.eax_1.extended_features);
585 		print_extended_features(cpuInfo.eax_1.extended_features);
586 	}
587 
588 	/* Extended CPUID */
589 	if (maxExtendedFunction >= 1) {
590 		get_cpuid(&cpuInfo, 0x80000001, cpu);
591 		print_processor_signature(info, &cpuInfo, "Extended AMD: ");
592 
593 		if ((info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_AMD_x86
594 			|| (info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86) {
595 			print_amd_features(cpuInfo.regs.edx);
596 			if (maxExtendedFunction >= 7) {
597 				get_cpuid(&cpuInfo, 0x80000007, cpu);
598 				print_amd_power_management_features(cpuInfo.regs.edx);
599 			}
600 		} else if ((info->cpu_type & B_CPU_x86_VENDOR_MASK)
601 				== B_CPU_TRANSMETA_x86)
602 			print_transmeta_features(cpuInfo.regs.edx);
603 	}
604 
605 	/* Cache/TLB descriptors */
606 	if (maxExtendedFunction >= 5) {
607 		if (!strncmp(baseInfo.eax_0.vendor_id, "CyrixInstead", 12)) {
608 			get_cpuid(&cpuInfo, 0x00000002, cpu);
609 			print_intel_cache_descriptors(info->cpu_type, &cpuInfo);
610 		} else if ((info->cpu_type & B_CPU_x86_VENDOR_MASK)
611 				== B_CPU_INTEL_x86) {
612 			// Intel does not support extended function 5 (but it does 6 hmm)
613 			print_intel_cache_desc(cpu);
614 		} else {
615 			print_cache_desc(cpu);
616 		}
617 	}
618 
619 	if (maxStandardFunction >= 2) {
620 		do {
621 			get_cpuid(&cpuInfo, 2, cpu);
622 
623 			if (cpuInfo.eax_2.call_num > 0)
624 				print_intel_cache_descriptors(info->cpu_type, &cpuInfo);
625 		} while (cpuInfo.eax_2.call_num > 1);
626 	}
627 
628 	/* Serial number */
629 	if (maxStandardFunction >= 3) {
630 		cpuid_info flagsInfo;
631 		get_cpuid(&flagsInfo, 1, cpu);
632 
633 		if (flagsInfo.eax_1.features & (1UL << 18)) {
634 			get_cpuid(&cpuInfo, 3, cpu);
635 			printf("Serial number: %04lx-%04lx-%04lx-%04lx-%04lx-%04lx\n",
636 				flagsInfo.eax_1.features >> 16,
637 				flagsInfo.eax_1.features & 0xffff,
638 				cpuInfo.regs.edx >> 16, cpuInfo.regs.edx & 0xffff,
639 				cpuInfo.regs.ecx >> 16, cpuInfo.regs.edx & 0xffff);
640 		}
641 	}
642 
643 	putchar('\n');
644 }
645 
646 #endif	// __INTEL__
647 
648 
649 static void
650 dump_cpus(system_info *info)
651 {
652 	const char *vendor = get_cpu_vendor_string(info->cpu_type);
653 	const char *model = get_cpu_model_string(info);
654 	char modelString[32];
655 
656 	if (model == NULL && vendor == NULL)
657 		model = "(Unknown)";
658 	else if (model == NULL) {
659 		model = modelString;
660 		snprintf(modelString, 32, "(Unknown %x)", info->cpu_type);
661 	}
662 
663 	printf("%ld %s%s%s, revision %04lx running at %LdMHz (ID: 0x%08lx "
664 		"0x%08lx)\n\n", info->cpu_count,
665 		vendor ? vendor : "", vendor ? " " : "", model,
666 		info->cpu_revision,
667 		info->cpu_clock_speed / 1000000,
668 		info->id[0], info->id[1]);
669 
670 #ifdef __INTEL__
671 	for (int32 cpu = 0; cpu < info->cpu_count; cpu++)
672 		dump_cpu(info, cpu);
673 #endif	// __INTEL__
674 }
675 
676 
677 static void
678 dump_mem(system_info *info)
679 {
680 	printf("%10lu bytes free      (used/max %10lu / %10lu)\n",
681 		B_PAGE_SIZE * (uint32)(info->max_pages - info->used_pages),
682 		B_PAGE_SIZE * (uint32)info->used_pages,
683 		B_PAGE_SIZE * (uint32)info->max_pages);
684 	printf("                           (cached   %10lu)\n",
685 		B_PAGE_SIZE * (uint32)info->cached_pages);
686 }
687 
688 
689 static void
690 dump_sem(system_info *info)
691 {
692 	printf("%10ld semaphores free (used/max %10ld / %10ld)\n",
693 		info->max_sems - info->used_sems,
694 		info->used_sems, info->max_sems);
695 }
696 
697 
698 static void
699 dump_ports(system_info *info)
700 {
701 	printf("%10ld ports free      (used/max %10ld / %10ld)\n",
702 		info->max_ports - info->used_ports,
703 		info->used_ports, info->max_ports);
704 }
705 
706 
707 static void
708 dump_thread(system_info *info)
709 {
710 	printf("%10ld threads free    (used/max %10ld / %10ld)\n",
711 		info->max_threads - info->used_threads,
712 		info->used_threads, info->max_threads);
713 }
714 
715 
716 static void
717 dump_team(system_info *info)
718 {
719 	printf("%10ld teams free      (used/max %10ld / %10ld)\n",
720 		info->max_teams - info->used_teams,
721 		info->used_teams, info->max_teams);
722 }
723 
724 
725 static void
726 dump_kinfo(system_info *info)
727 {
728 	printf("Kernel name: %s built on: %s %s version 0x%Lx\n",
729 		info->kernel_name,
730 		info->kernel_build_date, info->kernel_build_time,
731 		info->kernel_version );
732 }
733 
734 
735 static void
736 dump_system_info(system_info *info)
737 {
738 	dump_kinfo(info);
739 	dump_cpus(info);
740 	dump_mem(info);
741 	dump_sem(info);
742 	dump_ports(info);
743 	dump_thread(info);
744 	dump_team(info);
745 }
746 
747 
748 int
749 main(int argc, char *argv[])
750 {
751 	if (!is_computer_on()) {
752 		printf("The computer is not on! No info available\n");
753 		exit(EXIT_FAILURE);
754 	}
755 
756 	system_info info;
757 	if (get_system_info(&info) != B_OK) {
758 		printf("Error getting system information!\n");
759 		return 1;
760 	}
761 
762 	if (argc <= 1) {
763 		dump_system_info(&info);
764 	} else {
765 		for (int i = 1; i < argc; i++) {
766 			const char *opt = argv[i];
767 			if (strncmp(opt, "-id", strlen(opt)) == 0) {
768 				/* note: the original also assumes this option on "sysinfo -" */
769 				printf("0x%.8lx 0x%.8lx\n", info.id[0], info.id[1]);
770 			} else if (strncmp(opt, "-cpu", strlen(opt)) == 0) {
771 				dump_cpus(&info);
772 			} else if (strncmp(opt, "-mem", strlen(opt)) == 0) {
773 				dump_mem(&info);
774 			} else if (strncmp(opt, "-semaphores", strlen(opt)) == 0) {
775 				dump_sem(&info);
776 			} else if (strncmp(opt, "-ports", strlen(opt)) == 0) {
777 				dump_ports(&info);
778 			} else if (strncmp(opt, "-threads", strlen(opt)) == 0) {
779 				dump_thread(&info);
780 			} else if (strncmp(opt, "-teams", strlen(opt)) == 0) {
781 				dump_team(&info);
782 			} else if (strncmp(opt, "-kinfo", strlen(opt)) == 0) {
783 				dump_kinfo(&info);
784 			} else if (strncmp(opt, "-platform", strlen(opt)) == 0) {
785 				dump_platform(&info);
786 			} else if (strncmp(opt, "-disable_cpu_sn", strlen(opt)) == 0) {
787 				/* TODO: printf("CPU #%d serial number:  old state: %s,  new state: %s\n", ... ); */
788 				fprintf(stderr, "Sorry, not yet implemented\n");
789 			} else {
790 				const char *name = strrchr(argv[0], '/');
791 				if (name == NULL)
792 					name = argv[0];
793 				else
794 					name++;
795 
796 				fprintf(stderr, "Usage:\n");
797 				fprintf(stderr, "  %s [-id|-cpu|-mem|-semaphore|-ports|-threads|-teams|-platform|-disable_cpu_sn|-kinfo]\n", name);
798 				return 0;
799 			}
800 		}
801 	}
802 	return 0;
803 }
804