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