GNU Linux-libre 6.9.1-gnu
[releases.git] / tools / arch / x86 / kcpuid / kcpuid.c
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
12 typedef unsigned int u32;
13 typedef unsigned long long u64;
14
15 char *def_csv = "/usr/share/misc/cpuid.csv";
16 char *user_csv;
17
18
19 /* Cover both single-bit flag and multiple-bits fields */
20 struct 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 */
30 struct reg_desc {
31         /* number of valid entries */
32         int nr;
33         struct bits_desc descs[32];
34 };
35
36 enum cpuid_reg {
37         R_EAX = 0,
38         R_EBX,
39         R_ECX,
40         R_EDX,
41         NR_REGS
42 };
43
44 static const char * const reg_names[] = {
45         "EAX", "EBX", "ECX", "EDX",
46 };
47
48 struct 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) */
56 struct 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
65 struct 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  */
77 struct cpuid_range *leafs_basic, *leafs_ext;
78
79 static int num_leafs;
80 static bool is_amd;
81 static bool show_details;
82 static bool show_raw;
83 static bool show_flags_only = true;
84 static u32 user_index = 0xFFFFFFFF;
85 static u32 user_sub = 0xFFFFFFFF;
86 static int flines;
87
88 static 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
99 static 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
124 static 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 */
139 static 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
182 static 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(&func->leafs[i]);
204         }
205 }
206
207 #define MAX_SUBLEAF_NUM         32
208 struct 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, &ebx, &ecx, &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, &ebx, &ecx, &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, &ebx, &ecx, &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  */
291 static 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
391 err_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 */
398 static 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 */
435 static 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
476 static 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(leaf->eax, &leaf->info[R_EAX], R_EAX);
490         decode_bits(leaf->ebx, &leaf->info[R_EBX], R_EBX);
491         decode_bits(leaf->ecx, &leaf->info[R_ECX], R_ECX);
492         decode_bits(leaf->edx, &leaf->info[R_EDX], R_EDX);
493
494         if (!show_raw && show_details)
495                 printf("\n");
496 }
497
498 static 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(&func->leafs[i]);
507 }
508
509 static void show_range(struct cpuid_range *range)
510 {
511         int i;
512
513         for (i = 0; i < range->nr; i++)
514                 show_func(&range->funcs[i]);
515 }
516
517 static 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
532 static 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(leafs_basic);
539                 raw_dump_range(leafs_ext);
540                 return;
541         }
542
543         if (user_index != 0xFFFFFFFF) {
544                 /* Only show specific leaf/subleaf info */
545                 func = index_to_func(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(&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(leafs_basic);
567         show_range(leafs_ext);
568 }
569
570 static 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, &ebx, &ecx, &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
587 static 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
601 static 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
613 static 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  */
663 int 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 }