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