1*eed8d5dbSAndy Fiddaman /*
2*eed8d5dbSAndy Fiddaman * This file and its contents are supplied under the terms of the
3*eed8d5dbSAndy Fiddaman * Common Development and Distribution License ("CDDL"), version 1.0.
4*eed8d5dbSAndy Fiddaman * You may only use this file in accordance with the terms of version
5*eed8d5dbSAndy Fiddaman * 1.0 of the CDDL.
6*eed8d5dbSAndy Fiddaman *
7*eed8d5dbSAndy Fiddaman * A full copy of the text of the CDDL should have accompanied this
8*eed8d5dbSAndy Fiddaman * source. A copy of the CDDL is also available via the Internet at
9*eed8d5dbSAndy Fiddaman * http://www.illumos.org/license/CDDL.
10*eed8d5dbSAndy Fiddaman */
11*eed8d5dbSAndy Fiddaman
12*eed8d5dbSAndy Fiddaman /*
13*eed8d5dbSAndy Fiddaman * Copyright 2026 Oxide Computer Company
14*eed8d5dbSAndy Fiddaman */
15*eed8d5dbSAndy Fiddaman
16*eed8d5dbSAndy Fiddaman /*
17*eed8d5dbSAndy Fiddaman * Implement logic related to the state of PCIe links. These operate on a PCIe
18*eed8d5dbSAndy Fiddaman * bridge (e.g. a root port) by way of the private ioctls implemented by the
19*eed8d5dbSAndy Fiddaman * 'pcieb' driver.
20*eed8d5dbSAndy Fiddaman */
21*eed8d5dbSAndy Fiddaman
22*eed8d5dbSAndy Fiddaman #include <err.h>
23*eed8d5dbSAndy Fiddaman #include <errno.h>
24*eed8d5dbSAndy Fiddaman #include <fcntl.h>
25*eed8d5dbSAndy Fiddaman #include <inttypes.h>
26*eed8d5dbSAndy Fiddaman #include <limits.h>
27*eed8d5dbSAndy Fiddaman #include <ofmt.h>
28*eed8d5dbSAndy Fiddaman #include <stdbool.h>
29*eed8d5dbSAndy Fiddaman #include <stdio.h>
30*eed8d5dbSAndy Fiddaman #include <stdlib.h>
31*eed8d5dbSAndy Fiddaman #include <string.h>
32*eed8d5dbSAndy Fiddaman #include <time.h>
33*eed8d5dbSAndy Fiddaman #include <strings.h>
34*eed8d5dbSAndy Fiddaman #include <unistd.h>
35*eed8d5dbSAndy Fiddaman #include <sys/stat.h>
36*eed8d5dbSAndy Fiddaman #include <sys/time.h>
37*eed8d5dbSAndy Fiddaman #include <sys/types.h>
38*eed8d5dbSAndy Fiddaman #include <sys/sysmacros.h>
39*eed8d5dbSAndy Fiddaman #include <sys/debug.h>
40*eed8d5dbSAndy Fiddaman
41*eed8d5dbSAndy Fiddaman #include <pcieb_ioctl.h>
42*eed8d5dbSAndy Fiddaman
43*eed8d5dbSAndy Fiddaman #include "pcieadm.h"
44*eed8d5dbSAndy Fiddaman
45*eed8d5dbSAndy Fiddaman /*
46*eed8d5dbSAndy Fiddaman * Go through and initialize the common logic for most of the link related
47*eed8d5dbSAndy Fiddaman * commands that need to use the pcieb ioctls. This includes:
48*eed8d5dbSAndy Fiddaman *
49*eed8d5dbSAndy Fiddaman * - Verifying it makes sense
50*eed8d5dbSAndy Fiddaman * - Setting privileges
51*eed8d5dbSAndy Fiddaman * - Opening the actual devctl file
52*eed8d5dbSAndy Fiddaman * - Finding the dip
53*eed8d5dbSAndy Fiddaman */
54*eed8d5dbSAndy Fiddaman static int
pcieadm_link_pcieb_open(pcieadm_t * pcip,const char * device,bool write)55*eed8d5dbSAndy Fiddaman pcieadm_link_pcieb_open(pcieadm_t *pcip, const char *device, bool write)
56*eed8d5dbSAndy Fiddaman {
57*eed8d5dbSAndy Fiddaman const char *drv;
58*eed8d5dbSAndy Fiddaman
59*eed8d5dbSAndy Fiddaman /*
60*eed8d5dbSAndy Fiddaman * We retain FILE_DAC_READ and FILE_DAC_SEARCH to find and open the
61*eed8d5dbSAndy Fiddaman * device file and SYS_DEVICES for the ioctls themselves. To open it for
62*eed8d5dbSAndy Fiddaman * writing (some bridge ioctls, such as retrain, mutate link state) we
63*eed8d5dbSAndy Fiddaman * also need FILE_DAC_WRITE to override the node's permissions, and
64*eed8d5dbSAndy Fiddaman * FILE_WRITE because, unlike FILE_READ, it is stripped from our minimal
65*eed8d5dbSAndy Fiddaman * privilege set.
66*eed8d5dbSAndy Fiddaman */
67*eed8d5dbSAndy Fiddaman VERIFY0(priv_addset(pcip->pia_priv_eff, PRIV_SYS_DEVICES));
68*eed8d5dbSAndy Fiddaman VERIFY0(priv_addset(pcip->pia_priv_eff, PRIV_FILE_DAC_READ));
69*eed8d5dbSAndy Fiddaman VERIFY0(priv_addset(pcip->pia_priv_eff, PRIV_FILE_DAC_SEARCH));
70*eed8d5dbSAndy Fiddaman if (write) {
71*eed8d5dbSAndy Fiddaman VERIFY0(priv_addset(pcip->pia_priv_eff, PRIV_FILE_WRITE));
72*eed8d5dbSAndy Fiddaman VERIFY0(priv_addset(pcip->pia_priv_eff, PRIV_FILE_DAC_WRITE));
73*eed8d5dbSAndy Fiddaman }
74*eed8d5dbSAndy Fiddaman pcieadm_init_privs(pcip);
75*eed8d5dbSAndy Fiddaman
76*eed8d5dbSAndy Fiddaman pcieadm_find_dip(pcip, device);
77*eed8d5dbSAndy Fiddaman drv = di_driver_name(pcip->pia_devi);
78*eed8d5dbSAndy Fiddaman if (drv == NULL || strcmp(drv, "pcieb") != 0) {
79*eed8d5dbSAndy Fiddaman errx(EXIT_FAILURE, "device %s is not a PCIe bridge: found "
80*eed8d5dbSAndy Fiddaman "driver %s, but expected pcieb", device,
81*eed8d5dbSAndy Fiddaman drv == NULL ? "<none>" : drv);
82*eed8d5dbSAndy Fiddaman }
83*eed8d5dbSAndy Fiddaman
84*eed8d5dbSAndy Fiddaman for (di_minor_t m = di_minor_next(pcip->pia_devi, DI_MINOR_NIL);
85*eed8d5dbSAndy Fiddaman m != NULL; m = di_minor_next(pcip->pia_devi, m)) {
86*eed8d5dbSAndy Fiddaman if (strcmp(di_minor_name(m), "devctl") == 0) {
87*eed8d5dbSAndy Fiddaman char buf[PATH_MAX], *mp;
88*eed8d5dbSAndy Fiddaman int fd;
89*eed8d5dbSAndy Fiddaman
90*eed8d5dbSAndy Fiddaman mp = di_devfs_minor_path(m);
91*eed8d5dbSAndy Fiddaman if (mp == NULL) {
92*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to get devfs path "
93*eed8d5dbSAndy Fiddaman "for %s devctl minor", device);
94*eed8d5dbSAndy Fiddaman }
95*eed8d5dbSAndy Fiddaman
96*eed8d5dbSAndy Fiddaman if (snprintf(buf, sizeof (buf), "/devices%s", mp) >=
97*eed8d5dbSAndy Fiddaman sizeof (buf)) {
98*eed8d5dbSAndy Fiddaman errx(EXIT_FAILURE, "failed to construct devfs "
99*eed8d5dbSAndy Fiddaman "minor path for %s devctl minor: internal "
100*eed8d5dbSAndy Fiddaman "path buffer would have overflowed",
101*eed8d5dbSAndy Fiddaman device);
102*eed8d5dbSAndy Fiddaman }
103*eed8d5dbSAndy Fiddaman di_devfs_path_free(mp);
104*eed8d5dbSAndy Fiddaman
105*eed8d5dbSAndy Fiddaman if (setppriv(PRIV_SET, PRIV_EFFECTIVE,
106*eed8d5dbSAndy Fiddaman pcip->pia_priv_eff) != 0) {
107*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to raise privileges");
108*eed8d5dbSAndy Fiddaman }
109*eed8d5dbSAndy Fiddaman
110*eed8d5dbSAndy Fiddaman fd = open(buf, write ? O_RDWR : O_RDONLY);
111*eed8d5dbSAndy Fiddaman if (fd < 0) {
112*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE,
113*eed8d5dbSAndy Fiddaman "failed to open %s devctl minor %s",
114*eed8d5dbSAndy Fiddaman device, buf);
115*eed8d5dbSAndy Fiddaman }
116*eed8d5dbSAndy Fiddaman
117*eed8d5dbSAndy Fiddaman if (setppriv(PRIV_SET, PRIV_EFFECTIVE,
118*eed8d5dbSAndy Fiddaman pcip->pia_priv_min) != 0) {
119*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE,
120*eed8d5dbSAndy Fiddaman "failed to reduce privileges");
121*eed8d5dbSAndy Fiddaman }
122*eed8d5dbSAndy Fiddaman
123*eed8d5dbSAndy Fiddaman return (fd);
124*eed8d5dbSAndy Fiddaman }
125*eed8d5dbSAndy Fiddaman }
126*eed8d5dbSAndy Fiddaman
127*eed8d5dbSAndy Fiddaman errx(EXIT_FAILURE, "failed to find devctl minor for %s", device);
128*eed8d5dbSAndy Fiddaman }
129*eed8d5dbSAndy Fiddaman
130*eed8d5dbSAndy Fiddaman static void
pcieadm_link_limit_usage(FILE * f)131*eed8d5dbSAndy Fiddaman pcieadm_link_limit_usage(FILE *f)
132*eed8d5dbSAndy Fiddaman {
133*eed8d5dbSAndy Fiddaman (void) fprintf(f, "\tlink limit\t[-s speed] -d device\n");
134*eed8d5dbSAndy Fiddaman }
135*eed8d5dbSAndy Fiddaman
136*eed8d5dbSAndy Fiddaman static void
pcieadm_link_limit_help(const char * fmt,...)137*eed8d5dbSAndy Fiddaman pcieadm_link_limit_help(const char *fmt, ...)
138*eed8d5dbSAndy Fiddaman {
139*eed8d5dbSAndy Fiddaman if (fmt != NULL) {
140*eed8d5dbSAndy Fiddaman va_list ap;
141*eed8d5dbSAndy Fiddaman
142*eed8d5dbSAndy Fiddaman va_start(ap, fmt);
143*eed8d5dbSAndy Fiddaman vwarnx(fmt, ap);
144*eed8d5dbSAndy Fiddaman va_end(ap);
145*eed8d5dbSAndy Fiddaman (void) fprintf(stderr, "\n");
146*eed8d5dbSAndy Fiddaman }
147*eed8d5dbSAndy Fiddaman
148*eed8d5dbSAndy Fiddaman (void) fprintf(stderr, "Usage: %s link limit [-s speed] -d device\n",
149*eed8d5dbSAndy Fiddaman pcieadm_progname);
150*eed8d5dbSAndy Fiddaman (void) fprintf(stderr, "Print or set the administrative limit on the "
151*eed8d5dbSAndy Fiddaman "corresponding PCIe link\n\n"
152*eed8d5dbSAndy Fiddaman "\t-d device\tthe PCIe bridge to operate on (driver instance,"
153*eed8d5dbSAndy Fiddaman "\n\t\t\t/devices path, or b/d/f)\n"
154*eed8d5dbSAndy Fiddaman "\t-s speed\tlimit the device to the specified PCIe gen/speed "
155*eed8d5dbSAndy Fiddaman "(e.g.\n\t\t\t2.5, 32, gen2, gen3, etc.)\n");
156*eed8d5dbSAndy Fiddaman }
157*eed8d5dbSAndy Fiddaman
158*eed8d5dbSAndy Fiddaman typedef struct {
159*eed8d5dbSAndy Fiddaman uint32_t pls_speed;
160*eed8d5dbSAndy Fiddaman const char *pls_gts;
161*eed8d5dbSAndy Fiddaman const char *pls_gen;
162*eed8d5dbSAndy Fiddaman const char *pls_alt;
163*eed8d5dbSAndy Fiddaman } pcieadm_link_speed_t;
164*eed8d5dbSAndy Fiddaman
165*eed8d5dbSAndy Fiddaman static const pcieadm_link_speed_t pcieadm_link_speeds[] = {
166*eed8d5dbSAndy Fiddaman { PCIEB_LINK_SPEED_GEN1, "2.5", "gen1", NULL },
167*eed8d5dbSAndy Fiddaman { PCIEB_LINK_SPEED_GEN2, "5.0", "gen2", "5" },
168*eed8d5dbSAndy Fiddaman { PCIEB_LINK_SPEED_GEN3, "8.0", "gen3", "8" },
169*eed8d5dbSAndy Fiddaman { PCIEB_LINK_SPEED_GEN4, "16.0", "gen4", "16" },
170*eed8d5dbSAndy Fiddaman { PCIEB_LINK_SPEED_GEN5, "32.0", "gen5", "32" },
171*eed8d5dbSAndy Fiddaman { PCIEB_LINK_SPEED_GEN6, "64.0", "gen6", "64" }
172*eed8d5dbSAndy Fiddaman };
173*eed8d5dbSAndy Fiddaman
174*eed8d5dbSAndy Fiddaman static uint32_t
pcieadm_parse_pcieb_speed(const char * str)175*eed8d5dbSAndy Fiddaman pcieadm_parse_pcieb_speed(const char *str)
176*eed8d5dbSAndy Fiddaman {
177*eed8d5dbSAndy Fiddaman for (uint_t i = 0; i < ARRAY_SIZE(pcieadm_link_speeds); i++) {
178*eed8d5dbSAndy Fiddaman const pcieadm_link_speed_t *pls = &pcieadm_link_speeds[i];
179*eed8d5dbSAndy Fiddaman
180*eed8d5dbSAndy Fiddaman if (strcasecmp(str, pls->pls_gts) == 0 ||
181*eed8d5dbSAndy Fiddaman strcasecmp(str, pls->pls_gen) == 0 ||
182*eed8d5dbSAndy Fiddaman (pls->pls_alt != NULL &&
183*eed8d5dbSAndy Fiddaman strcasecmp(str, pls->pls_alt) == 0)) {
184*eed8d5dbSAndy Fiddaman return (pls->pls_speed);
185*eed8d5dbSAndy Fiddaman }
186*eed8d5dbSAndy Fiddaman }
187*eed8d5dbSAndy Fiddaman
188*eed8d5dbSAndy Fiddaman errx(EXIT_FAILURE, "failed to parse speed: %s", str);
189*eed8d5dbSAndy Fiddaman }
190*eed8d5dbSAndy Fiddaman
191*eed8d5dbSAndy Fiddaman static const pcieadm_link_speed_t *
pcieadm_link_speed_lookup(uint32_t speed)192*eed8d5dbSAndy Fiddaman pcieadm_link_speed_lookup(uint32_t speed)
193*eed8d5dbSAndy Fiddaman {
194*eed8d5dbSAndy Fiddaman for (uint_t i = 0; i < ARRAY_SIZE(pcieadm_link_speeds); i++) {
195*eed8d5dbSAndy Fiddaman if (pcieadm_link_speeds[i].pls_speed == speed)
196*eed8d5dbSAndy Fiddaman return (&pcieadm_link_speeds[i]);
197*eed8d5dbSAndy Fiddaman }
198*eed8d5dbSAndy Fiddaman
199*eed8d5dbSAndy Fiddaman return (NULL);
200*eed8d5dbSAndy Fiddaman }
201*eed8d5dbSAndy Fiddaman
202*eed8d5dbSAndy Fiddaman static int
pcieadm_link_limit(pcieadm_t * pcip,int argc,char * argv[])203*eed8d5dbSAndy Fiddaman pcieadm_link_limit(pcieadm_t *pcip, int argc, char *argv[])
204*eed8d5dbSAndy Fiddaman {
205*eed8d5dbSAndy Fiddaman int c, ret = EXIT_SUCCESS;
206*eed8d5dbSAndy Fiddaman const char *device = NULL, *speed = NULL;
207*eed8d5dbSAndy Fiddaman pcieb_ioctl_target_speed_t pits;
208*eed8d5dbSAndy Fiddaman
209*eed8d5dbSAndy Fiddaman while ((c = getopt(argc, argv, ":d:s:")) != -1) {
210*eed8d5dbSAndy Fiddaman switch (c) {
211*eed8d5dbSAndy Fiddaman case 'd':
212*eed8d5dbSAndy Fiddaman device = optarg;
213*eed8d5dbSAndy Fiddaman break;
214*eed8d5dbSAndy Fiddaman case 's':
215*eed8d5dbSAndy Fiddaman speed = optarg;
216*eed8d5dbSAndy Fiddaman break;
217*eed8d5dbSAndy Fiddaman case ':':
218*eed8d5dbSAndy Fiddaman pcieadm_link_limit_help("Option -%c requires an "
219*eed8d5dbSAndy Fiddaman "argument", optopt);
220*eed8d5dbSAndy Fiddaman exit(EXIT_USAGE);
221*eed8d5dbSAndy Fiddaman case '?':
222*eed8d5dbSAndy Fiddaman default:
223*eed8d5dbSAndy Fiddaman pcieadm_link_limit_help("unknown option: -%c",
224*eed8d5dbSAndy Fiddaman optopt);
225*eed8d5dbSAndy Fiddaman exit(EXIT_USAGE);
226*eed8d5dbSAndy Fiddaman }
227*eed8d5dbSAndy Fiddaman
228*eed8d5dbSAndy Fiddaman }
229*eed8d5dbSAndy Fiddaman
230*eed8d5dbSAndy Fiddaman if (device == NULL) {
231*eed8d5dbSAndy Fiddaman pcieadm_link_limit_help("missing required device argument "
232*eed8d5dbSAndy Fiddaman "(-d)");
233*eed8d5dbSAndy Fiddaman exit(EXIT_USAGE);
234*eed8d5dbSAndy Fiddaman }
235*eed8d5dbSAndy Fiddaman
236*eed8d5dbSAndy Fiddaman argc -= optind;
237*eed8d5dbSAndy Fiddaman argv += optind;
238*eed8d5dbSAndy Fiddaman if (argc != 0) {
239*eed8d5dbSAndy Fiddaman errx(EXIT_USAGE, "encountered extraneous arguments starting "
240*eed8d5dbSAndy Fiddaman "with %s", argv[0]);
241*eed8d5dbSAndy Fiddaman }
242*eed8d5dbSAndy Fiddaman
243*eed8d5dbSAndy Fiddaman int fd = pcieadm_link_pcieb_open(pcip, device, speed != NULL);
244*eed8d5dbSAndy Fiddaman bzero(&pits, sizeof (pits));
245*eed8d5dbSAndy Fiddaman
246*eed8d5dbSAndy Fiddaman if (speed != NULL) {
247*eed8d5dbSAndy Fiddaman pits.pits_speed = pcieadm_parse_pcieb_speed(speed);
248*eed8d5dbSAndy Fiddaman
249*eed8d5dbSAndy Fiddaman if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != 0)
250*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to raise privileges");
251*eed8d5dbSAndy Fiddaman
252*eed8d5dbSAndy Fiddaman if (ioctl(fd, PCIEB_IOCTL_SET_TARGET_SPEED, &pits) != 0) {
253*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to set %s target speed",
254*eed8d5dbSAndy Fiddaman device);
255*eed8d5dbSAndy Fiddaman }
256*eed8d5dbSAndy Fiddaman
257*eed8d5dbSAndy Fiddaman if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != 0)
258*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to reduce privileges");
259*eed8d5dbSAndy Fiddaman } else {
260*eed8d5dbSAndy Fiddaman const pcieadm_link_speed_t *pls;
261*eed8d5dbSAndy Fiddaman
262*eed8d5dbSAndy Fiddaman if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != 0)
263*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to raise privileges");
264*eed8d5dbSAndy Fiddaman
265*eed8d5dbSAndy Fiddaman if (ioctl(fd, PCIEB_IOCTL_GET_TARGET_SPEED, &pits) != 0) {
266*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to get %s target speed",
267*eed8d5dbSAndy Fiddaman device);
268*eed8d5dbSAndy Fiddaman }
269*eed8d5dbSAndy Fiddaman
270*eed8d5dbSAndy Fiddaman if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != 0)
271*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to reduce privileges");
272*eed8d5dbSAndy Fiddaman
273*eed8d5dbSAndy Fiddaman pls = pcieadm_link_speed_lookup(pits.pits_speed);
274*eed8d5dbSAndy Fiddaman if (pls != NULL) {
275*eed8d5dbSAndy Fiddaman (void) printf("Target speed: %s GT/s (%s)\n",
276*eed8d5dbSAndy Fiddaman pls->pls_gts, pls->pls_gen);
277*eed8d5dbSAndy Fiddaman } else {
278*eed8d5dbSAndy Fiddaman (void) printf("Target speed: unknown speed value: "
279*eed8d5dbSAndy Fiddaman "0x%x\n", pits.pits_speed);
280*eed8d5dbSAndy Fiddaman }
281*eed8d5dbSAndy Fiddaman
282*eed8d5dbSAndy Fiddaman if ((pits.pits_flags & ~PCIEB_FLAGS_ADMIN_SET) != 0) {
283*eed8d5dbSAndy Fiddaman (void) printf("Unknown flags: 0x%x\n", pits.pits_flags);
284*eed8d5dbSAndy Fiddaman } else if ((pits.pits_flags & PCIEB_FLAGS_ADMIN_SET) != 0) {
285*eed8d5dbSAndy Fiddaman (void) printf("Flags: Admin Set Speed\n");
286*eed8d5dbSAndy Fiddaman }
287*eed8d5dbSAndy Fiddaman }
288*eed8d5dbSAndy Fiddaman
289*eed8d5dbSAndy Fiddaman VERIFY0(close(fd));
290*eed8d5dbSAndy Fiddaman return (ret);
291*eed8d5dbSAndy Fiddaman }
292*eed8d5dbSAndy Fiddaman
293*eed8d5dbSAndy Fiddaman static void
pcieadm_link_retrain_usage(FILE * f)294*eed8d5dbSAndy Fiddaman pcieadm_link_retrain_usage(FILE *f)
295*eed8d5dbSAndy Fiddaman {
296*eed8d5dbSAndy Fiddaman (void) fprintf(f, "\tlink retrain\t-d device\n");
297*eed8d5dbSAndy Fiddaman }
298*eed8d5dbSAndy Fiddaman
299*eed8d5dbSAndy Fiddaman static void
pcieadm_link_retrain_help(const char * fmt,...)300*eed8d5dbSAndy Fiddaman pcieadm_link_retrain_help(const char *fmt, ...)
301*eed8d5dbSAndy Fiddaman {
302*eed8d5dbSAndy Fiddaman if (fmt != NULL) {
303*eed8d5dbSAndy Fiddaman va_list ap;
304*eed8d5dbSAndy Fiddaman
305*eed8d5dbSAndy Fiddaman va_start(ap, fmt);
306*eed8d5dbSAndy Fiddaman vwarnx(fmt, ap);
307*eed8d5dbSAndy Fiddaman va_end(ap);
308*eed8d5dbSAndy Fiddaman (void) fprintf(stderr, "\n");
309*eed8d5dbSAndy Fiddaman }
310*eed8d5dbSAndy Fiddaman
311*eed8d5dbSAndy Fiddaman (void) fprintf(stderr, "Usage: %s link retrain -d device\n",
312*eed8d5dbSAndy Fiddaman pcieadm_progname);
313*eed8d5dbSAndy Fiddaman (void) fprintf(stderr,
314*eed8d5dbSAndy Fiddaman "Retrain the link on the specified upstream port by requesting\n"
315*eed8d5dbSAndy Fiddaman "it via the PCIe Link Control register.\n\n"
316*eed8d5dbSAndy Fiddaman "\t-d device\tthe PCIe bridge to operate on (driver instance,\n"
317*eed8d5dbSAndy Fiddaman "\t\t\t/devices path, or b/d/f)\n");
318*eed8d5dbSAndy Fiddaman }
319*eed8d5dbSAndy Fiddaman
320*eed8d5dbSAndy Fiddaman static int
pcieadm_link_retrain(pcieadm_t * pcip,int argc,char * argv[])321*eed8d5dbSAndy Fiddaman pcieadm_link_retrain(pcieadm_t *pcip, int argc, char *argv[])
322*eed8d5dbSAndy Fiddaman {
323*eed8d5dbSAndy Fiddaman int c, ret = EXIT_SUCCESS;
324*eed8d5dbSAndy Fiddaman const char *device = NULL;
325*eed8d5dbSAndy Fiddaman
326*eed8d5dbSAndy Fiddaman while ((c = getopt(argc, argv, ":d:")) != -1) {
327*eed8d5dbSAndy Fiddaman switch (c) {
328*eed8d5dbSAndy Fiddaman case 'd':
329*eed8d5dbSAndy Fiddaman device = optarg;
330*eed8d5dbSAndy Fiddaman break;
331*eed8d5dbSAndy Fiddaman case ':':
332*eed8d5dbSAndy Fiddaman pcieadm_link_retrain_help("Option -%c requires an "
333*eed8d5dbSAndy Fiddaman "argument", optopt);
334*eed8d5dbSAndy Fiddaman exit(EXIT_USAGE);
335*eed8d5dbSAndy Fiddaman case '?':
336*eed8d5dbSAndy Fiddaman default:
337*eed8d5dbSAndy Fiddaman pcieadm_link_retrain_help("unknown option: -%c",
338*eed8d5dbSAndy Fiddaman optopt);
339*eed8d5dbSAndy Fiddaman exit(EXIT_USAGE);
340*eed8d5dbSAndy Fiddaman }
341*eed8d5dbSAndy Fiddaman }
342*eed8d5dbSAndy Fiddaman
343*eed8d5dbSAndy Fiddaman if (device == NULL) {
344*eed8d5dbSAndy Fiddaman pcieadm_link_retrain_help("missing required device argument "
345*eed8d5dbSAndy Fiddaman "(-d)");
346*eed8d5dbSAndy Fiddaman exit(EXIT_USAGE);
347*eed8d5dbSAndy Fiddaman }
348*eed8d5dbSAndy Fiddaman
349*eed8d5dbSAndy Fiddaman argc -= optind;
350*eed8d5dbSAndy Fiddaman argv += optind;
351*eed8d5dbSAndy Fiddaman if (argc != 0) {
352*eed8d5dbSAndy Fiddaman errx(EXIT_USAGE, "encountered extraneous arguments starting "
353*eed8d5dbSAndy Fiddaman "with %s", argv[0]);
354*eed8d5dbSAndy Fiddaman }
355*eed8d5dbSAndy Fiddaman
356*eed8d5dbSAndy Fiddaman int fd = pcieadm_link_pcieb_open(pcip, device, true);
357*eed8d5dbSAndy Fiddaman
358*eed8d5dbSAndy Fiddaman if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_eff) != 0)
359*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to raise privileges");
360*eed8d5dbSAndy Fiddaman
361*eed8d5dbSAndy Fiddaman if (ioctl(fd, PCIEB_IOCTL_RETRAIN) != 0) {
362*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to retrain link %s", device);
363*eed8d5dbSAndy Fiddaman }
364*eed8d5dbSAndy Fiddaman
365*eed8d5dbSAndy Fiddaman if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pcip->pia_priv_min) != 0)
366*eed8d5dbSAndy Fiddaman err(EXIT_FAILURE, "failed to reduce privileges");
367*eed8d5dbSAndy Fiddaman
368*eed8d5dbSAndy Fiddaman VERIFY0(close(fd));
369*eed8d5dbSAndy Fiddaman return (ret);
370*eed8d5dbSAndy Fiddaman }
371*eed8d5dbSAndy Fiddaman
372*eed8d5dbSAndy Fiddaman static const pcieadm_cmdtab_t pcieadm_cmds_link[] = {
373*eed8d5dbSAndy Fiddaman { "limit", pcieadm_link_limit, pcieadm_link_limit_usage },
374*eed8d5dbSAndy Fiddaman { "retrain", pcieadm_link_retrain, pcieadm_link_retrain_usage },
375*eed8d5dbSAndy Fiddaman { NULL }
376*eed8d5dbSAndy Fiddaman };
377*eed8d5dbSAndy Fiddaman
378*eed8d5dbSAndy Fiddaman int
pcieadm_link(pcieadm_t * pcip,int argc,char * argv[])379*eed8d5dbSAndy Fiddaman pcieadm_link(pcieadm_t *pcip, int argc, char *argv[])
380*eed8d5dbSAndy Fiddaman {
381*eed8d5dbSAndy Fiddaman return (pcieadm_walk_tab(pcip, pcieadm_cmds_link, argc, argv));
382*eed8d5dbSAndy Fiddaman }
383*eed8d5dbSAndy Fiddaman
384*eed8d5dbSAndy Fiddaman void
pcieadm_link_usage(FILE * f)385*eed8d5dbSAndy Fiddaman pcieadm_link_usage(FILE *f)
386*eed8d5dbSAndy Fiddaman {
387*eed8d5dbSAndy Fiddaman pcieadm_walk_usage(pcieadm_cmds_link, f);
388*eed8d5dbSAndy Fiddaman }
389