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