1 // SPDX-License-Identifier: GPL-2.0
10 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
11 #define min(a, b) (((a) < (b)) ? (a) : (b))
13 typedef unsigned int u32
;
14 typedef unsigned long long u64
;
16 char *def_csv
= "/usr/share/misc/cpuid.csv";
20 /* Cover both single-bit flag and multiple-bits fields */
22 /* start and end bits */
24 /* 0 or 1 for 1-bit flag */
30 /* descriptor info for eax/ebx/ecx/edx */
32 /* number of valid entries */
34 struct bits_desc descs
[32];
45 static const char * const reg_names
[] = {
46 "EAX", "EBX", "ECX", "EDX",
52 u32 eax
, ebx
, ecx
, edx
;
53 struct reg_desc info
[NR_REGS
];
56 /* Represent one leaf (basic or extended) */
59 * Array of subleafs for this func, if there is no subleafs
60 * then the leafs[0] is the main leaf
62 struct subleaf
*leafs
;
67 /* array of main leafs */
68 struct cpuid_func
*funcs
;
69 /* number of valid leafs */
75 * basic: basic functions range: [0... ]
76 * ext: extended functions range: [0x80000000... ]
78 struct cpuid_range
*leafs_basic
, *leafs_ext
;
81 static bool show_details
;
83 static bool show_flags_only
= true;
84 static u32 user_index
= 0xFFFFFFFF;
85 static u32 user_sub
= 0xFFFFFFFF;
88 static inline void cpuid(u32
*eax
, u32
*ebx
, u32
*ecx
, u32
*edx
)
90 /* ecx is often an input as well as an output. */
96 : "0" (*eax
), "2" (*ecx
));
99 static inline bool has_subleafs(u32 f
)
101 u32 with_subleaves
[] = {
102 0x4, 0x7, 0xb, 0xd, 0xf, 0x10, 0x12,
103 0x14, 0x17, 0x18, 0x1b, 0x1d, 0x1f, 0x23,
104 0x8000001d, 0x80000020, 0x80000026,
107 for (unsigned i
= 0; i
< ARRAY_SIZE(with_subleaves
); i
++)
108 if (f
== with_subleaves
[i
])
114 static void leaf_print_raw(struct subleaf
*leaf
)
116 if (has_subleafs(leaf
->index
)) {
118 printf("0x%08x: subleafs:\n", leaf
->index
);
120 printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
121 leaf
->sub
, leaf
->eax
, leaf
->ebx
, leaf
->ecx
, leaf
->edx
);
123 printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
124 leaf
->index
, leaf
->eax
, leaf
->ebx
, leaf
->ecx
, leaf
->edx
);
128 /* Return true is the input eax/ebx/ecx/edx are all zero */
129 static bool cpuid_store(struct cpuid_range
*range
, u32 f
, int subleaf
,
130 u32 a
, u32 b
, u32 c
, u32 d
)
132 struct cpuid_func
*func
;
133 struct subleaf
*leaf
;
136 if (a
== 0 && b
== 0 && c
== 0 && d
== 0)
140 * Cut off vendor-prefix from CPUID function as we're using it as an
141 * index into ->funcs.
143 func
= &range
->funcs
[f
& 0xffff];
146 func
->leafs
= malloc(sizeof(struct subleaf
));
148 perror("malloc func leaf");
153 func
->leafs
= realloc(func
->leafs
, (s
+ 1) * sizeof(*leaf
));
155 perror("realloc f->leafs");
160 leaf
= &func
->leafs
[s
];
172 static void raw_dump_range(struct cpuid_range
*range
)
177 printf("%s Leafs :\n", range
->is_ext
? "Extended" : "Basic");
178 printf("================\n");
180 for (f
= 0; (int)f
< range
->nr
; f
++) {
181 struct cpuid_func
*func
= &range
->funcs
[f
];
187 /* Skip leaf without valid items */
191 /* First item is the main leaf, followed by all subleafs */
192 for (i
= 0; i
< func
->nr
; i
++)
193 leaf_print_raw(&func
->leafs
[i
]);
197 #define MAX_SUBLEAF_NUM 64
198 struct cpuid_range
*setup_cpuid_range(u32 input_eax
)
200 u32 max_func
, idx_func
, subleaf
, max_subleaf
;
201 u32 eax
, ebx
, ecx
, edx
, f
= input_eax
;
202 struct cpuid_range
*range
;
208 cpuid(&eax
, &ebx
, &ecx
, &edx
);
210 idx_func
= (max_func
& 0xffff) + 1;
212 range
= malloc(sizeof(struct cpuid_range
));
214 perror("malloc range");
216 if (input_eax
& 0x80000000)
217 range
->is_ext
= true;
219 range
->is_ext
= false;
221 range
->funcs
= malloc(sizeof(struct cpuid_func
) * idx_func
);
223 perror("malloc range->funcs");
225 range
->nr
= idx_func
;
226 memset(range
->funcs
, 0, sizeof(struct cpuid_func
) * idx_func
);
228 for (; f
<= max_func
; f
++) {
232 cpuid(&eax
, &ebx
, &ecx
, &edx
);
233 allzero
= cpuid_store(range
, f
, subleaf
, eax
, ebx
, ecx
, edx
);
237 if (!has_subleafs(f
))
240 max_subleaf
= MAX_SUBLEAF_NUM
;
243 * Some can provide the exact number of subleafs,
244 * others have to be tried (0xf)
246 if (f
== 0x7 || f
== 0x14 || f
== 0x17 || f
== 0x18 || f
== 0x1d)
247 max_subleaf
= min((eax
& 0xff) + 1, max_subleaf
);
259 for (subleaf
= 1; subleaf
< max_subleaf
; subleaf
++) {
263 cpuid(&eax
, &ebx
, &ecx
, &edx
);
264 allzero
= cpuid_store(range
, f
, subleaf
,
276 * The basic row format for cpuid.csv is
277 * LEAF,SUBLEAF,register_name,bits,short name,long description
280 * 0, 0, EAX, 31:0, max_basic_leafs, Max input value for supported subleafs
281 * 1, 0, ECX, 0, sse3, Streaming SIMD Extensions 3(SSE3)
283 static int parse_line(char *line
)
287 struct cpuid_range
*range
;
288 struct cpuid_func
*func
;
289 struct subleaf
*leaf
;
304 struct reg_desc
*reg
;
305 struct bits_desc
*bdesc
;
308 u32 subleaf_start
, subleaf_end
;
309 unsigned bit_start
, bit_end
;
311 /* Skip comments and NULL line */
312 if (line
[0] == '#' || line
[0] == '\n')
315 strncpy(buffer
, line
, 511);
318 for (i
= 0; i
< 5; i
++) {
319 tokens
[i
] = strtok(str
, ",");
324 tokens
[5] = strtok(str
, "\n");
328 /* index/main-leaf */
329 index
= strtoull(tokens
[0], NULL
, 0);
331 if (index
& 0x80000000)
337 /* Skip line parsing for non-existing indexes */
338 if ((int)index
>= range
->nr
)
341 func
= &range
->funcs
[index
];
343 /* Return if the index has no valid item on this platform */
349 end
= strtok(buf
, ":");
350 start
= strtok(NULL
, ":");
351 subleaf_end
= strtoul(end
, NULL
, 0);
353 /* A subleaf range is given? */
355 subleaf_start
= strtoul(start
, NULL
, 0);
356 subleaf_end
= min(subleaf_end
, (u32
)(func
->nr
- 1));
357 if (subleaf_start
> subleaf_end
)
360 subleaf_start
= subleaf_end
;
361 if (subleaf_start
> (u32
)(func
->nr
- 1))
367 if (strcasestr(buf
, "EAX"))
369 else if (strcasestr(buf
, "EBX"))
371 else if (strcasestr(buf
, "ECX"))
373 else if (strcasestr(buf
, "EDX"))
378 /* bit flag or bits field */
380 end
= strtok(buf
, ":");
381 start
= strtok(NULL
, ":");
382 bit_end
= strtoul(end
, NULL
, 0);
383 bit_start
= (start
) ? strtoul(start
, NULL
, 0) : bit_end
;
385 for (sub
= subleaf_start
; sub
<= subleaf_end
; sub
++) {
386 leaf
= &func
->leafs
[sub
];
387 reg
= &leaf
->info
[reg_index
];
388 bdesc
= ®
->descs
[reg
->nr
++];
390 bdesc
->end
= bit_end
;
391 bdesc
->start
= bit_start
;
392 strcpy(bdesc
->simp
, strtok(tokens
[4], " \t"));
393 strcpy(bdesc
->detail
, tokens
[5]);
398 printf("Warning: wrong line format:\n");
399 printf("\tline[%d]: %s\n", flines
, line
);
403 /* Parse csv file, and construct the array of all leafs and subleafs */
404 static void parse_text(void)
407 char *filename
, *line
= NULL
;
414 filename
= user_csv
? user_csv
: def_csv
;
415 file
= fopen(filename
, "r");
417 /* Fallback to a csv in the same dir */
418 file
= fopen("./cpuid.csv", "r");
422 printf("Fail to open '%s'\n", filename
);
427 ret
= getline(&line
, &len
, file
);
440 /* Decode every eax/ebx/ecx/edx */
441 static void decode_bits(u32 value
, struct reg_desc
*rdesc
, enum cpuid_reg reg
)
443 struct bits_desc
*bdesc
;
449 printf("\t %s: 0x%08x\n", reg_names
[reg
], value
);
453 for (i
= 0; i
< rdesc
->nr
; i
++) {
454 bdesc
= &rdesc
->descs
[i
];
456 start
= bdesc
->start
;
459 /* single bit flag */
460 if (value
& (1 << start
))
461 printf("\t%-20s %s%s%s\n",
463 show_flags_only
? "" : "\t\t\t",
464 show_details
? "-" : "",
465 show_details
? bdesc
->detail
: ""
472 mask
= ((u64
)1 << (end
- start
+ 1)) - 1;
473 printf("\t%-20s\t: 0x%-8x\t%s%s\n",
475 (value
>> start
) & mask
,
476 show_details
? "-" : "",
477 show_details
? bdesc
->detail
: ""
483 static void show_leaf(struct subleaf
*leaf
)
489 leaf_print_raw(leaf
);
492 printf("CPUID_0x%x_ECX[0x%x]:\n",
493 leaf
->index
, leaf
->sub
);
496 decode_bits(leaf
->eax
, &leaf
->info
[R_EAX
], R_EAX
);
497 decode_bits(leaf
->ebx
, &leaf
->info
[R_EBX
], R_EBX
);
498 decode_bits(leaf
->ecx
, &leaf
->info
[R_ECX
], R_ECX
);
499 decode_bits(leaf
->edx
, &leaf
->info
[R_EDX
], R_EDX
);
501 if (!show_raw
&& show_details
)
505 static void show_func(struct cpuid_func
*func
)
512 for (i
= 0; i
< func
->nr
; i
++)
513 show_leaf(&func
->leafs
[i
]);
516 static void show_range(struct cpuid_range
*range
)
520 for (i
= 0; i
< range
->nr
; i
++)
521 show_func(&range
->funcs
[i
]);
524 static inline struct cpuid_func
*index_to_func(u32 index
)
526 struct cpuid_range
*range
;
529 range
= (index
& 0x80000000) ? leafs_ext
: leafs_basic
;
530 func_idx
= index
& 0xffff;
532 if ((func_idx
+ 1) > (u32
)range
->nr
) {
533 printf("ERR: invalid input index (0x%x)\n", index
);
536 return &range
->funcs
[func_idx
];
539 static void show_info(void)
541 struct cpuid_func
*func
;
544 /* Show all of the raw output of 'cpuid' instr */
545 raw_dump_range(leafs_basic
);
546 raw_dump_range(leafs_ext
);
550 if (user_index
!= 0xFFFFFFFF) {
551 /* Only show specific leaf/subleaf info */
552 func
= index_to_func(user_index
);
556 /* Dump the raw data also */
559 if (user_sub
!= 0xFFFFFFFF) {
560 if (user_sub
+ 1 <= (u32
)func
->nr
) {
561 show_leaf(&func
->leafs
[user_sub
]);
565 printf("ERR: invalid input subleaf (0x%x)\n", user_sub
);
572 printf("CPU features:\n=============\n\n");
573 show_range(leafs_basic
);
574 show_range(leafs_ext
);
577 static void setup_platform_cpuid(void)
579 u32 eax
, ebx
, ecx
, edx
;
582 eax
= ebx
= ecx
= edx
= 0;
583 cpuid(&eax
, &ebx
, &ecx
, &edx
);
586 if (ebx
== 0x68747541)
589 /* Setup leafs for the basic and extended range */
590 leafs_basic
= setup_cpuid_range(0x0);
591 leafs_ext
= setup_cpuid_range(0x80000000);
594 static void usage(void)
596 printf("kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n"
597 "\t-a|--all Show both bit flags and complex bit fields info\n"
598 "\t-b|--bitflags Show boolean flags only\n"
599 "\t-d|--detail Show details of the flag/fields (default)\n"
600 "\t-f|--flags Specify the cpuid csv file\n"
601 "\t-h|--help Show usage info\n"
602 "\t-l|--leaf=index Specify the leaf you want to check\n"
603 "\t-r|--raw Show raw cpuid data\n"
604 "\t-s|--subleaf=sub Specify the subleaf you want to check\n"
608 static struct option opts
[] = {
609 { "all", no_argument
, NULL
, 'a' }, /* show both bit flags and fields */
610 { "bitflags", no_argument
, NULL
, 'b' }, /* only show bit flags, default on */
611 { "detail", no_argument
, NULL
, 'd' }, /* show detail descriptions */
612 { "file", required_argument
, NULL
, 'f' }, /* use user's cpuid file */
613 { "help", no_argument
, NULL
, 'h'}, /* show usage */
614 { "leaf", required_argument
, NULL
, 'l'}, /* only check a specific leaf */
615 { "raw", no_argument
, NULL
, 'r'}, /* show raw CPUID leaf data */
616 { "subleaf", required_argument
, NULL
, 's'}, /* check a specific subleaf */
620 static int parse_options(int argc
, char *argv
[])
624 while ((c
= getopt_long(argc
, argv
, "abdf:hl:rs:",
628 show_flags_only
= false;
631 show_flags_only
= true;
645 user_index
= strtoul(optarg
, NULL
, 0);
652 user_sub
= strtoul(optarg
, NULL
, 0);
655 printf("%s: Invalid option '%c'\n", argv
[0], optopt
);
663 * Do 4 things in turn:
664 * 1. Parse user options
665 * 2. Parse and store all the CPUID leaf data supported on this platform
666 * 2. Parse the csv file, while skipping leafs which are not available
668 * 3. Print leafs info based on user options
670 int main(int argc
, char *argv
[])
672 if (parse_options(argc
, argv
))
675 /* Setup the cpuid leafs of current platform */
676 setup_platform_cpuid();
678 /* Read and parse the 'cpuid.csv' */