import less(1)
[unleashed/tickless.git] / usr / src / lib / fm / topo / modules / common / pcibus / did.c
blob59ba171c753a51d3ae021d9e793bf1a4fbe8b2ab
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
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.
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>
45 #include <hostbridge.h>
46 #include <pcibus.h>
47 #include <did_props.h>
49 #include "did_impl.h"
51 static void slotnm_destroy(slotnm_t *);
53 static slotnm_t *
54 slotnm_create(topo_mod_t *mp, int dev, char *str)
56 slotnm_t *p;
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);
68 return (p);
71 static void
72 slotnm_destroy(slotnm_t *p)
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));
82 static int
83 di_devtype_get(topo_mod_t *mp, di_node_t src, char **devtype)
85 int sz;
86 uchar_t *buf;
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..
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;
100 if (*devtype != NULL)
101 return (0);
102 return (-1);
105 typedef struct smbios_slot_cb {
106 int cb_slotnum;
107 const char *cb_label;
108 } smbios_slot_cb_t;
110 static int
111 di_smbios_find_slot(smbios_hdl_t *shp, const smbios_struct_t *strp, void *data)
113 smbios_slot_cb_t *cbp = data;
114 smbios_slot_t slot;
116 if (strp->smbstr_type != SMB_TYPE_SLOT ||
117 smbios_info_slot(shp, strp->smbstr_id, &slot) != 0)
118 return (0);
120 if (slot.smbl_id == cbp->cb_slotnum) {
121 cbp->cb_label = slot.smbl_name;
122 return (1);
125 return (0);
128 static int
129 di_physlotinfo_get(topo_mod_t *mp, di_node_t src, int *slotnum, char **slotname)
131 char *slotbuf;
132 int sz;
133 uchar_t *buf;
134 smbios_hdl_t *shp;
135 boolean_t got_slotprop = B_FALSE;
137 *slotnum = -1;
139 (void) di_uintprop_get(mp, src, DI_PHYSPROP, (uint_t *)slotnum);
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.
146 if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &buf) == 0 &&
147 sz > 4) {
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).
153 got_slotprop = B_TRUE;
154 (void) sscanf((char *)&buf[4], "Slot%d", slotnum);
157 if (*slotnum == -1)
158 return (0);
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
166 if ((shp = topo_mod_smbios(mp)) != NULL) {
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.
177 smbios_slot_cb_t cbdata;
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 {
192 * Make generic description string "SLOT <num>", allow up to
193 * 10 digits for number
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);
200 if ((*slotname = topo_mod_strdup(mp, slotbuf)) == NULL)
201 return (-1);
203 topo_mod_dprintf(mp, "%s: node=%p: slotname=%s\n",
204 __func__, src, *slotname);
206 return (0);
209 static int
210 di_slotinfo_get(topo_mod_t *mp, di_node_t src, int *nslots,
211 slotnm_t **slotnames)
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;
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);
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);
242 if (lastslot == NULL)
243 *slotnames = lastslot = newslot;
244 else {
245 lastslot->snm_next = newslot;
246 lastslot = newslot;
248 (*nslots)++;
251 return (0);
255 did_physlot(did_t *did)
257 assert(did != NULL);
258 return (did->dp_physlot);
262 did_physlot_exists(did_t *did)
264 assert(did != NULL);
265 return ((did->dp_physlot >= 0) || (did->dp_nslots > 0));
268 did_t *
269 did_create(topo_mod_t *mp, di_node_t src,
270 int ibrd, int ibrdge, int irc, int ibus)
272 did_t *np;
273 did_t *pd;
274 uint_t code;
275 uint_t reg;
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);
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;
291 * We must have a reg prop and from it we extract the bus #,
292 * device #, and function #.
294 if (di_uintprop_get(mp, src, DI_REGPROP, &reg) < 0) {
295 topo_mod_free(mp, np, sizeof (did_t));
296 return (NULL);
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);
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.
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;
320 * There *may* be a device type we can capture.
322 (void) di_devtype_get(mp, src, &np->dp_devtype);
324 if (irc >= 0) {
326 * This is a pciex node.
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);
335 } else {
337 * This is a pci node.
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);
348 did_hash_insert(mp, src, np);
349 did_hold(np);
350 return (np);
353 did_t *
354 did_link_get(did_t *dp)
356 assert(dp != NULL);
357 return (dp->dp_link);
360 did_t *
361 did_chain_get(did_t *dp)
363 assert(dp != NULL);
364 return (dp->dp_chain);
367 void
368 did_link_set(topo_mod_t *mod, tnode_t *head, did_t *tail)
370 did_t *hd, *pd;
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;
381 void
382 did_did_link_set(did_t *from, did_t *to)
384 assert(from != NULL && to != NULL);
385 from->dp_link = to;
388 void
389 did_did_chain_set(did_t *from, did_t *to)
391 assert(from != NULL && to != NULL);
392 from->dp_chain = to;
395 void
396 did_destroy(did_t *dp)
398 assert(dp != NULL);
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
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));
418 void
419 did_hold(did_t *dp)
421 assert(dp != NULL);
422 dp->dp_refcnt++;
425 void
426 did_rele(did_t *dp)
428 assert(dp != NULL);
429 assert(dp->dp_refcnt > 0);
430 dp->dp_refcnt--;
433 di_node_t
434 did_dinode(did_t *dp)
436 assert(dp != NULL);
437 assert(dp->dp_src != NULL);
438 return (dp->dp_src);
441 topo_mod_t *
442 did_mod(did_t *dp)
444 assert(dp != NULL);
445 return (dp->dp_mod);
448 void
449 did_markrc(did_t *dp)
451 assert(dp != NULL);
452 dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT;
455 void
456 did_BDF(did_t *dp, int *bus, int *dev, int *fn)
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;
468 did_board(did_t *did)
470 assert(did != NULL);
471 return (did->dp_board);
475 did_bridge(did_t *did)
477 assert(did != NULL);
478 return (did->dp_bridge);
482 did_rc(did_t *did)
484 assert(did != NULL);
485 return (did->dp_rc);
489 did_excap(did_t *dp)
491 assert(dp != NULL);
492 return ((int)dp->dp_excap);
495 void
496 did_excap_set(did_t *dp, int type)
498 dp->dp_excap = type;
502 did_bdf(did_t *dp)
504 assert(dp != NULL);
505 return ((int)dp->dp_bdf);
508 const char *
509 did_physlot_name(did_t *dp, int dev)
511 slotnm_t *slot;
513 assert(dp != NULL);
516 * For pciex, name will be in dp_physlot_name
518 if (dp->dp_physlot_name != NULL)
519 return (dp->dp_physlot_name);
522 * For pci, name will be in dp_slotnames
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);
532 char *
533 did_slot_label_get(did_t *did)
535 assert(did != NULL);
536 return (did->dp_slot_label);
539 void
540 did_slot_label_set(did_t *did, char *l)
542 assert(did != NULL);
543 did->dp_slot_label = l;
546 did_t *
547 did_find(topo_mod_t *mp, di_node_t dn)
549 return (did_hash_lookup(mp, dn));
553 pci_BDF_get(topo_mod_t *mp, di_node_t dn, int *bus, int *dev, int *fn)
555 did_t *dp;
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);
567 pci_classcode_get(topo_mod_t *mp, di_node_t dn, uint_t *class, uint_t *sub)
569 did_t *dp;
571 if ((dp = did_find(mp, dn)) == NULL)
572 return (-1);
573 if (dp->dp_class < 0) {
574 did_rele(dp);
575 return (-1);
577 *class = dp->dp_class;
578 *sub = dp->dp_subclass;
579 did_rele(dp);
580 return (0);
583 char *
584 pci_devtype_get(topo_mod_t *mp, di_node_t dn)
586 did_t *dp;
588 if ((dp = did_find(mp, dn)) == NULL)
589 return (NULL);
590 did_rele(dp);
591 return (dp->dp_devtype);
595 pciex_cap_get(topo_mod_t *mp, di_node_t dn)
597 did_t *dp;
599 if ((dp = did_find(mp, dn)) == NULL)
600 return (-1);
601 did_rele(dp);
602 return (dp->dp_excap);
605 void
606 did_setspecific(topo_mod_t *mp, void *data)
608 did_t *hbdid;
610 hbdid = (did_t *)data;
611 topo_mod_setspecific(mp, hbdid->dp_hash);
614 void
615 did_settnode(did_t *pd, tnode_t *tn)
617 assert(tn != NULL);
618 pd->dp_tnode = tn;
621 tnode_t *
622 did_gettnode(did_t *pd)
624 return (pd->dp_tnode);