diff options
Diffstat (limited to 'usr.sbin/mlx5tool/mlx5tool.c')
| -rw-r--r-- | usr.sbin/mlx5tool/mlx5tool.c | 399 |
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); +} |
