1 /*
2 * Copyright © 2015-2021 Inria. All rights reserved.
3 * See COPYING in top-level directory.
4 */
5
6 #include "private/autogen/config.h"
7 #include "hwloc.h"
8 #include "misc.h"
9
10 #include <stdio.h>
11 #include <fcntl.h>
12 #include <sys/stat.h>
13 #include <errno.h>
14
15 #include "private/cpuid-x86.h"
16
17 #if defined(HWLOC_WIN_SYS) && !defined(__CYGWIN__)
18 #include <direct.h>
19 #define mkdir(name, mode) _mkdir(name)
20 #include <io.h>
21 #define access _access
22 #ifndef X_OK
23 #define X_OK 00 /* meaningless */
24 #endif
25 #ifndef W_OK
26 #define W_OK 02
27 #endif
28 #endif
29
30 static int verbose = 1;
31
dump_one_cpuid(FILE * output,unsigned * regs,unsigned inregmask)32 static void dump_one_cpuid(FILE *output, unsigned *regs, unsigned inregmask)
33 {
34 unsigned i;
35
36 /* clear unused inputs */
37 for(i=0; i<4; i++)
38 if (!(inregmask & (1<<i)))
39 regs[i] = 0;
40
41 fprintf(output, "%x %x %x %x %x", inregmask, regs[0], regs[1], regs[2], regs[3]);
42 hwloc_x86_cpuid(®s[0], ®s[1], ®s[2], ®s[3]);
43 fprintf(output, " => %x %x %x %x\n", regs[0], regs[1], regs[2], regs[3]);
44 }
45
dump_one_proc(hwloc_topology_t topo,hwloc_obj_t pu,const char * path)46 static int dump_one_proc(hwloc_topology_t topo, hwloc_obj_t pu, const char *path)
47 {
48 unsigned regs[4];
49 unsigned highest_cpuid, highest_ext_cpuid;
50 unsigned i;
51 int has_intel_x2apic = 0;
52 int has_intel_sgx = 0;
53 int has_amd_topoext = 0;
54 FILE *output;
55 int err;
56
57 err = hwloc_set_cpubind(topo, pu->cpuset, HWLOC_CPUBIND_PROCESS);
58 if (err < 0) {
59 err = hwloc_set_cpubind(topo, pu->cpuset, HWLOC_CPUBIND_THREAD);
60 if (err < 0) {
61 fprintf(stderr, "Cannot bind to PU P#%u\n", pu->os_index);
62 return -1;
63 }
64 }
65
66 if (path) {
67 output = fopen(path, "w");
68 if (!output) {
69 fprintf(stderr, "Cannot open file '%s' for writing: %s\n", path, strerror(errno));
70 return -1;
71 }
72 if (verbose)
73 printf("Gathering CPUID of PU P#%u in path %s ...\n", pu->os_index, path);
74 } else {
75 output = stdout;
76 if (verbose)
77 printf("Gathering CPUID of PU P#%u on stdout ...\n", pu->os_index);
78 }
79
80 fprintf(output, "# mask e[abcd]x => e[abcd]x\n");
81
82 regs[0] = 0;
83 hwloc_x86_cpuid(®s[0], ®s[1], ®s[2], ®s[3]);
84 highest_cpuid = regs[0];
85 regs[0] = 0x80000000;
86 hwloc_x86_cpuid(®s[0], ®s[1], ®s[2], ®s[3]);
87 highest_ext_cpuid = regs[0];
88
89 /* 0x0 = Highest cpuid + Vendor string */
90 regs[0] = 0x0;
91 dump_one_cpuid(output, regs, 0x1);
92
93 /* 0x1 = Family, Model, Stepping, Topology, Features */
94 if (highest_cpuid >= 0x1) {
95 regs[0] = 0x1;
96 dump_one_cpuid(output, regs, 0x1);
97 if (regs[2] & (1 << 21))
98 has_intel_x2apic = 1;
99 }
100
101 /* 0x2 = Cache + TLB on Intel ; Reserved on AMD */
102 if (highest_cpuid >= 0x2) {
103 regs[0] = 0x2;
104 dump_one_cpuid(output, regs, 0x1);
105 }
106
107 /* 0x3 = Processor serial number on Intel P3, reserved otherwise ; Reserved on AMD */
108 if (highest_cpuid >= 0x3) {
109 regs[0] = 0x3;
110 dump_one_cpuid(output, regs, 0x1);
111 }
112
113 /* 0x4 = Caches on Intel ; Reserved on AMD */
114 if (highest_cpuid >= 0x4) {
115 for(i=0; ; i++) {
116 regs[0] = 0x4; regs[2] = i;
117 dump_one_cpuid(output, regs, 0x5);
118 if (!(regs[0] & 0x1f))
119 /* invalid, no more caches */
120 break;
121 }
122 }
123
124 /* 0x5 = Monitor/mwait */
125 if (highest_cpuid >= 0x5) {
126 regs[0] = 0x5;
127 dump_one_cpuid(output, regs, 0x1);
128 }
129
130 /* 0x6 = Thermal and Power management */
131 if (highest_cpuid >= 0x6) {
132 regs[0] = 0x6;
133 dump_one_cpuid(output, regs, 0x1);
134 }
135
136 /* 0x7 = Extended features */
137 if (highest_cpuid >= 0x7) {
138 unsigned max;
139 regs[0] = 0x7; regs[2] = 0;
140 dump_one_cpuid(output, regs, 0x5);
141 if (regs[1] & (1<<2))
142 has_intel_sgx = 1;
143 max = regs[0];
144 for(i=1; i<=max; i++) {
145 regs[0] = 0x7; regs[2] = i;
146 dump_one_cpuid(output, regs, 0x5);
147 }
148 }
149
150 /* 0x9 = DCA on Intel ; Reserved on AMD */
151 if (highest_cpuid >= 0x9) {
152 regs[0] = 0x9;
153 dump_one_cpuid(output, regs, 0x1);
154 }
155
156 /* 0xa = Perf monitoring on Intel ; Reserved on AMD */
157 if (highest_cpuid >= 0xa) {
158 regs[0] = 0xa;
159 dump_one_cpuid(output, regs, 0x1);
160 }
161
162 /* 0xb = Extended topology on Intel ; Reserved on AMD */
163 if (has_intel_x2apic && highest_cpuid >= 0xb) {
164 for(i=0; ; i++) {
165 regs[0] = 0xb; regs[2] = i;
166 dump_one_cpuid(output, regs, 0x5);
167 if (!(regs[2] & 0xff00))
168 /* invalid, no more levels */
169 break;
170 }
171 }
172
173 /* 0xd = Extended state enumeration */
174 if (highest_cpuid >= 0xd) {
175 unsigned xcr0_l, xcr0_h, ia32xss_l, ia32xss_h;
176
177 regs[0] = 0xd; regs[2] = 0;
178 dump_one_cpuid(output, regs, 0x5);
179 xcr0_l = regs[0]; xcr0_h = regs[3];
180
181 regs[0] = 0xd; regs[2] = 1;
182 dump_one_cpuid(output, regs, 0x5);
183 ia32xss_l = regs[2]; ia32xss_h = regs[3];
184
185 for(i=2; i<32; i++) {
186 if ((xcr0_l | ia32xss_l) & (1<<i)) {
187 regs[0] = 0xd; regs[2] = i;
188 dump_one_cpuid(output, regs, 0x5);
189 }
190 }
191 for(i=0; i<32; i++) {
192 if ((xcr0_h | ia32xss_h) & (1<<i)) {
193 regs[0] = 0xd; regs[2] = i+32;
194 dump_one_cpuid(output, regs, 0x5);
195 }
196 }
197 }
198
199 /* 0xf = Platform/L3 QoS enumeration on Intel and AMD */
200 if (highest_cpuid >= 0xf) {
201 regs[0] = 0xf; regs[2] = 0;
202 dump_one_cpuid(output, regs, 0x5);
203 regs[0] = 0xf; regs[2] = 1;
204 dump_one_cpuid(output, regs, 0x5);
205 }
206
207 /* 0x10 = Platform/L3 QoS enforcement enumeration on Intel and AMD */
208 if (highest_cpuid >= 0x10) {
209 /* Intel Resource Director Technology (Intel RDT) Allocation */
210 regs[0] = 0x10; regs[2] = 0;
211 dump_one_cpuid(output, regs, 0x5);
212 /* L3 Cache Allocation Technology */
213 regs[0] = 0x10; regs[2] = 1;
214 dump_one_cpuid(output, regs, 0x5);
215 /* L2 Cache Allocation Technology */
216 regs[0] = 0x10; regs[2] = 2;
217 dump_one_cpuid(output, regs, 0x5);
218 /* Memory Bandwidth Allocation */
219 regs[0] = 0x10; regs[2] = 3;
220 dump_one_cpuid(output, regs, 0x5);
221 }
222
223 /* 0x12 = SGX Attributes Enumeration on Intel ; Reserved on AMD */
224 if (has_intel_sgx && highest_cpuid >= 0x12) {
225 regs[0] = 0x12; regs[2] = 0;
226 dump_one_cpuid(output, regs, 0x5);
227 regs[0] = 0x12; regs[2] = 1;
228 dump_one_cpuid(output, regs, 0x5);
229 for(i=2; ; i++) {
230 regs[0] = 0x12; regs[2] = i;
231 dump_one_cpuid(output, regs, 0x5);
232 if (!(regs[0] & 0xf))
233 /* invalid, no more subleaves */
234 break;
235 }
236 }
237
238 /* 0x14 = Processor trace enumeration on Intel ; Reserved on AMD */
239 if (highest_cpuid >= 0x14) {
240 regs[0] = 0x14; regs[2] = 0;
241 dump_one_cpuid(output, regs, 0x5);
242 regs[0] = 0x14; regs[2] = 1;
243 dump_one_cpuid(output, regs, 0x5);
244 }
245
246 /* 0x15 = Timestamp counter/core crystal clock on Intel ; Reserved on AMD */
247 if (highest_cpuid >= 0x15) {
248 regs[0] = 0x15;
249 dump_one_cpuid(output, regs, 0x1);
250 }
251
252 /* 0x16 = Processor frequency on Intel ; Reserved on AMD */
253 if (highest_cpuid >= 0x16) {
254 regs[0] = 0x16;
255 dump_one_cpuid(output, regs, 0x1);
256 }
257
258 /* 0x17 = System-On-Chip Vendor Attribute on Intel ; Reserved on AMD */
259 if (highest_cpuid >= 0x17) {
260 unsigned maxsocid;
261 regs[0] = 0x17; regs[2] = 0;
262 dump_one_cpuid(output, regs, 0x5);
263 maxsocid = regs[0];
264 if (maxsocid >= 3) {
265 for(i=1; i<=maxsocid; i++) {
266 regs[0] = 0x17; regs[2] = i;
267 dump_one_cpuid(output, regs, 0x5);
268 }
269 }
270 }
271
272 /* 0x18 = Deterministic Address Translation Parameters on Intel ; Reserved on AMD */
273 if (highest_cpuid >= 0x18) {
274 unsigned max;
275 regs[0] = 0x18; regs[2] = 0;
276 dump_one_cpuid(output, regs, 0x5);
277 max = regs[0];
278 for(i=1; i<=max; i++) {
279 regs[0] = 0x18; regs[2] = i;
280 regs[3] = 0; /* mark as invalid in case the cpuid call doesn't do anything */
281 dump_one_cpuid(output, regs, 0x5);
282 if (!(regs[3] & 0x1f))
283 /* invalid, but it doesn't mean the next subleaf will be invalid */
284 continue;
285 }
286 }
287
288 /* 0x19 = Key Locker Leaf on Intel ; Reserved on AMD */
289 if (highest_cpuid >= 0x19) {
290 regs[0] = 0x19;
291 dump_one_cpuid(output, regs, 0x1);
292 }
293
294 /* 0x1a = Hybrid Information Enumeration Leaf on Intel ; Reserved on AMD */
295 if (highest_cpuid >= 0x1a) {
296 regs[0] = 0x1a; regs[2] = 0;
297 dump_one_cpuid(output, regs, 0x5);
298 }
299
300 /* 0x1b = (Removed) PCONFIG Information on Intel ; Reserved on AMD */
301
302 /* 0x1f = V2 Extended Topology Enumeration on Intel ; Reserved on AMD */
303 if (highest_cpuid >= 0x1f) {
304 for(i=0; ; i++) {
305 regs[0] = 0x1f; regs[2] = i;
306 dump_one_cpuid(output, regs, 0x5);
307 if (!(regs[2] & 0xff00))
308 /* invalid, no more levels */
309 break;
310 }
311 }
312
313 if (highest_cpuid > 0x1f) {
314 static int reported = 0;
315 if (!reported)
316 fprintf(stderr, "WARNING: Processor supports new CPUID leaves upto 0x%x\n", highest_cpuid);
317 reported = 1;
318 }
319
320 /* 0x80000000 = Largest extended cpuid */
321 regs[0] = 0x80000000;
322 dump_one_cpuid(output, regs, 0x1);
323
324 /* 0x80000001 = Extended processor signature and features */
325 if (highest_ext_cpuid >= 0x80000001) {
326 regs[0] = 0x80000001;
327 dump_one_cpuid(output, regs, 0x1);
328 if (regs[2] & (1 << 22))
329 has_amd_topoext = 1;
330 }
331
332 /* 0x80000002-4 = Processor name string */
333 if (highest_ext_cpuid >= 0x80000002) {
334 regs[0] = 0x80000002;
335 dump_one_cpuid(output, regs, 0x1);
336 }
337 if (highest_ext_cpuid >= 0x80000003) {
338 regs[0] = 0x80000003;
339 dump_one_cpuid(output, regs, 0x1);
340 }
341 if (highest_ext_cpuid >= 0x80000004) {
342 regs[0] = 0x80000004;
343 dump_one_cpuid(output, regs, 0x1);
344 }
345
346 /* 0x80000005 = L1 and TLB on AMD ; Reserved on Intel */
347 if (highest_ext_cpuid >= 0x80000005) {
348 regs[0] = 0x80000005;
349 dump_one_cpuid(output, regs, 0x1);
350 }
351
352 /* 0x80000006 = L2, L3 and TLB on AMD ; L2 and reserved on Intel */
353 if (highest_ext_cpuid >= 0x80000006) {
354 regs[0] = 0x80000006;
355 dump_one_cpuid(output, regs, 0x1);
356 }
357
358 /* 0x80000007 = Advanced power management on AMD ; Almost reserved on Intel */
359 if (highest_ext_cpuid >= 0x80000007) {
360 regs[0] = 0x80000007;
361 dump_one_cpuid(output, regs, 0x1);
362 }
363
364 /* 0x80000008 = Long mode and topology on AMD ; Long mode on Intel */
365 if (highest_ext_cpuid >= 0x80000008) {
366 regs[0] = 0x80000008;
367 dump_one_cpuid(output, regs, 0x1);
368 }
369
370 /* 0x8000000a = SVM on AMD ; Reserved on Intel */
371 if (highest_ext_cpuid >= 0x8000000a) {
372 regs[0] = 0x8000000a;
373 dump_one_cpuid(output, regs, 0x1);
374 }
375
376 /* 0x80000019 = TLB1G + Perf optim identifiers on AMD ; Reserved on Intel */
377 if (highest_ext_cpuid >= 0x80000019) {
378 regs[0] = 0x80000019;
379 dump_one_cpuid(output, regs, 0x1);
380 }
381
382 /* 0x8000001a = Performance Optimization Identifiers on AMD ; Reserved on Intel */
383 if (highest_ext_cpuid >= 0x8000001a) {
384 regs[0] = 0x8000001a;
385 dump_one_cpuid(output, regs, 0x1);
386 }
387
388 /* 0x8000001b = IBS on AMD ; Reserved on Intel */
389 if (highest_ext_cpuid >= 0x8000001b) {
390 regs[0] = 0x8000001b;
391 dump_one_cpuid(output, regs, 0x1);
392 }
393
394 /* 0x8000001c = Profiling on AMD ; Reserved on Intel */
395 if (highest_ext_cpuid >= 0x8000001c) {
396 regs[0] = 0x8000001c;
397 dump_one_cpuid(output, regs, 0x1);
398 }
399
400 /* 0x8000001d = Cache properties on AMD ; Reserved on Intel */
401 if (highest_ext_cpuid >= 0x8000001d) {
402 for(i=0; ; i++) {
403 regs[0] = 0x8000001d; regs[2] = i;
404 dump_one_cpuid(output, regs, 0x5);
405 if (!(regs[0] & 0x1f))
406 /* no such cache, no more cache */
407 break;
408 }
409 }
410
411 /* 0x8000001e = Topoext on AMD ; Reserved on Intel */
412 if (has_amd_topoext && highest_ext_cpuid >= 0x8000001e) {
413 regs[0] = 0x8000001e;
414 dump_one_cpuid(output, regs, 0x1);
415 }
416
417 /* 0x8000001f = Encrypted Memory Capabilities ; Reserved on Intel */
418 if (highest_ext_cpuid >= 0x8000001f) {
419 regs[0] = 0x8000001f;
420 dump_one_cpuid(output, regs, 0x1);
421 }
422
423 /* 0x80000020 = Platform QoS Enforcement for Memory Bandwidth */
424 if (highest_ext_cpuid >= 0x80000020) {
425 regs[0] = 0x80000020; regs[2] = 0;
426 dump_one_cpuid(output, regs, 0x5);
427 regs[0] = 0x80000020; regs[2] = 1;
428 dump_one_cpuid(output, regs, 0x5);
429 }
430
431 if (highest_ext_cpuid > 0x8000001f) {
432 static int reported = 0;
433 if (!reported)
434 fprintf(stderr, "WARNING: Processor supports new extended CPUID leaves upto 0x%x\n", highest_ext_cpuid);
435 reported = 1;
436 }
437
438 if (path)
439 fclose(output);
440 return 0;
441 }
442
usage(const char * callname,FILE * where)443 void usage(const char *callname, FILE *where)
444 {
445 fprintf(where, "Usage : %s [ options ] ... [ outdir ]\n", callname);
446 fprintf(where, " outdir is an optional output directory instead of cpuid/\n");
447 fprintf(where, "Options:\n");
448 fprintf(where, " -c <n> Only gather for logical processor with logical index <n>\n");
449 fprintf(where, " -s --silent Do not show verbose messages\n");
450 fprintf(where, " -h --help Show this usage\n");
451 }
452
main(int argc,const char * const argv[])453 int main(int argc, const char * const argv[])
454 {
455 hwloc_topology_t topo;
456 hwloc_obj_t pu;
457 const char *basedir;
458 const char *callname;
459 char *path;
460 size_t pathlen;
461 unsigned idx = (unsigned) -1;
462 int err;
463 int ret = EXIT_SUCCESS;
464
465 callname = argv[0];
466 argc--; argv++;
467
468 hwloc_utils_check_api_version(callname);
469
470 if (!hwloc_have_x86_cpuid()) {
471 fprintf(stderr, "CPUID not supported.\n");
472 ret = EXIT_FAILURE;
473 goto out;
474 }
475
476 while (argc > 0 && argv[0][0] == '-' && argv[0][1] != '\0') {
477 if (argc >= 2 && !strcmp(argv[0], "-c")) {
478 idx = atoi(argv[1]);
479 argc -= 2;
480 argv += 2;
481 } else if (argc >= 1 && (!strcmp(argv[0], "-s") || !strcmp(argv[0], "--silent"))) {
482 verbose--;
483 argc--;
484 argv++;
485 } else if (!strcmp(argv[0], "-h") || !strcmp(argv[0], "--help")) {
486 usage(callname, stdout);
487 goto out;
488 } else {
489 usage(callname, stderr);
490 ret = EXIT_FAILURE;
491 goto out;
492 }
493 }
494
495 basedir = "./cpuid";
496 if (argc >= 1)
497 basedir = argv[0];
498
499 if (!getenv("HWLOC_COMPONENTS"))
500 putenv((char *) "HWLOC_COMPONENTS=no_os,stop");
501
502 hwloc_topology_init(&topo);
503 hwloc_topology_set_all_types_filter(topo, HWLOC_TYPE_FILTER_KEEP_NONE);
504 err = hwloc_topology_load(topo);
505 if (err < 0) {
506 fprintf(stderr, "Failed to load topology\n");
507 ret = EXIT_FAILURE;
508 goto out;
509 }
510
511 if (!hwloc_topology_is_thissystem(topo)) {
512 fprintf(stderr, "%s must run on the current system topology, while this topology doesn't come from this system.\n", callname);
513 ret = EXIT_FAILURE;
514 goto out;
515 }
516
517 if (!strcmp(basedir, "-")) {
518 if (verbose)
519 printf("Gathering on stdout ...\n");
520 if (idx == (unsigned) -1) {
521 fprintf(stderr, "Cannot gather multiple PUs on stdout.\n");
522 ret = EXIT_FAILURE;
523 goto out;
524 }
525 path = NULL;
526 pathlen = 0;
527 } else {
528 err = mkdir(basedir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
529 if (err < 0) {
530 if (access(basedir, X_OK|W_OK) < 0) {
531 fprintf(stderr, "Could not create/open destination directory %s\n", basedir);
532 ret = EXIT_FAILURE;
533 goto out_with_topo;
534 }
535 }
536 if (verbose)
537 printf("Gathering in directory %s ...\n", basedir);
538
539 pathlen = strlen(basedir) + 20; /* for '/pu%u' or '/hwloc-cpuid-info' */
540 path = malloc(pathlen);
541 }
542
543 if (idx == (unsigned) -1) {
544 FILE *file;
545 pu = NULL;
546 while ((pu = hwloc_get_next_obj_by_type(topo, HWLOC_OBJ_PU, pu)) != NULL) {
547 idx = pu->os_index;
548 if (path)
549 snprintf(path, pathlen, "%s/pu%u", basedir, idx);
550 dump_one_proc(topo, pu, path);
551 }
552
553 snprintf(path, pathlen, "%s/hwloc-cpuid-info", basedir);
554 file = fopen(path, "w");
555 if (file) {
556 fprintf(file, "Architecture: x86\n");
557 fclose(file);
558 if (verbose)
559 printf("Summary written to %s\n", path);
560 } else {
561 fprintf(stderr, "Failed to open summary file '%s' for writing: %s\n", path, strerror(errno));
562 }
563 } else {
564 pu = hwloc_get_pu_obj_by_os_index(topo, idx);
565 if (!pu) {
566 fprintf(stderr, "Cannot find PU P#%u\n", idx);
567 ret = EXIT_FAILURE;
568 goto out_with_path;
569 } else {
570 if (path)
571 snprintf(path, pathlen, "%s/pu%u", basedir, idx);
572 dump_one_proc(topo, pu, path);
573 }
574 }
575
576 if (verbose)
577 printf("\n"
578 "WARNING: Do not post these files on a public list or website unless you\n"
579 "WARNING: are sure that no information about this platform is sensitive.\n");
580
581 out_with_path:
582 free(path);
583 out_with_topo:
584 hwloc_topology_destroy(topo);
585 out:
586 return ret;
587 }
588