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 = ®->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