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