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