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 * 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 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 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 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 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 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 255 did_physlot(did_t *did) 256 { 257 assert(did != NULL); 258 return (did->dp_physlot); 259 } 260 261 int 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 * 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 if (di_slotinfo_get(mp, src, &np->dp_nslots, 340 &np->dp_slotnames) < 0) { 341 if (np->dp_devtype != NULL) 342 topo_mod_strfree(mp, np->dp_devtype); 343 topo_mod_free(mp, np, sizeof (did_t)); 344 return (NULL); 345 } 346 } 347 did_hash_insert(mp, src, np); 348 did_hold(np); 349 return (np); 350 } 351 352 did_t * 353 did_link_get(did_t *dp) 354 { 355 assert(dp != NULL); 356 return (dp->dp_link); 357 } 358 359 did_t * 360 did_chain_get(did_t *dp) 361 { 362 assert(dp != NULL); 363 return (dp->dp_chain); 364 } 365 366 void 367 did_link_set(topo_mod_t *mod, tnode_t *head, did_t *tail) 368 { 369 did_t *hd, *pd; 370 371 assert(head != NULL); 372 pd = hd = did_find(mod, topo_node_getspecific(head)); 373 assert(hd != NULL); 374 while ((hd = did_link_get(hd)) != NULL) 375 pd = hd; 376 pd->dp_link = tail; 377 tail->dp_link = NULL; 378 } 379 380 void 381 did_did_link_set(did_t *from, did_t *to) 382 { 383 assert(from != NULL && to != NULL); 384 from->dp_link = to; 385 } 386 387 void 388 did_did_chain_set(did_t *from, did_t *to) 389 { 390 assert(from != NULL && to != NULL); 391 from->dp_chain = to; 392 } 393 394 void 395 did_destroy(did_t *dp) 396 { 397 assert(dp != NULL); 398 399 /* 400 * did_destroy() is called only from did_hash_destroy() when 401 * all references to the did_t have been released. We can 402 * safely destroy the did_t. If at some later time, more 403 * fine-grained reference count control is desired, this 404 * code will need to change 405 */ 406 407 if (dp->dp_devtype != NULL) 408 topo_mod_strfree(dp->dp_mod, dp->dp_devtype); 409 if (dp->dp_physlot_name != NULL) 410 topo_mod_strfree(dp->dp_mod, dp->dp_physlot_name); 411 if (dp->dp_slot_label != NULL) 412 topo_mod_strfree(dp->dp_mod, dp->dp_slot_label); 413 slotnm_destroy(dp->dp_slotnames); 414 topo_mod_free(dp->dp_mod, dp, sizeof (did_t)); 415 } 416 417 void 418 did_hold(did_t *dp) 419 { 420 assert(dp != NULL); 421 dp->dp_refcnt++; 422 } 423 424 void 425 did_rele(did_t *dp) 426 { 427 assert(dp != NULL); 428 assert(dp->dp_refcnt > 0); 429 dp->dp_refcnt--; 430 } 431 432 di_node_t 433 did_dinode(did_t *dp) 434 { 435 assert(dp != NULL); 436 assert(dp->dp_src != NULL); 437 return (dp->dp_src); 438 } 439 440 topo_mod_t * 441 did_mod(did_t *dp) 442 { 443 assert(dp != NULL); 444 return (dp->dp_mod); 445 } 446 447 void 448 did_markrc(did_t *dp) 449 { 450 assert(dp != NULL); 451 dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT; 452 } 453 454 void 455 did_BDF(did_t *dp, int *bus, int *dev, int *fn) 456 { 457 assert(dp != NULL); 458 if (bus != NULL) 459 *bus = dp->dp_bus; 460 if (dev != NULL) 461 *dev = dp->dp_dev; 462 if (fn != NULL) 463 *fn = dp->dp_fn; 464 } 465 466 int 467 did_board(did_t *did) 468 { 469 assert(did != NULL); 470 return (did->dp_board); 471 } 472 473 int 474 did_bridge(did_t *did) 475 { 476 assert(did != NULL); 477 return (did->dp_bridge); 478 } 479 480 int 481 did_rc(did_t *did) 482 { 483 assert(did != NULL); 484 return (did->dp_rc); 485 } 486 487 int 488 did_excap(did_t *dp) 489 { 490 assert(dp != NULL); 491 return ((int)dp->dp_excap); 492 } 493 494 void 495 did_excap_set(did_t *dp, int type) 496 { 497 dp->dp_excap = type; 498 } 499 500 int 501 did_bdf(did_t *dp) 502 { 503 assert(dp != NULL); 504 return ((int)dp->dp_bdf); 505 } 506 507 const char * 508 did_physlot_name(did_t *dp, int dev) 509 { 510 slotnm_t *slot; 511 512 assert(dp != NULL); 513 514 /* 515 * For pciex, name will be in dp_physlot_name 516 */ 517 if (dp->dp_physlot_name != NULL) 518 return (dp->dp_physlot_name); 519 520 /* 521 * For pci, name will be in dp_slotnames 522 */ 523 for (slot = dp->dp_slotnames; slot != NULL; slot = slot->snm_next) 524 if (slot->snm_dev == dev) 525 break; 526 if (slot != NULL) 527 return (slot->snm_name); 528 return (NULL); 529 } 530 531 char * 532 did_slot_label_get(did_t *did) 533 { 534 assert(did != NULL); 535 return (did->dp_slot_label); 536 } 537 538 void 539 did_slot_label_set(did_t *did, char *l) 540 { 541 assert(did != NULL); 542 did->dp_slot_label = l; 543 } 544 545 did_t * 546 did_find(topo_mod_t *mp, di_node_t dn) 547 { 548 return (did_hash_lookup(mp, dn)); 549 } 550 551 int 552 pci_BDF_get(topo_mod_t *mp, di_node_t dn, int *bus, int *dev, int *fn) 553 { 554 did_t *dp; 555 556 if ((dp = did_find(mp, dn)) == NULL) 557 return (-1); 558 *bus = dp->dp_bus; 559 *dev = dp->dp_dev; 560 *fn = dp->dp_fn; 561 did_rele(dp); 562 return (0); 563 } 564 565 int 566 pci_classcode_get(topo_mod_t *mp, di_node_t dn, uint_t *class, uint_t *sub) 567 { 568 did_t *dp; 569 570 if ((dp = did_find(mp, dn)) == NULL) 571 return (-1); 572 if (dp->dp_class < 0) { 573 did_rele(dp); 574 return (-1); 575 } 576 *class = dp->dp_class; 577 *sub = dp->dp_subclass; 578 did_rele(dp); 579 return (0); 580 } 581 582 char * 583 pci_devtype_get(topo_mod_t *mp, di_node_t dn) 584 { 585 did_t *dp; 586 587 if ((dp = did_find(mp, dn)) == NULL) 588 return (NULL); 589 did_rele(dp); 590 return (dp->dp_devtype); 591 } 592 593 int 594 pciex_cap_get(topo_mod_t *mp, di_node_t dn) 595 { 596 did_t *dp; 597 598 if ((dp = did_find(mp, dn)) == NULL) 599 return (-1); 600 did_rele(dp); 601 return (dp->dp_excap); 602 } 603 604 void 605 did_setspecific(topo_mod_t *mp, void *data) 606 { 607 did_t *hbdid; 608 609 hbdid = (did_t *)data; 610 topo_mod_setspecific(mp, hbdid->dp_hash); 611 } 612 613 void 614 did_settnode(did_t *pd, tnode_t *tn) 615 { 616 assert(tn != NULL); 617 pd->dp_tnode = tn; 618 } 619 620 tnode_t * 621 did_gettnode(did_t *pd) 622 { 623 return (pd->dp_tnode); 624 } 625