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