Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #define _GNU_SOURCE
0003 
0004 #include <stdio.h>
0005 #include <stdbool.h>
0006 #include <stdlib.h>
0007 #include <string.h>
0008 #include <getopt.h>
0009 
0010 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
0011 
0012 typedef unsigned int u32;
0013 typedef unsigned long long u64;
0014 
0015 char *def_csv = "/usr/share/misc/cpuid.csv";
0016 char *user_csv;
0017 
0018 
0019 /* Cover both single-bit flag and multiple-bits fields */
0020 struct bits_desc {
0021     /* start and end bits */
0022     int start, end;
0023     /* 0 or 1 for 1-bit flag */
0024     int value;
0025     char simp[32];
0026     char detail[256];
0027 };
0028 
0029 /* descriptor info for eax/ebx/ecx/edx */
0030 struct reg_desc {
0031     /* number of valid entries */
0032     int nr;
0033     struct bits_desc descs[32];
0034 };
0035 
0036 enum {
0037     R_EAX = 0,
0038     R_EBX,
0039     R_ECX,
0040     R_EDX,
0041     NR_REGS
0042 };
0043 
0044 struct subleaf {
0045     u32 index;
0046     u32 sub;
0047     u32 eax, ebx, ecx, edx;
0048     struct reg_desc info[NR_REGS];
0049 };
0050 
0051 /* Represent one leaf (basic or extended) */
0052 struct cpuid_func {
0053     /*
0054      * Array of subleafs for this func, if there is no subleafs
0055      * then the leafs[0] is the main leaf
0056      */
0057     struct subleaf *leafs;
0058     int nr;
0059 };
0060 
0061 struct cpuid_range {
0062     /* array of main leafs */
0063     struct cpuid_func *funcs;
0064     /* number of valid leafs */
0065     int nr;
0066     bool is_ext;
0067 };
0068 
0069 /*
0070  * basic:  basic functions range: [0... ]
0071  * ext:    extended functions range: [0x80000000... ]
0072  */
0073 struct cpuid_range *leafs_basic, *leafs_ext;
0074 
0075 static int num_leafs;
0076 static bool is_amd;
0077 static bool show_details;
0078 static bool show_raw;
0079 static bool show_flags_only = true;
0080 static u32 user_index = 0xFFFFFFFF;
0081 static u32 user_sub = 0xFFFFFFFF;
0082 static int flines;
0083 
0084 static inline void cpuid(u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
0085 {
0086     /* ecx is often an input as well as an output. */
0087     asm volatile("cpuid"
0088         : "=a" (*eax),
0089           "=b" (*ebx),
0090           "=c" (*ecx),
0091           "=d" (*edx)
0092         : "0" (*eax), "2" (*ecx));
0093 }
0094 
0095 static inline bool has_subleafs(u32 f)
0096 {
0097     if (f == 0x7 || f == 0xd)
0098         return true;
0099 
0100     if (is_amd) {
0101         if (f == 0x8000001d)
0102             return true;
0103         return false;
0104     }
0105 
0106     switch (f) {
0107     case 0x4:
0108     case 0xb:
0109     case 0xf:
0110     case 0x10:
0111     case 0x14:
0112     case 0x18:
0113     case 0x1f:
0114         return true;
0115     default:
0116         return false;
0117     }
0118 }
0119 
0120 static void leaf_print_raw(struct subleaf *leaf)
0121 {
0122     if (has_subleafs(leaf->index)) {
0123         if (leaf->sub == 0)
0124             printf("0x%08x: subleafs:\n", leaf->index);
0125 
0126         printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
0127             leaf->sub, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
0128     } else {
0129         printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
0130             leaf->index, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
0131     }
0132 }
0133 
0134 /* Return true is the input eax/ebx/ecx/edx are all zero */
0135 static bool cpuid_store(struct cpuid_range *range, u32 f, int subleaf,
0136             u32 a, u32 b, u32 c, u32 d)
0137 {
0138     struct cpuid_func *func;
0139     struct subleaf *leaf;
0140     int s = 0;
0141 
0142     if (a == 0 && b == 0 && c == 0 && d == 0)
0143         return true;
0144 
0145     /*
0146      * Cut off vendor-prefix from CPUID function as we're using it as an
0147      * index into ->funcs.
0148      */
0149     func = &range->funcs[f & 0xffff];
0150 
0151     if (!func->leafs) {
0152         func->leafs = malloc(sizeof(struct subleaf));
0153         if (!func->leafs)
0154             perror("malloc func leaf");
0155 
0156         func->nr = 1;
0157     } else {
0158         s = func->nr;
0159         func->leafs = realloc(func->leafs, (s + 1) * sizeof(*leaf));
0160         if (!func->leafs)
0161             perror("realloc f->leafs");
0162 
0163         func->nr++;
0164     }
0165 
0166     leaf = &func->leafs[s];
0167 
0168     leaf->index = f;
0169     leaf->sub = subleaf;
0170     leaf->eax = a;
0171     leaf->ebx = b;
0172     leaf->ecx = c;
0173     leaf->edx = d;
0174 
0175     return false;
0176 }
0177 
0178 static void raw_dump_range(struct cpuid_range *range)
0179 {
0180     u32 f;
0181     int i;
0182 
0183     printf("%s Leafs :\n", range->is_ext ? "Extended" : "Basic");
0184     printf("================\n");
0185 
0186     for (f = 0; (int)f < range->nr; f++) {
0187         struct cpuid_func *func = &range->funcs[f];
0188         u32 index = f;
0189 
0190         if (range->is_ext)
0191             index += 0x80000000;
0192 
0193         /* Skip leaf without valid items */
0194         if (!func->nr)
0195             continue;
0196 
0197         /* First item is the main leaf, followed by all subleafs */
0198         for (i = 0; i < func->nr; i++)
0199             leaf_print_raw(&func->leafs[i]);
0200     }
0201 }
0202 
0203 #define MAX_SUBLEAF_NUM     32
0204 struct cpuid_range *setup_cpuid_range(u32 input_eax)
0205 {
0206     u32 max_func, idx_func;
0207     int subleaf;
0208     struct cpuid_range *range;
0209     u32 eax, ebx, ecx, edx;
0210     u32 f = input_eax;
0211     int max_subleaf;
0212     bool allzero;
0213 
0214     eax = input_eax;
0215     ebx = ecx = edx = 0;
0216 
0217     cpuid(&eax, &ebx, &ecx, &edx);
0218     max_func = eax;
0219     idx_func = (max_func & 0xffff) + 1;
0220 
0221     range = malloc(sizeof(struct cpuid_range));
0222     if (!range)
0223         perror("malloc range");
0224 
0225     if (input_eax & 0x80000000)
0226         range->is_ext = true;
0227     else
0228         range->is_ext = false;
0229 
0230     range->funcs = malloc(sizeof(struct cpuid_func) * idx_func);
0231     if (!range->funcs)
0232         perror("malloc range->funcs");
0233 
0234     range->nr = idx_func;
0235     memset(range->funcs, 0, sizeof(struct cpuid_func) * idx_func);
0236 
0237     for (; f <= max_func; f++) {
0238         eax = f;
0239         subleaf = ecx = 0;
0240 
0241         cpuid(&eax, &ebx, &ecx, &edx);
0242         allzero = cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
0243         if (allzero)
0244             continue;
0245         num_leafs++;
0246 
0247         if (!has_subleafs(f))
0248             continue;
0249 
0250         max_subleaf = MAX_SUBLEAF_NUM;
0251 
0252         /*
0253          * Some can provide the exact number of subleafs,
0254          * others have to be tried (0xf)
0255          */
0256         if (f == 0x7 || f == 0x14 || f == 0x17 || f == 0x18)
0257             max_subleaf = (eax & 0xff) + 1;
0258 
0259         if (f == 0xb)
0260             max_subleaf = 2;
0261 
0262         for (subleaf = 1; subleaf < max_subleaf; subleaf++) {
0263             eax = f;
0264             ecx = subleaf;
0265 
0266             cpuid(&eax, &ebx, &ecx, &edx);
0267             allzero = cpuid_store(range, f, subleaf,
0268                         eax, ebx, ecx, edx);
0269             if (allzero)
0270                 continue;
0271             num_leafs++;
0272         }
0273 
0274     }
0275 
0276     return range;
0277 }
0278 
0279 /*
0280  * The basic row format for cpuid.csv  is
0281  *  LEAF,SUBLEAF,register_name,bits,short name,long description
0282  *
0283  * like:
0284  *  0,    0,  EAX,   31:0, max_basic_leafs,  Max input value for supported subleafs
0285  *  1,    0,  ECX,      0, sse3,  Streaming SIMD Extensions 3(SSE3)
0286  */
0287 static int parse_line(char *line)
0288 {
0289     char *str;
0290     int i;
0291     struct cpuid_range *range;
0292     struct cpuid_func *func;
0293     struct subleaf *leaf;
0294     u32 index;
0295     u32 sub;
0296     char buffer[512];
0297     char *buf;
0298     /*
0299      * Tokens:
0300      *  1. leaf
0301      *  2. subleaf
0302      *  3. register
0303      *  4. bits
0304      *  5. short name
0305      *  6. long detail
0306      */
0307     char *tokens[6];
0308     struct reg_desc *reg;
0309     struct bits_desc *bdesc;
0310     int reg_index;
0311     char *start, *end;
0312 
0313     /* Skip comments and NULL line */
0314     if (line[0] == '#' || line[0] == '\n')
0315         return 0;
0316 
0317     strncpy(buffer, line, 511);
0318     buffer[511] = 0;
0319     str = buffer;
0320     for (i = 0; i < 5; i++) {
0321         tokens[i] = strtok(str, ",");
0322         if (!tokens[i])
0323             goto err_exit;
0324         str = NULL;
0325     }
0326     tokens[5] = strtok(str, "\n");
0327     if (!tokens[5])
0328         goto err_exit;
0329 
0330     /* index/main-leaf */
0331     index = strtoull(tokens[0], NULL, 0);
0332 
0333     if (index & 0x80000000)
0334         range = leafs_ext;
0335     else
0336         range = leafs_basic;
0337 
0338     index &= 0x7FFFFFFF;
0339     /* Skip line parsing for non-existing indexes */
0340     if ((int)index >= range->nr)
0341         return -1;
0342 
0343     func = &range->funcs[index];
0344 
0345     /* Return if the index has no valid item on this platform */
0346     if (!func->nr)
0347         return 0;
0348 
0349     /* subleaf */
0350     sub = strtoul(tokens[1], NULL, 0);
0351     if ((int)sub > func->nr)
0352         return -1;
0353 
0354     leaf = &func->leafs[sub];
0355     buf = tokens[2];
0356 
0357     if (strcasestr(buf, "EAX"))
0358         reg_index = R_EAX;
0359     else if (strcasestr(buf, "EBX"))
0360         reg_index = R_EBX;
0361     else if (strcasestr(buf, "ECX"))
0362         reg_index = R_ECX;
0363     else if (strcasestr(buf, "EDX"))
0364         reg_index = R_EDX;
0365     else
0366         goto err_exit;
0367 
0368     reg = &leaf->info[reg_index];
0369     bdesc = &reg->descs[reg->nr++];
0370 
0371     /* bit flag or bits field */
0372     buf = tokens[3];
0373 
0374     end = strtok(buf, ":");
0375     bdesc->end = strtoul(end, NULL, 0);
0376     bdesc->start = bdesc->end;
0377 
0378     /* start != NULL means it is bit fields */
0379     start = strtok(NULL, ":");
0380     if (start)
0381         bdesc->start = strtoul(start, NULL, 0);
0382 
0383     strcpy(bdesc->simp, tokens[4]);
0384     strcpy(bdesc->detail, tokens[5]);
0385     return 0;
0386 
0387 err_exit:
0388     printf("Warning: wrong line format:\n");
0389     printf("\tline[%d]: %s\n", flines, line);
0390     return -1;
0391 }
0392 
0393 /* Parse csv file, and construct the array of all leafs and subleafs */
0394 static void parse_text(void)
0395 {
0396     FILE *file;
0397     char *filename, *line = NULL;
0398     size_t len = 0;
0399     int ret;
0400 
0401     if (show_raw)
0402         return;
0403 
0404     filename = user_csv ? user_csv : def_csv;
0405     file = fopen(filename, "r");
0406     if (!file) {
0407         /* Fallback to a csv in the same dir */
0408         file = fopen("./cpuid.csv", "r");
0409     }
0410 
0411     if (!file) {
0412         printf("Fail to open '%s'\n", filename);
0413         return;
0414     }
0415 
0416     while (1) {
0417         ret = getline(&line, &len, file);
0418         flines++;
0419         if (ret > 0)
0420             parse_line(line);
0421 
0422         if (feof(file))
0423             break;
0424     }
0425 
0426     fclose(file);
0427 }
0428 
0429 
0430 /* Decode every eax/ebx/ecx/edx */
0431 static void decode_bits(u32 value, struct reg_desc *rdesc)
0432 {
0433     struct bits_desc *bdesc;
0434     int start, end, i;
0435     u32 mask;
0436 
0437     for (i = 0; i < rdesc->nr; i++) {
0438         bdesc = &rdesc->descs[i];
0439 
0440         start = bdesc->start;
0441         end = bdesc->end;
0442         if (start == end) {
0443             /* single bit flag */
0444             if (value & (1 << start))
0445                 printf("\t%-20s %s%s\n",
0446                     bdesc->simp,
0447                     show_details ? "-" : "",
0448                     show_details ? bdesc->detail : ""
0449                     );
0450         } else {
0451             /* bit fields */
0452             if (show_flags_only)
0453                 continue;
0454 
0455             mask = ((u64)1 << (end - start + 1)) - 1;
0456             printf("\t%-20s\t: 0x%-8x\t%s%s\n",
0457                     bdesc->simp,
0458                     (value >> start) & mask,
0459                     show_details ? "-" : "",
0460                     show_details ? bdesc->detail : ""
0461                     );
0462         }
0463     }
0464 }
0465 
0466 static void show_leaf(struct subleaf *leaf)
0467 {
0468     if (!leaf)
0469         return;
0470 
0471     if (show_raw)
0472         leaf_print_raw(leaf);
0473 
0474     decode_bits(leaf->eax, &leaf->info[R_EAX]);
0475     decode_bits(leaf->ebx, &leaf->info[R_EBX]);
0476     decode_bits(leaf->ecx, &leaf->info[R_ECX]);
0477     decode_bits(leaf->edx, &leaf->info[R_EDX]);
0478 }
0479 
0480 static void show_func(struct cpuid_func *func)
0481 {
0482     int i;
0483 
0484     if (!func)
0485         return;
0486 
0487     for (i = 0; i < func->nr; i++)
0488         show_leaf(&func->leafs[i]);
0489 }
0490 
0491 static void show_range(struct cpuid_range *range)
0492 {
0493     int i;
0494 
0495     for (i = 0; i < range->nr; i++)
0496         show_func(&range->funcs[i]);
0497 }
0498 
0499 static inline struct cpuid_func *index_to_func(u32 index)
0500 {
0501     struct cpuid_range *range;
0502 
0503     range = (index & 0x80000000) ? leafs_ext : leafs_basic;
0504     index &= 0x7FFFFFFF;
0505 
0506     if (((index & 0xFFFF) + 1) > (u32)range->nr) {
0507         printf("ERR: invalid input index (0x%x)\n", index);
0508         return NULL;
0509     }
0510     return &range->funcs[index];
0511 }
0512 
0513 static void show_info(void)
0514 {
0515     struct cpuid_func *func;
0516 
0517     if (show_raw) {
0518         /* Show all of the raw output of 'cpuid' instr */
0519         raw_dump_range(leafs_basic);
0520         raw_dump_range(leafs_ext);
0521         return;
0522     }
0523 
0524     if (user_index != 0xFFFFFFFF) {
0525         /* Only show specific leaf/subleaf info */
0526         func = index_to_func(user_index);
0527         if (!func)
0528             return;
0529 
0530         /* Dump the raw data also */
0531         show_raw = true;
0532 
0533         if (user_sub != 0xFFFFFFFF) {
0534             if (user_sub + 1 <= (u32)func->nr) {
0535                 show_leaf(&func->leafs[user_sub]);
0536                 return;
0537             }
0538 
0539             printf("ERR: invalid input subleaf (0x%x)\n", user_sub);
0540         }
0541 
0542         show_func(func);
0543         return;
0544     }
0545 
0546     printf("CPU features:\n=============\n\n");
0547     show_range(leafs_basic);
0548     show_range(leafs_ext);
0549 }
0550 
0551 static void setup_platform_cpuid(void)
0552 {
0553      u32 eax, ebx, ecx, edx;
0554 
0555     /* Check vendor */
0556     eax = ebx = ecx = edx = 0;
0557     cpuid(&eax, &ebx, &ecx, &edx);
0558 
0559     /* "htuA" */
0560     if (ebx == 0x68747541)
0561         is_amd = true;
0562 
0563     /* Setup leafs for the basic and extended range */
0564     leafs_basic = setup_cpuid_range(0x0);
0565     leafs_ext = setup_cpuid_range(0x80000000);
0566 }
0567 
0568 static void usage(void)
0569 {
0570     printf("kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n"
0571         "\t-a|--all             Show both bit flags and complex bit fields info\n"
0572         "\t-b|--bitflags        Show boolean flags only\n"
0573         "\t-d|--detail          Show details of the flag/fields (default)\n"
0574         "\t-f|--flags           Specify the cpuid csv file\n"
0575         "\t-h|--help            Show usage info\n"
0576         "\t-l|--leaf=index      Specify the leaf you want to check\n"
0577         "\t-r|--raw             Show raw cpuid data\n"
0578         "\t-s|--subleaf=sub     Specify the subleaf you want to check\n"
0579     );
0580 }
0581 
0582 static struct option opts[] = {
0583     { "all", no_argument, NULL, 'a' },      /* show both bit flags and fields */
0584     { "bitflags", no_argument, NULL, 'b' },     /* only show bit flags, default on */
0585     { "detail", no_argument, NULL, 'd' },       /* show detail descriptions */
0586     { "file", required_argument, NULL, 'f' },   /* use user's cpuid file */
0587     { "help", no_argument, NULL, 'h'},      /* show usage */
0588     { "leaf", required_argument, NULL, 'l'},    /* only check a specific leaf */
0589     { "raw", no_argument, NULL, 'r'},       /* show raw CPUID leaf data */
0590     { "subleaf", required_argument, NULL, 's'}, /* check a specific subleaf */
0591     { NULL, 0, NULL, 0 }
0592 };
0593 
0594 static int parse_options(int argc, char *argv[])
0595 {
0596     int c;
0597 
0598     while ((c = getopt_long(argc, argv, "abdf:hl:rs:",
0599                     opts, NULL)) != -1)
0600         switch (c) {
0601         case 'a':
0602             show_flags_only = false;
0603             break;
0604         case 'b':
0605             show_flags_only = true;
0606             break;
0607         case 'd':
0608             show_details = true;
0609             break;
0610         case 'f':
0611             user_csv = optarg;
0612             break;
0613         case 'h':
0614             usage();
0615             exit(1);
0616             break;
0617         case 'l':
0618             /* main leaf */
0619             user_index = strtoul(optarg, NULL, 0);
0620             break;
0621         case 'r':
0622             show_raw = true;
0623             break;
0624         case 's':
0625             /* subleaf */
0626             user_sub = strtoul(optarg, NULL, 0);
0627             break;
0628         default:
0629             printf("%s: Invalid option '%c'\n", argv[0], optopt);
0630             return -1;
0631     }
0632 
0633     return 0;
0634 }
0635 
0636 /*
0637  * Do 4 things in turn:
0638  * 1. Parse user options
0639  * 2. Parse and store all the CPUID leaf data supported on this platform
0640  * 2. Parse the csv file, while skipping leafs which are not available
0641  *    on this platform
0642  * 3. Print leafs info based on user options
0643  */
0644 int main(int argc, char *argv[])
0645 {
0646     if (parse_options(argc, argv))
0647         return -1;
0648 
0649     /* Setup the cpuid leafs of current platform */
0650     setup_platform_cpuid();
0651 
0652     /* Read and parse the 'cpuid.csv' */
0653     parse_text();
0654 
0655     show_info();
0656     return 0;
0657 }