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