0001
0002 #define _GNU_SOURCE
0003
0004 #include <stdio.h>
0005 #include <stdbool.h>
0006 #include <stdlib.h>
0007 #include <string.h>
0008 #include <getopt.h>
0009
0010 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
0011
0012 typedef unsigned int u32;
0013 typedef unsigned long long u64;
0014
0015 char *def_csv = "/usr/share/misc/cpuid.csv";
0016 char *user_csv;
0017
0018
0019
0020 struct bits_desc {
0021
0022 int start, end;
0023
0024 int value;
0025 char simp[32];
0026 char detail[256];
0027 };
0028
0029
0030 struct reg_desc {
0031
0032 int nr;
0033 struct bits_desc descs[32];
0034 };
0035
0036 enum {
0037 R_EAX = 0,
0038 R_EBX,
0039 R_ECX,
0040 R_EDX,
0041 NR_REGS
0042 };
0043
0044 struct subleaf {
0045 u32 index;
0046 u32 sub;
0047 u32 eax, ebx, ecx, edx;
0048 struct reg_desc info[NR_REGS];
0049 };
0050
0051
0052 struct cpuid_func {
0053
0054
0055
0056
0057 struct subleaf *leafs;
0058 int nr;
0059 };
0060
0061 struct cpuid_range {
0062
0063 struct cpuid_func *funcs;
0064
0065 int nr;
0066 bool is_ext;
0067 };
0068
0069
0070
0071
0072
0073 struct cpuid_range *leafs_basic, *leafs_ext;
0074
0075 static int num_leafs;
0076 static bool is_amd;
0077 static bool show_details;
0078 static bool show_raw;
0079 static bool show_flags_only = true;
0080 static u32 user_index = 0xFFFFFFFF;
0081 static u32 user_sub = 0xFFFFFFFF;
0082 static int flines;
0083
0084 static inline void cpuid(u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
0085 {
0086
0087 asm volatile("cpuid"
0088 : "=a" (*eax),
0089 "=b" (*ebx),
0090 "=c" (*ecx),
0091 "=d" (*edx)
0092 : "0" (*eax), "2" (*ecx));
0093 }
0094
0095 static inline bool has_subleafs(u32 f)
0096 {
0097 if (f == 0x7 || f == 0xd)
0098 return true;
0099
0100 if (is_amd) {
0101 if (f == 0x8000001d)
0102 return true;
0103 return false;
0104 }
0105
0106 switch (f) {
0107 case 0x4:
0108 case 0xb:
0109 case 0xf:
0110 case 0x10:
0111 case 0x14:
0112 case 0x18:
0113 case 0x1f:
0114 return true;
0115 default:
0116 return false;
0117 }
0118 }
0119
0120 static void leaf_print_raw(struct subleaf *leaf)
0121 {
0122 if (has_subleafs(leaf->index)) {
0123 if (leaf->sub == 0)
0124 printf("0x%08x: subleafs:\n", leaf->index);
0125
0126 printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
0127 leaf->sub, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
0128 } else {
0129 printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
0130 leaf->index, leaf->eax, leaf->ebx, leaf->ecx, leaf->edx);
0131 }
0132 }
0133
0134
0135 static bool cpuid_store(struct cpuid_range *range, u32 f, int subleaf,
0136 u32 a, u32 b, u32 c, u32 d)
0137 {
0138 struct cpuid_func *func;
0139 struct subleaf *leaf;
0140 int s = 0;
0141
0142 if (a == 0 && b == 0 && c == 0 && d == 0)
0143 return true;
0144
0145
0146
0147
0148
0149 func = &range->funcs[f & 0xffff];
0150
0151 if (!func->leafs) {
0152 func->leafs = malloc(sizeof(struct subleaf));
0153 if (!func->leafs)
0154 perror("malloc func leaf");
0155
0156 func->nr = 1;
0157 } else {
0158 s = func->nr;
0159 func->leafs = realloc(func->leafs, (s + 1) * sizeof(*leaf));
0160 if (!func->leafs)
0161 perror("realloc f->leafs");
0162
0163 func->nr++;
0164 }
0165
0166 leaf = &func->leafs[s];
0167
0168 leaf->index = f;
0169 leaf->sub = subleaf;
0170 leaf->eax = a;
0171 leaf->ebx = b;
0172 leaf->ecx = c;
0173 leaf->edx = d;
0174
0175 return false;
0176 }
0177
0178 static void raw_dump_range(struct cpuid_range *range)
0179 {
0180 u32 f;
0181 int i;
0182
0183 printf("%s Leafs :\n", range->is_ext ? "Extended" : "Basic");
0184 printf("================\n");
0185
0186 for (f = 0; (int)f < range->nr; f++) {
0187 struct cpuid_func *func = &range->funcs[f];
0188 u32 index = f;
0189
0190 if (range->is_ext)
0191 index += 0x80000000;
0192
0193
0194 if (!func->nr)
0195 continue;
0196
0197
0198 for (i = 0; i < func->nr; i++)
0199 leaf_print_raw(&func->leafs[i]);
0200 }
0201 }
0202
0203 #define MAX_SUBLEAF_NUM 32
0204 struct cpuid_range *setup_cpuid_range(u32 input_eax)
0205 {
0206 u32 max_func, idx_func;
0207 int subleaf;
0208 struct cpuid_range *range;
0209 u32 eax, ebx, ecx, edx;
0210 u32 f = input_eax;
0211 int max_subleaf;
0212 bool allzero;
0213
0214 eax = input_eax;
0215 ebx = ecx = edx = 0;
0216
0217 cpuid(&eax, &ebx, &ecx, &edx);
0218 max_func = eax;
0219 idx_func = (max_func & 0xffff) + 1;
0220
0221 range = malloc(sizeof(struct cpuid_range));
0222 if (!range)
0223 perror("malloc range");
0224
0225 if (input_eax & 0x80000000)
0226 range->is_ext = true;
0227 else
0228 range->is_ext = false;
0229
0230 range->funcs = malloc(sizeof(struct cpuid_func) * idx_func);
0231 if (!range->funcs)
0232 perror("malloc range->funcs");
0233
0234 range->nr = idx_func;
0235 memset(range->funcs, 0, sizeof(struct cpuid_func) * idx_func);
0236
0237 for (; f <= max_func; f++) {
0238 eax = f;
0239 subleaf = ecx = 0;
0240
0241 cpuid(&eax, &ebx, &ecx, &edx);
0242 allzero = cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
0243 if (allzero)
0244 continue;
0245 num_leafs++;
0246
0247 if (!has_subleafs(f))
0248 continue;
0249
0250 max_subleaf = MAX_SUBLEAF_NUM;
0251
0252
0253
0254
0255
0256 if (f == 0x7 || f == 0x14 || f == 0x17 || f == 0x18)
0257 max_subleaf = (eax & 0xff) + 1;
0258
0259 if (f == 0xb)
0260 max_subleaf = 2;
0261
0262 for (subleaf = 1; subleaf < max_subleaf; subleaf++) {
0263 eax = f;
0264 ecx = subleaf;
0265
0266 cpuid(&eax, &ebx, &ecx, &edx);
0267 allzero = cpuid_store(range, f, subleaf,
0268 eax, ebx, ecx, edx);
0269 if (allzero)
0270 continue;
0271 num_leafs++;
0272 }
0273
0274 }
0275
0276 return range;
0277 }
0278
0279
0280
0281
0282
0283
0284
0285
0286
0287 static int parse_line(char *line)
0288 {
0289 char *str;
0290 int i;
0291 struct cpuid_range *range;
0292 struct cpuid_func *func;
0293 struct subleaf *leaf;
0294 u32 index;
0295 u32 sub;
0296 char buffer[512];
0297 char *buf;
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307 char *tokens[6];
0308 struct reg_desc *reg;
0309 struct bits_desc *bdesc;
0310 int reg_index;
0311 char *start, *end;
0312
0313
0314 if (line[0] == '#' || line[0] == '\n')
0315 return 0;
0316
0317 strncpy(buffer, line, 511);
0318 buffer[511] = 0;
0319 str = buffer;
0320 for (i = 0; i < 5; i++) {
0321 tokens[i] = strtok(str, ",");
0322 if (!tokens[i])
0323 goto err_exit;
0324 str = NULL;
0325 }
0326 tokens[5] = strtok(str, "\n");
0327 if (!tokens[5])
0328 goto err_exit;
0329
0330
0331 index = strtoull(tokens[0], NULL, 0);
0332
0333 if (index & 0x80000000)
0334 range = leafs_ext;
0335 else
0336 range = leafs_basic;
0337
0338 index &= 0x7FFFFFFF;
0339
0340 if ((int)index >= range->nr)
0341 return -1;
0342
0343 func = &range->funcs[index];
0344
0345
0346 if (!func->nr)
0347 return 0;
0348
0349
0350 sub = strtoul(tokens[1], NULL, 0);
0351 if ((int)sub > func->nr)
0352 return -1;
0353
0354 leaf = &func->leafs[sub];
0355 buf = tokens[2];
0356
0357 if (strcasestr(buf, "EAX"))
0358 reg_index = R_EAX;
0359 else if (strcasestr(buf, "EBX"))
0360 reg_index = R_EBX;
0361 else if (strcasestr(buf, "ECX"))
0362 reg_index = R_ECX;
0363 else if (strcasestr(buf, "EDX"))
0364 reg_index = R_EDX;
0365 else
0366 goto err_exit;
0367
0368 reg = &leaf->info[reg_index];
0369 bdesc = ®->descs[reg->nr++];
0370
0371
0372 buf = tokens[3];
0373
0374 end = strtok(buf, ":");
0375 bdesc->end = strtoul(end, NULL, 0);
0376 bdesc->start = bdesc->end;
0377
0378
0379 start = strtok(NULL, ":");
0380 if (start)
0381 bdesc->start = strtoul(start, NULL, 0);
0382
0383 strcpy(bdesc->simp, tokens[4]);
0384 strcpy(bdesc->detail, tokens[5]);
0385 return 0;
0386
0387 err_exit:
0388 printf("Warning: wrong line format:\n");
0389 printf("\tline[%d]: %s\n", flines, line);
0390 return -1;
0391 }
0392
0393
0394 static void parse_text(void)
0395 {
0396 FILE *file;
0397 char *filename, *line = NULL;
0398 size_t len = 0;
0399 int ret;
0400
0401 if (show_raw)
0402 return;
0403
0404 filename = user_csv ? user_csv : def_csv;
0405 file = fopen(filename, "r");
0406 if (!file) {
0407
0408 file = fopen("./cpuid.csv", "r");
0409 }
0410
0411 if (!file) {
0412 printf("Fail to open '%s'\n", filename);
0413 return;
0414 }
0415
0416 while (1) {
0417 ret = getline(&line, &len, file);
0418 flines++;
0419 if (ret > 0)
0420 parse_line(line);
0421
0422 if (feof(file))
0423 break;
0424 }
0425
0426 fclose(file);
0427 }
0428
0429
0430
0431 static void decode_bits(u32 value, struct reg_desc *rdesc)
0432 {
0433 struct bits_desc *bdesc;
0434 int start, end, i;
0435 u32 mask;
0436
0437 for (i = 0; i < rdesc->nr; i++) {
0438 bdesc = &rdesc->descs[i];
0439
0440 start = bdesc->start;
0441 end = bdesc->end;
0442 if (start == end) {
0443
0444 if (value & (1 << start))
0445 printf("\t%-20s %s%s\n",
0446 bdesc->simp,
0447 show_details ? "-" : "",
0448 show_details ? bdesc->detail : ""
0449 );
0450 } else {
0451
0452 if (show_flags_only)
0453 continue;
0454
0455 mask = ((u64)1 << (end - start + 1)) - 1;
0456 printf("\t%-20s\t: 0x%-8x\t%s%s\n",
0457 bdesc->simp,
0458 (value >> start) & mask,
0459 show_details ? "-" : "",
0460 show_details ? bdesc->detail : ""
0461 );
0462 }
0463 }
0464 }
0465
0466 static void show_leaf(struct subleaf *leaf)
0467 {
0468 if (!leaf)
0469 return;
0470
0471 if (show_raw)
0472 leaf_print_raw(leaf);
0473
0474 decode_bits(leaf->eax, &leaf->info[R_EAX]);
0475 decode_bits(leaf->ebx, &leaf->info[R_EBX]);
0476 decode_bits(leaf->ecx, &leaf->info[R_ECX]);
0477 decode_bits(leaf->edx, &leaf->info[R_EDX]);
0478 }
0479
0480 static void show_func(struct cpuid_func *func)
0481 {
0482 int i;
0483
0484 if (!func)
0485 return;
0486
0487 for (i = 0; i < func->nr; i++)
0488 show_leaf(&func->leafs[i]);
0489 }
0490
0491 static void show_range(struct cpuid_range *range)
0492 {
0493 int i;
0494
0495 for (i = 0; i < range->nr; i++)
0496 show_func(&range->funcs[i]);
0497 }
0498
0499 static inline struct cpuid_func *index_to_func(u32 index)
0500 {
0501 struct cpuid_range *range;
0502
0503 range = (index & 0x80000000) ? leafs_ext : leafs_basic;
0504 index &= 0x7FFFFFFF;
0505
0506 if (((index & 0xFFFF) + 1) > (u32)range->nr) {
0507 printf("ERR: invalid input index (0x%x)\n", index);
0508 return NULL;
0509 }
0510 return &range->funcs[index];
0511 }
0512
0513 static void show_info(void)
0514 {
0515 struct cpuid_func *func;
0516
0517 if (show_raw) {
0518
0519 raw_dump_range(leafs_basic);
0520 raw_dump_range(leafs_ext);
0521 return;
0522 }
0523
0524 if (user_index != 0xFFFFFFFF) {
0525
0526 func = index_to_func(user_index);
0527 if (!func)
0528 return;
0529
0530
0531 show_raw = true;
0532
0533 if (user_sub != 0xFFFFFFFF) {
0534 if (user_sub + 1 <= (u32)func->nr) {
0535 show_leaf(&func->leafs[user_sub]);
0536 return;
0537 }
0538
0539 printf("ERR: invalid input subleaf (0x%x)\n", user_sub);
0540 }
0541
0542 show_func(func);
0543 return;
0544 }
0545
0546 printf("CPU features:\n=============\n\n");
0547 show_range(leafs_basic);
0548 show_range(leafs_ext);
0549 }
0550
0551 static void setup_platform_cpuid(void)
0552 {
0553 u32 eax, ebx, ecx, edx;
0554
0555
0556 eax = ebx = ecx = edx = 0;
0557 cpuid(&eax, &ebx, &ecx, &edx);
0558
0559
0560 if (ebx == 0x68747541)
0561 is_amd = true;
0562
0563
0564 leafs_basic = setup_cpuid_range(0x0);
0565 leafs_ext = setup_cpuid_range(0x80000000);
0566 }
0567
0568 static void usage(void)
0569 {
0570 printf("kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n"
0571 "\t-a|--all Show both bit flags and complex bit fields info\n"
0572 "\t-b|--bitflags Show boolean flags only\n"
0573 "\t-d|--detail Show details of the flag/fields (default)\n"
0574 "\t-f|--flags Specify the cpuid csv file\n"
0575 "\t-h|--help Show usage info\n"
0576 "\t-l|--leaf=index Specify the leaf you want to check\n"
0577 "\t-r|--raw Show raw cpuid data\n"
0578 "\t-s|--subleaf=sub Specify the subleaf you want to check\n"
0579 );
0580 }
0581
0582 static struct option opts[] = {
0583 { "all", no_argument, NULL, 'a' },
0584 { "bitflags", no_argument, NULL, 'b' },
0585 { "detail", no_argument, NULL, 'd' },
0586 { "file", required_argument, NULL, 'f' },
0587 { "help", no_argument, NULL, 'h'},
0588 { "leaf", required_argument, NULL, 'l'},
0589 { "raw", no_argument, NULL, 'r'},
0590 { "subleaf", required_argument, NULL, 's'},
0591 { NULL, 0, NULL, 0 }
0592 };
0593
0594 static int parse_options(int argc, char *argv[])
0595 {
0596 int c;
0597
0598 while ((c = getopt_long(argc, argv, "abdf:hl:rs:",
0599 opts, NULL)) != -1)
0600 switch (c) {
0601 case 'a':
0602 show_flags_only = false;
0603 break;
0604 case 'b':
0605 show_flags_only = true;
0606 break;
0607 case 'd':
0608 show_details = true;
0609 break;
0610 case 'f':
0611 user_csv = optarg;
0612 break;
0613 case 'h':
0614 usage();
0615 exit(1);
0616 break;
0617 case 'l':
0618
0619 user_index = strtoul(optarg, NULL, 0);
0620 break;
0621 case 'r':
0622 show_raw = true;
0623 break;
0624 case 's':
0625
0626 user_sub = strtoul(optarg, NULL, 0);
0627 break;
0628 default:
0629 printf("%s: Invalid option '%c'\n", argv[0], optopt);
0630 return -1;
0631 }
0632
0633 return 0;
0634 }
0635
0636
0637
0638
0639
0640
0641
0642
0643
0644 int main(int argc, char *argv[])
0645 {
0646 if (parse_options(argc, argv))
0647 return -1;
0648
0649
0650 setup_platform_cpuid();
0651
0652
0653 parse_text();
0654
0655 show_info();
0656 return 0;
0657 }