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
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%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
pcieadm_show_devs_match(pcieadm_show_devs_t * psd,pcieadm_show_devs_ofmt_t * psdo)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
pcieadm_show_devs_walk_cb(di_node_t node,void * arg)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", ®s);
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
pcieadm_show_devs_usage(FILE * f)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
pcieadm_show_devs_help(const char * fmt,...)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
pcieadm_show_devs(pcieadm_t * pcip,int argc,char * argv[])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