xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/pcibus/did_props.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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