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