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