162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#define _GNU_SOURCE
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <stdio.h>
562306a36Sopenharmony_ci#include <stdbool.h>
662306a36Sopenharmony_ci#include <stdlib.h>
762306a36Sopenharmony_ci#include <string.h>
862306a36Sopenharmony_ci#include <getopt.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_citypedef unsigned int u32;
1362306a36Sopenharmony_citypedef unsigned long long u64;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cichar *def_csv = "/usr/share/misc/cpuid.csv";
1662306a36Sopenharmony_cichar *user_csv;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* Cover both single-bit flag and multiple-bits fields */
2062306a36Sopenharmony_cistruct bits_desc {
2162306a36Sopenharmony_ci	/* start and end bits */
2262306a36Sopenharmony_ci	int start, end;
2362306a36Sopenharmony_ci	/* 0 or 1 for 1-bit flag */
2462306a36Sopenharmony_ci	int value;
2562306a36Sopenharmony_ci	char simp[32];
2662306a36Sopenharmony_ci	char detail[256];
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* descriptor info for eax/ebx/ecx/edx */
3062306a36Sopenharmony_cistruct reg_desc {
3162306a36Sopenharmony_ci	/* number of valid entries */
3262306a36Sopenharmony_ci	int nr;
3362306a36Sopenharmony_ci	struct bits_desc descs[32];
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cienum cpuid_reg {
3762306a36Sopenharmony_ci	R_EAX = 0,
3862306a36Sopenharmony_ci	R_EBX,
3962306a36Sopenharmony_ci	R_ECX,
4062306a36Sopenharmony_ci	R_EDX,
4162306a36Sopenharmony_ci	NR_REGS
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const char * const reg_names[] = {
4562306a36Sopenharmony_ci	"EAX", "EBX", "ECX", "EDX",
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct subleaf {
4962306a36Sopenharmony_ci	u32 index;
5062306a36Sopenharmony_ci	u32 sub;
5162306a36Sopenharmony_ci	u32 eax, ebx, ecx, edx;
5262306a36Sopenharmony_ci	struct reg_desc info[NR_REGS];
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* Represent one leaf (basic or extended) */
5662306a36Sopenharmony_cistruct cpuid_func {
5762306a36Sopenharmony_ci	/*
5862306a36Sopenharmony_ci	 * Array of subleafs for this func, if there is no subleafs
5962306a36Sopenharmony_ci	 * then the leafs[0] is the main leaf
6062306a36Sopenharmony_ci	 */
6162306a36Sopenharmony_ci	struct subleaf *leafs;
6262306a36Sopenharmony_ci	int nr;
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct cpuid_range {
6662306a36Sopenharmony_ci	/* array of main leafs */
6762306a36Sopenharmony_ci	struct cpuid_func *funcs;
6862306a36Sopenharmony_ci	/* number of valid leafs */
6962306a36Sopenharmony_ci	int nr;
7062306a36Sopenharmony_ci	bool is_ext;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/*
7462306a36Sopenharmony_ci * basic:  basic functions range: [0... ]
7562306a36Sopenharmony_ci * ext:    extended functions range: [0x80000000... ]
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_cistruct cpuid_range *leafs_basic, *leafs_ext;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int num_leafs;
8062306a36Sopenharmony_cistatic bool is_amd;
8162306a36Sopenharmony_cistatic bool show_details;
8262306a36Sopenharmony_cistatic bool show_raw;
8362306a36Sopenharmony_cistatic bool show_flags_only = true;
8462306a36Sopenharmony_cistatic u32 user_index = 0xFFFFFFFF;
8562306a36Sopenharmony_cistatic u32 user_sub = 0xFFFFFFFF;
8662306a36Sopenharmony_cistatic int flines;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic inline void cpuid(u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	/* ecx is often an input as well as an output. */
9162306a36Sopenharmony_ci	asm volatile("cpuid"
9262306a36Sopenharmony_ci	    : "=a" (*eax),
9362306a36Sopenharmony_ci	      "=b" (*ebx),
9462306a36Sopenharmony_ci	      "=c" (*ecx),
9562306a36Sopenharmony_ci	      "=d" (*edx)
9662306a36Sopenharmony_ci	    : "0" (*eax), "2" (*ecx));
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic inline bool has_subleafs(u32 f)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	if (f == 0x7 || f == 0xd)
10262306a36Sopenharmony_ci		return true;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (is_amd) {
10562306a36Sopenharmony_ci		if (f == 0x8000001d)
10662306a36Sopenharmony_ci			return true;
10762306a36Sopenharmony_ci		return false;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	switch (f) {
11162306a36Sopenharmony_ci	case 0x4:
11262306a36Sopenharmony_ci	case 0xb:
11362306a36Sopenharmony_ci	case 0xf:
11462306a36Sopenharmony_ci	case 0x10:
11562306a36Sopenharmony_ci	case 0x14:
11662306a36Sopenharmony_ci	case 0x18:
11762306a36Sopenharmony_ci	case 0x1f:
11862306a36Sopenharmony_ci		return true;
11962306a36Sopenharmony_ci	default:
12062306a36Sopenharmony_ci		return false;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void leaf_print_raw(struct subleaf *leaf)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	if (has_subleafs(leaf->index)) {
12762306a36Sopenharmony_ci		if (leaf->sub == 0)
12862306a36Sopenharmony_ci			printf("0x%08x: subleafs:\n", leaf->index);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
13162306a36Sopenharmony_ci			leaf->sub, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
13262306a36Sopenharmony_ci	} else {
13362306a36Sopenharmony_ci		printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
13462306a36Sopenharmony_ci			leaf->index, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/* Return true is the input eax/ebx/ecx/edx are all zero */
13962306a36Sopenharmony_cistatic bool cpuid_store(struct cpuid_range *range, u32 f, int subleaf,
14062306a36Sopenharmony_ci			u32 a, u32 b, u32 c, u32 d)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct cpuid_func *func;
14362306a36Sopenharmony_ci	struct subleaf *leaf;
14462306a36Sopenharmony_ci	int s = 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (a == 0 && b == 0 && c == 0 && d == 0)
14762306a36Sopenharmony_ci		return true;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * Cut off vendor-prefix from CPUID function as we're using it as an
15162306a36Sopenharmony_ci	 * index into ->funcs.
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	func = &range->funcs[f & 0xffff];
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (!func->leafs) {
15662306a36Sopenharmony_ci		func->leafs = malloc(sizeof(struct subleaf));
15762306a36Sopenharmony_ci		if (!func->leafs)
15862306a36Sopenharmony_ci			perror("malloc func leaf");
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		func->nr = 1;
16162306a36Sopenharmony_ci	} else {
16262306a36Sopenharmony_ci		s = func->nr;
16362306a36Sopenharmony_ci		func->leafs = realloc(func->leafs, (s + 1) * sizeof(*leaf));
16462306a36Sopenharmony_ci		if (!func->leafs)
16562306a36Sopenharmony_ci			perror("realloc f->leafs");
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		func->nr++;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	leaf = &func->leafs[s];
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	leaf->index = f;
17362306a36Sopenharmony_ci	leaf->sub = subleaf;
17462306a36Sopenharmony_ci	leaf->eax = a;
17562306a36Sopenharmony_ci	leaf->ebx = b;
17662306a36Sopenharmony_ci	leaf->ecx = c;
17762306a36Sopenharmony_ci	leaf->edx = d;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return false;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void raw_dump_range(struct cpuid_range *range)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	u32 f;
18562306a36Sopenharmony_ci	int i;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	printf("%s Leafs :\n", range->is_ext ? "Extended" : "Basic");
18862306a36Sopenharmony_ci	printf("================\n");
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	for (f = 0; (int)f < range->nr; f++) {
19162306a36Sopenharmony_ci		struct cpuid_func *func = &range->funcs[f];
19262306a36Sopenharmony_ci		u32 index = f;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		if (range->is_ext)
19562306a36Sopenharmony_ci			index += 0x80000000;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		/* Skip leaf without valid items */
19862306a36Sopenharmony_ci		if (!func->nr)
19962306a36Sopenharmony_ci			continue;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		/* First item is the main leaf, followed by all subleafs */
20262306a36Sopenharmony_ci		for (i = 0; i < func->nr; i++)
20362306a36Sopenharmony_ci			leaf_print_raw(&func->leafs[i]);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci#define MAX_SUBLEAF_NUM		32
20862306a36Sopenharmony_cistruct cpuid_range *setup_cpuid_range(u32 input_eax)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	u32 max_func, idx_func;
21162306a36Sopenharmony_ci	int subleaf;
21262306a36Sopenharmony_ci	struct cpuid_range *range;
21362306a36Sopenharmony_ci	u32 eax, ebx, ecx, edx;
21462306a36Sopenharmony_ci	u32 f = input_eax;
21562306a36Sopenharmony_ci	int max_subleaf;
21662306a36Sopenharmony_ci	bool allzero;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	eax = input_eax;
21962306a36Sopenharmony_ci	ebx = ecx = edx = 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	cpuid(&eax, &ebx, &ecx, &edx);
22262306a36Sopenharmony_ci	max_func = eax;
22362306a36Sopenharmony_ci	idx_func = (max_func & 0xffff) + 1;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	range = malloc(sizeof(struct cpuid_range));
22662306a36Sopenharmony_ci	if (!range)
22762306a36Sopenharmony_ci		perror("malloc range");
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (input_eax & 0x80000000)
23062306a36Sopenharmony_ci		range->is_ext = true;
23162306a36Sopenharmony_ci	else
23262306a36Sopenharmony_ci		range->is_ext = false;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	range->funcs = malloc(sizeof(struct cpuid_func) * idx_func);
23562306a36Sopenharmony_ci	if (!range->funcs)
23662306a36Sopenharmony_ci		perror("malloc range->funcs");
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	range->nr = idx_func;
23962306a36Sopenharmony_ci	memset(range->funcs, 0, sizeof(struct cpuid_func) * idx_func);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	for (; f <= max_func; f++) {
24262306a36Sopenharmony_ci		eax = f;
24362306a36Sopenharmony_ci		subleaf = ecx = 0;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		cpuid(&eax, &ebx, &ecx, &edx);
24662306a36Sopenharmony_ci		allzero = cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
24762306a36Sopenharmony_ci		if (allzero)
24862306a36Sopenharmony_ci			continue;
24962306a36Sopenharmony_ci		num_leafs++;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		if (!has_subleafs(f))
25262306a36Sopenharmony_ci			continue;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		max_subleaf = MAX_SUBLEAF_NUM;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		/*
25762306a36Sopenharmony_ci		 * Some can provide the exact number of subleafs,
25862306a36Sopenharmony_ci		 * others have to be tried (0xf)
25962306a36Sopenharmony_ci		 */
26062306a36Sopenharmony_ci		if (f == 0x7 || f == 0x14 || f == 0x17 || f == 0x18)
26162306a36Sopenharmony_ci			max_subleaf = (eax & 0xff) + 1;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		if (f == 0xb)
26462306a36Sopenharmony_ci			max_subleaf = 2;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		for (subleaf = 1; subleaf < max_subleaf; subleaf++) {
26762306a36Sopenharmony_ci			eax = f;
26862306a36Sopenharmony_ci			ecx = subleaf;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci			cpuid(&eax, &ebx, &ecx, &edx);
27162306a36Sopenharmony_ci			allzero = cpuid_store(range, f, subleaf,
27262306a36Sopenharmony_ci						eax, ebx, ecx, edx);
27362306a36Sopenharmony_ci			if (allzero)
27462306a36Sopenharmony_ci				continue;
27562306a36Sopenharmony_ci			num_leafs++;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return range;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/*
28462306a36Sopenharmony_ci * The basic row format for cpuid.csv  is
28562306a36Sopenharmony_ci *	LEAF,SUBLEAF,register_name,bits,short name,long description
28662306a36Sopenharmony_ci *
28762306a36Sopenharmony_ci * like:
28862306a36Sopenharmony_ci *	0,    0,  EAX,   31:0, max_basic_leafs,  Max input value for supported subleafs
28962306a36Sopenharmony_ci *	1,    0,  ECX,      0, sse3,  Streaming SIMD Extensions 3(SSE3)
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_cistatic int parse_line(char *line)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	char *str;
29462306a36Sopenharmony_ci	int i;
29562306a36Sopenharmony_ci	struct cpuid_range *range;
29662306a36Sopenharmony_ci	struct cpuid_func *func;
29762306a36Sopenharmony_ci	struct subleaf *leaf;
29862306a36Sopenharmony_ci	u32 index;
29962306a36Sopenharmony_ci	u32 sub;
30062306a36Sopenharmony_ci	char buffer[512];
30162306a36Sopenharmony_ci	char *buf;
30262306a36Sopenharmony_ci	/*
30362306a36Sopenharmony_ci	 * Tokens:
30462306a36Sopenharmony_ci	 *  1. leaf
30562306a36Sopenharmony_ci	 *  2. subleaf
30662306a36Sopenharmony_ci	 *  3. register
30762306a36Sopenharmony_ci	 *  4. bits
30862306a36Sopenharmony_ci	 *  5. short name
30962306a36Sopenharmony_ci	 *  6. long detail
31062306a36Sopenharmony_ci	 */
31162306a36Sopenharmony_ci	char *tokens[6];
31262306a36Sopenharmony_ci	struct reg_desc *reg;
31362306a36Sopenharmony_ci	struct bits_desc *bdesc;
31462306a36Sopenharmony_ci	int reg_index;
31562306a36Sopenharmony_ci	char *start, *end;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* Skip comments and NULL line */
31862306a36Sopenharmony_ci	if (line[0] == '#' || line[0] == '\n')
31962306a36Sopenharmony_ci		return 0;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	strncpy(buffer, line, 511);
32262306a36Sopenharmony_ci	buffer[511] = 0;
32362306a36Sopenharmony_ci	str = buffer;
32462306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
32562306a36Sopenharmony_ci		tokens[i] = strtok(str, ",");
32662306a36Sopenharmony_ci		if (!tokens[i])
32762306a36Sopenharmony_ci			goto err_exit;
32862306a36Sopenharmony_ci		str = NULL;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci	tokens[5] = strtok(str, "\n");
33162306a36Sopenharmony_ci	if (!tokens[5])
33262306a36Sopenharmony_ci		goto err_exit;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* index/main-leaf */
33562306a36Sopenharmony_ci	index = strtoull(tokens[0], NULL, 0);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (index & 0x80000000)
33862306a36Sopenharmony_ci		range = leafs_ext;
33962306a36Sopenharmony_ci	else
34062306a36Sopenharmony_ci		range = leafs_basic;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	index &= 0x7FFFFFFF;
34362306a36Sopenharmony_ci	/* Skip line parsing for non-existing indexes */
34462306a36Sopenharmony_ci	if ((int)index >= range->nr)
34562306a36Sopenharmony_ci		return -1;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	func = &range->funcs[index];
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Return if the index has no valid item on this platform */
35062306a36Sopenharmony_ci	if (!func->nr)
35162306a36Sopenharmony_ci		return 0;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* subleaf */
35462306a36Sopenharmony_ci	sub = strtoul(tokens[1], NULL, 0);
35562306a36Sopenharmony_ci	if ((int)sub > func->nr)
35662306a36Sopenharmony_ci		return -1;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	leaf = &func->leafs[sub];
35962306a36Sopenharmony_ci	buf = tokens[2];
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (strcasestr(buf, "EAX"))
36262306a36Sopenharmony_ci		reg_index = R_EAX;
36362306a36Sopenharmony_ci	else if (strcasestr(buf, "EBX"))
36462306a36Sopenharmony_ci		reg_index = R_EBX;
36562306a36Sopenharmony_ci	else if (strcasestr(buf, "ECX"))
36662306a36Sopenharmony_ci		reg_index = R_ECX;
36762306a36Sopenharmony_ci	else if (strcasestr(buf, "EDX"))
36862306a36Sopenharmony_ci		reg_index = R_EDX;
36962306a36Sopenharmony_ci	else
37062306a36Sopenharmony_ci		goto err_exit;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	reg = &leaf->info[reg_index];
37362306a36Sopenharmony_ci	bdesc = &reg->descs[reg->nr++];
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* bit flag or bits field */
37662306a36Sopenharmony_ci	buf = tokens[3];
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	end = strtok(buf, ":");
37962306a36Sopenharmony_ci	bdesc->end = strtoul(end, NULL, 0);
38062306a36Sopenharmony_ci	bdesc->start = bdesc->end;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* start != NULL means it is bit fields */
38362306a36Sopenharmony_ci	start = strtok(NULL, ":");
38462306a36Sopenharmony_ci	if (start)
38562306a36Sopenharmony_ci		bdesc->start = strtoul(start, NULL, 0);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	strcpy(bdesc->simp, tokens[4]);
38862306a36Sopenharmony_ci	strcpy(bdesc->detail, tokens[5]);
38962306a36Sopenharmony_ci	return 0;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cierr_exit:
39262306a36Sopenharmony_ci	printf("Warning: wrong line format:\n");
39362306a36Sopenharmony_ci	printf("\tline[%d]: %s\n", flines, line);
39462306a36Sopenharmony_ci	return -1;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/* Parse csv file, and construct the array of all leafs and subleafs */
39862306a36Sopenharmony_cistatic void parse_text(void)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	FILE *file;
40162306a36Sopenharmony_ci	char *filename, *line = NULL;
40262306a36Sopenharmony_ci	size_t len = 0;
40362306a36Sopenharmony_ci	int ret;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (show_raw)
40662306a36Sopenharmony_ci		return;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	filename = user_csv ? user_csv : def_csv;
40962306a36Sopenharmony_ci	file = fopen(filename, "r");
41062306a36Sopenharmony_ci	if (!file) {
41162306a36Sopenharmony_ci		/* Fallback to a csv in the same dir */
41262306a36Sopenharmony_ci		file = fopen("./cpuid.csv", "r");
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (!file) {
41662306a36Sopenharmony_ci		printf("Fail to open '%s'\n", filename);
41762306a36Sopenharmony_ci		return;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	while (1) {
42162306a36Sopenharmony_ci		ret = getline(&line, &len, file);
42262306a36Sopenharmony_ci		flines++;
42362306a36Sopenharmony_ci		if (ret > 0)
42462306a36Sopenharmony_ci			parse_line(line);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		if (feof(file))
42762306a36Sopenharmony_ci			break;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	fclose(file);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/* Decode every eax/ebx/ecx/edx */
43562306a36Sopenharmony_cistatic void decode_bits(u32 value, struct reg_desc *rdesc, enum cpuid_reg reg)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct bits_desc *bdesc;
43862306a36Sopenharmony_ci	int start, end, i;
43962306a36Sopenharmony_ci	u32 mask;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (!rdesc->nr) {
44262306a36Sopenharmony_ci		if (show_details)
44362306a36Sopenharmony_ci			printf("\t %s: 0x%08x\n", reg_names[reg], value);
44462306a36Sopenharmony_ci		return;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	for (i = 0; i < rdesc->nr; i++) {
44862306a36Sopenharmony_ci		bdesc = &rdesc->descs[i];
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		start = bdesc->start;
45162306a36Sopenharmony_ci		end = bdesc->end;
45262306a36Sopenharmony_ci		if (start == end) {
45362306a36Sopenharmony_ci			/* single bit flag */
45462306a36Sopenharmony_ci			if (value & (1 << start))
45562306a36Sopenharmony_ci				printf("\t%-20s %s%s\n",
45662306a36Sopenharmony_ci					bdesc->simp,
45762306a36Sopenharmony_ci					show_details ? "-" : "",
45862306a36Sopenharmony_ci					show_details ? bdesc->detail : ""
45962306a36Sopenharmony_ci					);
46062306a36Sopenharmony_ci		} else {
46162306a36Sopenharmony_ci			/* bit fields */
46262306a36Sopenharmony_ci			if (show_flags_only)
46362306a36Sopenharmony_ci				continue;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci			mask = ((u64)1 << (end - start + 1)) - 1;
46662306a36Sopenharmony_ci			printf("\t%-20s\t: 0x%-8x\t%s%s\n",
46762306a36Sopenharmony_ci					bdesc->simp,
46862306a36Sopenharmony_ci					(value >> start) & mask,
46962306a36Sopenharmony_ci					show_details ? "-" : "",
47062306a36Sopenharmony_ci					show_details ? bdesc->detail : ""
47162306a36Sopenharmony_ci					);
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic void show_leaf(struct subleaf *leaf)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	if (!leaf)
47962306a36Sopenharmony_ci		return;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (show_raw) {
48262306a36Sopenharmony_ci		leaf_print_raw(leaf);
48362306a36Sopenharmony_ci	} else {
48462306a36Sopenharmony_ci		if (show_details)
48562306a36Sopenharmony_ci			printf("CPUID_0x%x_ECX[0x%x]:\n",
48662306a36Sopenharmony_ci				leaf->index, leaf->sub);
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	decode_bits(leaf->eax, &leaf->info[R_EAX], R_EAX);
49062306a36Sopenharmony_ci	decode_bits(leaf->ebx, &leaf->info[R_EBX], R_EBX);
49162306a36Sopenharmony_ci	decode_bits(leaf->ecx, &leaf->info[R_ECX], R_ECX);
49262306a36Sopenharmony_ci	decode_bits(leaf->edx, &leaf->info[R_EDX], R_EDX);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (!show_raw && show_details)
49562306a36Sopenharmony_ci		printf("\n");
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic void show_func(struct cpuid_func *func)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	int i;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (!func)
50362306a36Sopenharmony_ci		return;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	for (i = 0; i < func->nr; i++)
50662306a36Sopenharmony_ci		show_leaf(&func->leafs[i]);
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic void show_range(struct cpuid_range *range)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	int i;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	for (i = 0; i < range->nr; i++)
51462306a36Sopenharmony_ci		show_func(&range->funcs[i]);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic inline struct cpuid_func *index_to_func(u32 index)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct cpuid_range *range;
52062306a36Sopenharmony_ci	u32 func_idx;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	range = (index & 0x80000000) ? leafs_ext : leafs_basic;
52362306a36Sopenharmony_ci	func_idx = index & 0xffff;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if ((func_idx + 1) > (u32)range->nr) {
52662306a36Sopenharmony_ci		printf("ERR: invalid input index (0x%x)\n", index);
52762306a36Sopenharmony_ci		return NULL;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci	return &range->funcs[func_idx];
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void show_info(void)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct cpuid_func *func;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (show_raw) {
53762306a36Sopenharmony_ci		/* Show all of the raw output of 'cpuid' instr */
53862306a36Sopenharmony_ci		raw_dump_range(leafs_basic);
53962306a36Sopenharmony_ci		raw_dump_range(leafs_ext);
54062306a36Sopenharmony_ci		return;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (user_index != 0xFFFFFFFF) {
54462306a36Sopenharmony_ci		/* Only show specific leaf/subleaf info */
54562306a36Sopenharmony_ci		func = index_to_func(user_index);
54662306a36Sopenharmony_ci		if (!func)
54762306a36Sopenharmony_ci			return;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		/* Dump the raw data also */
55062306a36Sopenharmony_ci		show_raw = true;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		if (user_sub != 0xFFFFFFFF) {
55362306a36Sopenharmony_ci			if (user_sub + 1 <= (u32)func->nr) {
55462306a36Sopenharmony_ci				show_leaf(&func->leafs[user_sub]);
55562306a36Sopenharmony_ci				return;
55662306a36Sopenharmony_ci			}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci			printf("ERR: invalid input subleaf (0x%x)\n", user_sub);
55962306a36Sopenharmony_ci		}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		show_func(func);
56262306a36Sopenharmony_ci		return;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	printf("CPU features:\n=============\n\n");
56662306a36Sopenharmony_ci	show_range(leafs_basic);
56762306a36Sopenharmony_ci	show_range(leafs_ext);
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic void setup_platform_cpuid(void)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	 u32 eax, ebx, ecx, edx;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* Check vendor */
57562306a36Sopenharmony_ci	eax = ebx = ecx = edx = 0;
57662306a36Sopenharmony_ci	cpuid(&eax, &ebx, &ecx, &edx);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/* "htuA" */
57962306a36Sopenharmony_ci	if (ebx == 0x68747541)
58062306a36Sopenharmony_ci		is_amd = true;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* Setup leafs for the basic and extended range */
58362306a36Sopenharmony_ci	leafs_basic = setup_cpuid_range(0x0);
58462306a36Sopenharmony_ci	leafs_ext = setup_cpuid_range(0x80000000);
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic void usage(void)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	printf("kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n"
59062306a36Sopenharmony_ci		"\t-a|--all             Show both bit flags and complex bit fields info\n"
59162306a36Sopenharmony_ci		"\t-b|--bitflags        Show boolean flags only\n"
59262306a36Sopenharmony_ci		"\t-d|--detail          Show details of the flag/fields (default)\n"
59362306a36Sopenharmony_ci		"\t-f|--flags           Specify the cpuid csv file\n"
59462306a36Sopenharmony_ci		"\t-h|--help            Show usage info\n"
59562306a36Sopenharmony_ci		"\t-l|--leaf=index      Specify the leaf you want to check\n"
59662306a36Sopenharmony_ci		"\t-r|--raw             Show raw cpuid data\n"
59762306a36Sopenharmony_ci		"\t-s|--subleaf=sub     Specify the subleaf you want to check\n"
59862306a36Sopenharmony_ci	);
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic struct option opts[] = {
60262306a36Sopenharmony_ci	{ "all", no_argument, NULL, 'a' },		/* show both bit flags and fields */
60362306a36Sopenharmony_ci	{ "bitflags", no_argument, NULL, 'b' },		/* only show bit flags, default on */
60462306a36Sopenharmony_ci	{ "detail", no_argument, NULL, 'd' },		/* show detail descriptions */
60562306a36Sopenharmony_ci	{ "file", required_argument, NULL, 'f' },	/* use user's cpuid file */
60662306a36Sopenharmony_ci	{ "help", no_argument, NULL, 'h'},		/* show usage */
60762306a36Sopenharmony_ci	{ "leaf", required_argument, NULL, 'l'},	/* only check a specific leaf */
60862306a36Sopenharmony_ci	{ "raw", no_argument, NULL, 'r'},		/* show raw CPUID leaf data */
60962306a36Sopenharmony_ci	{ "subleaf", required_argument, NULL, 's'},	/* check a specific subleaf */
61062306a36Sopenharmony_ci	{ NULL, 0, NULL, 0 }
61162306a36Sopenharmony_ci};
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic int parse_options(int argc, char *argv[])
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	int c;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	while ((c = getopt_long(argc, argv, "abdf:hl:rs:",
61862306a36Sopenharmony_ci					opts, NULL)) != -1)
61962306a36Sopenharmony_ci		switch (c) {
62062306a36Sopenharmony_ci		case 'a':
62162306a36Sopenharmony_ci			show_flags_only = false;
62262306a36Sopenharmony_ci			break;
62362306a36Sopenharmony_ci		case 'b':
62462306a36Sopenharmony_ci			show_flags_only = true;
62562306a36Sopenharmony_ci			break;
62662306a36Sopenharmony_ci		case 'd':
62762306a36Sopenharmony_ci			show_details = true;
62862306a36Sopenharmony_ci			break;
62962306a36Sopenharmony_ci		case 'f':
63062306a36Sopenharmony_ci			user_csv = optarg;
63162306a36Sopenharmony_ci			break;
63262306a36Sopenharmony_ci		case 'h':
63362306a36Sopenharmony_ci			usage();
63462306a36Sopenharmony_ci			exit(1);
63562306a36Sopenharmony_ci			break;
63662306a36Sopenharmony_ci		case 'l':
63762306a36Sopenharmony_ci			/* main leaf */
63862306a36Sopenharmony_ci			user_index = strtoul(optarg, NULL, 0);
63962306a36Sopenharmony_ci			break;
64062306a36Sopenharmony_ci		case 'r':
64162306a36Sopenharmony_ci			show_raw = true;
64262306a36Sopenharmony_ci			break;
64362306a36Sopenharmony_ci		case 's':
64462306a36Sopenharmony_ci			/* subleaf */
64562306a36Sopenharmony_ci			user_sub = strtoul(optarg, NULL, 0);
64662306a36Sopenharmony_ci			break;
64762306a36Sopenharmony_ci		default:
64862306a36Sopenharmony_ci			printf("%s: Invalid option '%c'\n", argv[0], optopt);
64962306a36Sopenharmony_ci			return -1;
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	return 0;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci/*
65662306a36Sopenharmony_ci * Do 4 things in turn:
65762306a36Sopenharmony_ci * 1. Parse user options
65862306a36Sopenharmony_ci * 2. Parse and store all the CPUID leaf data supported on this platform
65962306a36Sopenharmony_ci * 2. Parse the csv file, while skipping leafs which are not available
66062306a36Sopenharmony_ci *    on this platform
66162306a36Sopenharmony_ci * 3. Print leafs info based on user options
66262306a36Sopenharmony_ci */
66362306a36Sopenharmony_ciint main(int argc, char *argv[])
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	if (parse_options(argc, argv))
66662306a36Sopenharmony_ci		return -1;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* Setup the cpuid leafs of current platform */
66962306a36Sopenharmony_ci	setup_platform_cpuid();
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* Read and parse the 'cpuid.csv' */
67262306a36Sopenharmony_ci	parse_text();
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	show_info();
67562306a36Sopenharmony_ci	return 0;
67662306a36Sopenharmony_ci}
677