1330653Shselasky/*-
2330653Shselasky * Copyright (c) 2018, Mellanox Technologies, Ltd.  All rights reserved.
3330653Shselasky *
4330653Shselasky * Redistribution and use in source and binary forms, with or without
5330653Shselasky * modification, are permitted provided that the following conditions
6330653Shselasky * are met:
7330653Shselasky * 1. Redistributions of source code must retain the above copyright
8330653Shselasky *    notice, this list of conditions and the following disclaimer.
9330653Shselasky * 2. Redistributions in binary form must reproduce the above copyright
10330653Shselasky *    notice, this list of conditions and the following disclaimer in the
11330653Shselasky *    documentation and/or other materials provided with the distribution.
12330653Shselasky *
13330653Shselasky * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14330653Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15330653Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16330653Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17330653Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18330653Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19330653Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20330653Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21330653Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22330653Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23330653Shselasky * SUCH DAMAGE.
24330653Shselasky */
25330653Shselasky
26330653Shselasky#include <sys/cdefs.h>
27330653Shselasky__FBSDID("$FreeBSD: stable/11/usr.sbin/mlx5tool/mlx5tool.c 353256 2019-10-07 10:24:19Z hselasky $");
28330653Shselasky
29330653Shselasky#include <sys/param.h>
30330653Shselasky#include <sys/ioctl.h>
31347841Shselasky#include <sys/mman.h>
32347841Shselasky#include <sys/stat.h>
33330653Shselasky#include <dev/mlx5/mlx5io.h>
34330653Shselasky#include <ctype.h>
35330653Shselasky#include <err.h>
36330653Shselasky#include <errno.h>
37330653Shselasky#include <fcntl.h>
38330653Shselasky#include <paths.h>
39330653Shselasky#include <stdio.h>
40330653Shselasky#include <stdlib.h>
41330653Shselasky#include <string.h>
42330653Shselasky#include <unistd.h>
43330653Shselasky
44330653Shselasky/* stolen from pciconf.c: parsesel() */
45330653Shselaskystatic int
46347840Shselaskyparse_pci_addr(const char *addrstr, struct mlx5_tool_addr *addr)
47330653Shselasky{
48330653Shselasky	char *eppos;
49330653Shselasky	unsigned long selarr[4];
50330653Shselasky	int i;
51330653Shselasky
52331593Shselasky	if (addrstr == NULL) {
53331593Shselasky		warnx("no pci address specified");
54331593Shselasky		return (1);
55331593Shselasky	}
56330653Shselasky	if (strncmp(addrstr, "pci", 3) == 0) {
57330653Shselasky		addrstr += 3;
58330653Shselasky		i = 0;
59330653Shselasky		while (isdigit(*addrstr) && i < 4) {
60330653Shselasky			selarr[i++] = strtoul(addrstr, &eppos, 10);
61330653Shselasky			addrstr = eppos;
62330653Shselasky			if (*addrstr == ':')
63330653Shselasky				addrstr++;
64330653Shselasky		}
65330653Shselasky		if (i > 0 && *addrstr == '\0') {
66330653Shselasky			addr->func = (i > 2) ? selarr[--i] : 0;
67330653Shselasky			addr->slot = (i > 0) ? selarr[--i] : 0;
68330653Shselasky			addr->bus = (i > 0) ? selarr[--i] : 0;
69330653Shselasky			addr->domain = (i > 0) ? selarr[--i] : 0;
70330653Shselasky			return (0);
71330653Shselasky		}
72330653Shselasky	}
73330653Shselasky	warnx("invalid pci address %s", addrstr);
74330653Shselasky	return (1);
75330653Shselasky}
76330653Shselasky
77330653Shselaskystatic int
78347840Shselaskymlx5tool_save_dump(int ctldev, const struct mlx5_tool_addr *addr,
79330653Shselasky    const char *dumpname)
80330653Shselasky{
81330653Shselasky	struct mlx5_fwdump_get fdg;
82330653Shselasky	struct mlx5_fwdump_reg *rege;
83330653Shselasky	FILE *dump;
84330653Shselasky	size_t cnt;
85330653Shselasky	int error, res;
86330653Shselasky
87330653Shselasky	if (dumpname == NULL)
88330653Shselasky		dump = stdout;
89330653Shselasky	else
90330653Shselasky		dump = fopen(dumpname, "w");
91330653Shselasky	if (dump == NULL) {
92330653Shselasky		warn("open %s", dumpname);
93330653Shselasky		return (1);
94330653Shselasky	}
95330653Shselasky	res = 1;
96330653Shselasky	memset(&fdg, 0, sizeof(fdg));
97330653Shselasky	fdg.devaddr = *addr;
98330653Shselasky	error = ioctl(ctldev, MLX5_FWDUMP_GET, &fdg);
99330653Shselasky	if (error != 0) {
100330653Shselasky		warn("MLX5_FWDUMP_GET dumpsize");
101330653Shselasky		goto out;
102330653Shselasky	}
103330653Shselasky	rege = calloc(fdg.reg_filled, sizeof(*rege));
104330653Shselasky	if (rege == NULL) {
105330653Shselasky		warn("alloc rege");
106330653Shselasky		goto out;
107330653Shselasky	}
108330653Shselasky	fdg.buf = rege;
109330653Shselasky	fdg.reg_cnt = fdg.reg_filled;
110330653Shselasky	error = ioctl(ctldev, MLX5_FWDUMP_GET, &fdg);
111330653Shselasky	if (error != 0) {
112330653Shselasky		if (errno == ENOENT)
113330653Shselasky			warnx("no dump recorded");
114330653Shselasky		else
115330653Shselasky			warn("MLX5_FWDUMP_GET dump fetch");
116330653Shselasky		goto out;
117330653Shselasky	}
118330653Shselasky	for (cnt = 0; cnt < fdg.reg_cnt; cnt++, rege++)
119330653Shselasky		fprintf(dump, "0x%08x\t0x%08x\n", rege->addr, rege->val);
120330653Shselasky	res = 0;
121330653Shselaskyout:
122330653Shselasky	if (dump != stdout)
123330653Shselasky		fclose(dump);
124330653Shselasky	return (res);
125330653Shselasky}
126330653Shselasky
127330653Shselaskystatic int
128347840Shselaskymlx5tool_dump_reset(int ctldev, const struct mlx5_tool_addr *addr)
129330653Shselasky{
130330653Shselasky
131330653Shselasky	if (ioctl(ctldev, MLX5_FWDUMP_RESET, addr) == -1) {
132330653Shselasky		warn("MLX5_FWDUMP_RESET");
133330653Shselasky		return (1);
134330653Shselasky	}
135330653Shselasky	return (0);
136330653Shselasky}
137330653Shselasky
138330653Shselaskystatic int
139347840Shselaskymlx5tool_dump_force(int ctldev, const struct mlx5_tool_addr *addr)
140330653Shselasky{
141330653Shselasky
142330653Shselasky	if (ioctl(ctldev, MLX5_FWDUMP_FORCE, addr) == -1) {
143330653Shselasky		warn("MLX5_FWDUMP_FORCE");
144330653Shselasky		return (1);
145330653Shselasky	}
146330653Shselasky	return (0);
147330653Shselasky}
148330653Shselasky
149347841Shselaskystatic int
150347841Shselaskymlx5tool_fw_update(int ctldev, const struct mlx5_tool_addr *addr,
151347841Shselasky    const char *img_fw_path)
152347841Shselasky{
153347841Shselasky	struct stat st;
154347841Shselasky	struct mlx5_fw_update fwup;
155347841Shselasky	int error, fd, res;
156347841Shselasky
157347841Shselasky	res = 0;
158347841Shselasky	fd = open(img_fw_path, O_RDONLY);
159347841Shselasky	if (fd == -1) {
160347841Shselasky		warn("Unable to open %s", img_fw_path);
161347841Shselasky		res = 1;
162347841Shselasky		goto close_fd;
163347841Shselasky	}
164347841Shselasky	error = fstat(fd, &st);
165347841Shselasky	if (error != 0) {
166347841Shselasky		warn("Unable to stat %s", img_fw_path);
167347841Shselasky		res = 1;
168347841Shselasky		goto close_fd;
169347841Shselasky	}
170347841Shselasky	memset(&fwup, 0, sizeof(fwup));
171347841Shselasky	memcpy(&fwup.devaddr, addr, sizeof(fwup.devaddr));
172347841Shselasky	fwup.img_fw_data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE,
173347841Shselasky	    fd, 0);
174347841Shselasky	if (fwup.img_fw_data == MAP_FAILED) {
175347841Shselasky		warn("Unable to mmap %s", img_fw_path);
176347841Shselasky		res = 1;
177347841Shselasky		goto close_fd;
178347841Shselasky	}
179347841Shselasky	fwup.img_fw_data_len = st.st_size;
180347841Shselasky
181347841Shselasky	error = ioctl(ctldev, MLX5_FW_UPDATE, &fwup);
182347841Shselasky	if (error == -1) {
183347841Shselasky		warn("MLX5_FW_UPDATE");
184347841Shselasky	}
185347841Shselasky
186347841Shselasky	munmap(fwup.img_fw_data, st.st_size);
187347841Shselaskyclose_fd:
188347841Shselasky	close(fd);
189347841Shselasky	return (res);
190347841Shselasky}
191347841Shselasky
192347869Shselaskystatic int
193347869Shselaskymlx5tool_fw_reset(int ctldev, const struct mlx5_tool_addr *addr)
194347869Shselasky{
195347869Shselasky
196347869Shselasky	if (ioctl(ctldev, MLX5_FW_RESET, addr) == -1) {
197347869Shselasky		warn("MLX5_FW_RESET");
198347869Shselasky		return (1);
199347869Shselasky	}
200347869Shselasky	return (0);
201347869Shselasky}
202347869Shselasky
203353242Shselasky#define	MLX5_EEPROM_HIGH_PAGE_OFFSET		128
204353242Shselasky#define	MLX5_EEPROM_PAGE_LENGTH			256
205353242Shselasky
206330653Shselaskystatic void
207353242Shselaskymlx5tool_eeprom_print(struct mlx5_eeprom_get *eeprom_info)
208353242Shselasky{
209353256Shselasky	int index_in_row, line_length, row;
210353256Shselasky	size_t byte_to_write;
211353242Shselasky
212353242Shselasky	byte_to_write = 0;
213353242Shselasky	line_length = 16;
214353242Shselasky
215353242Shselasky	printf("\nOffset\t\tValues\n");
216353242Shselasky	printf("------\t\t------");
217353242Shselasky	while (byte_to_write < eeprom_info->eeprom_info_out_len) {
218353256Shselasky		printf("\n0x%04zX\t\t", byte_to_write);
219353242Shselasky		for (index_in_row = 0; index_in_row < line_length;
220353242Shselasky		    index_in_row++) {
221353242Shselasky			printf("%02X ",
222353242Shselasky			    ((uint8_t *)eeprom_info->eeprom_info_buf)[
223353242Shselasky			    byte_to_write]);
224353242Shselasky			byte_to_write++;
225353242Shselasky		}
226353242Shselasky	}
227353242Shselasky
228353242Shselasky	if (eeprom_info->eeprom_info_page_valid) {
229353242Shselasky		row = MLX5_EEPROM_HIGH_PAGE_OFFSET;
230353242Shselasky		printf("\n\nUpper Page 0x03\n");
231353242Shselasky		printf("\nOffset\t\tValues\n");
232353242Shselasky		printf("------\t\t------");
233353242Shselasky		for (row = MLX5_EEPROM_HIGH_PAGE_OFFSET;
234353242Shselasky		    row < MLX5_EEPROM_PAGE_LENGTH;) {
235353242Shselasky			printf("\n0x%04X\t\t", row);
236353242Shselasky			for (index_in_row = 0;
237353242Shselasky			     index_in_row < line_length;
238353242Shselasky			     index_in_row++) {
239353242Shselasky				printf("%02X ",
240353242Shselasky				    ((uint8_t *)eeprom_info->
241353242Shselasky				    eeprom_info_buf)[byte_to_write]);
242353242Shselasky				byte_to_write++;
243353242Shselasky				row++;
244353242Shselasky			}
245353242Shselasky		}
246353242Shselasky	}
247353242Shselasky	printf("\n");
248353242Shselasky}
249353242Shselasky
250353242Shselaskystatic int
251353242Shselaskymlx5tool_get_eeprom_info(int ctldev, const struct mlx5_tool_addr *addr)
252353242Shselasky{
253353242Shselasky	struct mlx5_eeprom_get eeprom_info;
254353242Shselasky	int error;
255353242Shselasky
256353242Shselasky	memset(&eeprom_info, 0, sizeof(eeprom_info));
257353242Shselasky	eeprom_info.devaddr = *addr;
258353242Shselasky
259353242Shselasky	error = ioctl(ctldev, MLX5_EEPROM_GET, &eeprom_info);
260353242Shselasky	if (error != 0) {
261353242Shselasky		warn("MLX5_EEPROM_GET");
262353242Shselasky		return (error);
263353242Shselasky	}
264353242Shselasky	eeprom_info.eeprom_info_buf =
265353242Shselasky	    malloc(eeprom_info.eeprom_info_out_len + MLX5_EEPROM_PAGE_LENGTH);
266353242Shselasky	if (eeprom_info.eeprom_info_buf == NULL) {
267353242Shselasky		warn("alloc eeprom_info.eeprom_info_buf ");
268353242Shselasky		return (ENOMEM);
269353242Shselasky	}
270353242Shselasky	error = ioctl(ctldev, MLX5_EEPROM_GET, &eeprom_info);
271353242Shselasky	if (error != 0) {
272353242Shselasky		warn("MLX5_EEPROM_GET");
273353242Shselasky		free(eeprom_info.eeprom_info_buf);
274353242Shselasky		return (error);
275353242Shselasky	}
276353242Shselasky
277353242Shselasky	mlx5tool_eeprom_print(&eeprom_info);
278353242Shselasky
279353242Shselasky	free(eeprom_info.eeprom_info_buf);
280353242Shselasky	return (0);
281353242Shselasky}
282353242Shselasky
283353242Shselaskystatic void
284330653Shselaskyusage(void)
285330653Shselasky{
286330653Shselasky
287330653Shselasky	fprintf(stderr,
288347841Shselasky	    "Usage: mlx5tool -d pci<d:b:s:f> [-w -o dump.file | -r |"
289347869Shselasky	    " -e | -f fw.mfa2 | -z]\n");
290330653Shselasky	fprintf(stderr, "\t-w - write firmware dump to the specified file\n");
291330653Shselasky	fprintf(stderr, "\t-r - reset dump\n");
292353242Shselasky	fprintf(stderr, "\t-E - get eeprom info\n");
293330653Shselasky	fprintf(stderr, "\t-e - force dump\n");
294347841Shselasky	fprintf(stderr, "\t-f fw.img - flash firmware from fw.img\n");
295347869Shselasky	fprintf(stderr, "\t-z - initiate firmware reset\n");
296330653Shselasky	exit(1);
297330653Shselasky}
298330653Shselasky
299330653Shselaskyenum mlx5_action {
300330653Shselasky	ACTION_DUMP_GET,
301330653Shselasky	ACTION_DUMP_RESET,
302330653Shselasky	ACTION_DUMP_FORCE,
303347841Shselasky	ACTION_FW_UPDATE,
304347869Shselasky	ACTION_FW_RESET,
305353242Shselasky	ACTION_GET_EEPROM_INFO,
306330653Shselasky	ACTION_NONE,
307330653Shselasky};
308330653Shselasky
309330653Shselaskyint
310330653Shselaskymain(int argc, char *argv[])
311330653Shselasky{
312347840Shselasky	struct mlx5_tool_addr addr;
313330653Shselasky	char *dumpname;
314330653Shselasky	char *addrstr;
315347841Shselasky	char *img_fw_path;
316330653Shselasky	int c, ctldev, res;
317330653Shselasky	enum mlx5_action act;
318330653Shselasky
319330653Shselasky	act = ACTION_NONE;
320330653Shselasky	addrstr = NULL;
321330653Shselasky	dumpname = NULL;
322347841Shselasky	img_fw_path = NULL;
323353242Shselasky	while ((c = getopt(argc, argv, "d:Eef:ho:rwz")) != -1) {
324330653Shselasky		switch (c) {
325330653Shselasky		case 'd':
326330653Shselasky			addrstr = optarg;
327330653Shselasky			break;
328330653Shselasky		case 'w':
329347870Shselasky			if (act != ACTION_NONE)
330347870Shselasky				usage();
331330653Shselasky			act = ACTION_DUMP_GET;
332330653Shselasky			break;
333353242Shselasky		case 'E':
334353242Shselasky			if (act != ACTION_NONE)
335353242Shselasky				usage();
336353242Shselasky			act = ACTION_GET_EEPROM_INFO;
337353242Shselasky			break;
338330653Shselasky		case 'e':
339347870Shselasky			if (act != ACTION_NONE)
340347870Shselasky				usage();
341347831Shselasky			act = ACTION_DUMP_FORCE;
342330653Shselasky			break;
343330653Shselasky		case 'o':
344330653Shselasky			dumpname = optarg;
345330653Shselasky			break;
346330653Shselasky		case 'r':
347347870Shselasky			if (act != ACTION_NONE)
348347870Shselasky				usage();
349330653Shselasky			act = ACTION_DUMP_RESET;
350330653Shselasky			break;
351347841Shselasky		case 'f':
352347870Shselasky			if (act != ACTION_NONE)
353347870Shselasky				usage();
354347841Shselasky			act = ACTION_FW_UPDATE;
355347841Shselasky			img_fw_path = optarg;
356347841Shselasky			break;
357347869Shselasky		case 'z':
358347870Shselasky			if (act != ACTION_NONE)
359347870Shselasky				usage();
360347869Shselasky			act = ACTION_FW_RESET;
361347869Shselasky			break;
362330653Shselasky		case 'h':
363330653Shselasky		default:
364330653Shselasky			usage();
365330653Shselasky		}
366330653Shselasky	}
367347841Shselasky	if (act == ACTION_NONE || (dumpname != NULL &&
368347841Shselasky	    act != ACTION_DUMP_GET) || (img_fw_path != NULL &&
369347841Shselasky	    act != ACTION_FW_UPDATE))
370330653Shselasky		usage();
371330653Shselasky	if (parse_pci_addr(addrstr, &addr) != 0)
372330653Shselasky		exit(1);
373330653Shselasky
374330653Shselasky	ctldev = open(MLX5_DEV_PATH, O_RDWR);
375330653Shselasky	if (ctldev == -1)
376330653Shselasky		err(1, "open "MLX5_DEV_PATH);
377330653Shselasky	switch (act) {
378330653Shselasky	case ACTION_DUMP_GET:
379330653Shselasky		res = mlx5tool_save_dump(ctldev, &addr, dumpname);
380330653Shselasky		break;
381330653Shselasky	case ACTION_DUMP_RESET:
382330653Shselasky		res = mlx5tool_dump_reset(ctldev, &addr);
383330653Shselasky		break;
384330653Shselasky	case ACTION_DUMP_FORCE:
385330653Shselasky		res = mlx5tool_dump_force(ctldev, &addr);
386330653Shselasky		break;
387347841Shselasky	case ACTION_FW_UPDATE:
388347841Shselasky		res = mlx5tool_fw_update(ctldev, &addr, img_fw_path);
389347841Shselasky		break;
390347869Shselasky	case ACTION_FW_RESET:
391347869Shselasky		res = mlx5tool_fw_reset(ctldev, &addr);
392347869Shselasky		break;
393353242Shselasky	case ACTION_GET_EEPROM_INFO:
394353242Shselasky		res = mlx5tool_get_eeprom_info(ctldev, &addr);
395353242Shselasky		break;
396330653Shselasky	default:
397330653Shselasky		res = 0;
398330653Shselasky		break;
399330653Shselasky	}
400330653Shselasky	close(ctldev);
401330653Shselasky	exit(res);
402330653Shselasky}
403