Index: head/usr.sbin/mlx5tool/mlx5tool.8 =================================================================== --- head/usr.sbin/mlx5tool/mlx5tool.8 (revision 352983) +++ head/usr.sbin/mlx5tool/mlx5tool.8 (revision 352984) @@ -1,117 +1,122 @@ .\" .\" Copyright (c) 2018, 2019 Mellanox Technologies .\" 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 THE 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 THE 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. .\" .\" $FreeBSD$ .\" .Dd May 7, 2019 .Dt mlx5tool 8 .Os .Sh NAME .Nm mlx5tool -.Nd Utility for managing Connect-X 4/5 Mellanox network adapters +.Nd Utility for managing Connect-X 4/5/6 Mellanox network adapters .Sh SYNOPSIS .Nm .Fl d Ar domain:bus:slot:func +.Fl E +.Nm +.Fl d Ar domain:bus:slot:func .Fl e .Nm .Fl d Ar domain:bus:slot:func -.Fl rn +.Fl f Ar file.mfa2 .Nm .Fl d Ar domain:bus:slot:func .Fl o Ar file .Fl w .Nm .Fl d Ar domain:bus:slot:func -.Fl f Ar file.mfa2 +.Fl r .Nm .Fl d Ar domain:bus:slot:func .Fl z .Sh DESCRIPTION The .Nm -utility is provided for management of the Connect-X 4 and 5 network adapters +utility is provided for management of the Connect-X4, 5 and 6 network adapters in the aspects not covered by the generic .Xr ifconfig 8 command, mostly related to the PCIe attachment and internal card working. The utility executes commands on specific adapter, which is addressed using .Em device:bus:slot:function conventions of the PCIe buses. You can match adapters ethernet name and addresses using the .X pciconf 8 utility. The address is passed as an argument of the .Fl d option, which must be specified for each invocation. .Pp When the driver detects the malfunctioning of the hardware, or by user request, it is possible to create .Em firmware dump , which contains debugging information about internal device state, for analysis of the failure by the Mellanox support team. .Pp The following commands are currently implemented: .Bl -tag -width indent +.It Fl E +Print EEPROM information .It Fl e Take the snapshot of the firmware registers state and store it in the kernel buffer. The buffer must be empty, in other words, no dumps should be written so far, or existing dump cleared with the -.Fl r -command for the specified device. -.It Fl r -Clear the stored firmware dump, preparing the kernel buffer for -the next dump. -.It Fl w -Fetches the stored firmware dump and writes it into the file specified -by the -.Fl o -option argument. .It Fl f Flashes the firmware image .Fa file.mfa2 to the specified adapter. Image must be in MFA2 pack format and contain a component suitable for the adapter hardware. .Pp Typically, PCIe link-level reset is required to activate the newly flashed image, which can be performed by the system reboot or using the .Fl z option. +.Fl r +command for the specified device. +.It Fl r +Clear the stored firmware dump, preparing the kernel buffer for +the next dump. +.It Fl w +Fetches the stored firmware dump and writes it into the file specified +by the +.Fl o +option argument. .It Fl z Performs PCIe link-level reset on the specified device. .El .Sh FILES The .Pa /dev/mlx5ctl .Xr devfs 5 node is used to pass commands to the driver. .Sh SEE ALSO .Xr mlx5en 4 , .Xr mlx5ib 4 , .Xr mlx5io 4 , .Xr ifconfig 8 and .Xr pciconf 8 . Index: head/usr.sbin/mlx5tool/mlx5tool.c =================================================================== --- head/usr.sbin/mlx5tool/mlx5tool.c (revision 352983) +++ head/usr.sbin/mlx5tool/mlx5tool.c (revision 352984) @@ -1,312 +1,401 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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) +{ + unsigned int byte_to_write, index_in_row, line_length, row; + + 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%04X\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 [-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:ef:ho:rwz")) != -1) { + 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); }