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

source code of linux/tools/arch/x86/kcpuid/kcpuid.c