]> mj.ucw.cz Git - pciutils.git/blob - lmr/margin_args.c
lspci: add VirtIO SharedMemory capability support
[pciutils.git] / lmr / margin_args.c
1 /*
2  *      The PCI Utilities -- Parse pcilmr utility arguments
3  *
4  *      Copyright (c) 2024 KNS Group LLC (YADRO)
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL v2+.
7  *
8  *      SPDX-License-Identifier: GPL-2.0-or-later
9  */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "lmr.h"
16
17 const char *usage
18   = "! Utility requires preliminary preparation of the system. Refer to the pcilmr man page !\n\n"
19     "Brief usage (see man for all options):\n"
20     "pcilmr [--margin] [<common options>] <link port> [<link options>] [<link port> [<link "
21     "options>] ...]\n"
22     "pcilmr --full [<common options>]\n"
23     "pcilmr --scan\n\n"
24     "You can specify Downstream or Upstream Port of the Link.\nPort Specifier:\n"
25     "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
26     "Modes:\n"
27     "--margin\t\tMargin selected Links\n"
28     "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
29     "--scan\t\t\tScan for Links available for margining\n\n"
30     "Margining options (see man for all options):\n\n"
31     "Common (for all specified links) options:\n"
32     "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n\n"
33     "Link specific options:\n"
34     "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
35     "\t\t\tDefault: all available Receivers (including Retimers).\n"
36     "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
37     "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n";
38
39 static struct pci_dev *
40 dev_for_filter(struct pci_access *pacc, char *filter)
41 {
42   struct pci_filter pci_filter;
43   pci_filter_init(pacc, &pci_filter);
44   if (pci_filter_parse_slot(&pci_filter, filter))
45     die("Invalid device ID: %s\n", filter);
46
47   if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
48     die("Invalid device ID: %s\n", filter);
49
50   if (pci_filter.domain == -1)
51     pci_filter.domain = 0;
52
53   for (struct pci_dev *p = pacc->devices; p; p = p->next)
54     {
55       if (pci_filter_match(&pci_filter, p))
56         return p;
57     }
58
59   die("No such PCI device: %s or you don't have enough privileges.\n", filter);
60 }
61
62 static u8
63 parse_csv_arg(char *arg, u8 *vals)
64 {
65   u8 cnt = 0;
66   char *token = strtok(arg, ",");
67   while (token)
68     {
69       vals[cnt] = atoi(token);
70       cnt++;
71       token = strtok(NULL, ",");
72     }
73   return cnt;
74 }
75
76 static u8
77 find_ready_links(struct pci_access *pacc, struct margin_link *links, bool cnt_only)
78 {
79   u8 cnt = 0;
80   for (struct pci_dev *p = pacc->devices; p; p = p->next)
81     {
82       if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
83         {
84           struct pci_dev *down = NULL;
85           struct pci_dev *up = NULL;
86           margin_find_pair(pacc, p, &down, &up);
87
88           if (down && margin_verify_link(down, up)
89               && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
90             {
91               if (!cnt_only)
92                 margin_fill_link(down, up, &(links[cnt]));
93               cnt++;
94             }
95         }
96     }
97   return cnt;
98 }
99
100 static void
101 init_link_args(struct margin_link_args *link_args, struct margin_com_args *com_args)
102 {
103   memset(link_args, 0, sizeof(*link_args));
104   link_args->common = com_args;
105   link_args->parallel_lanes = 1;
106 }
107
108 static void
109 parse_dev_args(int argc, char **argv, struct margin_link_args *args, u8 link_speed)
110 {
111   if (argc == optind)
112     return;
113   int c;
114   while ((c = getopt(argc, argv, "+r:l:p:t:v:VTg:")) != -1)
115     {
116       switch (c)
117         {
118           case 't':
119             args->steps_t = atoi(optarg);
120             break;
121           case 'T':
122             args->steps_t = 63;
123             break;
124           case 'v':
125             args->steps_v = atoi(optarg);
126             break;
127           case 'V':
128             args->steps_v = 127;
129             break;
130           case 'p':
131             args->parallel_lanes = atoi(optarg);
132             break;
133           case 'l':
134             args->lanes_n = parse_csv_arg(optarg, args->lanes);
135             break;
136           case 'r':
137             args->recvs_n = parse_csv_arg(optarg, args->recvs);
138             break;
139             case 'g': {
140               char recv[2] = { 0 };
141               char dir[2] = { 0 };
142               char unit[4] = { 0 };
143               float criteria = 0.0;
144               char eye[2] = { 0 };
145               int cons[3] = { 0 };
146
147               int ret = sscanf(optarg, "%1[1-6]%1[tv]=%f%n%3[%,ps]%n%1[f]%n", recv, dir, &criteria,
148                                &cons[0], unit, &cons[1], eye, &cons[2]);
149               if (ret < 3)
150                 {
151                   ret = sscanf(optarg, "%1[1-6]%1[tv]=%1[f]%n,%f%n%2[ps%]%n", recv, dir, eye,
152                                &cons[0], &criteria, &cons[1], unit, &cons[2]);
153                   if (ret < 3)
154                     die("Invalid arguments\n\n%s", usage);
155                 }
156
157               int consumed = 0;
158               for (int i = 0; i < 3; i++)
159                 if (cons[i] > consumed)
160                   consumed = cons[i];
161               if ((size_t)consumed != strlen(optarg))
162                 die("Invalid arguments\n\n%s", usage);
163               if (criteria < 0)
164                 die("Invalid arguments\n\n%s", usage);
165               if (strstr(unit, ",") && eye[0] == 0)
166                 die("Invalid arguments\n\n%s", usage);
167
168               u8 recv_n = recv[0] - '0' - 1;
169               if (dir[0] == 'v')
170                 {
171                   if (unit[0] != ',' && unit[0] != 0)
172                     die("Invalid arguments\n\n%s", usage);
173                   args->recv_args[recv_n].v.valid = true;
174                   args->recv_args[recv_n].v.criteria = criteria;
175                   if (eye[0] != 0)
176                     args->recv_args[recv_n].v.one_side_is_whole = true;
177                 }
178               else
179                 {
180                   if (unit[0] == '%')
181                     criteria = criteria / 100.0 * margin_ui[link_speed];
182                   else if (unit[0] != 0 && (unit[0] != 'p' || unit[1] != 's'))
183                     die("Invalid arguments\n\n%s", usage);
184                   else if (unit[0] == 0 && criteria != 0)
185                     die("Invalid arguments\n\n%s", usage);
186                   args->recv_args[recv_n].t.valid = true;
187                   args->recv_args[recv_n].t.criteria = criteria;
188                   if (eye[0] != 0)
189                     args->recv_args[recv_n].t.one_side_is_whole = true;
190                 }
191               break;
192             }
193           case '?':
194             die("Invalid arguments\n\n%s", usage);
195             break;
196           default:
197             return;
198         }
199     }
200 }
201
202 struct margin_link *
203 margin_parse_util_args(struct pci_access *pacc, int argc, char **argv, enum margin_mode mode,
204                        u8 *links_n)
205 {
206   struct margin_com_args *com_args = xmalloc(sizeof(*com_args));
207   com_args->error_limit = 4;
208   com_args->run_margin = true;
209   com_args->verbosity = 1;
210   com_args->steps_utility = 0;
211   com_args->dir_for_csv = NULL;
212   com_args->save_csv = false;
213   com_args->dwell_time = 1;
214
215   int c;
216   while ((c = getopt(argc, argv, "+e:co:d:")) != -1)
217     {
218       switch (c)
219         {
220           case 'c':
221             com_args->run_margin = false;
222             break;
223           case 'e':
224             com_args->error_limit = atoi(optarg);
225             break;
226           case 'o':
227             com_args->dir_for_csv = optarg;
228             com_args->save_csv = true;
229             break;
230           case 'd':
231             com_args->dwell_time = atoi(optarg);
232             break;
233           default:
234             die("Invalid arguments\n\n%s", usage);
235         }
236     }
237
238   bool status = true;
239   if (mode == FULL && optind != argc)
240     status = false;
241   if (mode == MARGIN && optind == argc)
242     status = false;
243   if (!status && argc > 1)
244     die("Invalid arguments\n\n%s", usage);
245   if (!status)
246     {
247       printf("%s", usage);
248       exit(0);
249     }
250
251   u8 ports_n = 0;
252   struct margin_link *links = NULL;
253   char err[128];
254
255   if (mode == FULL)
256     {
257       ports_n = find_ready_links(pacc, NULL, true);
258       if (ports_n == 0)
259         die("Links not found or you don't have enough privileges.\n");
260       else
261         {
262           links = xmalloc(ports_n * sizeof(*links));
263           find_ready_links(pacc, links, false);
264           for (int i = 0; i < ports_n; i++)
265             init_link_args(&(links[i].args), com_args);
266         }
267     }
268   else if (mode == MARGIN)
269     {
270       while (optind != argc)
271         {
272           struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
273           optind++;
274           links = xrealloc(links, (ports_n + 1) * sizeof(*links));
275           struct pci_dev *down;
276           struct pci_dev *up;
277           if (!margin_find_pair(pacc, dev, &down, &up))
278             die("Cannot find pair for the specified device: %s\n", argv[optind - 1]);
279           struct pci_cap *cap = pci_find_cap(down, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
280           if (!cap)
281             die("Looks like you don't have enough privileges to access "
282                 "Device Configuration Space.\nTry to run utility as root.\n");
283           if (!margin_fill_link(down, up, &(links[ports_n])))
284             {
285               margin_gen_bdfs(down, up, err, sizeof(err));
286               die("Link %s is not ready for margining.\n"
287                   "Link data rate must be 16 GT/s or 32 GT/s.\n"
288                   "Downstream Component must be at D0 PM state.\n",
289                   err);
290             }
291           init_link_args(&(links[ports_n].args), com_args);
292           parse_dev_args(argc, argv, &(links[ports_n].args),
293                          links[ports_n].down_port.link_speed - 4);
294           ports_n++;
295         }
296     }
297   else
298     die("Bug in the args parsing!\n");
299
300   *links_n = ports_n;
301   return links;
302 }