linux/tools/arch/x86/kcpuid/kcpuid.c
<<
>>
Prefs
   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 {
  37        R_EAX = 0,
  38        R_EBX,
  39        R_ECX,
  40        R_EDX,
  41        NR_REGS
  42};
  43
  44struct 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) */
  52struct 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
  61struct 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 */
  73struct cpuid_range *leafs_basic, *leafs_ext;
  74
  75static int num_leafs;
  76static bool is_amd;
  77static bool show_details;
  78static bool show_raw;
  79static bool show_flags_only = true;
  80static u32 user_index = 0xFFFFFFFF;
  81static u32 user_sub = 0xFFFFFFFF;
  82static int flines;
  83
  84static 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
  95static 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
 120static 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 */
 135static 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
 178static 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
 204struct 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 */
 287static 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
 387err_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 */
 394static 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 */
 431static 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
 466static 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
 480static 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
 491static 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
 499static 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
 513static 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
 551static 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
 568static 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
 582static 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
 594static 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 */
 644int 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}
 658