1 /*
2  * Copyright © 2009-2017 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 <hwloc/plugins.h>
9 #include <private/private.h>
10 #include <private/debug.h>
11 #include <private/misc.h>
12 
13 #ifdef HWLOC_DEBUG
14 static void
hwloc_pci_traverse_print_cb(void * cbdata __hwloc_attribute_unused,struct hwloc_obj * pcidev)15 hwloc_pci_traverse_print_cb(void * cbdata __hwloc_attribute_unused,
16                             struct hwloc_obj *pcidev)
17 {
18   char busid[14];
19   hwloc_obj_t parent;
20 
21   /* indent */
22   parent = pcidev->parent;
23   while (parent) {
24     hwloc_debug("%s", "  ");
25     parent = parent->parent;
26   }
27 
28   snprintf(busid, sizeof(busid), "%04x:%02x:%02x.%01x",
29            pcidev->attr->pcidev.domain, pcidev->attr->pcidev.bus, pcidev->attr->pcidev.dev, pcidev->attr->pcidev.func);
30 
31   if (pcidev->type == HWLOC_OBJ_BRIDGE) {
32     if (pcidev->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_HOST)
33       hwloc_debug("HostBridge");
34     else
35       hwloc_debug("Bridge [%04x:%04x]", busid,
36                   pcidev->attr->pcidev.vendor_id, pcidev->attr->pcidev.device_id);
37     hwloc_debug(" to %04x:[%02x:%02x]\n",
38                 pcidev->attr->bridge.downstream.pci.domain, pcidev->attr->bridge.downstream.pci.secondary_bus, pcidev->attr->bridge.downstream.pci.subordinate_bus);
39   } else
40     hwloc_debug("%s Device [%04x:%04x (%04x:%04x) rev=%02x class=%04x]\n", busid,
41                 pcidev->attr->pcidev.vendor_id, pcidev->attr->pcidev.device_id,
42                 pcidev->attr->pcidev.subvendor_id, pcidev->attr->pcidev.subdevice_id,
43                 pcidev->attr->pcidev.revision, pcidev->attr->pcidev.class_id);
44 }
45 #endif /* HWLOC_DEBUG */
46 
47 static void
hwloc_pci_traverse_lookuposdevices_cb(void * cbdata,struct hwloc_obj * pcidev)48 hwloc_pci_traverse_lookuposdevices_cb(void * cbdata,
49                                       struct hwloc_obj *pcidev)
50 {
51   struct hwloc_backend *backend = cbdata;
52 
53   if (pcidev->type == HWLOC_OBJ_BRIDGE)
54     return;
55 
56   hwloc_backends_notify_new_object(backend, pcidev);
57 }
58 
59 static void
hwloc_pci__traverse(void * cbdata,struct hwloc_obj * root,void (* cb)(void * cbdata,struct hwloc_obj *))60 hwloc_pci__traverse(void * cbdata, struct hwloc_obj *root,
61                     void (*cb)(void * cbdata, struct hwloc_obj *))
62 {
63   struct hwloc_obj *child = root->first_child;
64   while (child) {
65     cb(cbdata, child);
66     if (child->type == HWLOC_OBJ_BRIDGE)
67       hwloc_pci__traverse(cbdata, child, cb);
68     child = child->next_sibling;
69   }
70 }
71 
72 static void
hwloc_pci_traverse(void * cbdata,struct hwloc_obj * root,void (* cb)(void * cbdata,struct hwloc_obj *))73 hwloc_pci_traverse(void * cbdata, struct hwloc_obj *root,
74                    void (*cb)(void * cbdata, struct hwloc_obj *))
75 {
76   hwloc_pci__traverse(cbdata, root, cb);
77 }
78 
79 enum hwloc_pci_busid_comparison_e {
80   HWLOC_PCI_BUSID_LOWER,
81   HWLOC_PCI_BUSID_HIGHER,
82   HWLOC_PCI_BUSID_INCLUDED,
83   HWLOC_PCI_BUSID_SUPERSET
84 };
85 
86 static enum hwloc_pci_busid_comparison_e
hwloc_pci_compare_busids(struct hwloc_obj * a,struct hwloc_obj * b)87 hwloc_pci_compare_busids(struct hwloc_obj *a, struct hwloc_obj *b)
88 {
89   if (a->type == HWLOC_OBJ_BRIDGE)
90     assert(a->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI);
91   if (b->type == HWLOC_OBJ_BRIDGE)
92     assert(b->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI);
93 
94   if (a->attr->pcidev.domain < b->attr->pcidev.domain)
95     return HWLOC_PCI_BUSID_LOWER;
96   if (a->attr->pcidev.domain > b->attr->pcidev.domain)
97     return HWLOC_PCI_BUSID_HIGHER;
98 
99   if (a->type == HWLOC_OBJ_BRIDGE
100       && b->attr->pcidev.bus >= a->attr->bridge.downstream.pci.secondary_bus
101       && b->attr->pcidev.bus <= a->attr->bridge.downstream.pci.subordinate_bus)
102     return HWLOC_PCI_BUSID_SUPERSET;
103   if (b->type == HWLOC_OBJ_BRIDGE
104       && a->attr->pcidev.bus >= b->attr->bridge.downstream.pci.secondary_bus
105       && a->attr->pcidev.bus <= b->attr->bridge.downstream.pci.subordinate_bus)
106     return HWLOC_PCI_BUSID_INCLUDED;
107 
108   if (a->attr->pcidev.bus < b->attr->pcidev.bus)
109     return HWLOC_PCI_BUSID_LOWER;
110   if (a->attr->pcidev.bus > b->attr->pcidev.bus)
111     return HWLOC_PCI_BUSID_HIGHER;
112 
113   if (a->attr->pcidev.dev < b->attr->pcidev.dev)
114     return HWLOC_PCI_BUSID_LOWER;
115   if (a->attr->pcidev.dev > b->attr->pcidev.dev)
116     return HWLOC_PCI_BUSID_HIGHER;
117 
118   if (a->attr->pcidev.func < b->attr->pcidev.func)
119     return HWLOC_PCI_BUSID_LOWER;
120   if (a->attr->pcidev.func > b->attr->pcidev.func)
121     return HWLOC_PCI_BUSID_HIGHER;
122 
123   /* Should never reach here.  Abort on both debug builds and
124      non-debug builds */
125   assert(0);
126   fprintf(stderr, "Bad assertion in hwloc %s:%d (aborting)\n", __FILE__, __LINE__);
127   exit(1);
128 }
129 
130 static void
hwloc_pci_add_child_before(struct hwloc_obj * root,struct hwloc_obj * child,struct hwloc_obj * new)131 hwloc_pci_add_child_before(struct hwloc_obj *root, struct hwloc_obj *child, struct hwloc_obj *new)
132 {
133   if (child) {
134     new->prev_sibling = child->prev_sibling;
135     child->prev_sibling = new;
136   } else {
137     new->prev_sibling = root->last_child;
138     root->last_child = new;
139   }
140 
141   if (new->prev_sibling)
142     new->prev_sibling->next_sibling = new;
143   else
144     root->first_child = new;
145   new->next_sibling = child;
146 
147   new->parent = root; /* so that hwloc_pci_traverse_print_cb() can indent by depth */
148 }
149 
150 static void
hwloc_pci_remove_child(struct hwloc_obj * root,struct hwloc_obj * child)151 hwloc_pci_remove_child(struct hwloc_obj *root, struct hwloc_obj *child)
152 {
153   if (child->next_sibling)
154     child->next_sibling->prev_sibling = child->prev_sibling;
155   else
156     root->last_child = child->prev_sibling;
157   if (child->prev_sibling)
158     child->prev_sibling->next_sibling = child->next_sibling;
159   else
160     root->first_child = child->next_sibling;
161   child->prev_sibling = NULL;
162   child->next_sibling = NULL;
163 }
164 
165 static void hwloc_pci_add_object(struct hwloc_obj *root, struct hwloc_obj *new);
166 
167 static void
hwloc_pci_try_insert_siblings_below_new_bridge(struct hwloc_obj * root,struct hwloc_obj * new)168 hwloc_pci_try_insert_siblings_below_new_bridge(struct hwloc_obj *root, struct hwloc_obj *new)
169 {
170   enum hwloc_pci_busid_comparison_e comp;
171   struct hwloc_obj *current, *next;
172 
173   next = new->next_sibling;
174   while (next) {
175     current = next;
176     next = current->next_sibling;
177 
178     comp = hwloc_pci_compare_busids(current, new);
179     assert(comp != HWLOC_PCI_BUSID_SUPERSET);
180     if (comp == HWLOC_PCI_BUSID_HIGHER)
181       continue;
182     assert(comp == HWLOC_PCI_BUSID_INCLUDED);
183 
184     /* move this object below the new bridge */
185     hwloc_pci_remove_child(root, current);
186     hwloc_pci_add_object(new, current);
187   }
188 }
189 
190 static void
hwloc_pci_add_object(struct hwloc_obj * root,struct hwloc_obj * new)191 hwloc_pci_add_object(struct hwloc_obj *root, struct hwloc_obj *new)
192 {
193   struct hwloc_obj *current;
194 
195   current = root->first_child;
196   while (current) {
197     enum hwloc_pci_busid_comparison_e comp = hwloc_pci_compare_busids(new, current);
198     switch (comp) {
199     case HWLOC_PCI_BUSID_HIGHER:
200       /* go further */
201       current = current->next_sibling;
202       continue;
203     case HWLOC_PCI_BUSID_INCLUDED:
204       /* insert below current bridge */
205       hwloc_pci_add_object(current, new);
206       return;
207     case HWLOC_PCI_BUSID_LOWER:
208     case HWLOC_PCI_BUSID_SUPERSET:
209       /* insert before current object */
210       hwloc_pci_add_child_before(root, current, new);
211       /* walk next siblings and move them below new bridge if needed */
212       hwloc_pci_try_insert_siblings_below_new_bridge(root, new);
213       return;
214     }
215   }
216   /* add to the end of the list if higher than everybody */
217   hwloc_pci_add_child_before(root, NULL, new);
218 }
219 
220 static struct hwloc_obj *
hwloc_pci_fixup_hostbridge_parent(struct hwloc_topology * topology __hwloc_attribute_unused,struct hwloc_obj * hostbridge,struct hwloc_obj * parent)221 hwloc_pci_fixup_hostbridge_parent(struct hwloc_topology *topology __hwloc_attribute_unused,
222                                   struct hwloc_obj *hostbridge,
223                                   struct hwloc_obj *parent)
224 {
225   /* Xeon E5v3 in cluster-on-die mode only have PCI on the first NUMA node of each package.
226    * but many dual-processor host report the second PCI hierarchy on 2nd NUMA of first package.
227    */
228   if (parent->depth >= 2
229       && parent->type == HWLOC_OBJ_NUMANODE
230       && parent->sibling_rank == 1 && parent->parent->arity == 2
231       && parent->parent->type == HWLOC_OBJ_PACKAGE
232       && parent->parent->sibling_rank == 0 && parent->parent->parent->arity == 2) {
233     const char *cpumodel = hwloc_obj_get_info_by_name(parent->parent, "CPUModel");
234     if (cpumodel && strstr(cpumodel, "Xeon")) {
235       if (!hwloc_hide_errors()) {
236         fprintf(stderr, "****************************************************************************\n");
237         fprintf(stderr, "* hwloc %s has encountered an incorrect PCI locality information.\n", HWLOC_VERSION);
238         fprintf(stderr, "* PCI bus %04x:%02x is supposedly close to 2nd NUMA node of 1st package,\n",
239                 hostbridge->first_child->attr->pcidev.domain, hostbridge->first_child->attr->pcidev.bus);
240         fprintf(stderr, "* however hwloc believes this is impossible on this architecture.\n");
241         fprintf(stderr, "* Therefore the PCI bus will be moved to 1st NUMA node of 2nd package.\n");
242         fprintf(stderr, "*\n");
243         fprintf(stderr, "* If you feel this fixup is wrong, disable it by setting in your environment\n");
244         fprintf(stderr, "* HWLOC_PCI_%04x_%02x_LOCALCPUS= (empty value), and report the problem\n",
245                 hostbridge->first_child->attr->pcidev.domain, hostbridge->first_child->attr->pcidev.bus);
246         fprintf(stderr, "* to the hwloc's user mailing list together with the XML output of lstopo.\n");
247         fprintf(stderr, "*\n");
248         fprintf(stderr, "* You may silence this message by setting HWLOC_HIDE_ERRORS=1 in your environment.\n");
249         fprintf(stderr, "****************************************************************************\n");
250       }
251       return parent->parent->next_sibling->first_child;
252     }
253   }
254 
255   return parent;
256 }
257 
258 static struct hwloc_obj *
hwloc_pci_find_hostbridge_parent(struct hwloc_topology * topology,struct hwloc_backend * backend,struct hwloc_obj * hostbridge)259 hwloc_pci_find_hostbridge_parent(struct hwloc_topology *topology, struct hwloc_backend *backend,
260                                  struct hwloc_obj *hostbridge)
261 {
262   hwloc_bitmap_t cpuset = hwloc_bitmap_alloc();
263   struct hwloc_obj *parent;
264   const char *env;
265   int err;
266 
267   /* override the cpuset with the environment if given */
268   int forced = 0;
269   char envname[256];
270   snprintf(envname, sizeof(envname), "HWLOC_PCI_%04x_%02x_LOCALCPUS",
271            hostbridge->first_child->attr->pcidev.domain, hostbridge->first_child->attr->pcidev.bus);
272   env = getenv(envname);
273   if (env)
274     /* if env exists but is empty, don't let quirks change what the OS reports */
275     forced = 1;
276   if (env && *env) {
277     /* force the hostbridge cpuset */
278     hwloc_debug("Overriding localcpus using %s in the environment\n", envname);
279     hwloc_bitmap_sscanf(cpuset, env);
280   } else {
281     /* get the hostbridge cpuset by acking the OS backend.
282      * it's not a PCI device, so we use its first child locality info.
283      */
284     err = hwloc_backends_get_obj_cpuset(backend, hostbridge->first_child, cpuset);
285     if (err < 0)
286       /* if we got nothing, assume the hostbridge is attached to the top of hierarchy */
287       hwloc_bitmap_copy(cpuset, hwloc_topology_get_topology_cpuset(topology));
288   }
289 
290   hwloc_debug_bitmap("Attaching hostbridge to cpuset %s\n", cpuset);
291 
292   /* restrict to the existing topology cpuset to avoid errors later */
293   hwloc_bitmap_and(cpuset, cpuset, hwloc_topology_get_topology_cpuset(topology));
294 
295   /* if the remaining cpuset is empty, take the root */
296   if (hwloc_bitmap_iszero(cpuset))
297     hwloc_bitmap_copy(cpuset, hwloc_topology_get_topology_cpuset(topology));
298 
299   /* attach the hostbridge now that it contains the right objects */
300   parent = hwloc_get_obj_covering_cpuset(topology, cpuset);
301   /* in the worst case, we got the root object */
302 
303   if (hwloc_bitmap_isequal(cpuset, parent->cpuset)) {
304     /* this object has the right cpuset, but it could be a cache or so,
305      * go up as long as the cpuset is the same
306      */
307     while (parent->parent && hwloc_bitmap_isequal(parent->cpuset, parent->parent->cpuset))
308       parent = parent->parent;
309 
310     if (!forced)
311       parent = hwloc_pci_fixup_hostbridge_parent(topology, hostbridge, parent);
312 
313   } else {
314     /* the object we found is too large, insert an intermediate group */
315     hwloc_obj_t group_obj = hwloc_alloc_setup_object(HWLOC_OBJ_GROUP, -1);
316     if (group_obj) {
317       group_obj->cpuset = hwloc_bitmap_dup(cpuset);
318       group_obj->complete_cpuset = hwloc_bitmap_dup(cpuset);
319       group_obj->attr->group.depth = (unsigned) -1;
320       parent = hwloc__insert_object_by_cpuset(topology, group_obj, hwloc_report_os_error);
321       if (parent == group_obj)
322         /* if didn't get merged, setup its sets */
323         hwloc_fill_object_sets(group_obj);
324       if (!parent)
325         /* Failed to insert the parent, maybe a conflicting cpuset, attach to the root object instead */
326         parent = hwloc_get_root_obj(topology);
327     }
328   }
329 
330   hwloc_bitmap_free(cpuset);
331 
332   return parent;
333 }
334 
335 int
hwloc_insert_pci_device_list(struct hwloc_backend * backend,struct hwloc_obj * first_obj)336 hwloc_insert_pci_device_list(struct hwloc_backend *backend,
337                              struct hwloc_obj *first_obj)
338 {
339   struct hwloc_topology *topology = backend->topology;
340   struct hwloc_obj fakeparent;
341   struct hwloc_obj *obj;
342   unsigned current_hostbridge;
343 
344   if (!first_obj)
345     /* found nothing, exit */
346     return 0;
347 
348   /* first, organise object as tree under a fake parent object */
349   fakeparent.parent = NULL;
350   fakeparent.first_child = NULL;
351   fakeparent.last_child = NULL;
352   while (first_obj) {
353     obj = first_obj;
354     first_obj = obj->next_sibling;
355     hwloc_pci_add_object(&fakeparent, obj);
356   }
357 
358 #ifdef HWLOC_DEBUG
359   hwloc_debug("%s", "\nPCI hierarchy under fake parent:\n");
360   hwloc_pci_traverse(NULL, &fakeparent, hwloc_pci_traverse_print_cb);
361   hwloc_debug("%s", "\n");
362 #endif
363 
364   /* walk the hierarchy, and lookup OS devices */
365   hwloc_pci_traverse(backend, &fakeparent, hwloc_pci_traverse_lookuposdevices_cb);
366 
367   /*
368    * fakeparent lists all objects connected to any upstream bus in the machine.
369    * We now create one real hostbridge object per upstream bus.
370    * It's not actually a PCI device so we have to create it.
371    */
372   current_hostbridge = 0;
373   while (fakeparent.first_child) {
374     /* start a new host bridge */
375     struct hwloc_obj *hostbridge = hwloc_alloc_setup_object(HWLOC_OBJ_BRIDGE, current_hostbridge++);
376     struct hwloc_obj *child = fakeparent.first_child;
377     struct hwloc_obj *next_child;
378     struct hwloc_obj *parent;
379     unsigned short current_domain = child->attr->pcidev.domain;
380     unsigned char current_bus = child->attr->pcidev.bus;
381     unsigned char current_subordinate = current_bus;
382 
383     hwloc_debug("Starting new PCI hostbridge %04x:%02x\n", current_domain, current_bus);
384 
385     /*
386      * attach all objects from the same upstream domain/bus
387      */
388   next_child:
389     next_child = child->next_sibling;
390     hwloc_pci_remove_child(&fakeparent, child);
391     hwloc_pci_add_child_before(hostbridge, NULL, child);
392 
393     /* compute hostbridge secondary/subordinate buses */
394     if (child->type == HWLOC_OBJ_BRIDGE
395         && child->attr->bridge.downstream.pci.subordinate_bus > current_subordinate)
396       current_subordinate = child->attr->bridge.downstream.pci.subordinate_bus;
397 
398     /* use next child if it has the same domains/bus */
399     child = next_child;
400     if (child
401         && child->attr->pcidev.domain == current_domain
402         && child->attr->pcidev.bus == current_bus)
403       goto next_child;
404 
405     /* finish setting up this hostbridge */
406     hostbridge->attr->bridge.upstream_type = HWLOC_OBJ_BRIDGE_HOST;
407     hostbridge->attr->bridge.downstream_type = HWLOC_OBJ_BRIDGE_PCI;
408     hostbridge->attr->bridge.downstream.pci.domain = current_domain;
409     hostbridge->attr->bridge.downstream.pci.secondary_bus = current_bus;
410     hostbridge->attr->bridge.downstream.pci.subordinate_bus = current_subordinate;
411     hwloc_debug("New PCI hostbridge %04x:[%02x-%02x]\n",
412                 current_domain, current_bus, current_subordinate);
413 
414     /* attach the hostbridge where it belongs */
415     parent = hwloc_pci_find_hostbridge_parent(topology, backend, hostbridge);
416     hwloc_insert_object_by_parent(topology, parent, hostbridge);
417   }
418 
419   return 1;
420 }
421 
422 #define HWLOC_PCI_STATUS 0x06
423 #define HWLOC_PCI_STATUS_CAP_LIST 0x10
424 #define HWLOC_PCI_CAPABILITY_LIST 0x34
425 #define HWLOC_PCI_CAP_LIST_ID 0
426 #define HWLOC_PCI_CAP_LIST_NEXT 1
427 
428 unsigned
hwloc_pci_find_cap(const unsigned char * config,unsigned cap)429 hwloc_pci_find_cap(const unsigned char *config, unsigned cap)
430 {
431   unsigned char seen[256] = { 0 };
432   unsigned char ptr; /* unsigned char to make sure we stay within the 256-byte config space */
433 
434   if (!(config[HWLOC_PCI_STATUS] & HWLOC_PCI_STATUS_CAP_LIST))
435     return 0;
436 
437   for (ptr = config[HWLOC_PCI_CAPABILITY_LIST] & ~3;
438        ptr; /* exit if next is 0 */
439        ptr = config[ptr + HWLOC_PCI_CAP_LIST_NEXT] & ~3) {
440     unsigned char id;
441 
442     /* Looped around! */
443     if (seen[ptr])
444       break;
445     seen[ptr] = 1;
446 
447     id = config[ptr + HWLOC_PCI_CAP_LIST_ID];
448     if (id == cap)
449       return ptr;
450     if (id == 0xff) /* exit if id is 0 or 0xff */
451       break;
452   }
453   return 0;
454 }
455 
456 #define HWLOC_PCI_EXP_LNKSTA 0x12
457 #define HWLOC_PCI_EXP_LNKSTA_SPEED 0x000f
458 #define HWLOC_PCI_EXP_LNKSTA_WIDTH 0x03f0
459 
460 int
hwloc_pci_find_linkspeed(const unsigned char * config,unsigned offset,float * linkspeed)461 hwloc_pci_find_linkspeed(const unsigned char *config,
462                          unsigned offset, float *linkspeed)
463 {
464   unsigned linksta, speed, width;
465   float lanespeed;
466 
467   memcpy(&linksta, &config[offset + HWLOC_PCI_EXP_LNKSTA], 4);
468   speed = linksta & HWLOC_PCI_EXP_LNKSTA_SPEED; /* PCIe generation */
469   width = (linksta & HWLOC_PCI_EXP_LNKSTA_WIDTH) >> 4; /* how many lanes */
470   /* PCIe Gen1 = 2.5GT/s signal-rate per lane with 8/10 encoding    = 0.25GB/s data-rate per lane
471    * PCIe Gen2 = 5  GT/s signal-rate per lane with 8/10 encoding    = 0.5 GB/s data-rate per lane
472    * PCIe Gen3 = 8  GT/s signal-rate per lane with 128/130 encoding = 1   GB/s data-rate per lane
473    * PCIe Gen4 = 16 GT/s signal-rate per lane with 128/130 encoding = 2   GB/s data-rate per lane
474    */
475 
476   /* lanespeed in Gbit/s */
477   if (speed <= 2)
478     lanespeed = 2.5f * speed * 0.8f;
479   else
480     lanespeed = 8.0f * (1<<(speed-3)) * 128/130; /* assume Gen5 will be 32 GT/s and so on */
481 
482   /* linkspeed in GB/s */
483   *linkspeed = lanespeed * width / 8;
484   return 0;
485 }
486 
487 #define HWLOC_PCI_HEADER_TYPE 0x0e
488 #define HWLOC_PCI_HEADER_TYPE_BRIDGE 1
489 #define HWLOC_PCI_CLASS_BRIDGE_PCI 0x0604
490 #define HWLOC_PCI_PRIMARY_BUS 0x18
491 #define HWLOC_PCI_SECONDARY_BUS 0x19
492 #define HWLOC_PCI_SUBORDINATE_BUS 0x1a
493 
494 int
hwloc_pci_prepare_bridge(hwloc_obj_t obj,const unsigned char * config)495 hwloc_pci_prepare_bridge(hwloc_obj_t obj,
496                          const unsigned char *config)
497 {
498   unsigned char headertype;
499   unsigned isbridge;
500   struct hwloc_pcidev_attr_s *pattr = &obj->attr->pcidev;
501   struct hwloc_bridge_attr_s *battr;
502 
503   headertype = config[HWLOC_PCI_HEADER_TYPE] & 0x7f;
504   isbridge = (pattr->class_id == HWLOC_PCI_CLASS_BRIDGE_PCI
505               && headertype == HWLOC_PCI_HEADER_TYPE_BRIDGE);
506 
507   if (!isbridge)
508     return 0;
509 
510   battr = &obj->attr->bridge;
511 
512   if (config[HWLOC_PCI_PRIMARY_BUS] != pattr->bus) {
513     /* Sometimes the config space contains 00 instead of the actual primary bus number.
514      * Always trust the bus ID because it was built by the system which has more information
515      * to workaround such problems (e.g. ACPI information about PCI parent/children).
516      */
517     hwloc_debug("  %04x:%02x:%02x.%01x bridge with (ignored) invalid PCI_PRIMARY_BUS %02x\n",
518                 pattr->domain, pattr->bus, pattr->dev, pattr->func, config[HWLOC_PCI_PRIMARY_BUS]);
519   }
520 
521   obj->type = HWLOC_OBJ_BRIDGE;
522   battr->upstream_type = HWLOC_OBJ_BRIDGE_PCI;
523   battr->downstream_type = HWLOC_OBJ_BRIDGE_PCI;
524   battr->downstream.pci.domain = pattr->domain;
525   battr->downstream.pci.secondary_bus = config[HWLOC_PCI_SECONDARY_BUS];
526   battr->downstream.pci.subordinate_bus = config[HWLOC_PCI_SUBORDINATE_BUS];
527 
528   if (battr->downstream.pci.secondary_bus <= pattr->bus
529       || battr->downstream.pci.subordinate_bus <= pattr->bus
530       || battr->downstream.pci.secondary_bus > battr->downstream.pci.subordinate_bus) {
531     /* This should catch most cases of invalid bridge information
532      * (e.g. 00 for secondary and subordinate).
533      * Ideally we would also check that [secondary-subordinate] is included
534      * in the parent bridge [secondary+1:subordinate]. But that's hard to do
535      * because objects may be discovered out of order (especially in the fsroot case).
536      */
537     hwloc_debug("  %04x:%02x:%02x.%01x bridge has invalid secondary-subordinate buses [%02x-%02x]\n",
538                 pattr->domain, pattr->bus, pattr->dev, pattr->func,
539                 battr->downstream.pci.secondary_bus, battr->downstream.pci.subordinate_bus);
540     hwloc_free_unlinked_object(obj);
541     return -1;
542   }
543 
544   return 0;
545 }
546