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