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