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