Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/arch/x86/kcpuid/kcpuid.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0
2
#define _GNU_SOURCE
3
4
#include <cpuid.h>
5
#include <err.h>
6
#include <getopt.h>
7
#include <stdbool.h>
8
#include <stdio.h>
9
#include <stdlib.h>
10
#include <string.h>
11
12
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
13
#define min(a, b) (((a) < (b)) ? (a) : (b))
14
#define __noreturn __attribute__((__noreturn__))
15
16
typedef unsigned int u32;
17
typedef unsigned long long u64;
18
19
char *def_csv = "/usr/share/misc/cpuid.csv";
20
char *user_csv;
21
22
23
/* Cover both single-bit flag and multiple-bits fields */
24
struct bits_desc {
25
/* start and end bits */
26
int start, end;
27
/* 0 or 1 for 1-bit flag */
28
int value;
29
char simp[32];
30
char detail[256];
31
};
32
33
/* descriptor info for eax/ebx/ecx/edx */
34
struct reg_desc {
35
/* number of valid entries */
36
int nr;
37
struct bits_desc descs[32];
38
};
39
40
enum cpuid_reg {
41
R_EAX = 0,
42
R_EBX,
43
R_ECX,
44
R_EDX,
45
NR_REGS
46
};
47
48
static const char * const reg_names[] = {
49
"EAX", "EBX", "ECX", "EDX",
50
};
51
52
struct subleaf {
53
u32 index;
54
u32 sub;
55
u32 output[NR_REGS];
56
struct reg_desc info[NR_REGS];
57
};
58
59
/* Represent one leaf (basic or extended) */
60
struct cpuid_func {
61
/*
62
* Array of subleafs for this func, if there is no subleafs
63
* then the leafs[0] is the main leaf
64
*/
65
struct subleaf *leafs;
66
int nr;
67
};
68
69
enum range_index {
70
RANGE_STD = 0, /* Standard */
71
RANGE_EXT = 0x80000000, /* Extended */
72
RANGE_TSM = 0x80860000, /* Transmeta */
73
RANGE_CTR = 0xc0000000, /* Centaur/Zhaoxin */
74
};
75
76
#define CPUID_INDEX_MASK 0xffff0000
77
#define CPUID_FUNCTION_MASK (~CPUID_INDEX_MASK)
78
79
struct cpuid_range {
80
/* array of main leafs */
81
struct cpuid_func *funcs;
82
/* number of valid leafs */
83
int nr;
84
enum range_index index;
85
};
86
87
static struct cpuid_range ranges[] = {
88
{ .index = RANGE_STD, },
89
{ .index = RANGE_EXT, },
90
{ .index = RANGE_TSM, },
91
{ .index = RANGE_CTR, },
92
};
93
94
static char *range_to_str(struct cpuid_range *range)
95
{
96
switch (range->index) {
97
case RANGE_STD: return "Standard";
98
case RANGE_EXT: return "Extended";
99
case RANGE_TSM: return "Transmeta";
100
case RANGE_CTR: return "Centaur";
101
default: return NULL;
102
}
103
}
104
105
#define __for_each_cpuid_range(range, __condition) \
106
for (unsigned int i = 0; \
107
i < ARRAY_SIZE(ranges) && ((range) = &ranges[i]) && (__condition); \
108
i++)
109
110
#define for_each_valid_cpuid_range(range) __for_each_cpuid_range(range, (range)->nr != 0)
111
#define for_each_cpuid_range(range) __for_each_cpuid_range(range, true)
112
113
struct cpuid_range *index_to_cpuid_range(u32 index)
114
{
115
u32 func_idx = index & CPUID_FUNCTION_MASK;
116
u32 range_idx = index & CPUID_INDEX_MASK;
117
struct cpuid_range *range;
118
119
for_each_valid_cpuid_range(range) {
120
if (range->index == range_idx && (u32)range->nr > func_idx)
121
return range;
122
}
123
124
return NULL;
125
}
126
127
static bool show_details;
128
static bool show_raw;
129
static bool show_flags_only = true;
130
static u32 user_index = 0xFFFFFFFF;
131
static u32 user_sub = 0xFFFFFFFF;
132
static int flines;
133
134
/*
135
* Force using <cpuid.h> __cpuid_count() instead of __cpuid(). The
136
* latter leaves ECX uninitialized, which can break CPUID queries.
137
*/
138
139
#define cpuid(leaf, a, b, c, d) \
140
__cpuid_count(leaf, 0, a, b, c, d)
141
142
#define cpuid_count(leaf, subleaf, a, b, c, d) \
143
__cpuid_count(leaf, subleaf, a, b, c, d)
144
145
static inline bool has_subleafs(u32 f)
146
{
147
u32 with_subleaves[] = {
148
0x4, 0x7, 0xb, 0xd, 0xf, 0x10, 0x12,
149
0x14, 0x17, 0x18, 0x1b, 0x1d, 0x1f, 0x23,
150
0x8000001d, 0x80000020, 0x80000026,
151
};
152
153
for (unsigned i = 0; i < ARRAY_SIZE(with_subleaves); i++)
154
if (f == with_subleaves[i])
155
return true;
156
157
return false;
158
}
159
160
static void leaf_print_raw(struct subleaf *leaf)
161
{
162
if (has_subleafs(leaf->index)) {
163
if (leaf->sub == 0)
164
printf("0x%08x: subleafs:\n", leaf->index);
165
166
printf(" %2d: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n", leaf->sub,
167
leaf->output[0], leaf->output[1], leaf->output[2], leaf->output[3]);
168
} else {
169
printf("0x%08x: EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n", leaf->index,
170
leaf->output[0], leaf->output[1], leaf->output[2], leaf->output[3]);
171
}
172
}
173
174
/* Return true is the input eax/ebx/ecx/edx are all zero */
175
static bool cpuid_store(struct cpuid_range *range, u32 f, int subleaf,
176
u32 a, u32 b, u32 c, u32 d)
177
{
178
struct cpuid_func *func;
179
struct subleaf *leaf;
180
int s = 0;
181
182
if (a == 0 && b == 0 && c == 0 && d == 0)
183
return true;
184
185
/*
186
* Cut off vendor-prefix from CPUID function as we're using it as an
187
* index into ->funcs.
188
*/
189
func = &range->funcs[f & CPUID_FUNCTION_MASK];
190
191
if (!func->leafs) {
192
func->leafs = malloc(sizeof(struct subleaf));
193
if (!func->leafs)
194
err(EXIT_FAILURE, NULL);
195
196
func->nr = 1;
197
} else {
198
s = func->nr;
199
func->leafs = realloc(func->leafs, (s + 1) * sizeof(*leaf));
200
if (!func->leafs)
201
err(EXIT_FAILURE, NULL);
202
203
func->nr++;
204
}
205
206
leaf = &func->leafs[s];
207
208
leaf->index = f;
209
leaf->sub = subleaf;
210
leaf->output[R_EAX] = a;
211
leaf->output[R_EBX] = b;
212
leaf->output[R_ECX] = c;
213
leaf->output[R_EDX] = d;
214
215
return false;
216
}
217
218
static void raw_dump_range(struct cpuid_range *range)
219
{
220
printf("%s Leafs :\n", range_to_str(range));
221
printf("================\n");
222
223
for (u32 f = 0; (int)f < range->nr; f++) {
224
struct cpuid_func *func = &range->funcs[f];
225
226
/* Skip leaf without valid items */
227
if (!func->nr)
228
continue;
229
230
/* First item is the main leaf, followed by all subleafs */
231
for (int i = 0; i < func->nr; i++)
232
leaf_print_raw(&func->leafs[i]);
233
}
234
}
235
236
#define MAX_SUBLEAF_NUM 64
237
#define MAX_RANGE_INDEX_OFFSET 0xff
238
void setup_cpuid_range(struct cpuid_range *range)
239
{
240
u32 max_func, range_funcs_sz;
241
u32 eax, ebx, ecx, edx;
242
243
cpuid(range->index, max_func, ebx, ecx, edx);
244
245
/*
246
* If the CPUID range's maximum function value is garbage, then it
247
* is not recognized by this CPU. Set the range's number of valid
248
* leaves to zero so that for_each_valid_cpu_range() can ignore it.
249
*/
250
if (max_func < range->index || max_func > (range->index + MAX_RANGE_INDEX_OFFSET)) {
251
range->nr = 0;
252
return;
253
}
254
255
range->nr = (max_func & CPUID_FUNCTION_MASK) + 1;
256
range_funcs_sz = range->nr * sizeof(struct cpuid_func);
257
258
range->funcs = malloc(range_funcs_sz);
259
if (!range->funcs)
260
err(EXIT_FAILURE, NULL);
261
262
memset(range->funcs, 0, range_funcs_sz);
263
264
for (u32 f = range->index; f <= max_func; f++) {
265
u32 max_subleaf = MAX_SUBLEAF_NUM;
266
bool allzero;
267
268
cpuid(f, eax, ebx, ecx, edx);
269
270
allzero = cpuid_store(range, f, 0, eax, ebx, ecx, edx);
271
if (allzero)
272
continue;
273
274
if (!has_subleafs(f))
275
continue;
276
277
/*
278
* Some can provide the exact number of subleafs,
279
* others have to be tried (0xf)
280
*/
281
if (f == 0x7 || f == 0x14 || f == 0x17 || f == 0x18 || f == 0x1d)
282
max_subleaf = min((eax & 0xff) + 1, max_subleaf);
283
if (f == 0xb)
284
max_subleaf = 2;
285
if (f == 0x1f)
286
max_subleaf = 6;
287
if (f == 0x23)
288
max_subleaf = 4;
289
if (f == 0x80000020)
290
max_subleaf = 4;
291
if (f == 0x80000026)
292
max_subleaf = 5;
293
294
for (u32 subleaf = 1; subleaf < max_subleaf; subleaf++) {
295
cpuid_count(f, subleaf, eax, ebx, ecx, edx);
296
297
allzero = cpuid_store(range, f, subleaf, eax, ebx, ecx, edx);
298
if (allzero)
299
continue;
300
}
301
302
}
303
}
304
305
/*
306
* The basic row format for cpuid.csv is
307
* LEAF,SUBLEAF,register_name,bits,short name,long description
308
*
309
* like:
310
* 0, 0, EAX, 31:0, max_basic_leafs, Max input value for supported subleafs
311
* 1, 0, ECX, 0, sse3, Streaming SIMD Extensions 3(SSE3)
312
*/
313
static void parse_line(char *line)
314
{
315
char *str;
316
struct cpuid_range *range;
317
struct cpuid_func *func;
318
struct subleaf *leaf;
319
u32 index;
320
char buffer[512];
321
char *buf;
322
/*
323
* Tokens:
324
* 1. leaf
325
* 2. subleaf
326
* 3. register
327
* 4. bits
328
* 5. short name
329
* 6. long detail
330
*/
331
char *tokens[6];
332
struct reg_desc *reg;
333
struct bits_desc *bdesc;
334
int reg_index;
335
char *start, *end;
336
u32 subleaf_start, subleaf_end;
337
unsigned bit_start, bit_end;
338
339
/* Skip comments and NULL line */
340
if (line[0] == '#' || line[0] == '\n')
341
return;
342
343
strncpy(buffer, line, 511);
344
buffer[511] = 0;
345
str = buffer;
346
for (int i = 0; i < 5; i++) {
347
tokens[i] = strtok(str, ",");
348
if (!tokens[i])
349
goto err_exit;
350
str = NULL;
351
}
352
tokens[5] = strtok(str, "\n");
353
if (!tokens[5])
354
goto err_exit;
355
356
/* index/main-leaf */
357
index = strtoull(tokens[0], NULL, 0);
358
359
/*
360
* Skip line parsing if the index is not covered by known-valid
361
* CPUID ranges on this CPU.
362
*/
363
range = index_to_cpuid_range(index);
364
if (!range)
365
return;
366
367
/* Skip line parsing if the index CPUID output is all zero */
368
index &= CPUID_FUNCTION_MASK;
369
func = &range->funcs[index];
370
if (!func->nr)
371
return;
372
373
/* subleaf */
374
buf = tokens[1];
375
end = strtok(buf, ":");
376
start = strtok(NULL, ":");
377
subleaf_end = strtoul(end, NULL, 0);
378
379
/* A subleaf range is given? */
380
if (start) {
381
subleaf_start = strtoul(start, NULL, 0);
382
subleaf_end = min(subleaf_end, (u32)(func->nr - 1));
383
if (subleaf_start > subleaf_end)
384
return;
385
} else {
386
subleaf_start = subleaf_end;
387
if (subleaf_start > (u32)(func->nr - 1))
388
return;
389
}
390
391
/* register */
392
buf = tokens[2];
393
if (strcasestr(buf, "EAX"))
394
reg_index = R_EAX;
395
else if (strcasestr(buf, "EBX"))
396
reg_index = R_EBX;
397
else if (strcasestr(buf, "ECX"))
398
reg_index = R_ECX;
399
else if (strcasestr(buf, "EDX"))
400
reg_index = R_EDX;
401
else
402
goto err_exit;
403
404
/* bit flag or bits field */
405
buf = tokens[3];
406
end = strtok(buf, ":");
407
start = strtok(NULL, ":");
408
bit_end = strtoul(end, NULL, 0);
409
bit_start = (start) ? strtoul(start, NULL, 0) : bit_end;
410
411
for (u32 sub = subleaf_start; sub <= subleaf_end; sub++) {
412
leaf = &func->leafs[sub];
413
reg = &leaf->info[reg_index];
414
bdesc = &reg->descs[reg->nr++];
415
416
bdesc->end = bit_end;
417
bdesc->start = bit_start;
418
strcpy(bdesc->simp, strtok(tokens[4], " \t"));
419
strcpy(bdesc->detail, tokens[5]);
420
}
421
return;
422
423
err_exit:
424
warnx("Wrong line format:\n"
425
"\tline[%d]: %s", flines, line);
426
}
427
428
/* Parse csv file, and construct the array of all leafs and subleafs */
429
static void parse_text(void)
430
{
431
FILE *file;
432
char *filename, *line = NULL;
433
size_t len = 0;
434
int ret;
435
436
if (show_raw)
437
return;
438
439
filename = user_csv ? user_csv : def_csv;
440
file = fopen(filename, "r");
441
if (!file) {
442
/* Fallback to a csv in the same dir */
443
file = fopen("./cpuid.csv", "r");
444
}
445
446
if (!file)
447
err(EXIT_FAILURE, "%s", filename);
448
449
while (1) {
450
ret = getline(&line, &len, file);
451
flines++;
452
if (ret > 0)
453
parse_line(line);
454
455
if (feof(file))
456
break;
457
}
458
459
fclose(file);
460
}
461
462
static void show_reg(const struct reg_desc *rdesc, u32 value)
463
{
464
const struct bits_desc *bdesc;
465
int start, end;
466
u32 mask;
467
468
for (int i = 0; i < rdesc->nr; i++) {
469
bdesc = &rdesc->descs[i];
470
471
start = bdesc->start;
472
end = bdesc->end;
473
if (start == end) {
474
/* single bit flag */
475
if (value & (1 << start))
476
printf("\t%-20s %s%s%s\n",
477
bdesc->simp,
478
show_flags_only ? "" : "\t\t\t",
479
show_details ? "-" : "",
480
show_details ? bdesc->detail : ""
481
);
482
} else {
483
/* bit fields */
484
if (show_flags_only)
485
continue;
486
487
mask = ((u64)1 << (end - start + 1)) - 1;
488
printf("\t%-20s\t: 0x%-8x\t%s%s\n",
489
bdesc->simp,
490
(value >> start) & mask,
491
show_details ? "-" : "",
492
show_details ? bdesc->detail : ""
493
);
494
}
495
}
496
}
497
498
static void show_reg_header(bool has_entries, u32 leaf, u32 subleaf, const char *reg_name)
499
{
500
if (show_details && has_entries)
501
printf("CPUID_0x%x_%s[0x%x]:\n", leaf, reg_name, subleaf);
502
}
503
504
static void show_leaf(struct subleaf *leaf)
505
{
506
if (show_raw)
507
leaf_print_raw(leaf);
508
509
for (int i = R_EAX; i < NR_REGS; i++) {
510
show_reg_header((leaf->info[i].nr > 0), leaf->index, leaf->sub, reg_names[i]);
511
show_reg(&leaf->info[i], leaf->output[i]);
512
}
513
514
if (!show_raw && show_details)
515
printf("\n");
516
}
517
518
static void show_func(struct cpuid_func *func)
519
{
520
for (int i = 0; i < func->nr; i++)
521
show_leaf(&func->leafs[i]);
522
}
523
524
static void show_range(struct cpuid_range *range)
525
{
526
for (int i = 0; i < range->nr; i++)
527
show_func(&range->funcs[i]);
528
}
529
530
static inline struct cpuid_func *index_to_func(u32 index)
531
{
532
u32 func_idx = index & CPUID_FUNCTION_MASK;
533
struct cpuid_range *range;
534
535
range = index_to_cpuid_range(index);
536
if (!range)
537
return NULL;
538
539
return &range->funcs[func_idx];
540
}
541
542
static void show_info(void)
543
{
544
struct cpuid_range *range;
545
struct cpuid_func *func;
546
547
if (show_raw) {
548
/* Show all of the raw output of 'cpuid' instr */
549
for_each_valid_cpuid_range(range)
550
raw_dump_range(range);
551
return;
552
}
553
554
if (user_index != 0xFFFFFFFF) {
555
/* Only show specific leaf/subleaf info */
556
func = index_to_func(user_index);
557
if (!func)
558
errx(EXIT_FAILURE, "Invalid input leaf (0x%x)", user_index);
559
560
/* Dump the raw data also */
561
show_raw = true;
562
563
if (user_sub != 0xFFFFFFFF) {
564
if (user_sub + 1 > (u32)func->nr) {
565
errx(EXIT_FAILURE, "Leaf 0x%x has no valid subleaf = 0x%x",
566
user_index, user_sub);
567
}
568
569
show_leaf(&func->leafs[user_sub]);
570
return;
571
}
572
573
show_func(func);
574
return;
575
}
576
577
printf("CPU features:\n=============\n\n");
578
for_each_valid_cpuid_range(range)
579
show_range(range);
580
}
581
582
static void __noreturn usage(int exit_code)
583
{
584
errx(exit_code, "kcpuid [-abdfhr] [-l leaf] [-s subleaf]\n"
585
"\t-a|--all Show both bit flags and complex bit fields info\n"
586
"\t-b|--bitflags Show boolean flags only\n"
587
"\t-d|--detail Show details of the flag/fields (default)\n"
588
"\t-f|--flags Specify the CPUID CSV file\n"
589
"\t-h|--help Show usage info\n"
590
"\t-l|--leaf=index Specify the leaf you want to check\n"
591
"\t-r|--raw Show raw CPUID data\n"
592
"\t-s|--subleaf=sub Specify the subleaf you want to check"
593
);
594
}
595
596
static struct option opts[] = {
597
{ "all", no_argument, NULL, 'a' }, /* show both bit flags and fields */
598
{ "bitflags", no_argument, NULL, 'b' }, /* only show bit flags, default on */
599
{ "detail", no_argument, NULL, 'd' }, /* show detail descriptions */
600
{ "file", required_argument, NULL, 'f' }, /* use user's cpuid file */
601
{ "help", no_argument, NULL, 'h'}, /* show usage */
602
{ "leaf", required_argument, NULL, 'l'}, /* only check a specific leaf */
603
{ "raw", no_argument, NULL, 'r'}, /* show raw CPUID leaf data */
604
{ "subleaf", required_argument, NULL, 's'}, /* check a specific subleaf */
605
{ NULL, 0, NULL, 0 }
606
};
607
608
static void parse_options(int argc, char *argv[])
609
{
610
int c;
611
612
while ((c = getopt_long(argc, argv, "abdf:hl:rs:",
613
opts, NULL)) != -1)
614
switch (c) {
615
case 'a':
616
show_flags_only = false;
617
break;
618
case 'b':
619
show_flags_only = true;
620
break;
621
case 'd':
622
show_details = true;
623
break;
624
case 'f':
625
user_csv = optarg;
626
break;
627
case 'h':
628
usage(EXIT_SUCCESS);
629
case 'l':
630
/* main leaf */
631
user_index = strtoul(optarg, NULL, 0);
632
break;
633
case 'r':
634
show_raw = true;
635
break;
636
case 's':
637
/* subleaf */
638
user_sub = strtoul(optarg, NULL, 0);
639
break;
640
default:
641
usage(EXIT_FAILURE);
642
}
643
}
644
645
/*
646
* Do 4 things in turn:
647
* 1. Parse user options
648
* 2. Parse and store all the CPUID leaf data supported on this platform
649
* 2. Parse the csv file, while skipping leafs which are not available
650
* on this platform
651
* 3. Print leafs info based on user options
652
*/
653
int main(int argc, char *argv[])
654
{
655
struct cpuid_range *range;
656
657
parse_options(argc, argv);
658
659
/* Setup the cpuid leafs of current platform */
660
for_each_cpuid_range(range)
661
setup_cpuid_range(range);
662
663
/* Read and parse the 'cpuid.csv' */
664
parse_text();
665
666
show_info();
667
return 0;
668
}
669
670