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