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