1 /*- 2 * Copyright (c) 2011 Chelsio Communications, Inc. 3 * All rights reserved. 4 * Written by: Navdeep Parhar <np@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/ioctl.h> 33 #include <sys/mman.h> 34 #include <sys/socket.h> 35 #include <sys/stat.h> 36 37 #include <arpa/inet.h> 38 #include <net/ethernet.h> 39 #include <net/sff8472.h> 40 #include <netinet/in.h> 41 42 #include <ctype.h> 43 #include <err.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <limits.h> 47 #include <stdint.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 #include <pcap.h> 53 54 #include "t4_ioctl.h" 55 #include "tcb_common.h" 56 57 #define in_range(val, lo, hi) ( val < 0 || (val <= hi && val >= lo)) 58 #define max(x, y) ((x) > (y) ? (x) : (y)) 59 60 static const char *progname, *nexus; 61 static int chip_id; /* 4 for T4, 5 for T5 */ 62 63 struct reg_info { 64 const char *name; 65 uint32_t addr; 66 uint32_t len; 67 }; 68 69 struct mod_regs { 70 const char *name; 71 const struct reg_info *ri; 72 }; 73 74 struct field_desc { 75 const char *name; /* Field name */ 76 unsigned short start; /* Start bit position */ 77 unsigned short end; /* End bit position */ 78 unsigned char shift; /* # of low order bits omitted and implicitly 0 */ 79 unsigned char hex; /* Print field in hex instead of decimal */ 80 unsigned char islog2; /* Field contains the base-2 log of the value */ 81 }; 82 83 #include "reg_defs_t4.c" 84 #include "reg_defs_t5.c" 85 #include "reg_defs_t6.c" 86 #include "reg_defs_t4vf.c" 87 88 static void 89 usage(FILE *fp) 90 { 91 fprintf(fp, "Usage: %s <nexus> [operation]\n", progname); 92 fprintf(fp, 93 "\tclearstats <port> clear port statistics\n" 94 "\tcontext <type> <id> show an SGE context\n" 95 "\tdumpstate <dump.bin> dump chip state\n" 96 "\tfilter <idx> [<param> <val>] ... set a filter\n" 97 "\tfilter <idx> delete|clear delete a filter\n" 98 "\tfilter list list all filters\n" 99 "\tfilter mode [<match>] ... get/set global filter mode\n" 100 "\ti2c <port> <devaddr> <addr> [<len>] read from i2c device\n" 101 "\tloadboot <bi.bin> [pf|offset <val>] install boot image\n" 102 "\tloadboot clear [pf|offset <val>] remove boot image\n" 103 "\tloadboot-cfg <bc.bin> install boot config\n" 104 "\tloadboot-cfg clear remove boot config\n" 105 "\tloadcfg <fw-config.txt> install configuration file\n" 106 "\tloadcfg clear remove configuration file\n" 107 "\tloadfw <fw-image.bin> install firmware\n" 108 "\tmemdump <addr> <len> dump a memory range\n" 109 "\tmodinfo <port> [raw] optics/cable information\n" 110 "\tpolicy <policy.txt> install offload policy\n" 111 "\tpolicy clear remove offload policy\n" 112 "\treg <address>[=<val>] read/write register\n" 113 "\treg64 <address>[=<val>] read/write 64 bit register\n" 114 "\tregdump [<module>] ... dump registers\n" 115 "\tsched-class params <param> <val> .. configure TX scheduler class\n" 116 "\tsched-queue <port> <queue> <class> bind NIC queues to TX Scheduling class\n" 117 "\tstdio interactive mode\n" 118 "\ttcb <tid> read TCB\n" 119 "\ttracer <idx> tx<n>|rx<n> set and enable a tracer\n" 120 "\ttracer <idx> disable|enable disable or enable a tracer\n" 121 "\ttracer list list all tracers\n" 122 ); 123 } 124 125 static inline unsigned int 126 get_card_vers(unsigned int version) 127 { 128 return (version & 0x3ff); 129 } 130 131 static int 132 real_doit(unsigned long cmd, void *data, const char *cmdstr) 133 { 134 static int fd = -1; 135 int rc = 0; 136 137 if (fd == -1) { 138 char buf[64]; 139 140 snprintf(buf, sizeof(buf), "/dev/%s", nexus); 141 if ((fd = open(buf, O_RDWR)) < 0) { 142 warn("open(%s)", nexus); 143 rc = errno; 144 return (rc); 145 } 146 chip_id = nexus[1] - '0'; 147 } 148 149 rc = ioctl(fd, cmd, data); 150 if (rc < 0) { 151 warn("%s", cmdstr); 152 rc = errno; 153 } 154 155 return (rc); 156 } 157 #define doit(x, y) real_doit(x, y, #x) 158 159 static char * 160 str_to_number(const char *s, long *val, long long *vall) 161 { 162 char *p; 163 164 if (vall) 165 *vall = strtoll(s, &p, 0); 166 else if (val) 167 *val = strtol(s, &p, 0); 168 else 169 p = NULL; 170 171 return (p); 172 } 173 174 static int 175 read_reg(long addr, int size, long long *val) 176 { 177 struct t4_reg reg; 178 int rc; 179 180 reg.addr = (uint32_t) addr; 181 reg.size = (uint32_t) size; 182 reg.val = 0; 183 184 rc = doit(CHELSIO_T4_GETREG, ®); 185 186 *val = reg.val; 187 188 return (rc); 189 } 190 191 static int 192 write_reg(long addr, int size, long long val) 193 { 194 struct t4_reg reg; 195 196 reg.addr = (uint32_t) addr; 197 reg.size = (uint32_t) size; 198 reg.val = (uint64_t) val; 199 200 return doit(CHELSIO_T4_SETREG, ®); 201 } 202 203 static int 204 register_io(int argc, const char *argv[], int size) 205 { 206 char *p, *v; 207 long addr; 208 long long val; 209 int w = 0, rc; 210 211 if (argc == 1) { 212 /* <reg> OR <reg>=<value> */ 213 214 p = str_to_number(argv[0], &addr, NULL); 215 if (*p) { 216 if (*p != '=') { 217 warnx("invalid register \"%s\"", argv[0]); 218 return (EINVAL); 219 } 220 221 w = 1; 222 v = p + 1; 223 p = str_to_number(v, NULL, &val); 224 225 if (*p) { 226 warnx("invalid value \"%s\"", v); 227 return (EINVAL); 228 } 229 } 230 231 } else if (argc == 2) { 232 /* <reg> <value> */ 233 234 w = 1; 235 236 p = str_to_number(argv[0], &addr, NULL); 237 if (*p) { 238 warnx("invalid register \"%s\"", argv[0]); 239 return (EINVAL); 240 } 241 242 p = str_to_number(argv[1], NULL, &val); 243 if (*p) { 244 warnx("invalid value \"%s\"", argv[1]); 245 return (EINVAL); 246 } 247 } else { 248 warnx("reg: invalid number of arguments (%d)", argc); 249 return (EINVAL); 250 } 251 252 if (w) 253 rc = write_reg(addr, size, val); 254 else { 255 rc = read_reg(addr, size, &val); 256 if (rc == 0) 257 printf("0x%llx [%llu]\n", val, val); 258 } 259 260 return (rc); 261 } 262 263 static inline uint32_t 264 xtract(uint32_t val, int shift, int len) 265 { 266 return (val >> shift) & ((1 << len) - 1); 267 } 268 269 static int 270 dump_block_regs(const struct reg_info *reg_array, const uint32_t *regs) 271 { 272 uint32_t reg_val = 0; 273 274 for ( ; reg_array->name; ++reg_array) 275 if (!reg_array->len) { 276 reg_val = regs[reg_array->addr / 4]; 277 printf("[%#7x] %-47s %#-10x %u\n", reg_array->addr, 278 reg_array->name, reg_val, reg_val); 279 } else { 280 uint32_t v = xtract(reg_val, reg_array->addr, 281 reg_array->len); 282 283 printf(" %*u:%u %-47s %#-10x %u\n", 284 reg_array->addr < 10 ? 3 : 2, 285 reg_array->addr + reg_array->len - 1, 286 reg_array->addr, reg_array->name, v, v); 287 } 288 289 return (1); 290 } 291 292 static int 293 dump_regs_table(int argc, const char *argv[], const uint32_t *regs, 294 const struct mod_regs *modtab, int nmodules) 295 { 296 int i, j, match; 297 298 for (i = 0; i < argc; i++) { 299 for (j = 0; j < nmodules; j++) { 300 if (!strcmp(argv[i], modtab[j].name)) 301 break; 302 } 303 304 if (j == nmodules) { 305 warnx("invalid register block \"%s\"", argv[i]); 306 fprintf(stderr, "\nAvailable blocks:"); 307 for ( ; nmodules; nmodules--, modtab++) 308 fprintf(stderr, " %s", modtab->name); 309 fprintf(stderr, "\n"); 310 return (EINVAL); 311 } 312 } 313 314 for ( ; nmodules; nmodules--, modtab++) { 315 316 match = argc == 0 ? 1 : 0; 317 for (i = 0; !match && i < argc; i++) { 318 if (!strcmp(argv[i], modtab->name)) 319 match = 1; 320 } 321 322 if (match) 323 dump_block_regs(modtab->ri, regs); 324 } 325 326 return (0); 327 } 328 329 #define T4_MODREGS(name) { #name, t4_##name##_regs } 330 static int 331 dump_regs_t4(int argc, const char *argv[], const uint32_t *regs) 332 { 333 static struct mod_regs t4_mod[] = { 334 T4_MODREGS(sge), 335 { "pci", t4_pcie_regs }, 336 T4_MODREGS(dbg), 337 T4_MODREGS(mc), 338 T4_MODREGS(ma), 339 { "edc0", t4_edc_0_regs }, 340 { "edc1", t4_edc_1_regs }, 341 T4_MODREGS(cim), 342 T4_MODREGS(tp), 343 T4_MODREGS(ulp_rx), 344 T4_MODREGS(ulp_tx), 345 { "pmrx", t4_pm_rx_regs }, 346 { "pmtx", t4_pm_tx_regs }, 347 T4_MODREGS(mps), 348 { "cplsw", t4_cpl_switch_regs }, 349 T4_MODREGS(smb), 350 { "i2c", t4_i2cm_regs }, 351 T4_MODREGS(mi), 352 T4_MODREGS(uart), 353 T4_MODREGS(pmu), 354 T4_MODREGS(sf), 355 T4_MODREGS(pl), 356 T4_MODREGS(le), 357 T4_MODREGS(ncsi), 358 T4_MODREGS(xgmac) 359 }; 360 361 return dump_regs_table(argc, argv, regs, t4_mod, nitems(t4_mod)); 362 } 363 #undef T4_MODREGS 364 365 #define T5_MODREGS(name) { #name, t5_##name##_regs } 366 static int 367 dump_regs_t5(int argc, const char *argv[], const uint32_t *regs) 368 { 369 static struct mod_regs t5_mod[] = { 370 T5_MODREGS(sge), 371 { "pci", t5_pcie_regs }, 372 T5_MODREGS(dbg), 373 { "mc0", t5_mc_0_regs }, 374 { "mc1", t5_mc_1_regs }, 375 T5_MODREGS(ma), 376 { "edc0", t5_edc_t50_regs }, 377 { "edc1", t5_edc_t51_regs }, 378 T5_MODREGS(cim), 379 T5_MODREGS(tp), 380 { "ulprx", t5_ulp_rx_regs }, 381 { "ulptx", t5_ulp_tx_regs }, 382 { "pmrx", t5_pm_rx_regs }, 383 { "pmtx", t5_pm_tx_regs }, 384 T5_MODREGS(mps), 385 { "cplsw", t5_cpl_switch_regs }, 386 T5_MODREGS(smb), 387 { "i2c", t5_i2cm_regs }, 388 T5_MODREGS(mi), 389 T5_MODREGS(uart), 390 T5_MODREGS(pmu), 391 T5_MODREGS(sf), 392 T5_MODREGS(pl), 393 T5_MODREGS(le), 394 T5_MODREGS(ncsi), 395 T5_MODREGS(mac), 396 { "hma", t5_hma_t5_regs } 397 }; 398 399 return dump_regs_table(argc, argv, regs, t5_mod, nitems(t5_mod)); 400 } 401 #undef T5_MODREGS 402 403 #define T6_MODREGS(name) { #name, t6_##name##_regs } 404 static int 405 dump_regs_t6(int argc, const char *argv[], const uint32_t *regs) 406 { 407 static struct mod_regs t6_mod[] = { 408 T6_MODREGS(sge), 409 { "pci", t6_pcie_regs }, 410 T6_MODREGS(dbg), 411 { "mc0", t6_mc_0_regs }, 412 T6_MODREGS(ma), 413 { "edc0", t6_edc_t60_regs }, 414 { "edc1", t6_edc_t61_regs }, 415 T6_MODREGS(cim), 416 T6_MODREGS(tp), 417 { "ulprx", t6_ulp_rx_regs }, 418 { "ulptx", t6_ulp_tx_regs }, 419 { "pmrx", t6_pm_rx_regs }, 420 { "pmtx", t6_pm_tx_regs }, 421 T6_MODREGS(mps), 422 { "cplsw", t6_cpl_switch_regs }, 423 T6_MODREGS(smb), 424 { "i2c", t6_i2cm_regs }, 425 T6_MODREGS(mi), 426 T6_MODREGS(uart), 427 T6_MODREGS(pmu), 428 T6_MODREGS(sf), 429 T6_MODREGS(pl), 430 T6_MODREGS(le), 431 T6_MODREGS(ncsi), 432 T6_MODREGS(mac), 433 { "hma", t6_hma_t6_regs } 434 }; 435 436 return dump_regs_table(argc, argv, regs, t6_mod, nitems(t6_mod)); 437 } 438 #undef T6_MODREGS 439 440 static int 441 dump_regs_t4vf(int argc, const char *argv[], const uint32_t *regs) 442 { 443 static struct mod_regs t4vf_mod[] = { 444 { "sge", t4vf_sge_regs }, 445 { "mps", t4vf_mps_regs }, 446 { "pl", t4vf_pl_regs }, 447 { "mbdata", t4vf_mbdata_regs }, 448 { "cim", t4vf_cim_regs }, 449 }; 450 451 return dump_regs_table(argc, argv, regs, t4vf_mod, nitems(t4vf_mod)); 452 } 453 454 static int 455 dump_regs_t5vf(int argc, const char *argv[], const uint32_t *regs) 456 { 457 static struct mod_regs t5vf_mod[] = { 458 { "sge", t5vf_sge_regs }, 459 { "mps", t4vf_mps_regs }, 460 { "pl", t5vf_pl_regs }, 461 { "mbdata", t4vf_mbdata_regs }, 462 { "cim", t4vf_cim_regs }, 463 }; 464 465 return dump_regs_table(argc, argv, regs, t5vf_mod, nitems(t5vf_mod)); 466 } 467 468 static int 469 dump_regs_t6vf(int argc, const char *argv[], const uint32_t *regs) 470 { 471 static struct mod_regs t6vf_mod[] = { 472 { "sge", t5vf_sge_regs }, 473 { "mps", t4vf_mps_regs }, 474 { "pl", t6vf_pl_regs }, 475 { "mbdata", t4vf_mbdata_regs }, 476 { "cim", t4vf_cim_regs }, 477 }; 478 479 return dump_regs_table(argc, argv, regs, t6vf_mod, nitems(t6vf_mod)); 480 } 481 482 static int 483 dump_regs(int argc, const char *argv[]) 484 { 485 int vers, revision, rc; 486 struct t4_regdump regs; 487 uint32_t len; 488 489 len = max(T4_REGDUMP_SIZE, T5_REGDUMP_SIZE); 490 regs.data = calloc(1, len); 491 if (regs.data == NULL) { 492 warnc(ENOMEM, "regdump"); 493 return (ENOMEM); 494 } 495 496 regs.len = len; 497 rc = doit(CHELSIO_T4_REGDUMP, ®s); 498 if (rc != 0) 499 return (rc); 500 501 vers = get_card_vers(regs.version); 502 revision = (regs.version >> 10) & 0x3f; 503 504 if (vers == 4) { 505 if (revision == 0x3f) 506 rc = dump_regs_t4vf(argc, argv, regs.data); 507 else 508 rc = dump_regs_t4(argc, argv, regs.data); 509 } else if (vers == 5) { 510 if (revision == 0x3f) 511 rc = dump_regs_t5vf(argc, argv, regs.data); 512 else 513 rc = dump_regs_t5(argc, argv, regs.data); 514 } else if (vers == 6) { 515 if (revision == 0x3f) 516 rc = dump_regs_t6vf(argc, argv, regs.data); 517 else 518 rc = dump_regs_t6(argc, argv, regs.data); 519 } else { 520 warnx("%s (type %d, rev %d) is not a known card.", 521 nexus, vers, revision); 522 return (ENOTSUP); 523 } 524 525 free(regs.data); 526 return (rc); 527 } 528 529 static void 530 do_show_info_header(uint32_t mode) 531 { 532 uint32_t i; 533 534 printf("%4s %8s", "Idx", "Hits"); 535 for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) { 536 switch (mode & i) { 537 case T4_FILTER_FCoE: 538 printf(" FCoE"); 539 break; 540 541 case T4_FILTER_PORT: 542 printf(" Port"); 543 break; 544 545 case T4_FILTER_VNIC: 546 if (mode & T4_FILTER_IC_VNIC) 547 printf(" VFvld:PF:VF"); 548 else 549 printf(" vld:oVLAN"); 550 break; 551 552 case T4_FILTER_VLAN: 553 printf(" vld:VLAN"); 554 break; 555 556 case T4_FILTER_IP_TOS: 557 printf(" TOS"); 558 break; 559 560 case T4_FILTER_IP_PROTO: 561 printf(" Prot"); 562 break; 563 564 case T4_FILTER_ETH_TYPE: 565 printf(" EthType"); 566 break; 567 568 case T4_FILTER_MAC_IDX: 569 printf(" MACIdx"); 570 break; 571 572 case T4_FILTER_MPS_HIT_TYPE: 573 printf(" MPS"); 574 break; 575 576 case T4_FILTER_IP_FRAGMENT: 577 printf(" Frag"); 578 break; 579 580 default: 581 /* compressed filter field not enabled */ 582 break; 583 } 584 } 585 printf(" %20s %20s %9s %9s %s\n", 586 "DIP", "SIP", "DPORT", "SPORT", "Action"); 587 } 588 589 /* 590 * Parse an argument sub-vector as a { <parameter name> <value>[:<mask>] } 591 * ordered tuple. If the parameter name in the argument sub-vector does not 592 * match the passed in parameter name, then a zero is returned for the 593 * function and no parsing is performed. If there is a match, then the value 594 * and optional mask are parsed and returned in the provided return value 595 * pointers. If no optional mask is specified, then a default mask of all 1s 596 * will be returned. 597 * 598 * An error in parsing the value[:mask] will result in an error message and 599 * program termination. 600 */ 601 static int 602 parse_val_mask(const char *param, const char *args[], uint32_t *val, 603 uint32_t *mask) 604 { 605 char *p; 606 607 if (strcmp(param, args[0]) != 0) 608 return (EINVAL); 609 610 *val = strtoul(args[1], &p, 0); 611 if (p > args[1]) { 612 if (p[0] == 0) { 613 *mask = ~0; 614 return (0); 615 } 616 617 if (p[0] == ':' && p[1] != 0) { 618 *mask = strtoul(p+1, &p, 0); 619 if (p[0] == 0) 620 return (0); 621 } 622 } 623 624 warnx("parameter \"%s\" has bad \"value[:mask]\" %s", 625 args[0], args[1]); 626 627 return (EINVAL); 628 } 629 630 /* 631 * Parse an argument sub-vector as a { <parameter name> <addr>[/<mask>] } 632 * ordered tuple. If the parameter name in the argument sub-vector does not 633 * match the passed in parameter name, then a zero is returned for the 634 * function and no parsing is performed. If there is a match, then the value 635 * and optional mask are parsed and returned in the provided return value 636 * pointers. If no optional mask is specified, then a default mask of all 1s 637 * will be returned. 638 * 639 * The value return parameter "afp" is used to specify the expected address 640 * family -- IPv4 or IPv6 -- of the address[/mask] and return its actual 641 * format. A passed in value of AF_UNSPEC indicates that either IPv4 or IPv6 642 * is acceptable; AF_INET means that only IPv4 addresses are acceptable; and 643 * AF_INET6 means that only IPv6 are acceptable. AF_INET is returned for IPv4 644 * and AF_INET6 for IPv6 addresses, respectively. IPv4 address/mask pairs are 645 * returned in the first four bytes of the address and mask return values with 646 * the address A.B.C.D returned with { A, B, C, D } returned in addresses { 0, 647 * 1, 2, 3}, respectively. 648 * 649 * An error in parsing the value[:mask] will result in an error message and 650 * program termination. 651 */ 652 static int 653 parse_ipaddr(const char *param, const char *args[], int *afp, uint8_t addr[], 654 uint8_t mask[]) 655 { 656 const char *colon, *afn; 657 char *slash; 658 uint8_t *m; 659 int af, ret; 660 unsigned int masksize; 661 662 /* 663 * Is this our parameter? 664 */ 665 if (strcmp(param, args[0]) != 0) 666 return (EINVAL); 667 668 /* 669 * Fundamental IPv4 versus IPv6 selection. 670 */ 671 colon = strchr(args[1], ':'); 672 if (!colon) { 673 afn = "IPv4"; 674 af = AF_INET; 675 masksize = 32; 676 } else { 677 afn = "IPv6"; 678 af = AF_INET6; 679 masksize = 128; 680 } 681 if (*afp == AF_UNSPEC) 682 *afp = af; 683 else if (*afp != af) { 684 warnx("address %s is not of expected family %s", 685 args[1], *afp == AF_INET ? "IP" : "IPv6"); 686 return (EINVAL); 687 } 688 689 /* 690 * Parse address (temporarily stripping off any "/mask" 691 * specification). 692 */ 693 slash = strchr(args[1], '/'); 694 if (slash) 695 *slash = 0; 696 ret = inet_pton(af, args[1], addr); 697 if (slash) 698 *slash = '/'; 699 if (ret <= 0) { 700 warnx("Cannot parse %s %s address %s", param, afn, args[1]); 701 return (EINVAL); 702 } 703 704 /* 705 * Parse optional mask specification. 706 */ 707 if (slash) { 708 char *p; 709 unsigned int prefix = strtoul(slash + 1, &p, 10); 710 711 if (p == slash + 1) { 712 warnx("missing address prefix for %s", param); 713 return (EINVAL); 714 } 715 if (*p) { 716 warnx("%s is not a valid address prefix", slash + 1); 717 return (EINVAL); 718 } 719 if (prefix > masksize) { 720 warnx("prefix %u is too long for an %s address", 721 prefix, afn); 722 return (EINVAL); 723 } 724 memset(mask, 0, masksize / 8); 725 masksize = prefix; 726 } 727 728 /* 729 * Fill in mask. 730 */ 731 for (m = mask; masksize >= 8; m++, masksize -= 8) 732 *m = ~0; 733 if (masksize) 734 *m = ~0 << (8 - masksize); 735 736 return (0); 737 } 738 739 /* 740 * Parse an argument sub-vector as a { <parameter name> <value> } ordered 741 * tuple. If the parameter name in the argument sub-vector does not match the 742 * passed in parameter name, then a zero is returned for the function and no 743 * parsing is performed. If there is a match, then the value is parsed and 744 * returned in the provided return value pointer. 745 */ 746 static int 747 parse_val(const char *param, const char *args[], uint32_t *val) 748 { 749 char *p; 750 751 if (strcmp(param, args[0]) != 0) 752 return (EINVAL); 753 754 *val = strtoul(args[1], &p, 0); 755 if (p > args[1] && p[0] == 0) 756 return (0); 757 758 warnx("parameter \"%s\" has bad \"value\" %s", args[0], args[1]); 759 return (EINVAL); 760 } 761 762 static void 763 filters_show_ipaddr(int type, uint8_t *addr, uint8_t *addrm) 764 { 765 int noctets, octet; 766 767 printf(" "); 768 if (type == 0) { 769 noctets = 4; 770 printf("%3s", " "); 771 } else 772 noctets = 16; 773 774 for (octet = 0; octet < noctets; octet++) 775 printf("%02x", addr[octet]); 776 printf("/"); 777 for (octet = 0; octet < noctets; octet++) 778 printf("%02x", addrm[octet]); 779 } 780 781 static void 782 do_show_one_filter_info(struct t4_filter *t, uint32_t mode) 783 { 784 uint32_t i; 785 786 printf("%4d", t->idx); 787 if (t->hits == UINT64_MAX) 788 printf(" %8s", "-"); 789 else 790 printf(" %8ju", t->hits); 791 792 /* 793 * Compressed header portion of filter. 794 */ 795 for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) { 796 switch (mode & i) { 797 case T4_FILTER_FCoE: 798 printf(" %1d/%1d", t->fs.val.fcoe, t->fs.mask.fcoe); 799 break; 800 801 case T4_FILTER_PORT: 802 printf(" %1d/%1d", t->fs.val.iport, t->fs.mask.iport); 803 break; 804 805 case T4_FILTER_VNIC: 806 if (mode & T4_FILTER_IC_VNIC) { 807 printf(" %1d:%1x:%02x/%1d:%1x:%02x", 808 t->fs.val.pfvf_vld, 809 (t->fs.val.vnic >> 13) & 0x7, 810 t->fs.val.vnic & 0x1fff, 811 t->fs.mask.pfvf_vld, 812 (t->fs.mask.vnic >> 13) & 0x7, 813 t->fs.mask.vnic & 0x1fff); 814 } else { 815 printf(" %1d:%04x/%1d:%04x", 816 t->fs.val.ovlan_vld, t->fs.val.vnic, 817 t->fs.mask.ovlan_vld, t->fs.mask.vnic); 818 } 819 break; 820 821 case T4_FILTER_VLAN: 822 printf(" %1d:%04x/%1d:%04x", 823 t->fs.val.vlan_vld, t->fs.val.vlan, 824 t->fs.mask.vlan_vld, t->fs.mask.vlan); 825 break; 826 827 case T4_FILTER_IP_TOS: 828 printf(" %02x/%02x", t->fs.val.tos, t->fs.mask.tos); 829 break; 830 831 case T4_FILTER_IP_PROTO: 832 printf(" %02x/%02x", t->fs.val.proto, t->fs.mask.proto); 833 break; 834 835 case T4_FILTER_ETH_TYPE: 836 printf(" %04x/%04x", t->fs.val.ethtype, 837 t->fs.mask.ethtype); 838 break; 839 840 case T4_FILTER_MAC_IDX: 841 printf(" %03x/%03x", t->fs.val.macidx, 842 t->fs.mask.macidx); 843 break; 844 845 case T4_FILTER_MPS_HIT_TYPE: 846 printf(" %1x/%1x", t->fs.val.matchtype, 847 t->fs.mask.matchtype); 848 break; 849 850 case T4_FILTER_IP_FRAGMENT: 851 printf(" %1d/%1d", t->fs.val.frag, t->fs.mask.frag); 852 break; 853 854 default: 855 /* compressed filter field not enabled */ 856 break; 857 } 858 } 859 860 /* 861 * Fixed portion of filter. 862 */ 863 filters_show_ipaddr(t->fs.type, t->fs.val.dip, t->fs.mask.dip); 864 filters_show_ipaddr(t->fs.type, t->fs.val.sip, t->fs.mask.sip); 865 printf(" %04x/%04x %04x/%04x", 866 t->fs.val.dport, t->fs.mask.dport, 867 t->fs.val.sport, t->fs.mask.sport); 868 869 /* 870 * Variable length filter action. 871 */ 872 if (t->fs.action == FILTER_DROP) 873 printf(" Drop"); 874 else if (t->fs.action == FILTER_SWITCH) { 875 printf(" Switch: port=%d", t->fs.eport); 876 if (t->fs.newdmac) 877 printf( 878 ", dmac=%02x:%02x:%02x:%02x:%02x:%02x " 879 ", l2tidx=%d", 880 t->fs.dmac[0], t->fs.dmac[1], 881 t->fs.dmac[2], t->fs.dmac[3], 882 t->fs.dmac[4], t->fs.dmac[5], 883 t->l2tidx); 884 if (t->fs.newsmac) 885 printf( 886 ", smac=%02x:%02x:%02x:%02x:%02x:%02x " 887 ", smtidx=%d", 888 t->fs.smac[0], t->fs.smac[1], 889 t->fs.smac[2], t->fs.smac[3], 890 t->fs.smac[4], t->fs.smac[5], 891 t->smtidx); 892 if (t->fs.newvlan == VLAN_REMOVE) 893 printf(", vlan=none"); 894 else if (t->fs.newvlan == VLAN_INSERT) 895 printf(", vlan=insert(%x)", t->fs.vlan); 896 else if (t->fs.newvlan == VLAN_REWRITE) 897 printf(", vlan=rewrite(%x)", t->fs.vlan); 898 } else { 899 printf(" Pass: Q="); 900 if (t->fs.dirsteer == 0) { 901 printf("RSS"); 902 if (t->fs.maskhash) 903 printf("(TCB=hash)"); 904 } else { 905 printf("%d", t->fs.iq); 906 if (t->fs.dirsteerhash == 0) 907 printf("(QID)"); 908 else 909 printf("(hash)"); 910 } 911 } 912 if (t->fs.prio) 913 printf(" Prio"); 914 if (t->fs.rpttid) 915 printf(" RptTID"); 916 printf("\n"); 917 } 918 919 static int 920 show_filters(void) 921 { 922 uint32_t mode = 0, header = 0; 923 struct t4_filter t; 924 int rc; 925 926 /* Get the global filter mode first */ 927 rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode); 928 if (rc != 0) 929 return (rc); 930 931 t.idx = 0; 932 for (t.idx = 0; ; t.idx++) { 933 rc = doit(CHELSIO_T4_GET_FILTER, &t); 934 if (rc != 0 || t.idx == 0xffffffff) 935 break; 936 937 if (!header) { 938 do_show_info_header(mode); 939 header = 1; 940 } 941 do_show_one_filter_info(&t, mode); 942 }; 943 944 return (rc); 945 } 946 947 static int 948 get_filter_mode(void) 949 { 950 uint32_t mode = 0; 951 int rc; 952 953 rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode); 954 if (rc != 0) 955 return (rc); 956 957 if (mode & T4_FILTER_IPv4) 958 printf("ipv4 "); 959 960 if (mode & T4_FILTER_IPv6) 961 printf("ipv6 "); 962 963 if (mode & T4_FILTER_IP_SADDR) 964 printf("sip "); 965 966 if (mode & T4_FILTER_IP_DADDR) 967 printf("dip "); 968 969 if (mode & T4_FILTER_IP_SPORT) 970 printf("sport "); 971 972 if (mode & T4_FILTER_IP_DPORT) 973 printf("dport "); 974 975 if (mode & T4_FILTER_IP_FRAGMENT) 976 printf("frag "); 977 978 if (mode & T4_FILTER_MPS_HIT_TYPE) 979 printf("matchtype "); 980 981 if (mode & T4_FILTER_MAC_IDX) 982 printf("macidx "); 983 984 if (mode & T4_FILTER_ETH_TYPE) 985 printf("ethtype "); 986 987 if (mode & T4_FILTER_IP_PROTO) 988 printf("proto "); 989 990 if (mode & T4_FILTER_IP_TOS) 991 printf("tos "); 992 993 if (mode & T4_FILTER_VLAN) 994 printf("vlan "); 995 996 if (mode & T4_FILTER_VNIC) { 997 if (mode & T4_FILTER_IC_VNIC) 998 printf("vnic_id "); 999 else 1000 printf("ovlan "); 1001 } 1002 1003 if (mode & T4_FILTER_PORT) 1004 printf("iport "); 1005 1006 if (mode & T4_FILTER_FCoE) 1007 printf("fcoe "); 1008 1009 printf("\n"); 1010 1011 return (0); 1012 } 1013 1014 static int 1015 set_filter_mode(int argc, const char *argv[]) 1016 { 1017 uint32_t mode = 0; 1018 int vnic = 0, ovlan = 0; 1019 1020 for (; argc; argc--, argv++) { 1021 if (!strcmp(argv[0], "frag")) 1022 mode |= T4_FILTER_IP_FRAGMENT; 1023 1024 if (!strcmp(argv[0], "matchtype")) 1025 mode |= T4_FILTER_MPS_HIT_TYPE; 1026 1027 if (!strcmp(argv[0], "macidx")) 1028 mode |= T4_FILTER_MAC_IDX; 1029 1030 if (!strcmp(argv[0], "ethtype")) 1031 mode |= T4_FILTER_ETH_TYPE; 1032 1033 if (!strcmp(argv[0], "proto")) 1034 mode |= T4_FILTER_IP_PROTO; 1035 1036 if (!strcmp(argv[0], "tos")) 1037 mode |= T4_FILTER_IP_TOS; 1038 1039 if (!strcmp(argv[0], "vlan")) 1040 mode |= T4_FILTER_VLAN; 1041 1042 if (!strcmp(argv[0], "ovlan")) { 1043 mode |= T4_FILTER_VNIC; 1044 ovlan++; 1045 } 1046 1047 if (!strcmp(argv[0], "vnic_id")) { 1048 mode |= T4_FILTER_VNIC; 1049 mode |= T4_FILTER_IC_VNIC; 1050 vnic++; 1051 } 1052 1053 if (!strcmp(argv[0], "iport")) 1054 mode |= T4_FILTER_PORT; 1055 1056 if (!strcmp(argv[0], "fcoe")) 1057 mode |= T4_FILTER_FCoE; 1058 } 1059 1060 if (vnic > 0 && ovlan > 0) { 1061 warnx("\"vnic_id\" and \"ovlan\" are mutually exclusive."); 1062 return (EINVAL); 1063 } 1064 1065 return doit(CHELSIO_T4_SET_FILTER_MODE, &mode); 1066 } 1067 1068 static int 1069 del_filter(uint32_t idx) 1070 { 1071 struct t4_filter t; 1072 1073 t.idx = idx; 1074 1075 return doit(CHELSIO_T4_DEL_FILTER, &t); 1076 } 1077 1078 static int 1079 set_filter(uint32_t idx, int argc, const char *argv[]) 1080 { 1081 int af = AF_UNSPEC, start_arg = 0; 1082 struct t4_filter t; 1083 1084 if (argc < 2) { 1085 warnc(EINVAL, "%s", __func__); 1086 return (EINVAL); 1087 }; 1088 bzero(&t, sizeof (t)); 1089 t.idx = idx; 1090 t.fs.hitcnts = 1; 1091 1092 for (start_arg = 0; start_arg + 2 <= argc; start_arg += 2) { 1093 const char **args = &argv[start_arg]; 1094 uint32_t val, mask; 1095 1096 if (!strcmp(argv[start_arg], "type")) { 1097 int newaf; 1098 if (!strcasecmp(argv[start_arg + 1], "ipv4")) 1099 newaf = AF_INET; 1100 else if (!strcasecmp(argv[start_arg + 1], "ipv6")) 1101 newaf = AF_INET6; 1102 else { 1103 warnx("invalid type \"%s\"; " 1104 "must be one of \"ipv4\" or \"ipv6\"", 1105 argv[start_arg + 1]); 1106 return (EINVAL); 1107 } 1108 1109 if (af != AF_UNSPEC && af != newaf) { 1110 warnx("conflicting IPv4/IPv6 specifications."); 1111 return (EINVAL); 1112 } 1113 af = newaf; 1114 } else if (!parse_val_mask("fcoe", args, &val, &mask)) { 1115 t.fs.val.fcoe = val; 1116 t.fs.mask.fcoe = mask; 1117 } else if (!parse_val_mask("iport", args, &val, &mask)) { 1118 t.fs.val.iport = val; 1119 t.fs.mask.iport = mask; 1120 } else if (!parse_val_mask("ovlan", args, &val, &mask)) { 1121 t.fs.val.vnic = val; 1122 t.fs.mask.vnic = mask; 1123 t.fs.val.ovlan_vld = 1; 1124 t.fs.mask.ovlan_vld = 1; 1125 } else if (!parse_val_mask("ivlan", args, &val, &mask)) { 1126 t.fs.val.vlan = val; 1127 t.fs.mask.vlan = mask; 1128 t.fs.val.vlan_vld = 1; 1129 t.fs.mask.vlan_vld = 1; 1130 } else if (!parse_val_mask("pf", args, &val, &mask)) { 1131 t.fs.val.vnic &= 0x1fff; 1132 t.fs.val.vnic |= (val & 0x7) << 13; 1133 t.fs.mask.vnic &= 0x1fff; 1134 t.fs.mask.vnic |= (mask & 0x7) << 13; 1135 t.fs.val.pfvf_vld = 1; 1136 t.fs.mask.pfvf_vld = 1; 1137 } else if (!parse_val_mask("vf", args, &val, &mask)) { 1138 t.fs.val.vnic &= 0xe000; 1139 t.fs.val.vnic |= val & 0x1fff; 1140 t.fs.mask.vnic &= 0xe000; 1141 t.fs.mask.vnic |= mask & 0x1fff; 1142 t.fs.val.pfvf_vld = 1; 1143 t.fs.mask.pfvf_vld = 1; 1144 } else if (!parse_val_mask("tos", args, &val, &mask)) { 1145 t.fs.val.tos = val; 1146 t.fs.mask.tos = mask; 1147 } else if (!parse_val_mask("proto", args, &val, &mask)) { 1148 t.fs.val.proto = val; 1149 t.fs.mask.proto = mask; 1150 } else if (!parse_val_mask("ethtype", args, &val, &mask)) { 1151 t.fs.val.ethtype = val; 1152 t.fs.mask.ethtype = mask; 1153 } else if (!parse_val_mask("macidx", args, &val, &mask)) { 1154 t.fs.val.macidx = val; 1155 t.fs.mask.macidx = mask; 1156 } else if (!parse_val_mask("matchtype", args, &val, &mask)) { 1157 t.fs.val.matchtype = val; 1158 t.fs.mask.matchtype = mask; 1159 } else if (!parse_val_mask("frag", args, &val, &mask)) { 1160 t.fs.val.frag = val; 1161 t.fs.mask.frag = mask; 1162 } else if (!parse_val_mask("dport", args, &val, &mask)) { 1163 t.fs.val.dport = val; 1164 t.fs.mask.dport = mask; 1165 } else if (!parse_val_mask("sport", args, &val, &mask)) { 1166 t.fs.val.sport = val; 1167 t.fs.mask.sport = mask; 1168 } else if (!parse_ipaddr("dip", args, &af, t.fs.val.dip, 1169 t.fs.mask.dip)) { 1170 /* nada */; 1171 } else if (!parse_ipaddr("sip", args, &af, t.fs.val.sip, 1172 t.fs.mask.sip)) { 1173 /* nada */; 1174 } else if (!strcmp(argv[start_arg], "action")) { 1175 if (!strcmp(argv[start_arg + 1], "pass")) 1176 t.fs.action = FILTER_PASS; 1177 else if (!strcmp(argv[start_arg + 1], "drop")) 1178 t.fs.action = FILTER_DROP; 1179 else if (!strcmp(argv[start_arg + 1], "switch")) 1180 t.fs.action = FILTER_SWITCH; 1181 else { 1182 warnx("invalid action \"%s\"; must be one of" 1183 " \"pass\", \"drop\" or \"switch\"", 1184 argv[start_arg + 1]); 1185 return (EINVAL); 1186 } 1187 } else if (!parse_val("hitcnts", args, &val)) { 1188 t.fs.hitcnts = val; 1189 } else if (!parse_val("prio", args, &val)) { 1190 t.fs.prio = val; 1191 } else if (!parse_val("rpttid", args, &val)) { 1192 t.fs.rpttid = 1; 1193 } else if (!parse_val("queue", args, &val)) { 1194 t.fs.dirsteer = 1; 1195 t.fs.iq = val; 1196 } else if (!parse_val("tcbhash", args, &val)) { 1197 t.fs.maskhash = 1; 1198 t.fs.dirsteerhash = 1; 1199 } else if (!parse_val("eport", args, &val)) { 1200 t.fs.eport = val; 1201 } else if (!strcmp(argv[start_arg], "dmac")) { 1202 struct ether_addr *daddr; 1203 1204 daddr = ether_aton(argv[start_arg + 1]); 1205 if (daddr == NULL) { 1206 warnx("invalid dmac address \"%s\"", 1207 argv[start_arg + 1]); 1208 return (EINVAL); 1209 } 1210 memcpy(t.fs.dmac, daddr, ETHER_ADDR_LEN); 1211 t.fs.newdmac = 1; 1212 } else if (!strcmp(argv[start_arg], "smac")) { 1213 struct ether_addr *saddr; 1214 1215 saddr = ether_aton(argv[start_arg + 1]); 1216 if (saddr == NULL) { 1217 warnx("invalid smac address \"%s\"", 1218 argv[start_arg + 1]); 1219 return (EINVAL); 1220 } 1221 memcpy(t.fs.smac, saddr, ETHER_ADDR_LEN); 1222 t.fs.newsmac = 1; 1223 } else if (!strcmp(argv[start_arg], "vlan")) { 1224 char *p; 1225 if (!strcmp(argv[start_arg + 1], "none")) { 1226 t.fs.newvlan = VLAN_REMOVE; 1227 } else if (argv[start_arg + 1][0] == '=') { 1228 t.fs.newvlan = VLAN_REWRITE; 1229 } else if (argv[start_arg + 1][0] == '+') { 1230 t.fs.newvlan = VLAN_INSERT; 1231 } else if (isdigit(argv[start_arg + 1][0]) && 1232 !parse_val_mask("vlan", args, &val, &mask)) { 1233 t.fs.val.vlan = val; 1234 t.fs.mask.vlan = mask; 1235 t.fs.val.vlan_vld = 1; 1236 t.fs.mask.vlan_vld = 1; 1237 } else { 1238 warnx("unknown vlan parameter \"%s\"; must" 1239 " be one of \"none\", \"=<vlan>\", " 1240 " \"+<vlan>\", or \"<vlan>\"", 1241 argv[start_arg + 1]); 1242 return (EINVAL); 1243 } 1244 if (t.fs.newvlan == VLAN_REWRITE || 1245 t.fs.newvlan == VLAN_INSERT) { 1246 t.fs.vlan = strtoul(argv[start_arg + 1] + 1, 1247 &p, 0); 1248 if (p == argv[start_arg + 1] + 1 || p[0] != 0) { 1249 warnx("invalid vlan \"%s\"", 1250 argv[start_arg + 1]); 1251 return (EINVAL); 1252 } 1253 } 1254 } else { 1255 warnx("invalid parameter \"%s\"", argv[start_arg]); 1256 return (EINVAL); 1257 } 1258 } 1259 if (start_arg != argc) { 1260 warnx("no value for \"%s\"", argv[start_arg]); 1261 return (EINVAL); 1262 } 1263 1264 /* 1265 * Check basic sanity of option combinations. 1266 */ 1267 if (t.fs.action != FILTER_SWITCH && 1268 (t.fs.eport || t.fs.newdmac || t.fs.newsmac || t.fs.newvlan)) { 1269 warnx("prio, port dmac, smac and vlan only make sense with" 1270 " \"action switch\""); 1271 return (EINVAL); 1272 } 1273 if (t.fs.action != FILTER_PASS && 1274 (t.fs.rpttid || t.fs.dirsteer || t.fs.maskhash)) { 1275 warnx("rpttid, queue and tcbhash don't make sense with" 1276 " action \"drop\" or \"switch\""); 1277 return (EINVAL); 1278 } 1279 if (t.fs.val.ovlan_vld && t.fs.val.pfvf_vld) { 1280 warnx("ovlan and vnic_id (pf/vf) are mutually exclusive"); 1281 return (EINVAL); 1282 } 1283 1284 t.fs.type = (af == AF_INET6 ? 1 : 0); /* default IPv4 */ 1285 return doit(CHELSIO_T4_SET_FILTER, &t); 1286 } 1287 1288 static int 1289 filter_cmd(int argc, const char *argv[]) 1290 { 1291 long long val; 1292 uint32_t idx; 1293 char *s; 1294 1295 if (argc == 0) { 1296 warnx("filter: no arguments."); 1297 return (EINVAL); 1298 }; 1299 1300 /* list */ 1301 if (strcmp(argv[0], "list") == 0) { 1302 if (argc != 1) 1303 warnx("trailing arguments after \"list\" ignored."); 1304 1305 return show_filters(); 1306 } 1307 1308 /* mode */ 1309 if (argc == 1 && strcmp(argv[0], "mode") == 0) 1310 return get_filter_mode(); 1311 1312 /* mode <mode> */ 1313 if (strcmp(argv[0], "mode") == 0) 1314 return set_filter_mode(argc - 1, argv + 1); 1315 1316 /* <idx> ... */ 1317 s = str_to_number(argv[0], NULL, &val); 1318 if (*s || val > 0xffffffffU) { 1319 warnx("\"%s\" is neither an index nor a filter subcommand.", 1320 argv[0]); 1321 return (EINVAL); 1322 } 1323 idx = (uint32_t) val; 1324 1325 /* <idx> delete|clear */ 1326 if (argc == 2 && 1327 (strcmp(argv[1], "delete") == 0 || strcmp(argv[1], "clear") == 0)) { 1328 return del_filter(idx); 1329 } 1330 1331 /* <idx> [<param> <val>] ... */ 1332 return set_filter(idx, argc - 1, argv + 1); 1333 } 1334 1335 /* 1336 * Shows the fields of a multi-word structure. The structure is considered to 1337 * consist of @nwords 32-bit words (i.e, it's an (@nwords * 32)-bit structure) 1338 * whose fields are described by @fd. The 32-bit words are given in @words 1339 * starting with the least significant 32-bit word. 1340 */ 1341 static void 1342 show_struct(const uint32_t *words, int nwords, const struct field_desc *fd) 1343 { 1344 unsigned int w = 0; 1345 const struct field_desc *p; 1346 1347 for (p = fd; p->name; p++) 1348 w = max(w, strlen(p->name)); 1349 1350 while (fd->name) { 1351 unsigned long long data; 1352 int first_word = fd->start / 32; 1353 int shift = fd->start % 32; 1354 int width = fd->end - fd->start + 1; 1355 unsigned long long mask = (1ULL << width) - 1; 1356 1357 data = (words[first_word] >> shift) | 1358 ((uint64_t)words[first_word + 1] << (32 - shift)); 1359 if (shift) 1360 data |= ((uint64_t)words[first_word + 2] << (64 - shift)); 1361 data &= mask; 1362 if (fd->islog2) 1363 data = 1 << data; 1364 printf("%-*s ", w, fd->name); 1365 printf(fd->hex ? "%#llx\n" : "%llu\n", data << fd->shift); 1366 fd++; 1367 } 1368 } 1369 1370 #define FIELD(name, start, end) { name, start, end, 0, 0, 0 } 1371 #define FIELD1(name, start) FIELD(name, start, start) 1372 1373 static void 1374 show_t5t6_ctxt(const struct t4_sge_context *p, int vers) 1375 { 1376 static struct field_desc egress_t5[] = { 1377 FIELD("DCA_ST:", 181, 191), 1378 FIELD1("StatusPgNS:", 180), 1379 FIELD1("StatusPgRO:", 179), 1380 FIELD1("FetchNS:", 178), 1381 FIELD1("FetchRO:", 177), 1382 FIELD1("Valid:", 176), 1383 FIELD("PCIeDataChannel:", 174, 175), 1384 FIELD1("StatusPgTPHintEn:", 173), 1385 FIELD("StatusPgTPHint:", 171, 172), 1386 FIELD1("FetchTPHintEn:", 170), 1387 FIELD("FetchTPHint:", 168, 169), 1388 FIELD1("FCThreshOverride:", 167), 1389 { "WRLength:", 162, 166, 9, 0, 1 }, 1390 FIELD1("WRLengthKnown:", 161), 1391 FIELD1("ReschedulePending:", 160), 1392 FIELD1("OnChipQueue:", 159), 1393 FIELD1("FetchSizeMode:", 158), 1394 { "FetchBurstMin:", 156, 157, 4, 0, 1 }, 1395 FIELD1("FLMPacking:", 155), 1396 FIELD("FetchBurstMax:", 153, 154), 1397 FIELD("uPToken:", 133, 152), 1398 FIELD1("uPTokenEn:", 132), 1399 FIELD1("UserModeIO:", 131), 1400 FIELD("uPFLCredits:", 123, 130), 1401 FIELD1("uPFLCreditEn:", 122), 1402 FIELD("FID:", 111, 121), 1403 FIELD("HostFCMode:", 109, 110), 1404 FIELD1("HostFCOwner:", 108), 1405 { "CIDXFlushThresh:", 105, 107, 0, 0, 1 }, 1406 FIELD("CIDX:", 89, 104), 1407 FIELD("PIDX:", 73, 88), 1408 { "BaseAddress:", 18, 72, 9, 1 }, 1409 FIELD("QueueSize:", 2, 17), 1410 FIELD1("QueueType:", 1), 1411 FIELD1("CachePriority:", 0), 1412 { NULL } 1413 }; 1414 static struct field_desc egress_t6[] = { 1415 FIELD("DCA_ST:", 181, 191), 1416 FIELD1("StatusPgNS:", 180), 1417 FIELD1("StatusPgRO:", 179), 1418 FIELD1("FetchNS:", 178), 1419 FIELD1("FetchRO:", 177), 1420 FIELD1("Valid:", 176), 1421 FIELD1("ReschedulePending_1:", 175), 1422 FIELD1("PCIeDataChannel:", 174), 1423 FIELD1("StatusPgTPHintEn:", 173), 1424 FIELD("StatusPgTPHint:", 171, 172), 1425 FIELD1("FetchTPHintEn:", 170), 1426 FIELD("FetchTPHint:", 168, 169), 1427 FIELD1("FCThreshOverride:", 167), 1428 { "WRLength:", 162, 166, 9, 0, 1 }, 1429 FIELD1("WRLengthKnown:", 161), 1430 FIELD1("ReschedulePending:", 160), 1431 FIELD("TimerIx:", 157, 159), 1432 FIELD1("FetchBurstMin:", 156), 1433 FIELD1("FLMPacking:", 155), 1434 FIELD("FetchBurstMax:", 153, 154), 1435 FIELD("uPToken:", 133, 152), 1436 FIELD1("uPTokenEn:", 132), 1437 FIELD1("UserModeIO:", 131), 1438 FIELD("uPFLCredits:", 123, 130), 1439 FIELD1("uPFLCreditEn:", 122), 1440 FIELD("FID:", 111, 121), 1441 FIELD("HostFCMode:", 109, 110), 1442 FIELD1("HostFCOwner:", 108), 1443 { "CIDXFlushThresh:", 105, 107, 0, 0, 1 }, 1444 FIELD("CIDX:", 89, 104), 1445 FIELD("PIDX:", 73, 88), 1446 { "BaseAddress:", 18, 72, 9, 1 }, 1447 FIELD("QueueSize:", 2, 17), 1448 FIELD1("QueueType:", 1), 1449 FIELD1("FetchSizeMode:", 0), 1450 { NULL } 1451 }; 1452 static struct field_desc fl_t5[] = { 1453 FIELD("DCA_ST:", 181, 191), 1454 FIELD1("StatusPgNS:", 180), 1455 FIELD1("StatusPgRO:", 179), 1456 FIELD1("FetchNS:", 178), 1457 FIELD1("FetchRO:", 177), 1458 FIELD1("Valid:", 176), 1459 FIELD("PCIeDataChannel:", 174, 175), 1460 FIELD1("StatusPgTPHintEn:", 173), 1461 FIELD("StatusPgTPHint:", 171, 172), 1462 FIELD1("FetchTPHintEn:", 170), 1463 FIELD("FetchTPHint:", 168, 169), 1464 FIELD1("FCThreshOverride:", 167), 1465 FIELD1("ReschedulePending:", 160), 1466 FIELD1("OnChipQueue:", 159), 1467 FIELD1("FetchSizeMode:", 158), 1468 { "FetchBurstMin:", 156, 157, 4, 0, 1 }, 1469 FIELD1("FLMPacking:", 155), 1470 FIELD("FetchBurstMax:", 153, 154), 1471 FIELD1("FLMcongMode:", 152), 1472 FIELD("MaxuPFLCredits:", 144, 151), 1473 FIELD("FLMcontextID:", 133, 143), 1474 FIELD1("uPTokenEn:", 132), 1475 FIELD1("UserModeIO:", 131), 1476 FIELD("uPFLCredits:", 123, 130), 1477 FIELD1("uPFLCreditEn:", 122), 1478 FIELD("FID:", 111, 121), 1479 FIELD("HostFCMode:", 109, 110), 1480 FIELD1("HostFCOwner:", 108), 1481 { "CIDXFlushThresh:", 105, 107, 0, 0, 1 }, 1482 FIELD("CIDX:", 89, 104), 1483 FIELD("PIDX:", 73, 88), 1484 { "BaseAddress:", 18, 72, 9, 1 }, 1485 FIELD("QueueSize:", 2, 17), 1486 FIELD1("QueueType:", 1), 1487 FIELD1("CachePriority:", 0), 1488 { NULL } 1489 }; 1490 static struct field_desc ingress_t5[] = { 1491 FIELD("DCA_ST:", 143, 153), 1492 FIELD1("ISCSICoalescing:", 142), 1493 FIELD1("Queue_Valid:", 141), 1494 FIELD1("TimerPending:", 140), 1495 FIELD1("DropRSS:", 139), 1496 FIELD("PCIeChannel:", 137, 138), 1497 FIELD1("SEInterruptArmed:", 136), 1498 FIELD1("CongestionMgtEnable:", 135), 1499 FIELD1("NoSnoop:", 134), 1500 FIELD1("RelaxedOrdering:", 133), 1501 FIELD1("GTSmode:", 132), 1502 FIELD1("TPHintEn:", 131), 1503 FIELD("TPHint:", 129, 130), 1504 FIELD1("UpdateScheduling:", 128), 1505 FIELD("UpdateDelivery:", 126, 127), 1506 FIELD1("InterruptSent:", 125), 1507 FIELD("InterruptIDX:", 114, 124), 1508 FIELD1("InterruptDestination:", 113), 1509 FIELD1("InterruptArmed:", 112), 1510 FIELD("RxIntCounter:", 106, 111), 1511 FIELD("RxIntCounterThreshold:", 104, 105), 1512 FIELD1("Generation:", 103), 1513 { "BaseAddress:", 48, 102, 9, 1 }, 1514 FIELD("PIDX:", 32, 47), 1515 FIELD("CIDX:", 16, 31), 1516 { "QueueSize:", 4, 15, 4, 0 }, 1517 { "QueueEntrySize:", 2, 3, 4, 0, 1 }, 1518 FIELD1("QueueEntryOverride:", 1), 1519 FIELD1("CachePriority:", 0), 1520 { NULL } 1521 }; 1522 static struct field_desc ingress_t6[] = { 1523 FIELD1("SP_NS:", 158), 1524 FIELD1("SP_RO:", 157), 1525 FIELD1("SP_TPHintEn:", 156), 1526 FIELD("SP_TPHint:", 154, 155), 1527 FIELD("DCA_ST:", 143, 153), 1528 FIELD1("ISCSICoalescing:", 142), 1529 FIELD1("Queue_Valid:", 141), 1530 FIELD1("TimerPending:", 140), 1531 FIELD1("DropRSS:", 139), 1532 FIELD("PCIeChannel:", 137, 138), 1533 FIELD1("SEInterruptArmed:", 136), 1534 FIELD1("CongestionMgtEnable:", 135), 1535 FIELD1("NoSnoop:", 134), 1536 FIELD1("RelaxedOrdering:", 133), 1537 FIELD1("GTSmode:", 132), 1538 FIELD1("TPHintEn:", 131), 1539 FIELD("TPHint:", 129, 130), 1540 FIELD1("UpdateScheduling:", 128), 1541 FIELD("UpdateDelivery:", 126, 127), 1542 FIELD1("InterruptSent:", 125), 1543 FIELD("InterruptIDX:", 114, 124), 1544 FIELD1("InterruptDestination:", 113), 1545 FIELD1("InterruptArmed:", 112), 1546 FIELD("RxIntCounter:", 106, 111), 1547 FIELD("RxIntCounterThreshold:", 104, 105), 1548 FIELD1("Generation:", 103), 1549 { "BaseAddress:", 48, 102, 9, 1 }, 1550 FIELD("PIDX:", 32, 47), 1551 FIELD("CIDX:", 16, 31), 1552 { "QueueSize:", 4, 15, 4, 0 }, 1553 { "QueueEntrySize:", 2, 3, 4, 0, 1 }, 1554 FIELD1("QueueEntryOverride:", 1), 1555 FIELD1("CachePriority:", 0), 1556 { NULL } 1557 }; 1558 static struct field_desc flm_t5[] = { 1559 FIELD1("Valid:", 89), 1560 FIELD("SplitLenMode:", 87, 88), 1561 FIELD1("TPHintEn:", 86), 1562 FIELD("TPHint:", 84, 85), 1563 FIELD1("NoSnoop:", 83), 1564 FIELD1("RelaxedOrdering:", 82), 1565 FIELD("DCA_ST:", 71, 81), 1566 FIELD("EQid:", 54, 70), 1567 FIELD("SplitEn:", 52, 53), 1568 FIELD1("PadEn:", 51), 1569 FIELD1("PackEn:", 50), 1570 FIELD1("Cache_Lock :", 49), 1571 FIELD1("CongDrop:", 48), 1572 FIELD("PackOffset:", 16, 47), 1573 FIELD("CIDX:", 8, 15), 1574 FIELD("PIDX:", 0, 7), 1575 { NULL } 1576 }; 1577 static struct field_desc flm_t6[] = { 1578 FIELD1("Valid:", 89), 1579 FIELD("SplitLenMode:", 87, 88), 1580 FIELD1("TPHintEn:", 86), 1581 FIELD("TPHint:", 84, 85), 1582 FIELD1("NoSnoop:", 83), 1583 FIELD1("RelaxedOrdering:", 82), 1584 FIELD("DCA_ST:", 71, 81), 1585 FIELD("EQid:", 54, 70), 1586 FIELD("SplitEn:", 52, 53), 1587 FIELD1("PadEn:", 51), 1588 FIELD1("PackEn:", 50), 1589 FIELD1("Cache_Lock :", 49), 1590 FIELD1("CongDrop:", 48), 1591 FIELD1("Inflight:", 47), 1592 FIELD1("CongEn:", 46), 1593 FIELD1("CongMode:", 45), 1594 FIELD("PackOffset:", 20, 39), 1595 FIELD("CIDX:", 8, 15), 1596 FIELD("PIDX:", 0, 7), 1597 { NULL } 1598 }; 1599 static struct field_desc conm_t5[] = { 1600 FIELD1("CngMPSEnable:", 21), 1601 FIELD("CngTPMode:", 19, 20), 1602 FIELD1("CngDBPHdr:", 18), 1603 FIELD1("CngDBPData:", 17), 1604 FIELD1("CngIMSG:", 16), 1605 { "CngChMap:", 0, 15, 0, 1, 0 }, 1606 { NULL } 1607 }; 1608 1609 if (p->mem_id == SGE_CONTEXT_EGRESS) { 1610 if (p->data[0] & 2) 1611 show_struct(p->data, 6, fl_t5); 1612 else if (vers == 5) 1613 show_struct(p->data, 6, egress_t5); 1614 else 1615 show_struct(p->data, 6, egress_t6); 1616 } else if (p->mem_id == SGE_CONTEXT_FLM) 1617 show_struct(p->data, 3, vers == 5 ? flm_t5 : flm_t6); 1618 else if (p->mem_id == SGE_CONTEXT_INGRESS) 1619 show_struct(p->data, 5, vers == 5 ? ingress_t5 : ingress_t6); 1620 else if (p->mem_id == SGE_CONTEXT_CNM) 1621 show_struct(p->data, 1, conm_t5); 1622 } 1623 1624 static void 1625 show_t4_ctxt(const struct t4_sge_context *p) 1626 { 1627 static struct field_desc egress_t4[] = { 1628 FIELD1("StatusPgNS:", 180), 1629 FIELD1("StatusPgRO:", 179), 1630 FIELD1("FetchNS:", 178), 1631 FIELD1("FetchRO:", 177), 1632 FIELD1("Valid:", 176), 1633 FIELD("PCIeDataChannel:", 174, 175), 1634 FIELD1("DCAEgrQEn:", 173), 1635 FIELD("DCACPUID:", 168, 172), 1636 FIELD1("FCThreshOverride:", 167), 1637 FIELD("WRLength:", 162, 166), 1638 FIELD1("WRLengthKnown:", 161), 1639 FIELD1("ReschedulePending:", 160), 1640 FIELD1("OnChipQueue:", 159), 1641 FIELD1("FetchSizeMode", 158), 1642 { "FetchBurstMin:", 156, 157, 4, 0, 1 }, 1643 { "FetchBurstMax:", 153, 154, 6, 0, 1 }, 1644 FIELD("uPToken:", 133, 152), 1645 FIELD1("uPTokenEn:", 132), 1646 FIELD1("UserModeIO:", 131), 1647 FIELD("uPFLCredits:", 123, 130), 1648 FIELD1("uPFLCreditEn:", 122), 1649 FIELD("FID:", 111, 121), 1650 FIELD("HostFCMode:", 109, 110), 1651 FIELD1("HostFCOwner:", 108), 1652 { "CIDXFlushThresh:", 105, 107, 0, 0, 1 }, 1653 FIELD("CIDX:", 89, 104), 1654 FIELD("PIDX:", 73, 88), 1655 { "BaseAddress:", 18, 72, 9, 1 }, 1656 FIELD("QueueSize:", 2, 17), 1657 FIELD1("QueueType:", 1), 1658 FIELD1("CachePriority:", 0), 1659 { NULL } 1660 }; 1661 static struct field_desc fl_t4[] = { 1662 FIELD1("StatusPgNS:", 180), 1663 FIELD1("StatusPgRO:", 179), 1664 FIELD1("FetchNS:", 178), 1665 FIELD1("FetchRO:", 177), 1666 FIELD1("Valid:", 176), 1667 FIELD("PCIeDataChannel:", 174, 175), 1668 FIELD1("DCAEgrQEn:", 173), 1669 FIELD("DCACPUID:", 168, 172), 1670 FIELD1("FCThreshOverride:", 167), 1671 FIELD1("ReschedulePending:", 160), 1672 FIELD1("OnChipQueue:", 159), 1673 FIELD1("FetchSizeMode", 158), 1674 { "FetchBurstMin:", 156, 157, 4, 0, 1 }, 1675 { "FetchBurstMax:", 153, 154, 6, 0, 1 }, 1676 FIELD1("FLMcongMode:", 152), 1677 FIELD("MaxuPFLCredits:", 144, 151), 1678 FIELD("FLMcontextID:", 133, 143), 1679 FIELD1("uPTokenEn:", 132), 1680 FIELD1("UserModeIO:", 131), 1681 FIELD("uPFLCredits:", 123, 130), 1682 FIELD1("uPFLCreditEn:", 122), 1683 FIELD("FID:", 111, 121), 1684 FIELD("HostFCMode:", 109, 110), 1685 FIELD1("HostFCOwner:", 108), 1686 { "CIDXFlushThresh:", 105, 107, 0, 0, 1 }, 1687 FIELD("CIDX:", 89, 104), 1688 FIELD("PIDX:", 73, 88), 1689 { "BaseAddress:", 18, 72, 9, 1 }, 1690 FIELD("QueueSize:", 2, 17), 1691 FIELD1("QueueType:", 1), 1692 FIELD1("CachePriority:", 0), 1693 { NULL } 1694 }; 1695 static struct field_desc ingress_t4[] = { 1696 FIELD1("NoSnoop:", 145), 1697 FIELD1("RelaxedOrdering:", 144), 1698 FIELD1("GTSmode:", 143), 1699 FIELD1("ISCSICoalescing:", 142), 1700 FIELD1("Valid:", 141), 1701 FIELD1("TimerPending:", 140), 1702 FIELD1("DropRSS:", 139), 1703 FIELD("PCIeChannel:", 137, 138), 1704 FIELD1("SEInterruptArmed:", 136), 1705 FIELD1("CongestionMgtEnable:", 135), 1706 FIELD1("DCAIngQEnable:", 134), 1707 FIELD("DCACPUID:", 129, 133), 1708 FIELD1("UpdateScheduling:", 128), 1709 FIELD("UpdateDelivery:", 126, 127), 1710 FIELD1("InterruptSent:", 125), 1711 FIELD("InterruptIDX:", 114, 124), 1712 FIELD1("InterruptDestination:", 113), 1713 FIELD1("InterruptArmed:", 112), 1714 FIELD("RxIntCounter:", 106, 111), 1715 FIELD("RxIntCounterThreshold:", 104, 105), 1716 FIELD1("Generation:", 103), 1717 { "BaseAddress:", 48, 102, 9, 1 }, 1718 FIELD("PIDX:", 32, 47), 1719 FIELD("CIDX:", 16, 31), 1720 { "QueueSize:", 4, 15, 4, 0 }, 1721 { "QueueEntrySize:", 2, 3, 4, 0, 1 }, 1722 FIELD1("QueueEntryOverride:", 1), 1723 FIELD1("CachePriority:", 0), 1724 { NULL } 1725 }; 1726 static struct field_desc flm_t4[] = { 1727 FIELD1("NoSnoop:", 79), 1728 FIELD1("RelaxedOrdering:", 78), 1729 FIELD1("Valid:", 77), 1730 FIELD("DCACPUID:", 72, 76), 1731 FIELD1("DCAFLEn:", 71), 1732 FIELD("EQid:", 54, 70), 1733 FIELD("SplitEn:", 52, 53), 1734 FIELD1("PadEn:", 51), 1735 FIELD1("PackEn:", 50), 1736 FIELD1("DBpriority:", 48), 1737 FIELD("PackOffset:", 16, 47), 1738 FIELD("CIDX:", 8, 15), 1739 FIELD("PIDX:", 0, 7), 1740 { NULL } 1741 }; 1742 static struct field_desc conm_t4[] = { 1743 FIELD1("CngDBPHdr:", 6), 1744 FIELD1("CngDBPData:", 5), 1745 FIELD1("CngIMSG:", 4), 1746 { "CngChMap:", 0, 3, 0, 1, 0}, 1747 { NULL } 1748 }; 1749 1750 if (p->mem_id == SGE_CONTEXT_EGRESS) 1751 show_struct(p->data, 6, (p->data[0] & 2) ? fl_t4 : egress_t4); 1752 else if (p->mem_id == SGE_CONTEXT_FLM) 1753 show_struct(p->data, 3, flm_t4); 1754 else if (p->mem_id == SGE_CONTEXT_INGRESS) 1755 show_struct(p->data, 5, ingress_t4); 1756 else if (p->mem_id == SGE_CONTEXT_CNM) 1757 show_struct(p->data, 1, conm_t4); 1758 } 1759 1760 #undef FIELD 1761 #undef FIELD1 1762 1763 static int 1764 get_sge_context(int argc, const char *argv[]) 1765 { 1766 int rc; 1767 char *p; 1768 long cid; 1769 struct t4_sge_context cntxt = {0}; 1770 1771 if (argc != 2) { 1772 warnx("sge_context: incorrect number of arguments."); 1773 return (EINVAL); 1774 } 1775 1776 if (!strcmp(argv[0], "egress")) 1777 cntxt.mem_id = SGE_CONTEXT_EGRESS; 1778 else if (!strcmp(argv[0], "ingress")) 1779 cntxt.mem_id = SGE_CONTEXT_INGRESS; 1780 else if (!strcmp(argv[0], "fl")) 1781 cntxt.mem_id = SGE_CONTEXT_FLM; 1782 else if (!strcmp(argv[0], "cong")) 1783 cntxt.mem_id = SGE_CONTEXT_CNM; 1784 else { 1785 warnx("unknown context type \"%s\"; known types are egress, " 1786 "ingress, fl, and cong.", argv[0]); 1787 return (EINVAL); 1788 } 1789 1790 p = str_to_number(argv[1], &cid, NULL); 1791 if (*p) { 1792 warnx("invalid context id \"%s\"", argv[1]); 1793 return (EINVAL); 1794 } 1795 cntxt.cid = cid; 1796 1797 rc = doit(CHELSIO_T4_GET_SGE_CONTEXT, &cntxt); 1798 if (rc != 0) 1799 return (rc); 1800 1801 if (chip_id == 4) 1802 show_t4_ctxt(&cntxt); 1803 else 1804 show_t5t6_ctxt(&cntxt, chip_id); 1805 1806 return (0); 1807 } 1808 1809 static int 1810 loadfw(int argc, const char *argv[]) 1811 { 1812 int rc, fd; 1813 struct t4_data data = {0}; 1814 const char *fname = argv[0]; 1815 struct stat st = {0}; 1816 1817 if (argc != 1) { 1818 warnx("loadfw: incorrect number of arguments."); 1819 return (EINVAL); 1820 } 1821 1822 fd = open(fname, O_RDONLY); 1823 if (fd < 0) { 1824 warn("open(%s)", fname); 1825 return (errno); 1826 } 1827 1828 if (fstat(fd, &st) < 0) { 1829 warn("fstat"); 1830 close(fd); 1831 return (errno); 1832 } 1833 1834 data.len = st.st_size; 1835 data.data = mmap(0, data.len, PROT_READ, MAP_PRIVATE, fd, 0); 1836 if (data.data == MAP_FAILED) { 1837 warn("mmap"); 1838 close(fd); 1839 return (errno); 1840 } 1841 1842 rc = doit(CHELSIO_T4_LOAD_FW, &data); 1843 munmap(data.data, data.len); 1844 close(fd); 1845 return (rc); 1846 } 1847 1848 static int 1849 loadcfg(int argc, const char *argv[]) 1850 { 1851 int rc, fd; 1852 struct t4_data data = {0}; 1853 const char *fname = argv[0]; 1854 struct stat st = {0}; 1855 1856 if (argc != 1) { 1857 warnx("loadcfg: incorrect number of arguments."); 1858 return (EINVAL); 1859 } 1860 1861 if (strcmp(fname, "clear") == 0) 1862 return (doit(CHELSIO_T4_LOAD_CFG, &data)); 1863 1864 fd = open(fname, O_RDONLY); 1865 if (fd < 0) { 1866 warn("open(%s)", fname); 1867 return (errno); 1868 } 1869 1870 if (fstat(fd, &st) < 0) { 1871 warn("fstat"); 1872 close(fd); 1873 return (errno); 1874 } 1875 1876 data.len = st.st_size; 1877 data.len &= ~3; /* Clip off to make it a multiple of 4 */ 1878 data.data = mmap(0, data.len, PROT_READ, MAP_PRIVATE, fd, 0); 1879 if (data.data == MAP_FAILED) { 1880 warn("mmap"); 1881 close(fd); 1882 return (errno); 1883 } 1884 1885 rc = doit(CHELSIO_T4_LOAD_CFG, &data); 1886 munmap(data.data, data.len); 1887 close(fd); 1888 return (rc); 1889 } 1890 1891 static int 1892 dumpstate(int argc, const char *argv[]) 1893 { 1894 int rc, fd; 1895 struct t4_cudbg_dump dump = {0}; 1896 const char *fname = argv[0]; 1897 1898 if (argc != 1) { 1899 warnx("dumpstate: incorrect number of arguments."); 1900 return (EINVAL); 1901 } 1902 1903 dump.wr_flash = 0; 1904 memset(&dump.bitmap, 0xff, sizeof(dump.bitmap)); 1905 dump.len = 8 * 1024 * 1024; 1906 dump.data = malloc(dump.len); 1907 if (dump.data == NULL) { 1908 return (ENOMEM); 1909 } 1910 1911 rc = doit(CHELSIO_T4_CUDBG_DUMP, &dump); 1912 if (rc != 0) 1913 goto done; 1914 1915 fd = open(fname, O_CREAT | O_TRUNC | O_EXCL | O_WRONLY, 1916 S_IRUSR | S_IRGRP | S_IROTH); 1917 if (fd < 0) { 1918 warn("open(%s)", fname); 1919 rc = errno; 1920 goto done; 1921 } 1922 write(fd, dump.data, dump.len); 1923 close(fd); 1924 done: 1925 free(dump.data); 1926 return (rc); 1927 } 1928 1929 static int 1930 read_mem(uint32_t addr, uint32_t len, void (*output)(uint32_t *, uint32_t)) 1931 { 1932 int rc; 1933 struct t4_mem_range mr; 1934 1935 mr.addr = addr; 1936 mr.len = len; 1937 mr.data = malloc(mr.len); 1938 1939 if (mr.data == 0) { 1940 warn("read_mem: malloc"); 1941 return (errno); 1942 } 1943 1944 rc = doit(CHELSIO_T4_GET_MEM, &mr); 1945 if (rc != 0) 1946 goto done; 1947 1948 if (output) 1949 (*output)(mr.data, mr.len); 1950 done: 1951 free(mr.data); 1952 return (rc); 1953 } 1954 1955 static int 1956 loadboot(int argc, const char *argv[]) 1957 { 1958 int rc, fd; 1959 long l; 1960 char *p; 1961 struct t4_bootrom br = {0}; 1962 const char *fname = argv[0]; 1963 struct stat st = {0}; 1964 1965 if (argc == 1) { 1966 br.pf_offset = 0; 1967 br.pfidx_addr = 0; 1968 } else if (argc == 3) { 1969 if (!strcmp(argv[1], "pf")) 1970 br.pf_offset = 0; 1971 else if (!strcmp(argv[1], "offset")) 1972 br.pf_offset = 1; 1973 else 1974 return (EINVAL); 1975 1976 p = str_to_number(argv[2], &l, NULL); 1977 if (*p) 1978 return (EINVAL); 1979 br.pfidx_addr = l; 1980 } else { 1981 warnx("loadboot: incorrect number of arguments."); 1982 return (EINVAL); 1983 } 1984 1985 if (strcmp(fname, "clear") == 0) 1986 return (doit(CHELSIO_T4_LOAD_BOOT, &br)); 1987 1988 fd = open(fname, O_RDONLY); 1989 if (fd < 0) { 1990 warn("open(%s)", fname); 1991 return (errno); 1992 } 1993 1994 if (fstat(fd, &st) < 0) { 1995 warn("fstat"); 1996 close(fd); 1997 return (errno); 1998 } 1999 2000 br.len = st.st_size; 2001 br.data = mmap(0, br.len, PROT_READ, MAP_PRIVATE, fd, 0); 2002 if (br.data == MAP_FAILED) { 2003 warn("mmap"); 2004 close(fd); 2005 return (errno); 2006 } 2007 2008 rc = doit(CHELSIO_T4_LOAD_BOOT, &br); 2009 munmap(br.data, br.len); 2010 close(fd); 2011 return (rc); 2012 } 2013 2014 static int 2015 loadbootcfg(int argc, const char *argv[]) 2016 { 2017 int rc, fd; 2018 struct t4_data bc = {0}; 2019 const char *fname = argv[0]; 2020 struct stat st = {0}; 2021 2022 if (argc != 1) { 2023 warnx("loadbootcfg: incorrect number of arguments."); 2024 return (EINVAL); 2025 } 2026 2027 if (strcmp(fname, "clear") == 0) 2028 return (doit(CHELSIO_T4_LOAD_BOOTCFG, &bc)); 2029 2030 fd = open(fname, O_RDONLY); 2031 if (fd < 0) { 2032 warn("open(%s)", fname); 2033 return (errno); 2034 } 2035 2036 if (fstat(fd, &st) < 0) { 2037 warn("fstat"); 2038 close(fd); 2039 return (errno); 2040 } 2041 2042 bc.len = st.st_size; 2043 bc.data = mmap(0, bc.len, PROT_READ, MAP_PRIVATE, fd, 0); 2044 if (bc.data == MAP_FAILED) { 2045 warn("mmap"); 2046 close(fd); 2047 return (errno); 2048 } 2049 2050 rc = doit(CHELSIO_T4_LOAD_BOOTCFG, &bc); 2051 munmap(bc.data, bc.len); 2052 close(fd); 2053 return (rc); 2054 } 2055 2056 /* 2057 * Display memory as list of 'n' 4-byte values per line. 2058 */ 2059 static void 2060 show_mem(uint32_t *buf, uint32_t len) 2061 { 2062 const char *s; 2063 int i, n = 8; 2064 2065 while (len) { 2066 for (i = 0; len && i < n; i++, buf++, len -= 4) { 2067 s = i ? " " : ""; 2068 printf("%s%08x", s, htonl(*buf)); 2069 } 2070 printf("\n"); 2071 } 2072 } 2073 2074 static int 2075 memdump(int argc, const char *argv[]) 2076 { 2077 char *p; 2078 long l; 2079 uint32_t addr, len; 2080 2081 if (argc != 2) { 2082 warnx("incorrect number of arguments."); 2083 return (EINVAL); 2084 } 2085 2086 p = str_to_number(argv[0], &l, NULL); 2087 if (*p) { 2088 warnx("invalid address \"%s\"", argv[0]); 2089 return (EINVAL); 2090 } 2091 addr = l; 2092 2093 p = str_to_number(argv[1], &l, NULL); 2094 if (*p) { 2095 warnx("memdump: invalid length \"%s\"", argv[1]); 2096 return (EINVAL); 2097 } 2098 len = l; 2099 2100 return (read_mem(addr, len, show_mem)); 2101 } 2102 2103 /* 2104 * Display TCB as list of 'n' 4-byte values per line. 2105 */ 2106 static void 2107 show_tcb(uint32_t *buf, uint32_t len) 2108 { 2109 unsigned char *tcb = (unsigned char *)buf; 2110 const char *s; 2111 int i, n = 8; 2112 2113 while (len) { 2114 for (i = 0; len && i < n; i++, buf++, len -= 4) { 2115 s = i ? " " : ""; 2116 printf("%s%08x", s, htonl(*buf)); 2117 } 2118 printf("\n"); 2119 } 2120 set_tcb_info(TIDTYPE_TCB, chip_id); 2121 set_print_style(PRNTSTYL_COMP); 2122 swizzle_tcb(tcb); 2123 parse_n_display_xcb(tcb); 2124 } 2125 2126 #define A_TP_CMM_TCB_BASE 0x7d10 2127 #define TCB_SIZE 128 2128 static int 2129 read_tcb(int argc, const char *argv[]) 2130 { 2131 char *p; 2132 long l; 2133 long long val; 2134 unsigned int tid; 2135 uint32_t addr; 2136 int rc; 2137 2138 if (argc != 1) { 2139 warnx("incorrect number of arguments."); 2140 return (EINVAL); 2141 } 2142 2143 p = str_to_number(argv[0], &l, NULL); 2144 if (*p) { 2145 warnx("invalid tid \"%s\"", argv[0]); 2146 return (EINVAL); 2147 } 2148 tid = l; 2149 2150 rc = read_reg(A_TP_CMM_TCB_BASE, 4, &val); 2151 if (rc != 0) 2152 return (rc); 2153 2154 addr = val + tid * TCB_SIZE; 2155 2156 return (read_mem(addr, TCB_SIZE, show_tcb)); 2157 } 2158 2159 static int 2160 read_i2c(int argc, const char *argv[]) 2161 { 2162 char *p; 2163 long l; 2164 struct t4_i2c_data i2cd; 2165 int rc, i; 2166 2167 if (argc < 3 || argc > 4) { 2168 warnx("incorrect number of arguments."); 2169 return (EINVAL); 2170 } 2171 2172 p = str_to_number(argv[0], &l, NULL); 2173 if (*p || l > UCHAR_MAX) { 2174 warnx("invalid port id \"%s\"", argv[0]); 2175 return (EINVAL); 2176 } 2177 i2cd.port_id = l; 2178 2179 p = str_to_number(argv[1], &l, NULL); 2180 if (*p || l > UCHAR_MAX) { 2181 warnx("invalid i2c device address \"%s\"", argv[1]); 2182 return (EINVAL); 2183 } 2184 i2cd.dev_addr = l; 2185 2186 p = str_to_number(argv[2], &l, NULL); 2187 if (*p || l > UCHAR_MAX) { 2188 warnx("invalid byte offset \"%s\"", argv[2]); 2189 return (EINVAL); 2190 } 2191 i2cd.offset = l; 2192 2193 if (argc == 4) { 2194 p = str_to_number(argv[3], &l, NULL); 2195 if (*p || l > sizeof(i2cd.data)) { 2196 warnx("invalid number of bytes \"%s\"", argv[3]); 2197 return (EINVAL); 2198 } 2199 i2cd.len = l; 2200 } else 2201 i2cd.len = 1; 2202 2203 rc = doit(CHELSIO_T4_GET_I2C, &i2cd); 2204 if (rc != 0) 2205 return (rc); 2206 2207 for (i = 0; i < i2cd.len; i++) 2208 printf("0x%x [%u]\n", i2cd.data[i], i2cd.data[i]); 2209 2210 return (0); 2211 } 2212 2213 static int 2214 clearstats(int argc, const char *argv[]) 2215 { 2216 char *p; 2217 long l; 2218 uint32_t port; 2219 2220 if (argc != 1) { 2221 warnx("incorrect number of arguments."); 2222 return (EINVAL); 2223 } 2224 2225 p = str_to_number(argv[0], &l, NULL); 2226 if (*p) { 2227 warnx("invalid port id \"%s\"", argv[0]); 2228 return (EINVAL); 2229 } 2230 port = l; 2231 2232 return doit(CHELSIO_T4_CLEAR_STATS, &port); 2233 } 2234 2235 static int 2236 show_tracers(void) 2237 { 2238 struct t4_tracer t; 2239 char *s; 2240 int rc, port_idx, i; 2241 long long val; 2242 2243 /* Magic values: MPS_TRC_CFG = 0x9800. MPS_TRC_CFG[1:1] = TrcEn */ 2244 rc = read_reg(0x9800, 4, &val); 2245 if (rc != 0) 2246 return (rc); 2247 printf("tracing is %s\n", val & 2 ? "ENABLED" : "DISABLED"); 2248 2249 t.idx = 0; 2250 for (t.idx = 0; ; t.idx++) { 2251 rc = doit(CHELSIO_T4_GET_TRACER, &t); 2252 if (rc != 0 || t.idx == 0xff) 2253 break; 2254 2255 if (t.tp.port < 4) { 2256 s = "Rx"; 2257 port_idx = t.tp.port; 2258 } else if (t.tp.port < 8) { 2259 s = "Tx"; 2260 port_idx = t.tp.port - 4; 2261 } else if (t.tp.port < 12) { 2262 s = "loopback"; 2263 port_idx = t.tp.port - 8; 2264 } else if (t.tp.port < 16) { 2265 s = "MPS Rx"; 2266 port_idx = t.tp.port - 12; 2267 } else if (t.tp.port < 20) { 2268 s = "MPS Tx"; 2269 port_idx = t.tp.port - 16; 2270 } else { 2271 s = "unknown"; 2272 port_idx = t.tp.port; 2273 } 2274 2275 printf("\ntracer %u (currently %s) captures ", t.idx, 2276 t.enabled ? "ENABLED" : "DISABLED"); 2277 if (t.tp.port < 8) 2278 printf("port %u %s, ", port_idx, s); 2279 else 2280 printf("%s %u, ", s, port_idx); 2281 printf("snap length: %u, min length: %u\n", t.tp.snap_len, 2282 t.tp.min_len); 2283 printf("packets captured %smatch filter\n", 2284 t.tp.invert ? "do not " : ""); 2285 if (t.tp.skip_ofst) { 2286 printf("filter pattern: "); 2287 for (i = 0; i < t.tp.skip_ofst * 2; i += 2) 2288 printf("%08x%08x", t.tp.data[i], 2289 t.tp.data[i + 1]); 2290 printf("/"); 2291 for (i = 0; i < t.tp.skip_ofst * 2; i += 2) 2292 printf("%08x%08x", t.tp.mask[i], 2293 t.tp.mask[i + 1]); 2294 printf("@0\n"); 2295 } 2296 printf("filter pattern: "); 2297 for (i = t.tp.skip_ofst * 2; i < T4_TRACE_LEN / 4; i += 2) 2298 printf("%08x%08x", t.tp.data[i], t.tp.data[i + 1]); 2299 printf("/"); 2300 for (i = t.tp.skip_ofst * 2; i < T4_TRACE_LEN / 4; i += 2) 2301 printf("%08x%08x", t.tp.mask[i], t.tp.mask[i + 1]); 2302 printf("@%u\n", (t.tp.skip_ofst + t.tp.skip_len) * 8); 2303 } 2304 2305 return (rc); 2306 } 2307 2308 static int 2309 tracer_onoff(uint8_t idx, int enabled) 2310 { 2311 struct t4_tracer t; 2312 2313 t.idx = idx; 2314 t.enabled = enabled; 2315 t.valid = 0; 2316 2317 return doit(CHELSIO_T4_SET_TRACER, &t); 2318 } 2319 2320 static void 2321 create_tracing_ifnet() 2322 { 2323 char *cmd[] = { 2324 "/sbin/ifconfig", __DECONST(char *, nexus), "create", NULL 2325 }; 2326 char *env[] = {NULL}; 2327 2328 if (vfork() == 0) { 2329 close(STDERR_FILENO); 2330 execve(cmd[0], cmd, env); 2331 _exit(0); 2332 } 2333 } 2334 2335 /* 2336 * XXX: Allow user to specify snaplen, minlen, and pattern (including inverted 2337 * matching). Right now this is a quick-n-dirty implementation that traces the 2338 * first 128B of all tx or rx on a port 2339 */ 2340 static int 2341 set_tracer(uint8_t idx, int argc, const char *argv[]) 2342 { 2343 struct t4_tracer t; 2344 int len, port; 2345 2346 bzero(&t, sizeof (t)); 2347 t.idx = idx; 2348 t.enabled = 1; 2349 t.valid = 1; 2350 2351 if (argc != 1) { 2352 warnx("must specify tx<n> or rx<n>."); 2353 return (EINVAL); 2354 } 2355 2356 len = strlen(argv[0]); 2357 if (len != 3) { 2358 warnx("argument must be 3 characters (tx<n> or rx<n>)"); 2359 return (EINVAL); 2360 } 2361 2362 if (strncmp(argv[0], "tx", 2) == 0) { 2363 port = argv[0][2] - '0'; 2364 if (port < 0 || port > 3) { 2365 warnx("'%c' in %s is invalid", argv[0][2], argv[0]); 2366 return (EINVAL); 2367 } 2368 port += 4; 2369 } else if (strncmp(argv[0], "rx", 2) == 0) { 2370 port = argv[0][2] - '0'; 2371 if (port < 0 || port > 3) { 2372 warnx("'%c' in %s is invalid", argv[0][2], argv[0]); 2373 return (EINVAL); 2374 } 2375 } else { 2376 warnx("argument '%s' isn't tx<n> or rx<n>", argv[0]); 2377 return (EINVAL); 2378 } 2379 2380 t.tp.snap_len = 128; 2381 t.tp.min_len = 0; 2382 t.tp.skip_ofst = 0; 2383 t.tp.skip_len = 0; 2384 t.tp.invert = 0; 2385 t.tp.port = port; 2386 2387 create_tracing_ifnet(); 2388 return doit(CHELSIO_T4_SET_TRACER, &t); 2389 } 2390 2391 static int 2392 tracer_cmd(int argc, const char *argv[]) 2393 { 2394 long long val; 2395 uint8_t idx; 2396 char *s; 2397 2398 if (argc == 0) { 2399 warnx("tracer: no arguments."); 2400 return (EINVAL); 2401 }; 2402 2403 /* list */ 2404 if (strcmp(argv[0], "list") == 0) { 2405 if (argc != 1) 2406 warnx("trailing arguments after \"list\" ignored."); 2407 2408 return show_tracers(); 2409 } 2410 2411 /* <idx> ... */ 2412 s = str_to_number(argv[0], NULL, &val); 2413 if (*s || val > 0xff) { 2414 warnx("\"%s\" is neither an index nor a tracer subcommand.", 2415 argv[0]); 2416 return (EINVAL); 2417 } 2418 idx = (int8_t)val; 2419 2420 /* <idx> disable */ 2421 if (argc == 2 && strcmp(argv[1], "disable") == 0) 2422 return tracer_onoff(idx, 0); 2423 2424 /* <idx> enable */ 2425 if (argc == 2 && strcmp(argv[1], "enable") == 0) 2426 return tracer_onoff(idx, 1); 2427 2428 /* <idx> ... */ 2429 return set_tracer(idx, argc - 1, argv + 1); 2430 } 2431 2432 static int 2433 modinfo_raw(int port_id) 2434 { 2435 uint8_t offset; 2436 struct t4_i2c_data i2cd; 2437 int rc; 2438 2439 for (offset = 0; offset < 96; offset += sizeof(i2cd.data)) { 2440 bzero(&i2cd, sizeof(i2cd)); 2441 i2cd.port_id = port_id; 2442 i2cd.dev_addr = 0xa0; 2443 i2cd.offset = offset; 2444 i2cd.len = sizeof(i2cd.data); 2445 rc = doit(CHELSIO_T4_GET_I2C, &i2cd); 2446 if (rc != 0) 2447 return (rc); 2448 printf("%02x: %02x %02x %02x %02x %02x %02x %02x %02x", 2449 offset, i2cd.data[0], i2cd.data[1], i2cd.data[2], 2450 i2cd.data[3], i2cd.data[4], i2cd.data[5], i2cd.data[6], 2451 i2cd.data[7]); 2452 2453 printf(" %c%c%c%c %c%c%c%c\n", 2454 isprint(i2cd.data[0]) ? i2cd.data[0] : '.', 2455 isprint(i2cd.data[1]) ? i2cd.data[1] : '.', 2456 isprint(i2cd.data[2]) ? i2cd.data[2] : '.', 2457 isprint(i2cd.data[3]) ? i2cd.data[3] : '.', 2458 isprint(i2cd.data[4]) ? i2cd.data[4] : '.', 2459 isprint(i2cd.data[5]) ? i2cd.data[5] : '.', 2460 isprint(i2cd.data[6]) ? i2cd.data[6] : '.', 2461 isprint(i2cd.data[7]) ? i2cd.data[7] : '.'); 2462 } 2463 2464 return (0); 2465 } 2466 2467 static int 2468 modinfo(int argc, const char *argv[]) 2469 { 2470 long port; 2471 char string[16], *p; 2472 struct t4_i2c_data i2cd; 2473 int rc, i; 2474 uint16_t temp, vcc, tx_bias, tx_power, rx_power; 2475 2476 if (argc < 1) { 2477 warnx("must supply a port"); 2478 return (EINVAL); 2479 } 2480 2481 if (argc > 2) { 2482 warnx("too many arguments"); 2483 return (EINVAL); 2484 } 2485 2486 p = str_to_number(argv[0], &port, NULL); 2487 if (*p || port > UCHAR_MAX) { 2488 warnx("invalid port id \"%s\"", argv[0]); 2489 return (EINVAL); 2490 } 2491 2492 if (argc == 2) { 2493 if (!strcmp(argv[1], "raw")) 2494 return (modinfo_raw(port)); 2495 else { 2496 warnx("second argument can only be \"raw\""); 2497 return (EINVAL); 2498 } 2499 } 2500 2501 bzero(&i2cd, sizeof(i2cd)); 2502 i2cd.len = 1; 2503 i2cd.port_id = port; 2504 i2cd.dev_addr = SFF_8472_BASE; 2505 2506 i2cd.offset = SFF_8472_ID; 2507 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2508 goto fail; 2509 2510 if (i2cd.data[0] > SFF_8472_ID_LAST) 2511 printf("Unknown ID\n"); 2512 else 2513 printf("ID: %s\n", sff_8472_id[i2cd.data[0]]); 2514 2515 bzero(&string, sizeof(string)); 2516 for (i = SFF_8472_VENDOR_START; i < SFF_8472_VENDOR_END; i++) { 2517 i2cd.offset = i; 2518 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2519 goto fail; 2520 string[i - SFF_8472_VENDOR_START] = i2cd.data[0]; 2521 } 2522 printf("Vendor %s\n", string); 2523 2524 bzero(&string, sizeof(string)); 2525 for (i = SFF_8472_SN_START; i < SFF_8472_SN_END; i++) { 2526 i2cd.offset = i; 2527 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2528 goto fail; 2529 string[i - SFF_8472_SN_START] = i2cd.data[0]; 2530 } 2531 printf("SN %s\n", string); 2532 2533 bzero(&string, sizeof(string)); 2534 for (i = SFF_8472_PN_START; i < SFF_8472_PN_END; i++) { 2535 i2cd.offset = i; 2536 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2537 goto fail; 2538 string[i - SFF_8472_PN_START] = i2cd.data[0]; 2539 } 2540 printf("PN %s\n", string); 2541 2542 bzero(&string, sizeof(string)); 2543 for (i = SFF_8472_REV_START; i < SFF_8472_REV_END; i++) { 2544 i2cd.offset = i; 2545 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2546 goto fail; 2547 string[i - SFF_8472_REV_START] = i2cd.data[0]; 2548 } 2549 printf("Rev %s\n", string); 2550 2551 i2cd.offset = SFF_8472_DIAG_TYPE; 2552 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2553 goto fail; 2554 2555 if ((char )i2cd.data[0] & (SFF_8472_DIAG_IMPL | 2556 SFF_8472_DIAG_INTERNAL)) { 2557 2558 /* Switch to reading from the Diagnostic address. */ 2559 i2cd.dev_addr = SFF_8472_DIAG; 2560 i2cd.len = 1; 2561 2562 i2cd.offset = SFF_8472_TEMP; 2563 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2564 goto fail; 2565 temp = i2cd.data[0] << 8; 2566 printf("Temp: "); 2567 if ((temp & SFF_8472_TEMP_SIGN) == SFF_8472_TEMP_SIGN) 2568 printf("-"); 2569 else 2570 printf("+"); 2571 printf("%dC\n", (temp & SFF_8472_TEMP_MSK) >> 2572 SFF_8472_TEMP_SHIFT); 2573 2574 i2cd.offset = SFF_8472_VCC; 2575 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2576 goto fail; 2577 vcc = i2cd.data[0] << 8; 2578 printf("Vcc %fV\n", vcc / SFF_8472_VCC_FACTOR); 2579 2580 i2cd.offset = SFF_8472_TX_BIAS; 2581 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2582 goto fail; 2583 tx_bias = i2cd.data[0] << 8; 2584 printf("TX Bias %fuA\n", tx_bias / SFF_8472_BIAS_FACTOR); 2585 2586 i2cd.offset = SFF_8472_TX_POWER; 2587 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2588 goto fail; 2589 tx_power = i2cd.data[0] << 8; 2590 printf("TX Power %fmW\n", tx_power / SFF_8472_POWER_FACTOR); 2591 2592 i2cd.offset = SFF_8472_RX_POWER; 2593 if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0) 2594 goto fail; 2595 rx_power = i2cd.data[0] << 8; 2596 printf("RX Power %fmW\n", rx_power / SFF_8472_POWER_FACTOR); 2597 2598 } else 2599 printf("Diagnostics not supported.\n"); 2600 2601 return(0); 2602 2603 fail: 2604 if (rc == EPERM) 2605 warnx("No module/cable in port %ld", port); 2606 return (rc); 2607 2608 } 2609 2610 /* XXX: pass in a low/high and do range checks as well */ 2611 static int 2612 get_sched_param(const char *param, const char *args[], long *val) 2613 { 2614 char *p; 2615 2616 if (strcmp(param, args[0]) != 0) 2617 return (EINVAL); 2618 2619 p = str_to_number(args[1], val, NULL); 2620 if (*p) { 2621 warnx("parameter \"%s\" has bad value \"%s\"", args[0], 2622 args[1]); 2623 return (EINVAL); 2624 } 2625 2626 return (0); 2627 } 2628 2629 static int 2630 sched_class(int argc, const char *argv[]) 2631 { 2632 struct t4_sched_params op; 2633 int errs, i; 2634 2635 memset(&op, 0xff, sizeof(op)); 2636 op.subcmd = -1; 2637 op.type = -1; 2638 if (argc == 0) { 2639 warnx("missing scheduling sub-command"); 2640 return (EINVAL); 2641 } 2642 if (!strcmp(argv[0], "config")) { 2643 op.subcmd = SCHED_CLASS_SUBCMD_CONFIG; 2644 op.u.config.minmax = -1; 2645 } else if (!strcmp(argv[0], "params")) { 2646 op.subcmd = SCHED_CLASS_SUBCMD_PARAMS; 2647 op.u.params.level = op.u.params.mode = op.u.params.rateunit = 2648 op.u.params.ratemode = op.u.params.channel = 2649 op.u.params.cl = op.u.params.minrate = op.u.params.maxrate = 2650 op.u.params.weight = op.u.params.pktsize = -1; 2651 } else { 2652 warnx("invalid scheduling sub-command \"%s\"", argv[0]); 2653 return (EINVAL); 2654 } 2655 2656 /* Decode remaining arguments ... */ 2657 errs = 0; 2658 for (i = 1; i < argc; i += 2) { 2659 const char **args = &argv[i]; 2660 long l; 2661 2662 if (i + 1 == argc) { 2663 warnx("missing argument for \"%s\"", args[0]); 2664 errs++; 2665 break; 2666 } 2667 2668 if (!strcmp(args[0], "type")) { 2669 if (!strcmp(args[1], "packet")) 2670 op.type = SCHED_CLASS_TYPE_PACKET; 2671 else { 2672 warnx("invalid type parameter \"%s\"", args[1]); 2673 errs++; 2674 } 2675 2676 continue; 2677 } 2678 2679 if (op.subcmd == SCHED_CLASS_SUBCMD_CONFIG) { 2680 if(!get_sched_param("minmax", args, &l)) 2681 op.u.config.minmax = (int8_t)l; 2682 else { 2683 warnx("unknown scheduler config parameter " 2684 "\"%s\"", args[0]); 2685 errs++; 2686 } 2687 2688 continue; 2689 } 2690 2691 /* Rest applies only to SUBCMD_PARAMS */ 2692 if (op.subcmd != SCHED_CLASS_SUBCMD_PARAMS) 2693 continue; 2694 2695 if (!strcmp(args[0], "level")) { 2696 if (!strcmp(args[1], "cl-rl")) 2697 op.u.params.level = SCHED_CLASS_LEVEL_CL_RL; 2698 else if (!strcmp(args[1], "cl-wrr")) 2699 op.u.params.level = SCHED_CLASS_LEVEL_CL_WRR; 2700 else if (!strcmp(args[1], "ch-rl")) 2701 op.u.params.level = SCHED_CLASS_LEVEL_CH_RL; 2702 else { 2703 warnx("invalid level parameter \"%s\"", 2704 args[1]); 2705 errs++; 2706 } 2707 } else if (!strcmp(args[0], "mode")) { 2708 if (!strcmp(args[1], "class")) 2709 op.u.params.mode = SCHED_CLASS_MODE_CLASS; 2710 else if (!strcmp(args[1], "flow")) 2711 op.u.params.mode = SCHED_CLASS_MODE_FLOW; 2712 else { 2713 warnx("invalid mode parameter \"%s\"", args[1]); 2714 errs++; 2715 } 2716 } else if (!strcmp(args[0], "rate-unit")) { 2717 if (!strcmp(args[1], "bits")) 2718 op.u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS; 2719 else if (!strcmp(args[1], "pkts")) 2720 op.u.params.rateunit = SCHED_CLASS_RATEUNIT_PKTS; 2721 else { 2722 warnx("invalid rate-unit parameter \"%s\"", 2723 args[1]); 2724 errs++; 2725 } 2726 } else if (!strcmp(args[0], "rate-mode")) { 2727 if (!strcmp(args[1], "relative")) 2728 op.u.params.ratemode = SCHED_CLASS_RATEMODE_REL; 2729 else if (!strcmp(args[1], "absolute")) 2730 op.u.params.ratemode = SCHED_CLASS_RATEMODE_ABS; 2731 else { 2732 warnx("invalid rate-mode parameter \"%s\"", 2733 args[1]); 2734 errs++; 2735 } 2736 } else if (!get_sched_param("channel", args, &l)) 2737 op.u.params.channel = (int8_t)l; 2738 else if (!get_sched_param("class", args, &l)) 2739 op.u.params.cl = (int8_t)l; 2740 else if (!get_sched_param("min-rate", args, &l)) 2741 op.u.params.minrate = (int32_t)l; 2742 else if (!get_sched_param("max-rate", args, &l)) 2743 op.u.params.maxrate = (int32_t)l; 2744 else if (!get_sched_param("weight", args, &l)) 2745 op.u.params.weight = (int16_t)l; 2746 else if (!get_sched_param("pkt-size", args, &l)) 2747 op.u.params.pktsize = (int16_t)l; 2748 else { 2749 warnx("unknown scheduler parameter \"%s\"", args[0]); 2750 errs++; 2751 } 2752 } 2753 2754 /* 2755 * Catch some logical fallacies in terms of argument combinations here 2756 * so we can offer more than just the EINVAL return from the driver. 2757 * The driver will be able to catch a lot more issues since it knows 2758 * the specifics of the device hardware capabilities like how many 2759 * channels, classes, etc. the device supports. 2760 */ 2761 if (op.type < 0) { 2762 warnx("sched \"type\" parameter missing"); 2763 errs++; 2764 } 2765 if (op.subcmd == SCHED_CLASS_SUBCMD_CONFIG) { 2766 if (op.u.config.minmax < 0) { 2767 warnx("sched config \"minmax\" parameter missing"); 2768 errs++; 2769 } 2770 } 2771 if (op.subcmd == SCHED_CLASS_SUBCMD_PARAMS) { 2772 if (op.u.params.level < 0) { 2773 warnx("sched params \"level\" parameter missing"); 2774 errs++; 2775 } 2776 if (op.u.params.mode < 0) { 2777 warnx("sched params \"mode\" parameter missing"); 2778 errs++; 2779 } 2780 if (op.u.params.rateunit < 0) { 2781 warnx("sched params \"rate-unit\" parameter missing"); 2782 errs++; 2783 } 2784 if (op.u.params.ratemode < 0) { 2785 warnx("sched params \"rate-mode\" parameter missing"); 2786 errs++; 2787 } 2788 if (op.u.params.channel < 0) { 2789 warnx("sched params \"channel\" missing"); 2790 errs++; 2791 } 2792 if (op.u.params.cl < 0) { 2793 warnx("sched params \"class\" missing"); 2794 errs++; 2795 } 2796 if (op.u.params.maxrate < 0 && 2797 (op.u.params.level == SCHED_CLASS_LEVEL_CL_RL || 2798 op.u.params.level == SCHED_CLASS_LEVEL_CH_RL)) { 2799 warnx("sched params \"max-rate\" missing for " 2800 "rate-limit level"); 2801 errs++; 2802 } 2803 if (op.u.params.weight < 0 && 2804 op.u.params.level == SCHED_CLASS_LEVEL_CL_WRR) { 2805 warnx("sched params \"weight\" missing for " 2806 "weighted-round-robin level"); 2807 errs++; 2808 } 2809 if (op.u.params.pktsize < 0 && 2810 (op.u.params.level == SCHED_CLASS_LEVEL_CL_RL || 2811 op.u.params.level == SCHED_CLASS_LEVEL_CH_RL)) { 2812 warnx("sched params \"pkt-size\" missing for " 2813 "rate-limit level"); 2814 errs++; 2815 } 2816 if (op.u.params.mode == SCHED_CLASS_MODE_FLOW && 2817 op.u.params.ratemode != SCHED_CLASS_RATEMODE_ABS) { 2818 warnx("sched params mode flow needs rate-mode absolute"); 2819 errs++; 2820 } 2821 if (op.u.params.ratemode == SCHED_CLASS_RATEMODE_REL && 2822 !in_range(op.u.params.maxrate, 1, 100)) { 2823 warnx("sched params \"max-rate\" takes " 2824 "percentage value(1-100) for rate-mode relative"); 2825 errs++; 2826 } 2827 if (op.u.params.ratemode == SCHED_CLASS_RATEMODE_ABS && 2828 !in_range(op.u.params.maxrate, 1, 100000000)) { 2829 warnx("sched params \"max-rate\" takes " 2830 "value(1-100000000) for rate-mode absolute"); 2831 errs++; 2832 } 2833 if (op.u.params.maxrate > 0 && 2834 op.u.params.maxrate < op.u.params.minrate) { 2835 warnx("sched params \"max-rate\" is less than " 2836 "\"min-rate\""); 2837 errs++; 2838 } 2839 } 2840 2841 if (errs > 0) { 2842 warnx("%d error%s in sched-class command", errs, 2843 errs == 1 ? "" : "s"); 2844 return (EINVAL); 2845 } 2846 2847 return doit(CHELSIO_T4_SCHED_CLASS, &op); 2848 } 2849 2850 static int 2851 sched_queue(int argc, const char *argv[]) 2852 { 2853 struct t4_sched_queue op = {0}; 2854 char *p; 2855 long val; 2856 2857 if (argc != 3) { 2858 /* need "<port> <queue> <class> */ 2859 warnx("incorrect number of arguments."); 2860 return (EINVAL); 2861 } 2862 2863 p = str_to_number(argv[0], &val, NULL); 2864 if (*p || val > UCHAR_MAX) { 2865 warnx("invalid port id \"%s\"", argv[0]); 2866 return (EINVAL); 2867 } 2868 op.port = (uint8_t)val; 2869 2870 if (!strcmp(argv[1], "all") || !strcmp(argv[1], "*")) 2871 op.queue = -1; 2872 else { 2873 p = str_to_number(argv[1], &val, NULL); 2874 if (*p || val < -1) { 2875 warnx("invalid queue \"%s\"", argv[1]); 2876 return (EINVAL); 2877 } 2878 op.queue = (int8_t)val; 2879 } 2880 2881 if (!strcmp(argv[2], "unbind") || !strcmp(argv[2], "clear")) 2882 op.cl = -1; 2883 else { 2884 p = str_to_number(argv[2], &val, NULL); 2885 if (*p || val < -1) { 2886 warnx("invalid class \"%s\"", argv[2]); 2887 return (EINVAL); 2888 } 2889 op.cl = (int8_t)val; 2890 } 2891 2892 return doit(CHELSIO_T4_SCHED_QUEUE, &op); 2893 } 2894 2895 static int 2896 parse_offload_settings_word(const char *s, char **pnext, const char *ws, 2897 int *pneg, struct offload_settings *os) 2898 { 2899 2900 while (*s == '!') { 2901 (*pneg)++; 2902 s++; 2903 } 2904 2905 if (!strcmp(s, "not")) { 2906 (*pneg)++; 2907 return (0); 2908 } 2909 2910 if (!strcmp(s, "offload")) { 2911 os->offload = (*pneg + 1) & 1; 2912 *pneg = 0; 2913 } else if (!strcmp(s , "coalesce")) { 2914 os->rx_coalesce = (*pneg + 1) & 1; 2915 *pneg = 0; 2916 } else if (!strcmp(s, "timestamp") || !strcmp(s, "tstamp")) { 2917 os->tstamp = (*pneg + 1) & 1; 2918 *pneg = 0; 2919 } else if (!strcmp(s, "sack")) { 2920 os->sack = (*pneg + 1) & 1; 2921 *pneg = 0; 2922 } else if (!strcmp(s, "nagle")) { 2923 os->nagle = (*pneg + 1) & 1; 2924 *pneg = 0; 2925 } else if (!strcmp(s, "ecn")) { 2926 os->ecn = (*pneg + 1) & 1; 2927 *pneg = 0; 2928 } else if (!strcmp(s, "ddp")) { 2929 os->ddp = (*pneg + 1) & 1; 2930 *pneg = 0; 2931 } else if (!strcmp(s, "tls")) { 2932 os->tls = (*pneg + 1) & 1; 2933 *pneg = 0; 2934 } else { 2935 char *param, *p; 2936 long val; 2937 2938 /* Settings with additional parameter handled here. */ 2939 2940 if (*pneg) { 2941 warnx("\"%s\" is not a valid keyword, or it does not " 2942 "support negation.", s); 2943 return (EINVAL); 2944 } 2945 2946 while ((param = strsep(pnext, ws)) != NULL) { 2947 if (*param != '\0') 2948 break; 2949 } 2950 if (param == NULL) { 2951 warnx("\"%s\" is not a valid keyword, or it requires a " 2952 "parameter that has not been provided.", s); 2953 return (EINVAL); 2954 } 2955 2956 if (!strcmp(s, "cong")) { 2957 if (!strcmp(param, "reno")) 2958 os->cong_algo = 0; 2959 else if (!strcmp(param, "tahoe")) 2960 os->cong_algo = 1; 2961 else if (!strcmp(param, "newreno")) 2962 os->cong_algo = 2; 2963 else if (!strcmp(param, "highspeed")) 2964 os->cong_algo = 3; 2965 else { 2966 warnx("unknown congestion algorithm \"%s\".", s); 2967 return (EINVAL); 2968 } 2969 } else if (!strcmp(s, "class")) { 2970 val = -1; 2971 p = str_to_number(param, &val, NULL); 2972 /* (nsched_cls - 1) is spelled 15 here. */ 2973 if (*p || val < 0 || val > 15) { 2974 warnx("invalid scheduling class \"%s\". " 2975 "\"class\" needs an integer value where " 2976 "0 <= value <= 15", param); 2977 return (EINVAL); 2978 } 2979 os->sched_class = val; 2980 } else if (!strcmp(s, "bind") || !strcmp(s, "txq") || 2981 !strcmp(s, "rxq")) { 2982 val = -1; 2983 if (strcmp(param, "random")) { 2984 p = str_to_number(param, &val, NULL); 2985 if (*p || val < 0 || val > 0xffff) { 2986 warnx("invalid queue specification " 2987 "\"%s\". \"%s\" needs an integer" 2988 " value, or \"random\".", 2989 param, s); 2990 return (EINVAL); 2991 } 2992 } 2993 if (!strcmp(s, "bind")) { 2994 os->txq = val; 2995 os->rxq = val; 2996 } else if (!strcmp(s, "txq")) { 2997 os->txq = val; 2998 } else if (!strcmp(s, "rxq")) { 2999 os->rxq = val; 3000 } else { 3001 return (EDOOFUS); 3002 } 3003 } else if (!strcmp(s, "mss")) { 3004 val = -1; 3005 p = str_to_number(param, &val, NULL); 3006 if (*p || val <= 0) { 3007 warnx("invalid MSS specification \"%s\". " 3008 "\"mss\" needs a positive integer value", 3009 param); 3010 return (EINVAL); 3011 } 3012 os->mss = val; 3013 } else { 3014 warnx("unknown settings keyword: \"%s\"", s); 3015 return (EINVAL); 3016 } 3017 } 3018 3019 return (0); 3020 } 3021 3022 static int 3023 parse_offload_settings(const char *settings_ro, struct offload_settings *os) 3024 { 3025 const char *ws = " \f\n\r\v\t"; 3026 char *settings, *s, *next; 3027 int rc, nsettings, neg; 3028 static const struct offload_settings default_settings = { 3029 .offload = 0, /* No settings imply !offload */ 3030 .rx_coalesce = -1, 3031 .cong_algo = -1, 3032 .sched_class = -1, 3033 .tstamp = -1, 3034 .sack = -1, 3035 .nagle = -1, 3036 .ecn = -1, 3037 .ddp = -1, 3038 .tls = -1, 3039 .txq = -1, 3040 .rxq = -1, 3041 .mss = -1, 3042 }; 3043 3044 *os = default_settings; 3045 3046 next = settings = strdup(settings_ro); 3047 if (settings == NULL) { 3048 warn (NULL); 3049 return (errno); 3050 } 3051 3052 nsettings = 0; 3053 rc = 0; 3054 neg = 0; 3055 while ((s = strsep(&next, ws)) != NULL) { 3056 if (*s == '\0') 3057 continue; 3058 nsettings++; 3059 rc = parse_offload_settings_word(s, &next, ws, &neg, os); 3060 if (rc != 0) 3061 goto done; 3062 } 3063 if (nsettings == 0) { 3064 warnx("no settings provided"); 3065 rc = EINVAL; 3066 goto done; 3067 } 3068 if (neg > 0) { 3069 warnx("%d stray negation(s) at end of offload settings", neg); 3070 rc = EINVAL; 3071 goto done; 3072 } 3073 done: 3074 free(settings); 3075 return (rc); 3076 } 3077 3078 static int 3079 isempty_line(char *line, size_t llen) 3080 { 3081 3082 /* skip leading whitespace */ 3083 while (isspace(*line)) { 3084 line++; 3085 llen--; 3086 } 3087 if (llen == 0 || *line == '#' || *line == '\n') 3088 return (1); 3089 3090 return (0); 3091 } 3092 3093 static int 3094 special_offload_rule(char *str) 3095 { 3096 3097 /* skip leading whitespaces */ 3098 while (isspace(*str)) 3099 str++; 3100 3101 /* check for special strings: "-", "all", "any" */ 3102 if (*str == '-') { 3103 str++; 3104 } else if (!strncmp(str, "all", 3) || !strncmp(str, "any", 3)) { 3105 str += 3; 3106 } else { 3107 return (0); 3108 } 3109 3110 /* skip trailing whitespaces */ 3111 while (isspace(*str)) 3112 str++; 3113 3114 return (*str == '\0'); 3115 } 3116 3117 /* 3118 * A rule has 3 parts: an open-type, a match expression, and offload settings. 3119 * 3120 * [<open-type>] <expr> => <settings> 3121 */ 3122 static int 3123 parse_offload_policy_line(size_t lno, char *line, size_t llen, pcap_t *pd, 3124 struct offload_rule *r) 3125 { 3126 char *expr, *settings, *s; 3127 3128 bzero(r, sizeof(*r)); 3129 3130 /* Skip leading whitespace. */ 3131 while (isspace(*line)) 3132 line++; 3133 /* Trim trailing whitespace */ 3134 s = &line[llen - 1]; 3135 while (isspace(*s)) { 3136 *s-- = '\0'; 3137 llen--; 3138 } 3139 3140 /* 3141 * First part of the rule: '[X]' where X = A/D/L/P 3142 */ 3143 if (*line++ != '[') { 3144 warnx("missing \"[\" on line %zd", lno); 3145 return (EINVAL); 3146 } 3147 switch (*line) { 3148 case 'A': 3149 case 'D': 3150 case 'L': 3151 case 'P': 3152 r->open_type = *line; 3153 break; 3154 default: 3155 warnx("invalid socket-type \"%c\" on line %zd.", *line, lno); 3156 return (EINVAL); 3157 } 3158 line++; 3159 if (*line++ != ']') { 3160 warnx("missing \"]\" after \"[%c\" on line %zd", 3161 r->open_type, lno); 3162 return (EINVAL); 3163 } 3164 3165 /* Skip whitespace. */ 3166 while (isspace(*line)) 3167 line++; 3168 3169 /* 3170 * Rest of the rule: <expr> => <settings> 3171 */ 3172 expr = line; 3173 s = strstr(line, "=>"); 3174 if (s == NULL) 3175 return (EINVAL); 3176 settings = s + 2; 3177 while (isspace(*settings)) 3178 settings++; 3179 *s = '\0'; 3180 3181 /* 3182 * <expr> is either a special name (all, any) or a pcap-filter(7). 3183 * In case of a special name the bpf_prog stays all-zero. 3184 */ 3185 if (!special_offload_rule(expr)) { 3186 if (pcap_compile(pd, &r->bpf_prog, expr, 1, 3187 PCAP_NETMASK_UNKNOWN) < 0) { 3188 warnx("failed to compile \"%s\" on line %zd: %s", expr, 3189 lno, pcap_geterr(pd)); 3190 return (EINVAL); 3191 } 3192 } 3193 3194 /* settings to apply on a match. */ 3195 if (parse_offload_settings(settings, &r->settings) != 0) { 3196 warnx("failed to parse offload settings \"%s\" on line %zd", 3197 settings, lno); 3198 pcap_freecode(&r->bpf_prog); 3199 return (EINVAL); 3200 } 3201 3202 return (0); 3203 3204 } 3205 3206 /* 3207 * Note that op itself is not dynamically allocated. 3208 */ 3209 static void 3210 free_offload_policy(struct t4_offload_policy *op) 3211 { 3212 int i; 3213 3214 for (i = 0; i < op->nrules; i++) { 3215 /* 3216 * pcap_freecode can cope with empty bpf_prog, which is the case 3217 * for an rule that matches on 'any/all/-'. 3218 */ 3219 pcap_freecode(&op->rule[i].bpf_prog); 3220 } 3221 free(op->rule); 3222 op->nrules = 0; 3223 op->rule = NULL; 3224 } 3225 3226 #define REALLOC_STRIDE 32 3227 3228 /* 3229 * Fills up op->nrules and op->rule. 3230 */ 3231 static int 3232 parse_offload_policy(const char *fname, struct t4_offload_policy *op) 3233 { 3234 FILE *fp; 3235 char *line; 3236 int lno, maxrules, rc; 3237 size_t lcap, llen; 3238 struct offload_rule *r; 3239 pcap_t *pd; 3240 3241 fp = fopen(fname, "r"); 3242 if (fp == NULL) { 3243 warn("Unable to open file \"%s\"", fname); 3244 return (errno); 3245 } 3246 pd = pcap_open_dead(DLT_EN10MB, 128); 3247 if (pd == NULL) { 3248 warnx("Failed to open pcap device"); 3249 fclose(fp); 3250 return (EIO); 3251 } 3252 3253 rc = 0; 3254 lno = 0; 3255 lcap = 0; 3256 maxrules = 0; 3257 op->nrules = 0; 3258 op->rule = NULL; 3259 line = NULL; 3260 3261 while ((llen = getline(&line, &lcap, fp)) != -1) { 3262 lno++; 3263 3264 /* Skip empty lines. */ 3265 if (isempty_line(line, llen)) 3266 continue; 3267 3268 if (op->nrules == maxrules) { 3269 maxrules += REALLOC_STRIDE; 3270 r = realloc(op->rule, 3271 maxrules * sizeof(struct offload_rule)); 3272 if (r == NULL) { 3273 warnx("failed to allocate memory for %d rules", 3274 maxrules); 3275 rc = ENOMEM; 3276 goto done; 3277 } 3278 op->rule = r; 3279 } 3280 3281 r = &op->rule[op->nrules]; 3282 rc = parse_offload_policy_line(lno, line, llen, pd, r); 3283 if (rc != 0) { 3284 warnx("Error parsing line %d of \"%s\"", lno, fname); 3285 goto done; 3286 } 3287 3288 op->nrules++; 3289 } 3290 free(line); 3291 3292 if (!feof(fp)) { 3293 warn("Error while reading from file \"%s\" at line %d", 3294 fname, lno); 3295 rc = errno; 3296 goto done; 3297 } 3298 3299 if (op->nrules == 0) { 3300 warnx("No valid rules found in \"%s\"", fname); 3301 rc = EINVAL; 3302 } 3303 done: 3304 pcap_close(pd); 3305 fclose(fp); 3306 if (rc != 0) { 3307 free_offload_policy(op); 3308 } 3309 3310 return (rc); 3311 } 3312 3313 static int 3314 load_offload_policy(int argc, const char *argv[]) 3315 { 3316 int rc = 0; 3317 const char *fname = argv[0]; 3318 struct t4_offload_policy op = {0}; 3319 3320 if (argc != 1) { 3321 warnx("incorrect number of arguments."); 3322 return (EINVAL); 3323 } 3324 3325 if (!strcmp(fname, "clear") || !strcmp(fname, "none")) { 3326 /* op.nrules is 0 and that means clear policy */ 3327 return (doit(CHELSIO_T4_SET_OFLD_POLICY, &op)); 3328 } 3329 3330 rc = parse_offload_policy(fname, &op); 3331 if (rc != 0) { 3332 /* Error message displayed already */ 3333 return (EINVAL); 3334 } 3335 3336 rc = doit(CHELSIO_T4_SET_OFLD_POLICY, &op); 3337 free_offload_policy(&op); 3338 3339 return (rc); 3340 } 3341 3342 static int 3343 run_cmd(int argc, const char *argv[]) 3344 { 3345 int rc = -1; 3346 const char *cmd = argv[0]; 3347 3348 /* command */ 3349 argc--; 3350 argv++; 3351 3352 if (!strcmp(cmd, "reg") || !strcmp(cmd, "reg32")) 3353 rc = register_io(argc, argv, 4); 3354 else if (!strcmp(cmd, "reg64")) 3355 rc = register_io(argc, argv, 8); 3356 else if (!strcmp(cmd, "regdump")) 3357 rc = dump_regs(argc, argv); 3358 else if (!strcmp(cmd, "filter")) 3359 rc = filter_cmd(argc, argv); 3360 else if (!strcmp(cmd, "context")) 3361 rc = get_sge_context(argc, argv); 3362 else if (!strcmp(cmd, "loadfw")) 3363 rc = loadfw(argc, argv); 3364 else if (!strcmp(cmd, "memdump")) 3365 rc = memdump(argc, argv); 3366 else if (!strcmp(cmd, "tcb")) 3367 rc = read_tcb(argc, argv); 3368 else if (!strcmp(cmd, "i2c")) 3369 rc = read_i2c(argc, argv); 3370 else if (!strcmp(cmd, "clearstats")) 3371 rc = clearstats(argc, argv); 3372 else if (!strcmp(cmd, "tracer")) 3373 rc = tracer_cmd(argc, argv); 3374 else if (!strcmp(cmd, "modinfo")) 3375 rc = modinfo(argc, argv); 3376 else if (!strcmp(cmd, "sched-class")) 3377 rc = sched_class(argc, argv); 3378 else if (!strcmp(cmd, "sched-queue")) 3379 rc = sched_queue(argc, argv); 3380 else if (!strcmp(cmd, "loadcfg")) 3381 rc = loadcfg(argc, argv); 3382 else if (!strcmp(cmd, "loadboot")) 3383 rc = loadboot(argc, argv); 3384 else if (!strcmp(cmd, "loadboot-cfg")) 3385 rc = loadbootcfg(argc, argv); 3386 else if (!strcmp(cmd, "dumpstate")) 3387 rc = dumpstate(argc, argv); 3388 else if (!strcmp(cmd, "policy")) 3389 rc = load_offload_policy(argc, argv); 3390 else { 3391 rc = EINVAL; 3392 warnx("invalid command \"%s\"", cmd); 3393 } 3394 3395 return (rc); 3396 } 3397 3398 #define MAX_ARGS 15 3399 static int 3400 run_cmd_loop(void) 3401 { 3402 int i, rc = 0; 3403 char buffer[128], *buf; 3404 const char *args[MAX_ARGS + 1]; 3405 3406 /* 3407 * Simple loop: displays a "> " prompt and processes any input as a 3408 * cxgbetool command. You're supposed to enter only the part after 3409 * "cxgbetool t4nexX". Use "quit" or "exit" to exit. 3410 */ 3411 for (;;) { 3412 fprintf(stdout, "> "); 3413 fflush(stdout); 3414 buf = fgets(buffer, sizeof(buffer), stdin); 3415 if (buf == NULL) { 3416 if (ferror(stdin)) { 3417 warn("stdin error"); 3418 rc = errno; /* errno from fgets */ 3419 } 3420 break; 3421 } 3422 3423 i = 0; 3424 while ((args[i] = strsep(&buf, " \t\n")) != NULL) { 3425 if (args[i][0] != 0 && ++i == MAX_ARGS) 3426 break; 3427 } 3428 args[i] = 0; 3429 3430 if (i == 0) 3431 continue; /* skip empty line */ 3432 3433 if (!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) 3434 break; 3435 3436 rc = run_cmd(i, args); 3437 } 3438 3439 /* rc normally comes from the last command (not including quit/exit) */ 3440 return (rc); 3441 } 3442 3443 int 3444 main(int argc, const char *argv[]) 3445 { 3446 int rc = -1; 3447 3448 progname = argv[0]; 3449 3450 if (argc == 2) { 3451 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { 3452 usage(stdout); 3453 exit(0); 3454 } 3455 } 3456 3457 if (argc < 3) { 3458 usage(stderr); 3459 exit(EINVAL); 3460 } 3461 3462 nexus = argv[1]; 3463 3464 /* progname and nexus */ 3465 argc -= 2; 3466 argv += 2; 3467 3468 if (argc == 1 && !strcmp(argv[0], "stdio")) 3469 rc = run_cmd_loop(); 3470 else 3471 rc = run_cmd(argc, argv); 3472 3473 return (rc); 3474 } 3475