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