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