xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/pcibus/did.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * did.c
29  *	The acronym did means "Dev-Info-Data".  Many properties and
30  *	characteristics of topology nodes are, with a bit of coaxing
31  *	derived from devinfo nodes.  These routines do some of the
32  *	derivation and also encapsulate the discoveries in did_t
33  *	structures that get associated with topology nodes as their
34  *	"private" data.
35  */
36 #include <alloca.h>
37 #include <assert.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <sys/types.h>
41 #include <fm/topo_mod.h>
42 #include <libnvpair.h>
43 #include <libdevinfo.h>
44 #include <sys/pcie.h>
45 
46 #include <hostbridge.h>
47 #include <pcibus.h>
48 #include <did_props.h>
49 
50 #include "did_impl.h"
51 
52 static void slotnm_destroy(slotnm_t *);
53 
54 static slotnm_t *
55 slotnm_create(topo_mod_t *mp, int dev, char *str)
56 {
57 	slotnm_t *p;
58 
59 	if ((p = topo_mod_alloc(mp, sizeof (slotnm_t))) == NULL)
60 		return (NULL);
61 	p->snm_mod = mp;
62 	p->snm_next = NULL;
63 	p->snm_dev = dev;
64 	p->snm_name = topo_mod_strdup(mp, str);
65 	if (p->snm_name == NULL) {
66 		slotnm_destroy(p);
67 		return (NULL);
68 	}
69 	return (p);
70 }
71 
72 static void
73 slotnm_destroy(slotnm_t *p)
74 {
75 	if (p == NULL)
76 		return;
77 	slotnm_destroy(p->snm_next);
78 	if (p->snm_name != NULL)
79 		topo_mod_strfree(p->snm_mod, p->snm_name);
80 	topo_mod_free(p->snm_mod, p, sizeof (slotnm_t));
81 }
82 
83 static int
84 slotnm_cp(did_t *from, did_t *to, int *nslots)
85 {
86 	slotnm_t *nxt, *new;
87 	slotnm_t *last = NULL;
88 
89 	*nslots = 0;
90 	for (nxt = from->dp_slotnames; nxt != NULL; nxt = nxt->snm_next) {
91 		new = slotnm_create(to->dp_mod, nxt->snm_dev, nxt->snm_name);
92 		if (new == NULL) {
93 			if (to->dp_slotnames != NULL)
94 				slotnm_destroy(to->dp_slotnames);
95 			to->dp_slotnames = NULL;
96 			*nslots = 0;
97 			return (-1);
98 		}
99 		if (last == NULL) {
100 			to->dp_slotnames = last = new;
101 		} else {
102 			last->snm_next = new;
103 			last = new;
104 		}
105 		(*nslots)++;
106 	}
107 	if (*nslots > 0)
108 		topo_mod_dprintf(to->dp_mod,
109 		    "%p inherits %d slot label(s) from %p.\n",
110 		    to, *nslots, from);
111 	return (0);
112 }
113 
114 static int
115 di_devtype_get(topo_mod_t *mp, di_node_t src, char **devtype)
116 {
117 	int sz;
118 	uchar_t *buf;
119 
120 	/*
121 	 * For PCI the device type defined the type of device directly below.
122 	 * For PCIe RP and Switches, the device-type should be "pciex".  For
123 	 * PCIe-PCI and PCI-PCI bridges it should be "pci".  NICs = "network",
124 	 * Graphics = "display", etc..
125 	 */
126 	if (di_bytes_get(mp, src, DI_DEVTYPPROP, &sz, &buf) == 0) {
127 		*devtype = topo_mod_strdup(mp, (char *)buf);
128 	} else {
129 		*devtype = NULL;
130 	}
131 
132 	if (*devtype != NULL)
133 		return (0);
134 	return (-1);
135 }
136 
137 static int
138 di_physlotinfo_get(topo_mod_t *mp, di_node_t src, int *slotnum, char **slotname)
139 {
140 	char *slotbuf;
141 	int sz;
142 	uchar_t *buf;
143 
144 	*slotnum = -1;
145 	(void) di_uintprop_get(mp, src, DI_PHYSPROP, (uint_t *)slotnum);
146 
147 	if (*slotnum == -1)
148 		return (0);
149 
150 	/*
151 	 * For PCI-Express, there is only one downstream device, so check for
152 	 * a slot-names property, and if it exists, ignore the slotmask value
153 	 * and use the string as the label.
154 	 */
155 	if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &buf) == 0 &&
156 	    sz > 4) {
157 		slotbuf = (char *)&buf[4];
158 		topo_mod_dprintf(mp, "di_physlotinfo_get: node=%p: "
159 		    "found %s property\n", src, DI_SLOTPROP);
160 	} else {
161 		/*
162 		 * Make generic description string "SLOT <num>", allow up to
163 		 * 10 digits for number
164 		 */
165 		slotbuf = alloca(16);
166 		(void) snprintf(slotbuf, 16, "SLOT %d", *slotnum);
167 		topo_mod_dprintf(mp, "di_physlotinfo_get: node=%p: "
168 		    "using generic slot name\n", src);
169 	}
170 	if ((*slotname = topo_mod_strdup(mp, slotbuf)) == NULL)
171 		return (-1);
172 
173 	topo_mod_dprintf(mp, "di_physlotinfo_get: node=%p: slotname=%s\n",
174 	    src, *slotname);
175 
176 	return (0);
177 }
178 
179 static int
180 di_slotinfo_get(topo_mod_t *mp, di_node_t src, int *nslots,
181     slotnm_t **slotnames)
182 {
183 	slotnm_t *lastslot = NULL;
184 	slotnm_t *newslot;
185 	uchar_t *slotbuf;
186 	uint_t slotmap = 0;
187 	char *slotname;
188 	int andbit;
189 	int sz = -1;
190 
191 	*slotnames = NULL;
192 	*nslots = 0;
193 	if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &slotbuf) < 0)
194 		return (0);
195 	if (sz < sizeof (uint_t))
196 		return (0);
197 	bcopy(slotbuf, &slotmap, sizeof (uint_t));
198 	if (slotmap == 0)
199 		return (0);
200 
201 	slotname = (char *)&slotbuf[4];
202 	for (andbit = 0; andbit < 32; andbit++) {
203 		if (slotmap & (1 << andbit)) {
204 			char *s = slotname;
205 			slotname += strlen(s) + 1;
206 			if ((newslot = slotnm_create(mp, andbit, s)) == NULL) {
207 				slotnm_destroy(*slotnames);
208 				*slotnames = NULL;
209 				*nslots = 0;
210 				return (-1);
211 			}
212 			if (lastslot == NULL)
213 				*slotnames = lastslot = newslot;
214 			else {
215 				lastslot->snm_next = newslot;
216 				lastslot = newslot;
217 			}
218 			(*nslots)++;
219 		}
220 	}
221 	return (0);
222 }
223 
224 int
225 did_physlot(did_t *did)
226 {
227 	assert(did != NULL);
228 	return (did->dp_physlot);
229 }
230 
231 int
232 did_physlot_exists(did_t *did)
233 {
234 	assert(did != NULL);
235 	return ((did->dp_physlot >= 0) || (did->dp_nslots > 0));
236 }
237 
238 did_t *
239 did_create(topo_mod_t *mp, di_node_t src,
240     int ibrd, int ibrdge, int irc, int ibus)
241 {
242 	did_t *np;
243 	did_t *pd;
244 	uint_t code;
245 	uint_t reg;
246 
247 	if ((pd = did_hash_lookup(mp, src)) != NULL) {
248 		topo_mod_dprintf(mp, "Attempt to create existing did_t.\n");
249 		assert(ibus == TRUST_BDF || (pd->dp_bus == ibus));
250 		return (pd);
251 	}
252 
253 	if ((np = topo_mod_zalloc(mp, sizeof (did_t))) == NULL)
254 		return (NULL);
255 	np->dp_mod = mp;
256 	np->dp_src = src;
257 	np->dp_hash = (did_hash_t *)topo_mod_getspecific(mp);
258 	np->dp_tnode = NULL;
259 
260 	/*
261 	 * We must have a reg prop and from it we extract the bus #,
262 	 * device #, and function #.
263 	 */
264 	if (di_uintprop_get(mp, src, DI_REGPROP, &reg) < 0) {
265 		topo_mod_free(mp, np, sizeof (did_t));
266 		return (NULL);
267 	}
268 	np->dp_board = ibrd;
269 	np->dp_bridge = ibrdge;
270 	np->dp_rc = irc;
271 	if (ibus == TRUST_BDF)
272 		np->dp_bus = PCI_REG_BUS_G(reg);
273 	else
274 		np->dp_bus = ibus;
275 	np->dp_dev = PCI_REG_DEV_G(reg);
276 	np->dp_fn = PCI_REG_FUNC_G(reg);
277 	np->dp_bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) |
278 	    PCI_REG_FUNC_G(reg);
279 	/*
280 	 * There *may* be a class code we can capture.  If there wasn't
281 	 * one, capture that fact by setting the class value to -1.
282 	 */
283 	if (di_uintprop_get(mp, src, DI_CCPROP, &code) == 0) {
284 		np->dp_class = GETCLASS(code);
285 		np->dp_subclass = GETSUBCLASS(code);
286 	} else {
287 		np->dp_class = -1;
288 	}
289 	/*
290 	 * There *may* be a device type we can capture.
291 	 */
292 	(void) di_devtype_get(mp, src, &np->dp_devtype);
293 	/*
294 	 * There *may* be a physical slot number property we can capture.
295 	 */
296 	if (di_physlotinfo_get(mp,
297 	    src, &np->dp_physlot, &np->dp_physlot_name) < 0) {
298 		if (np->dp_devtype != NULL)
299 			topo_mod_strfree(mp, np->dp_devtype);
300 		topo_mod_free(mp, np, sizeof (did_t));
301 		return (NULL);
302 	}
303 	/*
304 	 * There *may* be PCI slot info we can capture
305 	 */
306 	if (di_slotinfo_get(mp, src, &np->dp_nslots, &np->dp_slotnames) < 0) {
307 		if (np->dp_devtype != NULL)
308 			topo_mod_strfree(mp, np->dp_devtype);
309 		if (np->dp_physlot_name != NULL)
310 			topo_mod_strfree(mp, np->dp_physlot_name);
311 		topo_mod_free(mp, np, sizeof (did_t));
312 		return (NULL);
313 	}
314 	did_hash_insert(mp, src, np);
315 	did_hold(np);
316 	return (np);
317 }
318 
319 did_t *
320 did_link_get(did_t *dp)
321 {
322 	assert(dp != NULL);
323 	return (dp->dp_link);
324 }
325 
326 did_t *
327 did_chain_get(did_t *dp)
328 {
329 	assert(dp != NULL);
330 	return (dp->dp_chain);
331 }
332 
333 void
334 did_link_set(topo_mod_t *mod, tnode_t *head, did_t *tail)
335 {
336 	did_t *hd, *pd;
337 
338 	assert(head != NULL);
339 	pd = hd = did_find(mod, topo_node_getspecific(head));
340 	assert(hd != NULL);
341 	while ((hd = did_link_get(hd)) != NULL)
342 		pd = hd;
343 	pd->dp_link = tail;
344 	tail->dp_link = NULL;
345 }
346 
347 void
348 did_did_link_set(did_t *from, did_t *to)
349 {
350 	assert(from != NULL && to != NULL);
351 	from->dp_link = to;
352 }
353 
354 void
355 did_did_chain_set(did_t *from, did_t *to)
356 {
357 	assert(from != NULL && to != NULL);
358 	from->dp_chain = to;
359 }
360 
361 void
362 did_destroy(did_t *dp)
363 {
364 	assert(dp != NULL);
365 
366 	/*
367 	 * did_destroy() is called only from did_hash_destroy() when
368 	 * all references to the did_t have been released.  We can
369 	 * safely destroy the did_t.  If at some later time, more
370 	 * fine-grained reference count control is desired, this
371 	 * code will need to change
372 	 */
373 
374 	if (dp->dp_devtype != NULL)
375 		topo_mod_strfree(dp->dp_mod, dp->dp_devtype);
376 	if (dp->dp_physlot_name != NULL)
377 		topo_mod_strfree(dp->dp_mod, dp->dp_physlot_name);
378 	if (dp->dp_slot_label != NULL)
379 		topo_mod_strfree(dp->dp_mod, dp->dp_slot_label);
380 	slotnm_destroy(dp->dp_slotnames);
381 	topo_mod_free(dp->dp_mod, dp, sizeof (did_t));
382 }
383 
384 void
385 did_hold(did_t *dp)
386 {
387 	assert(dp != NULL);
388 	dp->dp_refcnt++;
389 }
390 
391 void
392 did_rele(did_t *dp)
393 {
394 	assert(dp != NULL);
395 	assert(dp->dp_refcnt > 0);
396 	dp->dp_refcnt--;
397 }
398 
399 di_node_t
400 did_dinode(did_t *dp)
401 {
402 	assert(dp != NULL);
403 	assert(dp->dp_src != NULL);
404 	return (dp->dp_src);
405 }
406 
407 topo_mod_t *
408 did_mod(did_t *dp)
409 {
410 	assert(dp != NULL);
411 	return (dp->dp_mod);
412 }
413 
414 void
415 did_markrc(did_t *dp)
416 {
417 	assert(dp != NULL);
418 	dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT;
419 }
420 
421 void
422 did_BDF(did_t *dp, int *bus, int *dev, int *fn)
423 {
424 	assert(dp != NULL);
425 	if (bus != NULL)
426 		*bus = dp->dp_bus;
427 	if (dev != NULL)
428 		*dev = dp->dp_dev;
429 	if (fn != NULL)
430 		*fn = dp->dp_fn;
431 }
432 
433 int
434 did_board(did_t *did)
435 {
436 	assert(did != NULL);
437 	return (did->dp_board);
438 }
439 
440 int
441 did_bridge(did_t *did)
442 {
443 	assert(did != NULL);
444 	return (did->dp_bridge);
445 }
446 
447 int
448 did_rc(did_t *did)
449 {
450 	assert(did != NULL);
451 	return (did->dp_rc);
452 }
453 
454 static int
455 did_numlabels(did_t *dp)
456 {
457 	assert(dp != NULL);
458 	return (dp->dp_nslots);
459 }
460 
461 int
462 did_excap(did_t *dp)
463 {
464 	assert(dp != NULL);
465 	return ((int)dp->dp_excap);
466 }
467 
468 void
469 did_excap_set(did_t *dp, int type)
470 {
471 	dp->dp_excap = type;
472 }
473 
474 int
475 did_bdf(did_t *dp)
476 {
477 	assert(dp != NULL);
478 	return ((int)dp->dp_bdf);
479 }
480 
481 const char *
482 did_physlot_name(did_t *dp, int dev)
483 {
484 	slotnm_t *slot;
485 
486 	assert(dp != NULL);
487 	if (dp->dp_physlot_name != NULL)
488 		return (dp->dp_physlot_name);
489 	for (slot = dp->dp_slotnames; slot != NULL; slot = slot->snm_next)
490 		if (slot->snm_dev == dev)
491 			break;
492 	if (slot != NULL)
493 		return (slot->snm_name);
494 	return (NULL);
495 }
496 
497 char *
498 did_slot_label_get(did_t *did)
499 {
500 	assert(did != NULL);
501 	return (did->dp_slot_label);
502 }
503 
504 void
505 did_slot_label_set(did_t *did, char *l)
506 {
507 	assert(did != NULL);
508 	did->dp_slot_label = l;
509 }
510 
511 did_t *
512 did_find(topo_mod_t *mp, di_node_t dn)
513 {
514 	return (did_hash_lookup(mp, dn));
515 }
516 
517 int
518 pci_BDF_get(topo_mod_t *mp, di_node_t dn, int *bus, int *dev, int *fn)
519 {
520 	did_t *dp;
521 
522 	if ((dp = did_find(mp, dn)) == NULL)
523 		return (-1);
524 	*bus = dp->dp_bus;
525 	*dev = dp->dp_dev;
526 	*fn = dp->dp_fn;
527 	did_rele(dp);
528 	return (0);
529 }
530 
531 int
532 pci_classcode_get(topo_mod_t *mp, di_node_t dn, uint_t *class, uint_t *sub)
533 {
534 	did_t *dp;
535 
536 	if ((dp = did_find(mp, dn)) == NULL)
537 		return (-1);
538 	if (dp->dp_class < 0) {
539 		did_rele(dp);
540 		return (-1);
541 	}
542 	*class = dp->dp_class;
543 	*sub = dp->dp_subclass;
544 	did_rele(dp);
545 	return (0);
546 }
547 
548 char *
549 pci_devtype_get(topo_mod_t *mp, di_node_t dn)
550 {
551 	did_t *dp;
552 
553 	if ((dp = did_find(mp, dn)) == NULL)
554 		return (NULL);
555 	did_rele(dp);
556 	return (dp->dp_devtype);
557 }
558 
559 int
560 pciex_cap_get(topo_mod_t *mp, di_node_t dn)
561 {
562 	did_t *dp;
563 
564 	if ((dp = did_find(mp, dn)) == NULL)
565 		return (-1);
566 	did_rele(dp);
567 	return (dp->dp_excap);
568 }
569 
570 int
571 did_inherit(did_t *pdp, did_t *dp)
572 {
573 	/*
574 	 * If the child already has a label, we're done.
575 	 */
576 	assert(dp != NULL);
577 	if (did_numlabels(dp) > 0)
578 		return (0);
579 
580 	assert(pdp != NULL);
581 
582 	/*
583 	 * If the child and parent are the same, we're done.
584 	 */
585 	if (dp == pdp)
586 		return (0);
587 
588 	if (pdp->dp_physlot_name != NULL) {
589 		topo_mod_dprintf(dp->dp_mod,
590 		    "%p inherits physlot label from %p.\n", dp, pdp);
591 		dp->dp_physlot_name =
592 		    topo_mod_strdup(dp->dp_mod, pdp->dp_physlot_name);
593 		if (dp->dp_physlot_name == NULL)
594 			return (-1);
595 	}
596 	if (slotnm_cp(pdp, dp, &dp->dp_nslots) < 0)
597 		return (-1);
598 	return (0);
599 }
600 
601 void
602 did_setspecific(topo_mod_t *mp, void *data)
603 {
604 	did_t *hbdid;
605 
606 	hbdid = (did_t *)data;
607 	topo_mod_setspecific(mp, hbdid->dp_hash);
608 }
609 
610 void
611 did_settnode(did_t *pd, tnode_t *tn)
612 {
613 	assert(tn != NULL);
614 	pd->dp_tnode = tn;
615 }
616 
617 tnode_t *
618 did_gettnode(did_t *pd)
619 {
620 	return (pd->dp_tnode);
621 }
622