xref: /illumos-gate/usr/src/cmd/pcieadm/pcieadm_devs.c (revision 6829565747b60c2b3bf0d5b160e4eec95ed952bc)
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 2025 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
pcieadm_speed2gen(int64_t speed)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 *
pcieadm_speed2str(int64_t speed)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
pcieadm_show_devs_ofmt_cb(ofmt_arg_t * ofarg,char * buf,uint_t buflen)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%d", 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%d", 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 		/*
294 		 * We need to distinguish three different groups of things here:
295 		 *
296 		 *  - Something is a PCI device.
297 		 *  - We have a PCIe device where the link is down and therefore
298 		 *    have no current width or speed.
299 		 *  - We have a PCIe device which is up.
300 		 *
301 		 * A PCIe device should always have a maximum width value. This
302 		 * is required and therefore should be a good proxy for whether
303 		 * or not we have a PCIe device.
304 		 */
305 		if (psdo->psdo_mwidth == -1) {
306 			if (strlcat(buf, "PCI", buflen) >= buflen) {
307 				return (B_FALSE);
308 			}
309 			break;
310 		}
311 
312 		/*
313 		 * If we don't have a valid link up, indicate we don't know.
314 		 * While the link is probably down, we don't have that as a
315 		 * guarantee right now.
316 		 */
317 		if (psdo->psdo_cspeed == -1 || psdo->psdo_cwidth == -1) {
318 			if (strlcat(buf, "PCIe unknown", buflen) >= buflen) {
319 				return (B_FALSE);
320 			}
321 			break;
322 		}
323 
324 		if (snprintf(buf, buflen, "PCIe Gen %ux%d",
325 		    pcieadm_speed2gen(psdo->psdo_cspeed),
326 		    psdo->psdo_cwidth) >= buflen) {
327 			return (B_FALSE);
328 		}
329 		break;
330 	default:
331 		abort();
332 	}
333 	return (B_TRUE);
334 }
335 
336 static const char *pcieadm_show_dev_fields = "bdf,type,instance,device";
337 static const char *pcieadm_show_dev_speeds =
338 	"bdf,driver,maxspeed,curspeed,maxwidth,curwidth,supspeeds";
339 static const ofmt_field_t pcieadm_show_dev_ofmt[] = {
340 	{ "VID", 6, PCIEADM_SDO_VID, pcieadm_show_devs_ofmt_cb },
341 	{ "DID", 6, PCIEADM_SDO_DID, pcieadm_show_devs_ofmt_cb },
342 	{ "REV", 6, PCIEADM_SDO_REV, pcieadm_show_devs_ofmt_cb },
343 	{ "SUBVID", 6, PCIEADM_SDO_SUBVID, pcieadm_show_devs_ofmt_cb },
344 	{ "SUBSYS", 6, PCIEADM_SDO_SUBSYS, pcieadm_show_devs_ofmt_cb },
345 	{ "BDF", 8, PCIEADM_SDO_BDF, pcieadm_show_devs_ofmt_cb },
346 	{ "DRIVER", 15, PCIEADM_SDO_DRIVER, pcieadm_show_devs_ofmt_cb },
347 	{ "INSTANCE", 15, PCIEADM_SDO_INSTANCE, pcieadm_show_devs_ofmt_cb },
348 	{ "INSTNUM", 8, PCIEADM_SDO_INSTNUM, pcieadm_show_devs_ofmt_cb },
349 	{ "TYPE", 15, PCIEADM_SDO_TYPE, pcieadm_show_devs_ofmt_cb },
350 	{ "VENDOR", 30, PCIEADM_SDO_VENDOR, pcieadm_show_devs_ofmt_cb },
351 	{ "DEVICE", 30, PCIEADM_SDO_DEVICE, pcieadm_show_devs_ofmt_cb },
352 	{ "SUBVENDOR", 30, PCIEADM_SDO_SUBVENDOR, pcieadm_show_devs_ofmt_cb },
353 	{ "SUBSYSTEM", 30, PCIEADM_SDO_SUBSYSTEM, pcieadm_show_devs_ofmt_cb },
354 	{ "PATH", 30, PCIEADM_SDO_PATH, pcieadm_show_devs_ofmt_cb },
355 	{ "BUS", 4, PCIEADM_SDO_BDF_BUS, pcieadm_show_devs_ofmt_cb },
356 	{ "DEV", 4, PCIEADM_SDO_BDF_DEV, pcieadm_show_devs_ofmt_cb },
357 	{ "FUNC", 4, PCIEADM_SDO_BDF_FUNC, pcieadm_show_devs_ofmt_cb },
358 	{ "MAXSPEED", 10, PCIEADM_SDO_MAXSPEED, pcieadm_show_devs_ofmt_cb },
359 	{ "MAXWIDTH", 10, PCIEADM_SDO_MAXWIDTH, pcieadm_show_devs_ofmt_cb },
360 	{ "CURSPEED", 10, PCIEADM_SDO_CURSPEED, pcieadm_show_devs_ofmt_cb },
361 	{ "CURWIDTH", 10, PCIEADM_SDO_CURWIDTH, pcieadm_show_devs_ofmt_cb },
362 	{ "SUPSPEEDS", 20, PCIEADM_SDO_SUPSPEEDS, pcieadm_show_devs_ofmt_cb },
363 	{ NULL, 0, 0, NULL }
364 };
365 
366 static boolean_t
pcieadm_show_devs_match(pcieadm_show_devs_t * psd,pcieadm_show_devs_ofmt_t * psdo)367 pcieadm_show_devs_match(pcieadm_show_devs_t *psd,
368     pcieadm_show_devs_ofmt_t *psdo)
369 {
370 	char dinst[128], bdf[128];
371 
372 	if (psd->psd_nfilts == 0) {
373 		return (B_TRUE);
374 	}
375 
376 	if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1) {
377 		(void) snprintf(dinst, sizeof (dinst), "%s%d",
378 		    psdo->psdo_driver, psdo->psdo_instance);
379 	}
380 	(void) snprintf(bdf, sizeof (bdf), "%x/%x/%x", psdo->psdo_bus,
381 	    psdo->psdo_dev, psdo->psdo_func);
382 
383 	for (uint_t i = 0; i < psd->psd_nfilts; i++) {
384 		const char *filt = psd->psd_filts[i];
385 
386 		if (strcmp(filt, psdo->psdo_path) == 0) {
387 			psd->psd_used[i] = B_TRUE;
388 			return (B_TRUE);
389 		}
390 
391 		if (strcmp(filt, bdf) == 0) {
392 			psd->psd_used[i] = B_TRUE;
393 			return (B_TRUE);
394 		}
395 
396 		if (psdo->psdo_driver != NULL &&
397 		    strcmp(filt, psdo->psdo_driver) == 0) {
398 			psd->psd_used[i] = B_TRUE;
399 			return (B_TRUE);
400 		}
401 
402 		if (psdo->psdo_driver != NULL && psdo->psdo_instance != -1 &&
403 		    strcmp(filt, dinst) == 0) {
404 			psd->psd_used[i] = B_TRUE;
405 			return (B_TRUE);
406 		}
407 
408 		if (strncmp("/devices", filt, strlen("/devices")) == 0) {
409 			filt += strlen("/devices");
410 		}
411 
412 		if (strcmp(filt, psdo->psdo_path) == 0) {
413 			psd->psd_used[i] = B_TRUE;
414 			return (B_TRUE);
415 		}
416 	}
417 	return (B_FALSE);
418 }
419 
420 static int
pcieadm_show_devs_walk_cb(di_node_t node,void * arg)421 pcieadm_show_devs_walk_cb(di_node_t node, void *arg)
422 {
423 	int nprop, *regs = NULL, *did, *vid, *mwidth, *cwidth, *rev;
424 	int *subvid, *subsys;
425 	int64_t *mspeed, *cspeed, *sspeeds;
426 	char *path = NULL;
427 	pcieadm_show_devs_t *psd = arg;
428 	int ret = DI_WALK_CONTINUE;
429 	char venstr[64], devstr[64], subvenstr[64], subsysstr[64];
430 	pcieadm_show_devs_ofmt_t oarg;
431 	pcidb_hdl_t *pcidb = psd->psd_pia->pia_pcidb;
432 
433 	bzero(&oarg, sizeof (oarg));
434 
435 	path = di_devfs_path(node);
436 	if (path == NULL) {
437 		err(EXIT_FAILURE, "failed to construct devfs path for node: "
438 		    "%s", di_node_name(node));
439 	}
440 
441 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regs);
442 	if (nprop <= 0) {
443 		errx(EXIT_FAILURE, "failed to lookup regs array for %s",
444 		    path);
445 	}
446 
447 	oarg.psdo_path = path;
448 	oarg.psdo_bus = PCI_REG_BUS_G(regs[0]);
449 	oarg.psdo_dev = PCI_REG_DEV_G(regs[0]);
450 	oarg.psdo_func = PCI_REG_FUNC_G(regs[0]);
451 
452 	if (oarg.psdo_func != 0 && !psd->psd_funcs) {
453 		goto done;
454 	}
455 
456 	oarg.psdo_driver = di_driver_name(node);
457 	oarg.psdo_instance = di_instance(node);
458 
459 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did);
460 	if (nprop != 1) {
461 		oarg.psdo_did = -1;
462 	} else {
463 		oarg.psdo_did = (uint16_t)*did;
464 	}
465 
466 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid);
467 	if (nprop != 1) {
468 		oarg.psdo_vid = -1;
469 	} else {
470 		oarg.psdo_vid = (uint16_t)*vid;
471 	}
472 
473 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "revision-id", &rev);
474 	if (nprop != 1) {
475 		oarg.psdo_rev = -1;
476 	} else {
477 		oarg.psdo_rev = (uint16_t)*rev;
478 	}
479 
480 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-vendor-id",
481 	    &subvid);
482 	if (nprop != 1) {
483 		oarg.psdo_subvid = -1;
484 	} else {
485 		oarg.psdo_subvid = (uint16_t)*subvid;
486 	}
487 
488 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-id",
489 	    &subsys);
490 	if (nprop != 1) {
491 		oarg.psdo_subsys = -1;
492 	} else {
493 		oarg.psdo_subsys = (uint16_t)*subsys;
494 	}
495 
496 	oarg.psdo_vendor = "--";
497 	if (oarg.psdo_vid != -1) {
498 		pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb,
499 		    oarg.psdo_vid);
500 		if (vend != NULL) {
501 			oarg.psdo_vendor = pcidb_vendor_name(vend);
502 		} else {
503 			(void) snprintf(venstr, sizeof (venstr),
504 			    "Unknown vendor: 0x%x", oarg.psdo_vid);
505 			oarg.psdo_vendor = venstr;
506 		}
507 	}
508 
509 	oarg.psdo_device = "--";
510 	if (oarg.psdo_vid != -1 && oarg.psdo_did != -1) {
511 		pcidb_device_t *dev = pcidb_lookup_device(pcidb,
512 		    oarg.psdo_vid, oarg.psdo_did);
513 		if (dev != NULL) {
514 			oarg.psdo_device = pcidb_device_name(dev);
515 		} else {
516 			(void) snprintf(devstr, sizeof (devstr),
517 			    "Unknown device: 0x%x", oarg.psdo_did);
518 			oarg.psdo_device = devstr;
519 		}
520 	}
521 
522 	/*
523 	 * The pci.ids database organizes subsystems under devices. We look at
524 	 * the subsystem vendor separately because even if the device or other
525 	 * information is not known, we may still be able to figure out what it
526 	 * is.
527 	 */
528 	oarg.psdo_subvendor = "--";
529 	oarg.psdo_subsystem = "--";
530 	if (oarg.psdo_subvid != -1) {
531 		pcidb_vendor_t *vend = pcidb_lookup_vendor(pcidb,
532 		    oarg.psdo_subvid);
533 		if (vend != NULL) {
534 			oarg.psdo_subvendor = pcidb_vendor_name(vend);
535 		} else {
536 			(void) snprintf(subvenstr, sizeof (subvenstr),
537 			    "Unknown vendor: 0x%x", oarg.psdo_vid);
538 			oarg.psdo_subvendor = subvenstr;
539 		}
540 	}
541 
542 	if (oarg.psdo_vid != -1 && oarg.psdo_did != -1 &&
543 	    oarg.psdo_subvid != -1 && oarg.psdo_subsys != -1) {
544 		pcidb_subvd_t *subvd = pcidb_lookup_subvd(pcidb, oarg.psdo_vid,
545 		    oarg.psdo_did, oarg.psdo_subvid, oarg.psdo_subsys);
546 		if (subvd != NULL) {
547 			oarg.psdo_subsystem = pcidb_subvd_name(subvd);
548 		} else {
549 			(void) snprintf(subsysstr, sizeof (subsysstr),
550 			    "Unknown subsystem: 0x%x", oarg.psdo_subsys);
551 			oarg.psdo_subsystem = subsysstr;
552 		}
553 	}
554 
555 
556 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
557 	    "pcie-link-maximum-width", &mwidth);
558 	if (nprop != 1) {
559 		oarg.psdo_mwidth = -1;
560 	} else {
561 		oarg.psdo_mwidth = *mwidth;
562 	}
563 
564 	nprop = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
565 	    "pcie-link-current-width", &cwidth);
566 	if (nprop != 1) {
567 		oarg.psdo_cwidth = -1;
568 	} else {
569 		oarg.psdo_cwidth = *cwidth;
570 	}
571 
572 	nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
573 	    "pcie-link-maximum-speed", &mspeed);
574 	if (nprop != 1) {
575 		oarg.psdo_mspeed = -1;
576 	} else {
577 		oarg.psdo_mspeed = *mspeed;
578 	}
579 
580 	nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
581 	    "pcie-link-current-speed", &cspeed);
582 	if (nprop != 1) {
583 		oarg.psdo_cspeed = -1;
584 	} else {
585 		oarg.psdo_cspeed = *cspeed;
586 	}
587 
588 	nprop = di_prop_lookup_int64(DDI_DEV_T_ANY, node,
589 	    "pcie-link-supported-speeds", &sspeeds);
590 	if (nprop > 0) {
591 		oarg.psdo_nspeeds = nprop;
592 		oarg.psdo_sspeeds = sspeeds;
593 	} else {
594 		oarg.psdo_nspeeds = 0;
595 		oarg.psdo_sspeeds = NULL;
596 	}
597 
598 	if (pcieadm_show_devs_match(psd, &oarg)) {
599 		ofmt_print(psd->psd_ofmt, &oarg);
600 		psd->psd_nprint++;
601 	}
602 
603 done:
604 	if (path != NULL) {
605 		di_devfs_path_free(path);
606 	}
607 
608 	return (ret);
609 }
610 
611 void
pcieadm_show_devs_usage(FILE * f)612 pcieadm_show_devs_usage(FILE *f)
613 {
614 	(void) fprintf(f, "\tshow-devs\t[-F] [-H] [-s | -o field[,...] [-p]] "
615 	    "[filter...]\n");
616 }
617 
618 static void
pcieadm_show_devs_help(const char * fmt,...)619 pcieadm_show_devs_help(const char *fmt, ...)
620 {
621 	if (fmt != NULL) {
622 		va_list ap;
623 
624 		va_start(ap, fmt);
625 		vwarnx(fmt, ap);
626 		va_end(ap);
627 		(void) fprintf(stderr, "\n");
628 	}
629 
630 	(void) fprintf(stderr, "Usage:  %s show-devs [-F] [-H] [-s | -o "
631 	    "field[,...] [-p]] [filter...]\n", pcieadm_progname);
632 
633 	(void) fprintf(stderr, "\nList PCI devices and functions in the "
634 	    "system. Each <filter> selects a set\nof devices to show and "
635 	    "can be a driver name, instance, /devices path, or\nb/d/f.\n\n"
636 	    "\t-F\t\tdo not display PCI functions\n"
637 	    "\t-H\t\tomit the column header\n"
638 	    "\t-o field\toutput fields to print\n"
639 	    "\t-p\t\tparsable output (requires -o)\n"
640 	    "\t-s\t\tlist speeds and widths\n\n"
641 	    "The following fields are supported:\n"
642 	    "\tvid\t\tthe PCI vendor ID in hex\n"
643 	    "\tdid\t\tthe PCI device ID in hex\n"
644 	    "\trev\t\tthe PCI device revision in hex\n"
645 	    "\tsubvid\t\tthe PCI subsystem vendor ID in hex\n"
646 	    "\tsubsys\t\tthe PCI subsystem ID in hex\n"
647 	    "\tvendor\t\tthe name of the PCI vendor\n"
648 	    "\tdevice\t\tthe name of the PCI device\n"
649 	    "\tsubvendor\tthe name of the PCI subsystem vendor\n"
650 	    "\tsubsystem\tthe name of the PCI subsystem\n"
651 	    "\tinstance\tthe name of this particular instance, e.g. igb0\n"
652 	    "\tdriver\t\tthe name of the driver attached to the device\n"
653 	    "\tinstnum\t\tthe instance number of a device, e.g. 2 for nvme2\n"
654 	    "\tpath\t\tthe /devices path of the device\n"
655 	    "\tbdf\t\tthe PCI bus/device/function, with values in hex\n"
656 	    "\tbus\t\tthe PCI bus number of the device in hex\n"
657 	    "\tdev\t\tthe PCI device number of the device in hex\n"
658 	    "\tfunc\t\tthe PCI function number of the device in hex\n"
659 	    "\ttype\t\ta string describing the PCIe generation and width\n"
660 	    "\tmaxspeed\tthe maximum supported PCIe speed of the device\n"
661 	    "\tcurspeed\tthe current PCIe speed of the device\n"
662 	    "\tmaxwidth\tthe maximum supported PCIe lane count of the device\n"
663 	    "\tcurwidth\tthe current lane count of the PCIe device\n"
664 	    "\tsupspeeds\tthe list of speeds the device supports\n");
665 }
666 
667 int
pcieadm_show_devs(pcieadm_t * pcip,int argc,char * argv[])668 pcieadm_show_devs(pcieadm_t *pcip, int argc, char *argv[])
669 {
670 	int c, ret;
671 	uint_t flags = 0;
672 	const char *fields = NULL;
673 	pcieadm_show_devs_t psd;
674 	pcieadm_di_walk_t walk;
675 	ofmt_status_t oferr;
676 	boolean_t parse = B_FALSE;
677 	boolean_t speeds = B_FALSE;
678 
679 	/*
680 	 * show-devs relies solely on the devinfo snapshot we already took.
681 	 * Formalize our privs immediately.
682 	 */
683 	pcieadm_init_privs(pcip);
684 
685 	bzero(&psd, sizeof (psd));
686 	psd.psd_pia = pcip;
687 	psd.psd_funcs = B_TRUE;
688 
689 	while ((c = getopt(argc, argv, ":FHo:ps")) != -1) {
690 		switch (c) {
691 		case 'F':
692 			psd.psd_funcs = B_FALSE;
693 			break;
694 		case 'p':
695 			parse = B_TRUE;
696 			flags |= OFMT_PARSABLE;
697 			break;
698 		case 'H':
699 			flags |= OFMT_NOHEADER;
700 			break;
701 		case 's':
702 			speeds = B_TRUE;
703 			break;
704 		case 'o':
705 			fields = optarg;
706 			break;
707 		case ':':
708 			pcieadm_show_devs_help("option -%c requires an "
709 			    "argument", optopt);
710 			exit(EXIT_USAGE);
711 		case '?':
712 			pcieadm_show_devs_help("unknown option: -%c", optopt);
713 			exit(EXIT_USAGE);
714 		}
715 	}
716 
717 	if (parse && fields == NULL) {
718 		errx(EXIT_USAGE, "-p requires fields specified with -o");
719 	}
720 
721 	if (fields != NULL && speeds) {
722 		errx(EXIT_USAGE, "-s cannot be used with with -o");
723 	}
724 
725 	if (fields == NULL) {
726 		if (speeds) {
727 			fields = pcieadm_show_dev_speeds;
728 		} else {
729 			fields = pcieadm_show_dev_fields;
730 		}
731 	}
732 
733 	argc -= optind;
734 	argv += optind;
735 
736 	if (argc > 0) {
737 		psd.psd_nfilts = argc;
738 		psd.psd_filts = argv;
739 		psd.psd_used = calloc(argc, sizeof (boolean_t));
740 		if (psd.psd_used == NULL) {
741 			err(EXIT_FAILURE, "failed to allocate filter tracking "
742 			    "memory");
743 		}
744 	}
745 
746 	oferr = ofmt_open(fields, pcieadm_show_dev_ofmt, flags, 0,
747 	    &psd.psd_ofmt);
748 	ofmt_check(oferr, parse, psd.psd_ofmt, pcieadm_ofmt_errx, warnx);
749 
750 	walk.pdw_arg = &psd;
751 	walk.pdw_func = pcieadm_show_devs_walk_cb;
752 
753 	pcieadm_di_walk(pcip, &walk);
754 
755 	ret = EXIT_SUCCESS;
756 	for (int i = 0; i < psd.psd_nfilts; i++) {
757 		if (!psd.psd_used[i]) {
758 			warnx("filter '%s' did not match any devices",
759 			    psd.psd_filts[i]);
760 			ret = EXIT_FAILURE;
761 		}
762 	}
763 
764 	if (psd.psd_nprint == 0) {
765 		ret = EXIT_FAILURE;
766 	}
767 
768 	return (ret);
769 }
770