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 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