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 = ®->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 521 range = (index & 0x80000000) ? leafs_ext : leafs_basic; 522 index &= 0x7FFFFFFF; 523 524 if (((index & 0xFFFF) + 1) > (u32)range->nr) { 525 printf("ERR: invalid input index (0x%x)\n", index); 526 return NULL; 527 } 528 return &range->funcs[index]; 529 } 530 531 static void show_info(void) 532 { 533 struct cpuid_func *func; 534 535 if (show_raw) { 536 /* Show all of the raw output of 'cpuid' instr */ 537 raw_dump_range(leafs_basic); 538 raw_dump_range(leafs_ext); 539 return; 540 } 541 542 if (user_index != 0xFFFFFFFF) { 543 /* Only show specific leaf/subleaf info */ 544 func = index_to_func(user_index); 545 if (!func) 546 return; 547 548 /* Dump the raw data also */ 549 show_raw = true; 550 551 if (user_sub != 0xFFFFFFFF) { 552 if (user_sub + 1 <= (u32)func->nr) { 553 show_leaf(&func->leafs[user_sub]); 554 return; 555 } 556 557 printf("ERR: invalid input subleaf (0x%x)\n", user_sub); 558 } 559 560 show_func(func); 561 return; 562 } 563 564 printf("CPU features:\n=============\n\n"); 565 show_range(leafs_basic); 566 show_range(leafs_ext); 567 } 568 569 static void setup_platform_cpuid(void) 570 { 571 u32 eax, ebx, ecx, edx; 572 573 /* Check vendor */ 574 eax = ebx = ecx = edx = 0; 575 cpuid(&eax, &ebx, &ecx, &edx); 576 577 /* "htuA" */ 578 if (ebx == 0x68747541) 579 is_amd = true; 580 581 /* Setup leafs for the basic and extended range */ 582 leafs_basic = setup_cpuid_range(0x0); 583 leafs_ext = setup_cpuid_range(0x80000000); 584 } 585 586 static void usage(void) 587 { 588 printf("kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n" 589 "\t-a|--all Show both bit flags and complex bit fields info\n" 590 "\t-b|--bitflags Show boolean flags only\n" 591 "\t-d|--detail Show details of the flag/fields (default)\n" 592 "\t-f|--flags Specify the cpuid csv file\n" 593 "\t-h|--help Show usage info\n" 594 "\t-l|--leaf=index Specify the leaf you want to check\n" 595 "\t-r|--raw Show raw cpuid data\n" 596 "\t-s|--subleaf=sub Specify the subleaf you want to check\n" 597 ); 598 } 599 600 static struct option opts[] = { 601 { "all", no_argument, NULL, 'a' }, /* show both bit flags and fields */ 602 { "bitflags", no_argument, NULL, 'b' }, /* only show bit flags, default on */ 603 { "detail", no_argument, NULL, 'd' }, /* show detail descriptions */ 604 { "file", required_argument, NULL, 'f' }, /* use user's cpuid file */ 605 { "help", no_argument, NULL, 'h'}, /* show usage */ 606 { "leaf", required_argument, NULL, 'l'}, /* only check a specific leaf */ 607 { "raw", no_argument, NULL, 'r'}, /* show raw CPUID leaf data */ 608 { "subleaf", required_argument, NULL, 's'}, /* check a specific subleaf */ 609 { NULL, 0, NULL, 0 } 610 }; 611 612 static int parse_options(int argc, char *argv[]) 613 { 614 int c; 615 616 while ((c = getopt_long(argc, argv, "abdf:hl:rs:", 617 opts, NULL)) != -1) 618 switch (c) { 619 case 'a': 620 show_flags_only = false; 621 break; 622 case 'b': 623 show_flags_only = true; 624 break; 625 case 'd': 626 show_details = true; 627 break; 628 case 'f': 629 user_csv = optarg; 630 break; 631 case 'h': 632 usage(); 633 exit(1); 634 break; 635 case 'l': 636 /* main leaf */ 637 user_index = strtoul(optarg, NULL, 0); 638 break; 639 case 'r': 640 show_raw = true; 641 break; 642 case 's': 643 /* subleaf */ 644 user_sub = strtoul(optarg, NULL, 0); 645 break; 646 default: 647 printf("%s: Invalid option '%c'\n", argv[0], optopt); 648 return -1; 649 } 650 651 return 0; 652 } 653 654 /* 655 * Do 4 things in turn: 656 * 1. Parse user options 657 * 2. Parse and store all the CPUID leaf data supported on this platform 658 * 2. Parse the csv file, while skipping leafs which are not available 659 * on this platform 660 * 3. Print leafs info based on user options 661 */ 662 int main(int argc, char *argv[]) 663 { 664 if (parse_options(argc, argv)) 665 return -1; 666 667 /* Setup the cpuid leafs of current platform */ 668 setup_platform_cpuid(); 669 670 /* Read and parse the 'cpuid.csv' */ 671 parse_text(); 672 673 show_info(); 674 return 0; 675 } 676