xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/pcibus/did.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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_label = topo_mod_strdup(mp, str);
65 	if (p->snm_label == 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_label != NULL)
79 		topo_mod_strfree(p->snm_mod, p->snm_label);
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_label);
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 **slotnm)
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 	if (*slotnum == -1)
147 		return (0);
148 
149 	/*
150 	 * For PCI-Express, there is only one downstream device, so check for
151 	 * a slot-names property, and if it exists, ignore the slotmask value
152 	 * and use the string as the label.
153 	 */
154 	if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &buf) == 0 &&
155 	    sz > 4) {
156 		slotbuf = (char *)&buf[4];
157 	} else {
158 		/*
159 		 * Make generic description string "SLOT <num>", allow up to
160 		 * 10 digits for number
161 		 */
162 		slotbuf = alloca(16);
163 		(void) snprintf(slotbuf, 16, "SLOT %d", *slotnum);
164 	}
165 	if ((*slotnm = topo_mod_strdup(mp, slotbuf)) == NULL)
166 		return (-1);
167 
168 	return (0);
169 }
170 
171 static int
172 di_slotinfo_get(topo_mod_t *mp, di_node_t src, int *nslots, slotnm_t **slots)
173 {
174 	slotnm_t *lastslot = NULL;
175 	slotnm_t *newslot;
176 	uchar_t *slotbuf;
177 	uint_t slotmap = 0;
178 	char *slotname;
179 	int andbit;
180 	int sz = -1;
181 
182 	*slots = NULL;
183 	*nslots = 0;
184 	if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &slotbuf) < 0)
185 		return (0);
186 	if (sz < sizeof (uint_t))
187 		return (0);
188 	bcopy(slotbuf, &slotmap, sizeof (uint_t));
189 	if (slotmap == 0)
190 		return (0);
191 
192 	slotname = (char *)&slotbuf[4];
193 	for (andbit = 0; andbit < 32; andbit++) {
194 		if (slotmap & (1 << andbit)) {
195 			char *s = slotname;
196 			slotname += strlen(s) + 1;
197 			if ((newslot = slotnm_create(mp, andbit, s)) == NULL) {
198 				slotnm_destroy(*slots);
199 				*slots = NULL;
200 				*nslots = 0;
201 				return (-1);
202 			}
203 			if (lastslot == NULL)
204 				*slots = lastslot = newslot;
205 			else {
206 				lastslot->snm_next = newslot;
207 				lastslot = newslot;
208 			}
209 			(*nslots)++;
210 		}
211 	}
212 	return (0);
213 }
214 
215 int
216 did_physslot(did_t *did)
217 {
218 	assert(did != NULL);
219 	return (did->dp_physlot);
220 }
221 
222 did_t *
223 did_create(topo_mod_t *mp, di_node_t src,
224     int ibrd, int ibrdge, int irc, int ibus)
225 {
226 	did_t *np;
227 	did_t *pd;
228 	uint_t code;
229 	uint_t reg;
230 
231 	if ((pd = did_hash_lookup(mp, src)) != NULL) {
232 		topo_mod_dprintf(mp, "Attempt to create existing did_t.\n");
233 		assert(ibus == TRUST_BDF || (pd->dp_bus == ibus));
234 		return (pd);
235 	}
236 
237 	if ((np = topo_mod_zalloc(mp, sizeof (did_t))) == NULL)
238 		return (NULL);
239 	np->dp_mod = mp;
240 	np->dp_src = src;
241 	np->dp_hash = (did_hash_t *)topo_mod_getspecific(mp);
242 	np->dp_tnode = NULL;
243 
244 	/*
245 	 * We must have a reg prop and from it we extract the bus #,
246 	 * device #, and function #.
247 	 */
248 	if (di_uintprop_get(mp, src, DI_REGPROP, &reg) < 0) {
249 		topo_mod_free(mp, np, sizeof (did_t));
250 		return (NULL);
251 	}
252 	np->dp_board = ibrd;
253 	np->dp_bridge = ibrdge;
254 	np->dp_rc = irc;
255 	if (ibus == TRUST_BDF)
256 		np->dp_bus = PCI_REG_BUS_G(reg);
257 	else
258 		np->dp_bus = ibus;
259 	np->dp_dev = PCI_REG_DEV_G(reg);
260 	np->dp_fn = PCI_REG_FUNC_G(reg);
261 	np->dp_bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) |
262 	    PCI_REG_FUNC_G(reg);
263 	/*
264 	 * There *may* be a class code we can capture.  If there wasn't
265 	 * one, capture that fact by setting the class value to -1.
266 	 */
267 	if (di_uintprop_get(mp, src, DI_CCPROP, &code) == 0) {
268 		np->dp_class = GETCLASS(code);
269 		np->dp_subclass = GETSUBCLASS(code);
270 	} else {
271 		np->dp_class = -1;
272 	}
273 	/*
274 	 * There *may* be a device type we can capture.
275 	 */
276 	(void) di_devtype_get(mp, src, &np->dp_devtype);
277 	/*
278 	 * There *may* be a physical slot number property we can capture.
279 	 */
280 	if (di_physlotinfo_get(mp,
281 	    src, &np->dp_physlot, &np->dp_physlot_label) < 0) {
282 		if (np->dp_devtype != NULL)
283 			topo_mod_strfree(mp, np->dp_devtype);
284 		topo_mod_free(mp, np, sizeof (did_t));
285 		return (NULL);
286 	}
287 	/*
288 	 * There *may* be PCI slot info we can capture
289 	 */
290 	if (di_slotinfo_get(mp, src, &np->dp_nslots, &np->dp_slotnames) < 0) {
291 		if (np->dp_devtype != NULL)
292 			topo_mod_strfree(mp, np->dp_devtype);
293 		if (np->dp_physlot_label != NULL)
294 			topo_mod_strfree(mp, np->dp_physlot_label);
295 		topo_mod_free(mp, np, sizeof (did_t));
296 		return (NULL);
297 	}
298 	did_hash_insert(mp, src, np);
299 	did_hold(np);
300 	return (np);
301 }
302 
303 did_t *
304 did_link_get(did_t *dp)
305 {
306 	assert(dp != NULL);
307 	return (dp->dp_link);
308 }
309 
310 did_t *
311 did_chain_get(did_t *dp)
312 {
313 	assert(dp != NULL);
314 	return (dp->dp_chain);
315 }
316 
317 void
318 did_link_set(topo_mod_t *mod, tnode_t *head, did_t *tail)
319 {
320 	did_t *hd, *pd;
321 
322 	assert(head != NULL);
323 	pd = hd = did_find(mod, topo_node_getspecific(head));
324 	assert(hd != NULL);
325 	while ((hd = did_link_get(hd)) != NULL)
326 		pd = hd;
327 	pd->dp_link = tail;
328 	tail->dp_link = NULL;
329 }
330 
331 void
332 did_did_link_set(did_t *from, did_t *to)
333 {
334 	assert(from != NULL && to != NULL);
335 	from->dp_link = to;
336 }
337 
338 void
339 did_did_chain_set(did_t *from, did_t *to)
340 {
341 	assert(from != NULL && to != NULL);
342 	from->dp_chain = to;
343 }
344 
345 void
346 did_destroy(did_t *dp)
347 {
348 	assert(dp != NULL);
349 
350 	/*
351 	 * did_destroy() is called only from did_hash_destroy() when
352 	 * all references to the did_t have been released.  We can
353 	 * safely destroy the did_t.  If at some later time, more
354 	 * fine-grained reference count control is desired, this
355 	 * code will need to change
356 	 */
357 
358 	if (dp->dp_devtype != NULL)
359 		topo_mod_strfree(dp->dp_mod, dp->dp_devtype);
360 	if (dp->dp_physlot_label != NULL)
361 		topo_mod_strfree(dp->dp_mod, dp->dp_physlot_label);
362 	slotnm_destroy(dp->dp_slotnames);
363 	topo_mod_free(dp->dp_mod, dp, sizeof (did_t));
364 }
365 
366 void
367 did_hold(did_t *dp)
368 {
369 	assert(dp != NULL);
370 	dp->dp_refcnt++;
371 }
372 
373 void
374 did_rele(did_t *dp)
375 {
376 	assert(dp != NULL);
377 	assert(dp->dp_refcnt > 0);
378 	dp->dp_refcnt--;
379 }
380 
381 di_node_t
382 did_dinode(did_t *dp)
383 {
384 	assert(dp != NULL);
385 	assert(dp->dp_src != NULL);
386 	return (dp->dp_src);
387 }
388 
389 topo_mod_t *
390 did_mod(did_t *dp)
391 {
392 	assert(dp != NULL);
393 	return (dp->dp_mod);
394 }
395 
396 void
397 did_markrc(did_t *dp)
398 {
399 	assert(dp != NULL);
400 	dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT;
401 }
402 
403 void
404 did_BDF(did_t *dp, int *bus, int *dev, int *fn)
405 {
406 	assert(dp != NULL);
407 	if (bus != NULL)
408 		*bus = dp->dp_bus;
409 	if (dev != NULL)
410 		*dev = dp->dp_dev;
411 	if (fn != NULL)
412 		*fn = dp->dp_fn;
413 }
414 
415 int
416 did_board(did_t *did)
417 {
418 	assert(did != NULL);
419 	return (did->dp_board);
420 }
421 
422 int
423 did_bridge(did_t *did)
424 {
425 	assert(did != NULL);
426 	return (did->dp_bridge);
427 }
428 
429 int
430 did_rc(did_t *did)
431 {
432 	assert(did != NULL);
433 	return (did->dp_rc);
434 }
435 
436 static int
437 did_numlabels(did_t *dp)
438 {
439 	assert(dp != NULL);
440 	return (dp->dp_nslots);
441 }
442 
443 int
444 did_excap(did_t *dp)
445 {
446 	assert(dp != NULL);
447 	return ((int)dp->dp_excap);
448 }
449 
450 void
451 did_excap_set(did_t *dp, int type)
452 {
453 	dp->dp_excap = type;
454 }
455 
456 int
457 did_bdf(did_t *dp)
458 {
459 	assert(dp != NULL);
460 	return ((int)dp->dp_bdf);
461 }
462 
463 const char *
464 did_label(did_t *dp, int dev)
465 {
466 	slotnm_t *slot;
467 
468 	assert(dp != NULL);
469 	if (dp->dp_physlot_label != NULL)
470 		return (dp->dp_physlot_label);
471 	for (slot = dp->dp_slotnames; slot != NULL; slot = slot->snm_next)
472 		if (slot->snm_dev == dev)
473 			break;
474 	if (slot != NULL)
475 		return (slot->snm_label);
476 	return (NULL);
477 }
478 
479 did_t *
480 did_find(topo_mod_t *mp, di_node_t dn)
481 {
482 	return (did_hash_lookup(mp, dn));
483 }
484 
485 int
486 pci_BDF_get(topo_mod_t *mp, di_node_t dn, int *bus, int *dev, int *fn)
487 {
488 	did_t *dp;
489 
490 	if ((dp = did_find(mp, dn)) == NULL)
491 		return (-1);
492 	*bus = dp->dp_bus;
493 	*dev = dp->dp_dev;
494 	*fn = dp->dp_fn;
495 	did_rele(dp);
496 	return (0);
497 }
498 
499 int
500 pci_classcode_get(topo_mod_t *mp, di_node_t dn, uint_t *class, uint_t *sub)
501 {
502 	did_t *dp;
503 
504 	if ((dp = did_find(mp, dn)) == NULL)
505 		return (-1);
506 	if (dp->dp_class < 0) {
507 		did_rele(dp);
508 		return (-1);
509 	}
510 	*class = dp->dp_class;
511 	*sub = dp->dp_subclass;
512 	did_rele(dp);
513 	return (0);
514 }
515 
516 char *
517 pci_devtype_get(topo_mod_t *mp, di_node_t dn)
518 {
519 	did_t *dp;
520 
521 	if ((dp = did_find(mp, dn)) == NULL)
522 		return (NULL);
523 	did_rele(dp);
524 	return (dp->dp_devtype);
525 }
526 
527 int
528 pciex_cap_get(topo_mod_t *mp, di_node_t dn)
529 {
530 	did_t *dp;
531 
532 	if ((dp = did_find(mp, dn)) == NULL)
533 		return (-1);
534 	did_rele(dp);
535 	return (dp->dp_excap);
536 }
537 
538 int
539 did_inherit(did_t *pdp, did_t *dp)
540 {
541 	/*
542 	 * If the child already has a label, we're done.
543 	 */
544 	assert(dp != NULL);
545 	if (did_numlabels(dp) > 0)
546 		return (0);
547 
548 	assert(pdp != NULL);
549 
550 	/*
551 	 * If the child and parent are the same, we're done.
552 	 */
553 	if (dp == pdp)
554 		return (0);
555 
556 	if (pdp->dp_physlot_label != NULL) {
557 		topo_mod_dprintf(dp->dp_mod,
558 		    "%p inherits physlot label from %p.\n", dp, pdp);
559 		dp->dp_physlot_label =
560 		    topo_mod_strdup(dp->dp_mod, pdp->dp_physlot_label);
561 		if (dp->dp_physlot_label == NULL)
562 			return (-1);
563 	}
564 	if (slotnm_cp(pdp, dp, &dp->dp_nslots) < 0)
565 		return (-1);
566 	return (0);
567 }
568 
569 void
570 did_setspecific(topo_mod_t *mp, void *data)
571 {
572 	did_t *hbdid;
573 
574 	hbdid = (did_t *)data;
575 	topo_mod_setspecific(mp, hbdid->dp_hash);
576 }
577 
578 void
579 did_settnode(did_t *pd, tnode_t *tn)
580 {
581 	assert(tn != NULL);
582 	pd->dp_tnode = tn;
583 }
584 
585 tnode_t *
586 did_gettnode(did_t *pd)
587 {
588 	return (pd->dp_tnode);
589 }
590