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