xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/pcibus/did.c (revision 3f9d6ad73e45c6823b409f93b0c8d4f62861d2d5)
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 		if (di_slotinfo_get(mp, src, &np->dp_nslots,
340 		    &np->dp_slotnames) < 0) {
341 			if (np->dp_devtype != NULL)
342 				topo_mod_strfree(mp, np->dp_devtype);
343 			topo_mod_free(mp, np, sizeof (did_t));
344 			return (NULL);
345 		}
346 	}
347 	did_hash_insert(mp, src, np);
348 	did_hold(np);
349 	return (np);
350 }
351 
352 did_t *
353 did_link_get(did_t *dp)
354 {
355 	assert(dp != NULL);
356 	return (dp->dp_link);
357 }
358 
359 did_t *
360 did_chain_get(did_t *dp)
361 {
362 	assert(dp != NULL);
363 	return (dp->dp_chain);
364 }
365 
366 void
367 did_link_set(topo_mod_t *mod, tnode_t *head, did_t *tail)
368 {
369 	did_t *hd, *pd;
370 
371 	assert(head != NULL);
372 	pd = hd = did_find(mod, topo_node_getspecific(head));
373 	assert(hd != NULL);
374 	while ((hd = did_link_get(hd)) != NULL)
375 		pd = hd;
376 	pd->dp_link = tail;
377 	tail->dp_link = NULL;
378 }
379 
380 void
381 did_did_link_set(did_t *from, did_t *to)
382 {
383 	assert(from != NULL && to != NULL);
384 	from->dp_link = to;
385 }
386 
387 void
388 did_did_chain_set(did_t *from, did_t *to)
389 {
390 	assert(from != NULL && to != NULL);
391 	from->dp_chain = to;
392 }
393 
394 void
395 did_destroy(did_t *dp)
396 {
397 	assert(dp != NULL);
398 
399 	/*
400 	 * did_destroy() is called only from did_hash_destroy() when
401 	 * all references to the did_t have been released.  We can
402 	 * safely destroy the did_t.  If at some later time, more
403 	 * fine-grained reference count control is desired, this
404 	 * code will need to change
405 	 */
406 
407 	if (dp->dp_devtype != NULL)
408 		topo_mod_strfree(dp->dp_mod, dp->dp_devtype);
409 	if (dp->dp_physlot_name != NULL)
410 		topo_mod_strfree(dp->dp_mod, dp->dp_physlot_name);
411 	if (dp->dp_slot_label != NULL)
412 		topo_mod_strfree(dp->dp_mod, dp->dp_slot_label);
413 	slotnm_destroy(dp->dp_slotnames);
414 	topo_mod_free(dp->dp_mod, dp, sizeof (did_t));
415 }
416 
417 void
418 did_hold(did_t *dp)
419 {
420 	assert(dp != NULL);
421 	dp->dp_refcnt++;
422 }
423 
424 void
425 did_rele(did_t *dp)
426 {
427 	assert(dp != NULL);
428 	assert(dp->dp_refcnt > 0);
429 	dp->dp_refcnt--;
430 }
431 
432 di_node_t
433 did_dinode(did_t *dp)
434 {
435 	assert(dp != NULL);
436 	assert(dp->dp_src != NULL);
437 	return (dp->dp_src);
438 }
439 
440 topo_mod_t *
441 did_mod(did_t *dp)
442 {
443 	assert(dp != NULL);
444 	return (dp->dp_mod);
445 }
446 
447 void
448 did_markrc(did_t *dp)
449 {
450 	assert(dp != NULL);
451 	dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT;
452 }
453 
454 void
455 did_BDF(did_t *dp, int *bus, int *dev, int *fn)
456 {
457 	assert(dp != NULL);
458 	if (bus != NULL)
459 		*bus = dp->dp_bus;
460 	if (dev != NULL)
461 		*dev = dp->dp_dev;
462 	if (fn != NULL)
463 		*fn = dp->dp_fn;
464 }
465 
466 int
467 did_board(did_t *did)
468 {
469 	assert(did != NULL);
470 	return (did->dp_board);
471 }
472 
473 int
474 did_bridge(did_t *did)
475 {
476 	assert(did != NULL);
477 	return (did->dp_bridge);
478 }
479 
480 int
481 did_rc(did_t *did)
482 {
483 	assert(did != NULL);
484 	return (did->dp_rc);
485 }
486 
487 int
488 did_excap(did_t *dp)
489 {
490 	assert(dp != NULL);
491 	return ((int)dp->dp_excap);
492 }
493 
494 void
495 did_excap_set(did_t *dp, int type)
496 {
497 	dp->dp_excap = type;
498 }
499 
500 int
501 did_bdf(did_t *dp)
502 {
503 	assert(dp != NULL);
504 	return ((int)dp->dp_bdf);
505 }
506 
507 const char *
508 did_physlot_name(did_t *dp, int dev)
509 {
510 	slotnm_t *slot;
511 
512 	assert(dp != NULL);
513 
514 	/*
515 	 * For pciex, name will be in dp_physlot_name
516 	 */
517 	if (dp->dp_physlot_name != NULL)
518 		return (dp->dp_physlot_name);
519 
520 	/*
521 	 * For pci, name will be in dp_slotnames
522 	 */
523 	for (slot = dp->dp_slotnames; slot != NULL; slot = slot->snm_next)
524 		if (slot->snm_dev == dev)
525 			break;
526 	if (slot != NULL)
527 		return (slot->snm_name);
528 	return (NULL);
529 }
530 
531 char *
532 did_slot_label_get(did_t *did)
533 {
534 	assert(did != NULL);
535 	return (did->dp_slot_label);
536 }
537 
538 void
539 did_slot_label_set(did_t *did, char *l)
540 {
541 	assert(did != NULL);
542 	did->dp_slot_label = l;
543 }
544 
545 did_t *
546 did_find(topo_mod_t *mp, di_node_t dn)
547 {
548 	return (did_hash_lookup(mp, dn));
549 }
550 
551 int
552 pci_BDF_get(topo_mod_t *mp, di_node_t dn, int *bus, int *dev, int *fn)
553 {
554 	did_t *dp;
555 
556 	if ((dp = did_find(mp, dn)) == NULL)
557 		return (-1);
558 	*bus = dp->dp_bus;
559 	*dev = dp->dp_dev;
560 	*fn = dp->dp_fn;
561 	did_rele(dp);
562 	return (0);
563 }
564 
565 int
566 pci_classcode_get(topo_mod_t *mp, di_node_t dn, uint_t *class, uint_t *sub)
567 {
568 	did_t *dp;
569 
570 	if ((dp = did_find(mp, dn)) == NULL)
571 		return (-1);
572 	if (dp->dp_class < 0) {
573 		did_rele(dp);
574 		return (-1);
575 	}
576 	*class = dp->dp_class;
577 	*sub = dp->dp_subclass;
578 	did_rele(dp);
579 	return (0);
580 }
581 
582 char *
583 pci_devtype_get(topo_mod_t *mp, di_node_t dn)
584 {
585 	did_t *dp;
586 
587 	if ((dp = did_find(mp, dn)) == NULL)
588 		return (NULL);
589 	did_rele(dp);
590 	return (dp->dp_devtype);
591 }
592 
593 int
594 pciex_cap_get(topo_mod_t *mp, di_node_t dn)
595 {
596 	did_t *dp;
597 
598 	if ((dp = did_find(mp, dn)) == NULL)
599 		return (-1);
600 	did_rele(dp);
601 	return (dp->dp_excap);
602 }
603 
604 void
605 did_setspecific(topo_mod_t *mp, void *data)
606 {
607 	did_t *hbdid;
608 
609 	hbdid = (did_t *)data;
610 	topo_mod_setspecific(mp, hbdid->dp_hash);
611 }
612 
613 void
614 did_settnode(did_t *pd, tnode_t *tn)
615 {
616 	assert(tn != NULL);
617 	pd->dp_tnode = tn;
618 }
619 
620 tnode_t *
621 did_gettnode(did_t *pd)
622 {
623 	return (pd->dp_tnode);
624 }
625