1
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
20struct bits_desc {
21
22 int start, end;
23
24 int value;
25 char simp[32];
26 char detail[256];
27};
28
29
30struct reg_desc {
31
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
52struct cpuid_func {
53
54
55
56
57 struct subleaf *leafs;
58 int nr;
59};
60
61struct cpuid_range {
62
63 struct cpuid_func *funcs;
64
65 int nr;
66 bool is_ext;
67};
68
69
70
71
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
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
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
147
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
194 if (!func->nr)
195 continue;
196
197
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
254
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
281
282
283
284
285
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
300
301
302
303
304
305
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
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
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
340 if ((int)index >= range->nr)
341 return -1;
342
343 func = &range->funcs[index];
344
345
346 if (!func->nr)
347 return 0;
348
349
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
372 buf = tokens[3];
373
374 end = strtok(buf, ":");
375 bdesc->end = strtoul(end, NULL, 0);
376 bdesc->start = bdesc->end;
377
378
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
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
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
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
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
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
519 raw_dump_range(leafs_basic);
520 raw_dump_range(leafs_ext);
521 return;
522 }
523
524 if (user_index != 0xFFFFFFFF) {
525
526 func = index_to_func(user_index);
527 if (!func)
528 return;
529
530
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
556 eax = ebx = ecx = edx = 0;
557 cpuid(&eax, &ebx, &ecx, &edx);
558
559
560 if (ebx == 0x68747541)
561 is_amd = true;
562
563
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' },
584 { "bitflags", no_argument, NULL, 'b' },
585 { "detail", no_argument, NULL, 'd' },
586 { "file", required_argument, NULL, 'f' },
587 { "help", no_argument, NULL, 'h'},
588 { "leaf", required_argument, NULL, 'l'},
589 { "raw", no_argument, NULL, 'r'},
590 { "subleaf", required_argument, NULL, 's'},
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
619 user_index = strtoul(optarg, NULL, 0);
620 break;
621 case 'r':
622 show_raw = true;
623 break;
624 case 's':
625
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
638
639
640
641
642
643
644int main(int argc, char *argv[])
645{
646 if (parse_options(argc, argv))
647 return -1;
648
649
650 setup_platform_cpuid();
651
652
653 parse_text();
654
655 show_info();
656 return 0;
657}
658