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
22struct 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 */
170static 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 */
182static 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 */
192static 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 */
201static 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 */
210static 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
218static void
219print_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
294static void
295print_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
318static void
319print_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
339static void
340print_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
359static void
360print_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
380static void
381print_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
421static void
422print_transmeta_features(uint32 features)
423{
424	if (features & (1 << 16))
425		printf("\t\tFCMOV\n");
426}
427
428#endif	// __i386__ || __x86_64__
429
430
431static void
432print_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
454static void
455print_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
487static void
488dump_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
515static void
516dump_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
676static void
677dump_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
736static void
737dump_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
749static void
750dump_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
759static void
760dump_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
769static void
770dump_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
779static void
780dump_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
789static void
790dump_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
799static void
800dump_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
812int
813main(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