xref: /haiku/src/bin/sysinfo.cpp (revision 16c83730262f1e4f0fc69d80744bb36dcfbbe3af)
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, "FMA", "CX16", "xTPR", "PDCM",
413 		NULL, "PCID", "DCA", "SSE4.1", "SSE4.2", "x2APIC", "MOVEB", "POPCNT",
414 		"TSC-DEADLINE", "AES", "XSAVE", "OSXSAVE", "AVX", "F16C", "RDRND",
415 		"HYPERVISOR"
416 	};
417 	int32 found = 0;
418 
419 	for (int32 i = 0; i < 32; i++) {
420 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
421 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
422 			found++;
423 			if (found > 0 && (found % 16) == 0) {
424 				putchar('\n');
425 				found = 0;
426 			}
427 		}
428 	}
429 
430 	if (found != 0)
431 		putchar('\n');
432 }
433 
434 
435 static void
436 print_features(uint32 features)
437 {
438 	static const char *kFeatures[32] = {
439 		"FPU", "VME", "DE", "PSE",
440 		"TSC", "MSR", "PAE", "MCE",
441 		"CX8", "APIC", NULL, "SEP",
442 		"MTRR", "PGE", "MCA", "CMOV",
443 		"PAT", "PSE36", "PSN", "CFLUSH",
444 		NULL, "DS", "ACPI", "MMX",
445 		"FXSTR", "SSE", "SSE2", "SS",
446 		"HTT", "TM", NULL, "PBE",
447 	};
448 	int32 found = 0;
449 
450 	for (int32 i = 0; i < 32; i++) {
451 		if ((features & (1UL << i)) && kFeatures[i] != NULL) {
452 			printf("%s%s", found == 0 ? "\t\t" : " ", kFeatures[i]);
453 			found++;
454 			if (found > 0 && (found % 16) == 0) {
455 				putchar('\n');
456 				found = 0;
457 			}
458 		}
459 	}
460 
461 	if (found != 0)
462 		putchar('\n');
463 }
464 
465 
466 #if defined(__INTEL__) || defined(__x86_64__)
467 
468 static void
469 print_processor_signature(system_info *sys_info, cpuid_info *info,
470 	const char *prefix)
471 {
472 
473 	if ((sys_info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_AMD_x86) {
474 
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 ((sys_info->cpu_type & B_CPU_x86_VENDOR_MASK)
486 			== B_CPU_INTEL_x86) {
487 		// model calculation is different for INTEL
488 		printf("\t%s%sype %" B_PRIu32 ", family %" B_PRIu32 ", model %" B_PRIu32
489 			", stepping %" B_PRIu32 ", features 0x%08" B_PRIx32 "\n",
490 			prefix ? prefix : "", prefix && prefix[0] ? "t" : "T",
491 			info->eax_1.type,
492 			info->eax_1.family + (info->eax_1.family == 0xf
493 				? info->eax_1.extended_family : 0),
494 			info->eax_1.model
495 				+ ((info->eax_1.family == 0xf || info->eax_1.family == 0x6)
496 					? info->eax_1.extended_model << 4 : 0),
497 			info->eax_1.stepping,
498 			info->eax_1.features);
499 	}
500 }
501 
502 #endif	// __INTEL__ || __x86_64__
503 
504 
505 static void
506 dump_platform(system_info *info)
507 {
508 	printf("%s\n",
509 		info->platform_type == B_AT_CLONE_PLATFORM ? "IntelArchitecture" :
510 		info->platform_type == B_MAC_PLATFORM      ? "Macintosh" :
511 		info->platform_type == B_BEBOX_PLATFORM    ? "BeBox" : "unknown");
512 }
513 
514 
515 #if defined(__INTEL__) || defined(__x86_64__)
516 
517 static void
518 dump_cpu(system_info *info, int32 cpu)
519 {
520 	// References:
521 	// http://grafi.ii.pw.edu.pl/gbm/x86/cpuid.html
522 	// http://www.sandpile.org/ia32/cpuid.htm
523 	// http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/TN13.pdf (Duron erratum)
524 
525 	cpuid_info baseInfo;
526 	if (get_cpuid(&baseInfo, 0, cpu) != B_OK) {
527 		// this CPU doesn't support cpuid
528 		return;
529 	}
530 
531 	int32 maxStandardFunction = baseInfo.eax_0.max_eax;
532 	if (maxStandardFunction >= 500) {
533 		// old Pentium sample chips has cpu signature here
534 		maxStandardFunction = 0;
535 	}
536 
537 	// Extended cpuid
538 
539 	cpuid_info cpuInfo;
540 	get_cpuid(&cpuInfo, 0x80000000, cpu);
541 
542 	// Extended cpuid is only supported if max_eax is greater than the
543 	// service id
544 	int32 maxExtendedFunction = 0;
545 	if (cpuInfo.eax_0.max_eax > 0x80000000)
546 		maxExtendedFunction = cpuInfo.eax_0.max_eax & 0xff;
547 
548 	if (maxExtendedFunction >=4 ) {
549 		char buffer[49];
550 		char *name = buffer;
551 
552 		memset(buffer, 0, sizeof(buffer));
553 
554 		for (int32 i = 0; i < 3; i++) {
555 			cpuid_info nameInfo;
556 			get_cpuid(&nameInfo, 0x80000002 + i, cpu);
557 
558 			memcpy(name, &nameInfo.regs.eax, 4);
559 			memcpy(name + 4, &nameInfo.regs.ebx, 4);
560 			memcpy(name + 8, &nameInfo.regs.ecx, 4);
561 			memcpy(name + 12, &nameInfo.regs.edx, 4);
562 			name += 16;
563 		}
564 
565 		// cut off leading spaces (names are right aligned)
566 		name = buffer;
567 		while (name[0] == ' ')
568 			name++;
569 
570 		// the BIOS may not have set the processor name
571 		if (name[0])
572 			printf("CPU #%" B_PRId32 ": \"%s\"\n", cpu, name);
573 		else {
574 			// Intel CPUs don't seem to have the genuine vendor field
575 			printf("CPU #%" B_PRId32 ": %.12s\n", cpu,
576 				(info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86 ?
577 					baseInfo.eax_0.vendor_id : cpuInfo.eax_0.vendor_id);
578 		}
579 	} else {
580 		printf("CPU #%" B_PRId32 ": %.12s\n", cpu, baseInfo.eax_0.vendor_id);
581 		if (maxStandardFunction == 0)
582 			return;
583 	}
584 
585 	get_cpuid(&cpuInfo, 1, cpu);
586 	print_processor_signature(info, &cpuInfo, NULL);
587 	print_features(cpuInfo.eax_1.features);
588 
589 	if (maxStandardFunction >= 1) {
590 		/* Extended features */
591 		printf("\tExtended Intel: 0x%08" B_PRIx32 "\n", cpuInfo.eax_1.extended_features);
592 		print_extended_features(cpuInfo.eax_1.extended_features);
593 	}
594 
595 	/* Extended CPUID */
596 	if (maxExtendedFunction >= 1) {
597 		get_cpuid(&cpuInfo, 0x80000001, cpu);
598 		print_processor_signature(info, &cpuInfo, "Extended AMD: ");
599 
600 		if ((info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_AMD_x86
601 			|| (info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86) {
602 			print_amd_features(cpuInfo.regs.edx);
603 			if (maxExtendedFunction >= 7) {
604 				get_cpuid(&cpuInfo, 0x80000007, cpu);
605 				print_amd_power_management_features(cpuInfo.regs.edx);
606 			}
607 		} else if ((info->cpu_type & B_CPU_x86_VENDOR_MASK)
608 				== B_CPU_TRANSMETA_x86)
609 			print_transmeta_features(cpuInfo.regs.edx);
610 	}
611 
612 	/* Cache/TLB descriptors */
613 	if (maxExtendedFunction >= 5) {
614 		if (!strncmp(baseInfo.eax_0.vendor_id, "CyrixInstead", 12)) {
615 			get_cpuid(&cpuInfo, 0x00000002, cpu);
616 			print_intel_cache_descriptors(info->cpu_type, &cpuInfo);
617 		} else if ((info->cpu_type & B_CPU_x86_VENDOR_MASK)
618 				== B_CPU_INTEL_x86) {
619 			// Intel does not support extended function 5 (but it does 6 hmm)
620 			print_intel_cache_desc(cpu);
621 		} else {
622 			print_cache_desc(cpu);
623 		}
624 	}
625 
626 	if (maxStandardFunction >= 2) {
627 		do {
628 			get_cpuid(&cpuInfo, 2, cpu);
629 
630 			if (cpuInfo.eax_2.call_num > 0)
631 				print_intel_cache_descriptors(info->cpu_type, &cpuInfo);
632 		} while (cpuInfo.eax_2.call_num > 1);
633 	}
634 
635 	/* Serial number */
636 	if (maxStandardFunction >= 3) {
637 		cpuid_info flagsInfo;
638 		get_cpuid(&flagsInfo, 1, cpu);
639 
640 		if (flagsInfo.eax_1.features & (1UL << 18)) {
641 			get_cpuid(&cpuInfo, 3, cpu);
642 			printf("Serial number: %04" B_PRIx32 "-%04" B_PRIx32 "-%04" B_PRIx32
643 				"-%04" B_PRIx32 "-%04" B_PRIx32 "-%04" B_PRIx32 "\n",
644 				flagsInfo.eax_1.features >> 16,
645 				flagsInfo.eax_1.features & 0xffff,
646 				cpuInfo.regs.edx >> 16, cpuInfo.regs.edx & 0xffff,
647 				cpuInfo.regs.ecx >> 16, cpuInfo.regs.edx & 0xffff);
648 		}
649 	}
650 
651 	putchar('\n');
652 }
653 
654 #endif	// __INTEL__ || __x86_64__
655 
656 
657 static void
658 dump_cpus(system_info *info)
659 {
660 	const char *vendor = get_cpu_vendor_string(info->cpu_type);
661 	const char *model = get_cpu_model_string(info);
662 	char modelString[32];
663 
664 	if (model == NULL && vendor == NULL)
665 		model = "(Unknown)";
666 	else if (model == NULL) {
667 		model = modelString;
668 		snprintf(modelString, 32, "(Unknown %x)", info->cpu_type);
669 	}
670 
671 	printf("%" B_PRId32 " %s%s%s, revision %04" B_PRIx32 " running at %"
672 		B_PRId64 "MHz (ID: 0x%08" B_PRIx32 " 0x%08" B_PRIx32 ")\n\n",
673 		info->cpu_count,
674 		vendor ? vendor : "", vendor ? " " : "", model,
675 		info->cpu_revision,
676 		info->cpu_clock_speed / 1000000,
677 		info->id[0], info->id[1]);
678 
679 #if defined(__INTEL__) || defined(__x86_64__)
680 	for (int32 cpu = 0; cpu < info->cpu_count; cpu++)
681 		dump_cpu(info, cpu);
682 #endif	// __INTEL__ || __x86_64__
683 }
684 
685 
686 static void
687 dump_mem(system_info *info)
688 {
689 	printf("%10" B_PRIu64 " bytes free      (used/max %10" B_PRIu64 " / %10"
690 		B_PRIu64 ")\n",
691 		B_PAGE_SIZE * (uint64)(info->max_pages - info->used_pages),
692 		B_PAGE_SIZE * (uint64)info->used_pages,
693 		B_PAGE_SIZE * (uint64)info->max_pages);
694 	printf("                           (cached   %10" B_PRIu64 ")\n",
695 		B_PAGE_SIZE * (uint64)info->cached_pages);
696 }
697 
698 
699 static void
700 dump_sem(system_info *info)
701 {
702 	printf("%10" B_PRId32 " semaphores free (used/max %10" B_PRId32 " / %10"
703 		B_PRId32 ")\n",
704 		info->max_sems - info->used_sems,
705 		info->used_sems, info->max_sems);
706 }
707 
708 
709 static void
710 dump_ports(system_info *info)
711 {
712 	printf("%10" B_PRId32 " ports free      (used/max %10" B_PRId32 " / %10"
713 		B_PRId32 ")\n",
714 		info->max_ports - info->used_ports,
715 		info->used_ports, info->max_ports);
716 }
717 
718 
719 static void
720 dump_thread(system_info *info)
721 {
722 	printf("%10" B_PRId32 " threads free    (used/max %10" B_PRId32 " / %10"
723 		B_PRId32 ")\n",
724 		info->max_threads - info->used_threads,
725 		info->used_threads, info->max_threads);
726 }
727 
728 
729 static void
730 dump_team(system_info *info)
731 {
732 	printf("%10" B_PRId32 " teams free      (used/max %10" B_PRId32 " / %10"
733 		B_PRId32 ")\n",
734 		info->max_teams - info->used_teams,
735 		info->used_teams, info->max_teams);
736 }
737 
738 
739 static void
740 dump_kinfo(system_info *info)
741 {
742 	printf("Kernel name: %s built on: %s %s version 0x%" B_PRIx64 "\n",
743 		info->kernel_name,
744 		info->kernel_build_date, info->kernel_build_time,
745 		info->kernel_version );
746 }
747 
748 
749 static void
750 dump_system_info(system_info *info)
751 {
752 	dump_kinfo(info);
753 	dump_cpus(info);
754 	dump_mem(info);
755 	dump_sem(info);
756 	dump_ports(info);
757 	dump_thread(info);
758 	dump_team(info);
759 }
760 
761 
762 int
763 main(int argc, char *argv[])
764 {
765 	if (!is_computer_on()) {
766 		printf("The computer is not on! No info available\n");
767 		exit(EXIT_FAILURE);
768 	}
769 
770 	system_info info;
771 	if (get_system_info(&info) != B_OK) {
772 		printf("Error getting system information!\n");
773 		return 1;
774 	}
775 
776 	if (argc <= 1) {
777 		dump_system_info(&info);
778 	} else {
779 		for (int i = 1; i < argc; i++) {
780 			const char *opt = argv[i];
781 			if (strncmp(opt, "-id", strlen(opt)) == 0) {
782 				/* note: the original also assumes this option on "sysinfo -" */
783 				printf("0x%.8" B_PRIx32 " 0x%.8" B_PRIx32 "\n", info.id[0],
784 					info.id[1]);
785 			} else if (strncmp(opt, "-cpu", strlen(opt)) == 0) {
786 				dump_cpus(&info);
787 			} else if (strncmp(opt, "-mem", strlen(opt)) == 0) {
788 				dump_mem(&info);
789 			} else if (strncmp(opt, "-semaphores", strlen(opt)) == 0) {
790 				dump_sem(&info);
791 			} else if (strncmp(opt, "-ports", strlen(opt)) == 0) {
792 				dump_ports(&info);
793 			} else if (strncmp(opt, "-threads", strlen(opt)) == 0) {
794 				dump_thread(&info);
795 			} else if (strncmp(opt, "-teams", strlen(opt)) == 0) {
796 				dump_team(&info);
797 			} else if (strncmp(opt, "-kinfo", strlen(opt)) == 0) {
798 				dump_kinfo(&info);
799 			} else if (strncmp(opt, "-platform", strlen(opt)) == 0) {
800 				dump_platform(&info);
801 			} else if (strncmp(opt, "-disable_cpu_sn", strlen(opt)) == 0) {
802 				/* TODO: printf("CPU #%d serial number:  old state: %s,  new state: %s\n", ... ); */
803 				fprintf(stderr, "Sorry, not yet implemented\n");
804 			} else {
805 				const char *name = strrchr(argv[0], '/');
806 				if (name == NULL)
807 					name = argv[0];
808 				else
809 					name++;
810 
811 				fprintf(stderr, "Usage:\n");
812 				fprintf(stderr, "  %s [-id|-cpu|-mem|-semaphore|-ports|-threads|-teams|-platform|-disable_cpu_sn|-kinfo]\n", name);
813 				return 0;
814 			}
815 		}
816 	}
817 	return 0;
818 }
819