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 * Copyright 2019 Joyent, Inc. 25 */ 26 27 #include <alloca.h> 28 #include <assert.h> 29 #include <fm/topo_mod.h> 30 #include <libnvpair.h> 31 #include <string.h> 32 #include <sys/fm/protocol.h> 33 34 #include <did.h> 35 #include <pcibus.h> 36 #include <pcibus_labels.h> 37 38 extern slotnm_rewrite_t *Slot_Rewrites; 39 extern physlot_names_t *Physlot_Names; 40 extern missing_names_t *Missing_Names; 41 42 /* 43 * Do a platform specific label lookup based on physical slot number. 44 */ 45 static const char * 46 pci_label_physlot_lookup(topo_mod_t *mod, char *platform, did_t *dp) 47 { 48 const char *rlabel = NULL; 49 int n, p, i; 50 51 topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s\n", 52 __func__, platform); 53 54 if ((n = did_physlot(dp)) < 0 || Physlot_Names == NULL || 55 platform == NULL) 56 return (NULL); 57 58 topo_mod_dprintf(mod, "%s: doing a lookup for physlot=%d\n", 59 __func__, n); 60 61 for (p = 0; p < Physlot_Names->psn_nplats; p++) { 62 topo_mod_dprintf(mod, "%s: comparing against platform=%s\n", 63 __func__, Physlot_Names->psn_names[p].pnm_platform); 64 if (strcasecmp(Physlot_Names->psn_names[p].pnm_platform, 65 platform) != 0) 66 continue; 67 topo_mod_dprintf(mod, "%s: found lookup table for this " 68 "platform\n", __func__); 69 for (i = 0; i < Physlot_Names->psn_names[p].pnm_nnames; i++) { 70 physnm_t ps; 71 ps = Physlot_Names->psn_names[p].pnm_names[i]; 72 if (ps.ps_num == n) { 73 topo_mod_dprintf(mod, "%s: matched entry=%d, " 74 "label=%s\n", __func__, i, ps.ps_label); 75 rlabel = ps.ps_label; 76 break; 77 } 78 } 79 break; 80 } 81 if (rlabel != NULL) { 82 topo_mod_dprintf(mod, "%s: returning label=%s\n", 83 __func__, rlabel); 84 } 85 return (rlabel); 86 } 87 88 /* 89 * Do a platform specific label lookup based on slot name. 90 */ 91 static const char * 92 pci_label_slotname_lookup(topo_mod_t *mod, char *platform, 93 const char *label, did_t *dp) 94 { 95 const char *rlabel = label; 96 int s, i, ret; 97 98 if (Slot_Rewrites == NULL || platform == NULL) 99 return (rlabel); 100 101 topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s\n", 102 __func__, platform); 103 104 for (s = 0; s < Slot_Rewrites->srw_nplats; s++) { 105 topo_mod_dprintf(mod, "%s: comparing against platform=%s\n", 106 __func__, Slot_Rewrites->srw_platrewrites[s].prw_platform); 107 if (strcasecmp(Slot_Rewrites->srw_platrewrites[s].prw_platform, 108 platform) != 0) 109 continue; 110 topo_mod_dprintf(mod, "%s: found lookup table for this " 111 "platform\n", __func__); 112 for (i = 0; 113 i < Slot_Rewrites->srw_platrewrites[s].prw_nrewrites; 114 i++) { 115 slot_rwd_t rw; 116 rw = Slot_Rewrites->srw_platrewrites[s].prw_rewrites[i]; 117 if (strcmp(rw.srw_obp, label) == 0) { 118 topo_mod_dprintf(mod, "%s: matched entry=%d, " 119 "old_label=%s, new_label=%s\n", 120 __func__, i, rw.srw_obp, 121 rw.srw_new ? rw.srw_new : NULL); 122 /* 123 * If a test function is specified then call 124 * it to do an additional check. 125 */ 126 if (rw.srw_test != NULL) { 127 topo_mod_dprintf(mod, 128 "%s: calling test function=%p\n", 129 __func__, rw.srw_test); 130 if ((ret = rw.srw_test(mod, dp)) != 0) 131 rlabel = rw.srw_new; 132 topo_mod_dprintf(mod, 133 "%s: test function return=%d\n", 134 __func__, ret); 135 } else { 136 rlabel = rw.srw_new; 137 } 138 break; 139 } 140 } 141 break; 142 } 143 topo_mod_dprintf(mod, "%s: returning label=%s\n", __func__, 144 rlabel ? rlabel : "NULL"); 145 return (rlabel); 146 } 147 148 /* 149 * Do a platform specific label lookup based on bus, dev, etc. 150 */ 151 static const char * 152 pci_label_missing_lookup(topo_mod_t *mod, char *platform, did_t *dp) 153 { 154 const char *rlabel = NULL; 155 int board, bridge, rc, bus, dev; 156 int p, i, ret; 157 158 if (Missing_Names == NULL || platform == NULL) 159 return (NULL); 160 161 bridge = did_bridge(dp); 162 board = did_board(dp); 163 rc = did_rc(dp); 164 did_BDF(dp, &bus, &dev, NULL); 165 166 topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s, " 167 "board=%d, bridge=%d, rc=%d, bus=%d, dev=%d\n", 168 __func__, platform, board, bridge, rc, bus, dev); 169 170 for (p = 0; p < Missing_Names->mn_nplats; p++) { 171 topo_mod_dprintf(mod, "%s: comparing against platform=%s\n", 172 __func__, Missing_Names->mn_names[p].pdl_platform); 173 if (strcasecmp(Missing_Names->mn_names[p].pdl_platform, 174 platform) != 0) 175 continue; 176 topo_mod_dprintf(mod, "%s: found lookup table for this " 177 "platform\n", __func__); 178 for (i = 0; i < Missing_Names->mn_names[p].pdl_nnames; i++) { 179 devlab_t m; 180 m = Missing_Names->mn_names[p].pdl_names[i]; 181 if (m.dl_board == board && m.dl_bridge == bridge && 182 m.dl_rc == rc && 183 (m.dl_bus == -1 || m.dl_bus == bus) && 184 (m.dl_dev == -1 || m.dl_dev == dev)) { 185 topo_mod_dprintf(mod, "%s: matched entry=%d, " 186 "label=%s\n", __func__, i, m.dl_label); 187 /* 188 * If a test function is specified then call 189 * it to do an additional test. 190 */ 191 if (m.dl_test != NULL) { 192 topo_mod_dprintf(mod, 193 "%s: calling test function=%p\n", 194 __func__, m.dl_test); 195 if ((ret = m.dl_test(mod, dp)) != 0) 196 rlabel = m.dl_label; 197 topo_mod_dprintf(mod, 198 "%s: test function return=%d\n", 199 __func__, ret); 200 if (ret) 201 break; 202 } else { 203 rlabel = m.dl_label; 204 break; 205 } 206 } 207 } 208 break; 209 } 210 if (rlabel != NULL) { 211 topo_mod_dprintf(mod, "%s: match found, label=%s\n", 212 __func__, rlabel); 213 } 214 return (rlabel); 215 } 216 217 /* 218 * Do an overall slot label lookup for the device node. 219 */ 220 char * 221 pci_slot_label_lookup(topo_mod_t *mod, tnode_t *node, did_t *dp, did_t *pdp) 222 { 223 tnode_t *anode, *apnode; 224 did_t *adp, *apdp; 225 char *plat, *pp, *l = NULL, *ancestor_l = NULL, *new_l = NULL; 226 int err, b, d, f, done = 0; 227 size_t len; 228 229 did_BDF(dp, &b, &d, &f); 230 231 topo_mod_dprintf(mod, "%s: entry: node=%p, node_name=%s, " 232 "node_inst=%" PRIu64 ", dp=%p, dp_bdf=%d/%d/%d, pdp=%p\n", 233 __func__, node, topo_node_name(node), topo_node_instance(node), 234 dp, b, d, f, pdp); 235 236 /* 237 * If this device has a physical slot number then check if 238 * an ancestor also has a slot label. 239 * 240 * If an ancestor has a slot label, then this node's label 241 * is generated by concatenating a default label onto the 242 * ancestor's label. 243 * 244 * We grab pairs of ancestors (parent and child) as we go up 245 * the tree because the parent is checked for the presence 246 * of a slot while the child contains the label. 247 * 248 * Note that this algorithm only applies to nodes which have 249 * a physical slot number. (i.e. PCIE devices or PCI/PCIX 250 * devices off of a PCIE to PCIX switch) 251 */ 252 if (did_physlot(pdp) >= 0) { 253 254 topo_mod_dprintf(mod, "%s: node=%p: node has a physical " 255 "slot=%d, checking ancestors for slots\n", 256 __func__, node, did_physlot(pdp)); 257 258 /* 259 * Get this device's physical slot name. 260 */ 261 l = (char *)did_physlot_name(pdp, d); 262 anode = topo_node_parent(node); 263 264 /* 265 * Check ancestors for a slot label until we 266 * either find one or hit a non-pci device. 267 */ 268 while (!done) { 269 270 /* 271 * Get next ancestor node and data pointers. 272 */ 273 anode = topo_node_parent(anode); 274 if (anode != NULL) { 275 adp = did_find(mod, 276 topo_node_getspecific(anode)); 277 apnode = topo_node_parent(anode); 278 if (apnode != NULL) 279 apdp = did_find(mod, 280 topo_node_getspecific(apnode)); 281 else 282 apdp = NULL; 283 } else { 284 apnode = NULL; 285 apdp = adp = NULL; 286 } 287 288 topo_mod_dprintf(mod, "%s: node=%p: checking next " 289 "two ancestors: anode=%p, adp=%p " 290 "apnode=%p, apdp=%p\n", 291 __func__, node, anode, adp, apnode, apdp); 292 if ((anode != NULL) && (adp != NULL)) { 293 did_BDF(adp, &b, &d, &f); 294 topo_mod_dprintf(mod, "%s: node=%p: " 295 "anode_name=%s[%" PRIu64 "], " 296 "anode_bdf=%d/%d/%d\n", __func__, node, 297 topo_node_name(anode), 298 topo_node_instance(anode), b, d, f); 299 } 300 if ((apnode != NULL) && (apdp != NULL)) { 301 did_BDF(apdp, &b, &d, &f); 302 topo_mod_dprintf(mod, "%s: node=%p: " 303 "apnode_name=%s[%" PRIu64 "], " 304 "apnode_bdf=%d/%d/%d\n", 305 __func__, node, topo_node_name(apnode), 306 topo_node_instance(apnode), b, d, f); 307 } 308 309 /* 310 * If the ancestors do not exist or are not pci 311 * devices then we're done searching. 312 * 313 * Otherwise, if the ancestor has a physical slot, 314 * and it is a different slot than the one we 315 * started with then lookup the ancestor label, 316 * and we're done. 317 */ 318 if ((anode == NULL) || (adp == NULL) || 319 (apnode == NULL) || (apdp == NULL)) { 320 done++; 321 } else if (did_physlot_exists(apdp) && 322 (apdp != pdp)) { 323 if (topo_node_label(anode, &ancestor_l, 324 &err) != 0) { 325 topo_mod_dprintf(mod, 326 "%s: node=%p: topo_node_label() " 327 "FAILED!", __func__, node); 328 (void) topo_mod_seterrno(mod, err); 329 return (NULL); 330 } 331 done++; 332 topo_mod_dprintf(mod, "%s: node=%p: found " 333 "ancestor with a slot, label=%s ", 334 __func__, node, ancestor_l); 335 } 336 } 337 if (ancestor_l == NULL) { 338 topo_mod_dprintf(mod, "%s: node=%p: no ancestor " 339 "slot found\n", __func__, node); 340 } 341 } 342 343 /* 344 * If we found an ancestor with a slot label, and this node has 345 * a physical slot number label then concatenate the two to form 346 * this node's label. Otherwise, do a full slot label lookup. 347 */ 348 if (ancestor_l && l) { 349 topo_mod_dprintf(mod, "%s: node=%p: concatenating " 350 "ancestor_l=%s and l=%s\n", 351 __func__, node, ancestor_l, l); 352 len = strlen(ancestor_l) + strlen(l) + 2; 353 new_l = alloca(len); 354 (void) snprintf(new_l, len, "%s/%s", ancestor_l, l); 355 l = new_l; 356 } else { 357 /* 358 * Get platform name used for lookups. 359 */ 360 if (topo_prop_get_string(node, FM_FMRI_AUTHORITY, 361 FM_FMRI_AUTH_PRODUCT, &plat, &err) < 0) { 362 (void) topo_mod_seterrno(mod, err); 363 return (NULL); 364 } 365 /* 366 * Trim SUNW, from the platform name 367 */ 368 pp = strchr(plat, ','); 369 if (pp == NULL) 370 pp = plat; 371 else 372 ++pp; 373 /* 374 * Get device number used for lookup. 375 */ 376 did_BDF(dp, NULL, &d, NULL); 377 378 /* 379 * The slot label is determined in the following order: 380 * - Platform specific lookup based on physical slot #. 381 * - Platform specific lookup based on default label string. 382 * - Platform specific lookup based on device number. 383 * - Default label. 384 * The default label is based on the slot names property 385 * if it exists, else it is a generic name derived from 386 * the slot #. 387 */ 388 if ((l = (char *)pci_label_physlot_lookup(mod, pp, pdp)) 389 == NULL) { 390 if ((l = (char *)did_physlot_name(dp, d)) != NULL) { 391 l = (char *) 392 pci_label_slotname_lookup(mod, pp, l, dp); 393 } 394 if (l == NULL) { 395 l = (char *) 396 pci_label_missing_lookup(mod, pp, dp); 397 } 398 } 399 topo_mod_strfree(mod, plat); 400 } 401 402 /* 403 * If we calculated a slot label, then save it in the 404 * node's data structure so we can free it later. 405 */ 406 if (l) { 407 if (did_slot_label_get(dp) != NULL) 408 topo_mod_strfree(mod, did_slot_label_get(dp)); 409 l = topo_mod_strdup(mod, l); 410 did_slot_label_set(dp, l); 411 } 412 413 topo_mod_dprintf(mod, "%s: exit: node=%p: label=%s\n", 414 __func__, node, (l ? l : "NULL")); 415 416 return (l); 417 } 418 419 int 420 pci_label_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out) 421 { 422 uint64_t ptr; 423 char *l; 424 did_t *dp, *pdp; 425 tnode_t *pnode; 426 char *nm; 427 int err; 428 429 /* 430 * If it's not a device or a PCI-express bus (which could potentially 431 * represent a slot, and therefore we might need to capture its slot 432 * name information), just inherit any label from our parent 433 */ 434 *out = NULL; 435 nm = topo_node_name(node); 436 if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 && 437 strcmp(nm, PCIEX_BUS) != 0) { 438 if (topo_node_label_set(node, NULL, &err) < 0) 439 if (err != ETOPO_PROP_NOENT) 440 return (topo_mod_seterrno(mod, err)); 441 return (0); 442 } 443 444 if (nvlist_lookup_uint64(in, TOPO_METH_LABEL_ARG_NVL, &ptr) != 0) { 445 topo_mod_dprintf(mod, 446 "%s: label method argument not found.\n", __func__); 447 return (-1); 448 } 449 dp = (did_t *)(uintptr_t)ptr; 450 pnode = did_gettnode(dp); 451 pdp = did_find(mod, topo_node_getspecific(pnode)); 452 453 /* 454 * Is there a slot label associated with the device? 455 */ 456 if ((l = pci_slot_label_lookup(mod, node, dp, pdp)) != NULL) { 457 nvlist_t *rnvl; 458 459 if (topo_mod_nvalloc(mod, &rnvl, NV_UNIQUE_NAME) != 0 || 460 nvlist_add_string(rnvl, TOPO_METH_LABEL_RET_STR, l) != 0) 461 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 462 *out = rnvl; 463 return (0); 464 } else { 465 if (topo_node_label_set(node, NULL, &err) < 0) 466 if (err != ETOPO_PROP_NOENT) 467 return (topo_mod_seterrno(mod, err)); 468 return (0); 469 } 470 } 471 472 int 473 pci_fru_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out) 474 { 475 int err = 0; 476 uint64_t ptr; 477 did_t *dp, *pdp; 478 tnode_t *pnode; 479 char *nm; 480 481 *out = NULL; 482 nm = topo_node_name(node); 483 if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 && 484 strcmp(nm, PCIEX_BUS) != 0) 485 return (0); 486 487 if (nvlist_lookup_uint64(in, "nv1", &ptr) != 0) { 488 topo_mod_dprintf(mod, 489 "%s: label method argument not found.\n", __func__); 490 return (-1); 491 } 492 dp = (did_t *)(uintptr_t)ptr; 493 pnode = did_gettnode(dp); 494 pdp = did_find(mod, topo_node_getspecific(pnode)); 495 496 /* 497 * Is there a slot label associated with the device? 498 */ 499 if (pci_slot_label_lookup(mod, pnode, dp, pdp) != NULL) { 500 nvlist_t *rnvl; 501 502 if (topo_node_resource(node, &rnvl, &err) < 0 || rnvl == NULL) { 503 topo_mod_dprintf(mod, "%s: error: %s\n", 504 __func__, topo_strerror(topo_mod_errno(mod))); 505 return (topo_mod_seterrno(mod, err)); 506 } 507 *out = rnvl; 508 } 509 return (0); 510 } 511