xref: /freebsd/usr.sbin/mlx5tool/mlx5tool.c (revision 6683132d54bd6d589889e43dabdc53d35e38a028)
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 static void
204 usage(void)
205 {
206 
207 	fprintf(stderr,
208 	    "Usage: mlx5tool -d pci<d:b:s:f> [-w -o dump.file | -r |"
209 	    " -e | -f fw.mfa2 | -z]\n");
210 	fprintf(stderr, "\t-w - write firmware dump to the specified file\n");
211 	fprintf(stderr, "\t-r - reset dump\n");
212 	fprintf(stderr, "\t-e - force dump\n");
213 	fprintf(stderr, "\t-f fw.img - flash firmware from fw.img\n");
214 	fprintf(stderr, "\t-z - initiate firmware reset\n");
215 	exit(1);
216 }
217 
218 enum mlx5_action {
219 	ACTION_DUMP_GET,
220 	ACTION_DUMP_RESET,
221 	ACTION_DUMP_FORCE,
222 	ACTION_FW_UPDATE,
223 	ACTION_FW_RESET,
224 	ACTION_NONE,
225 };
226 
227 int
228 main(int argc, char *argv[])
229 {
230 	struct mlx5_tool_addr addr;
231 	char *dumpname;
232 	char *addrstr;
233 	char *img_fw_path;
234 	int c, ctldev, res;
235 	enum mlx5_action act;
236 
237 	act = ACTION_NONE;
238 	addrstr = NULL;
239 	dumpname = NULL;
240 	img_fw_path = NULL;
241 	while ((c = getopt(argc, argv, "d:ef:ho:rwz")) != -1) {
242 		switch (c) {
243 		case 'd':
244 			addrstr = optarg;
245 			break;
246 		case 'w':
247 			if (act != ACTION_NONE)
248 				usage();
249 			act = ACTION_DUMP_GET;
250 			break;
251 		case 'e':
252 			if (act != ACTION_NONE)
253 				usage();
254 			act = ACTION_DUMP_FORCE;
255 			break;
256 		case 'o':
257 			dumpname = optarg;
258 			break;
259 		case 'r':
260 			if (act != ACTION_NONE)
261 				usage();
262 			act = ACTION_DUMP_RESET;
263 			break;
264 		case 'f':
265 			if (act != ACTION_NONE)
266 				usage();
267 			act = ACTION_FW_UPDATE;
268 			img_fw_path = optarg;
269 			break;
270 		case 'z':
271 			if (act != ACTION_NONE)
272 				usage();
273 			act = ACTION_FW_RESET;
274 			break;
275 		case 'h':
276 		default:
277 			usage();
278 		}
279 	}
280 	if (act == ACTION_NONE || (dumpname != NULL &&
281 	    act != ACTION_DUMP_GET) || (img_fw_path != NULL &&
282 	    act != ACTION_FW_UPDATE))
283 		usage();
284 	if (parse_pci_addr(addrstr, &addr) != 0)
285 		exit(1);
286 
287 	ctldev = open(MLX5_DEV_PATH, O_RDWR);
288 	if (ctldev == -1)
289 		err(1, "open "MLX5_DEV_PATH);
290 	switch (act) {
291 	case ACTION_DUMP_GET:
292 		res = mlx5tool_save_dump(ctldev, &addr, dumpname);
293 		break;
294 	case ACTION_DUMP_RESET:
295 		res = mlx5tool_dump_reset(ctldev, &addr);
296 		break;
297 	case ACTION_DUMP_FORCE:
298 		res = mlx5tool_dump_force(ctldev, &addr);
299 		break;
300 	case ACTION_FW_UPDATE:
301 		res = mlx5tool_fw_update(ctldev, &addr, img_fw_path);
302 		break;
303 	case ACTION_FW_RESET:
304 		res = mlx5tool_fw_reset(ctldev, &addr);
305 		break;
306 	default:
307 		res = 0;
308 		break;
309 	}
310 	close(ctldev);
311 	exit(res);
312 }
313