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