xref: /freebsd/usr.sbin/mlx5tool/mlx5tool.c (revision d0b2dbfa0ecf2bbc9709efc5e20baf8e4b44bbbf)
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
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
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
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
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
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
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
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
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
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
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