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