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