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