1 // SPDX-License-Identifier: GPL-2.0
10 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
12 typedef unsigned int u32;
13 typedef unsigned long long u64;
15 char *def_csv = "/usr/share/misc/cpuid.csv";
19 /* Cover both single-bit flag and multiple-bits fields */
21 /* start and end bits */
23 /* 0 or 1 for 1-bit flag */
29 /* descriptor info for eax/ebx/ecx/edx */
31 /* number of valid entries */
33 struct bits_desc descs[32];
47 u32 eax, ebx, ecx, edx;
48 struct reg_desc info[NR_REGS];
51 /* Represent one leaf (basic or extended) */
54 * Array of subleafs for this func, if there is no subleafs
55 * then the leafs[0] is the main leaf
57 struct subleaf *leafs;
62 /* array of main leafs */
63 struct cpuid_func *funcs;
64 /* number of valid leafs */
70 * basic: basic functions range: [0... ]
71 * ext: extended functions range: [0x80000000... ]
73 struct cpuid_range *leafs_basic, *leafs_ext;
77 static bool show_details;
79 static bool show_flags_only = true;
80 static u32 user_index = 0xFFFFFFFF;
81 static u32 user_sub = 0xFFFFFFFF;
84 static inline void cpuid(u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
86 /* ecx is often an input as well as an output. */
92 : "0" (*eax), "2" (*ecx));
95 static inline bool has_subleafs(u32 f)
97 if (f == 0x7 || f == 0xd)
120 static void leaf_print_raw(struct subleaf *leaf)
122 if (has_subleafs(leaf->index)) {
124 printf("0x%08x: subleafs:\n", leaf->index);
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);
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);
134 /* Return true is the input eax/ebx/ecx/edx are all zero */
135 static bool cpuid_store(struct cpuid_range *range, u32 f, int subleaf,
136 u32 a, u32 b, u32 c, u32 d)
138 struct cpuid_func *func;
139 struct subleaf *leaf;
142 if (a == 0 && b == 0 && c == 0 && d == 0)
146 * Cut off vendor-prefix from CPUID function as we're using it as an
147 * index into ->funcs.
149 func = &range->funcs[f & 0xffff];
152 func->leafs = malloc(sizeof(struct subleaf));
154 perror("malloc func leaf");
159 func->leafs = realloc(func->leafs, (s + 1) * sizeof(*leaf));
161 perror("realloc f->leafs");
166 leaf = &func->leafs[s];
178 static void raw_dump_range(struct cpuid_range *range)
183 printf("%s Leafs :\n", range->is_ext ? "Extended" : "Basic");
184 printf("================\n");
186 for (f = 0; (int)f < range->nr; f++) {
187 struct cpuid_func *func = &range->funcs[f];
193 /* Skip leaf without valid items */
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]);
203 #define MAX_SUBLEAF_NUM 32
204 struct cpuid_range *setup_cpuid_range(u32 input_eax)
206 u32 max_func, idx_func;
208 struct cpuid_range *range;
209 u32 eax, ebx, ecx, edx;
217 cpuid(&eax, &ebx, &ecx, &edx);
219 idx_func = (max_func & 0xffff) + 1;
221 range = malloc(sizeof(struct cpuid_range));
223 perror("malloc range");
225 if (input_eax & 0x80000000)
226 range->is_ext = true;
228 range->is_ext = false;
230 range->funcs = malloc(sizeof(struct cpuid_func) * idx_func);
232 perror("malloc range->funcs");
234 range->nr = idx_func;
235 memset(range->funcs, 0, sizeof(struct cpuid_func) * idx_func);
237 for (; f <= max_func; f++) {
241 cpuid(&eax, &ebx, &ecx, &edx);
242 allzero = cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
247 if (!has_subleafs(f))
250 max_subleaf = MAX_SUBLEAF_NUM;
253 * Some can provide the exact number of subleafs,
254 * others have to be tried (0xf)
256 if (f == 0x7 || f == 0x14 || f == 0x17 || f == 0x18)
257 max_subleaf = (eax & 0xff) + 1;
262 for (subleaf = 1; subleaf < max_subleaf; subleaf++) {
266 cpuid(&eax, &ebx, &ecx, &edx);
267 allzero = cpuid_store(range, f, subleaf,
280 * The basic row format for cpuid.csv is
281 * LEAF,SUBLEAF,register_name,bits,short name,long description
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)
287 static int parse_line(char *line)
291 struct cpuid_range *range;
292 struct cpuid_func *func;
293 struct subleaf *leaf;
308 struct reg_desc *reg;
309 struct bits_desc *bdesc;
313 /* Skip comments and NULL line */
314 if (line[0] == '#' || line[0] == '\n')
317 strncpy(buffer, line, 511);
320 for (i = 0; i < 5; i++) {
321 tokens[i] = strtok(str, ",");
326 tokens[5] = strtok(str, "\n");
330 /* index/main-leaf */
331 index = strtoull(tokens[0], NULL, 0);
333 if (index & 0x80000000)
339 /* Skip line parsing for non-existing indexes */
340 if ((int)index >= range->nr)
343 func = &range->funcs[index];
345 /* Return if the index has no valid item on this platform */
350 sub = strtoul(tokens[1], NULL, 0);
351 if ((int)sub > func->nr)
354 leaf = &func->leafs[sub];
357 if (strcasestr(buf, "EAX"))
359 else if (strcasestr(buf, "EBX"))
361 else if (strcasestr(buf, "ECX"))
363 else if (strcasestr(buf, "EDX"))
368 reg = &leaf->info[reg_index];
369 bdesc = ®->descs[reg->nr++];
371 /* bit flag or bits field */
374 end = strtok(buf, ":");
375 bdesc->end = strtoul(end, NULL, 0);
376 bdesc->start = bdesc->end;
378 /* start != NULL means it is bit fields */
379 start = strtok(NULL, ":");
381 bdesc->start = strtoul(start, NULL, 0);
383 strcpy(bdesc->simp, tokens[4]);
384 strcpy(bdesc->detail, tokens[5]);
388 printf("Warning: wrong line format:\n");
389 printf("\tline[%d]: %s\n", flines, line);
393 /* Parse csv file, and construct the array of all leafs and subleafs */
394 static void parse_text(void)
397 char *filename, *line = NULL;
404 filename = user_csv ? user_csv : def_csv;
405 file = fopen(filename, "r");
407 /* Fallback to a csv in the same dir */
408 file = fopen("./cpuid.csv", "r");
412 printf("Fail to open '%s'\n", filename);
417 ret = getline(&line, &len, file);
430 /* Decode every eax/ebx/ecx/edx */
431 static void decode_bits(u32 value, struct reg_desc *rdesc)
433 struct bits_desc *bdesc;
437 for (i = 0; i < rdesc->nr; i++) {
438 bdesc = &rdesc->descs[i];
440 start = bdesc->start;
443 /* single bit flag */
444 if (value & (1 << start))
445 printf("\t%-20s %s%s\n",
447 show_details ? "-" : "",
448 show_details ? bdesc->detail : ""
455 mask = ((u64)1 << (end - start + 1)) - 1;
456 printf("\t%-20s\t: 0x%-8x\t%s%s\n",
458 (value >> start) & mask,
459 show_details ? "-" : "",
460 show_details ? bdesc->detail : ""
466 static void show_leaf(struct subleaf *leaf)
472 leaf_print_raw(leaf);
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]);
480 static void show_func(struct cpuid_func *func)
487 for (i = 0; i < func->nr; i++)
488 show_leaf(&func->leafs[i]);
491 static void show_range(struct cpuid_range *range)
495 for (i = 0; i < range->nr; i++)
496 show_func(&range->funcs[i]);
499 static inline struct cpuid_func *index_to_func(u32 index)
501 struct cpuid_range *range;
503 range = (index & 0x80000000) ? leafs_ext : leafs_basic;
506 if (((index & 0xFFFF) + 1) > (u32)range->nr) {
507 printf("ERR: invalid input index (0x%x)\n", index);
510 return &range->funcs[index];
513 static void show_info(void)
515 struct cpuid_func *func;
518 /* Show all of the raw output of 'cpuid' instr */
519 raw_dump_range(leafs_basic);
520 raw_dump_range(leafs_ext);
524 if (user_index != 0xFFFFFFFF) {
525 /* Only show specific leaf/subleaf info */
526 func = index_to_func(user_index);
530 /* Dump the raw data also */
533 if (user_sub != 0xFFFFFFFF) {
534 if (user_sub + 1 <= (u32)func->nr) {
535 show_leaf(&func->leafs[user_sub]);
539 printf("ERR: invalid input subleaf (0x%x)\n", user_sub);
546 printf("CPU features:\n=============\n\n");
547 show_range(leafs_basic);
548 show_range(leafs_ext);
551 static void setup_platform_cpuid(void)
553 u32 eax, ebx, ecx, edx;
556 eax = ebx = ecx = edx = 0;
557 cpuid(&eax, &ebx, &ecx, &edx);
560 if (ebx == 0x68747541)
563 /* Setup leafs for the basic and extended range */
564 leafs_basic = setup_cpuid_range(0x0);
565 leafs_ext = setup_cpuid_range(0x80000000);
568 static void usage(void)
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"
582 static 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 */
594 static int parse_options(int argc, char *argv[])
598 while ((c = getopt_long(argc, argv, "abdf:hl:rs:",
602 show_flags_only = false;
605 show_flags_only = true;
619 user_index = strtoul(optarg, NULL, 0);
626 user_sub = strtoul(optarg, NULL, 0);
629 printf("%s: Invalid option '%c'\n", argv[0], optopt);
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
642 * 3. Print leafs info based on user options
644 int main(int argc, char *argv[])
646 if (parse_options(argc, argv))
649 /* Setup the cpuid leafs of current platform */
650 setup_platform_cpuid();
652 /* Read and parse the 'cpuid.csv' */