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 *
slotnm_create(topo_mod_t * mp,int dev,char * str)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
slotnm_destroy(slotnm_t * p)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
di_devtype_get(topo_mod_t * mp,di_node_t src,char ** devtype)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
di_smbios_find_slot(smbios_hdl_t * shp,const smbios_struct_t * strp,void * data)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
di_physlotinfo_get(topo_mod_t * mp,di_node_t src,int * slotnum,char ** slotname)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
di_slotinfo_get(topo_mod_t * mp,di_node_t src,int * nslots,slotnm_t ** slotnames)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
did_physlot(did_t * did)255 did_physlot(did_t *did)
256 {
257 assert(did != NULL);
258 return (did->dp_physlot);
259 }
260
261 int
did_physlot_exists(did_t * did)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 *
did_create(topo_mod_t * mp,di_node_t src,int ibrd,int ibrdge,int irc,int ibus)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, ®) < 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 *
did_link_get(did_t * dp)354 did_link_get(did_t *dp)
355 {
356 assert(dp != NULL);
357 return (dp->dp_link);
358 }
359
360 did_t *
did_chain_get(did_t * dp)361 did_chain_get(did_t *dp)
362 {
363 assert(dp != NULL);
364 return (dp->dp_chain);
365 }
366
367 void
did_link_set(topo_mod_t * mod,tnode_t * head,did_t * tail)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
did_did_link_set(did_t * from,did_t * to)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
did_did_chain_set(did_t * from,did_t * to)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
did_destroy(did_t * dp)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
did_hold(did_t * dp)419 did_hold(did_t *dp)
420 {
421 assert(dp != NULL);
422 dp->dp_refcnt++;
423 }
424
425 void
did_rele(did_t * dp)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
did_dinode(did_t * dp)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 *
did_mod(did_t * dp)442 did_mod(did_t *dp)
443 {
444 assert(dp != NULL);
445 return (dp->dp_mod);
446 }
447
448 void
did_markrc(did_t * dp)449 did_markrc(did_t *dp)
450 {
451 assert(dp != NULL);
452 dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT;
453 }
454
455 void
did_BDF(did_t * dp,int * bus,int * dev,int * fn)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
did_board(did_t * did)468 did_board(did_t *did)
469 {
470 assert(did != NULL);
471 return (did->dp_board);
472 }
473
474 int
did_bridge(did_t * did)475 did_bridge(did_t *did)
476 {
477 assert(did != NULL);
478 return (did->dp_bridge);
479 }
480
481 int
did_rc(did_t * did)482 did_rc(did_t *did)
483 {
484 assert(did != NULL);
485 return (did->dp_rc);
486 }
487
488 int
did_excap(did_t * dp)489 did_excap(did_t *dp)
490 {
491 assert(dp != NULL);
492 return ((int)dp->dp_excap);
493 }
494
495 void
did_excap_set(did_t * dp,int type)496 did_excap_set(did_t *dp, int type)
497 {
498 dp->dp_excap = type;
499 }
500
501 int
did_bdf(did_t * dp)502 did_bdf(did_t *dp)
503 {
504 assert(dp != NULL);
505 return ((int)dp->dp_bdf);
506 }
507
508 const char *
did_physlot_name(did_t * dp,int dev)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 *
did_slot_label_get(did_t * did)533 did_slot_label_get(did_t *did)
534 {
535 assert(did != NULL);
536 return (did->dp_slot_label);
537 }
538
539 void
did_slot_label_set(did_t * did,char * l)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 *
did_find(topo_mod_t * mp,di_node_t dn)547 did_find(topo_mod_t *mp, di_node_t dn)
548 {
549 return (did_hash_lookup(mp, dn));
550 }
551
552 int
pci_BDF_get(topo_mod_t * mp,di_node_t dn,int * bus,int * dev,int * fn)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
pci_classcode_get(topo_mod_t * mp,di_node_t dn,uint_t * class,uint_t * sub)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 *
pci_devtype_get(topo_mod_t * mp,di_node_t dn)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
pciex_cap_get(topo_mod_t * mp,di_node_t dn)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
did_setspecific(topo_mod_t * mp,void * data)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
did_settnode(did_t * pd,tnode_t * tn)615 did_settnode(did_t *pd, tnode_t *tn)
616 {
617 assert(tn != NULL);
618 pd->dp_tnode = tn;
619 }
620
621 tnode_t *
did_gettnode(did_t * pd)622 did_gettnode(did_t *pd)
623 {
624 return (pd->dp_tnode);
625 }
626