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