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 * Copyright 2019 Joyent, Inc. 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 di_devtype_get(topo_mod_t *mp, di_node_t src, char **devtype) 85 { 86 int sz; 87 uchar_t *buf; 88 89 /* 90 * For PCI the device type defined the type of device directly below. 91 * For PCIe RP and Switches, the device-type should be "pciex". For 92 * PCIe-PCI and PCI-PCI bridges it should be "pci". NICs = "network", 93 * Graphics = "display", etc.. 94 */ 95 if (di_bytes_get(mp, src, DI_DEVTYPPROP, &sz, &buf) == 0) { 96 *devtype = topo_mod_strdup(mp, (char *)buf); 97 } else { 98 *devtype = NULL; 99 } 100 101 if (*devtype != NULL) 102 return (0); 103 return (-1); 104 } 105 106 typedef struct smbios_slot_cb { 107 int cb_slotnum; 108 int cb_bdf; 109 const char *cb_label; 110 } smbios_slot_cb_t; 111 112 static int 113 di_smbios_find_slot_by_bdf(smbios_hdl_t *shp, const smbios_struct_t *strp, 114 void *data) 115 { 116 smbios_slot_cb_t *cbp = data; 117 smbios_slot_t slot; 118 int bus, df; 119 120 bus = (cbp->cb_bdf & 0xFF00) >> 8; 121 df = cbp->cb_bdf & 0xFF; 122 123 if (strp->smbstr_type != SMB_TYPE_SLOT || 124 smbios_info_slot(shp, strp->smbstr_id, &slot) != 0) 125 return (0); 126 127 if (slot.smbl_bus == bus && slot.smbl_df == df) { 128 cbp->cb_label = slot.smbl_name; 129 cbp->cb_slotnum = slot.smbl_id; 130 return (1); 131 } 132 133 return (0); 134 } 135 136 static int 137 di_smbios_find_slot_by_id(smbios_hdl_t *shp, const smbios_struct_t *strp, 138 void *data) 139 { 140 smbios_slot_cb_t *cbp = data; 141 smbios_slot_t slot; 142 143 if (strp->smbstr_type != SMB_TYPE_SLOT || 144 smbios_info_slot(shp, strp->smbstr_id, &slot) != 0) 145 return (0); 146 147 if (slot.smbl_id == cbp->cb_slotnum) { 148 cbp->cb_label = slot.smbl_name; 149 return (1); 150 } 151 152 return (0); 153 } 154 155 static int 156 di_physlotinfo_get(topo_mod_t *mp, di_node_t src, int bdf, int *slotnum, 157 char **slotname) 158 { 159 char *slotbuf = NULL; 160 int sz; 161 uchar_t *buf; 162 smbios_hdl_t *shp; 163 boolean_t got_slotprop = B_FALSE; 164 165 *slotnum = -1; 166 167 (void) di_uintprop_get(mp, src, DI_PHYSPROP, (uint_t *)slotnum); 168 169 /* 170 * For PCI-Express, there is only one downstream device, so check for 171 * a slot-names property, and if it exists, ignore the slotmask value 172 * and use the string as the label. 173 */ 174 if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &buf) == 0 && 175 sz > 4) { 176 /* 177 * If there is a DI_SLOTPROP of the form SlotX (ie set up from 178 * the IRQ routing table) then trust that in preference to 179 * DI_PHYSPROP (which is set up from the PCIe slotcap reg). 180 */ 181 got_slotprop = B_TRUE; 182 (void) sscanf((char *)&buf[4], "Slot%d", slotnum); 183 } 184 185 /* 186 * If the system supports SMBIOS (virtual certainty on X86) then we will 187 * source the label from the Type 9 (Slot) records. If we're unable 188 * to correlate the device with a slot record (as would happen with 189 * onboard PCIe devices), we return without setting slotname, which will 190 * ultimately result in the node inheriting the FRU label from its 191 * parent node. 192 * 193 * In the absence of any SMBIOS support (i.e. SPARC) then we will use 194 * the slot-names property, if available. Otherwise we'll fall back 195 * to fabricating a label based on the slot number. 196 */ 197 if ((shp = topo_mod_smbios(mp)) != NULL) { 198 /* 199 * The PCI spec describes slot number 0 as reserved for 200 * internal PCI devices. Unfortunately, not all platforms 201 * respect this. For that reason, we prefer to lookup the slot 202 * record using the device's BDF. However, SMBIOS 203 * implementations prior to 2.6 don't encode the BDF in the 204 * slot record. In that case we resort to looking up the 205 * slot record using the slot number. 206 */ 207 smbios_slot_cb_t cbdata; 208 smbios_version_t smbv; 209 boolean_t bdf_supp = B_TRUE; 210 211 cbdata.cb_slotnum = *slotnum; 212 cbdata.cb_bdf = bdf; 213 cbdata.cb_label = NULL; 214 215 /* 216 * The bus and device/fn payload members of the SMBIOS slot 217 * record were added in SMBIOS 2.6. 218 */ 219 smbios_info_smbios_version(shp, &smbv); 220 if (smbv.smbv_major < 2 || 221 (smbv.smbv_major == 2 && smbv.smbv_minor < 6)) { 222 bdf_supp = B_FALSE; 223 } 224 225 /* 226 * If the SMBIOS implementation is too old to look up the slot 227 * records by BDF and we weren't able to derive a slotnum then 228 * there is nothing we can do here. 229 */ 230 if (!bdf_supp && *slotnum == -1) 231 return (0); 232 233 if (bdf_supp) 234 (void) smbios_iter(shp, di_smbios_find_slot_by_bdf, 235 &cbdata); 236 else 237 (void) smbios_iter(shp, di_smbios_find_slot_by_id, 238 &cbdata); 239 240 if (cbdata.cb_label == NULL) 241 return (0); 242 243 slotbuf = (char *)cbdata.cb_label; 244 topo_mod_dprintf(mp, "%s: di_node=%p: using smbios name: %s\n", 245 __func__, src, slotbuf); 246 } else if (got_slotprop) { 247 slotbuf = (char *)&buf[4]; 248 topo_mod_dprintf(mp, "%s: di_node=%p: using %s property: %s\n", 249 __func__, src, DI_SLOTPROP, slotbuf); 250 } else { 251 /* 252 * Make generic description string "SLOT <num>", allow up to 253 * 10 digits for number. Bail, if we weren't able to derive 254 * a slotnum. 255 */ 256 if (*slotnum == -1) 257 return (0); 258 259 slotbuf = alloca(16); 260 (void) snprintf(slotbuf, 16, "SLOT %d", *slotnum); 261 topo_mod_dprintf(mp, "%s: di_node=%p: using fabricated slot " 262 "name: %s\n", __func__, src, slotbuf); 263 } 264 265 if ((*slotname = topo_mod_strdup(mp, slotbuf)) == NULL) { 266 /* topo errno set */ 267 return (-1); 268 } 269 return (0); 270 } 271 272 static int 273 di_slotinfo_get(topo_mod_t *mp, di_node_t src, int *nslots, 274 slotnm_t **slotnames) 275 { 276 slotnm_t *lastslot = NULL; 277 slotnm_t *newslot; 278 uchar_t *slotbuf; 279 uint_t slotmap = 0; 280 char *slotname; 281 int andbit; 282 int sz = -1; 283 284 *slotnames = NULL; 285 *nslots = 0; 286 if (di_bytes_get(mp, src, DI_SLOTPROP, &sz, &slotbuf) < 0) 287 return (0); 288 if (sz < sizeof (uint_t)) 289 return (0); 290 bcopy(slotbuf, &slotmap, sizeof (uint_t)); 291 if (slotmap == 0) 292 return (0); 293 294 slotname = (char *)&slotbuf[4]; 295 for (andbit = 0; andbit < 32; andbit++) { 296 if (slotmap & (1 << andbit)) { 297 char *s = slotname; 298 slotname += strlen(s) + 1; 299 if ((newslot = slotnm_create(mp, andbit, s)) == NULL) { 300 slotnm_destroy(*slotnames); 301 *slotnames = NULL; 302 *nslots = 0; 303 return (-1); 304 } 305 if (lastslot == NULL) 306 *slotnames = lastslot = newslot; 307 else { 308 lastslot->snm_next = newslot; 309 lastslot = newslot; 310 } 311 (*nslots)++; 312 } 313 } 314 return (0); 315 } 316 317 int 318 did_physlot(did_t *did) 319 { 320 assert(did != NULL); 321 return (did->dp_physlot); 322 } 323 324 int 325 did_physlot_exists(did_t *did) 326 { 327 assert(did != NULL); 328 return ((did->dp_physlot >= 0) || (did->dp_nslots > 0)); 329 } 330 331 did_t * 332 did_create(topo_mod_t *mp, di_node_t src, 333 int ibrd, int ibrdge, int irc, int ibus) 334 { 335 did_t *np; 336 did_t *pd; 337 uint_t code; 338 uint_t reg; 339 340 if ((pd = did_hash_lookup(mp, src)) != NULL) { 341 topo_mod_dprintf(mp, "Attempt to create existing did_t.\n"); 342 assert(ibus == TRUST_BDF || (pd->dp_bus == ibus)); 343 return (pd); 344 } 345 346 if ((np = topo_mod_zalloc(mp, sizeof (did_t))) == NULL) 347 return (NULL); 348 np->dp_mod = mp; 349 np->dp_src = src; 350 np->dp_hash = (did_hash_t *)topo_mod_getspecific(mp); 351 np->dp_tnode = NULL; 352 353 /* 354 * We must have a reg prop and from it we extract the bus #, 355 * device #, and function #. 356 */ 357 if (di_uintprop_get(mp, src, DI_REGPROP, ®) < 0) { 358 topo_mod_free(mp, np, sizeof (did_t)); 359 return (NULL); 360 } 361 np->dp_board = ibrd; 362 np->dp_bridge = ibrdge; 363 np->dp_rc = irc; 364 if (ibus == TRUST_BDF) 365 np->dp_bus = PCI_REG_BUS_G(reg); 366 else 367 np->dp_bus = ibus; 368 np->dp_dev = PCI_REG_DEV_G(reg); 369 np->dp_fn = PCI_REG_FUNC_G(reg); 370 np->dp_bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) | 371 PCI_REG_FUNC_G(reg); 372 /* 373 * There *may* be a class code we can capture. If there wasn't 374 * one, capture that fact by setting the class value to -1. 375 */ 376 if (di_uintprop_get(mp, src, DI_CCPROP, &code) == 0) { 377 np->dp_class = GETCLASS(code); 378 np->dp_subclass = GETSUBCLASS(code); 379 } else { 380 np->dp_class = -1; 381 } 382 /* 383 * There *may* be a device type we can capture. 384 */ 385 (void) di_devtype_get(mp, src, &np->dp_devtype); 386 387 if (irc >= 0) { 388 /* 389 * This is a pciex node. 390 */ 391 if (di_physlotinfo_get(mp, src, np->dp_bdf, &np->dp_physlot, 392 &np->dp_physlot_name) < 0) { 393 if (np->dp_devtype != NULL) 394 topo_mod_strfree(mp, np->dp_devtype); 395 topo_mod_free(mp, np, sizeof (did_t)); 396 return (NULL); 397 } 398 } else { 399 /* 400 * This is a pci node. 401 */ 402 np->dp_physlot = -1; 403 if (di_slotinfo_get(mp, src, &np->dp_nslots, 404 &np->dp_slotnames) < 0) { 405 if (np->dp_devtype != NULL) 406 topo_mod_strfree(mp, np->dp_devtype); 407 topo_mod_free(mp, np, sizeof (did_t)); 408 return (NULL); 409 } 410 } 411 did_hash_insert(mp, src, np); 412 did_hold(np); 413 return (np); 414 } 415 416 did_t * 417 did_link_get(did_t *dp) 418 { 419 assert(dp != NULL); 420 return (dp->dp_link); 421 } 422 423 did_t * 424 did_chain_get(did_t *dp) 425 { 426 assert(dp != NULL); 427 return (dp->dp_chain); 428 } 429 430 void 431 did_link_set(topo_mod_t *mod, tnode_t *head, did_t *tail) 432 { 433 did_t *hd, *pd; 434 435 assert(head != NULL); 436 pd = hd = did_find(mod, topo_node_getspecific(head)); 437 assert(hd != NULL); 438 while ((hd = did_link_get(hd)) != NULL) 439 pd = hd; 440 pd->dp_link = tail; 441 tail->dp_link = NULL; 442 } 443 444 void 445 did_did_link_set(did_t *from, did_t *to) 446 { 447 assert(from != NULL && to != NULL); 448 from->dp_link = to; 449 } 450 451 void 452 did_did_chain_set(did_t *from, did_t *to) 453 { 454 assert(from != NULL && to != NULL); 455 from->dp_chain = to; 456 } 457 458 void 459 did_destroy(did_t *dp) 460 { 461 assert(dp != NULL); 462 463 /* 464 * did_destroy() is called only from did_hash_destroy() when 465 * all references to the did_t have been released. We can 466 * safely destroy the did_t. If at some later time, more 467 * fine-grained reference count control is desired, this 468 * code will need to change 469 */ 470 471 if (dp->dp_devtype != NULL) 472 topo_mod_strfree(dp->dp_mod, dp->dp_devtype); 473 if (dp->dp_physlot_name != NULL) 474 topo_mod_strfree(dp->dp_mod, dp->dp_physlot_name); 475 if (dp->dp_slot_label != NULL) 476 topo_mod_strfree(dp->dp_mod, dp->dp_slot_label); 477 slotnm_destroy(dp->dp_slotnames); 478 topo_mod_free(dp->dp_mod, dp, sizeof (did_t)); 479 } 480 481 void 482 did_hold(did_t *dp) 483 { 484 assert(dp != NULL); 485 dp->dp_refcnt++; 486 } 487 488 void 489 did_rele(did_t *dp) 490 { 491 assert(dp != NULL); 492 assert(dp->dp_refcnt > 0); 493 dp->dp_refcnt--; 494 } 495 496 di_node_t 497 did_dinode(did_t *dp) 498 { 499 assert(dp != NULL); 500 assert(dp->dp_src != NULL); 501 return (dp->dp_src); 502 } 503 504 topo_mod_t * 505 did_mod(did_t *dp) 506 { 507 assert(dp != NULL); 508 return (dp->dp_mod); 509 } 510 511 void 512 did_markrc(did_t *dp) 513 { 514 assert(dp != NULL); 515 dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT; 516 } 517 518 void 519 did_BDF(did_t *dp, int *bus, int *dev, int *fn) 520 { 521 assert(dp != NULL); 522 if (bus != NULL) 523 *bus = dp->dp_bus; 524 if (dev != NULL) 525 *dev = dp->dp_dev; 526 if (fn != NULL) 527 *fn = dp->dp_fn; 528 } 529 530 int 531 did_board(did_t *did) 532 { 533 assert(did != NULL); 534 return (did->dp_board); 535 } 536 537 int 538 did_bridge(did_t *did) 539 { 540 assert(did != NULL); 541 return (did->dp_bridge); 542 } 543 544 int 545 did_rc(did_t *did) 546 { 547 assert(did != NULL); 548 return (did->dp_rc); 549 } 550 551 int 552 did_excap(did_t *dp) 553 { 554 assert(dp != NULL); 555 return ((int)dp->dp_excap); 556 } 557 558 void 559 did_excap_set(did_t *dp, int type) 560 { 561 dp->dp_excap = type; 562 } 563 564 int 565 did_bdf(did_t *dp) 566 { 567 assert(dp != NULL); 568 return ((int)dp->dp_bdf); 569 } 570 571 const char * 572 did_physlot_name(did_t *dp, int dev) 573 { 574 slotnm_t *slot; 575 576 assert(dp != NULL); 577 578 /* 579 * For pciex, name will be in dp_physlot_name 580 */ 581 if (dp->dp_physlot_name != NULL) 582 return (dp->dp_physlot_name); 583 584 /* 585 * For pci, name will be in dp_slotnames 586 */ 587 for (slot = dp->dp_slotnames; slot != NULL; slot = slot->snm_next) 588 if (slot->snm_dev == dev) 589 break; 590 if (slot != NULL) 591 return (slot->snm_name); 592 return (NULL); 593 } 594 595 char * 596 did_slot_label_get(did_t *did) 597 { 598 assert(did != NULL); 599 return (did->dp_slot_label); 600 } 601 602 void 603 did_slot_label_set(did_t *did, char *l) 604 { 605 assert(did != NULL); 606 did->dp_slot_label = l; 607 } 608 609 did_t * 610 did_find(topo_mod_t *mp, di_node_t dn) 611 { 612 return (did_hash_lookup(mp, dn)); 613 } 614 615 int 616 pci_BDF_get(topo_mod_t *mp, di_node_t dn, int *bus, int *dev, int *fn) 617 { 618 did_t *dp; 619 620 if ((dp = did_find(mp, dn)) == NULL) 621 return (-1); 622 *bus = dp->dp_bus; 623 *dev = dp->dp_dev; 624 *fn = dp->dp_fn; 625 did_rele(dp); 626 return (0); 627 } 628 629 int 630 pci_classcode_get(topo_mod_t *mp, di_node_t dn, uint_t *class, uint_t *sub) 631 { 632 did_t *dp; 633 634 if ((dp = did_find(mp, dn)) == NULL) 635 return (-1); 636 if (dp->dp_class < 0) { 637 did_rele(dp); 638 return (-1); 639 } 640 *class = dp->dp_class; 641 *sub = dp->dp_subclass; 642 did_rele(dp); 643 return (0); 644 } 645 646 char * 647 pci_devtype_get(topo_mod_t *mp, di_node_t dn) 648 { 649 did_t *dp; 650 651 if ((dp = did_find(mp, dn)) == NULL) 652 return (NULL); 653 did_rele(dp); 654 return (dp->dp_devtype); 655 } 656 657 int 658 pciex_cap_get(topo_mod_t *mp, di_node_t dn) 659 { 660 did_t *dp; 661 662 if ((dp = did_find(mp, dn)) == NULL) 663 return (-1); 664 did_rele(dp); 665 return (dp->dp_excap); 666 } 667 668 void 669 did_setspecific(topo_mod_t *mp, void *data) 670 { 671 did_t *hbdid; 672 673 hbdid = (did_t *)data; 674 topo_mod_setspecific(mp, hbdid->dp_hash); 675 } 676 677 void 678 did_settnode(did_t *pd, tnode_t *tn) 679 { 680 assert(tn != NULL); 681 pd->dp_tnode = tn; 682 } 683 684 tnode_t * 685 did_gettnode(did_t *pd) 686 { 687 return (pd->dp_tnode); 688 } 689