mlx5tool.c revision 353256
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: stable/11/usr.sbin/mlx5tool/mlx5tool.c 353256 2019-10-07 10:24:19Z hselasky $");
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() */
45static int
46parse_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
77static int
78mlx5tool_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;
121out:
122	if (dump != stdout)
123		fclose(dump);
124	return (res);
125}
126
127static int
128mlx5tool_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
138static int
139mlx5tool_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
149static int
150mlx5tool_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);
187close_fd:
188	close(fd);
189	return (res);
190}
191
192static int
193mlx5tool_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
206static void
207mlx5tool_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
250static int
251mlx5tool_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
283static void
284usage(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
299enum 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
309int
310main(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