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