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(&regs[0], &regs[1], &regs[2], &regs[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(&regs[0], &regs[1], &regs[2], &regs[3]);
84   highest_cpuid = regs[0];
85   regs[0] = 0x80000000;
86   hwloc_x86_cpuid(&regs[0], &regs[1], &regs[2], &regs[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