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