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", ®s);
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