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