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                 nvlist_free(out);
 616         } else
 617                 (void) topo_node_fru_set(tn, NULL, 0, &err);
 618 
 619         return (0);
 620 }
 621 
 622 /*ARGSUSED*/
 623 static int
 624 label_set(tnode_t *tn, did_t *pd,
 625     const char *dpnm, const char *tpgrp, const char *tpnm)
 626 {
 627         topo_mod_t *mp;
 628         nvlist_t *in, *out;
 629         char *label;
 630         int err;
 631 
 632         mp = did_mod(pd);
 633         /*
 634          * If this is a PCIEX_BUS and its parent is a PCIEX_ROOT,
 635          * check for a CPUBOARD predecessor.  If found, inherit its
 636          * parent's Label.  Otherwise, continue with label set.
 637          */
 638         if ((strcmp(topo_node_name(tn), PCIEX_BUS) == 0) &&
 639             (strcmp(topo_node_name(topo_node_parent(tn)), PCIEX_ROOT) == 0)) {
 640 
 641                 if (use_predecessor_label(mp, tn, CPUBOARD) == 0)
 642                         return (0);
 643         }
 644         if (topo_mod_nvalloc(mp, &in, NV_UNIQUE_NAME) != 0)
 645                 return (topo_mod_seterrno(mp, EMOD_FMRI_NVL));
 646         if (nvlist_add_uint64(in, TOPO_METH_LABEL_ARG_NVL, (uintptr_t)pd) !=
 647             0) {
 648                 nvlist_free(in);
 649                 return (topo_mod_seterrno(mp, EMOD_NOMEM));
 650         }
 651         if (topo_method_invoke(tn,
 652             TOPO_METH_LABEL, TOPO_METH_LABEL_VERSION, in, &out, &err) != 0) {
 653                 nvlist_free(in);
 654                 return (topo_mod_seterrno(mp, err));
 655         }
 656         nvlist_free(in);
 657         if (out != NULL &&
 658             nvlist_lookup_string(out, TOPO_METH_LABEL_RET_STR, &label) == 0) {
 659                 if (topo_prop_set_string(tn, TOPO_PGROUP_PROTOCOL,
 660                     TOPO_PROP_LABEL, TOPO_PROP_IMMUTABLE, label, &err) != 0) {
 661                         nvlist_free(out);
 662                         return (topo_mod_seterrno(mp, err));
 663                 }
 664                 nvlist_free(out);
 665         }
 666         return (0);
 667 }
 668 
 669 /*ARGSUSED*/
 670 static int
 671 EXCAP_set(tnode_t *tn, did_t *pd,
 672     const char *dpnm, const char *tpgrp, const char *tpnm)
 673 {
 674         int excap = did_excap(pd);
 675         int err;
 676         int e = 0;
 677 
 678         switch (excap & PCIE_PCIECAP_DEV_TYPE_MASK) {
 679         case PCIE_PCIECAP_DEV_TYPE_ROOT:
 680                 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
 681                     TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_ROOT, &err);
 682                 break;
 683         case PCIE_PCIECAP_DEV_TYPE_UP:
 684                 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
 685                     TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_SWUP, &err);
 686                 break;
 687         case PCIE_PCIECAP_DEV_TYPE_DOWN:
 688                 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
 689                     TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_SWDWN, &err);
 690                 break;
 691         case PCIE_PCIECAP_DEV_TYPE_PCI2PCIE:
 692                 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
 693                     TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_BUS, &err);
 694                 break;
 695         case PCIE_PCIECAP_DEV_TYPE_PCIE2PCI:
 696                 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
 697                     TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCI_BUS, &err);
 698                 break;
 699         case PCIE_PCIECAP_DEV_TYPE_PCIE_DEV:
 700                 e = topo_prop_set_string(tn, TOPO_PGROUP_PCI,
 701                     TOPO_PCI_EXCAP, TOPO_PROP_IMMUTABLE, PCIEX_DEVICE, &err);
 702                 break;
 703         }
 704         if (e != 0)
 705                 return (topo_mod_seterrno(did_mod(pd), err));
 706         return (0);
 707 }
 708 
 709 /*ARGSUSED*/
 710 static int
 711 DEVprop_set(tnode_t *tn, did_t *pd,
 712     const char *dpnm, const char *tpgrp, const char *tpnm)
 713 {
 714         topo_mod_t *mp;
 715         char *dnpath;
 716         char *path, *fpath;
 717         int d, f;
 718         int err, e;
 719 
 720         mp = did_mod(pd);
 721         if ((dnpath = di_devfs_path(did_dinode(pd))) == NULL) {
 722                 topo_mod_dprintf(mp, "NULL di_devfs_path.\n");
 723                 return (topo_mod_seterrno(mp, ETOPO_PROP_NOENT));
 724         }
 725         if ((path = topo_mod_strdup(mp, dnpath)) == NULL) {
 726                 di_devfs_path_free(dnpath);
 727                 return (-1);
 728         }
 729         di_devfs_path_free(dnpath);
 730 
 731         /* The DEV path is modified for hostbridges */
 732         if (strcmp(topo_node_name(tn), HOSTBRIDGE) == 0) {
 733                 fpath = dev_for_hostbridge(did_mod(pd), path);
 734         } else {
 735                 did_BDF(pd, NULL, &d, &f);
 736                 fpath = dev_path_fix(mp, path, d, f);
 737         }
 738         if (fpath == NULL)
 739                 return (-1);
 740         e = topo_prop_set_string(tn,
 741             tpgrp, tpnm, TOPO_PROP_IMMUTABLE, fpath, &err);
 742         topo_mod_strfree(mp, fpath);
 743         if (e != 0)
 744                 return (topo_mod_seterrno(mp, err));
 745         return (0);
 746 }
 747 
 748 /*ARGSUSED*/
 749 static int
 750 DRIVERprop_set(tnode_t *tn, did_t *pd,
 751     const char *dpnm, const char *tpgrp, const char *tpnm)
 752 {
 753         char *dnm;
 754         int err;
 755 
 756         if ((dnm = di_driver_name(did_dinode(pd))) == NULL)
 757                 return (0);
 758         if (topo_prop_set_string(tn,
 759             tpgrp, tpnm, TOPO_PROP_IMMUTABLE, dnm, &err) < 0)
 760                 return (topo_mod_seterrno(did_mod(pd), err));
 761 
 762         return (0);
 763 }
 764 
 765 /*ARGSUSED*/
 766 static int
 767 MODULEprop_set(tnode_t *tn, did_t *pd,
 768     const char *dpnm, const char *tpgrp, const char *tpnm)
 769 {
 770         nvlist_t *mod;
 771         topo_mod_t *mp;
 772         char *dnm;
 773         int err;
 774 
 775         if ((dnm = di_driver_name(did_dinode(pd))) == NULL)
 776                 return (0);
 777 
 778         mp = did_mod(pd);
 779         if ((mod = topo_mod_modfmri(mp, FM_MOD_SCHEME_VERSION, dnm)) == NULL)
 780                 return (0); /* driver maybe detached, return success */
 781 
 782         if (topo_prop_set_fmri(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE, mod,
 783             &err) < 0) {
 784                 nvlist_free(mod);
 785                 return (topo_mod_seterrno(mp, err));
 786         }
 787         nvlist_free(mod);
 788 
 789         return (0);
 790 }
 791 
 792 /*ARGSUSED*/
 793 static int
 794 maybe_di_chars_copy(tnode_t *tn, did_t *pd,
 795     const char *dpnm, const char *tpgrp, const char *tpnm)
 796 {
 797         topo_mod_t *mp;
 798         uchar_t *typbuf;
 799         char *tmpbuf;
 800         int sz = -1;
 801         int err, e;
 802 
 803         if (di_bytes_get(did_mod(pd), did_dinode(pd), dpnm, &sz, &typbuf) < 0)
 804                 return (0);
 805         mp = did_mod(pd);
 806 
 807         if ((tmpbuf = topo_mod_alloc(mp, sz + 1)) == NULL)
 808                 return (topo_mod_seterrno(mp, EMOD_NOMEM));
 809 
 810         bcopy(typbuf, tmpbuf, sz);
 811         tmpbuf[sz] = 0;
 812         e = topo_prop_set_string(tn,
 813             tpgrp, tpnm, TOPO_PROP_IMMUTABLE, tmpbuf, &err);
 814         topo_mod_free(mp, tmpbuf, sz + 1);
 815         if (e != 0)
 816                 return (topo_mod_seterrno(mp, err));
 817         return (0);
 818 }
 819 
 820 static int
 821 uint_to_strprop(topo_mod_t *mp, uint_t v, tnode_t *tn,
 822     const char *tpgrp, const char *tpnm)
 823 {
 824         char str[21]; /* sizeof (UINT64_MAX) + '\0' */
 825         int e;
 826 
 827         (void) snprintf(str, 21, "%x", v);
 828         if (topo_prop_set_string(tn,
 829             tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0)
 830                 return (topo_mod_seterrno(mp, e));
 831         return (0);
 832 }
 833 
 834 static int
 835 maybe_di_uint_to_str(tnode_t *tn, did_t *pd,
 836     const char *dpnm, const char *tpgrp, const char *tpnm)
 837 {
 838         uint_t v;
 839 
 840         if (di_uintprop_get(did_mod(pd), did_dinode(pd), dpnm, &v) < 0)
 841                 return (0);
 842 
 843         return (uint_to_strprop(did_mod(pd), v, tn, tpgrp, tpnm));
 844 }
 845 
 846 static int
 847 uint_to_dec_strprop(topo_mod_t *mp, uint_t v, tnode_t *tn,
 848     const char *tpgrp, const char *tpnm)
 849 {
 850         char str[21]; /* sizeof (UINT64_MAX) + '\0' */
 851         int e;
 852 
 853         (void) snprintf(str, 21, "%d", v);
 854         if (topo_prop_set_string(tn,
 855             tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0)
 856                 return (topo_mod_seterrno(mp, e));
 857         return (0);
 858 }
 859 
 860 static int
 861 maybe_di_uint_to_dec_str(tnode_t *tn, did_t *pd,
 862     const char *dpnm, const char *tpgrp, const char *tpnm)
 863 {
 864         uint_t v;
 865 
 866         if (di_uintprop_get(did_mod(pd), did_dinode(pd), dpnm, &v) < 0)
 867                 return (0);
 868 
 869         return (uint_to_dec_strprop(did_mod(pd), v, tn, tpgrp, tpnm));
 870 }
 871 
 872 static int
 873 AADDR_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp,
 874     const char *tpnm)
 875 {
 876         topo_mod_t *mp;
 877         uchar_t *typbuf;
 878         int sz = -1;
 879         int err, e;
 880 
 881         if (di_bytes_get(did_mod(pd), did_dinode(pd), dpnm, &sz, &typbuf) < 0)
 882                 return (0);
 883 
 884         mp = did_mod(pd);
 885 
 886         e = topo_prop_set_uint32_array(tn, tpgrp, tpnm, TOPO_PROP_IMMUTABLE,
 887             /*LINTED*/
 888             (uint32_t *)typbuf, sz/4, &err);
 889 
 890         if (e != 0)
 891                 return (topo_mod_seterrno(mp, err));
 892         return (0);
 893 }
 894 
 895 /*ARGSUSED*/
 896 static int
 897 BDF_set(tnode_t *tn, did_t *pd, const char *dpnm, const char *tpgrp,
 898     const char *tpnm)
 899 {
 900         int bdf;
 901         char str[23]; /* '0x' + sizeof (UINT64_MAX) + '\0' */
 902         int e;
 903 
 904         if ((bdf = did_bdf(pd)) <= 0)
 905                 return (0);
 906 
 907         (void) snprintf(str, 23, "0x%x", bdf);
 908         if (topo_prop_set_string(tn,
 909             tpgrp, tpnm, TOPO_PROP_IMMUTABLE, str, &e) < 0)
 910                 return (topo_mod_seterrno(did_mod(pd), e));
 911         return (0);
 912 }
 913 
 914 int
 915 did_props_set(tnode_t *tn, did_t *pd, txprop_t txarray[], int txnum)
 916 {
 917         topo_mod_t *mp;
 918         int i, r, e;
 919 
 920         mp = did_mod(pd);
 921         for (i = 0; i < txnum; i++) {
 922                 /*
 923                  * Ensure the property group has been created.
 924                  */
 925                 if (txarray[i].tx_tpgroup != NULL) {
 926                         if (topo_pgroup_create(tn, txarray[i].tx_tpgroup, &e)
 927                             < 0) {
 928                                 if (e != ETOPO_PROP_DEFD)
 929                                         return (topo_mod_seterrno(mp, e));
 930                         }
 931                 }
 932 
 933                 topo_mod_dprintf(mp,
 934                     "Setting property %s in group %s.\n",
 935                     txarray[i].tx_tprop, txarray[i].tx_tpgroup->tpi_name);
 936                 r = txarray[i].tx_xlate(tn, pd,
 937                     txarray[i].tx_diprop, txarray[i].tx_tpgroup->tpi_name,
 938                     txarray[i].tx_tprop);
 939                 if (r != 0) {
 940                         topo_mod_dprintf(mp, "failed.\n");
 941                         topo_mod_dprintf(mp, "Error was %s.\n",
 942                             topo_strerror(topo_mod_errno(mp)));
 943                         return (-1);
 944                 }
 945                 topo_mod_dprintf(mp, "succeeded.\n");
 946         }
 947         return (0);
 948 }