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=%d, 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[%d], anode_bdf=%d/%d/%d\n", 296 __func__, node, topo_node_name(anode), 297 topo_node_instance(anode), b, d, f); 298 } 299 if ((apnode != NULL) && (apdp != NULL)) { 300 did_BDF(apdp, &b, &d, &f); 301 topo_mod_dprintf(mod, "%s: node=%p: " 302 "apnode_name=%s[%d], " 303 "apnode_bdf=%d/%d/%d\n", 304 __func__, node, topo_node_name(apnode), 305 topo_node_instance(apnode), b, d, f); 306 } 307 308 /* 309 * If the ancestors do not exist or are not pci 310 * devices then we're done searching. 311 * 312 * Otherwise, if the ancestor has a physical slot, 313 * and it is a different slot than the one we 314 * started with then lookup the ancestor label, 315 * and we're done. 316 */ 317 if ((anode == NULL) || (adp == NULL) || 318 (apnode == NULL) || (apdp == NULL)) { 319 done++; 320 } else if (did_physlot_exists(apdp) && 321 (apdp != pdp)) { 322 if (topo_node_label(anode, &ancestor_l, 323 &err) != 0) { 324 topo_mod_dprintf(mod, 325 "%s: node=%p: topo_node_label() " 326 "FAILED!", __func__, node); 327 (void) topo_mod_seterrno(mod, err); 328 return (NULL); 329 } 330 done++; 331 topo_mod_dprintf(mod, "%s: node=%p: found " 332 "ancestor with a slot, label=%s ", 333 __func__, node, ancestor_l); 334 } 335 } 336 if (ancestor_l == NULL) { 337 topo_mod_dprintf(mod, "%s: node=%p: no ancestor " 338 "slot found\n", __func__, node); 339 } 340 } 341 342 /* 343 * If we found an ancestor with a slot label, and this node has 344 * a physical slot number label then concatenate the two to form 345 * this node's label. Otherwise, do a full slot label lookup. 346 */ 347 if (ancestor_l && l) { 348 topo_mod_dprintf(mod, "%s: node=%p: concatenating " 349 "ancestor_l=%s and l=%s\n", 350 __func__, node, ancestor_l, l); 351 len = strlen(ancestor_l) + strlen(l) + 2; 352 new_l = alloca(len); 353 (void) snprintf(new_l, len, "%s/%s", ancestor_l, l); 354 l = new_l; 355 } else { 356 /* 357 * Get platform name used for lookups. 358 */ 359 if (topo_prop_get_string(node, FM_FMRI_AUTHORITY, 360 FM_FMRI_AUTH_PRODUCT, &plat, &err) < 0) { 361 (void) topo_mod_seterrno(mod, err); 362 return (NULL); 363 } 364 /* 365 * Trim SUNW, from the platform name 366 */ 367 pp = strchr(plat, ','); 368 if (pp == NULL) 369 pp = plat; 370 else 371 ++pp; 372 /* 373 * Get device number used for lookup. 374 */ 375 did_BDF(dp, NULL, &d, NULL); 376 377 /* 378 * The slot label is determined in the following order: 379 * - Platform specific lookup based on physical slot #. 380 * - Platform specific lookup based on default label string. 381 * - Platform specific lookup based on device number. 382 * - Default label. 383 * The default label is based on the slot names property 384 * if it exists, else it is a generic name derived from 385 * the slot #. 386 */ 387 if ((l = (char *)pci_label_physlot_lookup(mod, pp, pdp)) 388 == NULL) { 389 if ((l = (char *)did_physlot_name(dp, d)) != NULL) { 390 l = (char *) 391 pci_label_slotname_lookup(mod, pp, l, dp); 392 } 393 if (l == NULL) { 394 l = (char *) 395 pci_label_missing_lookup(mod, pp, dp); 396 } 397 } 398 topo_mod_strfree(mod, plat); 399 } 400 401 /* 402 * If we calculated a slot label, then save it in the 403 * node's data structure so we can free it later. 404 */ 405 if (l) { 406 if (did_slot_label_get(dp) != NULL) 407 topo_mod_strfree(mod, did_slot_label_get(dp)); 408 l = topo_mod_strdup(mod, l); 409 did_slot_label_set(dp, l); 410 } 411 412 topo_mod_dprintf(mod, "%s: exit: node=%p: label=%s\n", 413 __func__, node, (l ? l : "NULL")); 414 415 return (l); 416 } 417 418 int 419 pci_label_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out) 420 { 421 uint64_t ptr; 422 char *l; 423 did_t *dp, *pdp; 424 tnode_t *pnode; 425 char *nm; 426 int err; 427 428 /* 429 * If it's not a device or a PCI-express bus (which could potentially 430 * represent a slot, and therefore we might need to capture its slot 431 * name information), just inherit any label from our parent 432 */ 433 *out = NULL; 434 nm = topo_node_name(node); 435 if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 && 436 strcmp(nm, PCIEX_BUS) != 0) { 437 if (topo_node_label_set(node, NULL, &err) < 0) 438 if (err != ETOPO_PROP_NOENT) 439 return (topo_mod_seterrno(mod, err)); 440 return (0); 441 } 442 443 if (nvlist_lookup_uint64(in, TOPO_METH_LABEL_ARG_NVL, &ptr) != 0) { 444 topo_mod_dprintf(mod, 445 "%s: label method argument not found.\n", __func__); 446 return (-1); 447 } 448 dp = (did_t *)(uintptr_t)ptr; 449 pnode = did_gettnode(dp); 450 pdp = did_find(mod, topo_node_getspecific(pnode)); 451 452 /* 453 * Is there a slot label associated with the device? 454 */ 455 if ((l = pci_slot_label_lookup(mod, node, dp, pdp)) != NULL) { 456 nvlist_t *rnvl; 457 458 if (topo_mod_nvalloc(mod, &rnvl, NV_UNIQUE_NAME) != 0 || 459 nvlist_add_string(rnvl, TOPO_METH_LABEL_RET_STR, l) != 0) 460 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 461 *out = rnvl; 462 return (0); 463 } else { 464 if (topo_node_label_set(node, NULL, &err) < 0) 465 if (err != ETOPO_PROP_NOENT) 466 return (topo_mod_seterrno(mod, err)); 467 return (0); 468 } 469 } 470 471 int 472 pci_fru_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out) 473 { 474 int err = 0; 475 uint64_t ptr; 476 did_t *dp, *pdp; 477 tnode_t *pnode; 478 char *nm; 479 480 *out = NULL; 481 nm = topo_node_name(node); 482 if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 && 483 strcmp(nm, PCIEX_BUS) != 0) 484 return (0); 485 486 if (nvlist_lookup_uint64(in, "nv1", &ptr) != 0) { 487 topo_mod_dprintf(mod, 488 "%s: label method argument not found.\n", __func__); 489 return (-1); 490 } 491 dp = (did_t *)(uintptr_t)ptr; 492 pnode = did_gettnode(dp); 493 pdp = did_find(mod, topo_node_getspecific(pnode)); 494 495 /* 496 * Is there a slot label associated with the device? 497 */ 498 if (pci_slot_label_lookup(mod, pnode, dp, pdp) != NULL) { 499 nvlist_t *rnvl; 500 501 if (topo_node_resource(node, &rnvl, &err) < 0 || rnvl == NULL) { 502 topo_mod_dprintf(mod, "%s: error: %s\n", 503 __func__, topo_strerror(topo_mod_errno(mod))); 504 return (topo_mod_seterrno(mod, err)); 505 } 506 *out = rnvl; 507 } 508 return (0); 509 } 510