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