kcpuid.c (13363B)
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 = ®->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}