File Index
Files and directories
Sign in
FILE

kcpuid.c

File preview with syntax highlighting.

linux-6.19.14/tools/arch/x86/kcpuid/kcpuid.c C
  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
 16typedef unsigned int u32;
 17typedef unsigned long long u64;
 18
 19char *def_csv = "/usr/share/misc/cpuid.csv";
 20char *user_csv;
 21
 22
 23/* Cover both single-bit flag and multiple-bits fields */
 24struct 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 */
 34struct reg_desc {
 35	/* number of valid entries */
 36	int nr;
 37	struct bits_desc descs[32];
 38};
 39
 40enum cpuid_reg {
 41	R_EAX = 0,
 42	R_EBX,
 43	R_ECX,
 44	R_EDX,
 45	NR_REGS
 46};
 47
 48static const char * const reg_names[] = {
 49	"EAX", "EBX", "ECX", "EDX",
 50};
 51
 52struct 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) */
 60struct 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
 69enum 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
 79struct 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
 87static struct cpuid_range ranges[] = {
 88	{	.index		= RANGE_STD,	},
 89	{	.index		= RANGE_EXT,	},
 90	{	.index		= RANGE_TSM,	},
 91	{	.index		= RANGE_CTR,	},
 92};
 93
 94static 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
113struct 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
127static bool show_details;
128static bool show_raw;
129static bool show_flags_only = true;
130static u32 user_index = 0xFFFFFFFF;
131static u32 user_sub = 0xFFFFFFFF;
132static 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
145static 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
160static 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 */
175static 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
218static 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
238void 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 */
313static 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
423err_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 */
429static 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
462static 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
498static 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
504static 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
518static 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
524static 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
530static 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
542static 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
582static 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
596static 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
608static 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 */
653int 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}
~/ Read-only file preview.