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 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 }
    950