1 /*
2  * Copyright © 2010-2021 Inria.  All rights reserved.
3  * Copyright © 2010-2013 Université Bordeaux
4  * Copyright © 2010-2011 Cisco Systems, Inc.  All rights reserved.
5  * See COPYING in top-level directory.
6  *
7  *
8  * This backend is only used when the operating system does not export
9  * the necessary hardware topology information to user-space applications.
10  * Currently, FreeBSD and NetBSD only add PUs and then fallback to this
11  * backend for CPU/Cache discovery.
12  *
13  * Other backends such as Linux have their own way to retrieve various
14  * pieces of hardware topology information from the operating system
15  * on various architectures, without having to use this x86-specific code.
16  * But this backend is still used after them to annotate some objects with
17  * additional details (CPU info in Package, Inclusiveness in Caches).
18  */
19 
20 #include "private/autogen/config.h"
21 #include "hwloc.h"
22 #include "private/private.h"
23 #include "private/debug.h"
24 #include "private/misc.h"
25 #include "private/cpuid-x86.h"
26 
27 #include <sys/types.h>
28 #ifdef HAVE_DIRENT_H
29 #include <dirent.h>
30 #endif
31 #ifdef HAVE_VALGRIND_VALGRIND_H
32 #include <valgrind/valgrind.h>
33 #endif
34 
35 struct hwloc_x86_backend_data_s {
36   unsigned nbprocs;
37   hwloc_bitmap_t apicid_set;
38   int apicid_unique;
39   char *src_cpuiddump_path;
40   int is_knl;
41 };
42 
43 /************************************
44  * Management of cpuid dump as input
45  */
46 
47 struct cpuiddump {
48   unsigned nr;
49   struct cpuiddump_entry {
50     unsigned inmask; /* which of ine[abcd]x are set on input */
51     unsigned ineax;
52     unsigned inebx;
53     unsigned inecx;
54     unsigned inedx;
55     unsigned outeax;
56     unsigned outebx;
57     unsigned outecx;
58     unsigned outedx;
59   } *entries;
60 };
61 
62 static void
cpuiddump_free(struct cpuiddump * cpuiddump)63 cpuiddump_free(struct cpuiddump *cpuiddump)
64 {
65   if (cpuiddump->nr)
66     free(cpuiddump->entries);
67   free(cpuiddump);
68 }
69 
70 static struct cpuiddump *
cpuiddump_read(const char * dirpath,unsigned idx)71 cpuiddump_read(const char *dirpath, unsigned idx)
72 {
73   struct cpuiddump *cpuiddump;
74   struct cpuiddump_entry *cur;
75   size_t filenamelen;
76   char *filename;
77   FILE *file;
78   char line[128];
79   unsigned nr;
80 
81   cpuiddump = malloc(sizeof(*cpuiddump));
82   if (!cpuiddump) {
83     fprintf(stderr, "Failed to allocate cpuiddump for PU #%u, ignoring cpuiddump.\n", idx);
84     goto out;
85   }
86 
87   filenamelen = strlen(dirpath) + 15;
88   filename = malloc(filenamelen);
89   if (!filename)
90     goto out_with_dump;
91   snprintf(filename, filenamelen, "%s/pu%u", dirpath, idx);
92   file = fopen(filename, "r");
93   if (!file) {
94     fprintf(stderr, "Could not read dumped cpuid file %s, ignoring cpuiddump.\n", filename);
95     goto out_with_filename;
96   }
97 
98   nr = 0;
99   while (fgets(line, sizeof(line), file))
100     nr++;
101   cpuiddump->entries = malloc(nr * sizeof(struct cpuiddump_entry));
102   if (!cpuiddump->entries) {
103     fprintf(stderr, "Failed to allocate %u cpuiddump entries for PU #%u, ignoring cpuiddump.\n", nr, idx);
104     goto out_with_file;
105   }
106 
107   fseek(file, 0, SEEK_SET);
108   cur = &cpuiddump->entries[0];
109   nr = 0;
110   while (fgets(line, sizeof(line), file)) {
111     if (*line == '#')
112       continue;
113     if (sscanf(line, "%x %x %x %x %x => %x %x %x %x",
114 	      &cur->inmask,
115 	      &cur->ineax, &cur->inebx, &cur->inecx, &cur->inedx,
116 	      &cur->outeax, &cur->outebx, &cur->outecx, &cur->outedx) == 9) {
117       cur++;
118       nr++;
119     }
120   }
121 
122   cpuiddump->nr = nr;
123   fclose(file);
124   free(filename);
125   return cpuiddump;
126 
127  out_with_file:
128   fclose(file);
129  out_with_filename:
130   free(filename);
131  out_with_dump:
132   free(cpuiddump);
133  out:
134   return NULL;
135 }
136 
137 static void
cpuiddump_find_by_input(unsigned * eax,unsigned * ebx,unsigned * ecx,unsigned * edx,struct cpuiddump * cpuiddump)138 cpuiddump_find_by_input(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx, struct cpuiddump *cpuiddump)
139 {
140   unsigned i;
141 
142   for(i=0; i<cpuiddump->nr; i++) {
143     struct cpuiddump_entry *entry = &cpuiddump->entries[i];
144     if ((entry->inmask & 0x1) && *eax != entry->ineax)
145       continue;
146     if ((entry->inmask & 0x2) && *ebx != entry->inebx)
147       continue;
148     if ((entry->inmask & 0x4) && *ecx != entry->inecx)
149       continue;
150     if ((entry->inmask & 0x8) && *edx != entry->inedx)
151       continue;
152     *eax = entry->outeax;
153     *ebx = entry->outebx;
154     *ecx = entry->outecx;
155     *edx = entry->outedx;
156     return;
157   }
158 
159   fprintf(stderr, "Couldn't find %x,%x,%x,%x in dumped cpuid, returning 0s.\n",
160 	  *eax, *ebx, *ecx, *edx);
161   *eax = 0;
162   *ebx = 0;
163   *ecx = 0;
164   *edx = 0;
165 }
166 
cpuid_or_from_dump(unsigned * eax,unsigned * ebx,unsigned * ecx,unsigned * edx,struct cpuiddump * src_cpuiddump)167 static void cpuid_or_from_dump(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx, struct cpuiddump *src_cpuiddump)
168 {
169   if (src_cpuiddump) {
170     cpuiddump_find_by_input(eax, ebx, ecx, edx, src_cpuiddump);
171   } else {
172     hwloc_x86_cpuid(eax, ebx, ecx, edx);
173   }
174 }
175 
176 /*******************************
177  * Core detection routines and structures
178  */
179 
180 enum hwloc_x86_disc_flags {
181   HWLOC_X86_DISC_FLAG_FULL = (1<<0), /* discover everything instead of only annotating */
182   HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES = (1<<1) /* use AMD topoext numanode information */
183 };
184 
185 #define has_topoext(features) ((features)[6] & (1 << 22))
186 #define has_x2apic(features) ((features)[4] & (1 << 21))
187 #define has_hybrid(features) ((features)[18] & (1 << 15))
188 
189 struct cacheinfo {
190   hwloc_obj_cache_type_t type;
191   unsigned level;
192   unsigned nbthreads_sharing;
193   unsigned cacheid;
194 
195   unsigned linesize;
196   unsigned linepart;
197   int inclusive;
198   int ways;
199   unsigned sets;
200   unsigned long size;
201 };
202 
203 struct procinfo {
204   unsigned present;
205   unsigned apicid;
206 #define PKG 0
207 #define CORE 1
208 #define NODE 2
209 #define UNIT 3
210 #define TILE 4
211 #define MODULE 5
212 #define DIE 6
213 #define HWLOC_X86_PROCINFO_ID_NR 7
214   unsigned ids[HWLOC_X86_PROCINFO_ID_NR];
215   unsigned *otherids;
216   unsigned levels;
217   unsigned numcaches;
218   struct cacheinfo *cache;
219   char cpuvendor[13];
220   char cpumodel[3*4*4+1];
221   unsigned cpustepping;
222   unsigned cpumodelnumber;
223   unsigned cpufamilynumber;
224 
225   unsigned hybridcoretype;
226   unsigned hybridnativemodel;
227 };
228 
229 enum cpuid_type {
230   intel,
231   amd,
232   zhaoxin,
233   hygon,
234   unknown
235 };
236 
237 /* AMD legacy cache information from specific CPUID 0x80000005-6 leaves */
setup__amd_cache_legacy(struct procinfo * infos,unsigned level,hwloc_obj_cache_type_t type,unsigned nbthreads_sharing,unsigned cpuid)238 static void setup__amd_cache_legacy(struct procinfo *infos, unsigned level, hwloc_obj_cache_type_t type, unsigned nbthreads_sharing, unsigned cpuid)
239 {
240   struct cacheinfo *cache, *tmpcaches;
241   unsigned cachenum;
242   unsigned long size = 0;
243 
244   if (level == 1)
245     size = ((cpuid >> 24)) << 10;
246   else if (level == 2)
247     size = ((cpuid >> 16)) << 10;
248   else if (level == 3)
249     size = ((cpuid >> 18)) << 19;
250   if (!size)
251     return;
252 
253   tmpcaches = realloc(infos->cache, (infos->numcaches+1)*sizeof(*infos->cache));
254   if (!tmpcaches)
255     /* failed to allocated, ignore that cache */
256     return;
257   infos->cache = tmpcaches;
258   cachenum = infos->numcaches++;
259 
260   cache = &infos->cache[cachenum];
261 
262   cache->type = type;
263   cache->level = level;
264   cache->nbthreads_sharing = nbthreads_sharing;
265   cache->linesize = cpuid & 0xff;
266   cache->linepart = 0;
267   cache->inclusive = 0; /* old AMD (K8-K10) supposed to have exclusive caches */
268 
269   if (level == 1) {
270     cache->ways = (cpuid >> 16) & 0xff;
271     if (cache->ways == 0xff)
272       /* Fully associative */
273       cache->ways = -1;
274   } else {
275     static const unsigned ways_tab[] = { 0, 1, 2, 0, 4, 0, 8, 0, 16, 0, 32, 48, 64, 96, 128, -1 };
276     unsigned ways = (cpuid >> 12) & 0xf;
277     cache->ways = ways_tab[ways];
278   }
279   cache->size = size;
280   cache->sets = 0;
281 
282   hwloc_debug("cache L%u t%u linesize %u ways %d size %luKB\n", cache->level, cache->nbthreads_sharing, cache->linesize, cache->ways, cache->size >> 10);
283 }
284 
285 /* AMD legacy cache information from CPUID 0x80000005-6 leaves */
read_amd_caches_legacy(struct procinfo * infos,struct cpuiddump * src_cpuiddump,unsigned legacy_max_log_proc)286 static void read_amd_caches_legacy(struct procinfo *infos, struct cpuiddump *src_cpuiddump, unsigned legacy_max_log_proc)
287 {
288   unsigned eax, ebx, ecx, edx;
289 
290   eax = 0x80000005;
291   cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
292   setup__amd_cache_legacy(infos, 1, HWLOC_OBJ_CACHE_DATA, 1, ecx); /* private L1d */
293   setup__amd_cache_legacy(infos, 1, HWLOC_OBJ_CACHE_INSTRUCTION, 1, edx); /* private L1i */
294 
295   eax = 0x80000006;
296   cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
297   if (ecx & 0xf000)
298     /* This is actually supported on Intel but LinePerTag isn't returned in bits 8-11.
299      * Could be useful if some Intels (at least before Core micro-architecture)
300      * support this leaf without leaf 0x4.
301      */
302     setup__amd_cache_legacy(infos, 2, HWLOC_OBJ_CACHE_UNIFIED, 1, ecx); /* private L2u */
303   if (edx & 0xf000)
304     setup__amd_cache_legacy(infos, 3, HWLOC_OBJ_CACHE_UNIFIED, legacy_max_log_proc, edx); /* package-wide L3u */
305 }
306 
307 /* AMD caches from CPUID 0x8000001d leaf (topoext) */
read_amd_caches_topoext(struct procinfo * infos,struct cpuiddump * src_cpuiddump)308 static void read_amd_caches_topoext(struct procinfo *infos, struct cpuiddump *src_cpuiddump)
309 {
310   unsigned eax, ebx, ecx, edx;
311   unsigned cachenum;
312   struct cacheinfo *cache;
313 
314   /* the code below doesn't want any other cache yet */
315   assert(!infos->numcaches);
316 
317   for (cachenum = 0; ; cachenum++) {
318     eax = 0x8000001d;
319     ecx = cachenum;
320     cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
321     if ((eax & 0x1f) == 0)
322       break;
323     infos->numcaches++;
324   }
325 
326   cache = infos->cache = malloc(infos->numcaches * sizeof(*infos->cache));
327   if (cache) {
328     for (cachenum = 0; ; cachenum++) {
329       unsigned long linesize, linepart, ways, sets;
330       eax = 0x8000001d;
331       ecx = cachenum;
332       cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
333 
334       if ((eax & 0x1f) == 0)
335 	break;
336       switch (eax & 0x1f) {
337       case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break;
338       case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break;
339       default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break;
340       }
341 
342       cache->level = (eax >> 5) & 0x7;
343       /* Note: actually number of cores */
344       cache->nbthreads_sharing = ((eax >> 14) &  0xfff) + 1;
345 
346       cache->linesize = linesize = (ebx & 0xfff) + 1;
347       cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1;
348       ways = ((ebx >> 22) & 0x3ff) + 1;
349 
350       if (eax & (1 << 9))
351 	/* Fully associative */
352 	cache->ways = -1;
353       else
354 	cache->ways = ways;
355       cache->sets = sets = ecx + 1;
356       cache->size = linesize * linepart * ways * sets;
357       cache->inclusive = edx & 0x2;
358 
359       hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n",
360 		  cachenum, cache->level,
361 		  cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u',
362 		  cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10);
363 
364       cache++;
365     }
366   } else {
367     infos->numcaches = 0;
368   }
369 }
370 
371 /* Intel cache info from CPUID 0x04 leaf */
read_intel_caches(struct hwloc_x86_backend_data_s * data,struct procinfo * infos,struct cpuiddump * src_cpuiddump)372 static void read_intel_caches(struct hwloc_x86_backend_data_s *data, struct procinfo *infos, struct cpuiddump *src_cpuiddump)
373 {
374   unsigned level;
375   struct cacheinfo *tmpcaches;
376   unsigned eax, ebx, ecx, edx;
377   unsigned oldnumcaches = infos->numcaches; /* in case we got caches above */
378   unsigned cachenum;
379   struct cacheinfo *cache;
380 
381   for (cachenum = 0; ; cachenum++) {
382     eax = 0x04;
383     ecx = cachenum;
384     cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
385 
386     hwloc_debug("cache %u type %u\n", cachenum, eax & 0x1f);
387     if ((eax & 0x1f) == 0)
388       break;
389     level = (eax >> 5) & 0x7;
390     if (data->is_knl && level == 3)
391       /* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */
392       break;
393     infos->numcaches++;
394   }
395 
396   tmpcaches = realloc(infos->cache, infos->numcaches * sizeof(*infos->cache));
397   if (!tmpcaches) {
398     infos->numcaches = oldnumcaches;
399   } else {
400     infos->cache = tmpcaches;
401     cache = &infos->cache[oldnumcaches];
402 
403     for (cachenum = 0; ; cachenum++) {
404       unsigned long linesize, linepart, ways, sets;
405       eax = 0x04;
406       ecx = cachenum;
407       cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
408 
409       if ((eax & 0x1f) == 0)
410 	break;
411       level = (eax >> 5) & 0x7;
412       if (data->is_knl && level == 3)
413 	/* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */
414 	break;
415       switch (eax & 0x1f) {
416       case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break;
417       case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break;
418       default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break;
419       }
420 
421       cache->level = level;
422       cache->nbthreads_sharing = ((eax >> 14) & 0xfff) + 1;
423 
424       cache->linesize = linesize = (ebx & 0xfff) + 1;
425       cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1;
426       ways = ((ebx >> 22) & 0x3ff) + 1;
427       if (eax & (1 << 9))
428         /* Fully associative */
429         cache->ways = -1;
430       else
431         cache->ways = ways;
432       cache->sets = sets = ecx + 1;
433       cache->size = linesize * linepart * ways * sets;
434       cache->inclusive = edx & 0x2;
435 
436       hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n",
437 		  cachenum, cache->level,
438 		  cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u',
439 		  cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10);
440       cache++;
441     }
442   }
443 }
444 
445 /* AMD core/thread info from CPUID 0x80000008 leaf */
read_amd_cores_legacy(struct procinfo * infos,struct cpuiddump * src_cpuiddump)446 static void read_amd_cores_legacy(struct procinfo *infos, struct cpuiddump *src_cpuiddump)
447 {
448   unsigned eax, ebx, ecx, edx;
449   unsigned max_nbcores;
450   unsigned max_nbthreads;
451   unsigned coreidsize;
452   unsigned logprocid;
453   unsigned threadid __hwloc_attribute_unused;
454 
455   eax = 0x80000008;
456   cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
457 
458   coreidsize = (ecx >> 12) & 0xf;
459   hwloc_debug("core ID size: %u\n", coreidsize);
460   if (!coreidsize) {
461     max_nbcores = (ecx & 0xff) + 1;
462   } else
463     max_nbcores = 1 << coreidsize;
464   hwloc_debug("Thus max # of cores: %u\n", max_nbcores);
465 
466   /* No multithreaded AMD for this old CPUID leaf */
467   max_nbthreads = 1 ;
468   hwloc_debug("and max # of threads: %u\n", max_nbthreads);
469 
470   /* legacy_max_log_proc is deprecated, it can be smaller than max_nbcores,
471    * which is the maximum number of cores that the processor could theoretically support
472    * (see "Multiple Core Calculation" in the AMD CPUID specification).
473    * Recompute packageid/coreid accordingly.
474    */
475   infos->ids[PKG] = infos->apicid / max_nbcores;
476   logprocid = infos->apicid % max_nbcores;
477   infos->ids[CORE] = logprocid / max_nbthreads;
478   threadid = logprocid % max_nbthreads;
479   hwloc_debug("this is thread %u of core %u\n", threadid, infos->ids[CORE]);
480 }
481 
482 /* AMD unit/node from CPUID 0x8000001e leaf (topoext) */
read_amd_cores_topoext(struct procinfo * infos,unsigned long flags,struct cpuiddump * src_cpuiddump)483 static void read_amd_cores_topoext(struct procinfo *infos, unsigned long flags, struct cpuiddump *src_cpuiddump)
484 {
485   unsigned apic_id, nodes_per_proc = 0;
486   unsigned eax, ebx, ecx, edx;
487 
488   eax = 0x8000001e;
489   cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
490   infos->apicid = apic_id = eax;
491 
492   if (flags & HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES) {
493     if (infos->cpufamilynumber == 0x16) {
494       /* ecx is reserved */
495       infos->ids[NODE] = 0;
496       nodes_per_proc = 1;
497     } else {
498       /* AMD other families or Hygon family 18h */
499       infos->ids[NODE] = ecx & 0xff;
500       nodes_per_proc = ((ecx >> 8) & 7) + 1;
501     }
502     if ((infos->cpufamilynumber == 0x15 && nodes_per_proc > 2)
503 	|| ((infos->cpufamilynumber == 0x17 || infos->cpufamilynumber == 0x18) && nodes_per_proc > 4)) {
504       hwloc_debug("warning: undefined nodes_per_proc value %u, assuming it means %u\n", nodes_per_proc, nodes_per_proc);
505     }
506   }
507 
508   if (infos->cpufamilynumber <= 0x16) { /* topoext appeared in 0x15 and compute-units were only used in 0x15 and 0x16 */
509     unsigned cores_per_unit;
510     /* coreid was obtained from read_amd_cores_legacy() earlier */
511     infos->ids[UNIT] = ebx & 0xff;
512     cores_per_unit = ((ebx >> 8) & 0xff) + 1;
513     hwloc_debug("topoext %08x, %u nodes, node %u, %u cores in unit %u\n", apic_id, nodes_per_proc, infos->ids[NODE], cores_per_unit, infos->ids[UNIT]);
514     /* coreid and unitid are package-wide (core 0-15 and unit 0-7 on 16-core 2-NUMAnode processor).
515      * The Linux kernel reduces theses to NUMA-node-wide (by applying %core_per_node and %unit_per node respectively).
516      * It's not clear if we should do this as well.
517      */
518   } else {
519     unsigned threads_per_core;
520     infos->ids[CORE] = ebx & 0xff;
521     threads_per_core = ((ebx >> 8) & 0xff) + 1;
522     hwloc_debug("topoext %08x, %u nodes, node %u, %u threads in core %u\n", apic_id, nodes_per_proc, infos->ids[NODE], threads_per_core, infos->ids[CORE]);
523   }
524 }
525 
526 /* Intel core/thread or even die/module/tile from CPUID 0x0b or 0x1f leaves (v1 and v2 extended topology enumeration) */
read_intel_cores_exttopoenum(struct procinfo * infos,unsigned leaf,struct cpuiddump * src_cpuiddump)527 static void read_intel_cores_exttopoenum(struct procinfo *infos, unsigned leaf, struct cpuiddump *src_cpuiddump)
528 {
529   unsigned level, apic_nextshift, apic_number, apic_type, apic_id = 0, apic_shift = 0, id;
530   unsigned threadid __hwloc_attribute_unused = 0; /* shut-up compiler */
531   unsigned eax, ebx, ecx = 0, edx;
532   int apic_packageshift = 0;
533 
534   for (level = 0; ; level++) {
535     ecx = level;
536     eax = leaf;
537     cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
538     if (!eax && !ebx)
539       break;
540     apic_packageshift = eax & 0x1f;
541   }
542 
543   if (level) {
544     infos->otherids = malloc(level * sizeof(*infos->otherids));
545     if (infos->otherids) {
546       infos->levels = level;
547       for (level = 0; ; level++) {
548 	ecx = level;
549 	eax = leaf;
550 	cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
551 	if (!eax && !ebx)
552 	  break;
553 	apic_nextshift = eax & 0x1f;
554 	apic_number = ebx & 0xffff;
555 	apic_type = (ecx & 0xff00) >> 8;
556 	apic_id = edx;
557 	id = (apic_id >> apic_shift) & ((1 << (apic_packageshift - apic_shift)) - 1);
558 	hwloc_debug("x2APIC %08x %u: nextshift %u num %2u type %u id %2u\n", apic_id, level, apic_nextshift, apic_number, apic_type, id);
559 	infos->apicid = apic_id;
560 	infos->otherids[level] = UINT_MAX;
561 	switch (apic_type) {
562 	case 1:
563 	  threadid = id;
564 	  /* apic_number is the actual number of threads per core */
565 	  break;
566 	case 2:
567 	  infos->ids[CORE] = id;
568 	  /* apic_number is the actual number of threads per die */
569 	  break;
570 	case 3:
571 	  infos->ids[MODULE] = id;
572 	  /* apic_number is the actual number of threads per tile */
573 	  break;
574 	case 4:
575 	  infos->ids[TILE] = id;
576 	  /* apic_number is the actual number of threads per die */
577 	  break;
578 	case 5:
579 	  infos->ids[DIE] = id;
580 	  /* apic_number is the actual number of threads per package */
581 	  break;
582 	default:
583 	  hwloc_debug("x2APIC %u: unknown type %u\n", level, apic_type);
584 	  infos->otherids[level] = apic_id >> apic_shift;
585 	  break;
586 	}
587 	apic_shift = apic_nextshift;
588       }
589       infos->apicid = apic_id;
590       infos->ids[PKG] = apic_id >> apic_shift;
591       hwloc_debug("x2APIC remainder: %u\n", infos->ids[PKG]);
592       hwloc_debug("this is thread %u of core %u\n", threadid, infos->ids[CORE]);
593     }
594   }
595 }
596 
597 /* Fetch information from the processor itself thanks to cpuid and store it in
598  * infos for summarize to analyze them globally */
look_proc(struct hwloc_backend * backend,struct procinfo * infos,unsigned long flags,unsigned highest_cpuid,unsigned highest_ext_cpuid,unsigned * features,enum cpuid_type cpuid_type,struct cpuiddump * src_cpuiddump)599 static void look_proc(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags, unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type, struct cpuiddump *src_cpuiddump)
600 {
601   struct hwloc_x86_backend_data_s *data = backend->private_data;
602   unsigned eax, ebx, ecx = 0, edx;
603   unsigned cachenum;
604   struct cacheinfo *cache;
605   unsigned regs[4];
606   unsigned legacy_max_log_proc; /* not valid on Intel processors with > 256 threads, or when cpuid 0x80000008 is supported */
607   unsigned legacy_log_proc_id;
608   unsigned _model, _extendedmodel, _family, _extendedfamily;
609 
610   infos->present = 1;
611 
612   /* Get apicid, legacy_max_log_proc, packageid, legacy_log_proc_id from cpuid 0x01 */
613   eax = 0x01;
614   cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
615   infos->apicid = ebx >> 24;
616   if (edx & (1 << 28))
617     legacy_max_log_proc = 1 << hwloc_flsl(((ebx >> 16) & 0xff) - 1);
618   else
619     legacy_max_log_proc = 1;
620   hwloc_debug("APIC ID 0x%02x legacy_max_log_proc %u\n", infos->apicid, legacy_max_log_proc);
621   infos->ids[PKG] = infos->apicid / legacy_max_log_proc;
622   legacy_log_proc_id = infos->apicid % legacy_max_log_proc;
623   hwloc_debug("phys %u legacy thread %u\n", infos->ids[PKG], legacy_log_proc_id);
624 
625   /* Get cpu model/family/stepping numbers from same cpuid */
626   _model          = (eax>>4) & 0xf;
627   _extendedmodel  = (eax>>16) & 0xf;
628   _family         = (eax>>8) & 0xf;
629   _extendedfamily = (eax>>20) & 0xff;
630   if ((cpuid_type == intel || cpuid_type == amd || cpuid_type == hygon) && _family == 0xf) {
631     infos->cpufamilynumber = _family + _extendedfamily;
632   } else {
633     infos->cpufamilynumber = _family;
634   }
635   if ((cpuid_type == intel && (_family == 0x6 || _family == 0xf))
636       || ((cpuid_type == amd || cpuid_type == hygon) && _family == 0xf)
637       || (cpuid_type == zhaoxin && (_family == 0x6 || _family == 0x7))) {
638     infos->cpumodelnumber = _model + (_extendedmodel << 4);
639   } else {
640     infos->cpumodelnumber = _model;
641   }
642   infos->cpustepping = eax & 0xf;
643 
644   if (cpuid_type == intel && infos->cpufamilynumber == 0x6 &&
645       (infos->cpumodelnumber == 0x57 || infos->cpumodelnumber == 0x85))
646     data->is_knl = 1; /* KNM is the same as KNL */
647 
648   /* Get cpu vendor string from cpuid 0x00 */
649   memset(regs, 0, sizeof(regs));
650   regs[0] = 0;
651   cpuid_or_from_dump(&regs[0], &regs[1], &regs[3], &regs[2], src_cpuiddump);
652   memcpy(infos->cpuvendor, regs+1, 4*3);
653   /* infos was calloc'ed, already ends with \0 */
654 
655   /* Get cpu model string from cpuid 0x80000002-4 */
656   if (highest_ext_cpuid >= 0x80000004) {
657     memset(regs, 0, sizeof(regs));
658     regs[0] = 0x80000002;
659     cpuid_or_from_dump(&regs[0], &regs[1], &regs[2], &regs[3], src_cpuiddump);
660     memcpy(infos->cpumodel, regs, 4*4);
661     regs[0] = 0x80000003;
662     cpuid_or_from_dump(&regs[0], &regs[1], &regs[2], &regs[3], src_cpuiddump);
663     memcpy(infos->cpumodel + 4*4, regs, 4*4);
664     regs[0] = 0x80000004;
665     cpuid_or_from_dump(&regs[0], &regs[1], &regs[2], &regs[3], src_cpuiddump);
666     memcpy(infos->cpumodel + 4*4*2, regs, 4*4);
667     /* infos was calloc'ed, already ends with \0 */
668   }
669 
670   if ((cpuid_type != amd && cpuid_type != hygon) && highest_cpuid >= 0x04) {
671     /* Get core/thread information from first cache reported by cpuid 0x04
672      * (not supported on AMD)
673      */
674     eax = 0x04;
675     ecx = 0;
676     cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
677     if ((eax & 0x1f) != 0) {
678       /* cache looks valid */
679       unsigned max_nbcores;
680       unsigned max_nbthreads;
681       unsigned threadid __hwloc_attribute_unused;
682       max_nbcores = ((eax >> 26) & 0x3f) + 1;
683       max_nbthreads = legacy_max_log_proc / max_nbcores;
684       hwloc_debug("thus %u threads\n", max_nbthreads);
685       threadid = legacy_log_proc_id % max_nbthreads;
686       infos->ids[CORE] = legacy_log_proc_id / max_nbthreads;
687       hwloc_debug("this is thread %u of core %u\n", threadid, infos->ids[CORE]);
688     }
689   }
690 
691   if (highest_cpuid >= 0x1a && has_hybrid(features)) {
692     /* Get hybrid cpu information from cpuid 0x1a */
693     eax = 0x1a;
694     ecx = 0;
695     cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
696     infos->hybridcoretype = eax >> 24;
697     infos->hybridnativemodel = eax & 0xffffff;
698   }
699 
700   /*********************************************************************************
701    * Get the hierarchy of thread, core, die, package, etc. from CPU-specific leaves
702    */
703 
704   if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000008 && !has_x2apic(features)) {
705     /* Get core/thread information from cpuid 0x80000008
706      * (not supported on Intel)
707      * We could ignore this codepath when x2apic is supported, but we may need
708      * nodeids if HWLOC_X86_TOPOEXT_NUMANODES is set.
709      */
710     read_amd_cores_legacy(infos, src_cpuiddump);
711   }
712 
713   if (cpuid_type != intel && cpuid_type != zhaoxin && has_topoext(features)) {
714     /* Get apicid, nodeid, unitid/coreid from cpuid 0x8000001e (AMD topology extension).
715      * Requires read_amd_cores_legacy() for coreid on family 0x15-16.
716      *
717      * Only needed when x2apic supported if NUMA nodes are needed.
718      */
719     read_amd_cores_topoext(infos, flags, src_cpuiddump);
720   }
721 
722   if ((cpuid_type == intel) && highest_cpuid >= 0x1f) {
723     /* Get package/die/module/tile/core/thread information from cpuid 0x1f
724      * (Intel v2 Extended Topology Enumeration)
725      */
726     read_intel_cores_exttopoenum(infos, 0x1f, src_cpuiddump);
727 
728   } else if ((cpuid_type == intel || cpuid_type == amd || cpuid_type == zhaoxin)
729 	     && highest_cpuid >= 0x0b && has_x2apic(features)) {
730     /* Get package/core/thread information from cpuid 0x0b
731      * (Intel v1 Extended Topology Enumeration)
732      */
733     read_intel_cores_exttopoenum(infos, 0x0b, src_cpuiddump);
734   }
735 
736   /**************************************
737    * Get caches from CPU-specific leaves
738    */
739 
740   infos->numcaches = 0;
741   infos->cache = NULL;
742 
743   if (cpuid_type != intel && cpuid_type != zhaoxin && has_topoext(features)) {
744     /* Get cache information from cpuid 0x8000001d (AMD topology extension) */
745     read_amd_caches_topoext(infos, src_cpuiddump);
746 
747   } else if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000006) {
748     /* If there's no topoext,
749      * get cache information from cpuid 0x80000005 and 0x80000006.
750      * (not supported on Intel)
751      * It looks like we cannot have 0x80000005 without 0x80000006.
752      */
753     read_amd_caches_legacy(infos, src_cpuiddump, legacy_max_log_proc);
754   }
755 
756   if ((cpuid_type != amd && cpuid_type != hygon) && highest_cpuid >= 0x04) {
757     /* Get cache information from cpuid 0x04
758      * (not supported on AMD)
759      */
760     read_intel_caches(data, infos, src_cpuiddump);
761   }
762 
763   /* Now that we have all info, compute cacheids and apply quirks */
764   for (cachenum = 0; cachenum < infos->numcaches; cachenum++) {
765     cache = &infos->cache[cachenum];
766 
767     /* default cacheid value */
768     cache->cacheid = infos->apicid / cache->nbthreads_sharing;
769 
770     if (cpuid_type == intel) {
771       /* round nbthreads_sharing to nearest power of two to build a mask (for clearing lower bits) */
772       unsigned bits = hwloc_flsl(cache->nbthreads_sharing-1);
773       unsigned mask = ~((1U<<bits) - 1);
774       cache->cacheid = infos->apicid & mask;
775 
776     } else if (cpuid_type == amd) {
777       /* AMD quirks */
778       if (infos->cpufamilynumber == 0x17
779 	  && cache->level == 3 && cache->nbthreads_sharing == 6) {
780 	/* AMD family 0x17 always shares L3 between 8 APIC ids,
781 	 * even when only 6 APIC ids are enabled and reported in nbthreads_sharing
782 	 * (on 24-core CPUs).
783 	 */
784 	cache->cacheid = infos->apicid / 8;
785 
786       } else if (infos->cpufamilynumber== 0x10 && infos->cpumodelnumber == 0x9
787 	  && cache->level == 3
788 	  && (cache->ways == -1 || (cache->ways % 2 == 0)) && cache->nbthreads_sharing >= 8) {
789 	/* Fix AMD family 0x10 model 0x9 (Magny-Cours) with 8 or 12 cores.
790 	 * The L3 (and its associativity) is actually split into two halves).
791 	 */
792 	if (cache->nbthreads_sharing == 16)
793 	  cache->nbthreads_sharing = 12; /* nbthreads_sharing is a power of 2 but the processor actually has 8 or 12 cores */
794 	cache->nbthreads_sharing /= 2;
795 	cache->size /= 2;
796 	if (cache->ways != -1)
797 	  cache->ways /= 2;
798 	/* AMD Magny-Cours 12-cores processor reserve APIC ids as AAAAAABBBBBB....
799 	 * among first L3 (A), second L3 (B), and unexisting cores (.).
800 	 * On multi-socket servers, L3 in non-first sockets may have APIC id ranges
801 	 * such as [16-21] that are not aligned on multiple of nbthreads_sharing (6).
802 	 * That means, we can't just compare apicid/nbthreads_sharing to identify siblings.
803 	 */
804 	cache->cacheid = (infos->apicid % legacy_max_log_proc) / cache->nbthreads_sharing /* cacheid within the package */
805 	  + 2 * (infos->apicid / legacy_max_log_proc); /* add 2 caches per previous package */
806 
807       } else if (infos->cpufamilynumber == 0x15
808 		 && (infos->cpumodelnumber == 0x1 /* Bulldozer */ || infos->cpumodelnumber == 0x2 /* Piledriver */)
809 		 && cache->level == 3 && cache->nbthreads_sharing == 6) {
810 	/* AMD Bulldozer and Piledriver 12-core processors have same APIC ids as Magny-Cours below,
811 	 * but we can't merge the checks because the original nbthreads_sharing must be exactly 6 here.
812 	 */
813 	cache->cacheid = (infos->apicid % legacy_max_log_proc) / cache->nbthreads_sharing /* cacheid within the package */
814 	  + 2 * (infos->apicid / legacy_max_log_proc); /* add 2 cache per previous package */
815       }
816     } else if (cpuid_type == hygon) {
817       if (infos->cpufamilynumber == 0x18
818 	  && cache->level == 3 && cache->nbthreads_sharing == 6) {
819         /* Hygon family 0x18 always shares L3 between 8 APIC ids,
820          * even when only 6 APIC ids are enabled and reported in nbthreads_sharing
821          * (on 24-core CPUs).
822          */
823         cache->cacheid = infos->apicid / 8;
824       }
825     }
826   }
827 
828   if (hwloc_bitmap_isset(data->apicid_set, infos->apicid))
829     data->apicid_unique = 0;
830   else
831     hwloc_bitmap_set(data->apicid_set, infos->apicid);
832 }
833 
834 static void
hwloc_x86_add_cpuinfos(hwloc_obj_t obj,struct procinfo * info,int replace)835 hwloc_x86_add_cpuinfos(hwloc_obj_t obj, struct procinfo *info, int replace)
836 {
837   char number[12];
838   if (info->cpuvendor[0])
839     hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUVendor", info->cpuvendor, replace);
840   snprintf(number, sizeof(number), "%u", info->cpufamilynumber);
841   hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUFamilyNumber", number, replace);
842   snprintf(number, sizeof(number), "%u", info->cpumodelnumber);
843   hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUModelNumber", number, replace);
844   if (info->cpumodel[0]) {
845     const char *c = info->cpumodel;
846     while (*c == ' ')
847       c++;
848     hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUModel", c, replace);
849   }
850   snprintf(number, sizeof(number), "%u", info->cpustepping);
851   hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUStepping", number, replace);
852 }
853 
854 static void
hwloc_x86_add_groups(hwloc_topology_t topology,struct procinfo * infos,unsigned nbprocs,hwloc_bitmap_t remaining_cpuset,unsigned type,const char * subtype,unsigned kind,int dont_merge)855 hwloc_x86_add_groups(hwloc_topology_t topology,
856 		     struct procinfo *infos,
857 		     unsigned nbprocs,
858 		     hwloc_bitmap_t remaining_cpuset,
859 		     unsigned type,
860 		     const char *subtype,
861 		     unsigned kind,
862 		     int dont_merge)
863 {
864   hwloc_bitmap_t obj_cpuset;
865   hwloc_obj_t obj;
866   unsigned i, j;
867 
868   while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
869     unsigned packageid = infos[i].ids[PKG];
870     unsigned id = infos[i].ids[type];
871 
872     if (id == (unsigned)-1) {
873       hwloc_bitmap_clr(remaining_cpuset, i);
874       continue;
875     }
876 
877     obj_cpuset = hwloc_bitmap_alloc();
878     for (j = i; j < nbprocs; j++) {
879       if (infos[j].ids[type] == (unsigned) -1) {
880 	hwloc_bitmap_clr(remaining_cpuset, j);
881 	continue;
882       }
883 
884       if (infos[j].ids[PKG] == packageid && infos[j].ids[type] == id) {
885 	hwloc_bitmap_set(obj_cpuset, j);
886 	hwloc_bitmap_clr(remaining_cpuset, j);
887       }
888     }
889 
890     obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, id);
891     obj->cpuset = obj_cpuset;
892     obj->subtype = strdup(subtype);
893     obj->attr->group.kind = kind;
894     obj->attr->group.dont_merge = dont_merge;
895     hwloc_debug_2args_bitmap("os %s %u has cpuset %s\n",
896 			     subtype, id, obj_cpuset);
897     hwloc__insert_object_by_cpuset(topology, NULL, obj, "x86:group");
898   }
899 }
900 
901 /* Analyse information stored in infos, and build/annotate topology levels accordingly */
summarize(struct hwloc_backend * backend,struct procinfo * infos,unsigned long flags)902 static void summarize(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags)
903 {
904   struct hwloc_topology *topology = backend->topology;
905   struct hwloc_x86_backend_data_s *data = backend->private_data;
906   unsigned nbprocs = data->nbprocs;
907   hwloc_bitmap_t complete_cpuset = hwloc_bitmap_alloc();
908   unsigned i, j, l, level;
909   int one = -1;
910   hwloc_bitmap_t remaining_cpuset;
911   int gotnuma = 0;
912   int fulldiscovery = (flags & HWLOC_X86_DISC_FLAG_FULL);
913 
914 #ifdef HWLOC_DEBUG
915   hwloc_debug("\nSummary of x86 CPUID topology:\n");
916   for(i=0; i<nbprocs; i++) {
917     hwloc_debug("PU %u present=%u apicid=%u on PKG %d CORE %d DIE %d NODE %d\n",
918                 i, infos[i].present, infos[i].apicid,
919                 infos[i].ids[PKG], infos[i].ids[CORE], infos[i].ids[DIE], infos[i].ids[NODE]);
920   }
921   hwloc_debug("\n");
922 #endif
923 
924   for (i = 0; i < nbprocs; i++)
925     if (infos[i].present) {
926       hwloc_bitmap_set(complete_cpuset, i);
927       one = i;
928     }
929 
930   if (one == -1) {
931     hwloc_bitmap_free(complete_cpuset);
932     return;
933   }
934 
935   remaining_cpuset = hwloc_bitmap_alloc();
936 
937   /* Ideally, when fulldiscovery=0, we could add any object that doesn't exist yet.
938    * But what if the x86 and the native backends disagree because one is buggy? Which one to trust?
939    * We only add missing caches, and annotate other existing objects for now.
940    */
941 
942   if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_PACKAGE)) {
943     /* Look for packages */
944     hwloc_obj_t package;
945 
946     hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
947     while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
948       if (fulldiscovery) {
949 	unsigned packageid = infos[i].ids[PKG];
950 	hwloc_bitmap_t package_cpuset = hwloc_bitmap_alloc();
951 
952 	for (j = i; j < nbprocs; j++) {
953 	  if (infos[j].ids[PKG] == packageid) {
954 	    hwloc_bitmap_set(package_cpuset, j);
955 	    hwloc_bitmap_clr(remaining_cpuset, j);
956 	  }
957 	}
958 	package = hwloc_alloc_setup_object(topology, HWLOC_OBJ_PACKAGE, packageid);
959 	package->cpuset = package_cpuset;
960 
961 	hwloc_x86_add_cpuinfos(package, &infos[i], 0);
962 
963 	hwloc_debug_1arg_bitmap("os package %u has cpuset %s\n",
964 				packageid, package_cpuset);
965 	hwloc__insert_object_by_cpuset(topology, NULL, package, "x86:package");
966 
967       } else {
968 	/* Annotate packages previously-existing packages */
969 	hwloc_bitmap_t set = hwloc_bitmap_alloc();
970 	hwloc_bitmap_set(set, i);
971 	package = hwloc_get_next_obj_covering_cpuset_by_type(topology, set, HWLOC_OBJ_PACKAGE, NULL);
972 	hwloc_bitmap_free(set);
973 	if (package) {
974 	  /* Found package above that PU, annotate if no such attribute yet */
975 	  hwloc_x86_add_cpuinfos(package, &infos[i], 1);
976 	  hwloc_bitmap_andnot(remaining_cpuset, remaining_cpuset, package->cpuset);
977 	} else {
978 	  /* No package, annotate the root object */
979 	  hwloc_x86_add_cpuinfos(hwloc_get_root_obj(topology), &infos[i], 1);
980 	  break;
981 	}
982       }
983     }
984   }
985 
986   /* Look for Numa nodes inside packages (cannot be filtered-out) */
987   if (fulldiscovery && (flags & HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES)) {
988     hwloc_bitmap_t node_cpuset;
989     hwloc_obj_t node;
990 
991     /* FIXME: if there's memory inside the root object, divide it into NUMA nodes? */
992 
993     hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
994     while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
995       unsigned packageid = infos[i].ids[PKG];
996       unsigned nodeid = infos[i].ids[NODE];
997 
998       if (nodeid == (unsigned)-1) {
999         hwloc_bitmap_clr(remaining_cpuset, i);
1000 	continue;
1001       }
1002 
1003       node_cpuset = hwloc_bitmap_alloc();
1004       for (j = i; j < nbprocs; j++) {
1005 	if (infos[j].ids[NODE] == (unsigned) -1) {
1006 	  hwloc_bitmap_clr(remaining_cpuset, j);
1007 	  continue;
1008 	}
1009 
1010         if (infos[j].ids[PKG] == packageid && infos[j].ids[NODE] == nodeid) {
1011           hwloc_bitmap_set(node_cpuset, j);
1012           hwloc_bitmap_clr(remaining_cpuset, j);
1013         }
1014       }
1015       node = hwloc_alloc_setup_object(topology, HWLOC_OBJ_NUMANODE, nodeid);
1016       node->cpuset = node_cpuset;
1017       node->nodeset = hwloc_bitmap_alloc();
1018       hwloc_bitmap_set(node->nodeset, nodeid);
1019       hwloc_debug_1arg_bitmap("os node %u has cpuset %s\n",
1020           nodeid, node_cpuset);
1021       hwloc__insert_object_by_cpuset(topology, NULL, node, "x86:numa");
1022       gotnuma++;
1023     }
1024   }
1025 
1026   if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP)) {
1027     if (fulldiscovery) {
1028       /* Look for AMD Compute units inside packages */
1029       hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1030       hwloc_x86_add_groups(topology, infos, nbprocs, remaining_cpuset,
1031 			   UNIT, "Compute Unit",
1032 			   HWLOC_GROUP_KIND_AMD_COMPUTE_UNIT, 0);
1033       /* Look for Intel Modules inside packages */
1034       hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1035       hwloc_x86_add_groups(topology, infos, nbprocs, remaining_cpuset,
1036 			   MODULE, "Module",
1037 			   HWLOC_GROUP_KIND_INTEL_MODULE, 0);
1038       /* Look for Intel Tiles inside packages */
1039       hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1040       hwloc_x86_add_groups(topology, infos, nbprocs, remaining_cpuset,
1041 			   TILE, "Tile",
1042 			   HWLOC_GROUP_KIND_INTEL_TILE, 0);
1043 
1044       /* Look for unknown objects */
1045       if (infos[one].otherids) {
1046 	for (level = infos[one].levels-1; level <= infos[one].levels-1; level--) {
1047 	  if (infos[one].otherids[level] != UINT_MAX) {
1048 	    hwloc_bitmap_t unknown_cpuset;
1049 	    hwloc_obj_t unknown_obj;
1050 
1051 	    hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1052 	    while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
1053 	      unsigned unknownid = infos[i].otherids[level];
1054 
1055 	      unknown_cpuset = hwloc_bitmap_alloc();
1056 	      for (j = i; j < nbprocs; j++) {
1057 		if (infos[j].otherids[level] == unknownid) {
1058 		  hwloc_bitmap_set(unknown_cpuset, j);
1059 		  hwloc_bitmap_clr(remaining_cpuset, j);
1060 		}
1061 	      }
1062 	      unknown_obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, unknownid);
1063 	      unknown_obj->cpuset = unknown_cpuset;
1064 	      unknown_obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_EXTTOPOENUM_UNKNOWN;
1065 	      unknown_obj->attr->group.subkind = level;
1066 	      hwloc_debug_2args_bitmap("os unknown%u %u has cpuset %s\n",
1067 				       level, unknownid, unknown_cpuset);
1068 	      hwloc__insert_object_by_cpuset(topology, NULL, unknown_obj, "x86:group:unknown");
1069 	    }
1070 	  }
1071 	}
1072       }
1073     }
1074   }
1075 
1076   if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_DIE)) {
1077     /* Look for Intel Dies inside packages */
1078     if (fulldiscovery) {
1079       hwloc_bitmap_t die_cpuset;
1080       hwloc_obj_t die;
1081 
1082       hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1083       while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
1084 	unsigned packageid = infos[i].ids[PKG];
1085 	unsigned dieid = infos[i].ids[DIE];
1086 
1087 	if (dieid == (unsigned) -1) {
1088 	  hwloc_bitmap_clr(remaining_cpuset, i);
1089 	  continue;
1090 	}
1091 
1092 	die_cpuset = hwloc_bitmap_alloc();
1093 	for (j = i; j < nbprocs; j++) {
1094 	  if (infos[j].ids[DIE] == (unsigned) -1) {
1095 	    hwloc_bitmap_clr(remaining_cpuset, j);
1096 	    continue;
1097 	  }
1098 
1099 	  if (infos[j].ids[PKG] == packageid && infos[j].ids[DIE] == dieid) {
1100 	    hwloc_bitmap_set(die_cpuset, j);
1101 	    hwloc_bitmap_clr(remaining_cpuset, j);
1102 	  }
1103 	}
1104 	die = hwloc_alloc_setup_object(topology, HWLOC_OBJ_DIE, dieid);
1105 	die->cpuset = die_cpuset;
1106 	hwloc_debug_1arg_bitmap("os die %u has cpuset %s\n",
1107 				dieid, die_cpuset);
1108 	hwloc__insert_object_by_cpuset(topology, NULL, die, "x86:die");
1109       }
1110     }
1111   }
1112 
1113   if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_CORE)) {
1114     /* Look for cores */
1115     if (fulldiscovery) {
1116       hwloc_bitmap_t core_cpuset;
1117       hwloc_obj_t core;
1118 
1119       hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1120       while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
1121 	unsigned packageid = infos[i].ids[PKG];
1122 	unsigned nodeid = infos[i].ids[NODE];
1123 	unsigned coreid = infos[i].ids[CORE];
1124 
1125 	if (coreid == (unsigned) -1) {
1126 	  hwloc_bitmap_clr(remaining_cpuset, i);
1127 	  continue;
1128 	}
1129 
1130 	core_cpuset = hwloc_bitmap_alloc();
1131 	for (j = i; j < nbprocs; j++) {
1132 	  if (infos[j].ids[CORE] == (unsigned) -1) {
1133 	    hwloc_bitmap_clr(remaining_cpuset, j);
1134 	    continue;
1135 	  }
1136 
1137 	  if (infos[j].ids[PKG] == packageid && infos[j].ids[NODE] == nodeid && infos[j].ids[CORE] == coreid) {
1138 	    hwloc_bitmap_set(core_cpuset, j);
1139 	    hwloc_bitmap_clr(remaining_cpuset, j);
1140 	  }
1141 	}
1142 	core = hwloc_alloc_setup_object(topology, HWLOC_OBJ_CORE, coreid);
1143 	core->cpuset = core_cpuset;
1144 	hwloc_debug_1arg_bitmap("os core %u has cpuset %s\n",
1145 				coreid, core_cpuset);
1146 	hwloc__insert_object_by_cpuset(topology, NULL, core, "x86:core");
1147       }
1148     }
1149   }
1150 
1151   /* Look for PUs (cannot be filtered-out) */
1152   if (fulldiscovery) {
1153     hwloc_debug("%s", "\n\n * CPU cpusets *\n\n");
1154     for (i=0; i<nbprocs; i++)
1155       if(infos[i].present) { /* Only add present PU. We don't know if others actually exist */
1156        struct hwloc_obj *obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_PU, i);
1157        obj->cpuset = hwloc_bitmap_alloc();
1158        hwloc_bitmap_only(obj->cpuset, i);
1159        hwloc_debug_1arg_bitmap("PU %u has cpuset %s\n", i, obj->cpuset);
1160        hwloc__insert_object_by_cpuset(topology, NULL, obj, "x86:pu");
1161      }
1162   }
1163 
1164   /* Look for caches */
1165   /* First find max level */
1166   level = 0;
1167   for (i = 0; i < nbprocs; i++)
1168     for (j = 0; j < infos[i].numcaches; j++)
1169       if (infos[i].cache[j].level > level)
1170         level = infos[i].cache[j].level;
1171   while (level > 0) {
1172     hwloc_obj_cache_type_t type;
1173     HWLOC_BUILD_ASSERT(HWLOC_OBJ_CACHE_DATA == HWLOC_OBJ_CACHE_UNIFIED+1);
1174     HWLOC_BUILD_ASSERT(HWLOC_OBJ_CACHE_INSTRUCTION == HWLOC_OBJ_CACHE_DATA+1);
1175     for (type = HWLOC_OBJ_CACHE_UNIFIED; type <= HWLOC_OBJ_CACHE_INSTRUCTION; type++) {
1176       /* Look for caches of that type at level level */
1177       hwloc_obj_type_t otype;
1178       hwloc_obj_t cache;
1179 
1180       otype = hwloc_cache_type_by_depth_type(level, type);
1181       if (otype == HWLOC_OBJ_TYPE_NONE)
1182 	continue;
1183       if (!hwloc_filter_check_keep_object_type(topology, otype))
1184 	continue;
1185 
1186       hwloc_bitmap_copy(remaining_cpuset, complete_cpuset);
1187       while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) {
1188 	hwloc_bitmap_t puset;
1189 
1190 	for (l = 0; l < infos[i].numcaches; l++) {
1191 	  if (infos[i].cache[l].level == level && infos[i].cache[l].type == type)
1192 	    break;
1193 	}
1194 	if (l == infos[i].numcaches) {
1195 	  /* no cache Llevel of that type in i */
1196 	  hwloc_bitmap_clr(remaining_cpuset, i);
1197 	  continue;
1198 	}
1199 
1200 	puset = hwloc_bitmap_alloc();
1201 	hwloc_bitmap_set(puset, i);
1202 	cache = hwloc_get_next_obj_covering_cpuset_by_type(topology, puset, otype, NULL);
1203 	hwloc_bitmap_free(puset);
1204 
1205 	if (cache) {
1206 	  /* Found cache above that PU, annotate if no such attribute yet */
1207 	  if (!hwloc_obj_get_info_by_name(cache, "Inclusive"))
1208 	    hwloc_obj_add_info(cache, "Inclusive", infos[i].cache[l].inclusive ? "1" : "0");
1209 	  hwloc_bitmap_andnot(remaining_cpuset, remaining_cpuset, cache->cpuset);
1210 	} else {
1211 	  /* Add the missing cache */
1212 	  hwloc_bitmap_t cache_cpuset;
1213 	  unsigned packageid = infos[i].ids[PKG];
1214 	  unsigned cacheid = infos[i].cache[l].cacheid;
1215 	  /* Now look for others sharing it */
1216 	  cache_cpuset = hwloc_bitmap_alloc();
1217 	  for (j = i; j < nbprocs; j++) {
1218 	    unsigned l2;
1219 	    for (l2 = 0; l2 < infos[j].numcaches; l2++) {
1220 	      if (infos[j].cache[l2].level == level && infos[j].cache[l2].type == type)
1221 		break;
1222 	    }
1223 	    if (l2 == infos[j].numcaches) {
1224 	      /* no cache Llevel of that type in j */
1225 	      hwloc_bitmap_clr(remaining_cpuset, j);
1226 	      continue;
1227 	    }
1228 	    if (infos[j].ids[PKG] == packageid && infos[j].cache[l2].cacheid == cacheid) {
1229 	      hwloc_bitmap_set(cache_cpuset, j);
1230 	      hwloc_bitmap_clr(remaining_cpuset, j);
1231 	    }
1232 	  }
1233 	  cache = hwloc_alloc_setup_object(topology, otype, HWLOC_UNKNOWN_INDEX);
1234 	  cache->attr->cache.depth = level;
1235 	  cache->attr->cache.size = infos[i].cache[l].size;
1236 	  cache->attr->cache.linesize = infos[i].cache[l].linesize;
1237 	  cache->attr->cache.associativity = infos[i].cache[l].ways;
1238 	  cache->attr->cache.type = infos[i].cache[l].type;
1239 	  cache->cpuset = cache_cpuset;
1240 	  hwloc_obj_add_info(cache, "Inclusive", infos[i].cache[l].inclusive ? "1" : "0");
1241 	  hwloc_debug_2args_bitmap("os L%u cache %u has cpuset %s\n",
1242 				   level, cacheid, cache_cpuset);
1243 	  hwloc__insert_object_by_cpuset(topology, NULL, cache, "x86:cache");
1244 	}
1245       }
1246     }
1247     level--;
1248   }
1249 
1250   /* FIXME: if KNL and L2 disabled, add tiles instead of L2 */
1251 
1252   hwloc_bitmap_free(remaining_cpuset);
1253   hwloc_bitmap_free(complete_cpuset);
1254 
1255   if (gotnuma)
1256     topology->support.discovery->numa = 1;
1257 }
1258 
1259 static int
look_procs(struct hwloc_backend * backend,struct procinfo * infos,unsigned long flags,unsigned highest_cpuid,unsigned highest_ext_cpuid,unsigned * features,enum cpuid_type cpuid_type,int (* get_cpubind)(hwloc_topology_t topology,hwloc_cpuset_t set,int flags),int (* set_cpubind)(hwloc_topology_t topology,hwloc_const_cpuset_t set,int flags),hwloc_bitmap_t restrict_set)1260 look_procs(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags,
1261 	   unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type,
1262 	   int (*get_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags),
1263 	   int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags),
1264            hwloc_bitmap_t restrict_set)
1265 {
1266   struct hwloc_x86_backend_data_s *data = backend->private_data;
1267   struct hwloc_topology *topology = backend->topology;
1268   unsigned nbprocs = data->nbprocs;
1269   hwloc_bitmap_t orig_cpuset = NULL;
1270   hwloc_bitmap_t set = NULL;
1271   unsigned i;
1272 
1273   if (!data->src_cpuiddump_path) {
1274     orig_cpuset = hwloc_bitmap_alloc();
1275     if (get_cpubind(topology, orig_cpuset, HWLOC_CPUBIND_STRICT)) {
1276       hwloc_bitmap_free(orig_cpuset);
1277       return -1;
1278     }
1279     set = hwloc_bitmap_alloc();
1280   }
1281 
1282   for (i = 0; i < nbprocs; i++) {
1283     struct cpuiddump *src_cpuiddump = NULL;
1284 
1285     if (restrict_set && !hwloc_bitmap_isset(restrict_set, i)) {
1286       /* skip this CPU outside of the binding mask */
1287       continue;
1288     }
1289 
1290     if (data->src_cpuiddump_path) {
1291       src_cpuiddump = cpuiddump_read(data->src_cpuiddump_path, i);
1292       if (!src_cpuiddump)
1293 	continue;
1294     } else {
1295       hwloc_bitmap_only(set, i);
1296       hwloc_debug("binding to CPU%u\n", i);
1297       if (set_cpubind(topology, set, HWLOC_CPUBIND_STRICT)) {
1298 	hwloc_debug("could not bind to CPU%u: %s\n", i, strerror(errno));
1299 	continue;
1300       }
1301     }
1302 
1303     look_proc(backend, &infos[i], flags, highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump);
1304 
1305     if (data->src_cpuiddump_path) {
1306       cpuiddump_free(src_cpuiddump);
1307     }
1308   }
1309 
1310   if (!data->src_cpuiddump_path) {
1311     set_cpubind(topology, orig_cpuset, 0);
1312     hwloc_bitmap_free(set);
1313     hwloc_bitmap_free(orig_cpuset);
1314   }
1315 
1316   if (data->apicid_unique) {
1317     summarize(backend, infos, flags);
1318 
1319     if (has_hybrid(features)) {
1320       /* use hybrid info for cpukinds */
1321       hwloc_bitmap_t atomset = hwloc_bitmap_alloc();
1322       hwloc_bitmap_t coreset = hwloc_bitmap_alloc();
1323       for(i=0; i<nbprocs; i++) {
1324         if (infos[i].hybridcoretype == 0x20)
1325           hwloc_bitmap_set(atomset, i);
1326         else if (infos[i].hybridcoretype == 0x40)
1327           hwloc_bitmap_set(coreset, i);
1328       }
1329       /* register IntelAtom set if any */
1330       if (!hwloc_bitmap_iszero(atomset)) {
1331         struct hwloc_info_s infoattr;
1332         infoattr.name = (char *) "CoreType";
1333         infoattr.value = (char *) "IntelAtom";
1334         hwloc_internal_cpukinds_register(topology, atomset, HWLOC_CPUKIND_EFFICIENCY_UNKNOWN, &infoattr, 1, 0);
1335         /* the cpuset is given to the callee */
1336       } else {
1337         hwloc_bitmap_free(atomset);
1338       }
1339       /* register IntelCore set if any */
1340       if (!hwloc_bitmap_iszero(coreset)) {
1341         struct hwloc_info_s infoattr;
1342         infoattr.name = (char *) "CoreType";
1343         infoattr.value = (char *) "IntelCore";
1344         hwloc_internal_cpukinds_register(topology, coreset, HWLOC_CPUKIND_EFFICIENCY_UNKNOWN, &infoattr, 1, 0);
1345         /* the cpuset is given to the callee */
1346       } else {
1347         hwloc_bitmap_free(coreset);
1348       }
1349     }
1350   }
1351   /* if !data->apicid_unique, do nothing and return success, so that the caller does nothing either */
1352 
1353   return 0;
1354 }
1355 
1356 #if defined HWLOC_FREEBSD_SYS && defined HAVE_CPUSET_SETID
1357 #include <sys/param.h>
1358 #include <sys/cpuset.h>
1359 typedef cpusetid_t hwloc_x86_os_state_t;
hwloc_x86_os_state_save(hwloc_x86_os_state_t * state,struct cpuiddump * src_cpuiddump)1360 static void hwloc_x86_os_state_save(hwloc_x86_os_state_t *state, struct cpuiddump *src_cpuiddump)
1361 {
1362   if (!src_cpuiddump) {
1363     /* temporary make all cpus available during discovery */
1364     cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1, state);
1365     cpuset_setid(CPU_WHICH_PID, -1, 0);
1366   }
1367 }
hwloc_x86_os_state_restore(hwloc_x86_os_state_t * state,struct cpuiddump * src_cpuiddump)1368 static void hwloc_x86_os_state_restore(hwloc_x86_os_state_t *state, struct cpuiddump *src_cpuiddump)
1369 {
1370   if (!src_cpuiddump) {
1371     /* restore initial cpuset */
1372     cpuset_setid(CPU_WHICH_PID, -1, *state);
1373   }
1374 }
1375 #else /* !defined HWLOC_FREEBSD_SYS || !defined HAVE_CPUSET_SETID */
1376 typedef void * hwloc_x86_os_state_t;
hwloc_x86_os_state_save(hwloc_x86_os_state_t * state __hwloc_attribute_unused,struct cpuiddump * src_cpuiddump __hwloc_attribute_unused)1377 static void hwloc_x86_os_state_save(hwloc_x86_os_state_t *state __hwloc_attribute_unused, struct cpuiddump *src_cpuiddump __hwloc_attribute_unused) { }
hwloc_x86_os_state_restore(hwloc_x86_os_state_t * state __hwloc_attribute_unused,struct cpuiddump * src_cpuiddump __hwloc_attribute_unused)1378 static void hwloc_x86_os_state_restore(hwloc_x86_os_state_t *state __hwloc_attribute_unused, struct cpuiddump *src_cpuiddump __hwloc_attribute_unused) { }
1379 #endif /* !defined HWLOC_FREEBSD_SYS || !defined HAVE_CPUSET_SETID */
1380 
1381 /* GenuineIntel */
1382 #define INTEL_EBX ('G' | ('e'<<8) | ('n'<<16) | ('u'<<24))
1383 #define INTEL_EDX ('i' | ('n'<<8) | ('e'<<16) | ('I'<<24))
1384 #define INTEL_ECX ('n' | ('t'<<8) | ('e'<<16) | ('l'<<24))
1385 
1386 /* AuthenticAMD */
1387 #define AMD_EBX ('A' | ('u'<<8) | ('t'<<16) | ('h'<<24))
1388 #define AMD_EDX ('e' | ('n'<<8) | ('t'<<16) | ('i'<<24))
1389 #define AMD_ECX ('c' | ('A'<<8) | ('M'<<16) | ('D'<<24))
1390 
1391 /* HYGON "HygonGenuine" */
1392 #define HYGON_EBX ('H' | ('y'<<8) | ('g'<<16) | ('o'<<24))
1393 #define HYGON_EDX ('n' | ('G'<<8) | ('e'<<16) | ('n'<<24))
1394 #define HYGON_ECX ('u' | ('i'<<8) | ('n'<<16) | ('e'<<24))
1395 
1396 /* (Zhaoxin) CentaurHauls */
1397 #define ZX_EBX ('C' | ('e'<<8) | ('n'<<16) | ('t'<<24))
1398 #define ZX_EDX ('a' | ('u'<<8) | ('r'<<16) | ('H'<<24))
1399 #define ZX_ECX ('a' | ('u'<<8) | ('l'<<16) | ('s'<<24))
1400 /* (Zhaoxin) Shanghai */
1401 #define SH_EBX (' ' | (' '<<8) | ('S'<<16) | ('h'<<24))
1402 #define SH_EDX ('a' | ('n'<<8) | ('g'<<16) | ('h'<<24))
1403 #define SH_ECX ('a' | ('i'<<8) | (' '<<16) | (' '<<24))
1404 
1405 /* fake cpubind for when nbprocs=1 and no binding support */
fake_get_cpubind(hwloc_topology_t topology __hwloc_attribute_unused,hwloc_cpuset_t set __hwloc_attribute_unused,int flags __hwloc_attribute_unused)1406 static int fake_get_cpubind(hwloc_topology_t topology __hwloc_attribute_unused,
1407 			    hwloc_cpuset_t set __hwloc_attribute_unused,
1408 			    int flags __hwloc_attribute_unused)
1409 {
1410   return 0;
1411 }
fake_set_cpubind(hwloc_topology_t topology __hwloc_attribute_unused,hwloc_const_cpuset_t set __hwloc_attribute_unused,int flags __hwloc_attribute_unused)1412 static int fake_set_cpubind(hwloc_topology_t topology __hwloc_attribute_unused,
1413 			    hwloc_const_cpuset_t set __hwloc_attribute_unused,
1414 			    int flags __hwloc_attribute_unused)
1415 {
1416   return 0;
1417 }
1418 
1419 static
hwloc_look_x86(struct hwloc_backend * backend,unsigned long flags)1420 int hwloc_look_x86(struct hwloc_backend *backend, unsigned long flags)
1421 {
1422   struct hwloc_x86_backend_data_s *data = backend->private_data;
1423   struct hwloc_topology *topology = backend->topology;
1424   unsigned nbprocs = data->nbprocs;
1425   unsigned eax, ebx, ecx = 0, edx;
1426   unsigned i;
1427   unsigned highest_cpuid;
1428   unsigned highest_ext_cpuid;
1429   /* This stores cpuid features with the same indexing as Linux */
1430   unsigned features[19] = { 0 };
1431   struct procinfo *infos = NULL;
1432   enum cpuid_type cpuid_type = unknown;
1433   hwloc_x86_os_state_t os_state;
1434   struct hwloc_binding_hooks hooks;
1435   struct hwloc_topology_support support;
1436   struct hwloc_topology_membind_support memsupport __hwloc_attribute_unused;
1437   int (*get_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags) = NULL;
1438   int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags) = NULL;
1439   hwloc_bitmap_t restrict_set = NULL;
1440   struct cpuiddump *src_cpuiddump = NULL;
1441   int ret = -1;
1442 
1443   /* check if binding works */
1444   memset(&hooks, 0, sizeof(hooks));
1445   support.membind = &memsupport;
1446   /* We could just copy the main hooks (except in some corner cases),
1447    * but the current overhead is negligible, so just always reget them.
1448    */
1449   hwloc_set_native_binding_hooks(&hooks, &support);
1450   /* in theory, those are only needed if !data->src_cpuiddump_path || HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_BINDING
1451    * but that's the vast majority of cases anyway, and the overhead is very small.
1452    */
1453 
1454   if (data->src_cpuiddump_path) {
1455     /* Just read cpuid from the dump (implies !topology->is_thissystem by default) */
1456     src_cpuiddump = cpuiddump_read(data->src_cpuiddump_path, 0);
1457     if (!src_cpuiddump)
1458       goto out;
1459 
1460   } else {
1461     /* Using real hardware.
1462      * However we don't enforce topology->is_thissystem so that
1463      * we may still force use this backend when debugging with !thissystem.
1464      */
1465 
1466     if (hooks.get_thisthread_cpubind && hooks.set_thisthread_cpubind) {
1467       get_cpubind = hooks.get_thisthread_cpubind;
1468       set_cpubind = hooks.set_thisthread_cpubind;
1469     } else if (hooks.get_thisproc_cpubind && hooks.set_thisproc_cpubind) {
1470       /* FIXME: if called by a multithreaded program, we will restore the original process binding
1471        * for each thread instead of their own original thread binding.
1472        * See issue #158.
1473        */
1474       get_cpubind = hooks.get_thisproc_cpubind;
1475       set_cpubind = hooks.set_thisproc_cpubind;
1476     } else {
1477       /* we need binding support if there are multiple PUs */
1478       if (nbprocs > 1)
1479 	goto out;
1480       get_cpubind = fake_get_cpubind;
1481       set_cpubind = fake_set_cpubind;
1482     }
1483   }
1484 
1485   if (topology->flags & HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING) {
1486     restrict_set = hwloc_bitmap_alloc();
1487     if (!restrict_set)
1488       goto out;
1489     if (hooks.get_thisproc_cpubind)
1490       hooks.get_thisproc_cpubind(topology, restrict_set, 0);
1491     else if (hooks.get_thisthread_cpubind)
1492       hooks.get_thisthread_cpubind(topology, restrict_set, 0);
1493     if (hwloc_bitmap_iszero(restrict_set)) {
1494       hwloc_bitmap_free(restrict_set);
1495       restrict_set = NULL;
1496     }
1497   }
1498 
1499   if (!src_cpuiddump && !hwloc_have_x86_cpuid())
1500     goto out;
1501 
1502   infos = calloc(nbprocs, sizeof(struct procinfo));
1503   if (NULL == infos)
1504     goto out;
1505   for (i = 0; i < nbprocs; i++) {
1506     infos[i].ids[PKG] = (unsigned) -1;
1507     infos[i].ids[CORE] = (unsigned) -1;
1508     infos[i].ids[NODE] = (unsigned) -1;
1509     infos[i].ids[UNIT] = (unsigned) -1;
1510     infos[i].ids[TILE] = (unsigned) -1;
1511     infos[i].ids[MODULE] = (unsigned) -1;
1512     infos[i].ids[DIE] = (unsigned) -1;
1513   }
1514 
1515   eax = 0x00;
1516   cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1517   highest_cpuid = eax;
1518   if (ebx == INTEL_EBX && ecx == INTEL_ECX && edx == INTEL_EDX)
1519     cpuid_type = intel;
1520   else if (ebx == AMD_EBX && ecx == AMD_ECX && edx == AMD_EDX)
1521     cpuid_type = amd;
1522   else if ((ebx == ZX_EBX && ecx == ZX_ECX && edx == ZX_EDX)
1523 	   || (ebx == SH_EBX && ecx == SH_ECX && edx == SH_EDX))
1524     cpuid_type = zhaoxin;
1525   else if (ebx == HYGON_EBX && ecx == HYGON_ECX && edx == HYGON_EDX)
1526     cpuid_type = hygon;
1527 
1528   hwloc_debug("highest cpuid %x, cpuid type %u\n", highest_cpuid, cpuid_type);
1529   if (highest_cpuid < 0x01) {
1530       goto out_with_infos;
1531   }
1532 
1533   eax = 0x01;
1534   cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1535   features[0] = edx;
1536   features[4] = ecx;
1537 
1538   eax = 0x80000000;
1539   cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1540   highest_ext_cpuid = eax;
1541 
1542   hwloc_debug("highest extended cpuid %x\n", highest_ext_cpuid);
1543 
1544   if (highest_cpuid >= 0x7) {
1545     eax = 0x7;
1546     ecx = 0;
1547     cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1548     features[9] = ebx;
1549     features[18] = edx;
1550   }
1551 
1552   if (cpuid_type != intel && highest_ext_cpuid >= 0x80000001) {
1553     eax = 0x80000001;
1554     cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump);
1555     features[1] = edx;
1556     features[6] = ecx;
1557   }
1558 
1559   hwloc_x86_os_state_save(&os_state, src_cpuiddump);
1560 
1561   ret = look_procs(backend, infos, flags,
1562 		   highest_cpuid, highest_ext_cpuid, features, cpuid_type,
1563 		   get_cpubind, set_cpubind, restrict_set);
1564   if (!ret)
1565     /* success, we're done */
1566     goto out_with_os_state;
1567 
1568   if (nbprocs == 1) {
1569     /* only one processor, no need to bind */
1570     look_proc(backend, &infos[0], flags, highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump);
1571     summarize(backend, infos, flags);
1572     ret = 0;
1573   }
1574 
1575 out_with_os_state:
1576   hwloc_x86_os_state_restore(&os_state, src_cpuiddump);
1577 
1578 out_with_infos:
1579   if (NULL != infos) {
1580     for (i = 0; i < nbprocs; i++) {
1581       free(infos[i].cache);
1582       free(infos[i].otherids);
1583     }
1584     free(infos);
1585   }
1586 
1587 out:
1588   hwloc_bitmap_free(restrict_set);
1589   if (src_cpuiddump)
1590     cpuiddump_free(src_cpuiddump);
1591   return ret;
1592 }
1593 
1594 static int
hwloc_x86_discover(struct hwloc_backend * backend,struct hwloc_disc_status * dstatus)1595 hwloc_x86_discover(struct hwloc_backend *backend, struct hwloc_disc_status *dstatus)
1596 {
1597   struct hwloc_x86_backend_data_s *data = backend->private_data;
1598   struct hwloc_topology *topology = backend->topology;
1599   unsigned long flags = 0;
1600   int alreadypus = 0;
1601   int ret;
1602 
1603   assert(dstatus->phase == HWLOC_DISC_PHASE_CPU);
1604 
1605   if (topology->flags & HWLOC_TOPOLOGY_FLAG_DONT_CHANGE_BINDING) {
1606     /* TODO: Things would work if there's a single PU, no need to rebind */
1607     return 0;
1608   }
1609 
1610   if (getenv("HWLOC_X86_TOPOEXT_NUMANODES")) {
1611     flags |= HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES;
1612   }
1613 
1614 #if HAVE_DECL_RUNNING_ON_VALGRIND
1615   if (RUNNING_ON_VALGRIND && !data->src_cpuiddump_path) {
1616     fprintf(stderr, "hwloc x86 backend cannot work under Valgrind, disabling.\n"
1617 	    "May be reenabled by dumping CPUIDs with hwloc-gather-cpuid\n"
1618 	    "and reloading them under Valgrind with HWLOC_CPUID_PATH.\n");
1619     return 0;
1620   }
1621 #endif
1622 
1623   if (data->src_cpuiddump_path) {
1624     assert(data->nbprocs > 0); /* enforced by hwloc_x86_component_instantiate() */
1625     topology->support.discovery->pu = 1;
1626   } else {
1627     int nbprocs = hwloc_fallback_nbprocessors(HWLOC_FALLBACK_NBPROCESSORS_INCLUDE_OFFLINE);
1628     if (nbprocs >= 1)
1629       topology->support.discovery->pu = 1;
1630     else
1631       nbprocs = 1;
1632     data->nbprocs = (unsigned) nbprocs;
1633   }
1634 
1635   if (topology->levels[0][0]->cpuset) {
1636     /* somebody else discovered things, reconnect levels so that we can look at them */
1637     hwloc_topology_reconnect(topology, 0);
1638     if (topology->nb_levels == 2 && topology->level_nbobjects[1] == data->nbprocs) {
1639       /* only PUs were discovered, as much as we would, complete the topology with everything else */
1640       alreadypus = 1;
1641       goto fulldiscovery;
1642     }
1643 
1644     /* several object types were added, we can't easily complete, just do partial discovery */
1645     ret = hwloc_look_x86(backend, flags);
1646     if (ret)
1647       hwloc_obj_add_info(topology->levels[0][0], "Backend", "x86");
1648     return 0;
1649   } else {
1650     /* topology is empty, initialize it */
1651     hwloc_alloc_root_sets(topology->levels[0][0]);
1652   }
1653 
1654 fulldiscovery:
1655   if (hwloc_look_x86(backend, flags | HWLOC_X86_DISC_FLAG_FULL) < 0) {
1656     /* if failed, create PUs */
1657     if (!alreadypus)
1658       hwloc_setup_pu_level(topology, data->nbprocs);
1659   }
1660 
1661   hwloc_obj_add_info(topology->levels[0][0], "Backend", "x86");
1662 
1663   if (!data->src_cpuiddump_path) { /* CPUID dump works for both x86 and x86_64 */
1664 #ifdef HAVE_UNAME
1665     hwloc_add_uname_info(topology, NULL); /* we already know is_thissystem() is true */
1666 #else
1667     /* uname isn't available, manually setup the "Architecture" info */
1668 #ifdef HWLOC_X86_64_ARCH
1669     hwloc_obj_add_info(topology->levels[0][0], "Architecture", "x86_64");
1670 #else
1671     hwloc_obj_add_info(topology->levels[0][0], "Architecture", "x86");
1672 #endif
1673 #endif
1674   }
1675 
1676   return 1;
1677 }
1678 
1679 static int
hwloc_x86_check_cpuiddump_input(const char * src_cpuiddump_path,hwloc_bitmap_t set)1680 hwloc_x86_check_cpuiddump_input(const char *src_cpuiddump_path, hwloc_bitmap_t set)
1681 {
1682 
1683 #if !(defined HWLOC_WIN_SYS && !defined __MINGW32__ && !defined __CYGWIN__) /* needs a lot of work */
1684   struct dirent *dirent;
1685   DIR *dir;
1686   char *path;
1687   FILE *file;
1688   char line [32];
1689 
1690   dir = opendir(src_cpuiddump_path);
1691   if (!dir)
1692     return -1;
1693 
1694   path = malloc(strlen(src_cpuiddump_path) + strlen("/hwloc-cpuid-info") + 1);
1695   if (!path)
1696     goto out_with_dir;
1697   sprintf(path, "%s/hwloc-cpuid-info", src_cpuiddump_path);
1698   file = fopen(path, "r");
1699   if (!file) {
1700     fprintf(stderr, "Couldn't open dumped cpuid summary %s\n", path);
1701     goto out_with_path;
1702   }
1703   if (!fgets(line, sizeof(line), file)) {
1704     fprintf(stderr, "Found read dumped cpuid summary in %s\n", path);
1705     fclose(file);
1706     goto out_with_path;
1707   }
1708   fclose(file);
1709   if (strcmp(line, "Architecture: x86\n")) {
1710     fprintf(stderr, "Found non-x86 dumped cpuid summary in %s: %s\n", path, line);
1711     goto out_with_path;
1712   }
1713   free(path);
1714 
1715   while ((dirent = readdir(dir)) != NULL) {
1716     if (!strncmp(dirent->d_name, "pu", 2)) {
1717       char *end;
1718       unsigned long idx = strtoul(dirent->d_name+2, &end, 10);
1719       if (!*end)
1720 	hwloc_bitmap_set(set, idx);
1721       else
1722 	fprintf(stderr, "Ignoring invalid dirent `%s' in dumped cpuid directory `%s'\n",
1723 		dirent->d_name, src_cpuiddump_path);
1724     }
1725   }
1726   closedir(dir);
1727 
1728   if (hwloc_bitmap_iszero(set)) {
1729     fprintf(stderr, "Did not find any valid pu%%u entry in dumped cpuid directory `%s'\n",
1730 	    src_cpuiddump_path);
1731     return -1;
1732   } else if (hwloc_bitmap_last(set) != hwloc_bitmap_weight(set) - 1) {
1733     /* The x86 backends enforces contigous set of PUs starting at 0 so far */
1734     fprintf(stderr, "Found non-contigous pu%%u range in dumped cpuid directory `%s'\n",
1735 	    src_cpuiddump_path);
1736     return -1;
1737   }
1738 
1739   return 0;
1740 
1741  out_with_path:
1742   free(path);
1743  out_with_dir:
1744   closedir(dir);
1745 #endif /* HWLOC_WIN_SYS & !__MINGW32__ needs a lot of work */
1746   return -1;
1747 }
1748 
1749 static void
hwloc_x86_backend_disable(struct hwloc_backend * backend)1750 hwloc_x86_backend_disable(struct hwloc_backend *backend)
1751 {
1752   struct hwloc_x86_backend_data_s *data = backend->private_data;
1753   hwloc_bitmap_free(data->apicid_set);
1754   free(data->src_cpuiddump_path);
1755   free(data);
1756 }
1757 
1758 static struct hwloc_backend *
hwloc_x86_component_instantiate(struct hwloc_topology * topology,struct hwloc_disc_component * component,unsigned excluded_phases __hwloc_attribute_unused,const void * _data1 __hwloc_attribute_unused,const void * _data2 __hwloc_attribute_unused,const void * _data3 __hwloc_attribute_unused)1759 hwloc_x86_component_instantiate(struct hwloc_topology *topology,
1760 				struct hwloc_disc_component *component,
1761 				unsigned excluded_phases __hwloc_attribute_unused,
1762 				const void *_data1 __hwloc_attribute_unused,
1763 				const void *_data2 __hwloc_attribute_unused,
1764 				const void *_data3 __hwloc_attribute_unused)
1765 {
1766   struct hwloc_backend *backend;
1767   struct hwloc_x86_backend_data_s *data;
1768   const char *src_cpuiddump_path;
1769 
1770   backend = hwloc_backend_alloc(topology, component);
1771   if (!backend)
1772     goto out;
1773 
1774   data = malloc(sizeof(*data));
1775   if (!data) {
1776     errno = ENOMEM;
1777     goto out_with_backend;
1778   }
1779 
1780   backend->private_data = data;
1781   backend->discover = hwloc_x86_discover;
1782   backend->disable = hwloc_x86_backend_disable;
1783 
1784   /* default values */
1785   data->is_knl = 0;
1786   data->apicid_set = hwloc_bitmap_alloc();
1787   data->apicid_unique = 1;
1788   data->src_cpuiddump_path = NULL;
1789 
1790   src_cpuiddump_path = getenv("HWLOC_CPUID_PATH");
1791   if (src_cpuiddump_path) {
1792     hwloc_bitmap_t set = hwloc_bitmap_alloc();
1793     if (!hwloc_x86_check_cpuiddump_input(src_cpuiddump_path, set)) {
1794       backend->is_thissystem = 0;
1795       data->src_cpuiddump_path = strdup(src_cpuiddump_path);
1796       assert(!hwloc_bitmap_iszero(set)); /* enforced by hwloc_x86_check_cpuiddump_input() */
1797       data->nbprocs = hwloc_bitmap_weight(set);
1798     } else {
1799       fprintf(stderr, "Ignoring dumped cpuid directory.\n");
1800     }
1801     hwloc_bitmap_free(set);
1802   }
1803 
1804   return backend;
1805 
1806  out_with_backend:
1807   free(backend);
1808  out:
1809   return NULL;
1810 }
1811 
1812 static struct hwloc_disc_component hwloc_x86_disc_component = {
1813   "x86",
1814   HWLOC_DISC_PHASE_CPU,
1815   HWLOC_DISC_PHASE_GLOBAL,
1816   hwloc_x86_component_instantiate,
1817   45, /* between native and no_os */
1818   1,
1819   NULL
1820 };
1821 
1822 const struct hwloc_component hwloc_x86_component = {
1823   HWLOC_COMPONENT_ABI,
1824   NULL, NULL,
1825   HWLOC_COMPONENT_TYPE_DISC,
1826   0,
1827   &hwloc_x86_disc_component
1828 };
1829