aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/mlx5tool/mlx5tool.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/mlx5tool/mlx5tool.c')
-rw-r--r--usr.sbin/mlx5tool/mlx5tool.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/usr.sbin/mlx5tool/mlx5tool.c b/usr.sbin/mlx5tool/mlx5tool.c
new file mode 100644
index 000000000000..f58434c91740
--- /dev/null
+++ b/usr.sbin/mlx5tool/mlx5tool.c
@@ -0,0 +1,399 @@
+/*-
+ * Copyright (c) 2018, Mellanox Technologies, Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <dev/mlx5/mlx5io.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* stolen from pciconf.c: parsesel() */
+static int
+parse_pci_addr(const char *addrstr, struct mlx5_tool_addr *addr)
+{
+ char *eppos;
+ unsigned long selarr[4];
+ int i;
+
+ if (addrstr == NULL) {
+ warnx("no pci address specified");
+ return (1);
+ }
+ if (strncmp(addrstr, "pci", 3) == 0) {
+ addrstr += 3;
+ i = 0;
+ while (isdigit(*addrstr) && i < 4) {
+ selarr[i++] = strtoul(addrstr, &eppos, 10);
+ addrstr = eppos;
+ if (*addrstr == ':')
+ addrstr++;
+ }
+ if (i > 0 && *addrstr == '\0') {
+ addr->func = (i > 2) ? selarr[--i] : 0;
+ addr->slot = (i > 0) ? selarr[--i] : 0;
+ addr->bus = (i > 0) ? selarr[--i] : 0;
+ addr->domain = (i > 0) ? selarr[--i] : 0;
+ return (0);
+ }
+ }
+ warnx("invalid pci address %s", addrstr);
+ return (1);
+}
+
+static int
+mlx5tool_save_dump(int ctldev, const struct mlx5_tool_addr *addr,
+ const char *dumpname)
+{
+ struct mlx5_fwdump_get fdg;
+ struct mlx5_fwdump_reg *rege;
+ FILE *dump;
+ size_t cnt;
+ int error, res;
+
+ if (dumpname == NULL)
+ dump = stdout;
+ else
+ dump = fopen(dumpname, "w");
+ if (dump == NULL) {
+ warn("open %s", dumpname);
+ return (1);
+ }
+ res = 1;
+ memset(&fdg, 0, sizeof(fdg));
+ fdg.devaddr = *addr;
+ error = ioctl(ctldev, MLX5_FWDUMP_GET, &fdg);
+ if (error != 0) {
+ warn("MLX5_FWDUMP_GET dumpsize");
+ goto out;
+ }
+ rege = calloc(fdg.reg_filled, sizeof(*rege));
+ if (rege == NULL) {
+ warn("alloc rege");
+ goto out;
+ }
+ fdg.buf = rege;
+ fdg.reg_cnt = fdg.reg_filled;
+ error = ioctl(ctldev, MLX5_FWDUMP_GET, &fdg);
+ if (error != 0) {
+ if (errno == ENOENT)
+ warnx("no dump recorded");
+ else
+ warn("MLX5_FWDUMP_GET dump fetch");
+ goto out;
+ }
+ for (cnt = 0; cnt < fdg.reg_cnt; cnt++, rege++)
+ fprintf(dump, "0x%08x\t0x%08x\n", rege->addr, rege->val);
+ res = 0;
+out:
+ if (dump != stdout)
+ fclose(dump);
+ return (res);
+}
+
+static int
+mlx5tool_dump_reset(int ctldev, const struct mlx5_tool_addr *addr)
+{
+
+ if (ioctl(ctldev, MLX5_FWDUMP_RESET, addr) == -1) {
+ warn("MLX5_FWDUMP_RESET");
+ return (1);
+ }
+ return (0);
+}
+
+static int
+mlx5tool_dump_force(int ctldev, const struct mlx5_tool_addr *addr)
+{
+
+ if (ioctl(ctldev, MLX5_FWDUMP_FORCE, addr) == -1) {
+ warn("MLX5_FWDUMP_FORCE");
+ return (1);
+ }
+ return (0);
+}
+
+static int
+mlx5tool_fw_update(int ctldev, const struct mlx5_tool_addr *addr,
+ const char *img_fw_path)
+{
+ struct stat st;
+ struct mlx5_fw_update fwup;
+ int error, fd, res;
+
+ res = 0;
+ fd = open(img_fw_path, O_RDONLY);
+ if (fd == -1) {
+ warn("Unable to open %s", img_fw_path);
+ res = 1;
+ goto close_fd;
+ }
+ error = fstat(fd, &st);
+ if (error != 0) {
+ warn("Unable to stat %s", img_fw_path);
+ res = 1;
+ goto close_fd;
+ }
+ memset(&fwup, 0, sizeof(fwup));
+ memcpy(&fwup.devaddr, addr, sizeof(fwup.devaddr));
+ fwup.img_fw_data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE,
+ fd, 0);
+ if (fwup.img_fw_data == MAP_FAILED) {
+ warn("Unable to mmap %s", img_fw_path);
+ res = 1;
+ goto close_fd;
+ }
+ fwup.img_fw_data_len = st.st_size;
+
+ error = ioctl(ctldev, MLX5_FW_UPDATE, &fwup);
+ if (error == -1) {
+ warn("MLX5_FW_UPDATE");
+ }
+
+ munmap(fwup.img_fw_data, st.st_size);
+close_fd:
+ close(fd);
+ return (res);
+}
+
+static int
+mlx5tool_fw_reset(int ctldev, const struct mlx5_tool_addr *addr)
+{
+
+ if (ioctl(ctldev, MLX5_FW_RESET, addr) == -1) {
+ warn("MLX5_FW_RESET");
+ return (1);
+ }
+ return (0);
+}
+
+#define MLX5_EEPROM_HIGH_PAGE_OFFSET 128
+#define MLX5_EEPROM_PAGE_LENGTH 256
+
+static void
+mlx5tool_eeprom_print(struct mlx5_eeprom_get *eeprom_info)
+{
+ int index_in_row, line_length, row;
+ size_t byte_to_write;
+
+ byte_to_write = 0;
+ line_length = 16;
+
+ printf("\nOffset\t\tValues\n");
+ printf("------\t\t------");
+ while (byte_to_write < eeprom_info->eeprom_info_out_len) {
+ printf("\n0x%04zX\t\t", byte_to_write);
+ for (index_in_row = 0; index_in_row < line_length;
+ index_in_row++) {
+ printf("%02X ",
+ ((uint8_t *)eeprom_info->eeprom_info_buf)[
+ byte_to_write]);
+ byte_to_write++;
+ }
+ }
+
+ if (eeprom_info->eeprom_info_page_valid) {
+ row = MLX5_EEPROM_HIGH_PAGE_OFFSET;
+ printf("\n\nUpper Page 0x03\n");
+ printf("\nOffset\t\tValues\n");
+ printf("------\t\t------");
+ for (row = MLX5_EEPROM_HIGH_PAGE_OFFSET;
+ row < MLX5_EEPROM_PAGE_LENGTH;) {
+ printf("\n0x%04X\t\t", row);
+ for (index_in_row = 0;
+ index_in_row < line_length;
+ index_in_row++) {
+ printf("%02X ",
+ ((uint8_t *)eeprom_info->
+ eeprom_info_buf)[byte_to_write]);
+ byte_to_write++;
+ row++;
+ }
+ }
+ }
+ printf("\n");
+}
+
+static int
+mlx5tool_get_eeprom_info(int ctldev, const struct mlx5_tool_addr *addr)
+{
+ struct mlx5_eeprom_get eeprom_info;
+ int error;
+
+ memset(&eeprom_info, 0, sizeof(eeprom_info));
+ eeprom_info.devaddr = *addr;
+
+ error = ioctl(ctldev, MLX5_EEPROM_GET, &eeprom_info);
+ if (error != 0) {
+ warn("MLX5_EEPROM_GET");
+ return (error);
+ }
+ eeprom_info.eeprom_info_buf =
+ malloc(eeprom_info.eeprom_info_out_len + MLX5_EEPROM_PAGE_LENGTH);
+ if (eeprom_info.eeprom_info_buf == NULL) {
+ warn("alloc eeprom_info.eeprom_info_buf ");
+ return (ENOMEM);
+ }
+ error = ioctl(ctldev, MLX5_EEPROM_GET, &eeprom_info);
+ if (error != 0) {
+ warn("MLX5_EEPROM_GET");
+ free(eeprom_info.eeprom_info_buf);
+ return (error);
+ }
+
+ mlx5tool_eeprom_print(&eeprom_info);
+
+ free(eeprom_info.eeprom_info_buf);
+ return (0);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "Usage: mlx5tool -d pci<d:b:s:f> [-w -o dump.file | -r |"
+ " -e | -f fw.mfa2 | -z]\n");
+ fprintf(stderr, "\t-w - write firmware dump to the specified file\n");
+ fprintf(stderr, "\t-r - reset dump\n");
+ fprintf(stderr, "\t-E - get eeprom info\n");
+ fprintf(stderr, "\t-e - force dump\n");
+ fprintf(stderr, "\t-f fw.img - flash firmware from fw.img\n");
+ fprintf(stderr, "\t-z - initiate firmware reset\n");
+ exit(1);
+}
+
+enum mlx5_action {
+ ACTION_DUMP_GET,
+ ACTION_DUMP_RESET,
+ ACTION_DUMP_FORCE,
+ ACTION_FW_UPDATE,
+ ACTION_FW_RESET,
+ ACTION_GET_EEPROM_INFO,
+ ACTION_NONE,
+};
+
+int
+main(int argc, char *argv[])
+{
+ struct mlx5_tool_addr addr;
+ char *dumpname;
+ char *addrstr;
+ char *img_fw_path;
+ int c, ctldev, res;
+ enum mlx5_action act;
+
+ act = ACTION_NONE;
+ addrstr = NULL;
+ dumpname = NULL;
+ img_fw_path = NULL;
+ while ((c = getopt(argc, argv, "d:Eef:ho:rwz")) != -1) {
+ switch (c) {
+ case 'd':
+ addrstr = optarg;
+ break;
+ case 'w':
+ if (act != ACTION_NONE)
+ usage();
+ act = ACTION_DUMP_GET;
+ break;
+ case 'E':
+ if (act != ACTION_NONE)
+ usage();
+ act = ACTION_GET_EEPROM_INFO;
+ break;
+ case 'e':
+ if (act != ACTION_NONE)
+ usage();
+ act = ACTION_DUMP_FORCE;
+ break;
+ case 'o':
+ dumpname = optarg;
+ break;
+ case 'r':
+ if (act != ACTION_NONE)
+ usage();
+ act = ACTION_DUMP_RESET;
+ break;
+ case 'f':
+ if (act != ACTION_NONE)
+ usage();
+ act = ACTION_FW_UPDATE;
+ img_fw_path = optarg;
+ break;
+ case 'z':
+ if (act != ACTION_NONE)
+ usage();
+ act = ACTION_FW_RESET;
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ }
+ if (act == ACTION_NONE || (dumpname != NULL &&
+ act != ACTION_DUMP_GET) || (img_fw_path != NULL &&
+ act != ACTION_FW_UPDATE))
+ usage();
+ if (parse_pci_addr(addrstr, &addr) != 0)
+ exit(1);
+
+ ctldev = open(MLX5_DEV_PATH, O_RDWR);
+ if (ctldev == -1)
+ err(1, "open "MLX5_DEV_PATH);
+ switch (act) {
+ case ACTION_DUMP_GET:
+ res = mlx5tool_save_dump(ctldev, &addr, dumpname);
+ break;
+ case ACTION_DUMP_RESET:
+ res = mlx5tool_dump_reset(ctldev, &addr);
+ break;
+ case ACTION_DUMP_FORCE:
+ res = mlx5tool_dump_force(ctldev, &addr);
+ break;
+ case ACTION_FW_UPDATE:
+ res = mlx5tool_fw_update(ctldev, &addr, img_fw_path);
+ break;
+ case ACTION_FW_RESET:
+ res = mlx5tool_fw_reset(ctldev, &addr);
+ break;
+ case ACTION_GET_EEPROM_INFO:
+ res = mlx5tool_get_eeprom_info(ctldev, &addr);
+ break;
+ default:
+ res = 0;
+ break;
+ }
+ close(ctldev);
+ exit(res);
+}