1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (c) 2018 by Chelsio Communications, Inc. 14 */ 15 16 /* 17 * Copyright 2019 Joyent, Inc. 18 */ 19 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <unistd.h> 23 #include <stropts.h> 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include <fcntl.h> 27 #include <sys/socket.h> 28 #include <strings.h> 29 #include <sys/varargs.h> 30 #include <errno.h> 31 #include <sys/byteorder.h> 32 #include <inttypes.h> 33 #include <sys/sysmacros.h> 34 35 #include "t4nex.h" 36 #include "version.h" 37 #include "osdep.h" 38 #include "t4fw_interface.h" 39 #include "cudbg.h" 40 #include "cudbg_lib_common.h" 41 42 #define CUDBG_SIZE (32 * 1024 * 1024) 43 #define CUDBG_MAX_ENTITY_STR_LEN 4096 44 #define MAX_PARAM_LEN 4096 45 46 char *option_list[] = { 47 "--collect", 48 "--view", 49 "--version", 50 }; 51 52 enum { 53 CUDBG_OPT_COLLECT, 54 CUDBG_OPT_VIEW, 55 CUDBG_OPT_VERSION, 56 }; 57 58 /* 59 * Firmware Device Log Dumping 60 */ 61 62 static const char * const devlog_level_strings[] = { 63 [FW_DEVLOG_LEVEL_EMERG] = "EMERG", 64 [FW_DEVLOG_LEVEL_CRIT] = "CRIT", 65 [FW_DEVLOG_LEVEL_ERR] = "ERR", 66 [FW_DEVLOG_LEVEL_NOTICE] = "NOTICE", 67 [FW_DEVLOG_LEVEL_INFO] = "INFO", 68 [FW_DEVLOG_LEVEL_DEBUG] = "DEBUG" 69 }; 70 71 static const char * const devlog_facility_strings[] = { 72 [FW_DEVLOG_FACILITY_CORE] = "CORE", 73 [FW_DEVLOG_FACILITY_CF] = "CF", 74 [FW_DEVLOG_FACILITY_SCHED] = "SCHED", 75 [FW_DEVLOG_FACILITY_TIMER] = "TIMER", 76 [FW_DEVLOG_FACILITY_RES] = "RES", 77 [FW_DEVLOG_FACILITY_HW] = "HW", 78 [FW_DEVLOG_FACILITY_FLR] = "FLR", 79 [FW_DEVLOG_FACILITY_DMAQ] = "DMAQ", 80 [FW_DEVLOG_FACILITY_PHY] = "PHY", 81 [FW_DEVLOG_FACILITY_MAC] = "MAC", 82 [FW_DEVLOG_FACILITY_PORT] = "PORT", 83 [FW_DEVLOG_FACILITY_VI] = "VI", 84 [FW_DEVLOG_FACILITY_FILTER] = "FILTER", 85 [FW_DEVLOG_FACILITY_ACL] = "ACL", 86 [FW_DEVLOG_FACILITY_TM] = "TM", 87 [FW_DEVLOG_FACILITY_QFC] = "QFC", 88 [FW_DEVLOG_FACILITY_DCB] = "DCB", 89 [FW_DEVLOG_FACILITY_ETH] = "ETH", 90 [FW_DEVLOG_FACILITY_OFLD] = "OFLD", 91 [FW_DEVLOG_FACILITY_RI] = "RI", 92 [FW_DEVLOG_FACILITY_ISCSI] = "ISCSI", 93 [FW_DEVLOG_FACILITY_FCOE] = "FCOE", 94 [FW_DEVLOG_FACILITY_FOISCSI] = "FOISCSI", 95 [FW_DEVLOG_FACILITY_FOFCOE] = "FOFCOE", 96 [FW_DEVLOG_FACILITY_CHNET] = "CHNET", 97 }; 98 99 static const char *progname; 100 int set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list); 101 102 static int check_option(char *opt) 103 { 104 int i; 105 106 for (i = 0; i < ARRAY_SIZE(option_list); i++) { 107 if (!strcmp(opt, option_list[i])) 108 return i; 109 } 110 return -1; 111 } 112 113 static void usage(FILE *fp) 114 { 115 fprintf(fp, "Usage: %s <path to t4nex#> [operation]\n", progname); 116 fprintf(fp, 117 "\tdevlog show device log\n" 118 "\tloadfw <FW image> Flash the FW image\n" 119 "\tcudbg Chelsio Unified Debugger\n"); 120 exit(fp == stderr ? 1 : 0); 121 } 122 123 __NORETURN static void 124 err(int code, const char *fmt, ...) 125 { 126 va_list ap; 127 int e = errno; 128 129 va_start(ap, fmt); 130 fprintf(stderr, "error: "); 131 vfprintf(stderr, fmt, ap); 132 fprintf(stderr, ": %s\n", strerror(e)); 133 va_end(ap); 134 exit(code); 135 } 136 137 static int 138 doit(const char *iff_name, unsigned long cmd, void *data) 139 { 140 int fd = 0; 141 int rc = 0; 142 143 if ((fd = open(iff_name, O_RDWR)) < 0) 144 return (-1); 145 146 rc = (ioctl(fd, cmd, data) < 0) ? errno : rc; 147 close(fd); 148 return (rc); 149 } 150 151 static void 152 get_devlog(int argc, char *argv[], int start_arg, const char *iff_name) 153 { 154 struct t4_devlog *devlog; 155 struct fw_devlog_e *entry, *buf; 156 int rc = 0, first = 0, nentries, i, j, len; 157 uint64_t ftstamp = UINT64_MAX; 158 159 devlog = malloc(T4_DEVLOG_SIZE + sizeof (struct t4_devlog)); 160 if (!devlog) 161 err(1, "%s: can't allocate devlog buffer", __func__); 162 163 devlog->len = T4_DEVLOG_SIZE; 164 /* Get device log */ 165 rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog); 166 if (rc == ENOBUFS) { 167 /* 168 * Default buffer size is not sufficient to hold device log. 169 * Driver has updated the devlog.len to indicate the expected 170 * size. Free the currently allocated devlog.data, allocate 171 * again with right size and retry. 172 */ 173 len = devlog->len; 174 free(devlog); 175 176 if ((devlog = malloc(len + sizeof (struct t4_devlog))) == NULL) 177 err(1, "%s: can't reallocate devlog buffer", __func__); 178 179 rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog); 180 } 181 if (rc) { 182 free(devlog); 183 err(1, "%s: can't get device log", __func__); 184 } 185 186 /* There are nentries number of entries in the buffer */ 187 nentries = (devlog->len / sizeof (struct fw_devlog_e)); 188 189 buf = (struct fw_devlog_e *)devlog->data; 190 191 /* Find the first entry */ 192 for (i = 0; i < nentries; i++) { 193 entry = &buf[i]; 194 195 if (entry->timestamp == 0) 196 break; 197 198 entry->timestamp = BE_64(entry->timestamp); 199 entry->seqno = BE_32(entry->seqno); 200 for (j = 0; j < 8; j++) 201 entry->params[j] = BE_32(entry->params[j]); 202 203 if (entry->timestamp < ftstamp) { 204 ftstamp = entry->timestamp; 205 first = i; 206 } 207 } 208 209 printf("%10s %15s %8s %8s %s\n", "Seq#", "Tstamp", "Level", 210 "Facility", "Message"); 211 212 i = first; 213 214 do { 215 entry = &buf[i]; 216 217 if (entry->timestamp == 0) 218 break; 219 220 printf("%10d %15llu %8s %8s ", entry->seqno, 221 entry->timestamp, 222 (entry->level < ARRAY_SIZE(devlog_level_strings) ? 223 devlog_level_strings[entry->level] : "UNKNOWN"), 224 (entry->facility < ARRAY_SIZE(devlog_facility_strings) ? 225 devlog_facility_strings[entry->facility] : "UNKNOWN")); 226 227 printf((const char *)entry->fmt, entry->params[0], 228 entry->params[1], entry->params[2], entry->params[3], 229 entry->params[4], entry->params[5], entry->params[6], 230 entry->params[7]); 231 232 if (++i == nentries) 233 i = 0; 234 235 } while (i != first); 236 237 free(devlog); 238 } 239 240 static void 241 load_fw(int argc, char *argv[], int start_arg, const char *iff_name) 242 { 243 const char *fname = argv[start_arg]; 244 struct t4_ldfw *fw; 245 struct stat sb; 246 size_t len; 247 int fd; 248 249 if (argc != 4) 250 err(1, "incorrect number of arguments."); 251 252 fd = open(fname, O_RDONLY); 253 if (fd < 0) 254 err(1, "%s: opening %s failed", __func__, fname); 255 if (fstat(fd, &sb) < 0) { 256 close(fd); 257 err(1, "%s: fstat %s failed", __func__, fname); 258 } 259 len = (size_t)sb.st_size; 260 261 fw = malloc(sizeof (struct t4_ldfw) + len); 262 if (!fw) { 263 close(fd); 264 err(1, "%s: %s allocate %ld bytes failed", 265 __func__, fname, sizeof (struct t4_ldfw) + len); 266 } 267 268 if (read(fd, fw->data, len) < len) { 269 close(fd); 270 free(fw); 271 err(1, "%s: %s read failed", __func__, fname); 272 } 273 274 close(fd); 275 276 fw->len = len; 277 278 if (doit(iff_name, T4_IOCTL_LOAD_FW, fw)) { 279 free(fw); 280 err(1, "%s: IOCTL failed", __func__); 281 } else { 282 printf("FW flash success, reload driver/reboot to take " 283 "effect\n"); 284 } 285 286 free(fw); 287 } 288 289 int read_input_file(char *in_file, void **buf, int *buf_size) 290 { 291 FILE *fptr = NULL; 292 size_t count; 293 int rc = 0; 294 295 fptr = fopen(in_file, "rb"); 296 if (!fptr) { 297 perror("error in opening file "); 298 rc = -1; 299 goto out; 300 } 301 rc = fseek(fptr, 0, SEEK_END); 302 if (rc < 0) { 303 perror("error in seeking file "); 304 rc = -1; 305 goto out; 306 } 307 *buf_size = ftell(fptr); 308 rc = fseek(fptr, 0, SEEK_SET); 309 if (rc < 0) { 310 perror("error in seeking file "); 311 rc = -1; 312 goto out; 313 } 314 *buf = (void *) malloc(*buf_size); 315 if (*buf == NULL) { 316 rc = CUDBG_STATUS_NOSPACE; 317 goto out; 318 } 319 memset(*buf, 0, *buf_size); 320 321 count = fread(*buf, 1, *buf_size, fptr); 322 if (count != *buf_size) { 323 perror("error in reading from file "); 324 goto out; 325 } 326 327 out: 328 if (fptr) 329 fclose(fptr); 330 331 return rc; 332 } 333 334 static void 335 do_collect(char *dbg_entity_list, const char *iff_name, const char *fname) 336 { 337 struct t4_cudbg_dump *cudbg; 338 int fd; 339 340 cudbg = malloc(sizeof(struct t4_cudbg_dump) + CUDBG_SIZE); 341 if (!cudbg) { 342 err(1, "%s:allocate %ld bytes failed", __func__, CUDBG_SIZE); 343 } 344 345 memset(cudbg, 0, sizeof(struct t4_cudbg_dump) + CUDBG_SIZE); 346 347 cudbg->len = CUDBG_SIZE; 348 349 set_dbg_entity(cudbg->bitmap, dbg_entity_list); 350 351 if (doit(iff_name, T4_IOCTL_GET_CUDBG, cudbg)) { 352 free(cudbg); 353 err(1, "%s: IOCTL failed", __func__); 354 } 355 356 fd = open(fname, O_CREAT | O_TRUNC | O_EXCL | O_WRONLY, 357 S_IRUSR | S_IRGRP | S_IROTH); 358 if (fd < 0) { 359 err(1, "%s: file open failed", __func__); 360 } 361 362 write(fd, cudbg->data, cudbg->len); 363 close(fd); 364 free(cudbg); 365 } 366 367 static void 368 do_view(char *dbg_entity_list, char *in_file) 369 { 370 void *handle = NULL; 371 void *buf = NULL; 372 int buf_size = 32 * 1024 * 1024; 373 int next_offset = 0; 374 int data_len; 375 int rc = 0; 376 377 handle = cudbg_alloc_handle(); 378 if (!handle) 379 goto out; 380 /* rcad from file */ 381 rc = read_input_file(in_file, &buf, &buf_size); 382 if (rc < 0) { 383 goto out; 384 } 385 386 set_dbg_entity(((struct cudbg_private *)handle)->dbg_init.dbg_bitmap, 387 dbg_entity_list); 388 do { 389 if (buf_size - next_offset <= 0) 390 break; 391 392 data_len = cudbg_view(handle, buf+next_offset, 393 buf_size-next_offset, NULL, 0); 394 next_offset += data_len; 395 if (data_len > 0) 396 printf("\n\t\t<========================END============="\ 397 "===========>\t\t\n\n\n"); 398 } while (data_len > 0); 399 400 out: 401 if (buf) 402 free(buf); 403 if (handle) 404 cudbg_free_handle(handle); 405 return; 406 } 407 408 typedef void (*cudbg_alias_get_entities_cb)(char *dst, u32 dst_size); 409 410 struct entity_access_list { 411 const char *name; 412 cudbg_alias_get_entities_cb get_entities_cb; 413 }; 414 415 void 416 cudbg_append_string(char *dst, u32 dst_size, char *src) 417 { 418 strlcat(dst, src, dst_size); 419 strlcat(dst, ",", dst_size); 420 } 421 422 static void 423 cudbg_alias_get_allregs(char *dst, u32 dst_size) 424 { 425 u32 i; 426 427 for (i = 0; i < ARRAY_SIZE(entity_list); i++) 428 if (entity_list[i].flag & (1 << ENTITY_FLAG_REGISTER)) 429 cudbg_append_string(dst, dst_size, entity_list[i].name); 430 } 431 432 static struct entity_access_list ATTRIBUTE_UNUSED entity_alias_list[] = { 433 {"allregs", cudbg_alias_get_allregs}, 434 }; 435 436 static int 437 check_dbg_entity(char *entity) 438 { 439 u32 i; 440 441 for (i = 0; i < ARRAY_SIZE(entity_list); i++) 442 if (!strcmp(entity, entity_list[i].name)) 443 return entity_list[i].bit; 444 return -1; 445 } 446 447 /* Get matching alias index from entity_alias_list[] */ 448 static 449 int get_alias(const char *entity) 450 { 451 u32 i; 452 453 for (i = 0; i < ARRAY_SIZE(entity_alias_list); i++) 454 if (!strcmp(entity, entity_alias_list[i].name)) 455 return i; 456 return -1; 457 } 458 459 static int 460 parse_entity_list(const char *dbg_entity_list, char *dst, 461 u32 dst_size) 462 { 463 char *tmp_dbg_entity_list; 464 char *dbg_entity; 465 int rc, i; 466 467 /* Holds single entity name de-limited by comma */ 468 tmp_dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN); 469 if (!tmp_dbg_entity_list) 470 return ENOMEM; 471 472 strlcpy(tmp_dbg_entity_list, dbg_entity_list, CUDBG_MAX_ENTITY_STR_LEN); 473 dbg_entity = strtok(tmp_dbg_entity_list, ","); 474 while (dbg_entity != NULL) { 475 /* See if specified entity name exists. If it doesn't 476 * exist, see if the entity name is an alias. 477 * If it's not a valid entity name, bail with error. 478 */ 479 rc = check_dbg_entity(dbg_entity); 480 if (rc < 0) { 481 i = get_alias(dbg_entity); 482 if (i < 0) { 483 /* Not an alias, and not a valid entity name */ 484 printf("\nUnknown entity: %s\n", dbg_entity); 485 rc = CUDBG_STATUS_ENTITY_NOT_FOUND; 486 goto out_err; 487 } else { 488 /* If alias is found, get all the corresponding 489 * debug entities related to the alias. 490 */ 491 entity_alias_list[i].get_entities_cb(dst, dst_size); 492 } 493 } else { 494 /* Not an alias, but is a valid entity name. 495 * So, append the corresponding debug entity. 496 */ 497 cudbg_append_string(dst, dst_size, entity_list[rc].name); 498 } 499 dbg_entity = strtok(NULL, ","); 500 } 501 502 rc = 0; 503 504 out_err: 505 free(tmp_dbg_entity_list); 506 return rc; 507 } 508 509 static 510 int get_entity_list(const char *in_buff, char **out_buff) 511 { 512 char *dbg_entity_list; 513 int rc; 514 515 /* Allocate enough to hold converted alias string. 516 * Must be freed by caller 517 */ 518 dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN); 519 if (!dbg_entity_list) 520 return ENOMEM; 521 522 memset(dbg_entity_list, 0, CUDBG_MAX_ENTITY_STR_LEN); 523 rc = parse_entity_list(in_buff, dbg_entity_list, 524 CUDBG_MAX_ENTITY_STR_LEN); 525 if (rc) { 526 free(dbg_entity_list); 527 return rc; 528 } 529 530 /* Remove the last comma */ 531 dbg_entity_list[strlen(dbg_entity_list) - 1] = '\0'; 532 *out_buff = dbg_entity_list; 533 return 0; 534 } 535 536 static void 537 put_entity_list(char *buf) 538 { 539 if (buf) 540 free(buf); 541 } 542 543 int 544 set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list) 545 { 546 int i, dbg_entity_bit, rc = 0; 547 char *dbg_entity; 548 char *dbg_entity_list_tmp; 549 550 dbg_entity_list_tmp = malloc(MAX_PARAM_LEN); 551 if (!dbg_entity_list_tmp) { 552 rc = CUDBG_STATUS_NOSPACE; 553 return rc; 554 } 555 556 if (dbg_entity_list != NULL) { 557 strlcpy(dbg_entity_list_tmp, dbg_entity_list, MAX_PARAM_LEN); 558 dbg_entity = strtok(dbg_entity_list_tmp, ","); 559 } 560 else 561 dbg_entity = NULL; 562 563 while (dbg_entity != NULL) { 564 rc = check_dbg_entity(dbg_entity); 565 if (rc < 0) { 566 printf("\n\tInvalid debug entity: %s\n", dbg_entity); 567 //Vishal cudbg_usage(); 568 goto out_free; 569 } 570 571 dbg_entity_bit = rc; 572 573 if (dbg_entity_bit == CUDBG_ALL) { 574 for (i = 1; i < CUDBG_MAX_ENTITY; i++) 575 set_dbg_bitmap(dbg_bitmap, i); 576 set_dbg_bitmap(dbg_bitmap, CUDBG_ALL); 577 break; 578 } else { 579 set_dbg_bitmap(dbg_bitmap, dbg_entity_bit); 580 } 581 582 dbg_entity = strtok(NULL, ","); 583 } 584 585 rc = 0; 586 587 out_free: 588 free(dbg_entity_list_tmp); 589 return rc; 590 } 591 592 593 static void 594 get_cudbg(int argc, char *argv[], int start_arg, const char *iff_name) 595 { 596 char *dbg_entity_list = NULL; 597 int rc = 0, option; 598 rc = check_option(argv[start_arg++]); 599 if (rc < 0) { 600 err(1, "%s:Invalid option provided", __func__); 601 } 602 option = rc; 603 604 if (option == CUDBG_OPT_VERSION) { 605 printf("Library Version %d.%d.%d\n", CUDBG_MAJOR_VERSION, 606 CUDBG_MINOR_VERSION, CUDBG_BUILD_VERSION); 607 return; 608 } 609 610 if (argc < 5) { 611 err(1, "Invalid number of arguments\n"); 612 } 613 rc = get_entity_list(argv[start_arg++], 614 &dbg_entity_list); 615 if (rc) { 616 err(1, "Error in parsing entity\n"); 617 } 618 619 if (argc < 6) { 620 err(1, "File name is missing\n"); 621 } 622 623 switch (option) { 624 case CUDBG_OPT_COLLECT: 625 do_collect(dbg_entity_list, iff_name, argv[start_arg]); 626 break; 627 case CUDBG_OPT_VIEW: 628 do_view(dbg_entity_list, argv[start_arg]); 629 break; 630 default: 631 err(1, "Wrong option provided\n"); 632 } 633 634 put_entity_list(dbg_entity_list); 635 } 636 637 static void 638 run_cmd(int argc, char *argv[], const char *iff_name) 639 { 640 if (strcmp(argv[2], "devlog") == 0) 641 get_devlog(argc, argv, 3, iff_name); 642 else if (strcmp(argv[2], "loadfw") == 0) 643 load_fw(argc, argv, 3, iff_name); 644 else if (strcmp(argv[2], "cudbg") == 0) 645 get_cudbg(argc, argv, 3, iff_name); 646 else 647 usage(stderr); 648 } 649 650 int 651 main(int argc, char *argv[]) 652 { 653 const char *iff_name; 654 655 progname = argv[0]; 656 657 if (argc == 2) { 658 if (strcmp(argv[1], "-h") == 0 || 659 strcmp(argv[1], "--help") == 0) { 660 usage(stdout); 661 } 662 663 if (strcmp(argv[1], "-v") == 0 || 664 strcmp(argv[1], "--version") == 0) { 665 printf("cxgbetool version %s\n", DRV_VERSION); 666 exit(0); 667 } 668 } 669 670 if (argc < 3) 671 usage(stderr); 672 673 iff_name = argv[1]; 674 675 run_cmd(argc, argv, iff_name); 676 677 return (0); 678 } 679