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