xref: /haiku/src/bin/sysinfo.cpp (revision 8c78892580f132d10e624aef96f835df8d94bf19)
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 || vendor == B_CPU_VENDOR_HYGON) {
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 			|| vendor == B_CPU_VENDOR_HYGON) {
601 			// 0x80000001 EDX has overlap of 64,ED,SY/SE between amd and intel
602 			printf("\tExtended Features (0x80000001): 0x%08" B_PRIx32 "\n",
603 				cpuInfo.eax_1.features);
604 			print_features(kAMDExtFeatures, cpuInfo.regs.edx);
605 		}
606 
607 		if (vendor == B_CPU_VENDOR_AMD || vendor == B_CPU_VENDOR_HYGON) {
608 			if (maxExtendedFunction >= 7) {
609 				get_cpuid(&cpuInfo, 0x80000007, cpu);
610 				printf("\tExtended Features (0x80000007): 0x%08" B_PRIx32 "\n",
611 					cpuInfo.regs.edx);
612 				print_features(kAMDExtFeaturesPower, cpuInfo.regs.edx);
613 			}
614 			if (maxExtendedFunction >= 8) {
615 				get_cpuid(&cpuInfo, 0x80000008, cpu);
616 				printf("\tExtended Features (0x80000008): 0x%08" B_PRIx32 "\n",
617 					cpuInfo.regs.ebx);
618 				print_features(kAMDExtFeaturesTwo, cpuInfo.regs.ebx);
619 			}
620 		} else if (vendor == B_CPU_VENDOR_TRANSMETA)
621 			print_transmeta_features(cpuInfo.regs.edx);
622 	}
623 
624 	/* Cache/TLB descriptors */
625 	if (maxExtendedFunction >= 5) {
626 		if (!strncmp(baseInfo.eax_0.vendor_id, "CyrixInstead", 12)) {
627 			get_cpuid(&cpuInfo, 0x00000002, cpu);
628 			print_intel_cache_descriptors(vendor, model, &cpuInfo);
629 		} else if (vendor == B_CPU_VENDOR_INTEL) {
630 			// Intel does not support extended function 5 (but it does 6 hmm)
631 			print_intel_cache_desc(cpu);
632 		} else {
633 			print_cache_desc(cpu);
634 		}
635 	}
636 
637 	if (maxStandardFunction >= 2) {
638 		do {
639 			get_cpuid(&cpuInfo, 2, cpu);
640 
641 			if (cpuInfo.eax_2.call_num > 0)
642 				print_intel_cache_descriptors(vendor, model, &cpuInfo);
643 		} while (cpuInfo.eax_2.call_num > 1);
644 	}
645 
646 	/* Serial number */
647 	if (maxStandardFunction >= 3) {
648 		cpuid_info flagsInfo;
649 		get_cpuid(&flagsInfo, 1, cpu);
650 
651 		if (flagsInfo.eax_1.features & (1UL << 18)) {
652 			get_cpuid(&cpuInfo, 3, cpu);
653 			printf("Serial number: %04" B_PRIx32 "-%04" B_PRIx32 "-%04" B_PRIx32
654 				"-%04" B_PRIx32 "-%04" B_PRIx32 "-%04" B_PRIx32 "\n",
655 				flagsInfo.eax_1.features >> 16,
656 				flagsInfo.eax_1.features & 0xffff,
657 				cpuInfo.regs.edx >> 16, cpuInfo.regs.edx & 0xffff,
658 				cpuInfo.regs.ecx >> 16, cpuInfo.regs.edx & 0xffff);
659 		}
660 	}
661 
662 	putchar('\n');
663 }
664 
665 #endif	// __i386__ || __x86_64__
666 
667 
668 static void
669 dump_cpus(system_info *info)
670 {
671 	uint32 topologyNodeCount = 0;
672 	cpu_topology_node_info* topology = NULL;
673 	get_cpu_topology_info(NULL, &topologyNodeCount);
674 	if (topologyNodeCount != 0)
675 		topology = new cpu_topology_node_info[topologyNodeCount];
676 	get_cpu_topology_info(topology, &topologyNodeCount);
677 
678 	enum cpu_platform platform = B_CPU_UNKNOWN;
679 	enum cpu_vendor cpuVendor = B_CPU_VENDOR_UNKNOWN;
680 	uint32 cpuModel = 0;
681 	uint64 frequency = 0;
682 	for (uint32 i = 0; i < topologyNodeCount; i++) {
683 		switch (topology[i].type) {
684 			case B_TOPOLOGY_ROOT:
685 				platform = topology[i].data.root.platform;
686 				break;
687 
688 			case B_TOPOLOGY_PACKAGE:
689 				cpuVendor = topology[i].data.package.vendor;
690 				break;
691 
692 			case B_TOPOLOGY_CORE:
693 				cpuModel = topology[i].data.core.model;
694 				frequency = topology[i].data.core.default_frequency;
695 				break;
696 
697 			default:
698 				break;
699 		}
700 	}
701 	delete[] topology;
702 
703 	const char *vendor = get_cpu_vendor_string(cpuVendor);
704 	const char *model = get_cpu_model_string(platform, cpuVendor, cpuModel);
705 	char modelString[32];
706 
707 	if (model == NULL && vendor == NULL)
708 		model = "(Unknown)";
709 	else if (model == NULL) {
710 		model = modelString;
711 		snprintf(modelString, 32, "(Unknown %" B_PRIx32 ")", cpuModel);
712 	}
713 
714 	printf("%" B_PRId32 " %s%s%s, revision %04" B_PRIx32 " running at %"
715 		B_PRIu64 "MHz\n\n",
716 		info->cpu_count,
717 		vendor ? vendor : "", vendor ? " " : "", model,
718 		cpuModel,
719 		frequency / 1000000);
720 
721 #if defined(__i386__) || defined(__x86_64__)
722 	for (uint32 cpu = 0; cpu < info->cpu_count; cpu++)
723 		dump_cpu(cpuVendor, cpuModel, cpu);
724 #endif	// __i386__ || __x86_64__
725 }
726 
727 
728 static void
729 dump_mem(system_info *info)
730 {
731 	printf("%10" B_PRIu64 " bytes free      (used/max %10" B_PRIu64 " / %10"
732 		B_PRIu64 ")\n",
733 		B_PAGE_SIZE * (uint64)(info->max_pages - info->used_pages),
734 		B_PAGE_SIZE * (uint64)info->used_pages,
735 		B_PAGE_SIZE * (uint64)info->max_pages);
736 	printf("                           (cached   %10" B_PRIu64 ")\n",
737 		B_PAGE_SIZE * (uint64)info->cached_pages);
738 }
739 
740 
741 static void
742 dump_sem(system_info *info)
743 {
744 	printf("%10" B_PRId32 " semaphores free (used/max %10" B_PRId32 " / %10"
745 		B_PRId32 ")\n",
746 		info->max_sems - info->used_sems,
747 		info->used_sems, info->max_sems);
748 }
749 
750 
751 static void
752 dump_ports(system_info *info)
753 {
754 	printf("%10" B_PRId32 " ports free      (used/max %10" B_PRId32 " / %10"
755 		B_PRId32 ")\n",
756 		info->max_ports - info->used_ports,
757 		info->used_ports, info->max_ports);
758 }
759 
760 
761 static void
762 dump_thread(system_info *info)
763 {
764 	printf("%10" B_PRId32 " threads free    (used/max %10" B_PRId32 " / %10"
765 		B_PRId32 ")\n",
766 		info->max_threads - info->used_threads,
767 		info->used_threads, info->max_threads);
768 }
769 
770 
771 static void
772 dump_team(system_info *info)
773 {
774 	printf("%10" B_PRId32 " teams free      (used/max %10" B_PRId32 " / %10"
775 		B_PRId32 ")\n",
776 		info->max_teams - info->used_teams,
777 		info->used_teams, info->max_teams);
778 }
779 
780 
781 static void
782 dump_kinfo(system_info *info)
783 {
784 	printf("Kernel name: %s built on: %s %s version 0x%" B_PRIx64 "\n",
785 		info->kernel_name,
786 		info->kernel_build_date, info->kernel_build_time,
787 		info->kernel_version );
788 }
789 
790 
791 static void
792 dump_system_info(system_info *info)
793 {
794 	dump_kinfo(info);
795 	dump_cpus(info);
796 	dump_mem(info);
797 	dump_sem(info);
798 	dump_ports(info);
799 	dump_thread(info);
800 	dump_team(info);
801 }
802 
803 
804 int
805 main(int argc, char *argv[])
806 {
807 	if (!is_computer_on()) {
808 		printf("The computer is not on! No info available\n");
809 		exit(EXIT_FAILURE);
810 	}
811 
812 	system_info info;
813 	if (get_system_info(&info) != B_OK) {
814 		printf("Error getting system information!\n");
815 		return 1;
816 	}
817 
818 	if (argc <= 1) {
819 		dump_system_info(&info);
820 	} else {
821 		for (int i = 1; i < argc; i++) {
822 			const char *opt = argv[i];
823 			if (strncmp(opt, "-id", strlen(opt)) == 0) {
824 				/* note: the original also assumes this option on "sysinfo -" */
825 				printf("%#.8x %#.8x\n", 0,0);
826 			} else if (strncmp(opt, "-cpu", strlen(opt)) == 0) {
827 				dump_cpus(&info);
828 			} else if (strncmp(opt, "-mem", strlen(opt)) == 0) {
829 				dump_mem(&info);
830 			} else if (strncmp(opt, "-semaphores", strlen(opt)) == 0) {
831 				dump_sem(&info);
832 			} else if (strncmp(opt, "-ports", strlen(opt)) == 0) {
833 				dump_ports(&info);
834 			} else if (strncmp(opt, "-threads", strlen(opt)) == 0) {
835 				dump_thread(&info);
836 			} else if (strncmp(opt, "-teams", strlen(opt)) == 0) {
837 				dump_team(&info);
838 			} else if (strncmp(opt, "-kinfo", strlen(opt)) == 0) {
839 				dump_kinfo(&info);
840 			} else if (strncmp(opt, "-platform", strlen(opt)) == 0) {
841 				dump_platform(&info);
842 			} else if (strncmp(opt, "-disable_cpu_sn", strlen(opt)) == 0) {
843 				/* TODO: printf("CPU #%d serial number:  old state: %s,  new state: %s\n", ... ); */
844 				fprintf(stderr, "Sorry, not yet implemented\n");
845 			} else {
846 				const char *name = strrchr(argv[0], '/');
847 				if (name == NULL)
848 					name = argv[0];
849 				else
850 					name++;
851 
852 				fprintf(stderr, "Usage:\n");
853 				fprintf(stderr, "  %s [-id|-cpu|-mem|-semaphore|-ports|-threads|-teams|-platform|-disable_cpu_sn|-kinfo]\n", name);
854 				return 0;
855 			}
856 		}
857 	}
858 	return 0;
859 }
860