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 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 * 354 did_link_get(did_t *dp) 355 { 356 assert(dp != NULL); 357 return (dp->dp_link); 358 } 359 360 did_t * 361 did_chain_get(did_t *dp) 362 { 363 assert(dp != NULL); 364 return (dp->dp_chain); 365 } 366 367 void 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 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 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 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 419 did_hold(did_t *dp) 420 { 421 assert(dp != NULL); 422 dp->dp_refcnt++; 423 } 424 425 void 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 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 * 442 did_mod(did_t *dp) 443 { 444 assert(dp != NULL); 445 return (dp->dp_mod); 446 } 447 448 void 449 did_markrc(did_t *dp) 450 { 451 assert(dp != NULL); 452 dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT; 453 } 454 455 void 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 468 did_board(did_t *did) 469 { 470 assert(did != NULL); 471 return (did->dp_board); 472 } 473 474 int 475 did_bridge(did_t *did) 476 { 477 assert(did != NULL); 478 return (did->dp_bridge); 479 } 480 481 int 482 did_rc(did_t *did) 483 { 484 assert(did != NULL); 485 return (did->dp_rc); 486 } 487 488 int 489 did_excap(did_t *dp) 490 { 491 assert(dp != NULL); 492 return ((int)dp->dp_excap); 493 } 494 495 void 496 did_excap_set(did_t *dp, int type) 497 { 498 dp->dp_excap = type; 499 } 500 501 int 502 did_bdf(did_t *dp) 503 { 504 assert(dp != NULL); 505 return ((int)dp->dp_bdf); 506 } 507 508 const char * 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 * 533 did_slot_label_get(did_t *did) 534 { 535 assert(did != NULL); 536 return (did->dp_slot_label); 537 } 538 539 void 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 * 547 did_find(topo_mod_t *mp, di_node_t dn) 548 { 549 return (did_hash_lookup(mp, dn)); 550 } 551 552 int 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 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 * 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 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 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 615 did_settnode(did_t *pd, tnode_t *tn) 616 { 617 assert(tn != NULL); 618 pd->dp_tnode = tn; 619 } 620 621 tnode_t * 622 did_gettnode(did_t *pd) 623 { 624 return (pd->dp_tnode); 625 } 626