xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c (revision 4e93fb0f6383eaac21897dcdae56b87118131e4d)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/fm/protocol.h>
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <alloca.h>
36 #include <sys/param.h>
37 #include <sys/pci.h>
38 #include <sys/pcie.h>
39 #include <libdevinfo.h>
40 #include <libnvpair.h>
41 #include <fm/topo_mod.h>
42 #include <fm/topo_hc.h>
43 
44 #include <hostbridge.h>
45 #include <pcibus.h>
46 #include <did.h>
47 #include <did_props.h>
48 #include <util.h>
49 
50 extern txprop_t Bus_common_props[];
51 extern txprop_t Dev_common_props[];
52 extern txprop_t Fn_common_props[];
53 extern int Bus_propcnt;
54 extern int Dev_propcnt;
55 extern int Fn_propcnt;
56 
57 extern int platform_pci_label(topo_mod_t *mod, tnode_t *, nvlist_t *,
58     nvlist_t **);
59 
60 static void pci_release(topo_mod_t *, tnode_t *);
61 static int pci_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
62     topo_instance_t, void *, void *);
63 static int pci_label(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 	{ NULL }
75 };
76 
77 int
78 _topo_init(topo_mod_t *modhdl, topo_version_t version)
79 {
80 	/*
81 	 * Turn on module debugging output
82 	 */
83 	if (getenv("TOPOPCIDBG") != NULL)
84 		topo_mod_setdebug(modhdl);
85 	topo_mod_dprintf(modhdl, "initializing pcibus builtin\n");
86 
87 	if (version != PCI_ENUMR_VERS)
88 		return (topo_mod_seterrno(modhdl, EMOD_VER_NEW));
89 
90 	topo_mod_register(modhdl, &Pci_info, TOPO_VERSION);
91 	topo_mod_dprintf(modhdl, "PCI Enumr initd\n");
92 
93 	return (0);
94 }
95 
96 void
97 _topo_fini(topo_mod_t *modhdl)
98 {
99 	topo_mod_unregister(modhdl);
100 }
101 
102 static int
103 pci_label(topo_mod_t *mp, tnode_t *node, topo_version_t version,
104     nvlist_t *in, nvlist_t **out)
105 {
106 	if (version > TOPO_METH_LABEL_VERSION)
107 		return (topo_mod_seterrno(mp, EMOD_VER_NEW));
108 	return (platform_pci_label(mp, node, in, out));
109 }
110 
111 static tnode_t *
112 pci_tnode_create(topo_mod_t *mod, tnode_t *parent,
113     const char *name, topo_instance_t i, void *priv)
114 {
115 	tnode_t *ntn;
116 
117 	if ((ntn = tnode_create(mod, parent, name, i, priv)) == NULL)
118 		return (NULL);
119 	if (topo_method_register(mod, ntn, Pci_methods) < 0) {
120 		topo_mod_dprintf(mod, "topo_method_register failed: %s\n",
121 		    topo_strerror(topo_mod_errno(mod)));
122 		topo_node_unbind(ntn);
123 		return (NULL);
124 	}
125 	return (ntn);
126 }
127 
128 /*ARGSUSED*/
129 static int
130 hostbridge_asdevice(topo_mod_t *mod, tnode_t *bus)
131 {
132 	di_node_t di;
133 	tnode_t *dev32;
134 
135 	di = topo_node_getspecific(bus);
136 	assert(di != DI_NODE_NIL);
137 
138 	if ((dev32 = pcidev_declare(mod, bus, di, 32)) == NULL)
139 		return (-1);
140 	if (pcifn_declare(mod, dev32, di, 0) == NULL)
141 		return (-1);
142 	return (0);
143 }
144 
145 tnode_t *
146 pciexfn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
147     topo_instance_t i)
148 {
149 	did_t *pd;
150 	tnode_t *ntn;
151 
152 	if ((pd = did_find(mod, dn)) == NULL)
153 		return (NULL);
154 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_FUNCTION, i, dn))
155 	    == NULL)
156 		return (NULL);
157 	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
158 		topo_node_unbind(ntn);
159 		return (NULL);
160 	}
161 	/*
162 	 * We may find pci-express buses or plain-pci buses beneath a function
163 	 */
164 	if (child_range_add(mod, ntn, PCIEX_BUS, 0, MAX_HB_BUSES) < 0) {
165 		topo_node_range_destroy(ntn, PCIEX_BUS);
166 		return (NULL);
167 	}
168 	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
169 		topo_node_range_destroy(ntn, PCI_BUS);
170 		return (NULL);
171 	}
172 	return (ntn);
173 }
174 
175 tnode_t *
176 pciexdev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
177     topo_instance_t i)
178 {
179 	did_t *pd;
180 	tnode_t *ntn;
181 
182 	if ((pd = did_find(mod, dn)) == NULL)
183 		return (NULL);
184 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_DEVICE, i, dn)) == NULL)
185 		return (NULL);
186 	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
187 		topo_node_unbind(ntn);
188 		return (NULL);
189 	}
190 
191 	/*
192 	 * We can expect to find pci-express functions beneath the device
193 	 */
194 	if (child_range_add(mod,
195 	    ntn, PCIEX_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
196 		topo_node_range_destroy(ntn, PCIEX_FUNCTION);
197 		return (NULL);
198 	}
199 	return (ntn);
200 }
201 
202 tnode_t *
203 pciexbus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
204     topo_instance_t i)
205 {
206 	did_t *pd;
207 	tnode_t *ntn;
208 
209 	if ((pd = did_find(mod, dn)) == NULL)
210 		return (NULL);
211 	if ((ntn = pci_tnode_create(mod, parent, PCIEX_BUS, i, dn)) == NULL)
212 		return (NULL);
213 	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
214 		topo_node_range_destroy(ntn, PCI_DEVICE);
215 		topo_node_unbind(ntn);
216 		return (NULL);
217 	}
218 	/*
219 	 * We can expect to find pci-express devices beneath the bus
220 	 */
221 	if (child_range_add(mod,
222 	    ntn, PCIEX_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
223 		topo_node_range_destroy(ntn, PCIEX_DEVICE);
224 		return (NULL);
225 	}
226 	return (ntn);
227 }
228 
229 tnode_t *
230 pcifn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
231     topo_instance_t i)
232 {
233 	did_t *pd;
234 	tnode_t *ntn;
235 
236 	if ((pd = did_find(mod, dn)) == NULL)
237 		return (NULL);
238 	if ((ntn = pci_tnode_create(mod, parent, PCI_FUNCTION, i, dn)) == NULL)
239 		return (NULL);
240 	if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) {
241 		topo_node_unbind(ntn);
242 		return (NULL);
243 	}
244 	/*
245 	 * We may find pci buses beneath a function
246 	 */
247 	if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
248 		topo_node_unbind(ntn);
249 		return (NULL);
250 	}
251 	return (ntn);
252 }
253 
254 tnode_t *
255 pcidev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
256     topo_instance_t i)
257 {
258 	did_t *pd;
259 	did_t *ppd;
260 	di_node_t pdn;
261 	tnode_t *ntn;
262 
263 	if ((pdn = topo_node_getspecific(parent)) == DI_NODE_NIL)
264 		return (NULL);
265 	if ((ppd = did_find(mod, pdn)) == NULL)
266 		return (NULL);
267 	if ((pd = did_find(mod, dn)) == NULL)
268 		return (NULL);
269 	if ((ntn = pci_tnode_create(mod, parent, PCI_DEVICE, i, dn)) == NULL)
270 		return (NULL);
271 	/*
272 	 * If our devinfo node is lacking certain information of its
273 	 * own, we may need/want to inherit the information available
274 	 * from our parent node's private data.
275 	 */
276 	did_inherit(ppd, pd);
277 	if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) {
278 		topo_node_unbind(ntn);
279 		return (NULL);
280 	}
281 
282 	/*
283 	 * We can expect to find pci functions beneath the device
284 	 */
285 	if (child_range_add(mod, ntn, PCI_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) {
286 		topo_node_range_destroy(ntn, PCI_FUNCTION);
287 		topo_node_unbind(ntn);
288 		return (NULL);
289 	}
290 	return (ntn);
291 }
292 
293 tnode_t *
294 pcibus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn,
295     topo_instance_t i)
296 {
297 	did_t *pd;
298 	tnode_t *ntn;
299 	int hbchild = 0;
300 
301 	if ((pd = did_find(mod, dn)) == NULL)
302 		return (NULL);
303 	if ((ntn = pci_tnode_create(mod, parent, PCI_BUS, i, dn)) == NULL)
304 		return (NULL);
305 	/*
306 	 * If our devinfo node is lacking certain information of its
307 	 * own, and our parent topology node is a hostbridge, we may
308 	 * need/want to inherit information available in the
309 	 * hostbridge node's private data.
310 	 */
311 	if (strcmp(topo_node_name(parent), HOSTBRIDGE) == 0)
312 		hbchild = 1;
313 	if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) {
314 		topo_node_unbind(ntn);
315 		return (NULL);
316 	}
317 	/*
318 	 * We can expect to find pci devices beneath the bus
319 	 */
320 	if (child_range_add(mod, ntn, PCI_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) {
321 		topo_node_unbind(ntn);
322 		return (NULL);
323 	}
324 	/*
325 	 * On each bus child of the hostbridge, we represent the
326 	 * hostbridge as a device outside the range of legal device
327 	 * numbers.
328 	 */
329 	if (hbchild == 1) {
330 		if (hostbridge_asdevice(mod, ntn) < 0) {
331 			topo_node_range_destroy(ntn, PCI_DEVICE);
332 			topo_node_unbind(ntn);
333 			return (NULL);
334 		}
335 	}
336 	return (ntn);
337 }
338 
339 static int
340 pci_bridge_declare(topo_mod_t *mod, tnode_t *fn, di_node_t din, int board,
341     int bridge, int rc, int depth)
342 {
343 	int err, excap, extyp;
344 
345 	excap = pciex_cap_get(mod, din);
346 	extyp = excap & PCIE_PCIECAP_DEV_TYPE_MASK;
347 	if (excap <= 0 ||
348 	    extyp != PCIE_PCIECAP_DEV_TYPE_PCIE2PCI)
349 		err = pci_children_instantiate(mod, fn, din, board, bridge,
350 		    rc, TRUST_BDF, depth + 1);
351 	else
352 		err = pci_children_instantiate(mod, fn, din, board, bridge,
353 		    rc - TO_PCI, TRUST_BDF, depth + 1);
354 	return (err);
355 }
356 
357 static int
358 declare_dev_and_fn(topo_mod_t *mod, tnode_t *bus, tnode_t **dev, di_node_t din,
359     int board, int bridge, int rc, int devno, int fnno, int depth)
360 {
361 	int err = 0;
362 	tnode_t *fn;
363 	uint_t class, subclass;
364 
365 	if (*dev == NULL) {
366 		if (rc >= 0)
367 			*dev = pciexdev_declare(mod, bus, din, devno);
368 		else
369 			*dev = pcidev_declare(mod, bus, din, devno);
370 		if (*dev == NULL)
371 			return (-1);
372 	}
373 	if (rc >= 0)
374 		fn = pciexfn_declare(mod, *dev, din, fnno);
375 	else
376 		fn = pcifn_declare(mod, *dev, din, fnno);
377 	if (fn == NULL)
378 		return (-1);
379 	if (pci_classcode_get(mod, din, &class, &subclass) < 0)
380 		return (-1);
381 
382 	/*
383 	 * This function may be a bridge.  If not, check for a possible
384 	 * topology map file and kick off its enumeration of lower-level
385 	 * devices.
386 	 */
387 	if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI)
388 		err = pci_bridge_declare(mod, fn, din, board, bridge, rc,
389 		    depth);
390 	else if (class == PCI_CLASS_MASS)
391 		(void) topo_mod_enummap(mod, fn, "storage", FM_FMRI_SCHEME_HC);
392 
393 	return (err);
394 }
395 
396 int
397 pci_children_instantiate(topo_mod_t *mod, tnode_t *parent, di_node_t pn,
398     int board, int bridge, int rc, int bover, int depth)
399 {
400 	did_t *pps[MAX_PCIBUS_DEVS][MAX_PCIDEV_FNS];
401 	did_t *bp = NULL;
402 	did_t *np;
403 	di_node_t sib;
404 	di_node_t din;
405 	tnode_t *bn = NULL;
406 	tnode_t *dn = NULL;
407 	int pb = -1;
408 	int b, d, f;
409 
410 	for (d = 0; d < MAX_PCIBUS_DEVS; d++)
411 		for (f = 0; f < MAX_PCIDEV_FNS; f++)
412 			pps[d][f] = NULL;
413 
414 	/* start at the parent's first sibling */
415 	sib = di_child_node(pn);
416 	while (sib != DI_NODE_NIL) {
417 		np = did_create(mod, sib, board, bridge, rc, bover);
418 		if (np == NULL)
419 			return (-1);
420 		did_BDF(np, &b, &d, &f);
421 		pps[d][f] = np;
422 		if (bp == NULL)
423 			bp = np;
424 		if (pb < 0)
425 			pb = ((bover == TRUST_BDF) ? b : bover);
426 		sib = di_sibling_node(sib);
427 	}
428 	if (pb < 0 && bover < 0)
429 		return (0);
430 	if (rc >= 0)
431 		bn = pciexbus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
432 	else
433 		bn = pcibus_declare(mod, parent, pn, ((pb < 0) ? bover : pb));
434 	if (bn == NULL)
435 		return (-1);
436 	if (pb < 0)
437 		return (0);
438 
439 	for (d = 0; d < MAX_PCIBUS_DEVS; d++) {
440 		for (f = 0; f < MAX_PCIDEV_FNS; f++) {
441 			if (pps[d][f] == NULL)
442 				continue;
443 			din = did_dinode(pps[d][f]);
444 			/*
445 			 * Ignore error and try to enumerate as much as
446 			 * possible.  If we ever need to check for an
447 			 * error all declared buses, devices and functions
448 			 * need to be cleaned up
449 			 */
450 			(void) declare_dev_and_fn(mod, bn,
451 			    &dn, din, board, bridge, rc, d, f, depth);
452 			did_rele(pps[d][f]);
453 		}
454 		dn = NULL;
455 	}
456 	return (0);
457 }
458 
459 static int
460 pciexbus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
461     topo_instance_t max)
462 {
463 	di_node_t pdn;
464 	int rc;
465 	int retval;
466 
467 	/*
468 	 * PCI-Express; root complex shares the hostbridge's instance
469 	 * number.  Parent node's private data is a simple di_node_t
470 	 * and we have to construct our own did hash and did_t.
471 	 */
472 	rc = topo_node_instance(ptn);
473 
474 	if ((pdn = topo_node_getspecific(ptn)) == DI_NODE_NIL) {
475 		topo_mod_dprintf(mp,
476 		    "Parent %s node missing private data.\n"
477 		    "Unable to proceed with %s enumeration.\n", pnm, PCIEX_BUS);
478 		return (0);
479 	}
480 	did_hash_init(mp);
481 	if (did_create(mp, pdn, 0, 0, rc, TRUST_BDF) == NULL)
482 		return (-1);	/* errno already set */
483 	retval = pci_children_instantiate(mp, ptn, pdn, 0, 0, rc,
484 	    (min == max) ? min : TRUST_BDF, 0);
485 	did_hash_fini(mp);
486 
487 	return (retval);
488 }
489 
490 static int
491 pcibus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min,
492     topo_instance_t max, void *data)
493 {
494 	did_t *didp, *hbdid = (did_t *)data;
495 	int retval;
496 
497 	/*
498 	 * XXTOPO: we should not be sharing private node data with another
499 	 * module. PCI Bus; Parent node's private data is a did_t.  We'll
500 	 * use the did hash established by the parent.
501 	 */
502 	did_setspecific(mp, data);
503 
504 	/*
505 	 * If we're looking for a specific bus-instance, find the right
506 	 * did_t in the chain, otherwise, there should be only one did_t.
507 	 */
508 	if (min == max) {
509 		int b;
510 		didp = hbdid;
511 		while (didp != NULL) {
512 			did_BDF(didp, &b, NULL, NULL);
513 			if (b == min)
514 				break;
515 			didp = did_link_get(didp);
516 		}
517 		if (didp == NULL) {
518 			topo_mod_dprintf(mp,
519 			    "Parent %s node missing private data related\n"
520 			    "to %s instance %d.\n", pnm, PCI_BUS, min);
521 			topo_mod_setspecific(mp, NULL);
522 			return (0);
523 		}
524 	} else {
525 		assert(did_link_get(hbdid) == NULL);
526 		didp = hbdid;
527 	}
528 	retval = pci_children_instantiate(mp, ptn, did_dinode(didp),
529 	    did_board(didp), did_bridge(didp), did_rc(didp),
530 	    (min == max) ? min : TRUST_BDF, 0);
531 
532 	topo_mod_setspecific(mp, NULL);
533 
534 	return (retval);
535 }
536 
537 /*ARGSUSED*/
538 static int
539 pci_enum(topo_mod_t *mod, tnode_t *ptn, const char *name,
540     topo_instance_t min, topo_instance_t max, void *notused, void *data)
541 {
542 	int retval;
543 	char *pname;
544 
545 	topo_mod_dprintf(mod, "Enumerating pci!\n");
546 
547 	if (strcmp(name, PCI_BUS) != 0 && strcmp(name, PCIEX_BUS) != 0) {
548 		topo_mod_dprintf(mod,
549 		    "Currently only know how to enumerate %s or %s.\n",
550 		    PCI_BUS, PCIEX_BUS);
551 		return (0);
552 	}
553 	pname = topo_node_name(ptn);
554 	if (strcmp(pname, HOSTBRIDGE) != 0 && strcmp(pname, PCIEX_ROOT) != 0) {
555 		topo_mod_dprintf(mod,
556 		    "Currently can only enumerate a %s or %s directly\n",
557 		    PCI_BUS, PCIEX_BUS);
558 		topo_mod_dprintf(mod,
559 		    "descended from a %s or %s node.\n",
560 		    HOSTBRIDGE, PCIEX_ROOT);
561 		return (0);
562 	}
563 
564 	if (strcmp(name, PCI_BUS) == 0) {
565 		retval = pcibus_enum(mod, ptn, pname, min, max, data);
566 	} else if (strcmp(name, PCIEX_BUS) == 0) {
567 		retval = pciexbus_enum(mod, ptn, pname, min, max);
568 	} else {
569 		topo_mod_dprintf(mod,
570 		    "Currently only know how to enumerate %s or %s not %s.\n",
571 		    PCI_BUS, PCIEX_BUS, name);
572 		return (0);
573 	}
574 
575 	return (retval);
576 }
577 
578 /*ARGSUSED*/
579 static void
580 pci_release(topo_mod_t *mp, tnode_t *node)
581 {
582 	topo_method_unregister_all(mp, node);
583 
584 	/*
585 	 * node private data (did_t) for this node is destroyed in
586 	 * did_hash_destroy()
587 	 */
588 
589 	topo_node_unbind(node);
590 }
591