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