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