xref: /illumos-gate/usr/src/cmd/pcieadm/pcieadm_devs.c (revision ecd18decdfb3e65c221849c385545953b84f1301)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2023 Oxide Computer Company
14  */
15 
16 #include <err.h>
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <ofmt.h>
20 #include <strings.h>
21 #include <sys/pci.h>
22 
23 #include "pcieadm.h"
24 
25 typedef struct pcieadm_show_devs {
26 	pcieadm_t *psd_pia;
27 	ofmt_handle_t psd_ofmt;
28 	boolean_t psd_funcs;
29 	int psd_nfilts;
30 	char **psd_filts;
31 	boolean_t *psd_used;
32 	uint_t psd_nprint;
33 } pcieadm_show_devs_t;
34 
35 typedef enum pcieadm_show_devs_otype {
36 	PCIEADM_SDO_VID,
37 	PCIEADM_SDO_DID,
38 	PCIEADM_SDO_REV,
39 	PCIEADM_SDO_SUBVID,
40 	PCIEADM_SDO_SUBSYS,
41 	PCIEADM_SDO_BDF,
42 	PCIEADM_SDO_BDF_BUS,
43 	PCIEADM_SDO_BDF_DEV,
44 	PCIEADM_SDO_BDF_FUNC,
45 	PCIEADM_SDO_DRIVER,
46 	PCIEADM_SDO_INSTANCE,
47 	PCIEADM_SDO_INSTNUM,
48 	PCIEADM_SDO_TYPE,
49 	PCIEADM_SDO_VENDOR,
50 	PCIEADM_SDO_DEVICE,
51 	PCIEADM_SDO_SUBVENDOR,
52 	PCIEADM_SDO_SUBSYSTEM,
53 	PCIEADM_SDO_PATH,
54 	PCIEADM_SDO_MAXSPEED,
55 	PCIEADM_SDO_MAXWIDTH,
56 	PCIEADM_SDO_CURSPEED,
57 	PCIEADM_SDO_CURWIDTH,
58 	PCIEADM_SDO_SUPSPEEDS
59 } pcieadm_show_devs_otype_t;
60 
61 typedef struct pcieadm_show_devs_ofmt {
62 	int psdo_vid;
63 	int psdo_did;
64 	int psdo_rev;
65 	int psdo_subvid;
66 	int psdo_subsys;
67 	uint_t psdo_bus;
68 	uint_t psdo_dev;
69 	uint_t psdo_func;
70 	const char *psdo_path;
71 	const char *psdo_vendor;
72 	const char *psdo_device;
73 	const char *psdo_subvendor;
74 	const char *psdo_subsystem;
75 	const char *psdo_driver;
76 	int psdo_instance;
77 	int psdo_mwidth;
78 	int psdo_cwidth;
79 	int64_t psdo_mspeed;
80 	int64_t psdo_cspeed;
81 	int psdo_nspeeds;
82 	int64_t *psdo_sspeeds;
83 } pcieadm_show_devs_ofmt_t;
84 
85 static uint_t
86 pcieadm_speed2gen(int64_t speed)
87 {
88 	if (speed == 2500000000LL) {
89 		return (1);
90 	} else if (speed == 5000000000LL) {
91 		return (2);
92 	} else if (speed == 8000000000LL) {
93 		return (3);
94 	} else if (speed == 16000000000LL) {
95 		return (4);
96 	} else if (speed == 32000000000LL) {
97 		return (5);
98 	} else {
99 		return (0);
100 	}
101 }
102 
103 static const char *
104 pcieadm_speed2str(int64_t speed)
105 {
106 	if (speed == 2500000000LL) {
107 		return ("2.5");
108 	} else if (speed == 5000000000LL) {
109 		return ("5.0");
110 	} else if (speed == 8000000000LL) {
111 		return ("8.0");
112 	} else if (speed == 16000000000LL) {
113 		return ("16.0");
114 	} else if (speed == 32000000000LL) {
115 		return ("32.0");
116 	} else {
117 		return (NULL);
118 	}
119 }
120 
121 static boolean_t
122 pcieadm_show_devs_ofmt_cb(ofmt_arg_t *ofarg, char *buf, uint_t buflen)
123 {
124 	const char *str;
125 	pcieadm_show_devs_ofmt_t *psdo = ofarg->ofmt_cbarg;
126 	boolean_t first = B_TRUE;
127 
128 	switch (ofarg->ofmt_id) {
129 	case PCIEADM_SDO_BDF:
130 		if (snprintf(buf, buflen, "%x/%x/%x", psdo->psdo_bus,
131 		    psdo->psdo_dev, psdo->psdo_func) >= buflen) {
132 			return (B_FALSE);
133 		}
134 		break;
135 	case PCIEADM_SDO_BDF_BUS:
136 		if (snprintf(buf, buflen, "%x", psdo->psdo_bus) >= buflen) {
137 			return (B_FALSE);
138 		}
139 		break;
140 	case PCIEADM_SDO_BDF_DEV:
141 		if (snprintf(buf, buflen, "%x", psdo->psdo_dev) >= buflen) {
142 			return (B_FALSE);
143 		}
144 		break;
145 	case PCIEADM_SDO_BDF_FUNC:
146 		if (snprintf(buf, buflen, "%x", psdo->psdo_func) >= buflen) {
147 			return (B_FALSE);
148 		}
149 		break;
150 	case PCIEADM_SDO_INSTANCE:
151 		if (psdo->psdo_driver == NULL || psdo->psdo_instance == -1) {
152 			(void) snprintf(buf, buflen, "--");
153 		} else if (snprintf(buf, buflen, "%s%d", psdo->psdo_driver,
154 		    psdo->psdo_instance) >= buflen) {
155 			return (B_FALSE);
156 		}
157 		break;
158 	case PCIEADM_SDO_DRIVER:
159 		if (psdo->psdo_driver == NULL) {
160 			(void) snprintf(buf, buflen, "--");
161 		} else if (strlcpy(buf, psdo->psdo_driver, buflen) >= buflen) {
162 			return (B_FALSE);
163 		}
164 		break;
165 	case PCIEADM_SDO_INSTNUM:
166 		if (psdo->psdo_instance == -1) {
167 			(void) snprintf(buf, buflen, "--");
168 		} else if (snprintf(buf, buflen, "%d", psdo->psdo_instance) >=
169 		    buflen) {
170 			return (B_FALSE);
171 		}
172 		break;
173 	case PCIEADM_SDO_PATH:
174 		if (strlcat(buf, psdo->psdo_path, buflen) >= buflen) {
175 			return (B_TRUE);
176 		}
177 		break;
178 	case PCIEADM_SDO_VID:
179 		if (psdo->psdo_vid == -1) {
180 			(void) strlcat(buf, "--", buflen);
181 		} else if (snprintf(buf, buflen, "%x", psdo->psdo_vid) >=
182 		    buflen) {
183 			return (B_FALSE);
184 		}
185 		break;
186 	case PCIEADM_SDO_DID:
187 		if (psdo->psdo_did == -1) {
188 			(void) strlcat(buf, "--", buflen);
189 		} else if (snprintf(buf, buflen, "%x", psdo->psdo_did) >=
190 		    buflen) {
191 			return (B_FALSE);
192 		}
193 		break;
194 	case PCIEADM_SDO_REV:
195 		if (psdo->psdo_rev == -1) {
196 			(void) strlcat(buf, "--", buflen);
197 		} else if (snprintf(buf, buflen, "%x", psdo->psdo_rev) >=
198 		    buflen) {
199 			return (B_FALSE);
200 		}
201 		break;
202 	case PCIEADM_SDO_SUBVID:
203 		if (psdo->psdo_subvid == -1) {
204 			(void) strlcat(buf, "--", buflen);
205 		} else if (snprintf(buf, buflen, "%x", psdo->psdo_subvid) >=
206 		    buflen) {
207 			return (B_FALSE);
208 		}
209 		break;
210 	case PCIEADM_SDO_SUBSYS:
211 		if (psdo->psdo_subsys == -1) {
212 			(void) strlcat(buf, "--", buflen);
213 		} else if (snprintf(buf, buflen, "%x", psdo->psdo_subsys) >=
214 		    buflen) {
215 			return (B_FALSE);
216 		}
217 		break;
218 	case PCIEADM_SDO_VENDOR:
219 		if (strlcat(buf, psdo->psdo_vendor, buflen) >= buflen) {
220 			return (B_FALSE);
221 		}
222 		break;
223 	case PCIEADM_SDO_DEVICE:
224 		if (strlcat(buf, psdo->psdo_device, buflen) >= buflen) {
225 			return (B_FALSE);
226 		}
227 		break;
228 	case PCIEADM_SDO_SUBVENDOR:
229 		if (strlcat(buf, psdo->psdo_subvendor, buflen) >= buflen) {
230 			return (B_FALSE);
231 		}
232 		break;
233 	case PCIEADM_SDO_SUBSYSTEM:
234 		if (strlcat(buf, psdo->psdo_subsystem, buflen) >= buflen) {
235 			return (B_FALSE);
236 		}
237 		break;
238 	case PCIEADM_SDO_MAXWIDTH:
239 		if (psdo->psdo_mwidth <= 0) {
240 			(void) strlcat(buf, "--", buflen);
241 		} else if (snprintf(buf, buflen, "x%u", psdo->psdo_mwidth) >=
242 		    buflen) {
243 			return (B_FALSE);
244 		}
245 		break;
246 	case PCIEADM_SDO_CURWIDTH:
247 		if (psdo->psdo_cwidth <= 0) {
248 			(void) strlcat(buf, "--", buflen);
249 		} else if (snprintf(buf, buflen, "x%u", psdo->psdo_cwidth) >=
250 		    buflen) {
251 			return (B_FALSE);
252 		}
253 		break;
254 	case PCIEADM_SDO_MAXSPEED:
255 		str = pcieadm_speed2str(psdo->psdo_mspeed);
256 		if (str == NULL) {
257 			(void) strlcat(buf, "--", buflen);
258 		} else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) {
259 			return (B_FALSE);
260 		}
261 		break;
262 	case PCIEADM_SDO_CURSPEED:
263 		str = pcieadm_speed2str(psdo->psdo_cspeed);
264 		if (str == NULL) {
265 			(void) strlcat(buf, "--", buflen);
266 		} else if (snprintf(buf, buflen, "%s GT/s", str) >= buflen) {
267 			return (B_FALSE);
268 		}
269 		break;
270 	case PCIEADM_SDO_SUPSPEEDS:
271 		buf[0] = 0;
272 		for (int i = 0; i < psdo->psdo_nspeeds; i++) {
273 			const char *str;
274 
275 			str = pcieadm_speed2str(psdo->psdo_sspeeds[i]);
276 			if (str == NULL) {
277 				continue;
278 			}
279 
280 			if (!first) {
281 				if (strlcat(buf, ",", buflen) >= buflen) {
282 					return (B_FALSE);
283 				}
284 			}
285 			first = B_FALSE;
286 
287 			if (strlcat(buf, str, buflen) >= buflen) {
288 				return (B_FALSE);
289 			}
290 		}
291 		break;
292 	case PCIEADM_SDO_TYPE:
293 		if (pcieadm_speed2gen(psdo->psdo_cspeed) == 0 ||
294 		    psdo->psdo_mwidth == -1) {
295 			if (strlcat(buf, "PCI", buflen) >= buflen) {
296 				return (B_FALSE);
297 			}
298 		} else {
299 			if (snprintf(buf, buflen, "PCIe Gen %ux%u",
300 			    pcieadm_speed2gen(psdo->psdo_cspeed),
301 			    psdo->psdo_cwidth) >= buflen) {
302 				return (B_FALSE);
303 			}
304 		}
305 		break;
306 	default:
307 		abort();
308 	}
309 	return (B_TRUE);
310 }
311 
312 static const char *pcieadm_show_dev_fields = "bdf,type,instance,device";
313 static const char *pcieadm_show_dev_speeds =
314 	"bdf,driver,maxspeed,curspeed,maxwidth,curwidth,supspeeds";
315 static const ofmt_field_t pcieadm_show_dev_ofmt[] = {
316 	{ "VID", 6, PCIEADM_SDO_VID, pcieadm_show_devs_ofmt_cb },
317 	{ "DID", 6, PCIEADM_SDO_DID, pcieadm_show_devs_ofmt_cb },
318 	{ "REV", 6, PCIEADM_SDO_REV, pcieadm_show_devs_ofmt_cb },
319 	{ "SUBVID", 6, PCIEADM_SDO_SUBVID, pcieadm_show_devs_ofmt_cb },
320 	{ "SUBSYS", 6, PCIEADM_SDO_SUBSYS, pcieadm_show_devs_ofmt_cb },
321 	{ "BDF", 8, PCIEADM_SDO_BDF, pcieadm_show_devs_ofmt_cb },
322 	{ "DRIVER", 15, PCIEADM_SDO_DRIVER, pcieadm_show_devs_ofmt_cb },
323 	{ "INSTANCE", 15, PCIEADM_SDO_INSTANCE, pcieadm_show_devs_ofmt_cb },
324 	{ "INSTNUM", 8, PCIEADM_SDO_INSTNUM, pcieadm_show_devs_ofmt_cb },
325 	{ "TYPE", 15, PCIEADM_SDO_TYPE, pcieadm_show_devs_ofmt_cb },
326 	{ "VENDOR", 30, PCIEADM_SDO_VENDOR, pcieadm_show_devs_ofmt_cb },
327 	{ "DEVICE", 30, PCIEADM_SDO_DEVICE, pcieadm_show_devs_ofmt_cb },
328 	{ "SUBVENDOR", 30, PCIEADM_SDO_SUBVENDOR, pcieadm_show_devs_ofmt_cb },
329 	{ "SUBSYSTEM", 30, PCIEADM_SDO_SUBSYSTEM, pcieadm_show_devs_ofmt_cb },
330 	{ "PATH", 30, PCIEADM_SDO_PATH, pcieadm_show_devs_ofmt_cb },
331 	{ "BUS", 4, PCIEADM_SDO_BDF_BUS, pcieadm_show_devs_ofmt_cb },
332 	{ "DEV", 4, PCIEADM_SDO_BDF_DEV, pcieadm_show_devs_ofmt_cb },
333 	{ "FUNC", 4, PCIEADM_SDO_BDF_FUNC, pcieadm_show_devs_ofmt_cb },
334 	{ "MAXSPEED", 10, PCIEADM_SDO_MAXSPEED, pcieadm_show_devs_ofmt_cb },
335 	{ "MAXWIDTH", 10, PCIEADM_SDO_MAXWIDTH, pcieadm_show_devs_ofmt_cb },
336 	{ "CURSPEED", 10, PCIEADM_SDO_CURSPEED, pcieadm_show_devs_ofmt_cb },
337 	{ "CURWIDTH", 10, PCIEADM_SDO_CURWIDTH, pcieadm_show_devs_ofmt_cb },
338 	{ "SUPSPEEDS", 20, PCIEADM_SDO_SUPSPEEDS, pcieadm_show_devs_ofmt_cb },
339 	{ NULL, 0, 0, NULL }
340 };
341 
342 static boolean_t
343 pcieadm_show_devs_match(pcieadm_show_devs_t *psd,
344     pcieadm_show_devs_ofmt_t *psdo)
345 {
346 	char dinst[128], bdf[128];
347 
348 	if (psd->psd_nfilts == 0) {
349 		return (B_TRUE);
350 	}
351 
352 	if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1) {
353 		(void) snprintf(dinst, sizeof (dinst), "%s%d",
354 		    psdo->psdo_driver, psdo->psdo_instance);
355 	}
356 	(void) snprintf(bdf, sizeof (bdf), "%x/%x/%x", psdo->psdo_bus,
357 	    psdo->psdo_dev, psdo->psdo_func);
358 
359 	for (uint_t i = 0; i < psd->psd_nfilts; i++) {
360 		const char *filt = psd->psd_filts[i];
361 
362 		if (strcmp(filt, psdo->psdo_path) == 0) {
363 			psd->psd_used[i] = B_TRUE;
364 			return (B_TRUE);
365 		}
366 
367 		if (strcmp(filt, bdf) == 0) {
368 			psd->psd_used[i] = B_TRUE;
369 			return (B_TRUE);
370 		}
371 
372 		if (psdo->psdo_driver != NULL &&
373 		    strcmp(filt, psdo->psdo_driver) == 0) {
374 			psd->psd_used[i] = B_TRUE;
375 			return (B_TRUE);
376 		}
377 
378 		if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1 &&
379 		    strcmp(filt, dinst) == 0) {
380 			psd->psd_used[i] = B_TRUE;
381 			return (B_TRUE);
382 		}
383 
384 		if (strncmp("/devices", filt, strlen("/devices")) == 0) {
385 			filt += strlen("/devices");
386 		}
387 
388 		if (strcmp(filt, psdo->psdo_path) == 0) {
389 			psd->psd_used[i] = B_TRUE;
390 			return (B_TRUE);
391 		}
392 	}
393 	return (B_FALSE);
394 }
395 
396 static int
397 pcieadm_show_devs_walk_cb(di_node_t node, void *arg)
398 {
399 	int nprop, *regs = NULL, *did, *vid, *mwidth, *cwidth, *rev;
400 	int *subvid, *subsys;
401 	int64_t *mspeed, *cspeed, *sspeeds;
402 	char *path = NULL;
403 	pcieadm_show_devs_t *psd = arg;
404 	int ret = DI_WALK_CONTINUE;
405 	char venstr[64], devstr[64], subvenstr[64], subsysstr[64];
406 	pcieadm_show_devs_ofmt_t oarg;
407 	pcidb_hdl_t *pcidb = psd->psd_pia->pia_pcidb;
408 
409 	bzero(&oarg, sizeof (oarg));
410 
411 	path = di_devfs_path(node);
412 	if (path == NULL) {
413 		err(EXIT_FAILURE, "failed to construct devfs path for node: "
414 		    "%s", di_node_name(node));
415 	}
416 
417 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regs);
418 	if (nprop <= 0) {
419 		errx(EXIT_FAILURE, "failed to lookup regs array for %s",
420 		    path);
421 	}
422 
423 	oarg.psdo_path = path;
424 	oarg.psdo_bus = PCI_REG_BUS_G(regs[0]);
425 	oarg.psdo_dev = PCI_REG_DEV_G(regs[0]);
426 	oarg.psdo_func = PCI_REG_FUNC_G(regs[0]);
427 
428 	if (oarg.psdo_func != 0 && !psd->psd_funcs) {
429 		goto done;
430 	}
431 
432 	oarg.psdo_driver = di_driver_name(node);
433 	oarg.psdo_instance = di_instance(node);
434 
435 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did);
436 	if (nprop != 1) {
437 		oarg.psdo_did = -1;
438 	} else {
439 		oarg.psdo_did = (uint16_t)*did;
440 	}
441 
442 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid);
443 	if (nprop != 1) {
444 		oarg.psdo_vid = -1;
445 	} else {
446 		oarg.psdo_vid = (uint16_t)*vid;
447 	}
448 
449 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "revision-id", &rev);
450 	if (nprop != 1) {
451 		oarg.psdo_rev = -1;
452 	} else {
453 		oarg.psdo_rev = (uint16_t)*rev;
454 	}
455 
456 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-vendor-id",
457 	    &subvid);
458 	if (nprop != 1) {
459 		oarg.psdo_subvid = -1;
460 	} else {
461 		oarg.psdo_subvid = (uint16_t)*subvid;
462 	}
463 
464 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-id",
465 	    &subsys);
466 	if (nprop != 1) {
467 		oarg.psdo_subsys = -1;
468 	} else {
469 		oarg.psdo_subsys = (uint16_t)*subsys;
470 	}
471 
472 	oarg.psdo_vendor = "--";
473 	if (oarg.psdo_vid != -1) {
474 		pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb,
475 		    oarg.psdo_vid);
476 		if (vend != NULL) {
477 			oarg.psdo_vendor = pcidb_vendor_name(vend);
478 		} else {
479 			(void) snprintf(venstr, sizeof (venstr),
480 			    "Unknown vendor: 0x%x", oarg.psdo_vid);
481 			oarg.psdo_vendor = venstr;
482 		}
483 	}
484 
485 	oarg.psdo_device = "--";
486 	if (oarg.psdo_vid != -1 && oarg.psdo_did != -1) {
487 		pcidb_device_t *dev = pcidb_lookup_device(pcidb,
488 		    oarg.psdo_vid, oarg.psdo_did);
489 		if (dev != NULL) {
490 			oarg.psdo_device = pcidb_device_name(dev);
491 		} else {
492 			(void) snprintf(devstr, sizeof (devstr),
493 			    "Unknown device: 0x%x", oarg.psdo_did);
494 			oarg.psdo_device = devstr;
495 		}
496 	}
497 
498 	/*
499 	 * The pci.ids database organizes subsystems under devices. We look at
500 	 * the subsystem vendor separately because even if the device or other
501 	 * information is not known, we may still be able to figure out what it
502 	 * is.
503 	 */
504 	oarg.psdo_subvendor = "--";
505 	oarg.psdo_subsystem = "--";
506 	if (oarg.psdo_subvid != -1) {
507 		pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb,
508 		    oarg.psdo_subvid);
509 		if (vend != NULL) {
510 			oarg.psdo_subvendor = pcidb_vendor_name(vend);
511 		} else {
512 			(void) snprintf(subvenstr, sizeof (subvenstr),
513 			    "Unknown vendor: 0x%x", oarg.psdo_vid);
514 			oarg.psdo_subvendor = subvenstr;
515 		}
516 	}
517 
518 	if (oarg.psdo_vid != -1 && oarg.psdo_did != -1 &&
519 	    oarg.psdo_subvid != -1 && oarg.psdo_subsys != -1) {
520 		pcidb_subvd_t *subvd = pcidb_lookup_subvd(pcidb, oarg.psdo_vid,
521 		    oarg.psdo_did, oarg.psdo_subvid, oarg.psdo_subsys);
522 		if (subvd != NULL) {
523 			oarg.psdo_subsystem = pcidb_subvd_name(subvd);
524 		} else {
525 			(void) snprintf(subsysstr, sizeof (subsysstr),
526 			    "Unknown subsystem: 0x%x", oarg.psdo_subsys);
527 			oarg.psdo_subsystem = subsysstr;
528 		}
529 	}
530 
531 
532 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
533 	    "pcie-link-maximum-width", &mwidth);
534 	if (nprop != 1) {
535 		oarg.psdo_mwidth = -1;
536 	} else {
537 		oarg.psdo_mwidth = *mwidth;
538 	}
539 
540 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
541 	    "pcie-link-current-width", &cwidth);
542 	if (nprop != 1) {
543 		oarg.psdo_cwidth = -1;
544 	} else {
545 		oarg.psdo_cwidth = *cwidth;
546 	}
547 
548 	nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
549 	    "pcie-link-maximum-speed", &mspeed);
550 	if (nprop != 1) {
551 		oarg.psdo_mspeed = -1;
552 	} else {
553 		oarg.psdo_mspeed = *mspeed;
554 	}
555 
556 	nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
557 	    "pcie-link-current-speed", &cspeed);
558 	if (nprop != 1) {
559 		oarg.psdo_cspeed = -1;
560 	} else {
561 		oarg.psdo_cspeed = *cspeed;
562 	}
563 
564 	nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
565 	    "pcie-link-supported-speeds", &sspeeds);
566 	if (nprop > 0) {
567 		oarg.psdo_nspeeds = nprop;
568 		oarg.psdo_sspeeds = sspeeds;
569 	} else {
570 		oarg.psdo_nspeeds = 0;
571 		oarg.psdo_sspeeds = NULL;
572 	}
573 
574 	if (pcieadm_show_devs_match(psd, &oarg)) {
575 		ofmt_print(psd->psd_ofmt, &oarg);
576 		psd->psd_nprint++;
577 	}
578 
579 done:
580 	if (path != NULL) {
581 		di_devfs_path_free(path);
582 	}
583 
584 	return (ret);
585 }
586 
587 void
588 pcieadm_show_devs_usage(FILE *f)
589 {
590 	(void) fprintf(f, "\tshow-devs\t[-F] [-H] [-s | -o field[,...] [-p]] "
591 	    "[filter...]\n");
592 }
593 
594 static void
595 pcieadm_show_devs_help(const char *fmt, ...)
596 {
597 	if (fmt != NULL) {
598 		va_list ap;
599 
600 		va_start(ap, fmt);
601 		vwarnx(fmt, ap);
602 		va_end(ap);
603 		(void) fprintf(stderr, "\n");
604 	}
605 
606 	(void) fprintf(stderr, "Usage:  %s show-devs [-F] [-H] [-s | -o "
607 	    "field[,...] [-p]] [filter...]\n", pcieadm_progname);
608 
609 	(void) fprintf(stderr, "\nList PCI devices and functions in the "
610 	    "system. Each <filter> selects a set\nof devices to show and "
611 	    "can be a driver name, instance, /devices path, or\nb/d/f.\n\n"
612 	    "\t-F\t\tdo not display PCI functions\n"
613 	    "\t-H\t\tomit the column header\n"
614 	    "\t-o field\toutput fields to print\n"
615 	    "\t-p\t\tparsable output (requires -o)\n"
616 	    "\t-s\t\tlist speeds and widths\n\n"
617 	    "The following fields are supported:\n"
618 	    "\tvid\t\tthe PCI vendor ID in hex\n"
619 	    "\tdid\t\tthe PCI device ID in hex\n"
620 	    "\trev\t\tthe PCI device revision in hex\n"
621 	    "\tsubvid\t\tthe PCI subsystem vendor ID in hex\n"
622 	    "\tsubsys\t\tthe PCI subsystem ID in hex\n"
623 	    "\tvendor\t\tthe name of the PCI vendor\n"
624 	    "\tdevice\t\tthe name of the PCI device\n"
625 	    "\tsubvendor\t\tthe name of the PCI subsystem vendor\n"
626 	    "\tsubsystem\t\tthe name of the PCI subsystem\n"
627 	    "\tinstance\tthe name of this particular instance, e.g. igb0\n"
628 	    "\tdriver\t\tthe name of the driver attached to the device\n"
629 	    "\tinstnum\t\tthe instance number of a device, e.g. 2 for nvme2\n"
630 	    "\tpath\t\tthe /devices path of the device\n"
631 	    "\tbdf\t\tthe PCI bus/device/function, with values in hex\n"
632 	    "\tbus\t\tthe PCI bus number of the device in hex\n"
633 	    "\tdev\t\tthe PCI device number of the device in hex\n"
634 	    "\tfunc\t\tthe PCI function number of the device in hex\n"
635 	    "\ttype\t\ta string describing the PCIe generation and width\n"
636 	    "\tmaxspeed\tthe maximum supported PCIe speed of the device\n"
637 	    "\tcurspeed\tthe current PCIe speed of the device\n"
638 	    "\tmaxwidth\tthe maximum supported PCIe lane count of the device\n"
639 	    "\tcurwidth\tthe current lane count of the PCIe device\n"
640 	    "\tsupspeeds\tthe list of speeds the device supports\n");
641 }
642 
643 int
644 pcieadm_show_devs(pcieadm_t *pcip, int argc, char *argv[])
645 {
646 	int c, ret;
647 	uint_t flags = 0;
648 	const char *fields = NULL;
649 	pcieadm_show_devs_t psd;
650 	pcieadm_di_walk_t walk;
651 	ofmt_status_t oferr;
652 	boolean_t parse = B_FALSE;
653 	boolean_t speeds = B_FALSE;
654 
655 	/*
656 	 * show-devs relies solely on the devinfo snapshot we already took.
657 	 * Formalize our privs immediately.
658 	 */
659 	pcieadm_init_privs(pcip);
660 
661 	bzero(&psd, sizeof (psd));
662 	psd.psd_pia = pcip;
663 	psd.psd_funcs = B_TRUE;
664 
665 	while ((c = getopt(argc, argv, ":FHo:ps")) != -1) {
666 		switch (c) {
667 		case 'F':
668 			psd.psd_funcs = B_FALSE;
669 			break;
670 		case 'p':
671 			parse = B_TRUE;
672 			flags |= OFMT_PARSABLE;
673 			break;
674 		case 'H':
675 			flags |= OFMT_NOHEADER;
676 			break;
677 		case 's':
678 			speeds = B_TRUE;
679 			break;
680 		case 'o':
681 			fields = optarg;
682 			break;
683 		case ':':
684 			pcieadm_show_devs_help("option -%c requires an "
685 			    "argument", optopt);
686 			exit(EXIT_USAGE);
687 		case '?':
688 			pcieadm_show_devs_help("unknown option: -%c", optopt);
689 			exit(EXIT_USAGE);
690 		}
691 	}
692 
693 	if (parse && fields == NULL) {
694 		errx(EXIT_USAGE, "-p requires fields specified with -o");
695 	}
696 
697 	if (fields != NULL && speeds) {
698 		errx(EXIT_USAGE, "-s cannot be used with with -o");
699 	}
700 
701 	if (fields == NULL) {
702 		if (speeds) {
703 			fields = pcieadm_show_dev_speeds;
704 		} else {
705 			fields = pcieadm_show_dev_fields;
706 		}
707 	}
708 
709 	argc -= optind;
710 	argv += optind;
711 
712 	if (argc > 0) {
713 		psd.psd_nfilts = argc;
714 		psd.psd_filts = argv;
715 		psd.psd_used = calloc(argc, sizeof (boolean_t));
716 		if (psd.psd_used == NULL) {
717 			err(EXIT_FAILURE, "failed to allocate filter tracking "
718 			    "memory");
719 		}
720 	}
721 
722 	oferr = ofmt_open(fields, pcieadm_show_dev_ofmt, flags, 0,
723 	    &psd.psd_ofmt);
724 	ofmt_check(oferr, parse, psd.psd_ofmt, pcieadm_ofmt_errx, warnx);
725 
726 	walk.pdw_arg = &psd;
727 	walk.pdw_func = pcieadm_show_devs_walk_cb;
728 
729 	pcieadm_di_walk(pcip, &walk);
730 
731 	ret = EXIT_SUCCESS;
732 	for (int i = 0; i < psd.psd_nfilts; i++) {
733 		if (!psd.psd_used[i]) {
734 			warnx("filter '%s' did not match any devices",
735 			    psd.psd_filts[i]);
736 			ret = EXIT_FAILURE;
737 		}
738 	}
739 
740 	if (psd.psd_nprint == 0) {
741 		ret = EXIT_FAILURE;
742 	}
743 
744 	return (ret);
745 }
746