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