cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

kcpuid.c (13363B)


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