xref: /linux/tools/arch/x86/kcpuid/kcpuid.c (revision 55d0969c451159cff86949b38c39171cab962069)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 
4 #include <stdio.h>
5 #include <stdbool.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <getopt.h>
9 
10 #define ARRAY_SIZE(x)	(sizeof(x) / sizeof((x)[0]))
11 #define min(a, b)	(((a) < (b)) ? (a) : (b))
12 
13 typedef unsigned int u32;
14 typedef unsigned long long u64;
15 
16 char *def_csv = "/usr/share/misc/cpuid.csv";
17 char *user_csv;
18 
19 
20 /* Cover both single-bit flag and multiple-bits fields */
21 struct bits_desc {
22 	/* start and end bits */
23 	int start, end;
24 	/* 0 or 1 for 1-bit flag */
25 	int value;
26 	char simp[32];
27 	char detail[256];
28 };
29 
30 /* descriptor info for eax/ebx/ecx/edx */
31 struct reg_desc {
32 	/* number of valid entries */
33 	int nr;
34 	struct bits_desc descs[32];
35 };
36 
37 enum cpuid_reg {
38 	R_EAX = 0,
39 	R_EBX,
40 	R_ECX,
41 	R_EDX,
42 	NR_REGS
43 };
44 
45 static const char * const reg_names[] = {
46 	"EAX", "EBX", "ECX", "EDX",
47 };
48 
49 struct subleaf {
50 	u32 index;
51 	u32 sub;
52 	u32 eax, ebx, ecx, edx;
53 	struct reg_desc info[NR_REGS];
54 };
55 
56 /* Represent one leaf (basic or extended) */
57 struct cpuid_func {
58 	/*
59 	 * Array of subleafs for this func, if there is no subleafs
60 	 * then the leafs[0] is the main leaf
61 	 */
62 	struct subleaf *leafs;
63 	int nr;
64 };
65 
66 struct cpuid_range {
67 	/* array of main leafs */
68 	struct cpuid_func *funcs;
69 	/* number of valid leafs */
70 	int nr;
71 	bool is_ext;
72 };
73 
74 /*
75  * basic:  basic functions range: [0... ]
76  * ext:    extended functions range: [0x80000000... ]
77  */
78 struct cpuid_range *leafs_basic, *leafs_ext;
79 
80 static bool is_amd;
81 static bool show_details;
82 static bool show_raw;
83 static bool show_flags_only = true;
84 static u32 user_index = 0xFFFFFFFF;
85 static u32 user_sub = 0xFFFFFFFF;
86 static int flines;
87 
88 static inline void cpuid(u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
89 {
90 	/* ecx is often an input as well as an output. */
91 	asm volatile("cpuid"
92 	    : "=a" (*eax),
93 	      "=b" (*ebx),
94 	      "=c" (*ecx),
95 	      "=d" (*edx)
96 	    : "0" (*eax), "2" (*ecx));
97 }
98 
99 static inline bool has_subleafs(u32 f)
100 {
101 	u32 with_subleaves[] = {
102 		0x4,  0x7,  0xb,  0xd,  0xf,  0x10, 0x12,
103 		0x14, 0x17, 0x18, 0x1b, 0x1d, 0x1f, 0x23,
104 		0x8000001d, 0x80000020, 0x80000026,
105 	};
106 
107 	for (unsigned i = 0; i < ARRAY_SIZE(with_subleaves); i++)
108 		if (f == with_subleaves[i])
109 			return true;
110 
111 	return false;
112 }
113 
114 static void leaf_print_raw(struct subleaf *leaf)
115 {
116 	if (has_subleafs(leaf->index)) {
117 		if (leaf->sub == 0)
118 			printf("0x%08x: subleafs:\n", leaf->index);
119 
120 		printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
121 			leaf->sub, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
122 	} else {
123 		printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
124 			leaf->index, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
125 	}
126 }
127 
128 /* Return true is the input eax/ebx/ecx/edx are all zero */
129 static bool cpuid_store(struct cpuid_range *range, u32 f, int subleaf,
130 			u32 a, u32 b, u32 c, u32 d)
131 {
132 	struct cpuid_func *func;
133 	struct subleaf *leaf;
134 	int s = 0;
135 
136 	if (a == 0 && b == 0 && c == 0 && d == 0)
137 		return true;
138 
139 	/*
140 	 * Cut off vendor-prefix from CPUID function as we're using it as an
141 	 * index into ->funcs.
142 	 */
143 	func = &range->funcs[f & 0xffff];
144 
145 	if (!func->leafs) {
146 		func->leafs = malloc(sizeof(struct subleaf));
147 		if (!func->leafs)
148 			perror("malloc func leaf");
149 
150 		func->nr = 1;
151 	} else {
152 		s = func->nr;
153 		func->leafs = realloc(func->leafs, (s + 1) * sizeof(*leaf));
154 		if (!func->leafs)
155 			perror("realloc f->leafs");
156 
157 		func->nr++;
158 	}
159 
160 	leaf = &func->leafs[s];
161 
162 	leaf->index = f;
163 	leaf->sub = subleaf;
164 	leaf->eax = a;
165 	leaf->ebx = b;
166 	leaf->ecx = c;
167 	leaf->edx = d;
168 
169 	return false;
170 }
171 
172 static void raw_dump_range(struct cpuid_range *range)
173 {
174 	u32 f;
175 	int i;
176 
177 	printf("%s Leafs :\n", range->is_ext ? "Extended" : "Basic");
178 	printf("================\n");
179 
180 	for (f = 0; (int)f < range->nr; f++) {
181 		struct cpuid_func *func = &range->funcs[f];
182 		u32 index = f;
183 
184 		if (range->is_ext)
185 			index += 0x80000000;
186 
187 		/* Skip leaf without valid items */
188 		if (!func->nr)
189 			continue;
190 
191 		/* First item is the main leaf, followed by all subleafs */
192 		for (i = 0; i < func->nr; i++)
193 			leaf_print_raw(&func->leafs[i]);
194 	}
195 }
196 
197 #define MAX_SUBLEAF_NUM		64
198 struct cpuid_range *setup_cpuid_range(u32 input_eax)
199 {
200 	u32 max_func, idx_func, subleaf, max_subleaf;
201 	u32 eax, ebx, ecx, edx, f = input_eax;
202 	struct cpuid_range *range;
203 	bool allzero;
204 
205 	eax = input_eax;
206 	ebx = ecx = edx = 0;
207 
208 	cpuid(&eax, &ebx, &ecx, &edx);
209 	max_func = eax;
210 	idx_func = (max_func & 0xffff) + 1;
211 
212 	range = malloc(sizeof(struct cpuid_range));
213 	if (!range)
214 		perror("malloc range");
215 
216 	if (input_eax & 0x80000000)
217 		range->is_ext = true;
218 	else
219 		range->is_ext = false;
220 
221 	range->funcs = malloc(sizeof(struct cpuid_func) * idx_func);
222 	if (!range->funcs)
223 		perror("malloc range->funcs");
224 
225 	range->nr = idx_func;
226 	memset(range->funcs, 0, sizeof(struct cpuid_func) * idx_func);
227 
228 	for (; f <= max_func; f++) {
229 		eax = f;
230 		subleaf = ecx = 0;
231 
232 		cpuid(&eax, &ebx, &ecx, &edx);
233 		allzero = cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
234 		if (allzero)
235 			continue;
236 
237 		if (!has_subleafs(f))
238 			continue;
239 
240 		max_subleaf = MAX_SUBLEAF_NUM;
241 
242 		/*
243 		 * Some can provide the exact number of subleafs,
244 		 * others have to be tried (0xf)
245 		 */
246 		if (f == 0x7 || f == 0x14 || f == 0x17 || f == 0x18 || f == 0x1d)
247 			max_subleaf = min((eax & 0xff) + 1, max_subleaf);
248 		if (f == 0xb)
249 			max_subleaf = 2;
250 		if (f == 0x1f)
251 			max_subleaf = 6;
252 		if (f == 0x23)
253 			max_subleaf = 4;
254 		if (f == 0x80000020)
255 			max_subleaf = 4;
256 		if (f == 0x80000026)
257 			max_subleaf = 5;
258 
259 		for (subleaf = 1; subleaf < max_subleaf; subleaf++) {
260 			eax = f;
261 			ecx = subleaf;
262 
263 			cpuid(&eax, &ebx, &ecx, &edx);
264 			allzero = cpuid_store(range, f, subleaf,
265 						eax, ebx, ecx, edx);
266 			if (allzero)
267 				continue;
268 		}
269 
270 	}
271 
272 	return range;
273 }
274 
275 /*
276  * The basic row format for cpuid.csv  is
277  *	LEAF,SUBLEAF,register_name,bits,short name,long description
278  *
279  * like:
280  *	0,    0,  EAX,   31:0, max_basic_leafs,  Max input value for supported subleafs
281  *	1,    0,  ECX,      0, sse3,  Streaming SIMD Extensions 3(SSE3)
282  */
283 static int parse_line(char *line)
284 {
285 	char *str;
286 	int i;
287 	struct cpuid_range *range;
288 	struct cpuid_func *func;
289 	struct subleaf *leaf;
290 	u32 index;
291 	u32 sub;
292 	char buffer[512];
293 	char *buf;
294 	/*
295 	 * Tokens:
296 	 *  1. leaf
297 	 *  2. subleaf
298 	 *  3. register
299 	 *  4. bits
300 	 *  5. short name
301 	 *  6. long detail
302 	 */
303 	char *tokens[6];
304 	struct reg_desc *reg;
305 	struct bits_desc *bdesc;
306 	int reg_index;
307 	char *start, *end;
308 	u32 subleaf_start, subleaf_end;
309 	unsigned bit_start, bit_end;
310 
311 	/* Skip comments and NULL line */
312 	if (line[0] == '#' || line[0] == '\n')
313 		return 0;
314 
315 	strncpy(buffer, line, 511);
316 	buffer[511] = 0;
317 	str = buffer;
318 	for (i = 0; i < 5; i++) {
319 		tokens[i] = strtok(str, ",");
320 		if (!tokens[i])
321 			goto err_exit;
322 		str = NULL;
323 	}
324 	tokens[5] = strtok(str, "\n");
325 	if (!tokens[5])
326 		goto err_exit;
327 
328 	/* index/main-leaf */
329 	index = strtoull(tokens[0], NULL, 0);
330 
331 	if (index & 0x80000000)
332 		range = leafs_ext;
333 	else
334 		range = leafs_basic;
335 
336 	index &= 0x7FFFFFFF;
337 	/* Skip line parsing for non-existing indexes */
338 	if ((int)index >= range->nr)
339 		return -1;
340 
341 	func = &range->funcs[index];
342 
343 	/* Return if the index has no valid item on this platform */
344 	if (!func->nr)
345 		return 0;
346 
347 	/* subleaf */
348 	buf = tokens[1];
349 	end = strtok(buf, ":");
350 	start = strtok(NULL, ":");
351 	subleaf_end = strtoul(end, NULL, 0);
352 
353 	/* A subleaf range is given? */
354 	if (start) {
355 		subleaf_start = strtoul(start, NULL, 0);
356 		subleaf_end = min(subleaf_end, (u32)(func->nr - 1));
357 		if (subleaf_start > subleaf_end)
358 			return 0;
359 	} else {
360 		subleaf_start = subleaf_end;
361 		if (subleaf_start > (u32)(func->nr - 1))
362 			return 0;
363 	}
364 
365 	/* register */
366 	buf = tokens[2];
367 	if (strcasestr(buf, "EAX"))
368 		reg_index = R_EAX;
369 	else if (strcasestr(buf, "EBX"))
370 		reg_index = R_EBX;
371 	else if (strcasestr(buf, "ECX"))
372 		reg_index = R_ECX;
373 	else if (strcasestr(buf, "EDX"))
374 		reg_index = R_EDX;
375 	else
376 		goto err_exit;
377 
378 	/* bit flag or bits field */
379 	buf = tokens[3];
380 	end = strtok(buf, ":");
381 	start = strtok(NULL, ":");
382 	bit_end = strtoul(end, NULL, 0);
383 	bit_start = (start) ? strtoul(start, NULL, 0) : bit_end;
384 
385 	for (sub = subleaf_start; sub <= subleaf_end; sub++) {
386 		leaf = &func->leafs[sub];
387 		reg = &leaf->info[reg_index];
388 		bdesc = &reg->descs[reg->nr++];
389 
390 		bdesc->end = bit_end;
391 		bdesc->start = bit_start;
392 		strcpy(bdesc->simp, strtok(tokens[4], " \t"));
393 		strcpy(bdesc->detail, tokens[5]);
394 	}
395 	return 0;
396 
397 err_exit:
398 	printf("Warning: wrong line format:\n");
399 	printf("\tline[%d]: %s\n", flines, line);
400 	return -1;
401 }
402 
403 /* Parse csv file, and construct the array of all leafs and subleafs */
404 static void parse_text(void)
405 {
406 	FILE *file;
407 	char *filename, *line = NULL;
408 	size_t len = 0;
409 	int ret;
410 
411 	if (show_raw)
412 		return;
413 
414 	filename = user_csv ? user_csv : def_csv;
415 	file = fopen(filename, "r");
416 	if (!file) {
417 		/* Fallback to a csv in the same dir */
418 		file = fopen("./cpuid.csv", "r");
419 	}
420 
421 	if (!file) {
422 		printf("Fail to open '%s'\n", filename);
423 		return;
424 	}
425 
426 	while (1) {
427 		ret = getline(&line, &len, file);
428 		flines++;
429 		if (ret > 0)
430 			parse_line(line);
431 
432 		if (feof(file))
433 			break;
434 	}
435 
436 	fclose(file);
437 }
438 
439 
440 /* Decode every eax/ebx/ecx/edx */
441 static void decode_bits(u32 value, struct reg_desc *rdesc, enum cpuid_reg reg)
442 {
443 	struct bits_desc *bdesc;
444 	int start, end, i;
445 	u32 mask;
446 
447 	if (!rdesc->nr) {
448 		if (show_details)
449 			printf("\t %s: 0x%08x\n", reg_names[reg], value);
450 		return;
451 	}
452 
453 	for (i = 0; i < rdesc->nr; i++) {
454 		bdesc = &rdesc->descs[i];
455 
456 		start = bdesc->start;
457 		end = bdesc->end;
458 		if (start == end) {
459 			/* single bit flag */
460 			if (value & (1 << start))
461 				printf("\t%-20s %s%s%s\n",
462 					bdesc->simp,
463 				        show_flags_only ? "" : "\t\t\t",
464 					show_details ? "-" : "",
465 					show_details ? bdesc->detail : ""
466 					);
467 		} else {
468 			/* bit fields */
469 			if (show_flags_only)
470 				continue;
471 
472 			mask = ((u64)1 << (end - start + 1)) - 1;
473 			printf("\t%-20s\t: 0x%-8x\t%s%s\n",
474 					bdesc->simp,
475 					(value >> start) & mask,
476 					show_details ? "-" : "",
477 					show_details ? bdesc->detail : ""
478 					);
479 		}
480 	}
481 }
482 
483 static void show_leaf(struct subleaf *leaf)
484 {
485 	if (!leaf)
486 		return;
487 
488 	if (show_raw) {
489 		leaf_print_raw(leaf);
490 	} else {
491 		if (show_details)
492 			printf("CPUID_0x%x_ECX[0x%x]:\n",
493 				leaf->index, leaf->sub);
494 	}
495 
496 	decode_bits(leaf->eax, &leaf->info[R_EAX], R_EAX);
497 	decode_bits(leaf->ebx, &leaf->info[R_EBX], R_EBX);
498 	decode_bits(leaf->ecx, &leaf->info[R_ECX], R_ECX);
499 	decode_bits(leaf->edx, &leaf->info[R_EDX], R_EDX);
500 
501 	if (!show_raw && show_details)
502 		printf("\n");
503 }
504 
505 static void show_func(struct cpuid_func *func)
506 {
507 	int i;
508 
509 	if (!func)
510 		return;
511 
512 	for (i = 0; i < func->nr; i++)
513 		show_leaf(&func->leafs[i]);
514 }
515 
516 static void show_range(struct cpuid_range *range)
517 {
518 	int i;
519 
520 	for (i = 0; i < range->nr; i++)
521 		show_func(&range->funcs[i]);
522 }
523 
524 static inline struct cpuid_func *index_to_func(u32 index)
525 {
526 	struct cpuid_range *range;
527 	u32 func_idx;
528 
529 	range = (index & 0x80000000) ? leafs_ext : leafs_basic;
530 	func_idx = index & 0xffff;
531 
532 	if ((func_idx + 1) > (u32)range->nr) {
533 		printf("ERR: invalid input index (0x%x)\n", index);
534 		return NULL;
535 	}
536 	return &range->funcs[func_idx];
537 }
538 
539 static void show_info(void)
540 {
541 	struct cpuid_func *func;
542 
543 	if (show_raw) {
544 		/* Show all of the raw output of 'cpuid' instr */
545 		raw_dump_range(leafs_basic);
546 		raw_dump_range(leafs_ext);
547 		return;
548 	}
549 
550 	if (user_index != 0xFFFFFFFF) {
551 		/* Only show specific leaf/subleaf info */
552 		func = index_to_func(user_index);
553 		if (!func)
554 			return;
555 
556 		/* Dump the raw data also */
557 		show_raw = true;
558 
559 		if (user_sub != 0xFFFFFFFF) {
560 			if (user_sub + 1 <= (u32)func->nr) {
561 				show_leaf(&func->leafs[user_sub]);
562 				return;
563 			}
564 
565 			printf("ERR: invalid input subleaf (0x%x)\n", user_sub);
566 		}
567 
568 		show_func(func);
569 		return;
570 	}
571 
572 	printf("CPU features:\n=============\n\n");
573 	show_range(leafs_basic);
574 	show_range(leafs_ext);
575 }
576 
577 static void setup_platform_cpuid(void)
578 {
579 	 u32 eax, ebx, ecx, edx;
580 
581 	/* Check vendor */
582 	eax = ebx = ecx = edx = 0;
583 	cpuid(&eax, &ebx, &ecx, &edx);
584 
585 	/* "htuA" */
586 	if (ebx == 0x68747541)
587 		is_amd = true;
588 
589 	/* Setup leafs for the basic and extended range */
590 	leafs_basic = setup_cpuid_range(0x0);
591 	leafs_ext = setup_cpuid_range(0x80000000);
592 }
593 
594 static void usage(void)
595 {
596 	printf("kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n"
597 		"\t-a|--all             Show both bit flags and complex bit fields info\n"
598 		"\t-b|--bitflags        Show boolean flags only\n"
599 		"\t-d|--detail          Show details of the flag/fields (default)\n"
600 		"\t-f|--flags           Specify the cpuid csv file\n"
601 		"\t-h|--help            Show usage info\n"
602 		"\t-l|--leaf=index      Specify the leaf you want to check\n"
603 		"\t-r|--raw             Show raw cpuid data\n"
604 		"\t-s|--subleaf=sub     Specify the subleaf you want to check\n"
605 	);
606 }
607 
608 static struct option opts[] = {
609 	{ "all", no_argument, NULL, 'a' },		/* show both bit flags and fields */
610 	{ "bitflags", no_argument, NULL, 'b' },		/* only show bit flags, default on */
611 	{ "detail", no_argument, NULL, 'd' },		/* show detail descriptions */
612 	{ "file", required_argument, NULL, 'f' },	/* use user's cpuid file */
613 	{ "help", no_argument, NULL, 'h'},		/* show usage */
614 	{ "leaf", required_argument, NULL, 'l'},	/* only check a specific leaf */
615 	{ "raw", no_argument, NULL, 'r'},		/* show raw CPUID leaf data */
616 	{ "subleaf", required_argument, NULL, 's'},	/* check a specific subleaf */
617 	{ NULL, 0, NULL, 0 }
618 };
619 
620 static int parse_options(int argc, char *argv[])
621 {
622 	int c;
623 
624 	while ((c = getopt_long(argc, argv, "abdf:hl:rs:",
625 					opts, NULL)) != -1)
626 		switch (c) {
627 		case 'a':
628 			show_flags_only = false;
629 			break;
630 		case 'b':
631 			show_flags_only = true;
632 			break;
633 		case 'd':
634 			show_details = true;
635 			break;
636 		case 'f':
637 			user_csv = optarg;
638 			break;
639 		case 'h':
640 			usage();
641 			exit(1);
642 			break;
643 		case 'l':
644 			/* main leaf */
645 			user_index = strtoul(optarg, NULL, 0);
646 			break;
647 		case 'r':
648 			show_raw = true;
649 			break;
650 		case 's':
651 			/* subleaf */
652 			user_sub = strtoul(optarg, NULL, 0);
653 			break;
654 		default:
655 			printf("%s: Invalid option '%c'\n", argv[0], optopt);
656 			return -1;
657 	}
658 
659 	return 0;
660 }
661 
662 /*
663  * Do 4 things in turn:
664  * 1. Parse user options
665  * 2. Parse and store all the CPUID leaf data supported on this platform
666  * 2. Parse the csv file, while skipping leafs which are not available
667  *    on this platform
668  * 3. Print leafs info based on user options
669  */
670 int main(int argc, char *argv[])
671 {
672 	if (parse_options(argc, argv))
673 		return -1;
674 
675 	/* Setup the cpuid leafs of current platform */
676 	setup_platform_cpuid();
677 
678 	/* Read and parse the 'cpuid.csv' */
679 	parse_text();
680 
681 	show_info();
682 	return 0;
683 }
684