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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright (c) 2018, Joyent, Inc. 28 */ 29 30 #include <assert.h> 31 #include <alloca.h> 32 #include <string.h> 33 #include <strings.h> 34 #include <limits.h> 35 #include <sys/types.h> 36 #include <sys/pci.h> 37 #include <sys/pcie.h> 38 #include <sys/fm/protocol.h> 39 #include <fm/topo_mod.h> 40 #include <fm/topo_hc.h> 41 #include <libdevinfo.h> 42 #include <hostbridge.h> 43 #include <pcibus.h> 44 #include <did.h> 45 #include <did_props.h> 46 #include <fm/libtopo.h> 47 #include <pcidb.h> 48 49 static int ASRU_set(tnode_t *, did_t *, 50 const char *, const char *, const char *); 51 static int FRU_set(tnode_t *, did_t *, 52 const char *, const char *, const char *); 53 static int DEVprop_set(tnode_t *, did_t *, 54 const char *, const char *, const char *); 55 static int DRIVERprop_set(tnode_t *, did_t *, 56 const char *, const char *, const char *); 57 static int MODULEprop_set(tnode_t *, did_t *, 58 const char *, const char *, const char *); 59 static int EXCAP_set(tnode_t *, did_t *, 60 const char *, const char *, const char *); 61 static int BDF_set(tnode_t *, did_t *, 62 const char *, const char *, const char *); 63 static int label_set(tnode_t *, did_t *, 64 const char *, const char *, const char *); 65 static int maybe_di_chars_copy(tnode_t *, did_t *, 66 const char *, const char *, const char *); 67 static int maybe_di_uint_to_str(tnode_t *, did_t *, 68 const char *, const char *, const char *); 69 static int maybe_di_uint_to_dec_str(tnode_t *, did_t *, 70 const char *, const char *, const char *); 71 static int AADDR_set(tnode_t *, did_t *, 72 const char *, const char *, const char *); 73 static int maybe_pcidb_set(tnode_t *, did_t *, 74 const char *, const char *, const char *); 75 76 /* 77 * Arrays of "property translation routines" to set the properties a 78 * given type of topology node should have. 79 * 80 * Note that the label_set translation *MUST COME BEFORE* the FRU 81 * translation. For the near term we're setting the FRU fmri to 82 * be a legacy-hc style FMRI based on the label, so the label needs 83 * to have been set before we do the FRU translation. 84 * 85 */ 86 87 static const topo_pgroup_info_t io_pgroup = 88 { TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 89 static const topo_pgroup_info_t pci_pgroup = 90 { TOPO_PGROUP_PCI, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; 91 92 static const topo_pgroup_info_t protocol_pgroup = { 93 TOPO_PGROUP_PROTOCOL, 94 TOPO_STABILITY_PRIVATE, 95 TOPO_STABILITY_PRIVATE, 96 1 97 }; /* Request to create protocol will be ignored by libtopo */ 98 99 txprop_t Fn_common_props[] = { 100 { NULL, &io_pgroup, TOPO_IO_DEV, DEVprop_set }, 101 { DI_DEVTYPPROP, &io_pgroup, TOPO_IO_DEVTYPE, maybe_di_chars_copy }, 102 { DI_DEVIDPROP, &pci_pgroup, TOPO_PCI_DEVID, maybe_di_uint_to_str }, 103 { NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set }, 104 { NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set }, 105 { "serd_io_device_nonfatal_n", &io_pgroup, "serd_io_device_nonfatal_n", 106 maybe_di_uint_to_dec_str }, 107 { "serd_io_device_nonfatal_t", &io_pgroup, "serd_io_device_nonfatal_t", 108 maybe_di_chars_copy }, 109 { "serd_io_device_nonfatal_btlp_n", &io_pgroup, 110 "serd_io_device_nonfatal_btlp_n", maybe_di_uint_to_dec_str }, 111 { "serd_io_device_nonfatal_btlp_t", &io_pgroup, 112 "serd_io_device_nonfatal_btlp_t", maybe_di_chars_copy }, 113 { "serd_io_device_nonfatal_bdllp_n", &io_pgroup, 114 "serd_io_device_nonfatal_bdllp_n", maybe_di_uint_to_dec_str }, 115 { "serd_io_device_nonfatal_bdllp_t", &io_pgroup, 116 "serd_io_device_nonfatal_bdllp_t", maybe_di_chars_copy }, 117 { "serd_io_device_nonfatal_re_n", &io_pgroup, 118 "serd_io_device_nonfatal_re_n", maybe_di_uint_to_dec_str }, 119 { "serd_io_device_nonfatal_re_t", &io_pgroup, 120 "serd_io_device_nonfatal_re_t", maybe_di_chars_copy }, 121 { "serd_io_device_nonfatal_rto_n", &io_pgroup, 122 "serd_io_device_nonfatal_rto_n", maybe_di_uint_to_dec_str }, 123 { "serd_io_device_nonfatal_rto_t", &io_pgroup, 124 "serd_io_device_nonfatal_rto_t", maybe_di_chars_copy }, 125 { "serd_io_device_nonfatal_rnr_n", &io_pgroup, 126 "serd_io_device_nonfatal_rnr_n", maybe_di_uint_to_dec_str }, 127 { "serd_io_device_nonfatal_rnr_t", &io_pgroup, 128 "serd_io_pciex_corrlink-bus_rnr_t", maybe_di_chars_copy }, 129 { "serd_io_pciex_corrlink-bus_btlp_n", &io_pgroup, 130 "serd_io_pciex_corrlink-bus_btlp_n", maybe_di_uint_to_dec_str }, 131 { "serd_io_pciex_corrlink-bus_btlp_t", &io_pgroup, 132 "serd_io_pciex_corrlink-bus_btlp_t", maybe_di_chars_copy }, 133 { "serd_io_pciex_corrlink-bus_bdllp_n", &io_pgroup, 134 "serd_io_pciex_corrlink-bus_bdllp_n", maybe_di_uint_to_dec_str }, 135 { "serd_io_pciex_corrlink-bus_bdllp_t", &io_pgroup, 136 "serd_io_pciex_corrlink-bus_bdllp_t", maybe_di_chars_copy }, 137 { "serd_io_pciex_corrlink-bus_re_n", &io_pgroup, 138 "serd_io_pciex_corrlink-bus_re_n", maybe_di_uint_to_dec_str }, 139 { "serd_io_pciex_corrlink-bus_re_t", &io_pgroup, 140 "serd_io_pciex_corrlink-bus_re_t", maybe_di_chars_copy }, 141 { "serd_io_pciex_corrlink-bus_rto_n", &io_pgroup, 142 "serd_io_pciex_corrlink-bus_rto_n", maybe_di_uint_to_dec_str }, 143 { "serd_io_pciex_corrlink-bus_rto_t", &io_pgroup, 144 "serd_io_pciex_corrlink-bus_rto_t", maybe_di_chars_copy }, 145 { "serd_io_pciex_corrlink-bus_rnr_n", &io_pgroup, 146 "serd_io_pciex_corrlink-bus_rnr_n", maybe_di_uint_to_dec_str }, 147 { "serd_io_pciex_corrlink-bus_rnr_t", &io_pgroup, 148 "serd_io_pciex_corrlink-bus_rnr_t", maybe_di_chars_copy }, 149 { NULL, &pci_pgroup, TOPO_PCI_EXCAP, EXCAP_set }, 150 { DI_CLASSPROP, &pci_pgroup, TOPO_PCI_CLASS, maybe_di_uint_to_str }, 151 { DI_VENDIDPROP, &pci_pgroup, TOPO_PCI_VENDID, maybe_di_uint_to_str }, 152 { DI_AADDRPROP, &pci_pgroup, TOPO_PCI_AADDR, AADDR_set }, 153 { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set }, 154 { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }, 155 { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }, 156 /* 157 * This entry will attempt to set the following three properties via 158 * lookups in the PCI database: 159 * - vendor-name 160 * - device-name 161 * - subsystem-name 162 */ 163 { NULL, &pci_pgroup, NULL, maybe_pcidb_set } 164 }; 165 166 txprop_t Dev_common_props[] = { 167 { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set }, 168 { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }, 169 { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set } 170 }; 171 172 txprop_t Bus_common_props[] = { 173 { DI_DEVTYPPROP, &io_pgroup, TOPO_IO_DEVTYPE, maybe_di_chars_copy }, 174 { NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set }, 175 { NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set }, 176 { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set }, 177 { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }, 178 { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set } 179 }; 180 181 txprop_t RC_common_props[] = { 182 { NULL, &io_pgroup, TOPO_IO_DEV, DEVprop_set }, 183 { DI_DEVTYPPROP, &io_pgroup, TOPO_IO_DEVTYPE, maybe_di_chars_copy }, 184 { NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set }, 185 { NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set }, 186 { NULL, &pci_pgroup, TOPO_PCI_EXCAP, EXCAP_set }, 187 { NULL, &pci_pgroup, TOPO_PCI_BDF, BDF_set }, 188 { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }, 189 /* 190 * These props need to be put at the end of table. x86pi has its 191 * own way to set them. 192 */ 193 { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set }, 194 { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set } 195 }; 196 197 txprop_t ExHB_common_props[] = { 198 { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }, 199 /* 200 * These props need to be put at the end of table. x86pi has its 201 * own way to set them. 202 */ 203 { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set }, 204 { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set } 205 }; 206 207 txprop_t IOB_common_props[] = { 208 { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set }, 209 { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set }, 210 { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set } 211 }; 212 213 txprop_t HB_common_props[] = { 214 { NULL, &io_pgroup, TOPO_IO_DEV, DEVprop_set }, 215 { NULL, &io_pgroup, TOPO_IO_DRIVER, DRIVERprop_set }, 216 { NULL, &io_pgroup, TOPO_IO_MODULE, MODULEprop_set }, 217 { NULL, &protocol_pgroup, TOPO_PROP_ASRU, ASRU_set }, 218 /* 219 * These props need to be put at the end of table. x86pi has its 220 * own way to set them. 221 */ 222 { NULL, &protocol_pgroup, TOPO_PROP_LABEL, label_set }, 223 { NULL, &protocol_pgroup, TOPO_PROP_FRU, FRU_set } 224 }; 225 226 int Bus_propcnt = sizeof (Bus_common_props) / sizeof (txprop_t); 227 int Dev_propcnt = sizeof (Dev_common_props) / sizeof (txprop_t); 228 int ExHB_propcnt = sizeof (ExHB_common_props) / sizeof (txprop_t); 229 int HB_propcnt = sizeof (HB_common_props) / sizeof (txprop_t); 230 int IOB_propcnt = sizeof (IOB_common_props) / sizeof (txprop_t); 231 int RC_propcnt = sizeof (RC_common_props) / sizeof (txprop_t); 232 int Fn_propcnt = sizeof (Fn_common_props) / sizeof (txprop_t); 233 234 /* 235 * If this devinfo node came originally from OBP data, we'll have prom 236 * properties associated with the node where we can find properties of 237 * interest. We ignore anything after the the first four bytes of the 238 * property, and interpet those first four bytes as our unsigned 239 * integer. If we don't find the property or it's not large enough, 240 * 'val' will remained unchanged and we'll return -1. Otherwise 'val' 241 * gets updated with the property value and we return 0. 242 */ 243 static int 244 promprop2uint(topo_mod_t *mod, di_node_t n, const char *propnm, uint_t *val) 245 { 246 di_prom_handle_t ptp = DI_PROM_HANDLE_NIL; 247 di_prom_prop_t pp = DI_PROM_PROP_NIL; 248 uchar_t *buf; 249 250 if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_HANDLE_NIL) 251 return (-1); 252 253 while ((pp = di_prom_prop_next(ptp, n, pp)) != DI_PROM_PROP_NIL) { 254 if (strcmp(di_prom_prop_name(pp), propnm) == 0) { 255 if (di_prom_prop_data(pp, &buf) < sizeof (uint_t)) 256 continue; 257 bcopy(buf, val, sizeof (uint_t)); 258 return (0); 259 } 260 } 261 return (-1); 262 } 263 264 /* 265 * If this devinfo node was added by the PCI hotplug framework it 266 * doesn't have the PROM properties, but hopefully has the properties 267 * we're looking for attached directly to the devinfo node. We only 268 * care about the first four bytes of the property, which we read as 269 * our unsigned integer. The remaining bytes are ignored. If we 270 * don't find the property we're looking for, or can't get its value, 271 * 'val' remains unchanged and we return -1. Otherwise 'val' gets the 272 * property value and we return 0. 273 */ 274 static int 275 hwprop2uint(di_node_t n, const char *propnm, uint_t *val) 276 { 277 di_prop_t hp = DI_PROP_NIL; 278 uchar_t *buf; 279 280 while ((hp = di_prop_next(n, hp)) != DI_PROP_NIL) { 281 if (strcmp(di_prop_name(hp), propnm) == 0) { 282 if (di_prop_bytes(hp, &buf) < sizeof (uint_t)) 283 continue; 284 bcopy(buf, val, sizeof (uint_t)); 285 return (0); 286 } 287 } 288 return (-1); 289 } 290 291 int 292 di_uintprop_get(topo_mod_t *mod, di_node_t n, const char *pnm, uint_t *pv) 293 { 294 if (hwprop2uint(n, pnm, pv) < 0) 295 if (promprop2uint(mod, n, pnm, pv) < 0) 296 return (-1); 297 return (0); 298 } 299 300 int 301 di_bytes_get(topo_mod_t *mod, di_node_t n, const char *pnm, int *sz, 302 uchar_t **db) 303 { 304 di_prom_handle_t ptp = DI_PROM_HANDLE_NIL; 305 di_prom_prop_t pp = DI_PROM_PROP_NIL; 306 di_prop_t hp = DI_PROP_NIL; 307 308 if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_HANDLE_NIL) 309 return (-1); 310 311 *sz = -1; 312 while ((hp = di_prop_next(n, hp)) != DI_PROP_NIL) { 313 if (strcmp(di_prop_name(hp), pnm) == 0) { 314 if ((*sz = di_prop_bytes(hp, db)) < 0) 315 continue; 316 break; 317 } 318 } 319 if (*sz < 0) { 320 while ((pp = di_prom_prop_next(ptp, n, pp)) != 321 DI_PROM_PROP_NIL) { 322 if (strcmp(di_prom_prop_name(pp), pnm) == 0) { 323 *sz = di_prom_prop_data(pp, db); 324 if (*sz < 0) 325 continue; 326 break; 327 } 328 } 329 } 330 331 if (*sz < 0) 332 return (-1); 333 return (0); 334 } 335 336 /* 337 * fix_dev_prop -- sometimes di_devfs_path() doesn't tell the whole 338 * story, leaving off the device and function number. Chances are if 339 * devfs doesn't put these on then we'll never see this device as an 340 * error detector called out in an ereport. Unfortunately, there are 341 * races and we sometimes do get ereports from devices that devfs 342 * decides aren't there. For example, the error injector card seems 343 * to bounce in and out of existence according to devfs. We tack on 344 * the missing dev and fn here so that the DEV property used to look 345 * up the topology node is correct. 346 */ 347 static char * 348 dev_path_fix(topo_mod_t *mp, char *path, int devno, int fnno) 349 { 350 char *lastslash; 351 char *newpath; 352 int need; 353 354 /* 355 * We only care about the last component of the dev path. If 356 * we don't find a slash, something is weird. 357 */ 358 lastslash = strrchr(path, '/'); 359 assert(lastslash != NULL); 360 361 /* 362 * If an @ sign is present in the last component, the 363 * di_devfs_path() result had the device,fn unit-address. 364 * In that case there's nothing we need do. 365 */ 366 if (strchr(lastslash, '@') != NULL) 367 return (path); 368 369 if (fnno == 0) 370 need = snprintf(NULL, 0, "%s@%x", path, devno); 371 else 372 need = snprintf(NULL, 0, "%s@%x,%x", path, devno, fnno); 373 need++; 374 375 if ((newpath = topo_mod_alloc(mp, need)) == NULL) { 376 topo_mod_strfree(mp, path); 377 return (NULL); 378 } 379 380 if (fnno == 0) 381 (void) snprintf(newpath, need, "%s@%x", path, devno); 382 else 383 (void) snprintf(newpath, need, "%s@%x,%x", path, devno, fnno); 384 385 topo_mod_strfree(mp, path); 386 return (newpath); 387 } 388 389 /* 390 * dev_for_hostbridge() -- For hostbridges we truncate the devfs path 391 * after the first element in the bus address. 392 */ 393 static char * 394 dev_for_hostbridge(topo_mod_t *mp, char *path) 395 { 396 char *lastslash; 397 char *newpath; 398 char *comma; 399 int plen; 400 401 plen = strlen(path) + 1; 402 403 /* 404 * We only care about the last component of the dev path. If 405 * we don't find a slash, something is weird. 406 */ 407 lastslash = strrchr(path, '/'); 408 assert(lastslash != NULL); 409 410 /* 411 * Find the comma in the last component component@x,y, and 412 * truncate the comma and any following number. 413 */ 414 comma = strchr(lastslash, ','); 415 assert(comma != NULL); 416 417 *comma = '\0'; 418 if ((newpath = topo_mod_strdup(mp, path)) == NULL) { 419 topo_mod_free(mp, path, plen); 420 return (NULL); 421 } 422 423 *comma = ','; 424 topo_mod_free(mp, path, plen); 425 return (newpath); 426 } 427 428 /*ARGSUSED*/ 429 static int 430 ASRU_set(tnode_t *tn, did_t *pd, 431 const char *dpnm, const char *tpgrp, const char *tpnm) 432 { 433 topo_mod_t *mp; 434 nvlist_t *fmri; 435 char *dnpath, *path, *fpath, *nm; 436 int d, e, f; 437 438 /* 439 * If this topology node represents a function of device, 440 * set the ASRU to a dev scheme FMRI based on the value of 441 * di_devfs_path(). If that path is NULL, set the ASRU to 442 * be the resource describing this topology node. If this 443 * isn't a function, inherit any ASRU from the parent. 444 */ 445 mp = did_mod(pd); 446 nm = topo_node_name(tn); 447 if ((strcmp(nm, PCI_BUS) == 0 && did_gettnode(pd) && 448 strcmp(topo_node_name(did_gettnode(pd)), HOSTBRIDGE) == 0) || 449 strcmp(nm, PCI_FUNCTION) == 0 || strcmp(nm, PCIEX_FUNCTION) == 0 || 450 strcmp(nm, PCIEX_ROOT) == 0) { 451 if ((dnpath = di_devfs_path(did_dinode(pd))) != NULL) { 452 /* 453 * Dup the path, dev_path_fix() may replace it and 454 * dev_path_fix() wouldn't know to use 455 * di_devfs_path_free() 456 */ 457 if ((path = topo_mod_strdup(mp, dnpath)) == NULL) { 458 di_devfs_path_free(dnpath); 459 return (topo_mod_seterrno(mp, EMOD_NOMEM)); 460 } 461 di_devfs_path_free(dnpath); 462 did_BDF(pd, NULL, &d, &f); 463 if ((fpath = dev_path_fix(mp, path, d, f)) == NULL) 464 return (topo_mod_seterrno(mp, EMOD_NOMEM)); 465 466 fmri = topo_mod_devfmri(mp, FM_DEV_SCHEME_VERSION, 467 fpath, NULL); 468 if (fmri == NULL) { 469 topo_mod_dprintf(mp, 470 "dev:///%s fmri creation failed.\n", fpath); 471 topo_mod_strfree(mp, fpath); 472 return (-1); 473 } 474 topo_mod_strfree(mp, fpath); 475 } else { 476 topo_mod_dprintf(mp, "NULL di_devfs_path.\n"); 477 if (topo_prop_get_fmri(tn, TOPO_PGROUP_PROTOCOL, 478 TOPO_PROP_RESOURCE, &fmri, &e) < 0) 479 return (topo_mod_seterrno(mp, e)); 480 } 481 if (topo_node_asru_set(tn, fmri, 0, &e) < 0) { 482 nvlist_free(fmri); 483 return (topo_mod_seterrno(mp, e)); 484 } 485 nvlist_free(fmri); 486 return (0); 487 } 488 (void) topo_node_asru_set(tn, NULL, 0, &e); 489 490 return (0); 491 } 492 493 /* 494 * Set the FRU property to the hc fmri of this tnode 495 */ 496 int 497 FRU_fmri_set(topo_mod_t *mp, tnode_t *tn) 498 { 499 nvlist_t *fmri; 500 int err, e; 501 502 if (topo_node_resource(tn, &fmri, &err) < 0 || 503 fmri == NULL) { 504 topo_mod_dprintf(mp, "FRU_fmri_set error: %s\n", 505 topo_strerror(topo_mod_errno(mp))); 506 return (topo_mod_seterrno(mp, err)); 507 } 508 e = topo_node_fru_set(tn, fmri, 0, &err); 509 nvlist_free(fmri); 510 if (e < 0) 511 return (topo_mod_seterrno(mp, err)); 512 return (0); 513 } 514 515 tnode_t * 516 find_predecessor(tnode_t *tn, char *mod_name) 517 { 518 tnode_t *pnode = topo_node_parent(tn); 519 520 while (pnode && (strcmp(topo_node_name(pnode), mod_name) != 0)) { 521 pnode = topo_node_parent(pnode); 522 } 523 return (pnode); 524 } 525 526 static int 527 use_predecessor_fru(tnode_t *tn, char *mod_name) 528 { 529 tnode_t *pnode = NULL; 530 nvlist_t *fru = NULL; 531 int err = 0; 532 533 if ((pnode = find_predecessor(tn, mod_name)) == NULL) 534 return (-1); 535 if ((pnode = topo_node_parent(pnode)) == NULL) 536 return (-1); 537 if (topo_node_fru(pnode, &fru, NULL, &err) != 0) 538 return (-1); 539 540 (void) topo_node_fru_set(tn, fru, 0, &err); 541 nvlist_free(fru); 542 543 return (0); 544 } 545 546 static int 547 use_predecessor_label(topo_mod_t *mod, tnode_t *tn, char *mod_name) 548 { 549 tnode_t *pnode = NULL; 550 int err = 0; 551 char *plabel = NULL; 552 553 if ((pnode = find_predecessor(tn, mod_name)) == NULL) 554 return (-1); 555 if ((pnode = topo_node_parent(pnode)) == NULL) 556 return (-1); 557 if (topo_node_label(pnode, &plabel, &err) != 0 || plabel == NULL) 558 return (-1); 559 560 (void) topo_node_label_set(tn, plabel, &err); 561 562 topo_mod_strfree(mod, plabel); 563 564 return (0); 565 } 566 567 568 /*ARGSUSED*/ 569 static int 570 FRU_set(tnode_t *tn, did_t *pd, 571 const char *dpnm, const char *tpgrp, const char *tpnm) 572 { 573 topo_mod_t *mp; 574 char *nm; 575 int e = 0, err = 0; 576 577 nm = topo_node_name(tn); 578 mp = did_mod(pd); 579 580 /* 581 * If this is a PCIEX_BUS and its parent is a PCIEX_ROOT, 582 * check for a CPUBOARD predecessor. If found, inherit its 583 * parent's FRU. Otherwise, continue with FRU set. 584 */ 585 if ((strcmp(nm, PCIEX_BUS) == 0) && 586 (strcmp(topo_node_name(topo_node_parent(tn)), PCIEX_ROOT) == 0)) { 587 588 if (use_predecessor_fru(tn, CPUBOARD) == 0) 589 return (0); 590 } 591 /* 592 * If this topology node represents something other than an 593 * ioboard or a device that implements a slot, inherit the 594 * parent's FRU value. If there is no label, inherit our 595 * parent's FRU value. Otherwise, munge up an fmri based on 596 * the label. 597 */ 598 if (strcmp(nm, IOBOARD) != 0 && strcmp(nm, PCI_DEVICE) != 0 && 599 strcmp(nm, PCIEX_DEVICE) != 0 && strcmp(nm, PCIEX_BUS) != 0) { 600 (void) topo_node_fru_set(tn, NULL, 0, &e); 601 return (0); 602 } 603 604 /* 605 * If ioboard, set fru fmri to hc fmri 606 */ 607 if (strcmp(nm, IOBOARD) == 0) { 608 e = FRU_fmri_set(mp, tn); 609 return (e); 610 } else if (strcmp(nm, PCI_DEVICE) == 0 || 611 strcmp(nm, PCIEX_DEVICE) == 0 || strcmp(nm, PCIEX_BUS) == 0) { 612 nvlist_t *in, *out; 613 614 mp = did_mod(pd); 615 if (topo_mod_nvalloc(mp, &in, NV_UNIQUE_NAME) != 0) 616 return (topo_mod_seterrno(mp, EMOD_FMRI_NVL)); 617 if (nvlist_add_uint64(in, "nv1", (uintptr_t)pd) != 0) { 618 nvlist_free(in); 619 return (topo_mod_seterrno(mp, EMOD_NOMEM)); 620 } 621 if (topo_method_invoke(tn, 622 TOPO_METH_FRU_COMPUTE, TOPO_METH_FRU_COMPUTE_VERSION, 623 in, &out, &err) != 0) { 624 nvlist_free(in); 625 return (topo_mod_seterrno(mp, err)); 626 } 627 nvlist_free(in); 628 (void) topo_node_fru_set(tn, out, 0, &err); 629 nvlist_free(out); 630 } else 631 (void) topo_node_fru_set(tn, NULL, 0, &err); 632 633 return (0); 634 } 635 636 /*ARGSUSED*/ 637 static int 638 label_set(tnode_t *tn, did_t *pd, 639 const char *dpnm, const char *tpgrp, const char *tpnm) 640 { 641 topo_mod_t *mp; 642 nvlist_t *in, *out; 643 char *label; 644 int err; 645 646 mp = did_mod(pd); 647 /* 648 * If this is a PCIEX_BUS and its parent is a PCIEX_ROOT, 649 * check for a CPUBOARD predecessor. If found, inherit its 650 * parent's Label. Otherwise, continue with label set. 651 */ 652 if ((strcmp(topo_node_name(tn), PCIEX_BUS) == 0) && 653 (strcmp(topo_node_name(topo_node_parent(tn)), PCIEX_ROOT) == 0)) { 654 655 if (use_predecessor_label(mp, tn, CPUBOARD) == 0) 656 return (0); 657 } 658 if (topo_mod_nvalloc(mp, &in, NV_UNIQUE_NAME) != 0) 659 return (topo_mod_seterrno(mp, EMOD_FMRI_NVL)); 660 if (nvlist_add_uint64(in, TOPO_METH_LABEL_ARG_NVL, (uintptr_t)pd) != 661 0) { 662 nvlist_free(in); 663 return (topo_mod_seterrno(mp, EMOD_NOMEM)); 664 } 665 if (topo_method_invoke(tn, 666 TOPO_METH_LABEL, TOPO_METH_LABEL_VERSION, in, &out, &err) != 0) { 667 nvlist_free(in); 668 return (topo_mod_seterrno(mp, err)); 669 } 670 nvlist_free(in); 671 if (out != NULL && 672 nvlist_lookup_string(out, TOPO_METH_LABEL_RET_STR, &label) == 0) { 673 if (topo_prop_set_string(tn, TOPO_PGROUP_PROTOCOL, 674 TOPO_PROP_LABEL, TOPO_PROP_IMMUTABLE, label, &err) != 0) { 675 nvlist_free(out); 676 return (topo_mod_seterrno(mp, err)); 677 } 678 nvlist_free(out); 679 } 680 return (0); 681 } 682 683 /*ARGSUSED*/ 684 static int 685 EXCAP_set(tnode_t *tn, did_t *pd, 686 const char *dpnm, const char *tpgrp, const char *tpnm) 687 { 688 int excap = did_excap(pd); 689 int err; 690 int e = 0; 691 692 switch (excap & PCIE_PCIECAP_DEV_TYPE_MASK) { 693 case PCIE_PCIECAP_DEV_TYPE_ROOT: 694 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI, 695 TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err); 696 break; 697 case PCIE_PCIECAP_DEV_TYPE_UP: 698 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI, 699 TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_SWUP, &err); 700 break; 701 case PCIE_PCIECAP_DEV_TYPE_DOWN: 702 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI, 703 TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_SWDWN, &err); 704 break; 705 case PCIE_PCIECAP_DEV_TYPE_PCI2PCIE: 706 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI, 707 TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_BUS, &err); 708 break; 709 case PCIE_PCIECAP_DEV_TYPE_PCIE2PCI: 710 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI, 711 TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCI_BUS, &err); 712 break; 713 case PCIE_PCIECAP_DEV_TYPE_PCIE_DEV: 714 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI, 715 TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_DEVICE, &err); 716 break; 717 } 718 if (e != 0) 719 return (topo_mod_seterrno(did_mod(pd), err)); 720 return (0); 721 } 722 723 /*ARGSUSED*/ 724 static int 725 DEVprop_set(tnode_t *tn, did_t *pd, 726 const char *dpnm, const char *tpgrp, const char *tpnm) 727 { 728 topo_mod_t *mp; 729 char *dnpath; 730 char *path, *fpath; 731 int d, f; 732 int err, e; 733 734 mp = did_mod(pd); 735 if ((dnpath = di_devfs_path(did_dinode(pd))) == NULL) { 736 topo_mod_dprintf(mp, "NULL di_devfs_path.\n"); 737 return (topo_mod_seterrno(mp, ETOPO_PROP_NOENT)); 738 } 739 if ((path = topo_mod_strdup(mp, dnpath)) == NULL) { 740 di_devfs_path_free(dnpath); 741 return (-1); 742 } 743 di_devfs_path_free(dnpath); 744 745 /* The DEV path is modified for hostbridges */ 746 if (strcmp(topo_node_name(tn), HOSTBRIDGE) == 0) { 747 fpath = dev_for_hostbridge(did_mod(pd), path); 748 } else { 749 did_BDF(pd, NULL, &d, &f); 750 fpath = dev_path_fix(mp, path, d, f); 751 } 752 if (fpath == NULL) 753 return (-1); 754 e = topo_prop_set_string(tn, 755 tpgrp, tpnm, TOPO_PROP_IMMUTABLE, fpath, &err); 756 topo_mod_strfree(mp, fpath); 757 if (e != 0) 758 return (topo_mod_seterrno(mp, err)); 759 return (0); 760 } 761 762 /*ARGSUSED*/ 763 static int 764 DRIVERprop_set(tnode_t *tn, did_t *pd, 765 const char *dpnm, const char *tpgrp, const char *tpnm) 766 { 767 char *dnm; 768 int err; 769 770 if ((dnm = di_driver_name(did_dinode(pd))) == NULL) 771 return (0); 772 if (topo_prop_set_string(tn, 773 tpgrp, tpnm, TOPO_PROP_IMMUTABLE, dnm, &err) < 0) 774 return (topo_mod_seterrno(did_mod(pd), err)); 775 776 return (0); 777 } 778 779 /*ARGSUSED*/ 780 static int 781 MODULEprop_set(tnode_t *tn, did_t *pd, 782 const char *dpnm, const char *tpgrp, const char *tpnm) 783 { 784 nvlist_t *mod; 785 topo_mod_t *mp; 786 char *dnm; 787 int err; 788 789 if ((dnm = di_driver_name(did_dinode(pd))) == NULL) 790 return (0); 791 792 mp = did_mod(pd); 793 if ((mod = topo_mod_modfmri(mp, FM_MOD_SCHEME_VERSION, dnm)) == NULL) 794 return (0); /* driver maybe detached, return success */ 795 796 if (topo_prop_set_fmri(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE, mod, 797 &err) < 0) { 798 nvlist_free(mod); 799 return (topo_mod_seterrno(mp, err)); 800 } 801 nvlist_free(mod); 802 803 return (0); 804 } 805 806 /*ARGSUSED*/ 807 static int 808 maybe_di_chars_copy(tnode_t *tn, did_t *pd, 809 const char *dpnm, const char *tpgrp, const char *tpnm) 810 { 811 topo_mod_t *mp; 812 uchar_t *typbuf; 813 char *tmpbuf; 814 int sz = -1; 815 int err, e; 816 817 if (di_bytes_get(did_mod(pd), did_dinode(pd), dpnm, &sz, &typbuf) < 0) 818 return (0); 819 mp = did_mod(pd); 820 821 if ((tmpbuf = topo_mod_alloc(mp, sz + 1)) == NULL) 822 return (topo_mod_seterrno(mp, EMOD_NOMEM)); 823 824 bcopy(typbuf, tmpbuf, sz); 825 tmpbuf[sz] = 0; 826 e = topo_prop_set_string(tn, 827 tpgrp, tpnm, TOPO_PROP_IMMUTABLE, tmpbuf, &err); 828 topo_mod_free(mp, tmpbuf, sz + 1); 829 if (e != 0) 830 return (topo_mod_seterrno(mp, err)); 831 return (0); 832 } 833 834 static int 835 uint_to_strprop(topo_mod_t *mp, uint_t v, tnode_t *tn, 836 const char *tpgrp, const char *tpnm) 837 { 838 char str[21]; /* sizeof (UINT64_MAX) + '\0' */ 839 int e; 840 841 (void) snprintf(str, 21, "%x", v); 842 if (topo_prop_set_string(tn, 843 tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0) 844 return (topo_mod_seterrno(mp, e)); 845 return (0); 846 } 847 848 static int 849 maybe_di_uint_to_str(tnode_t *tn, did_t *pd, 850 const char *dpnm, const char *tpgrp, const char *tpnm) 851 { 852 uint_t v; 853 854 if (di_uintprop_get(did_mod(pd), did_dinode(pd), dpnm, &v) < 0) 855 return (0); 856 857 return (uint_to_strprop(did_mod(pd), v, tn, tpgrp, tpnm)); 858 } 859 860 static int 861 uint_to_dec_strprop(topo_mod_t *mp, uint_t v, tnode_t *tn, 862 const char *tpgrp, const char *tpnm) 863 { 864 char str[21]; /* sizeof (UINT64_MAX) + '\0' */ 865 int e; 866 867 (void) snprintf(str, 21, "%d", v); 868 if (topo_prop_set_string(tn, 869 tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0) 870 return (topo_mod_seterrno(mp, e)); 871 return (0); 872 } 873 874 static int 875 maybe_di_uint_to_dec_str(tnode_t *tn, did_t *pd, 876 const char *dpnm, const char *tpgrp, const char *tpnm) 877 { 878 uint_t v; 879 880 if (di_uintprop_get(did_mod(pd), did_dinode(pd), dpnm, &v) < 0) 881 return (0); 882 883 return (uint_to_dec_strprop(did_mod(pd), v, tn, tpgrp, tpnm)); 884 } 885 886 static int 887 AADDR_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp, 888 const char *tpnm) 889 { 890 topo_mod_t *mp; 891 uchar_t *typbuf; 892 int sz = -1; 893 int err, e; 894 895 if (di_bytes_get(did_mod(pd), did_dinode(pd), dpnm, &sz, &typbuf) < 0) 896 return (0); 897 898 mp = did_mod(pd); 899 900 e = topo_prop_set_uint32_array(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE, 901 /*LINTED*/ 902 (uint32_t *)typbuf, sz/4, &err); 903 904 if (e != 0) 905 return (topo_mod_seterrno(mp, err)); 906 return (0); 907 } 908 909 /*ARGSUSED*/ 910 static int 911 BDF_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp, 912 const char *tpnm) 913 { 914 int bdf; 915 char str[23]; /* '0x' + sizeof (UINT64_MAX) + '\0' */ 916 int e; 917 918 if ((bdf = did_bdf(pd)) <= 0) 919 return (0); 920 921 (void) snprintf(str, 23, "0x%x", bdf); 922 if (topo_prop_set_string(tn, 923 tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0) 924 return (topo_mod_seterrno(did_mod(pd), e)); 925 return (0); 926 } 927 928 /*ARGSUSED*/ 929 static int 930 maybe_pcidb_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp, 931 const char *tpnm) 932 { 933 const char *vname, *dname = NULL, *ssname = NULL; 934 uint_t vid, pid, svid, ssid; 935 pcidb_vendor_t *pciv; 936 pcidb_device_t *pcid; 937 pcidb_subvd_t *pcis = NULL; 938 pcidb_hdl_t *pcih; 939 topo_mod_t *mod = did_mod(pd); 940 int err; 941 942 /* 943 * At a minimum, we need the vid/devid of the device to be able to 944 * lookup anything in the PCI database. So if we fail to look either 945 * of those up, bail out. 946 */ 947 if (di_uintprop_get(did_mod(pd), did_dinode(pd), DI_VENDIDPROP, &vid) < 948 0 || di_uintprop_get(did_mod(pd), did_dinode(pd), DI_DEVIDPROP, 949 &pid) < 0) { 950 return (0); 951 } 952 /* 953 * If we fail to lookup the vendor, by the vid that's also a 954 * deal-breaker. 955 */ 956 if ((pcih = topo_mod_pcidb(mod)) == NULL || 957 (pciv = pcidb_lookup_vendor(pcih, vid)) == NULL) { 958 return (0); 959 } 960 961 /* lookup vendor-name and set the topo property, if found */ 962 vname = pcidb_vendor_name(pciv); 963 if (vname != NULL && 964 topo_prop_set_string(tn, tpgrp, TOPO_PCI_VENDNM, 965 TOPO_PROP_IMMUTABLE, vname, &err) != 0) { 966 return (topo_mod_seterrno(mod, err)); 967 } 968 969 /* lookup device-name and set the topo property, if found */ 970 if ((pcid = pcidb_lookup_device_by_vendor(pciv, pid)) != NULL) { 971 dname = pcidb_device_name(pcid); 972 } 973 if (dname != NULL && 974 topo_prop_set_string(tn, tpgrp, TOPO_PCI_DEVNM, 975 TOPO_PROP_IMMUTABLE, dname, &err) != 0) { 976 return (topo_mod_seterrno(mod, err)); 977 } 978 979 /* 980 * Not all devices will have a subsystem-name that we can lookup, 981 * but if both subsystem-vendorid and subsystem-id exist in devinfo and 982 * if we were previously able to find the device by devid then we can 983 * at least attempt a lookup. If found, set the topo property. 984 */ 985 if (pcid != NULL && 986 di_uintprop_get(did_mod(pd), did_dinode(pd), DI_SUBVENDIDPROP, 987 &svid) == 0 && 988 di_uintprop_get(did_mod(pd), did_dinode(pd), DI_SUBSYSTEMID, 989 &ssid) == 0) { 990 pcis = pcidb_lookup_subvd_by_device(pcid, svid, ssid); 991 } 992 if (pcis != NULL) { 993 ssname = pcidb_subvd_name(pcis); 994 } 995 if (ssname != NULL && strlen(ssname) > 0 && 996 topo_prop_set_string(tn, tpgrp, TOPO_PCI_SUBSYSNM, 997 TOPO_PROP_IMMUTABLE, ssname, &err) != 0) { 998 return (topo_mod_seterrno(mod, err)); 999 } 1000 return (0); 1001 } 1002 1003 int 1004 did_props_set(tnode_t *tn, did_t *pd, txprop_t txarray[], int txnum) 1005 { 1006 topo_mod_t *mp; 1007 int i, r, e; 1008 1009 mp = did_mod(pd); 1010 for (i = 0; i < txnum; i++) { 1011 /* 1012 * Ensure the property group has been created. 1013 */ 1014 if (txarray[i].tx_tpgroup != NULL) { 1015 if (topo_pgroup_create(tn, txarray[i].tx_tpgroup, &e) 1016 < 0) { 1017 if (e != ETOPO_PROP_DEFD) 1018 return (topo_mod_seterrno(mp, e)); 1019 } 1020 } 1021 1022 topo_mod_dprintf(mp, 1023 "Setting property %s in group %s.\n", 1024 txarray[i].tx_tprop, txarray[i].tx_tpgroup->tpi_name); 1025 r = txarray[i].tx_xlate(tn, pd, 1026 txarray[i].tx_diprop, txarray[i].tx_tpgroup->tpi_name, 1027 txarray[i].tx_tprop); 1028 if (r != 0) { 1029 topo_mod_dprintf(mp, "failed.\n"); 1030 topo_mod_dprintf(mp, "Error was %s.\n", 1031 topo_strerror(topo_mod_errno(mp))); 1032 return (-1); 1033 } 1034 topo_mod_dprintf(mp, "succeeded.\n"); 1035 } 1036 return (0); 1037 } 1038