1 /*-
2 * Copyright (c) 2018, Mellanox Technologies, Ltd. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/cdefs.h>
27 #include <sys/param.h>
28 #include <sys/ioctl.h>
29 #include <sys/mman.h>
30 #include <sys/stat.h>
31 #include <dev/mlx5/mlx5io.h>
32 #include <ctype.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <paths.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 /* stolen from pciconf.c: parsesel() */
43 static int
parse_pci_addr(const char * addrstr,struct mlx5_tool_addr * addr)44 parse_pci_addr(const char *addrstr, struct mlx5_tool_addr *addr)
45 {
46 char *eppos;
47 unsigned long selarr[4];
48 int i;
49
50 if (addrstr == NULL) {
51 warnx("no pci address specified");
52 return (1);
53 }
54 if (strncmp(addrstr, "pci", 3) == 0) {
55 addrstr += 3;
56 i = 0;
57 while (isdigit(*addrstr) && i < 4) {
58 selarr[i++] = strtoul(addrstr, &eppos, 10);
59 addrstr = eppos;
60 if (*addrstr == ':')
61 addrstr++;
62 }
63 if (i > 0 && *addrstr == '\0') {
64 addr->func = (i > 2) ? selarr[--i] : 0;
65 addr->slot = (i > 0) ? selarr[--i] : 0;
66 addr->bus = (i > 0) ? selarr[--i] : 0;
67 addr->domain = (i > 0) ? selarr[--i] : 0;
68 return (0);
69 }
70 }
71 warnx("invalid pci address %s", addrstr);
72 return (1);
73 }
74
75 static int
mlx5tool_save_dump(int ctldev,const struct mlx5_tool_addr * addr,const char * dumpname)76 mlx5tool_save_dump(int ctldev, const struct mlx5_tool_addr *addr,
77 const char *dumpname)
78 {
79 struct mlx5_fwdump_get fdg;
80 struct mlx5_fwdump_reg *rege;
81 FILE *dump;
82 size_t cnt;
83 int error, res;
84
85 if (dumpname == NULL)
86 dump = stdout;
87 else
88 dump = fopen(dumpname, "w");
89 if (dump == NULL) {
90 warn("open %s", dumpname);
91 return (1);
92 }
93 res = 1;
94 memset(&fdg, 0, sizeof(fdg));
95 fdg.devaddr = *addr;
96 error = ioctl(ctldev, MLX5_FWDUMP_GET, &fdg);
97 if (error != 0) {
98 warn("MLX5_FWDUMP_GET dumpsize");
99 goto out;
100 }
101 rege = calloc(fdg.reg_filled, sizeof(*rege));
102 if (rege == NULL) {
103 warn("alloc rege");
104 goto out;
105 }
106 fdg.buf = rege;
107 fdg.reg_cnt = fdg.reg_filled;
108 error = ioctl(ctldev, MLX5_FWDUMP_GET, &fdg);
109 if (error != 0) {
110 if (errno == ENOENT)
111 warnx("no dump recorded");
112 else
113 warn("MLX5_FWDUMP_GET dump fetch");
114 goto out;
115 }
116 for (cnt = 0; cnt < fdg.reg_cnt; cnt++, rege++)
117 fprintf(dump, "0x%08x\t0x%08x\n", rege->addr, rege->val);
118 res = 0;
119 out:
120 if (dump != stdout)
121 fclose(dump);
122 return (res);
123 }
124
125 static int
mlx5tool_dump_reset(int ctldev,const struct mlx5_tool_addr * addr)126 mlx5tool_dump_reset(int ctldev, const struct mlx5_tool_addr *addr)
127 {
128
129 if (ioctl(ctldev, MLX5_FWDUMP_RESET, addr) == -1) {
130 warn("MLX5_FWDUMP_RESET");
131 return (1);
132 }
133 return (0);
134 }
135
136 static int
mlx5tool_dump_force(int ctldev,const struct mlx5_tool_addr * addr)137 mlx5tool_dump_force(int ctldev, const struct mlx5_tool_addr *addr)
138 {
139
140 if (ioctl(ctldev, MLX5_FWDUMP_FORCE, addr) == -1) {
141 warn("MLX5_FWDUMP_FORCE");
142 return (1);
143 }
144 return (0);
145 }
146
147 static int
mlx5tool_fw_update(int ctldev,const struct mlx5_tool_addr * addr,const char * img_fw_path)148 mlx5tool_fw_update(int ctldev, const struct mlx5_tool_addr *addr,
149 const char *img_fw_path)
150 {
151 struct stat st;
152 struct mlx5_fw_update fwup;
153 int error, fd, res;
154
155 res = 0;
156 fd = open(img_fw_path, O_RDONLY);
157 if (fd == -1) {
158 warn("Unable to open %s", img_fw_path);
159 res = 1;
160 goto close_fd;
161 }
162 error = fstat(fd, &st);
163 if (error != 0) {
164 warn("Unable to stat %s", img_fw_path);
165 res = 1;
166 goto close_fd;
167 }
168 memset(&fwup, 0, sizeof(fwup));
169 memcpy(&fwup.devaddr, addr, sizeof(fwup.devaddr));
170 fwup.img_fw_data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE,
171 fd, 0);
172 if (fwup.img_fw_data == MAP_FAILED) {
173 warn("Unable to mmap %s", img_fw_path);
174 res = 1;
175 goto close_fd;
176 }
177 fwup.img_fw_data_len = st.st_size;
178
179 error = ioctl(ctldev, MLX5_FW_UPDATE, &fwup);
180 if (error == -1) {
181 warn("MLX5_FW_UPDATE");
182 }
183
184 munmap(fwup.img_fw_data, st.st_size);
185 close_fd:
186 close(fd);
187 return (res);
188 }
189
190 static int
mlx5tool_fw_reset(int ctldev,const struct mlx5_tool_addr * addr)191 mlx5tool_fw_reset(int ctldev, const struct mlx5_tool_addr *addr)
192 {
193
194 if (ioctl(ctldev, MLX5_FW_RESET, addr) == -1) {
195 warn("MLX5_FW_RESET");
196 return (1);
197 }
198 return (0);
199 }
200
201 #define MLX5_EEPROM_HIGH_PAGE_OFFSET 128
202 #define MLX5_EEPROM_PAGE_LENGTH 256
203
204 static void
mlx5tool_eeprom_print(struct mlx5_eeprom_get * eeprom_info)205 mlx5tool_eeprom_print(struct mlx5_eeprom_get *eeprom_info)
206 {
207 int index_in_row, line_length, row;
208 size_t byte_to_write;
209
210 byte_to_write = 0;
211 line_length = 16;
212
213 printf("\nOffset\t\tValues\n");
214 printf("------\t\t------");
215 while (byte_to_write < eeprom_info->eeprom_info_out_len) {
216 printf("\n0x%04zX\t\t", byte_to_write);
217 for (index_in_row = 0; index_in_row < line_length;
218 index_in_row++) {
219 printf("%02X ",
220 ((uint8_t *)eeprom_info->eeprom_info_buf)[
221 byte_to_write]);
222 byte_to_write++;
223 }
224 }
225
226 if (eeprom_info->eeprom_info_page_valid) {
227 row = MLX5_EEPROM_HIGH_PAGE_OFFSET;
228 printf("\n\nUpper Page 0x03\n");
229 printf("\nOffset\t\tValues\n");
230 printf("------\t\t------");
231 for (row = MLX5_EEPROM_HIGH_PAGE_OFFSET;
232 row < MLX5_EEPROM_PAGE_LENGTH;) {
233 printf("\n0x%04X\t\t", row);
234 for (index_in_row = 0;
235 index_in_row < line_length;
236 index_in_row++) {
237 printf("%02X ",
238 ((uint8_t *)eeprom_info->
239 eeprom_info_buf)[byte_to_write]);
240 byte_to_write++;
241 row++;
242 }
243 }
244 }
245 printf("\n");
246 }
247
248 static int
mlx5tool_get_eeprom_info(int ctldev,const struct mlx5_tool_addr * addr)249 mlx5tool_get_eeprom_info(int ctldev, const struct mlx5_tool_addr *addr)
250 {
251 struct mlx5_eeprom_get eeprom_info;
252 int error;
253
254 memset(&eeprom_info, 0, sizeof(eeprom_info));
255 eeprom_info.devaddr = *addr;
256
257 error = ioctl(ctldev, MLX5_EEPROM_GET, &eeprom_info);
258 if (error != 0) {
259 warn("MLX5_EEPROM_GET");
260 return (error);
261 }
262 eeprom_info.eeprom_info_buf =
263 malloc(eeprom_info.eeprom_info_out_len + MLX5_EEPROM_PAGE_LENGTH);
264 if (eeprom_info.eeprom_info_buf == NULL) {
265 warn("alloc eeprom_info.eeprom_info_buf ");
266 return (ENOMEM);
267 }
268 error = ioctl(ctldev, MLX5_EEPROM_GET, &eeprom_info);
269 if (error != 0) {
270 warn("MLX5_EEPROM_GET");
271 free(eeprom_info.eeprom_info_buf);
272 return (error);
273 }
274
275 mlx5tool_eeprom_print(&eeprom_info);
276
277 free(eeprom_info.eeprom_info_buf);
278 return (0);
279 }
280
281 static void
usage(void)282 usage(void)
283 {
284
285 fprintf(stderr,
286 "Usage: mlx5tool -d pci<d:b:s:f> [-w -o dump.file | -r |"
287 " -e | -f fw.mfa2 | -z]\n");
288 fprintf(stderr, "\t-w - write firmware dump to the specified file\n");
289 fprintf(stderr, "\t-r - reset dump\n");
290 fprintf(stderr, "\t-E - get eeprom info\n");
291 fprintf(stderr, "\t-e - force dump\n");
292 fprintf(stderr, "\t-f fw.img - flash firmware from fw.img\n");
293 fprintf(stderr, "\t-z - initiate firmware reset\n");
294 exit(1);
295 }
296
297 enum mlx5_action {
298 ACTION_DUMP_GET,
299 ACTION_DUMP_RESET,
300 ACTION_DUMP_FORCE,
301 ACTION_FW_UPDATE,
302 ACTION_FW_RESET,
303 ACTION_GET_EEPROM_INFO,
304 ACTION_NONE,
305 };
306
307 int
main(int argc,char * argv[])308 main(int argc, char *argv[])
309 {
310 struct mlx5_tool_addr addr;
311 char *dumpname;
312 char *addrstr;
313 char *img_fw_path;
314 int c, ctldev, res;
315 enum mlx5_action act;
316
317 act = ACTION_NONE;
318 addrstr = NULL;
319 dumpname = NULL;
320 img_fw_path = NULL;
321 while ((c = getopt(argc, argv, "d:Eef:ho:rwz")) != -1) {
322 switch (c) {
323 case 'd':
324 addrstr = optarg;
325 break;
326 case 'w':
327 if (act != ACTION_NONE)
328 usage();
329 act = ACTION_DUMP_GET;
330 break;
331 case 'E':
332 if (act != ACTION_NONE)
333 usage();
334 act = ACTION_GET_EEPROM_INFO;
335 break;
336 case 'e':
337 if (act != ACTION_NONE)
338 usage();
339 act = ACTION_DUMP_FORCE;
340 break;
341 case 'o':
342 dumpname = optarg;
343 break;
344 case 'r':
345 if (act != ACTION_NONE)
346 usage();
347 act = ACTION_DUMP_RESET;
348 break;
349 case 'f':
350 if (act != ACTION_NONE)
351 usage();
352 act = ACTION_FW_UPDATE;
353 img_fw_path = optarg;
354 break;
355 case 'z':
356 if (act != ACTION_NONE)
357 usage();
358 act = ACTION_FW_RESET;
359 break;
360 case 'h':
361 default:
362 usage();
363 }
364 }
365 if (act == ACTION_NONE || (dumpname != NULL &&
366 act != ACTION_DUMP_GET) || (img_fw_path != NULL &&
367 act != ACTION_FW_UPDATE))
368 usage();
369 if (parse_pci_addr(addrstr, &addr) != 0)
370 exit(1);
371
372 ctldev = open(MLX5_DEV_PATH, O_RDWR);
373 if (ctldev == -1)
374 err(1, "open "MLX5_DEV_PATH);
375 switch (act) {
376 case ACTION_DUMP_GET:
377 res = mlx5tool_save_dump(ctldev, &addr, dumpname);
378 break;
379 case ACTION_DUMP_RESET:
380 res = mlx5tool_dump_reset(ctldev, &addr);
381 break;
382 case ACTION_DUMP_FORCE:
383 res = mlx5tool_dump_force(ctldev, &addr);
384 break;
385 case ACTION_FW_UPDATE:
386 res = mlx5tool_fw_update(ctldev, &addr, img_fw_path);
387 break;
388 case ACTION_FW_RESET:
389 res = mlx5tool_fw_reset(ctldev, &addr);
390 break;
391 case ACTION_GET_EEPROM_INFO:
392 res = mlx5tool_get_eeprom_info(ctldev, &addr);
393 break;
394 default:
395 res = 0;
396 break;
397 }
398 close(ctldev);
399 exit(res);
400 }
401