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