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_name = topo_mod_strdup(mp, str); 65 if (p->snm_name == 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_name != NULL) 79 topo_mod_strfree(p->snm_mod, p->snm_name); 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_name); 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 **slotname) 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 147 if (*slotnum == -1) 148 return (0); 149 150 /* 151 * For PCI-Express, there is only one downstream device, so check for 152 * a slot-names property, and if it exists, ignore the slotmask value 153 * and use the string as the label. 154 */ 155 if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &buf) == 0 && 156 sz > 4) { 157 slotbuf = (char *)&buf[4]; 158 topo_mod_dprintf(mp, "di_physlotinfo_get: node=%p: " 159 "found %s property\n", src, DI_SLOTPROP); 160 } else { 161 /* 162 * Make generic description string "SLOT <num>", allow up to 163 * 10 digits for number 164 */ 165 slotbuf = alloca(16); 166 (void) snprintf(slotbuf, 16, "SLOT %d", *slotnum); 167 topo_mod_dprintf(mp, "di_physlotinfo_get: node=%p: " 168 "using generic slot name\n", src); 169 } 170 if ((*slotname = topo_mod_strdup(mp, slotbuf)) == NULL) 171 return (-1); 172 173 topo_mod_dprintf(mp, "di_physlotinfo_get: node=%p: slotname=%s\n", 174 src, *slotname); 175 176 return (0); 177 } 178 179 static int 180 di_slotinfo_get(topo_mod_t *mp, di_node_t src, int *nslots, 181 slotnm_t **slotnames) 182 { 183 slotnm_t *lastslot = NULL; 184 slotnm_t *newslot; 185 uchar_t *slotbuf; 186 uint_t slotmap = 0; 187 char *slotname; 188 int andbit; 189 int sz = -1; 190 191 *slotnames = NULL; 192 *nslots = 0; 193 if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &slotbuf) < 0) 194 return (0); 195 if (sz < sizeof (uint_t)) 196 return (0); 197 bcopy(slotbuf, &slotmap, sizeof (uint_t)); 198 if (slotmap == 0) 199 return (0); 200 201 slotname = (char *)&slotbuf[4]; 202 for (andbit = 0; andbit < 32; andbit++) { 203 if (slotmap & (1 << andbit)) { 204 char *s = slotname; 205 slotname += strlen(s) + 1; 206 if ((newslot = slotnm_create(mp, andbit, s)) == NULL) { 207 slotnm_destroy(*slotnames); 208 *slotnames = NULL; 209 *nslots = 0; 210 return (-1); 211 } 212 if (lastslot == NULL) 213 *slotnames = lastslot = newslot; 214 else { 215 lastslot->snm_next = newslot; 216 lastslot = newslot; 217 } 218 (*nslots)++; 219 } 220 } 221 return (0); 222 } 223 224 int 225 did_physlot(did_t *did) 226 { 227 assert(did != NULL); 228 return (did->dp_physlot); 229 } 230 231 int 232 did_physlot_exists(did_t *did) 233 { 234 assert(did != NULL); 235 return ((did->dp_physlot >= 0) || (did->dp_nslots > 0)); 236 } 237 238 did_t * 239 did_create(topo_mod_t *mp, di_node_t src, 240 int ibrd, int ibrdge, int irc, int ibus) 241 { 242 did_t *np; 243 did_t *pd; 244 uint_t code; 245 uint_t reg; 246 247 if ((pd = did_hash_lookup(mp, src)) != NULL) { 248 topo_mod_dprintf(mp, "Attempt to create existing did_t.\n"); 249 assert(ibus == TRUST_BDF || (pd->dp_bus == ibus)); 250 return (pd); 251 } 252 253 if ((np = topo_mod_zalloc(mp, sizeof (did_t))) == NULL) 254 return (NULL); 255 np->dp_mod = mp; 256 np->dp_src = src; 257 np->dp_hash = (did_hash_t *)topo_mod_getspecific(mp); 258 np->dp_tnode = NULL; 259 260 /* 261 * We must have a reg prop and from it we extract the bus #, 262 * device #, and function #. 263 */ 264 if (di_uintprop_get(mp, src, DI_REGPROP, ®) < 0) { 265 topo_mod_free(mp, np, sizeof (did_t)); 266 return (NULL); 267 } 268 np->dp_board = ibrd; 269 np->dp_bridge = ibrdge; 270 np->dp_rc = irc; 271 if (ibus == TRUST_BDF) 272 np->dp_bus = PCI_REG_BUS_G(reg); 273 else 274 np->dp_bus = ibus; 275 np->dp_dev = PCI_REG_DEV_G(reg); 276 np->dp_fn = PCI_REG_FUNC_G(reg); 277 np->dp_bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) | 278 PCI_REG_FUNC_G(reg); 279 /* 280 * There *may* be a class code we can capture. If there wasn't 281 * one, capture that fact by setting the class value to -1. 282 */ 283 if (di_uintprop_get(mp, src, DI_CCPROP, &code) == 0) { 284 np->dp_class = GETCLASS(code); 285 np->dp_subclass = GETSUBCLASS(code); 286 } else { 287 np->dp_class = -1; 288 } 289 /* 290 * There *may* be a device type we can capture. 291 */ 292 (void) di_devtype_get(mp, src, &np->dp_devtype); 293 /* 294 * There *may* be a physical slot number property we can capture. 295 */ 296 if (di_physlotinfo_get(mp, 297 src, &np->dp_physlot, &np->dp_physlot_name) < 0) { 298 if (np->dp_devtype != NULL) 299 topo_mod_strfree(mp, np->dp_devtype); 300 topo_mod_free(mp, np, sizeof (did_t)); 301 return (NULL); 302 } 303 /* 304 * There *may* be PCI slot info we can capture 305 */ 306 if (di_slotinfo_get(mp, src, &np->dp_nslots, &np->dp_slotnames) < 0) { 307 if (np->dp_devtype != NULL) 308 topo_mod_strfree(mp, np->dp_devtype); 309 if (np->dp_physlot_name != NULL) 310 topo_mod_strfree(mp, np->dp_physlot_name); 311 topo_mod_free(mp, np, sizeof (did_t)); 312 return (NULL); 313 } 314 did_hash_insert(mp, src, np); 315 did_hold(np); 316 return (np); 317 } 318 319 did_t * 320 did_link_get(did_t *dp) 321 { 322 assert(dp != NULL); 323 return (dp->dp_link); 324 } 325 326 did_t * 327 did_chain_get(did_t *dp) 328 { 329 assert(dp != NULL); 330 return (dp->dp_chain); 331 } 332 333 void 334 did_link_set(topo_mod_t *mod, tnode_t *head, did_t *tail) 335 { 336 did_t *hd, *pd; 337 338 assert(head != NULL); 339 pd = hd = did_find(mod, topo_node_getspecific(head)); 340 assert(hd != NULL); 341 while ((hd = did_link_get(hd)) != NULL) 342 pd = hd; 343 pd->dp_link = tail; 344 tail->dp_link = NULL; 345 } 346 347 void 348 did_did_link_set(did_t *from, did_t *to) 349 { 350 assert(from != NULL && to != NULL); 351 from->dp_link = to; 352 } 353 354 void 355 did_did_chain_set(did_t *from, did_t *to) 356 { 357 assert(from != NULL && to != NULL); 358 from->dp_chain = to; 359 } 360 361 void 362 did_destroy(did_t *dp) 363 { 364 assert(dp != NULL); 365 366 /* 367 * did_destroy() is called only from did_hash_destroy() when 368 * all references to the did_t have been released. We can 369 * safely destroy the did_t. If at some later time, more 370 * fine-grained reference count control is desired, this 371 * code will need to change 372 */ 373 374 if (dp->dp_devtype != NULL) 375 topo_mod_strfree(dp->dp_mod, dp->dp_devtype); 376 if (dp->dp_physlot_name != NULL) 377 topo_mod_strfree(dp->dp_mod, dp->dp_physlot_name); 378 if (dp->dp_slot_label != NULL) 379 topo_mod_strfree(dp->dp_mod, dp->dp_slot_label); 380 slotnm_destroy(dp->dp_slotnames); 381 topo_mod_free(dp->dp_mod, dp, sizeof (did_t)); 382 } 383 384 void 385 did_hold(did_t *dp) 386 { 387 assert(dp != NULL); 388 dp->dp_refcnt++; 389 } 390 391 void 392 did_rele(did_t *dp) 393 { 394 assert(dp != NULL); 395 assert(dp->dp_refcnt > 0); 396 dp->dp_refcnt--; 397 } 398 399 di_node_t 400 did_dinode(did_t *dp) 401 { 402 assert(dp != NULL); 403 assert(dp->dp_src != NULL); 404 return (dp->dp_src); 405 } 406 407 topo_mod_t * 408 did_mod(did_t *dp) 409 { 410 assert(dp != NULL); 411 return (dp->dp_mod); 412 } 413 414 void 415 did_markrc(did_t *dp) 416 { 417 assert(dp != NULL); 418 dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT; 419 } 420 421 void 422 did_BDF(did_t *dp, int *bus, int *dev, int *fn) 423 { 424 assert(dp != NULL); 425 if (bus != NULL) 426 *bus = dp->dp_bus; 427 if (dev != NULL) 428 *dev = dp->dp_dev; 429 if (fn != NULL) 430 *fn = dp->dp_fn; 431 } 432 433 int 434 did_board(did_t *did) 435 { 436 assert(did != NULL); 437 return (did->dp_board); 438 } 439 440 int 441 did_bridge(did_t *did) 442 { 443 assert(did != NULL); 444 return (did->dp_bridge); 445 } 446 447 int 448 did_rc(did_t *did) 449 { 450 assert(did != NULL); 451 return (did->dp_rc); 452 } 453 454 static int 455 did_numlabels(did_t *dp) 456 { 457 assert(dp != NULL); 458 return (dp->dp_nslots); 459 } 460 461 int 462 did_excap(did_t *dp) 463 { 464 assert(dp != NULL); 465 return ((int)dp->dp_excap); 466 } 467 468 void 469 did_excap_set(did_t *dp, int type) 470 { 471 dp->dp_excap = type; 472 } 473 474 int 475 did_bdf(did_t *dp) 476 { 477 assert(dp != NULL); 478 return ((int)dp->dp_bdf); 479 } 480 481 const char * 482 did_physlot_name(did_t *dp, int dev) 483 { 484 slotnm_t *slot; 485 486 assert(dp != NULL); 487 if (dp->dp_physlot_name != NULL) 488 return (dp->dp_physlot_name); 489 for (slot = dp->dp_slotnames; slot != NULL; slot = slot->snm_next) 490 if (slot->snm_dev == dev) 491 break; 492 if (slot != NULL) 493 return (slot->snm_name); 494 return (NULL); 495 } 496 497 char * 498 did_slot_label_get(did_t *did) 499 { 500 assert(did != NULL); 501 return (did->dp_slot_label); 502 } 503 504 void 505 did_slot_label_set(did_t *did, char *l) 506 { 507 assert(did != NULL); 508 did->dp_slot_label = l; 509 } 510 511 did_t * 512 did_find(topo_mod_t *mp, di_node_t dn) 513 { 514 return (did_hash_lookup(mp, dn)); 515 } 516 517 int 518 pci_BDF_get(topo_mod_t *mp, di_node_t dn, int *bus, int *dev, int *fn) 519 { 520 did_t *dp; 521 522 if ((dp = did_find(mp, dn)) == NULL) 523 return (-1); 524 *bus = dp->dp_bus; 525 *dev = dp->dp_dev; 526 *fn = dp->dp_fn; 527 did_rele(dp); 528 return (0); 529 } 530 531 int 532 pci_classcode_get(topo_mod_t *mp, di_node_t dn, uint_t *class, uint_t *sub) 533 { 534 did_t *dp; 535 536 if ((dp = did_find(mp, dn)) == NULL) 537 return (-1); 538 if (dp->dp_class < 0) { 539 did_rele(dp); 540 return (-1); 541 } 542 *class = dp->dp_class; 543 *sub = dp->dp_subclass; 544 did_rele(dp); 545 return (0); 546 } 547 548 char * 549 pci_devtype_get(topo_mod_t *mp, di_node_t dn) 550 { 551 did_t *dp; 552 553 if ((dp = did_find(mp, dn)) == NULL) 554 return (NULL); 555 did_rele(dp); 556 return (dp->dp_devtype); 557 } 558 559 int 560 pciex_cap_get(topo_mod_t *mp, di_node_t dn) 561 { 562 did_t *dp; 563 564 if ((dp = did_find(mp, dn)) == NULL) 565 return (-1); 566 did_rele(dp); 567 return (dp->dp_excap); 568 } 569 570 int 571 did_inherit(did_t *pdp, did_t *dp) 572 { 573 /* 574 * If the child already has a label, we're done. 575 */ 576 assert(dp != NULL); 577 if (did_numlabels(dp) > 0) 578 return (0); 579 580 assert(pdp != NULL); 581 582 /* 583 * If the child and parent are the same, we're done. 584 */ 585 if (dp == pdp) 586 return (0); 587 588 if (pdp->dp_physlot_name != NULL) { 589 topo_mod_dprintf(dp->dp_mod, 590 "%p inherits physlot label from %p.\n", dp, pdp); 591 dp->dp_physlot_name = 592 topo_mod_strdup(dp->dp_mod, pdp->dp_physlot_name); 593 if (dp->dp_physlot_name == NULL) 594 return (-1); 595 } 596 if (slotnm_cp(pdp, dp, &dp->dp_nslots) < 0) 597 return (-1); 598 return (0); 599 } 600 601 void 602 did_setspecific(topo_mod_t *mp, void *data) 603 { 604 did_t *hbdid; 605 606 hbdid = (did_t *)data; 607 topo_mod_setspecific(mp, hbdid->dp_hash); 608 } 609 610 void 611 did_settnode(did_t *pd, tnode_t *tn) 612 { 613 assert(tn != NULL); 614 pd->dp_tnode = tn; 615 } 616 617 tnode_t * 618 did_gettnode(did_t *pd) 619 { 620 return (pd->dp_tnode); 621 } 622