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