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