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 <libtopo.h> 44 #include <libnvpair.h> 45 #include <libdevinfo.h> 46 #include <sys/pcie.h> 47 48 #include "topo_mod.h" 49 #include "hostbridge.h" 50 #include "pcibus.h" 51 #include "did_impl.h" 52 #include "did_props.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 122 *slotnum = -1; 123 (void) di_uintprop_get(src, DI_PHYSPROP, (uint_t *)slotnum); 124 /* 125 * If no physical slot number property was found, then the 126 * capabilities register may indicate the pci-express device 127 * implements a slot, and we should record which slot. 128 */ 129 if (*slotnum == -1 && (excap & PCIE_PCIECAP_SLOT_IMPL) != 0) { 130 uint_t slotcap; 131 int e; 132 e = di_uintprop_get(src, SAVED_PCIEX_SLOTCAP_REG, &slotcap); 133 if (e == 0) 134 *slotnum = slotcap >> PCIE_SLOTCAP_PHY_SLOT_NUM_SHIFT; 135 } 136 if (*slotnum == -1) 137 return (0); 138 139 /* 140 * Make generic description string "SLOT <num>", allow up 10 141 * digits for number 142 */ 143 slotbuf = alloca(16); 144 (void) snprintf(slotbuf, 16, "SLOT %d", *slotnum); 145 146 if ((*slotnm = topo_mod_strdup(mp, slotbuf)) == NULL) 147 return (-1); 148 149 return (0); 150 } 151 152 static int 153 di_slotinfo_get(topo_mod_t *mp, di_node_t src, int *nslots, slotnm_t **slots) 154 { 155 slotnm_t *lastslot = NULL; 156 slotnm_t *newslot; 157 uchar_t *slotbuf; 158 uint_t slotmap = 0; 159 char *slotname; 160 int andbit; 161 int sz = -1; 162 163 *slots = NULL; 164 *nslots = 0; 165 if (di_bytes_get(src, DI_SLOTPROP, &sz, &slotbuf) < 0) 166 return (0); 167 if (sz < sizeof (uint_t)) 168 return (0); 169 bcopy(slotbuf, &slotmap, sizeof (uint_t)); 170 if (slotmap == 0) 171 return (0); 172 173 slotname = (char *)&slotbuf[4]; 174 for (andbit = 0; andbit < 32; andbit++) { 175 if (slotmap & (1 << andbit)) { 176 char *s = slotname; 177 slotname += strlen(s) + 1; 178 if ((newslot = slotnm_create(mp, andbit, s)) == NULL) { 179 slotnm_destroy(*slots); 180 *slots = NULL; 181 *nslots = 0; 182 return (-1); 183 } 184 if (lastslot == NULL) 185 *slots = lastslot = newslot; 186 else 187 lastslot->snm_next = newslot; 188 (*nslots)++; 189 } 190 } 191 return (0); 192 } 193 194 int 195 did_physslot(did_t *did) 196 { 197 assert(did != NULL); 198 return (did->dp_physlot); 199 } 200 201 202 did_hash_t * 203 did_hash(did_t *did) 204 { 205 assert(did != NULL); 206 return (did->dp_hash); 207 } 208 209 did_t * 210 did_create(did_hash_t *dhash, di_node_t src, 211 int ibrd, int ibrdge, int irc, int ibus) 212 { 213 topo_mod_t *mp; 214 did_t *np; 215 did_t *pd; 216 uint_t code; 217 uint_t reg; 218 219 mp = dhash->dph_mod; 220 if ((pd = did_hash_lookup(dhash, src)) != NULL) { 221 topo_mod_dprintf(mp, "Attempt to create existing did_t.\n"); 222 assert(ibus == TRUST_BDF || (pd->dp_bus == ibus)); 223 return (pd); 224 } 225 226 if ((np = topo_mod_zalloc(mp, sizeof (did_t))) == NULL) 227 return (NULL); 228 np->dp_mod = mp; 229 np->dp_src = src; 230 np->dp_hash = dhash; 231 232 /* 233 * We must have a reg prop and from it we extract the bus #, 234 * device #, and function #. 235 */ 236 if (di_uintprop_get(src, DI_REGPROP, ®) < 0) { 237 topo_mod_free(mp, np, sizeof (did_t)); 238 return (NULL); 239 } 240 np->dp_board = ibrd; 241 np->dp_bridge = ibrdge; 242 np->dp_rc = irc; 243 if (ibus == TRUST_BDF) 244 np->dp_bus = PCI_REG_BUS_G(reg); 245 else 246 np->dp_bus = ibus; 247 np->dp_dev = PCI_REG_DEV_G(reg); 248 np->dp_fn = PCI_REG_FUNC_G(reg); 249 /* 250 * There *may* be a class code we can capture. If there wasn't 251 * one, capture that fact by setting the class value to -1. 252 */ 253 if (di_uintprop_get(src, DI_CCPROP, &code) == 0) { 254 np->dp_class = GETCLASS(code); 255 np->dp_subclass = GETSUBCLASS(code); 256 } else { 257 np->dp_class = -1; 258 } 259 /* 260 * There *may* be a PCI-express capabilities register we can capture. 261 * If there wasn't one, the capabilities will be the out-of-bounds 262 * value of zero. 263 */ 264 (void) di_uintprop_get(src, SAVED_PCIEX_CAP_REG, &np->dp_excap); 265 /* 266 * There *may* be a physical slot number property we can capture. 267 */ 268 if (di_physlotinfo_get(mp, 269 src, np->dp_excap, &np->dp_physlot, &np->dp_physlot_label) < 0) { 270 topo_mod_free(mp, np, sizeof (did_t)); 271 return (NULL); 272 } 273 /* 274 * There *may* be PCI slot info we can capture 275 */ 276 if (di_slotinfo_get(mp, src, &np->dp_nslots, &np->dp_slotnames) < 0) { 277 if (np->dp_physlot_label != NULL) 278 topo_mod_strfree(mp, np->dp_physlot_label); 279 topo_mod_free(mp, np, sizeof (did_t)); 280 return (NULL); 281 } 282 did_hash_insert(dhash, src, np); 283 did_hold(np); 284 return (np); 285 } 286 287 did_t * 288 did_link_get(did_t *dp) 289 { 290 assert(dp != NULL); 291 return (dp->dp_link); 292 } 293 294 did_t * 295 did_chain_get(did_t *dp) 296 { 297 assert(dp != NULL); 298 return (dp->dp_chain); 299 } 300 301 void 302 did_link_set(tnode_t *head, did_t *tail) 303 { 304 did_t *hd, *pd; 305 306 assert(head != NULL); 307 pd = hd = topo_node_private(head); 308 assert(hd != NULL); 309 while ((hd = did_link_get(hd)) != NULL) 310 pd = hd; 311 pd->dp_link = tail; 312 tail->dp_link = NULL; 313 } 314 315 void 316 did_did_link_set(did_t *from, did_t *to) 317 { 318 assert(from != NULL && to != NULL); 319 from->dp_link = to; 320 } 321 322 void 323 did_did_chain_set(did_t *from, did_t *to) 324 { 325 assert(from != NULL && to != NULL); 326 from->dp_chain = to; 327 } 328 329 void 330 did_destroy(did_t *dp) 331 { 332 assert(dp != NULL); 333 334 /* 335 * did_destroy() is called only from did_hash_destroy() when 336 * all references to the did_t have been released. We can 337 * safely destroy the did_t. If at some later time, more 338 * fine-grained reference count control is desired, this 339 * code will need to change 340 */ 341 342 if (dp->dp_physlot_label != NULL) 343 topo_mod_strfree(dp->dp_mod, dp->dp_physlot_label); 344 slotnm_destroy(dp->dp_slotnames); 345 topo_mod_free(dp->dp_mod, dp, sizeof (did_t)); 346 } 347 348 void 349 did_hold(did_t *dp) 350 { 351 assert(dp != NULL); 352 dp->dp_refcnt++; 353 } 354 355 void 356 did_rele(did_t *dp) 357 { 358 assert(dp != NULL); 359 assert(dp->dp_refcnt > 0); 360 dp->dp_refcnt--; 361 } 362 363 di_node_t 364 did_dinode(did_t *dp) 365 { 366 assert(dp != NULL); 367 assert(dp->dp_src != NULL); 368 return (dp->dp_src); 369 } 370 371 topo_mod_t * 372 did_mod(did_t *dp) 373 { 374 assert(dp != NULL); 375 return (dp->dp_mod); 376 } 377 378 void 379 did_markrc(did_t *dp) 380 { 381 assert(dp != NULL); 382 dp->dp_excap |= PCIE_PCIECAP_DEV_TYPE_ROOT; 383 } 384 385 void 386 did_BDF(did_t *dp, int *bus, int *dev, int *fn) 387 { 388 assert(dp != NULL); 389 if (bus != NULL) 390 *bus = dp->dp_bus; 391 if (dev != NULL) 392 *dev = dp->dp_dev; 393 if (fn != NULL) 394 *fn = dp->dp_fn; 395 } 396 397 int 398 did_board(did_t *did) 399 { 400 assert(did != NULL); 401 return (did->dp_board); 402 } 403 404 int 405 did_bridge(did_t *did) 406 { 407 assert(did != NULL); 408 return (did->dp_bridge); 409 } 410 411 int 412 did_rc(did_t *did) 413 { 414 assert(did != NULL); 415 return (did->dp_rc); 416 } 417 418 static int 419 did_numlabels(did_t *dp) 420 { 421 assert(dp != NULL); 422 return (dp->dp_nslots); 423 } 424 425 int 426 did_excap(did_t *dp) 427 { 428 assert(dp != NULL); 429 return ((int)dp->dp_excap); 430 } 431 432 const char * 433 did_label(did_t *dp, int dev) 434 { 435 slotnm_t *slot; 436 437 assert(dp != NULL); 438 if (dp->dp_physlot_label != NULL) 439 return (dp->dp_physlot_label); 440 for (slot = dp->dp_slotnames; slot != NULL; slot = slot->snm_next) 441 if (slot->snm_dev == dev) 442 break; 443 if (slot != NULL) 444 return (slot->snm_label); 445 return (NULL); 446 } 447 448 did_t * 449 did_find(did_hash_t *dhash, di_node_t dn) 450 { 451 return (did_hash_lookup(dhash, dn)); 452 } 453 454 int 455 pci_BDF_get(did_hash_t *dhash, di_node_t dn, int *bus, int *dev, int *fn) 456 { 457 did_t *dp; 458 459 if ((dp = did_find(dhash, dn)) == NULL) 460 return (-1); 461 *bus = dp->dp_bus; 462 *dev = dp->dp_dev; 463 *fn = dp->dp_fn; 464 did_rele(dp); 465 return (0); 466 } 467 468 int 469 pci_classcode_get(did_hash_t *dhash, 470 di_node_t dn, uint_t *class, uint_t *sub) 471 { 472 did_t *dp; 473 474 if ((dp = did_find(dhash, dn)) == NULL) 475 return (-1); 476 if (dp->dp_class < 0) { 477 did_rele(dp); 478 return (-1); 479 } 480 *class = dp->dp_class; 481 *sub = dp->dp_subclass; 482 did_rele(dp); 483 return (0); 484 } 485 486 int 487 pciex_cap_get(did_hash_t *dhash, di_node_t dn) 488 { 489 did_t *dp; 490 491 if ((dp = did_find(dhash, dn)) == NULL) 492 return (-1); 493 did_rele(dp); 494 return (dp->dp_excap); 495 } 496 497 int 498 did_inherit(tnode_t *parent, tnode_t *child) 499 { 500 did_t *pdp, *dp; 501 502 /* 503 * If the child already has a label, we're done. 504 */ 505 dp = topo_node_private(child); 506 assert(dp != NULL); 507 if (did_numlabels(dp) > 0) 508 return (0); 509 510 pdp = topo_node_private(parent); 511 assert(pdp != NULL); 512 513 /* 514 * If the child and parent are the same, we're done. 515 */ 516 if (dp == pdp) 517 return (0); 518 519 if (pdp->dp_physlot_label != NULL) { 520 topo_mod_dprintf(dp->dp_mod, 521 "%p inherits physlot label from %p.\n", dp, pdp); 522 dp->dp_physlot_label = 523 topo_mod_strdup(dp->dp_mod, pdp->dp_physlot_label); 524 if (dp->dp_physlot_label == NULL) 525 return (-1); 526 } 527 if (slotnm_cp(pdp, dp, &dp->dp_nslots) < 0) 528 return (-1); 529 return (0); 530 } 531