~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/arch/x86/kcpuid/kcpuid.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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             : "" (*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 }
677 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php