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