brintos

brintos / linux-shallow public Read only

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