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 2006 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 235 /* 236 * We must have a reg prop and from it we extract the bus #, 237 * device #, and function #. 238 */ 239 if (di_uintprop_get(mp, src, DI_REGPROP, ®) < 0) { 240 topo_mod_free(mp, np, sizeof (did_t)); 241 return (NULL); 242 } 243 np->dp_board = ibrd; 244 np->dp_bridge = ibrdge; 245 np->dp_rc = irc; 246 if (ibus == TRUST_BDF) 247 np->dp_bus = PCI_REG_BUS_G(reg); 248 else 249 np->dp_bus = ibus; 250 np->dp_dev = PCI_REG_DEV_G(reg); 251 np->dp_fn = PCI_REG_FUNC_G(reg); 252 np->dp_bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) | 253 PCI_REG_FUNC_G(reg); 254 /* 255 * There *may* be a class code we can capture. If there wasn't 256 * one, capture that fact by setting the class value to -1. 257 */ 258 if (di_uintprop_get(mp, src, DI_CCPROP, &code) == 0) { 259 np->dp_class = GETCLASS(code); 260 np->dp_subclass = GETSUBCLASS(code); 261 } else { 262 np->dp_class = -1; 263 } 264 /* 265 * There *may* be a PCI-express capabilities register we can capture. 266 * If there wasn't one, the capabilities will be the out-of-bounds 267 * value of zero. 268 */ 269 (void) di_uintprop_get(mp, src, "pcie-capid-reg", &np->dp_excap); 270 /* 271 * There *may* be a physical slot number property we can capture. 272 */ 273 if (di_physlotinfo_get(mp, 274 src, np->dp_excap, &np->dp_physlot, &np->dp_physlot_label) < 0) { 275 topo_mod_free(mp, np, sizeof (did_t)); 276 return (NULL); 277 } 278 /* 279 * There *may* be PCI slot info we can capture 280 */ 281 if (di_slotinfo_get(mp, src, &np->dp_nslots, &np->dp_slotnames) < 0) { 282 if (np->dp_physlot_label != NULL) 283 topo_mod_strfree(mp, np->dp_physlot_label); 284 topo_mod_free(mp, np, sizeof (did_t)); 285 return (NULL); 286 } 287 did_hash_insert(mp, src, np); 288 did_hold(np); 289 return (np); 290 } 291 292 did_t * 293 did_link_get(did_t *dp) 294 { 295 assert(dp != NULL); 296 return (dp->dp_link); 297 } 298 299 did_t * 300 did_chain_get(did_t *dp) 301 { 302 assert(dp != NULL); 303 return (dp->dp_chain); 304 } 305 306 void 307 did_link_set(topo_mod_t *mod, tnode_t *head, did_t *tail) 308 { 309 did_t *hd, *pd; 310 311 assert(head != NULL); 312 pd = hd = did_find(mod, topo_node_getspecific(head)); 313 assert(hd != NULL); 314 while ((hd = did_link_get(hd)) != NULL) 315 pd = hd; 316 pd->dp_link = tail; 317 tail->dp_link = NULL; 318 } 319 320 void 321 did_did_link_set(did_t *from, did_t *to) 322 { 323 assert(from != NULL && to != NULL); 324 from->dp_link = to; 325 } 326 327 void 328 did_did_chain_set(did_t *from, did_t *to) 329 { 330 assert(from != NULL && to != NULL); 331 from->dp_chain = to; 332 } 333 334 void 335 did_destroy(did_t *dp) 336 { 337 assert(dp != NULL); 338 339 /* 340 * did_destroy() is called only from did_hash_destroy() when 341 * all references to the did_t have been released. We can 342 * safely destroy the did_t. If at some later time, more 343 * fine-grained reference count control is desired, this 344 * code will need to change 345 */ 346 347 if (dp->dp_physlot_label != NULL) 348 topo_mod_strfree(dp->dp_mod, dp->dp_physlot_label); 349 slotnm_destroy(dp->dp_slotnames); 350 topo_mod_free(dp->dp_mod, dp, sizeof (did_t)); 351 } 352 353 void 354 did_hold(did_t *dp) 355 { 356 assert(dp != NULL); 357 dp->dp_refcnt++; 358 } 359 360 void 361 did_rele(did_t *dp) 362 { 363 assert(dp != NULL); 364 assert(dp->dp_refcnt > 0); 365 dp->dp_refcnt--; 366 } 367 368 di_node_t 369 did_dinode(did_t *dp) 370 { 371 assert(dp != NULL); 372 assert(dp->dp_src != NULL); 373 return (dp->dp_src); 374 } 375 376 topo_mod_t * 377 did_mod(did_t *dp) 378 { 379 assert(dp != NULL); 380 return (dp->dp_mod); 381 } 382 383 void 384 did_markrc(did_t *dp) 385 { 386 assert(dp != NULL); 387 dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT; 388 } 389 390 void 391 did_BDF(did_t *dp, int *bus, int *dev, int *fn) 392 { 393 assert(dp != NULL); 394 if (bus != NULL) 395 *bus = dp->dp_bus; 396 if (dev != NULL) 397 *dev = dp->dp_dev; 398 if (fn != NULL) 399 *fn = dp->dp_fn; 400 } 401 402 int 403 did_board(did_t *did) 404 { 405 assert(did != NULL); 406 return (did->dp_board); 407 } 408 409 int 410 did_bridge(did_t *did) 411 { 412 assert(did != NULL); 413 return (did->dp_bridge); 414 } 415 416 int 417 did_rc(did_t *did) 418 { 419 assert(did != NULL); 420 return (did->dp_rc); 421 } 422 423 static int 424 did_numlabels(did_t *dp) 425 { 426 assert(dp != NULL); 427 return (dp->dp_nslots); 428 } 429 430 int 431 did_excap(did_t *dp) 432 { 433 assert(dp != NULL); 434 return ((int)dp->dp_excap); 435 } 436 437 int 438 did_bdf(did_t *dp) 439 { 440 assert(dp != NULL); 441 return ((int)dp->dp_bdf); 442 } 443 444 const char * 445 did_label(did_t *dp, int dev) 446 { 447 slotnm_t *slot; 448 449 assert(dp != NULL); 450 if (dp->dp_physlot_label != NULL) 451 return (dp->dp_physlot_label); 452 for (slot = dp->dp_slotnames; slot != NULL; slot = slot->snm_next) 453 if (slot->snm_dev == dev) 454 break; 455 if (slot != NULL) 456 return (slot->snm_label); 457 return (NULL); 458 } 459 460 did_t * 461 did_find(topo_mod_t *mp, di_node_t dn) 462 { 463 return (did_hash_lookup(mp, dn)); 464 } 465 466 int 467 pci_BDF_get(topo_mod_t *mp, di_node_t dn, int *bus, int *dev, int *fn) 468 { 469 did_t *dp; 470 471 if ((dp = did_find(mp, dn)) == NULL) 472 return (-1); 473 *bus = dp->dp_bus; 474 *dev = dp->dp_dev; 475 *fn = dp->dp_fn; 476 did_rele(dp); 477 return (0); 478 } 479 480 int 481 pci_classcode_get(topo_mod_t *mp, di_node_t dn, uint_t *class, uint_t *sub) 482 { 483 did_t *dp; 484 485 if ((dp = did_find(mp, dn)) == NULL) 486 return (-1); 487 if (dp->dp_class < 0) { 488 did_rele(dp); 489 return (-1); 490 } 491 *class = dp->dp_class; 492 *sub = dp->dp_subclass; 493 did_rele(dp); 494 return (0); 495 } 496 497 int 498 pciex_cap_get(topo_mod_t *mp, di_node_t dn) 499 { 500 did_t *dp; 501 502 if ((dp = did_find(mp, dn)) == NULL) 503 return (-1); 504 did_rele(dp); 505 return (dp->dp_excap); 506 } 507 508 int 509 did_inherit(did_t *pdp, did_t *dp) 510 { 511 /* 512 * If the child already has a label, we're done. 513 */ 514 assert(dp != NULL); 515 if (did_numlabels(dp) > 0) 516 return (0); 517 518 assert(pdp != NULL); 519 520 /* 521 * If the child and parent are the same, we're done. 522 */ 523 if (dp == pdp) 524 return (0); 525 526 if (pdp->dp_physlot_label != NULL) { 527 topo_mod_dprintf(dp->dp_mod, 528 "%p inherits physlot label from %p.\n", dp, pdp); 529 dp->dp_physlot_label = 530 topo_mod_strdup(dp->dp_mod, pdp->dp_physlot_label); 531 if (dp->dp_physlot_label == NULL) 532 return (-1); 533 } 534 if (slotnm_cp(pdp, dp, &dp->dp_nslots) < 0) 535 return (-1); 536 return (0); 537 } 538 539 void 540 did_setspecific(topo_mod_t *mp, void *data) 541 { 542 did_t *hbdid; 543 544 hbdid = (did_t *)data; 545 topo_mod_setspecific(mp, hbdid->dp_hash); 546 } 547