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