xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c (revision f6da83d4178694e7113b71d1e452f15b296f73d8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <sys/fm/protocol.h>
27 #include <assert.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <strings.h>
32 #include <alloca.h>
33 #include <sys/param.h>
34 #include <sys/pci.h>
35 #include <sys/pcie.h>
36 #include <libdevinfo.h>
37 #include <libnvpair.h>
38 #include <fm/topo_mod.h>
39 #include <fm/topo_hc.h>
40 
41 #include <hostbridge.h>
42 #include <pcibus.h>
43 #include <did.h>
44 #include <did_props.h>
45 #include <util.h>
46 
47 extern txprop_t Bus_common_props[];
48 extern txprop_t Dev_common_props[];
49 extern txprop_t Fn_common_props[];
50 extern int Bus_propcnt;
51 extern int Dev_propcnt;
52 extern int Fn_propcnt;
53 
54 extern int platform_pci_label(topo_mod_t *mod, tnode_t *, nvlist_t *,
55     nvlist_t **);
56 extern int platform_pci_fru(topo_mod_t *mod, tnode_t *, nvlist_t *,
57     nvlist_t **);
58 static void pci_release(topo_mod_t *, tnode_t *);
59 static int pci_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
60     topo_instance_t, void *, void *);
61 static int pci_label(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
62     nvlist_t **);
63 static int pci_fru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
64     nvlist_t **);
65 
66 static const topo_modops_t Pci_ops =
67 	{ pci_enum, pci_release };
68 static const topo_modinfo_t Pci_info =
69 	{ PCI_BUS, FM_FMRI_SCHEME_HC, PCI_ENUMR_VERS, &Pci_ops };
70 
71 static const topo_method_t Pci_methods[] = {
72 	{ TOPO_METH_LABEL, TOPO_METH_LABEL_DESC,
73 	    TOPO_METH_LABEL_VERSION, TOPO_STABILITY_INTERNAL, pci_label },
74 	{ TOPO_METH_FRU_COMPUTE, TOPO_METH_FRU_COMPUTE_DESC,
75 	    TOPO_METH_FRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, pci_fru },
76 	{ NULL }
77 };
78 
79 int
80 _topo_init(topo_mod_t *modhdl, topo_version_t version)
81 {
82 	/*
83 	 * Turn on module debugging output
84 	 */
85 	if (getenv("TOPOPCIDBG") != NULL)
86 		topo_mod_setdebug(modhdl);
87 	topo_mod_dprintf(modhdl, "initializing pcibus builtin\n");
88 
89 	if (version != PCI_ENUMR_VERS)
90 		return (topo_mod_seterrno(modhdl, EMOD_VER_NEW));
91 
92 	if (topo_mod_register(modhdl, &Pci_info, TOPO_VERSION) != 0) {
93 		topo_mod_dprintf(modhdl, "failed to register module");
94 		return (-1);
95 	}
96 	topo_mod_dprintf(modhdl, "PCI Enumr initd\n");
97 
98 	return (0);
99 }
100 
101 void
102 _topo_fini(topo_mod_t *modhdl)
103 {
104 	topo_mod_unregister(modhdl);
105 }
106 
107 static int
108 pci_label(topo_mod_t *mp, tnode_t *node, topo_version_t version,
109     nvlist_t *in, nvlist_t **out)
110 {
111 	if (version > TOPO_METH_LABEL_VERSION)
112 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
113 	return (platform_pci_label(mp, node, in, out));
114 }
115 static int
116 pci_fru(topo_mod_t *mp, tnode_t *node, topo_version_t version,
117     nvlist_t *in, nvlist_t **out)
118 {
119 	if (version > TOPO_METH_FRU_COMPUTE_VERSION)
120 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
121 	return (platform_pci_fru(mp, node, in, out));
122 }
123 static tnode_t *
124 pci_tnode_create(topo_mod_t *mod, tnode_t *parent,
125     const char *name, topo_instance_t i, void *priv)
126 {
127 	tnode_t *ntn;
128 
129 	if ((ntn = tnode_create(mod, parent, name, i, priv)) == NULL)
130 		return (NULL);
131 	if (topo_method_register(mod, ntn, Pci_methods) < 0) {
132 		topo_mod_dprintf(mod, "topo_method_register failed: %s\n",
133 		    topo_strerror(topo_mod_errno(mod)));
134 		topo_node_unbind(ntn);
135 		return (NULL);
136 	}
137 	return (ntn);
138 }
139 
140 /*ARGSUSED*/
141 static int
142 hostbridge_asdevice(topo_mod_t *mod, tnode_t *bus)
143 {
144 	di_node_t di;
145 	tnode_t *dev32;
146 
147 	di = topo_node_getspecific(bus);
148 	assert(di != DI_NODE_NIL);
149 
150 	if ((dev32 = pcidev_declare(mod, bus, di, 32)) == NULL)
151 		return (-1);
152 	if (pcifn_declare(mod, dev32, di, 0) == NULL) {
153 		topo_node_unbind(dev32);
154 		return (-1);
155 	}
156 	return (0);
157 }
158 
159 tnode_t *
160 pciexfn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
161     topo_instance_t i)
162 {
163 	did_t *pd;
164 	tnode_t *ntn, *ptn;
165 	di_node_t pdn;
166 	uint_t class, subclass;
167 	char *devtyp, *pdevtyp;
168 	int pcie_devtyp, pexcap;
169 	boolean_t dev_is_pcie, pdev_is_pcie;
170 
171 	/* We need the parent's dev info node for some of the info */
172 	ptn = find_predecessor(parent, PCIEX_FUNCTION);
173 	/* If this is the first child under root, get root's ptn */
174 	if (ptn == NULL)
175 		ptn = find_predecessor(parent, PCIEX_ROOT);
176 	if (ptn == NULL)
177 		return (NULL);
178 	pdn = topo_node_getspecific(ptn);
179 
180 	/* Get the required info to populate the excap */
181 	(void) pci_classcode_get(mod, dn, &class, &subclass);
182 	devtyp = pci_devtype_get(mod, dn);
183 	pdevtyp = pci_devtype_get(mod, pdn);
184 	pexcap = pciex_cap_get(mod, pdn);
185 
186 	dev_is_pcie = devtyp && (strcmp(devtyp, "pciex") == 0);
187 	pdev_is_pcie = pdevtyp && (strcmp(pdevtyp, "pciex") == 0);
188 
189 	/*
190 	 * Populate the excap with correct PCIe device type.
191 	 *
192 	 * Device	Parent		Device		Parent	Device
193 	 * excap	device-type	device-type	excap	Class Code
194 	 * -------------------------------------------------------------------
195 	 * PCI(default)	pci		N/A		N/A	!= bridge
196 	 * PCIe		pciex		N/A		N/A	!= bridge
197 	 * Root Port	Defined in hostbridge
198 	 * Switch Up	pciex		pciex		!= up	= bridge
199 	 * Switch Down	pciex		pciex		= up	= bridge
200 	 * PCIe-PCI	pciex		pci		N/A	= bridge
201 	 * PCI-PCIe	pci		pciex		N/A	= bridge
202 	 */
203 	pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCI_DEV;
204 	if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) {
205 		if (pdev_is_pcie) {
206 			if (dev_is_pcie) {
207 				if (pexcap != PCIE_PCIECAP_DEV_TYPE_UP)
208 					pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_UP;
209 				else
210 					pcie_devtyp =
211 					    PCIE_PCIECAP_DEV_TYPE_DOWN;
212 			} else {
213 				pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCIE2PCI;
214 			}
215 		} else {
216 			if (dev_is_pcie)
217 				pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCI2PCIE;
218 		}
219 	} else {
220 		if (pdev_is_pcie)
221 			pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
222 	}
223 
224 	if ((pd = did_find(mod, dn)) == NULL)
225 		return (NULL);
226 	did_excap_set(pd, pcie_devtyp);
227 
228 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_FUNCTION, i, dn))
229 	    == NULL)
230 		return (NULL);
231 	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
232 		topo_node_unbind(ntn);
233 		return (NULL);
234 	}
235 	/*
236 	 * We may find pci-express buses or plain-pci buses beneath a function
237 	 */
238 	if (child_range_add(mod, ntn, PCIEX_BUS, 0, MAX_HB_BUSES) < 0) {
239 		topo_node_unbind(ntn);
240 		return (NULL);
241 	}
242 	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
243 		topo_node_range_destroy(ntn, PCIEX_BUS);
244 		topo_node_unbind(ntn);
245 		return (NULL);
246 	}
247 	return (ntn);
248 }
249 
250 tnode_t *
251 pciexdev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
252     topo_instance_t i)
253 {
254 	did_t *pd;
255 	tnode_t *ntn;
256 
257 	if ((pd = did_find(mod, dn)) == NULL)
258 		return (NULL);
259 	did_settnode(pd, parent);
260 
261 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_DEVICE, i, dn)) == NULL)
262 		return (NULL);
263 	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
264 		topo_node_unbind(ntn);
265 		return (NULL);
266 	}
267 
268 	/*
269 	 * We can expect to find pci-express functions beneath the device
270 	 */
271 	if (child_range_add(mod,
272 	    ntn, PCIEX_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
273 		topo_node_unbind(ntn);
274 		return (NULL);
275 	}
276 	return (ntn);
277 }
278 
279 tnode_t *
280 pciexbus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
281     topo_instance_t i)
282 {
283 	did_t *pd;
284 	tnode_t *ntn;
285 
286 	if ((pd = did_find(mod, dn)) == NULL)
287 		return (NULL);
288 	did_settnode(pd, parent);
289 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_BUS, i, dn)) == NULL)
290 		return (NULL);
291 	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
292 		topo_node_unbind(ntn);
293 		return (NULL);
294 	}
295 	/*
296 	 * We can expect to find pci-express devices beneath the bus
297 	 */
298 	if (child_range_add(mod,
299 	    ntn, PCIEX_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
300 		topo_node_unbind(ntn);
301 		return (NULL);
302 	}
303 	return (ntn);
304 }
305 
306 tnode_t *
307 pcifn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
308     topo_instance_t i)
309 {
310 	did_t *pd;
311 	tnode_t *ntn;
312 
313 	if ((pd = did_find(mod, dn)) == NULL)
314 		return (NULL);
315 	did_excap_set(pd, PCIE_PCIECAP_DEV_TYPE_PCI_DEV);
316 
317 	if ((ntn = pci_tnode_create(mod, parent, PCI_FUNCTION, i, dn)) == NULL)
318 		return (NULL);
319 	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
320 		topo_node_unbind(ntn);
321 		return (NULL);
322 	}
323 	/*
324 	 * We may find pci buses beneath a function
325 	 */
326 	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
327 		topo_node_unbind(ntn);
328 		return (NULL);
329 	}
330 	return (ntn);
331 }
332 
333 tnode_t *
334 pcidev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
335     topo_instance_t i)
336 {
337 	did_t *pd;
338 	tnode_t *ntn;
339 
340 	if ((pd = did_find(mod, dn)) == NULL)
341 		return (NULL);
342 	/* remember parent tnode */
343 	did_settnode(pd, parent);
344 
345 	if ((ntn = pci_tnode_create(mod, parent, PCI_DEVICE, i, dn)) == NULL)
346 		return (NULL);
347 	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
348 		topo_node_unbind(ntn);
349 		return (NULL);
350 	}
351 
352 	/*
353 	 * We can expect to find pci functions beneath the device
354 	 */
355 	if (child_range_add(mod, ntn, PCI_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
356 		topo_node_unbind(ntn);
357 		return (NULL);
358 	}
359 	return (ntn);
360 }
361 
362 tnode_t *
363 pcibus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
364     topo_instance_t i)
365 {
366 	did_t *pd;
367 	tnode_t *ntn;
368 	int hbchild = 0;
369 
370 	if ((pd = did_find(mod, dn)) == NULL)
371 		return (NULL);
372 	did_settnode(pd, parent);
373 	if ((ntn = pci_tnode_create(mod, parent, PCI_BUS, i, dn)) == NULL)
374 		return (NULL);
375 	/*
376 	 * If our devinfo node is lacking certain information of its
377 	 * own, and our parent topology node is a hostbridge, we may
378 	 * need/want to inherit information available in the
379 	 * hostbridge node's private data.
380 	 */
381 	if (strcmp(topo_node_name(parent), HOSTBRIDGE) == 0)
382 		hbchild = 1;
383 	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
384 		topo_node_unbind(ntn);
385 		return (NULL);
386 	}
387 	/*
388 	 * We can expect to find pci devices beneath the bus
389 	 */
390 	if (child_range_add(mod, ntn, PCI_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
391 		topo_node_unbind(ntn);
392 		return (NULL);
393 	}
394 	/*
395 	 * On each bus child of the hostbridge, we represent the
396 	 * hostbridge as a device outside the range of legal device
397 	 * numbers.
398 	 */
399 	if (hbchild == 1) {
400 		if (hostbridge_asdevice(mod, ntn) < 0) {
401 			topo_node_range_destroy(ntn, PCI_DEVICE);
402 			topo_node_unbind(ntn);
403 			return (NULL);
404 		}
405 	}
406 	return (ntn);
407 }
408 
409 static int
410 pci_bridge_declare(topo_mod_t *mod, tnode_t *fn, di_node_t din, int board,
411     int bridge, int rc, int depth)
412 {
413 	int err;
414 	char *devtyp;
415 
416 	devtyp = pci_devtype_get(mod, din);
417 	/* Check if the children are PCI or PCIe */
418 	if (devtyp && (strcmp(devtyp, "pciex") == 0))
419 		err = pci_children_instantiate(mod, fn, din, board, bridge,
420 		    rc, TRUST_BDF, depth + 1);
421 	else
422 		err = pci_children_instantiate(mod, fn, din, board, bridge,
423 		    rc - TO_PCI, TRUST_BDF, depth + 1);
424 	return (err);
425 }
426 
427 static void
428 declare_dev_and_fn(topo_mod_t *mod, tnode_t *bus, tnode_t **dev, di_node_t din,
429     int board, int bridge, int rc, int devno, int fnno, int depth)
430 {
431 	int dcnt = 0, rcnt;
432 	char *propstr;
433 	tnode_t *fn;
434 	uint_t class, subclass;
435 	uint_t vid, did;
436 	did_t *dp = NULL;
437 
438 	if (*dev == NULL) {
439 		if (rc >= 0)
440 			*dev = pciexdev_declare(mod, bus, din, devno);
441 		else
442 			*dev = pcidev_declare(mod, bus, din, devno);
443 		if (*dev == NULL)
444 			return;
445 		++dcnt;
446 	}
447 	if (rc >= 0)
448 		fn = pciexfn_declare(mod, *dev, din, fnno);
449 	else
450 		fn = pcifn_declare(mod, *dev, din, fnno);
451 
452 	if (fn == NULL) {
453 		if (dcnt) {
454 			topo_node_unbind(*dev);
455 			*dev = NULL;
456 		}
457 		return;
458 	}
459 
460 	if (pci_classcode_get(mod, din, &class, &subclass) < 0) {
461 		topo_node_unbind(fn);
462 		if (dcnt)
463 			topo_node_unbind(*dev);
464 		return;
465 	}
466 
467 	/*
468 	 * This function may be a bridge.  If not, check for a possible
469 	 * topology map file and kick off its enumeration of lower-level
470 	 * devices.
471 	 */
472 	if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) {
473 		(void) pci_bridge_declare(mod, fn, din, board, bridge, rc,
474 		    depth);
475 	}
476 
477 	/*
478 	 * Check for a Neptune-based NIC. This could either be a Neptune
479 	 * adapter card or an Neptune ASIC on a board (e.g. motherboard)
480 	 *
481 	 * For Netpune adapter cards, use xfp-hc-topology.xml to expand
482 	 * topology to include the XFP optical module, which is a FRU on
483 	 * the Neptune based 10giga fiber NICs.
484 	 *
485 	 * For Neptune ASICs, use the XAUI enumerator to expand topology.
486 	 * The 10giga ports are externalized by a XAUI cards, which
487 	 * are FRUs. The XAUI enumerator in turn instantiates the XFP
488 	 * optical module FRUs.
489 	 */
490 	else if (class == PCI_CLASS_NET &&
491 	    di_uintprop_get(mod, din, DI_VENDIDPROP, &vid) >= 0 &&
492 	    di_uintprop_get(mod, din, DI_DEVIDPROP, &did) >= 0) {
493 		if (vid == SUN_VENDOR_ID && did == NEPTUNE_DEVICE_ID) {
494 			/*
495 			 * Is this an adapter card? Check the bus's physlot
496 			 */
497 			dp = did_find(mod, topo_node_getspecific(bus));
498 			if (did_physlot(dp) >= 0) {
499 				topo_mod_dprintf(mod, "Found Neptune slot\n");
500 				(void) topo_mod_enummap(mod, fn,
501 				    "xfp", FM_FMRI_SCHEME_HC);
502 			} else {
503 				topo_mod_dprintf(mod, "Found Neptune ASIC\n");
504 				if (topo_mod_load(mod, XAUI, TOPO_VERSION) ==
505 				    NULL) {
506 					topo_mod_dprintf(mod, "pcibus enum "
507 					    "could not load xaui enum\n");
508 					(void) topo_mod_seterrno(mod,
509 					    EMOD_PARTIAL_ENUM);
510 					return;
511 				} else {
512 					if (topo_node_range_create(mod, fn,
513 					    XAUI, 0, 1) < 0) {
514 						topo_mod_dprintf(mod,
515 						    "child_range_add for "
516 						    "XAUI failed: %s\n",
517 						    topo_strerror(
518 						    topo_mod_errno(mod)));
519 						return;
520 					}
521 					(void) topo_mod_enumerate(mod, fn,
522 					    XAUI, XAUI, fnno, fnno, fn);
523 				}
524 			}
525 		}
526 	} else if (class == PCI_CLASS_MASS) {
527 		di_node_t cn;
528 		int niports = 0;
529 		extern void pci_iports_instantiate(topo_mod_t *, tnode_t *,
530 		    di_node_t, int);
531 		extern void pci_receptacle_instantiate(topo_mod_t *, tnode_t *,
532 		    di_node_t);
533 
534 		for (cn = di_child_node(din); cn != DI_NODE_NIL;
535 		    cn = di_sibling_node(cn)) {
536 			if (strcmp(di_node_name(cn), IPORT) == 0)
537 				niports++;
538 		}
539 		if (niports > 0)
540 			pci_iports_instantiate(mod, fn, din, niports);
541 
542 		if ((rcnt = di_prop_lookup_strings(DDI_DEV_T_ANY, din,
543 		    DI_RECEPTACLE_PHYMASK, &propstr)) > 0) {
544 			if (topo_node_range_create(mod, fn, RECEPTACLE, 0,
545 			    rcnt) >= 0)
546 				pci_receptacle_instantiate(mod, fn, din);
547 		}
548 	}
549 }
550 
551 int
552 pci_children_instantiate(topo_mod_t *mod, tnode_t *parent, di_node_t pn,
553     int board, int bridge, int rc, int bover, int depth)
554 {
555 	did_t *pps[MAX_PCIBUS_DEVS][MAX_PCIDEV_FNS];
556 	did_t *bp = NULL;
557 	did_t *np;
558 	di_node_t sib;
559 	di_node_t din;
560 	tnode_t *bn = NULL;
561 	tnode_t *dn = NULL;
562 	int pb = -1;
563 	int b, d, f;
564 
565 	for (d = 0; d < MAX_PCIBUS_DEVS; d++)
566 		for (f = 0; f < MAX_PCIDEV_FNS; f++)
567 			pps[d][f] = NULL;
568 
569 	/* start at the parent's first sibling */
570 	sib = di_child_node(pn);
571 	while (sib != DI_NODE_NIL) {
572 		np = did_create(mod, sib, board, bridge, rc, bover);
573 		if (np == NULL)
574 			return (-1);
575 		did_BDF(np, &b, &d, &f);
576 		pps[d][f] = np;
577 		if (bp == NULL)
578 			bp = np;
579 		if (pb < 0)
580 			pb = ((bover == TRUST_BDF) ? b : bover);
581 		sib = di_sibling_node(sib);
582 	}
583 	if (pb < 0 && bover < 0)
584 		return (0);
585 	if (rc >= 0)
586 		bn = pciexbus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
587 	else
588 		bn = pcibus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
589 	if (bn == NULL)
590 		return (-1);
591 	if (pb < 0)
592 		return (0);
593 
594 	for (d = 0; d < MAX_PCIBUS_DEVS; d++) {
595 		for (f = 0; f < MAX_PCIDEV_FNS; f++) {
596 			if (pps[d][f] == NULL)
597 				continue;
598 			din = did_dinode(pps[d][f]);
599 
600 			/*
601 			 * Try to enumerate as many devices and functions as
602 			 * possible.  If we fail to declare a device, break
603 			 * out of the function loop.
604 			 */
605 			declare_dev_and_fn(mod, bn,
606 			    &dn, din, board, bridge, rc, d, f, depth);
607 			did_rele(pps[d][f]);
608 
609 			if (dn == NULL)
610 				break;
611 		}
612 		dn = NULL;
613 	}
614 	return (0);
615 }
616 
617 static int
618 pciexbus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
619     topo_instance_t max)
620 {
621 	di_node_t pdn;
622 	int rc, hb;
623 	tnode_t *hbtn;
624 	int retval;
625 
626 	/*
627 	 * PCI-Express; parent node's private data is a simple di_node_t
628 	 * and we have to construct our own did hash and did_t.
629 	 */
630 	rc = topo_node_instance(ptn);
631 	if ((hbtn = topo_node_parent(ptn)) != NULL)
632 		hb = topo_node_instance(hbtn);
633 	else
634 		hb = rc;
635 
636 	if ((pdn = topo_node_getspecific(ptn)) == DI_NODE_NIL) {
637 		topo_mod_dprintf(mp,
638 		    "Parent %s node missing private data.\n"
639 		    "Unable to proceed with %s enumeration.\n", pnm, PCIEX_BUS);
640 		return (0);
641 	}
642 	if (did_hash_init(mp) != 0)
643 		return (-1);
644 	if ((did_create(mp, pdn, 0, hb, rc, TRUST_BDF)) == NULL)
645 		return (-1);	/* errno already set */
646 
647 	retval = pci_children_instantiate(mp, ptn, pdn, 0, hb, rc,
648 	    (min == max) ? min : TRUST_BDF, 0);
649 	did_hash_fini(mp);
650 
651 	return (retval);
652 }
653 
654 static int
655 pcibus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
656     topo_instance_t max, void *data)
657 {
658 	did_t *didp, *hbdid = (did_t *)data;
659 	int retval;
660 
661 	/*
662 	 * XXTOPO: we should not be sharing private node data with another
663 	 * module. PCI Bus; Parent node's private data is a did_t.  We'll
664 	 * use the did hash established by the parent.
665 	 */
666 	did_setspecific(mp, data);
667 
668 	/*
669 	 * If we're looking for a specific bus-instance, find the right
670 	 * did_t in the chain, otherwise, there should be only one did_t.
671 	 */
672 	if (min == max) {
673 		int b;
674 		didp = hbdid;
675 		while (didp != NULL) {
676 			did_BDF(didp, &b, NULL, NULL);
677 			if (b == min)
678 				break;
679 			didp = did_link_get(didp);
680 		}
681 		if (didp == NULL) {
682 			topo_mod_dprintf(mp,
683 			    "Parent %s node missing private data related\n"
684 			    "to %s instance %d.\n", pnm, PCI_BUS, min);
685 			topo_mod_setspecific(mp, NULL);
686 			return (0);
687 		}
688 	} else {
689 		assert(did_link_get(hbdid) == NULL);
690 		didp = hbdid;
691 	}
692 	retval = pci_children_instantiate(mp, ptn, did_dinode(didp),
693 	    did_board(didp), did_bridge(didp), did_rc(didp),
694 	    (min == max) ? min : TRUST_BDF, 0);
695 
696 	topo_mod_setspecific(mp, NULL);
697 
698 	return (retval);
699 }
700 
701 /*ARGSUSED*/
702 static int
703 pci_enum(topo_mod_t *mod, tnode_t *ptn, const char *name,
704     topo_instance_t min, topo_instance_t max, void *notused, void *data)
705 {
706 	int retval;
707 	char *pname;
708 
709 	topo_mod_dprintf(mod, "Enumerating pci!\n");
710 
711 	if (strcmp(name, PCI_BUS) != 0 && strcmp(name, PCIEX_BUS) != 0) {
712 		topo_mod_dprintf(mod,
713 		    "Currently only know how to enumerate %s or %s.\n",
714 		    PCI_BUS, PCIEX_BUS);
715 		return (0);
716 	}
717 	pname = topo_node_name(ptn);
718 	if (strcmp(pname, HOSTBRIDGE) != 0 && strcmp(pname, PCIEX_ROOT) != 0) {
719 		topo_mod_dprintf(mod,
720 		    "Currently can only enumerate a %s or %s directly\n",
721 		    PCI_BUS, PCIEX_BUS);
722 		topo_mod_dprintf(mod,
723 		    "descended from a %s or %s node.\n",
724 		    HOSTBRIDGE, PCIEX_ROOT);
725 		return (0);
726 	}
727 
728 	if (strcmp(name, PCI_BUS) == 0) {
729 		retval = pcibus_enum(mod, ptn, pname, min, max, data);
730 	} else if (strcmp(name, PCIEX_BUS) == 0) {
731 		retval = pciexbus_enum(mod, ptn, pname, min, max);
732 	} else {
733 		topo_mod_dprintf(mod,
734 		    "Currently only know how to enumerate %s or %s not %s.\n",
735 		    PCI_BUS, PCIEX_BUS, name);
736 		return (0);
737 	}
738 
739 	return (retval);
740 }
741 
742 /*ARGSUSED*/
743 static void
744 pci_release(topo_mod_t *mp, tnode_t *node)
745 {
746 	topo_method_unregister_all(mp, node);
747 
748 	/*
749 	 * node private data (did_t) for this node is destroyed in
750 	 * did_hash_destroy()
751 	 */
752 
753 	topo_node_unbind(node);
754 }
755