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