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