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