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() */
42static int
43parse_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
74static int
75mlx5tool_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;
118out:
119	if (dump != stdout)
120		fclose(dump);
121	return (res);
122}
123
124static int
125mlx5tool_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
135static int
136mlx5tool_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
146static int
147mlx5tool_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);
184close_fd:
185	close(fd);
186	return (res);
187}
188
189static int
190mlx5tool_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
203static void
204mlx5tool_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
247static int
248mlx5tool_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
280static void
281usage(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
296enum 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
306int
307main(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