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