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