]> mj.ucw.cz Git - pciutils.git/blob - lmr/margin_args.c
pcilmr: Move most of pcilmr arguments parsing logic to the separate file
[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     "Usage:\n"
20     "pcilmr [--margin] [<margining options>] <downstream component> ...\n"
21     "pcilmr --full [<margining options>]\n"
22     "pcilmr --scan\n\n"
23     "Device Specifier:\n"
24     "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
25     "Modes:\n"
26     "--margin\t\tMargin selected Links\n"
27     "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
28     "--scan\t\t\tScan for Links available for margining\n\n"
29     "Margining options:\n\n"
30     "Margining Test settings:\n"
31     "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n"
32     "-l <lane>[,<lane>...]\tSpecify lanes for margining. Default: all link lanes.\n"
33     "\t\t\tRemember that Device may use Lane Reversal for Lane numbering.\n"
34     "\t\t\tHowever, utility uses logical lane numbers in arguments and for logging.\n"
35     "\t\t\tUtility will automatically determine Lane Reversal and tune its calls.\n"
36     "-e <errors>\t\tSpecify Error Count Limit for margining. Default: 4.\n"
37     "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
38     "\t\t\tDefault: all available Receivers (including Retimers).\n"
39     "-p <parallel_lanes>\tSpecify number of lanes to margin simultaneously.\n"
40     "\t\t\tDefault: 1.\n"
41     "\t\t\tAccording to spec it's possible for Receiver to margin up\n"
42     "\t\t\tto MaxLanes + 1 lanes simultaneously, but usually this works\n"
43     "\t\t\tbad, so this option is for experiments mostly.\n"
44     "-T\t\t\tTime Margining will continue until the Error Count is no more\n"
45     "\t\t\tthan an Error Count Limit. Use this option to find Link limit.\n"
46     "-V\t\t\tSame as -T option, but for Voltage.\n"
47     "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
48     "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n"
49     "Use only one of -T/-t options at the same time (same for -V/-v).\n"
50     "Without these options utility will use MaxSteps from Device\n"
51     "capabilities as test limit.\n\n"
52     "Margining Log settings:\n"
53     "-o <directory>\t\tSave margining results in csv form into the\n"
54     "\t\t\tspecified directory. Utility will generate file with the\n"
55     "\t\t\tname in form of 'lmr_<downstream component>_Rx#_<timestamp>.csv'\n"
56     "\t\t\tfor each successfully tested receiver.\n";
57
58 static struct pci_dev *
59 dev_for_filter(struct pci_access *pacc, char *filter)
60 {
61   struct pci_filter pci_filter;
62   pci_filter_init(pacc, &pci_filter);
63   if (pci_filter_parse_slot(&pci_filter, filter))
64     die("Invalid device ID: %s\n", filter);
65
66   if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
67     die("Invalid device ID: %s\n", filter);
68
69   if (pci_filter.domain == -1)
70     pci_filter.domain = 0;
71
72   for (struct pci_dev *p = pacc->devices; p; p = p->next)
73     {
74       if (pci_filter_match(&pci_filter, p))
75         return p;
76     }
77
78   die("No such PCI device: %s or you don't have enough privileges.\n", filter);
79 }
80
81 static u8
82 parse_csv_arg(char *arg, u8 *vals)
83 {
84   u8 cnt = 0;
85   char *token = strtok(arg, ",");
86   while (token)
87     {
88       vals[cnt] = atoi(token);
89       cnt++;
90       token = strtok(NULL, ",");
91     }
92   return cnt;
93 }
94
95 static u8
96 find_ready_links(struct pci_access *pacc, struct margin_link *links, bool cnt_only)
97 {
98   u8 cnt = 0;
99   for (struct pci_dev *p = pacc->devices; p; p = p->next)
100     {
101       if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
102         {
103           struct pci_dev *down = NULL;
104           struct pci_dev *up = NULL;
105           margin_find_pair(pacc, p, &down, &up);
106
107           if (down && margin_verify_link(down, up)
108               && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
109             {
110               if (!cnt_only)
111                 margin_fill_link(down, up, &(links[cnt]));
112               cnt++;
113             }
114         }
115     }
116   return cnt;
117 }
118
119 static void
120 init_link_args(struct margin_link_args *link_args, struct margin_com_args *com_args)
121 {
122   memset(link_args, 0, sizeof(*link_args));
123   link_args->common = com_args;
124   link_args->parallel_lanes = 1;
125 }
126
127 static void
128 parse_dev_args(int argc, char **argv, struct margin_link_args *args, u8 link_speed)
129 {
130   if (argc == optind)
131     return;
132   int c;
133   while ((c = getopt(argc, argv, "+r:l:p:t:v:VT")) != -1)
134     {
135       switch (c)
136         {
137           case 't':
138             args->steps_t = atoi(optarg);
139             break;
140           case 'T':
141             args->steps_t = 63;
142             break;
143           case 'v':
144             args->steps_v = atoi(optarg);
145             break;
146           case 'V':
147             args->steps_v = 127;
148             break;
149           case 'p':
150             args->parallel_lanes = atoi(optarg);
151             break;
152           case 'l':
153             args->lanes_n = parse_csv_arg(optarg, args->lanes);
154             break;
155           case 'r':
156             args->recvs_n = parse_csv_arg(optarg, args->recvs);
157             break;
158           case '?':
159             die("Invalid arguments\n\n%s", usage);
160             break;
161           default:
162             return;
163         }
164     }
165 }
166
167 struct margin_link *
168 margin_parse_util_args(struct pci_access *pacc, int argc, char **argv, enum margin_mode mode,
169                        u8 *links_n)
170 {
171   struct margin_com_args *com_args = xmalloc(sizeof(*com_args));
172   com_args->error_limit = 4;
173   com_args->run_margin = true;
174   com_args->verbosity = 1;
175   com_args->steps_utility = 0;
176   com_args->dir_for_csv = NULL;
177   com_args->save_csv = false;
178
179   int c;
180   while ((c = getopt(argc, argv, "+e:co:")) != -1)
181     {
182       switch (c)
183         {
184           case 'c':
185             com_args->run_margin = false;
186             break;
187           case 'e':
188             com_args->error_limit = atoi(optarg);
189             break;
190           case 'o':
191             com_args->dir_for_csv = optarg;
192             com_args->save_csv = true;
193             break;
194           default:
195             die("Invalid arguments\n\n%s", usage);
196         }
197     }
198
199   bool status = true;
200   if (mode == FULL && optind != argc)
201     status = false;
202   if (mode == MARGIN && optind == argc)
203     status = false;
204   if (!status && argc > 1)
205     die("Invalid arguments\n\n%s", usage);
206   if (!status)
207     {
208       printf("%s", usage);
209       exit(0);
210     }
211
212   u8 ports_n = 0;
213   struct margin_link *links = NULL;
214   char err[128];
215
216   if (mode == FULL)
217     {
218       ports_n = find_ready_links(pacc, NULL, true);
219       if (ports_n == 0)
220         die("Links not found or you don't have enough privileges.\n");
221       else
222         {
223           links = xmalloc(ports_n * sizeof(*links));
224           find_ready_links(pacc, links, false);
225           for (int i = 0; i < ports_n; i++)
226             init_link_args(&(links[i].args), com_args);
227         }
228     }
229   else if (mode == MARGIN)
230     {
231       while (optind != argc)
232         {
233           struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
234           optind++;
235           links = xrealloc(links, (ports_n + 1) * sizeof(*links));
236           struct pci_dev *down;
237           struct pci_dev *up;
238           if (!margin_find_pair(pacc, dev, &down, &up))
239             die("Cannot find pair for the specified device: %s\n", argv[optind]);
240           struct pci_cap *cap = pci_find_cap(down, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
241           if (!cap)
242             die("Looks like you don't have enough privileges to access "
243                 "Device Configuration Space.\nTry to run utility as root.\n");
244           if (!margin_fill_link(down, up, &(links[ports_n])))
245             {
246               margin_gen_bdfs(down, up, err, sizeof(err));
247               die("Link %s is not ready for margining.\n"
248                   "Link data rate must be 16 GT/s or 32 GT/s.\n"
249                   "Downstream Component must be at D0 PM state.\n",
250                   err);
251             }
252           init_link_args(&(links[ports_n].args), com_args);
253           parse_dev_args(argc, argv, &(links[ports_n].args),
254                          links[ports_n].down_port.link_speed - 4);
255           ports_n++;
256         }
257     }
258   else
259     die("Bug in the args parsing!\n");
260
261   *links_n = ports_n;
262   return links;
263 }