1 /*- 2 * Copyright (c) 2018, Mellanox Technologies, Ltd. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 #include <sys/param.h> 28 #include <sys/ioctl.h> 29 #include <sys/mman.h> 30 #include <sys/stat.h> 31 #include <dev/mlx5/mlx5io.h> 32 #include <ctype.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <paths.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 /* stolen from pciconf.c: parsesel() */ 43 static int 44 parse_pci_addr(const char *addrstr, struct mlx5_tool_addr *addr) 45 { 46 char *eppos; 47 unsigned long selarr[4]; 48 int i; 49 50 if (addrstr == NULL) { 51 warnx("no pci address specified"); 52 return (1); 53 } 54 if (strncmp(addrstr, "pci", 3) == 0) { 55 addrstr += 3; 56 i = 0; 57 while (isdigit(*addrstr) && i < 4) { 58 selarr[i++] = strtoul(addrstr, &eppos, 10); 59 addrstr = eppos; 60 if (*addrstr == ':') 61 addrstr++; 62 } 63 if (i > 0 && *addrstr == '\0') { 64 addr->func = (i > 2) ? selarr[--i] : 0; 65 addr->slot = (i > 0) ? selarr[--i] : 0; 66 addr->bus = (i > 0) ? selarr[--i] : 0; 67 addr->domain = (i > 0) ? selarr[--i] : 0; 68 return (0); 69 } 70 } 71 warnx("invalid pci address %s", addrstr); 72 return (1); 73 } 74 75 static int 76 mlx5tool_save_dump(int ctldev, const struct mlx5_tool_addr *addr, 77 const char *dumpname) 78 { 79 struct mlx5_fwdump_get fdg; 80 struct mlx5_fwdump_reg *rege; 81 FILE *dump; 82 size_t cnt; 83 int error, res; 84 85 if (dumpname == NULL) 86 dump = stdout; 87 else 88 dump = fopen(dumpname, "w"); 89 if (dump == NULL) { 90 warn("open %s", dumpname); 91 return (1); 92 } 93 res = 1; 94 memset(&fdg, 0, sizeof(fdg)); 95 fdg.devaddr = *addr; 96 error = ioctl(ctldev, MLX5_FWDUMP_GET, &fdg); 97 if (error != 0) { 98 warn("MLX5_FWDUMP_GET dumpsize"); 99 goto out; 100 } 101 rege = calloc(fdg.reg_filled, sizeof(*rege)); 102 if (rege == NULL) { 103 warn("alloc rege"); 104 goto out; 105 } 106 fdg.buf = rege; 107 fdg.reg_cnt = fdg.reg_filled; 108 error = ioctl(ctldev, MLX5_FWDUMP_GET, &fdg); 109 if (error != 0) { 110 if (errno == ENOENT) 111 warnx("no dump recorded"); 112 else 113 warn("MLX5_FWDUMP_GET dump fetch"); 114 goto out; 115 } 116 for (cnt = 0; cnt < fdg.reg_cnt; cnt++, rege++) 117 fprintf(dump, "0x%08x\t0x%08x\n", rege->addr, rege->val); 118 res = 0; 119 out: 120 if (dump != stdout) 121 fclose(dump); 122 return (res); 123 } 124 125 static int 126 mlx5tool_dump_reset(int ctldev, const struct mlx5_tool_addr *addr) 127 { 128 129 if (ioctl(ctldev, MLX5_FWDUMP_RESET, addr) == -1) { 130 warn("MLX5_FWDUMP_RESET"); 131 return (1); 132 } 133 return (0); 134 } 135 136 static int 137 mlx5tool_dump_force(int ctldev, const struct mlx5_tool_addr *addr) 138 { 139 140 if (ioctl(ctldev, MLX5_FWDUMP_FORCE, addr) == -1) { 141 warn("MLX5_FWDUMP_FORCE"); 142 return (1); 143 } 144 return (0); 145 } 146 147 static int 148 mlx5tool_fw_update(int ctldev, const struct mlx5_tool_addr *addr, 149 const char *img_fw_path) 150 { 151 struct stat st; 152 struct mlx5_fw_update fwup; 153 int error, fd, res; 154 155 res = 0; 156 fd = open(img_fw_path, O_RDONLY); 157 if (fd == -1) { 158 warn("Unable to open %s", img_fw_path); 159 res = 1; 160 goto close_fd; 161 } 162 error = fstat(fd, &st); 163 if (error != 0) { 164 warn("Unable to stat %s", img_fw_path); 165 res = 1; 166 goto close_fd; 167 } 168 memset(&fwup, 0, sizeof(fwup)); 169 memcpy(&fwup.devaddr, addr, sizeof(fwup.devaddr)); 170 fwup.img_fw_data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, 171 fd, 0); 172 if (fwup.img_fw_data == MAP_FAILED) { 173 warn("Unable to mmap %s", img_fw_path); 174 res = 1; 175 goto close_fd; 176 } 177 fwup.img_fw_data_len = st.st_size; 178 179 error = ioctl(ctldev, MLX5_FW_UPDATE, &fwup); 180 if (error == -1) { 181 warn("MLX5_FW_UPDATE"); 182 } 183 184 munmap(fwup.img_fw_data, st.st_size); 185 close_fd: 186 close(fd); 187 return (res); 188 } 189 190 static int 191 mlx5tool_fw_reset(int ctldev, const struct mlx5_tool_addr *addr) 192 { 193 194 if (ioctl(ctldev, MLX5_FW_RESET, addr) == -1) { 195 warn("MLX5_FW_RESET"); 196 return (1); 197 } 198 return (0); 199 } 200 201 #define MLX5_EEPROM_HIGH_PAGE_OFFSET 128 202 #define MLX5_EEPROM_PAGE_LENGTH 256 203 204 static void 205 mlx5tool_eeprom_print(struct mlx5_eeprom_get *eeprom_info) 206 { 207 int index_in_row, line_length, row; 208 size_t byte_to_write; 209 210 byte_to_write = 0; 211 line_length = 16; 212 213 printf("\nOffset\t\tValues\n"); 214 printf("------\t\t------"); 215 while (byte_to_write < eeprom_info->eeprom_info_out_len) { 216 printf("\n0x%04zX\t\t", byte_to_write); 217 for (index_in_row = 0; index_in_row < line_length; 218 index_in_row++) { 219 printf("%02X ", 220 ((uint8_t *)eeprom_info->eeprom_info_buf)[ 221 byte_to_write]); 222 byte_to_write++; 223 } 224 } 225 226 if (eeprom_info->eeprom_info_page_valid) { 227 row = MLX5_EEPROM_HIGH_PAGE_OFFSET; 228 printf("\n\nUpper Page 0x03\n"); 229 printf("\nOffset\t\tValues\n"); 230 printf("------\t\t------"); 231 for (row = MLX5_EEPROM_HIGH_PAGE_OFFSET; 232 row < MLX5_EEPROM_PAGE_LENGTH;) { 233 printf("\n0x%04X\t\t", row); 234 for (index_in_row = 0; 235 index_in_row < line_length; 236 index_in_row++) { 237 printf("%02X ", 238 ((uint8_t *)eeprom_info-> 239 eeprom_info_buf)[byte_to_write]); 240 byte_to_write++; 241 row++; 242 } 243 } 244 } 245 printf("\n"); 246 } 247 248 static int 249 mlx5tool_get_eeprom_info(int ctldev, const struct mlx5_tool_addr *addr) 250 { 251 struct mlx5_eeprom_get eeprom_info; 252 int error; 253 254 memset(&eeprom_info, 0, sizeof(eeprom_info)); 255 eeprom_info.devaddr = *addr; 256 257 error = ioctl(ctldev, MLX5_EEPROM_GET, &eeprom_info); 258 if (error != 0) { 259 warn("MLX5_EEPROM_GET"); 260 return (error); 261 } 262 eeprom_info.eeprom_info_buf = 263 malloc(eeprom_info.eeprom_info_out_len + MLX5_EEPROM_PAGE_LENGTH); 264 if (eeprom_info.eeprom_info_buf == NULL) { 265 warn("alloc eeprom_info.eeprom_info_buf "); 266 return (ENOMEM); 267 } 268 error = ioctl(ctldev, MLX5_EEPROM_GET, &eeprom_info); 269 if (error != 0) { 270 warn("MLX5_EEPROM_GET"); 271 free(eeprom_info.eeprom_info_buf); 272 return (error); 273 } 274 275 mlx5tool_eeprom_print(&eeprom_info); 276 277 free(eeprom_info.eeprom_info_buf); 278 return (0); 279 } 280 281 static void 282 usage(void) 283 { 284 285 fprintf(stderr, 286 "Usage: mlx5tool -d pci<d:b:s:f> [-w -o dump.file | -r |" 287 " -e | -f fw.mfa2 | -z]\n"); 288 fprintf(stderr, "\t-w - write firmware dump to the specified file\n"); 289 fprintf(stderr, "\t-r - reset dump\n"); 290 fprintf(stderr, "\t-E - get eeprom info\n"); 291 fprintf(stderr, "\t-e - force dump\n"); 292 fprintf(stderr, "\t-f fw.img - flash firmware from fw.img\n"); 293 fprintf(stderr, "\t-z - initiate firmware reset\n"); 294 exit(1); 295 } 296 297 enum mlx5_action { 298 ACTION_DUMP_GET, 299 ACTION_DUMP_RESET, 300 ACTION_DUMP_FORCE, 301 ACTION_FW_UPDATE, 302 ACTION_FW_RESET, 303 ACTION_GET_EEPROM_INFO, 304 ACTION_NONE, 305 }; 306 307 int 308 main(int argc, char *argv[]) 309 { 310 struct mlx5_tool_addr addr; 311 char *dumpname; 312 char *addrstr; 313 char *img_fw_path; 314 int c, ctldev, res; 315 enum mlx5_action act; 316 317 act = ACTION_NONE; 318 addrstr = NULL; 319 dumpname = NULL; 320 img_fw_path = NULL; 321 while ((c = getopt(argc, argv, "d:Eef:ho:rwz")) != -1) { 322 switch (c) { 323 case 'd': 324 addrstr = optarg; 325 break; 326 case 'w': 327 if (act != ACTION_NONE) 328 usage(); 329 act = ACTION_DUMP_GET; 330 break; 331 case 'E': 332 if (act != ACTION_NONE) 333 usage(); 334 act = ACTION_GET_EEPROM_INFO; 335 break; 336 case 'e': 337 if (act != ACTION_NONE) 338 usage(); 339 act = ACTION_DUMP_FORCE; 340 break; 341 case 'o': 342 dumpname = optarg; 343 break; 344 case 'r': 345 if (act != ACTION_NONE) 346 usage(); 347 act = ACTION_DUMP_RESET; 348 break; 349 case 'f': 350 if (act != ACTION_NONE) 351 usage(); 352 act = ACTION_FW_UPDATE; 353 img_fw_path = optarg; 354 break; 355 case 'z': 356 if (act != ACTION_NONE) 357 usage(); 358 act = ACTION_FW_RESET; 359 break; 360 case 'h': 361 default: 362 usage(); 363 } 364 } 365 if (act == ACTION_NONE || (dumpname != NULL && 366 act != ACTION_DUMP_GET) || (img_fw_path != NULL && 367 act != ACTION_FW_UPDATE)) 368 usage(); 369 if (parse_pci_addr(addrstr, &addr) != 0) 370 exit(1); 371 372 ctldev = open(MLX5_DEV_PATH, O_RDWR); 373 if (ctldev == -1) 374 err(1, "open "MLX5_DEV_PATH); 375 switch (act) { 376 case ACTION_DUMP_GET: 377 res = mlx5tool_save_dump(ctldev, &addr, dumpname); 378 break; 379 case ACTION_DUMP_RESET: 380 res = mlx5tool_dump_reset(ctldev, &addr); 381 break; 382 case ACTION_DUMP_FORCE: 383 res = mlx5tool_dump_force(ctldev, &addr); 384 break; 385 case ACTION_FW_UPDATE: 386 res = mlx5tool_fw_update(ctldev, &addr, img_fw_path); 387 break; 388 case ACTION_FW_RESET: 389 res = mlx5tool_fw_reset(ctldev, &addr); 390 break; 391 case ACTION_GET_EEPROM_INFO: 392 res = mlx5tool_get_eeprom_info(ctldev, &addr); 393 break; 394 default: 395 res = 0; 396 break; 397 } 398 close(ctldev); 399 exit(res); 400 } 401