cxgbetool.c revision 245520
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: head/tools/tools/cxgbetool/cxgbetool.c 245520 2013-01-17 00:21:45Z np $"); 30 31#include <stdint.h> 32#include <stdlib.h> 33#include <unistd.h> 34#include <errno.h> 35#include <err.h> 36#include <fcntl.h> 37#include <string.h> 38#include <stdio.h> 39#include <sys/ioctl.h> 40#include <limits.h> 41#include <sys/mman.h> 42#include <sys/types.h> 43#include <sys/socket.h> 44#include <sys/stat.h> 45#include <net/ethernet.h> 46#include <netinet/in.h> 47#include <arpa/inet.h> 48 49#include "t4_ioctl.h" 50 51#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 52 53#define max(x, y) ((x) > (y) ? (x) : (y)) 54 55static const char *progname, *nexus; 56 57struct reg_info { 58 const char *name; 59 uint32_t addr; 60 uint32_t len; 61}; 62 63struct mod_regs { 64 const char *name; 65 const struct reg_info *ri; 66}; 67 68struct field_desc { 69 const char *name; /* Field name */ 70 unsigned short start; /* Start bit position */ 71 unsigned short end; /* End bit position */ 72 unsigned char shift; /* # of low order bits omitted and implicitly 0 */ 73 unsigned char hex; /* Print field in hex instead of decimal */ 74 unsigned char islog2; /* Field contains the base-2 log of the value */ 75}; 76 77#include "reg_defs_t4.c" 78#include "reg_defs_t4vf.c" 79 80static void 81usage(FILE *fp) 82{ 83 fprintf(fp, "Usage: %s <nexus> [operation]\n", progname); 84 fprintf(fp, 85 "\tclearstats <port> clear port statistics\n" 86 "\tcontext <type> <id> show an SGE context\n" 87 "\tfilter <idx> [<param> <val>] ... set a filter\n" 88 "\tfilter <idx> delete|clear delete a filter\n" 89 "\tfilter list list all filters\n" 90 "\tfilter mode [<match>] ... get/set global filter mode\n" 91 "\ti2c <port> <devaddr> <addr> [<len>] read from i2c device\n" 92 "\tloadfw <fw-image.bin> install firmware\n" 93 "\tmemdump <addr> <len> dump a memory range\n" 94 "\treg <address>[=<val>] read/write register\n" 95 "\treg64 <address>[=<val>] read/write 64 bit register\n" 96 "\tregdump [<module>] ... dump registers\n" 97 "\tstdio interactive mode\n" 98 "\ttcb <tid> read TCB\n" 99 ); 100} 101 102static inline unsigned int 103get_card_vers(unsigned int version) 104{ 105 return (version & 0x3ff); 106} 107 108static int 109real_doit(unsigned long cmd, void *data, const char *cmdstr) 110{ 111 static int fd = -1; 112 int rc = 0; 113 114 if (fd == -1) { 115 char buf[64]; 116 117 snprintf(buf, sizeof(buf), "/dev/%s", nexus); 118 if ((fd = open(buf, O_RDWR)) < 0) { 119 warn("open(%s)", nexus); 120 rc = errno; 121 return (rc); 122 } 123 } 124 125 rc = ioctl(fd, cmd, data); 126 if (rc < 0) { 127 warn("%s", cmdstr); 128 rc = errno; 129 } 130 131 return (rc); 132} 133#define doit(x, y) real_doit(x, y, #x) 134 135static char * 136str_to_number(const char *s, long *val, long long *vall) 137{ 138 char *p; 139 140 if (vall) 141 *vall = strtoll(s, &p, 0); 142 else if (val) 143 *val = strtol(s, &p, 0); 144 else 145 p = NULL; 146 147 return (p); 148} 149 150static int 151read_reg(long addr, int size, long long *val) 152{ 153 struct t4_reg reg; 154 int rc; 155 156 reg.addr = (uint32_t) addr; 157 reg.size = (uint32_t) size; 158 reg.val = 0; 159 160 rc = doit(CHELSIO_T4_GETREG, ®); 161 162 *val = reg.val; 163 164 return (rc); 165} 166 167static int 168write_reg(long addr, int size, long long val) 169{ 170 struct t4_reg reg; 171 172 reg.addr = (uint32_t) addr; 173 reg.size = (uint32_t) size; 174 reg.val = (uint64_t) val; 175 176 return doit(CHELSIO_T4_SETREG, ®); 177} 178 179static int 180register_io(int argc, const char *argv[], int size) 181{ 182 char *p, *v; 183 long addr; 184 long long val; 185 int w = 0, rc; 186 187 if (argc == 1) { 188 /* <reg> OR <reg>=<value> */ 189 190 p = str_to_number(argv[0], &addr, NULL); 191 if (*p) { 192 if (*p != '=') { 193 warnx("invalid register \"%s\"", argv[0]); 194 return (EINVAL); 195 } 196 197 w = 1; 198 v = p + 1; 199 p = str_to_number(v, NULL, &val); 200 201 if (*p) { 202 warnx("invalid value \"%s\"", v); 203 return (EINVAL); 204 } 205 } 206 207 } else if (argc == 2) { 208 /* <reg> <value> */ 209 210 w = 1; 211 212 p = str_to_number(argv[0], &addr, NULL); 213 if (*p) { 214 warnx("invalid register \"%s\"", argv[0]); 215 return (EINVAL); 216 } 217 218 p = str_to_number(argv[1], NULL, &val); 219 if (*p) { 220 warnx("invalid value \"%s\"", argv[1]); 221 return (EINVAL); 222 } 223 } else { 224 warnx("reg: invalid number of arguments (%d)", argc); 225 return (EINVAL); 226 } 227 228 if (w) 229 rc = write_reg(addr, size, val); 230 else { 231 rc = read_reg(addr, size, &val); 232 if (rc == 0) 233 printf("0x%llx [%llu]\n", val, val); 234 } 235 236 return (rc); 237} 238 239static inline uint32_t 240xtract(uint32_t val, int shift, int len) 241{ 242 return (val >> shift) & ((1 << len) - 1); 243} 244 245static int 246dump_block_regs(const struct reg_info *reg_array, const uint32_t *regs) 247{ 248 uint32_t reg_val = 0; 249 250 for ( ; reg_array->name; ++reg_array) 251 if (!reg_array->len) { 252 reg_val = regs[reg_array->addr / 4]; 253 printf("[%#7x] %-47s %#-10x %u\n", reg_array->addr, 254 reg_array->name, reg_val, reg_val); 255 } else { 256 uint32_t v = xtract(reg_val, reg_array->addr, 257 reg_array->len); 258 259 printf(" %*u:%u %-47s %#-10x %u\n", 260 reg_array->addr < 10 ? 3 : 2, 261 reg_array->addr + reg_array->len - 1, 262 reg_array->addr, reg_array->name, v, v); 263 } 264 265 return (1); 266} 267 268static int 269dump_regs_table(int argc, const char *argv[], const uint32_t *regs, 270 const struct mod_regs *modtab, int nmodules) 271{ 272 int i, j, match; 273 274 for (i = 0; i < argc; i++) { 275 for (j = 0; j < nmodules; j++) { 276 if (!strcmp(argv[i], modtab[j].name)) 277 break; 278 } 279 280 if (j == nmodules) { 281 warnx("invalid register block \"%s\"", argv[i]); 282 fprintf(stderr, "\nAvailable blocks:"); 283 for ( ; nmodules; nmodules--, modtab++) 284 fprintf(stderr, " %s", modtab->name); 285 fprintf(stderr, "\n"); 286 return (EINVAL); 287 } 288 } 289 290 for ( ; nmodules; nmodules--, modtab++) { 291 292 match = argc == 0 ? 1 : 0; 293 for (i = 0; !match && i < argc; i++) { 294 if (!strcmp(argv[i], modtab->name)) 295 match = 1; 296 } 297 298 if (match) 299 dump_block_regs(modtab->ri, regs); 300 } 301 302 return (0); 303} 304 305#define T4_MODREGS(name) { #name, t4_##name##_regs } 306static int 307dump_regs_t4(int argc, const char *argv[], const uint32_t *regs) 308{ 309 static struct mod_regs t4_mod[] = { 310 T4_MODREGS(sge), 311 { "pci", t4_pcie_regs }, 312 T4_MODREGS(dbg), 313 T4_MODREGS(mc), 314 T4_MODREGS(ma), 315 { "edc0", t4_edc_0_regs }, 316 { "edc1", t4_edc_1_regs }, 317 T4_MODREGS(cim), 318 T4_MODREGS(tp), 319 T4_MODREGS(ulp_rx), 320 T4_MODREGS(ulp_tx), 321 { "pmrx", t4_pm_rx_regs }, 322 { "pmtx", t4_pm_tx_regs }, 323 T4_MODREGS(mps), 324 { "cplsw", t4_cpl_switch_regs }, 325 T4_MODREGS(smb), 326 { "i2c", t4_i2cm_regs }, 327 T4_MODREGS(mi), 328 T4_MODREGS(uart), 329 T4_MODREGS(pmu), 330 T4_MODREGS(sf), 331 T4_MODREGS(pl), 332 T4_MODREGS(le), 333 T4_MODREGS(ncsi), 334 T4_MODREGS(xgmac) 335 }; 336 337 return dump_regs_table(argc, argv, regs, t4_mod, ARRAY_SIZE(t4_mod)); 338} 339#undef T4_MODREGS 340 341static int 342dump_regs_t4vf(int argc, const char *argv[], const uint32_t *regs) 343{ 344 static struct mod_regs t4vf_mod[] = { 345 { "sge", t4vf_sge_regs }, 346 { "mps", t4vf_mps_regs }, 347 { "pl", t4vf_pl_regs }, 348 { "mbdata", t4vf_mbdata_regs }, 349 { "cim", t4vf_cim_regs }, 350 }; 351 352 return dump_regs_table(argc, argv, regs, t4vf_mod, 353 ARRAY_SIZE(t4vf_mod)); 354} 355 356static int 357dump_regs(int argc, const char *argv[]) 358{ 359 int vers, revision, is_pcie, rc; 360 struct t4_regdump regs; 361 362 regs.data = calloc(1, T4_REGDUMP_SIZE); 363 if (regs.data == NULL) { 364 warnc(ENOMEM, "regdump"); 365 return (ENOMEM); 366 } 367 368 regs.len = T4_REGDUMP_SIZE; 369 rc = doit(CHELSIO_T4_REGDUMP, ®s); 370 if (rc != 0) 371 return (rc); 372 373 vers = get_card_vers(regs.version); 374 revision = (regs.version >> 10) & 0x3f; 375 is_pcie = (regs.version & 0x80000000) != 0; 376 377 if (vers == 4) { 378 if (revision == 0x3f) 379 rc = dump_regs_t4vf(argc, argv, regs.data); 380 else 381 rc = dump_regs_t4(argc, argv, regs.data); 382 } else { 383 warnx("%s (type %d, rev %d) is not a T4 card.", 384 nexus, vers, revision); 385 return (ENOTSUP); 386 } 387 388 free(regs.data); 389 return (rc); 390} 391 392static void 393do_show_info_header(uint32_t mode) 394{ 395 uint32_t i; 396 397 printf ("%4s %8s", "Idx", "Hits"); 398 for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) { 399 switch (mode & i) { 400 case T4_FILTER_FCoE: 401 printf (" FCoE"); 402 break; 403 404 case T4_FILTER_PORT: 405 printf (" Port"); 406 break; 407 408 case T4_FILTER_VNIC: 409 printf (" vld:VNIC"); 410 break; 411 412 case T4_FILTER_VLAN: 413 printf (" vld:VLAN"); 414 break; 415 416 case T4_FILTER_IP_TOS: 417 printf (" TOS"); 418 break; 419 420 case T4_FILTER_IP_PROTO: 421 printf (" Prot"); 422 break; 423 424 case T4_FILTER_ETH_TYPE: 425 printf (" EthType"); 426 break; 427 428 case T4_FILTER_MAC_IDX: 429 printf (" MACIdx"); 430 break; 431 432 case T4_FILTER_MPS_HIT_TYPE: 433 printf (" MPS"); 434 break; 435 436 case T4_FILTER_IP_FRAGMENT: 437 printf (" Frag"); 438 break; 439 440 default: 441 /* compressed filter field not enabled */ 442 break; 443 } 444 } 445 printf(" %20s %20s %9s %9s %s\n", 446 "DIP", "SIP", "DPORT", "SPORT", "Action"); 447} 448 449/* 450 * Parse an argument sub-vector as a { <parameter name> <value>[:<mask>] } 451 * ordered tuple. If the parameter name in the argument sub-vector does not 452 * match the passed in parameter name, then a zero is returned for the 453 * function and no parsing is performed. If there is a match, then the value 454 * and optional mask are parsed and returned in the provided return value 455 * pointers. If no optional mask is specified, then a default mask of all 1s 456 * will be returned. 457 * 458 * An error in parsing the value[:mask] will result in an error message and 459 * program termination. 460 */ 461static int 462parse_val_mask(const char *param, const char *args[], uint32_t *val, 463 uint32_t *mask) 464{ 465 char *p; 466 467 if (strcmp(param, args[0]) != 0) 468 return (EINVAL); 469 470 *val = strtoul(args[1], &p, 0); 471 if (p > args[1]) { 472 if (p[0] == 0) { 473 *mask = ~0; 474 return (0); 475 } 476 477 if (p[0] == ':' && p[1] != 0) { 478 *mask = strtoul(p+1, &p, 0); 479 if (p[0] == 0) 480 return (0); 481 } 482 } 483 484 warnx("parameter \"%s\" has bad \"value[:mask]\" %s", 485 args[0], args[1]); 486 487 return (EINVAL); 488} 489 490/* 491 * Parse an argument sub-vector as a { <parameter name> <addr>[/<mask>] } 492 * ordered tuple. If the parameter name in the argument sub-vector does not 493 * match the passed in parameter name, then a zero is returned for the 494 * function and no parsing is performed. If there is a match, then the value 495 * and optional mask are parsed and returned in the provided return value 496 * pointers. If no optional mask is specified, then a default mask of all 1s 497 * will be returned. 498 * 499 * The value return parameter "afp" is used to specify the expected address 500 * family -- IPv4 or IPv6 -- of the address[/mask] and return its actual 501 * format. A passed in value of AF_UNSPEC indicates that either IPv4 or IPv6 502 * is acceptable; AF_INET means that only IPv4 addresses are acceptable; and 503 * AF_INET6 means that only IPv6 are acceptable. AF_INET is returned for IPv4 504 * and AF_INET6 for IPv6 addresses, respectively. IPv4 address/mask pairs are 505 * returned in the first four bytes of the address and mask return values with 506 * the address A.B.C.D returned with { A, B, C, D } returned in addresses { 0, 507 * 1, 2, 3}, respectively. 508 * 509 * An error in parsing the value[:mask] will result in an error message and 510 * program termination. 511 */ 512static int 513parse_ipaddr(const char *param, const char *args[], int *afp, uint8_t addr[], 514 uint8_t mask[]) 515{ 516 const char *colon, *afn; 517 char *slash; 518 uint8_t *m; 519 int af, ret; 520 unsigned int masksize; 521 522 /* 523 * Is this our parameter? 524 */ 525 if (strcmp(param, args[0]) != 0) 526 return (EINVAL); 527 528 /* 529 * Fundamental IPv4 versus IPv6 selection. 530 */ 531 colon = strchr(args[1], ':'); 532 if (!colon) { 533 afn = "IPv4"; 534 af = AF_INET; 535 masksize = 32; 536 } else { 537 afn = "IPv6"; 538 af = AF_INET6; 539 masksize = 128; 540 } 541 if (*afp == AF_UNSPEC) 542 *afp = af; 543 else if (*afp != af) { 544 warnx("address %s is not of expected family %s", 545 args[1], *afp == AF_INET ? "IP" : "IPv6"); 546 return (EINVAL); 547 } 548 549 /* 550 * Parse address (temporarily stripping off any "/mask" 551 * specification). 552 */ 553 slash = strchr(args[1], '/'); 554 if (slash) 555 *slash = 0; 556 ret = inet_pton(af, args[1], addr); 557 if (slash) 558 *slash = '/'; 559 if (ret <= 0) { 560 warnx("Cannot parse %s %s address %s", param, afn, args[1]); 561 return (EINVAL); 562 } 563 564 /* 565 * Parse optional mask specification. 566 */ 567 if (slash) { 568 char *p; 569 unsigned int prefix = strtoul(slash + 1, &p, 10); 570 571 if (p == slash + 1) { 572 warnx("missing address prefix for %s", param); 573 return (EINVAL); 574 } 575 if (*p) { 576 warnx("%s is not a valid address prefix", slash + 1); 577 return (EINVAL); 578 } 579 if (prefix > masksize) { 580 warnx("prefix %u is too long for an %s address", 581 prefix, afn); 582 return (EINVAL); 583 } 584 memset(mask, 0, masksize / 8); 585 masksize = prefix; 586 } 587 588 /* 589 * Fill in mask. 590 */ 591 for (m = mask; masksize >= 8; m++, masksize -= 8) 592 *m = ~0; 593 if (masksize) 594 *m = ~0 << (8 - masksize); 595 596 return (0); 597} 598 599/* 600 * Parse an argument sub-vector as a { <parameter name> <value> } ordered 601 * tuple. If the parameter name in the argument sub-vector does not match the 602 * passed in parameter name, then a zero is returned for the function and no 603 * parsing is performed. If there is a match, then the value is parsed and 604 * returned in the provided return value pointer. 605 */ 606static int 607parse_val(const char *param, const char *args[], uint32_t *val) 608{ 609 char *p; 610 611 if (strcmp(param, args[0]) != 0) 612 return (EINVAL); 613 614 *val = strtoul(args[1], &p, 0); 615 if (p > args[1] && p[0] == 0) 616 return (0); 617 618 warnx("parameter \"%s\" has bad \"value\" %s", args[0], args[1]); 619 return (EINVAL); 620} 621 622static void 623filters_show_ipaddr(int type, uint8_t *addr, uint8_t *addrm) 624{ 625 int noctets, octet; 626 627 printf(" "); 628 if (type == 0) { 629 noctets = 4; 630 printf("%3s", " "); 631 } else 632 noctets = 16; 633 634 for (octet = 0; octet < noctets; octet++) 635 printf("%02x", addr[octet]); 636 printf("/"); 637 for (octet = 0; octet < noctets; octet++) 638 printf("%02x", addrm[octet]); 639} 640 641static void 642do_show_one_filter_info(struct t4_filter *t, uint32_t mode) 643{ 644 uint32_t i; 645 646 printf("%4d", t->idx); 647 if (t->hits == UINT64_MAX) 648 printf(" %8s", "-"); 649 else 650 printf(" %8ju", t->hits); 651 652 /* 653 * Compressed header portion of filter. 654 */ 655 for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) { 656 switch (mode & i) { 657 case T4_FILTER_FCoE: 658 printf(" %1d/%1d", t->fs.val.fcoe, t->fs.mask.fcoe); 659 break; 660 661 case T4_FILTER_PORT: 662 printf(" %1d/%1d", t->fs.val.iport, t->fs.mask.iport); 663 break; 664 665 case T4_FILTER_VNIC: 666 printf(" %1d:%1x:%02x/%1d:%1x:%02x", 667 t->fs.val.vnic_vld, (t->fs.val.vnic >> 7) & 0x7, 668 t->fs.val.vnic & 0x7f, t->fs.mask.vnic_vld, 669 (t->fs.mask.vnic >> 7) & 0x7, 670 t->fs.mask.vnic & 0x7f); 671 break; 672 673 case T4_FILTER_VLAN: 674 printf(" %1d:%04x/%1d:%04x", 675 t->fs.val.vlan_vld, t->fs.val.vlan, 676 t->fs.mask.vlan_vld, t->fs.mask.vlan); 677 break; 678 679 case T4_FILTER_IP_TOS: 680 printf(" %02x/%02x", t->fs.val.tos, t->fs.mask.tos); 681 break; 682 683 case T4_FILTER_IP_PROTO: 684 printf(" %02x/%02x", t->fs.val.proto, t->fs.mask.proto); 685 break; 686 687 case T4_FILTER_ETH_TYPE: 688 printf(" %04x/%04x", t->fs.val.ethtype, 689 t->fs.mask.ethtype); 690 break; 691 692 case T4_FILTER_MAC_IDX: 693 printf(" %03x/%03x", t->fs.val.macidx, 694 t->fs.mask.macidx); 695 break; 696 697 case T4_FILTER_MPS_HIT_TYPE: 698 printf(" %1x/%1x", t->fs.val.matchtype, 699 t->fs.mask.matchtype); 700 break; 701 702 case T4_FILTER_IP_FRAGMENT: 703 printf(" %1d/%1d", t->fs.val.frag, t->fs.mask.frag); 704 break; 705 706 default: 707 /* compressed filter field not enabled */ 708 break; 709 } 710 } 711 712 /* 713 * Fixed portion of filter. 714 */ 715 filters_show_ipaddr(t->fs.type, t->fs.val.dip, t->fs.mask.dip); 716 filters_show_ipaddr(t->fs.type, t->fs.val.sip, t->fs.mask.sip); 717 printf(" %04x/%04x %04x/%04x", 718 t->fs.val.dport, t->fs.mask.dport, 719 t->fs.val.sport, t->fs.mask.sport); 720 721 /* 722 * Variable length filter action. 723 */ 724 if (t->fs.action == FILTER_DROP) 725 printf(" Drop"); 726 else if (t->fs.action == FILTER_SWITCH) { 727 printf(" Switch: port=%d", t->fs.eport); 728 if (t->fs.newdmac) 729 printf( 730 ", dmac=%02x:%02x:%02x:%02x:%02x:%02x " 731 ", l2tidx=%d", 732 t->fs.dmac[0], t->fs.dmac[1], 733 t->fs.dmac[2], t->fs.dmac[3], 734 t->fs.dmac[4], t->fs.dmac[5], 735 t->l2tidx); 736 if (t->fs.newsmac) 737 printf( 738 ", smac=%02x:%02x:%02x:%02x:%02x:%02x " 739 ", smtidx=%d", 740 t->fs.smac[0], t->fs.smac[1], 741 t->fs.smac[2], t->fs.smac[3], 742 t->fs.smac[4], t->fs.smac[5], 743 t->smtidx); 744 if (t->fs.newvlan == VLAN_REMOVE) 745 printf(", vlan=none"); 746 else if (t->fs.newvlan == VLAN_INSERT) 747 printf(", vlan=insert(%x)", t->fs.vlan); 748 else if (t->fs.newvlan == VLAN_REWRITE) 749 printf(", vlan=rewrite(%x)", t->fs.vlan); 750 } else { 751 printf(" Pass: Q="); 752 if (t->fs.dirsteer == 0) { 753 printf("RSS"); 754 if (t->fs.maskhash) 755 printf("(TCB=hash)"); 756 } else { 757 printf("%d", t->fs.iq); 758 if (t->fs.dirsteerhash == 0) 759 printf("(QID)"); 760 else 761 printf("(hash)"); 762 } 763 } 764 if (t->fs.prio) 765 printf(" Prio"); 766 if (t->fs.rpttid) 767 printf(" RptTID"); 768 printf("\n"); 769} 770 771static int 772show_filters(void) 773{ 774 uint32_t mode = 0, header = 0; 775 struct t4_filter t; 776 int rc; 777 778 /* Get the global filter mode first */ 779 rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode); 780 if (rc != 0) 781 return (rc); 782 783 t.idx = 0; 784 for (t.idx = 0; ; t.idx++) { 785 rc = doit(CHELSIO_T4_GET_FILTER, &t); 786 if (rc != 0 || t.idx == 0xffffffff) 787 break; 788 789 if (!header) { 790 do_show_info_header(mode); 791 header = 1; 792 } 793 do_show_one_filter_info(&t, mode); 794 }; 795 796 return (rc); 797} 798 799static int 800get_filter_mode(void) 801{ 802 uint32_t mode = 0; 803 int rc; 804 805 rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode); 806 if (rc != 0) 807 return (rc); 808 809 if (mode & T4_FILTER_IPv4) 810 printf("ipv4 "); 811 812 if (mode & T4_FILTER_IPv6) 813 printf("ipv6 "); 814 815 if (mode & T4_FILTER_IP_SADDR) 816 printf("sip "); 817 818 if (mode & T4_FILTER_IP_DADDR) 819 printf("dip "); 820 821 if (mode & T4_FILTER_IP_SPORT) 822 printf("sport "); 823 824 if (mode & T4_FILTER_IP_DPORT) 825 printf("dport "); 826 827 if (mode & T4_FILTER_MPS_HIT_TYPE) 828 printf("matchtype "); 829 830 if (mode & T4_FILTER_MAC_IDX) 831 printf("macidx "); 832 833 if (mode & T4_FILTER_ETH_TYPE) 834 printf("ethtype "); 835 836 if (mode & T4_FILTER_IP_PROTO) 837 printf("proto "); 838 839 if (mode & T4_FILTER_IP_TOS) 840 printf("tos "); 841 842 if (mode & T4_FILTER_VLAN) 843 printf("vlan "); 844 845 if (mode & T4_FILTER_VNIC) 846 printf("vnic "); 847 848 if (mode & T4_FILTER_PORT) 849 printf("iport "); 850 851 if (mode & T4_FILTER_FCoE) 852 printf("fcoe "); 853 854 printf("\n"); 855 856 return (0); 857} 858 859static int 860set_filter_mode(int argc, const char *argv[]) 861{ 862 uint32_t mode = 0; 863 864 for (; argc; argc--, argv++) { 865 if (!strcmp(argv[0], "matchtype")) 866 mode |= T4_FILTER_MPS_HIT_TYPE; 867 868 if (!strcmp(argv[0], "macidx")) 869 mode |= T4_FILTER_MAC_IDX; 870 871 if (!strcmp(argv[0], "ethtype")) 872 mode |= T4_FILTER_ETH_TYPE; 873 874 if (!strcmp(argv[0], "proto")) 875 mode |= T4_FILTER_IP_PROTO; 876 877 if (!strcmp(argv[0], "tos")) 878 mode |= T4_FILTER_IP_TOS; 879 880 if (!strcmp(argv[0], "vlan")) 881 mode |= T4_FILTER_VLAN; 882 883 if (!strcmp(argv[0], "ovlan") || 884 !strcmp(argv[0], "vnic")) 885 mode |= T4_FILTER_VNIC; 886 887 if (!strcmp(argv[0], "iport")) 888 mode |= T4_FILTER_PORT; 889 890 if (!strcmp(argv[0], "fcoe")) 891 mode |= T4_FILTER_FCoE; 892 } 893 894 return doit(CHELSIO_T4_SET_FILTER_MODE, &mode); 895} 896 897static int 898del_filter(uint32_t idx) 899{ 900 struct t4_filter t; 901 902 t.idx = idx; 903 904 return doit(CHELSIO_T4_DEL_FILTER, &t); 905} 906 907static int 908set_filter(uint32_t idx, int argc, const char *argv[]) 909{ 910 int af = AF_UNSPEC, start_arg = 0; 911 struct t4_filter t; 912 913 if (argc < 2) { 914 warnc(EINVAL, "%s", __func__); 915 return (EINVAL); 916 }; 917 bzero(&t, sizeof (t)); 918 t.idx = idx; 919 920 for (start_arg = 0; start_arg + 2 <= argc; start_arg += 2) { 921 const char **args = &argv[start_arg]; 922 uint32_t val, mask; 923 924 if (!strcmp(argv[start_arg], "type")) { 925 int newaf; 926 if (!strcasecmp(argv[start_arg + 1], "ipv4")) 927 newaf = AF_INET; 928 else if (!strcasecmp(argv[start_arg + 1], "ipv6")) 929 newaf = AF_INET6; 930 else { 931 warnx("invalid type \"%s\"; " 932 "must be one of \"ipv4\" or \"ipv6\"", 933 argv[start_arg + 1]); 934 return (EINVAL); 935 } 936 937 if (af != AF_UNSPEC && af != newaf) { 938 warnx("conflicting IPv4/IPv6 specifications."); 939 return (EINVAL); 940 } 941 af = newaf; 942 } else if (!parse_val_mask("fcoe", args, &val, &mask)) { 943 t.fs.val.fcoe = val; 944 t.fs.mask.fcoe = mask; 945 } else if (!parse_val_mask("iport", args, &val, &mask)) { 946 t.fs.val.iport = val; 947 t.fs.mask.iport = mask; 948 } else if (!parse_val_mask("ovlan", args, &val, &mask)) { 949 t.fs.val.vnic = val; 950 t.fs.mask.vnic = mask; 951 t.fs.val.vnic_vld = 1; 952 t.fs.mask.vnic_vld = 1; 953 } else if (!parse_val_mask("vnic", args, &val, &mask)) { 954 t.fs.val.vnic = val; 955 t.fs.mask.vnic = mask; 956 t.fs.val.vnic_vld = 1; 957 t.fs.mask.vnic_vld = 1; 958 } else if (!parse_val_mask("ivlan", args, &val, &mask)) { 959 t.fs.val.vlan = val; 960 t.fs.mask.vlan = mask; 961 t.fs.val.vlan_vld = 1; 962 t.fs.mask.vlan_vld = 1; 963 } else if (!parse_val_mask("tos", args, &val, &mask)) { 964 t.fs.val.tos = val; 965 t.fs.mask.tos = mask; 966 } else if (!parse_val_mask("proto", args, &val, &mask)) { 967 t.fs.val.proto = val; 968 t.fs.mask.proto = mask; 969 } else if (!parse_val_mask("ethtype", args, &val, &mask)) { 970 t.fs.val.ethtype = val; 971 t.fs.mask.ethtype = mask; 972 } else if (!parse_val_mask("macidx", args, &val, &mask)) { 973 t.fs.val.macidx = val; 974 t.fs.mask.macidx = mask; 975 } else if (!parse_val_mask("matchtype", args, &val, &mask)) { 976 t.fs.val.matchtype = val; 977 t.fs.mask.matchtype = mask; 978 } else if (!parse_val_mask("frag", args, &val, &mask)) { 979 t.fs.val.frag = val; 980 t.fs.mask.frag = mask; 981 } else if (!parse_val_mask("dport", args, &val, &mask)) { 982 t.fs.val.dport = val; 983 t.fs.mask.dport = mask; 984 } else if (!parse_val_mask("sport", args, &val, &mask)) { 985 t.fs.val.sport = val; 986 t.fs.mask.sport = mask; 987 } else if (!parse_ipaddr("dip", args, &af, t.fs.val.dip, 988 t.fs.mask.dip)) { 989 /* nada */; 990 } else if (!parse_ipaddr("sip", args, &af, t.fs.val.sip, 991 t.fs.mask.sip)) { 992 /* nada */; 993 } else if (!strcmp(argv[start_arg], "action")) { 994 if (!strcmp(argv[start_arg + 1], "pass")) 995 t.fs.action = FILTER_PASS; 996 else if (!strcmp(argv[start_arg + 1], "drop")) 997 t.fs.action = FILTER_DROP; 998 else if (!strcmp(argv[start_arg + 1], "switch")) 999 t.fs.action = FILTER_SWITCH; 1000 else { 1001 warnx("invalid action \"%s\"; must be one of" 1002 " \"pass\", \"drop\" or \"switch\"", 1003 argv[start_arg + 1]); 1004 return (EINVAL); 1005 } 1006 } else if (!parse_val("hitcnts", args, &val)) { 1007 t.fs.hitcnts = val; 1008 } else if (!parse_val("prio", args, &val)) { 1009 t.fs.prio = val; 1010 } else if (!parse_val("rpttid", args, &val)) { 1011 t.fs.rpttid = 1; 1012 } else if (!parse_val("queue", args, &val)) { 1013 t.fs.dirsteer = 1; 1014 t.fs.iq = val; 1015 } else if (!parse_val("tcbhash", args, &val)) { 1016 t.fs.maskhash = 1; 1017 t.fs.dirsteerhash = 1; 1018 } else if (!parse_val("eport", args, &val)) { 1019 t.fs.eport = val; 1020 } else if (!strcmp(argv[start_arg], "dmac")) { 1021 struct ether_addr *daddr; 1022 1023 daddr = ether_aton(argv[start_arg + 1]); 1024 if (daddr == NULL) { 1025 warnx("invalid dmac address \"%s\"", 1026 argv[start_arg + 1]); 1027 return (EINVAL); 1028 } 1029 memcpy(t.fs.dmac, daddr, ETHER_ADDR_LEN); 1030 t.fs.newdmac = 1; 1031 } else if (!strcmp(argv[start_arg], "smac")) { 1032 struct ether_addr *saddr; 1033 1034 saddr = ether_aton(argv[start_arg + 1]); 1035 if (saddr == NULL) { 1036 warnx("invalid smac address \"%s\"", 1037 argv[start_arg + 1]); 1038 return (EINVAL); 1039 } 1040 memcpy(t.fs.smac, saddr, ETHER_ADDR_LEN); 1041 t.fs.newsmac = 1; 1042 } else if (!strcmp(argv[start_arg], "vlan")) { 1043 char *p; 1044 if (!strcmp(argv[start_arg + 1], "none")) { 1045 t.fs.newvlan = VLAN_REMOVE; 1046 } else if (argv[start_arg + 1][0] == '=') { 1047 t.fs.newvlan = VLAN_REWRITE; 1048 } else if (argv[start_arg + 1][0] == '+') { 1049 t.fs.newvlan = VLAN_INSERT; 1050 } else if (isdigit(argv[start_arg + 1][0]) && 1051 !parse_val_mask("vlan", args, &val, &mask)) { 1052 t.fs.val.vlan = val; 1053 t.fs.mask.vlan = mask; 1054 t.fs.val.vlan_vld = 1; 1055 t.fs.mask.vlan_vld = 1; 1056 } else { 1057 warnx("unknown vlan parameter \"%s\"; must" 1058 " be one of \"none\", \"=<vlan>\", " 1059 " \"+<vlan>\", or \"<vlan>\"", 1060 argv[start_arg + 1]); 1061 return (EINVAL); 1062 } 1063 if (t.fs.newvlan == VLAN_REWRITE || 1064 t.fs.newvlan == VLAN_INSERT) { 1065 t.fs.vlan = strtoul(argv[start_arg + 1] + 1, 1066 &p, 0); 1067 if (p == argv[start_arg + 1] + 1 || p[0] != 0) { 1068 warnx("invalid vlan \"%s\"", 1069 argv[start_arg + 1]); 1070 return (EINVAL); 1071 } 1072 } 1073 } else { 1074 warnx("invalid parameter \"%s\"", argv[start_arg]); 1075 return (EINVAL); 1076 } 1077 } 1078 if (start_arg != argc) { 1079 warnx("no value for \"%s\"", argv[start_arg]); 1080 return (EINVAL); 1081 } 1082 1083 /* 1084 * Check basic sanity of option combinations. 1085 */ 1086 if (t.fs.action != FILTER_SWITCH && 1087 (t.fs.eport || t.fs.newdmac || t.fs.newsmac || t.fs.newvlan)) { 1088 warnx("prio, port dmac, smac and vlan only make sense with" 1089 " \"action switch\""); 1090 return (EINVAL); 1091 } 1092 if (t.fs.action != FILTER_PASS && 1093 (t.fs.rpttid || t.fs.dirsteer || t.fs.maskhash)) { 1094 warnx("rpttid, queue and tcbhash don't make sense with" 1095 " action \"drop\" or \"switch\""); 1096 return (EINVAL); 1097 } 1098 1099 t.fs.type = (af == AF_INET6 ? 1 : 0); /* default IPv4 */ 1100 return doit(CHELSIO_T4_SET_FILTER, &t); 1101} 1102 1103static int 1104filter_cmd(int argc, const char *argv[]) 1105{ 1106 long long val; 1107 uint32_t idx; 1108 char *s; 1109 1110 if (argc == 0) { 1111 warnx("filter: no arguments."); 1112 return (EINVAL); 1113 }; 1114 1115 /* list */ 1116 if (strcmp(argv[0], "list") == 0) { 1117 if (argc != 1) 1118 warnx("trailing arguments after \"list\" ignored."); 1119 1120 return show_filters(); 1121 } 1122 1123 /* mode */ 1124 if (argc == 1 && strcmp(argv[0], "mode") == 0) 1125 return get_filter_mode(); 1126 1127 /* mode <mode> */ 1128 if (strcmp(argv[0], "mode") == 0) 1129 return set_filter_mode(argc - 1, argv + 1); 1130 1131 /* <idx> ... */ 1132 s = str_to_number(argv[0], NULL, &val); 1133 if (*s || val > 0xffffffffU) { 1134 warnx("\"%s\" is neither an index nor a filter subcommand.", 1135 argv[0]); 1136 return (EINVAL); 1137 } 1138 idx = (uint32_t) val; 1139 1140 /* <idx> delete|clear */ 1141 if (argc == 2 && 1142 (strcmp(argv[1], "delete") == 0 || strcmp(argv[1], "clear") == 0)) { 1143 return del_filter(idx); 1144 } 1145 1146 /* <idx> [<param> <val>] ... */ 1147 return set_filter(idx, argc - 1, argv + 1); 1148} 1149 1150/* 1151 * Shows the fields of a multi-word structure. The structure is considered to 1152 * consist of @nwords 32-bit words (i.e, it's an (@nwords * 32)-bit structure) 1153 * whose fields are described by @fd. The 32-bit words are given in @words 1154 * starting with the least significant 32-bit word. 1155 */ 1156static void 1157show_struct(const uint32_t *words, int nwords, const struct field_desc *fd) 1158{ 1159 unsigned int w = 0; 1160 const struct field_desc *p; 1161 1162 for (p = fd; p->name; p++) 1163 w = max(w, strlen(p->name)); 1164 1165 while (fd->name) { 1166 unsigned long long data; 1167 int first_word = fd->start / 32; 1168 int shift = fd->start % 32; 1169 int width = fd->end - fd->start + 1; 1170 unsigned long long mask = (1ULL << width) - 1; 1171 1172 data = (words[first_word] >> shift) | 1173 ((uint64_t)words[first_word + 1] << (32 - shift)); 1174 if (shift) 1175 data |= ((uint64_t)words[first_word + 2] << (64 - shift)); 1176 data &= mask; 1177 if (fd->islog2) 1178 data = 1 << data; 1179 printf("%-*s ", w, fd->name); 1180 printf(fd->hex ? "%#llx\n" : "%llu\n", data << fd->shift); 1181 fd++; 1182 } 1183} 1184 1185#define FIELD(name, start, end) { name, start, end, 0, 0, 0 } 1186#define FIELD1(name, start) FIELD(name, start, start) 1187 1188static void 1189show_sge_context(const struct t4_sge_context *p) 1190{ 1191 static struct field_desc egress[] = { 1192 FIELD1("StatusPgNS:", 180), 1193 FIELD1("StatusPgRO:", 179), 1194 FIELD1("FetchNS:", 178), 1195 FIELD1("FetchRO:", 177), 1196 FIELD1("Valid:", 176), 1197 FIELD("PCIeDataChannel:", 174, 175), 1198 FIELD1("DCAEgrQEn:", 173), 1199 FIELD("DCACPUID:", 168, 172), 1200 FIELD1("FCThreshOverride:", 167), 1201 FIELD("WRLength:", 162, 166), 1202 FIELD1("WRLengthKnown:", 161), 1203 FIELD1("ReschedulePending:", 160), 1204 FIELD1("OnChipQueue:", 159), 1205 FIELD1("FetchSizeMode", 158), 1206 { "FetchBurstMin:", 156, 157, 4, 0, 1 }, 1207 { "FetchBurstMax:", 153, 154, 6, 0, 1 }, 1208 FIELD("uPToken:", 133, 152), 1209 FIELD1("uPTokenEn:", 132), 1210 FIELD1("UserModeIO:", 131), 1211 FIELD("uPFLCredits:", 123, 130), 1212 FIELD1("uPFLCreditEn:", 122), 1213 FIELD("FID:", 111, 121), 1214 FIELD("HostFCMode:", 109, 110), 1215 FIELD1("HostFCOwner:", 108), 1216 { "CIDXFlushThresh:", 105, 107, 0, 0, 1 }, 1217 FIELD("CIDX:", 89, 104), 1218 FIELD("PIDX:", 73, 88), 1219 { "BaseAddress:", 18, 72, 9, 1 }, 1220 FIELD("QueueSize:", 2, 17), 1221 FIELD1("QueueType:", 1), 1222 FIELD1("CachePriority:", 0), 1223 { NULL } 1224 }; 1225 static struct field_desc fl[] = { 1226 FIELD1("StatusPgNS:", 180), 1227 FIELD1("StatusPgRO:", 179), 1228 FIELD1("FetchNS:", 178), 1229 FIELD1("FetchRO:", 177), 1230 FIELD1("Valid:", 176), 1231 FIELD("PCIeDataChannel:", 174, 175), 1232 FIELD1("DCAEgrQEn:", 173), 1233 FIELD("DCACPUID:", 168, 172), 1234 FIELD1("FCThreshOverride:", 167), 1235 FIELD("WRLength:", 162, 166), 1236 FIELD1("WRLengthKnown:", 161), 1237 FIELD1("ReschedulePending:", 160), 1238 FIELD1("OnChipQueue:", 159), 1239 FIELD1("FetchSizeMode", 158), 1240 { "FetchBurstMin:", 156, 157, 4, 0, 1 }, 1241 { "FetchBurstMax:", 153, 154, 6, 0, 1 }, 1242 FIELD1("FLMcongMode:", 152), 1243 FIELD("MaxuPFLCredits:", 144, 151), 1244 FIELD("FLMcontextID:", 133, 143), 1245 FIELD1("uPTokenEn:", 132), 1246 FIELD1("UserModeIO:", 131), 1247 FIELD("uPFLCredits:", 123, 130), 1248 FIELD1("uPFLCreditEn:", 122), 1249 FIELD("FID:", 111, 121), 1250 FIELD("HostFCMode:", 109, 110), 1251 FIELD1("HostFCOwner:", 108), 1252 { "CIDXFlushThresh:", 105, 107, 0, 0, 1 }, 1253 FIELD("CIDX:", 89, 104), 1254 FIELD("PIDX:", 73, 88), 1255 { "BaseAddress:", 18, 72, 9, 1 }, 1256 FIELD("QueueSize:", 2, 17), 1257 FIELD1("QueueType:", 1), 1258 FIELD1("CachePriority:", 0), 1259 { NULL } 1260 }; 1261 static struct field_desc ingress[] = { 1262 FIELD1("NoSnoop:", 145), 1263 FIELD1("RelaxedOrdering:", 144), 1264 FIELD1("GTSmode:", 143), 1265 FIELD1("ISCSICoalescing:", 142), 1266 FIELD1("Valid:", 141), 1267 FIELD1("TimerPending:", 140), 1268 FIELD1("DropRSS:", 139), 1269 FIELD("PCIeChannel:", 137, 138), 1270 FIELD1("SEInterruptArmed:", 136), 1271 FIELD1("CongestionMgtEnable:", 135), 1272 FIELD1("DCAIngQEnable:", 134), 1273 FIELD("DCACPUID:", 129, 133), 1274 FIELD1("UpdateScheduling:", 128), 1275 FIELD("UpdateDelivery:", 126, 127), 1276 FIELD1("InterruptSent:", 125), 1277 FIELD("InterruptIDX:", 114, 124), 1278 FIELD1("InterruptDestination:", 113), 1279 FIELD1("InterruptArmed:", 112), 1280 FIELD("RxIntCounter:", 106, 111), 1281 FIELD("RxIntCounterThreshold:", 104, 105), 1282 FIELD1("Generation:", 103), 1283 { "BaseAddress:", 48, 102, 9, 1 }, 1284 FIELD("PIDX:", 32, 47), 1285 FIELD("CIDX:", 16, 31), 1286 { "QueueSize:", 4, 15, 4, 0 }, 1287 { "QueueEntrySize:", 2, 3, 4, 0, 1 }, 1288 FIELD1("QueueEntryOverride:", 1), 1289 FIELD1("CachePriority:", 0), 1290 { NULL } 1291 }; 1292 static struct field_desc flm[] = { 1293 FIELD1("NoSnoop:", 79), 1294 FIELD1("RelaxedOrdering:", 78), 1295 FIELD1("Valid:", 77), 1296 FIELD("DCACPUID:", 72, 76), 1297 FIELD1("DCAFLEn:", 71), 1298 FIELD("EQid:", 54, 70), 1299 FIELD("SplitEn:", 52, 53), 1300 FIELD1("PadEn:", 51), 1301 FIELD1("PackEn:", 50), 1302 FIELD1("DBpriority:", 48), 1303 FIELD("PackOffset:", 16, 47), 1304 FIELD("CIDX:", 8, 15), 1305 FIELD("PIDX:", 0, 7), 1306 { NULL } 1307 }; 1308 static struct field_desc conm[] = { 1309 FIELD1("CngDBPHdr:", 6), 1310 FIELD1("CngDBPData:", 5), 1311 FIELD1("CngIMSG:", 4), 1312 FIELD("CngChMap:", 0, 3), 1313 { NULL } 1314 }; 1315 1316 if (p->mem_id == SGE_CONTEXT_EGRESS) 1317 show_struct(p->data, 6, (p->data[0] & 2) ? fl : egress); 1318 else if (p->mem_id == SGE_CONTEXT_FLM) 1319 show_struct(p->data, 3, flm); 1320 else if (p->mem_id == SGE_CONTEXT_INGRESS) 1321 show_struct(p->data, 5, ingress); 1322 else if (p->mem_id == SGE_CONTEXT_CNM) 1323 show_struct(p->data, 1, conm); 1324} 1325 1326#undef FIELD 1327#undef FIELD1 1328 1329static int 1330get_sge_context(int argc, const char *argv[]) 1331{ 1332 int rc; 1333 char *p; 1334 long cid; 1335 struct t4_sge_context cntxt = {0}; 1336 1337 if (argc != 2) { 1338 warnx("sge_context: incorrect number of arguments."); 1339 return (EINVAL); 1340 } 1341 1342 if (!strcmp(argv[0], "egress")) 1343 cntxt.mem_id = SGE_CONTEXT_EGRESS; 1344 else if (!strcmp(argv[0], "ingress")) 1345 cntxt.mem_id = SGE_CONTEXT_INGRESS; 1346 else if (!strcmp(argv[0], "fl")) 1347 cntxt.mem_id = SGE_CONTEXT_FLM; 1348 else if (!strcmp(argv[0], "cong")) 1349 cntxt.mem_id = SGE_CONTEXT_CNM; 1350 else { 1351 warnx("unknown context type \"%s\"; known types are egress, " 1352 "ingress, fl, and cong.", argv[0]); 1353 return (EINVAL); 1354 } 1355 1356 p = str_to_number(argv[1], &cid, NULL); 1357 if (*p) { 1358 warnx("invalid context id \"%s\"", argv[1]); 1359 return (EINVAL); 1360 } 1361 cntxt.cid = cid; 1362 1363 rc = doit(CHELSIO_T4_GET_SGE_CONTEXT, &cntxt); 1364 if (rc != 0) 1365 return (rc); 1366 1367 show_sge_context(&cntxt); 1368 return (0); 1369} 1370 1371static int 1372loadfw(int argc, const char *argv[]) 1373{ 1374 int rc, fd; 1375 struct t4_data data = {0}; 1376 const char *fname = argv[0]; 1377 struct stat st = {0}; 1378 1379 if (argc != 1) { 1380 warnx("loadfw: incorrect number of arguments."); 1381 return (EINVAL); 1382 } 1383 1384 fd = open(fname, O_RDONLY); 1385 if (fd < 0) { 1386 warn("open(%s)", fname); 1387 return (errno); 1388 } 1389 1390 if (fstat(fd, &st) < 0) { 1391 warn("fstat"); 1392 close(fd); 1393 return (errno); 1394 } 1395 1396 data.len = st.st_size; 1397 data.data = mmap(0, data.len, PROT_READ, 0, fd, 0); 1398 if (data.data == MAP_FAILED) { 1399 warn("mmap"); 1400 close(fd); 1401 return (errno); 1402 } 1403 1404 rc = doit(CHELSIO_T4_LOAD_FW, &data); 1405 munmap(data.data, data.len); 1406 close(fd); 1407 return (rc); 1408} 1409 1410static int 1411read_mem(uint32_t addr, uint32_t len, void (*output)(uint32_t *, uint32_t)) 1412{ 1413 int rc; 1414 struct t4_mem_range mr; 1415 1416 mr.addr = addr; 1417 mr.len = len; 1418 mr.data = malloc(mr.len); 1419 1420 if (mr.data == 0) { 1421 warn("read_mem: malloc"); 1422 return (errno); 1423 } 1424 1425 rc = doit(CHELSIO_T4_GET_MEM, &mr); 1426 if (rc != 0) 1427 goto done; 1428 1429 if (output) 1430 (*output)(mr.data, mr.len); 1431done: 1432 free(mr.data); 1433 return (rc); 1434} 1435 1436/* 1437 * Display memory as list of 'n' 4-byte values per line. 1438 */ 1439static void 1440show_mem(uint32_t *buf, uint32_t len) 1441{ 1442 const char *s; 1443 int i, n = 8; 1444 1445 while (len) { 1446 for (i = 0; len && i < n; i++, buf++, len -= 4) { 1447 s = i ? " " : ""; 1448 printf("%s%08x", s, htonl(*buf)); 1449 } 1450 printf("\n"); 1451 } 1452} 1453 1454static int 1455memdump(int argc, const char *argv[]) 1456{ 1457 char *p; 1458 long l; 1459 uint32_t addr, len; 1460 1461 if (argc != 2) { 1462 warnx("incorrect number of arguments."); 1463 return (EINVAL); 1464 } 1465 1466 p = str_to_number(argv[0], &l, NULL); 1467 if (*p) { 1468 warnx("invalid address \"%s\"", argv[0]); 1469 return (EINVAL); 1470 } 1471 addr = l; 1472 1473 p = str_to_number(argv[1], &l, NULL); 1474 if (*p) { 1475 warnx("memdump: invalid length \"%s\"", argv[1]); 1476 return (EINVAL); 1477 } 1478 len = l; 1479 1480 return (read_mem(addr, len, show_mem)); 1481} 1482 1483/* 1484 * Display TCB as list of 'n' 4-byte values per line. 1485 */ 1486static void 1487show_tcb(uint32_t *buf, uint32_t len) 1488{ 1489 const char *s; 1490 int i, n = 8; 1491 1492 while (len) { 1493 for (i = 0; len && i < n; i++, buf++, len -= 4) { 1494 s = i ? " " : ""; 1495 printf("%s%08x", s, htonl(*buf)); 1496 } 1497 printf("\n"); 1498 } 1499} 1500 1501#define A_TP_CMM_TCB_BASE 0x7d10 1502#define TCB_SIZE 128 1503static int 1504read_tcb(int argc, const char *argv[]) 1505{ 1506 char *p; 1507 long l; 1508 long long val; 1509 unsigned int tid; 1510 uint32_t addr; 1511 int rc; 1512 1513 if (argc != 1) { 1514 warnx("incorrect number of arguments."); 1515 return (EINVAL); 1516 } 1517 1518 p = str_to_number(argv[0], &l, NULL); 1519 if (*p) { 1520 warnx("invalid tid \"%s\"", argv[0]); 1521 return (EINVAL); 1522 } 1523 tid = l; 1524 1525 rc = read_reg(A_TP_CMM_TCB_BASE, 4, &val); 1526 if (rc != 0) 1527 return (rc); 1528 1529 addr = val + tid * TCB_SIZE; 1530 1531 return (read_mem(addr, TCB_SIZE, show_tcb)); 1532} 1533 1534static int 1535read_i2c(int argc, const char *argv[]) 1536{ 1537 char *p; 1538 long l; 1539 struct t4_i2c_data i2cd; 1540 int rc, i; 1541 1542 if (argc < 3 || argc > 4) { 1543 warnx("incorrect number of arguments."); 1544 return (EINVAL); 1545 } 1546 1547 p = str_to_number(argv[0], &l, NULL); 1548 if (*p || l > UCHAR_MAX) { 1549 warnx("invalid port id \"%s\"", argv[0]); 1550 return (EINVAL); 1551 } 1552 i2cd.port_id = l; 1553 1554 p = str_to_number(argv[1], &l, NULL); 1555 if (*p || l > UCHAR_MAX) { 1556 warnx("invalid i2c device address \"%s\"", argv[1]); 1557 return (EINVAL); 1558 } 1559 i2cd.dev_addr = l; 1560 1561 p = str_to_number(argv[2], &l, NULL); 1562 if (*p || l > UCHAR_MAX) { 1563 warnx("invalid byte offset \"%s\"", argv[2]); 1564 return (EINVAL); 1565 } 1566 i2cd.offset = l; 1567 1568 if (argc == 4) { 1569 p = str_to_number(argv[3], &l, NULL); 1570 if (*p || l > sizeof(i2cd.data)) { 1571 warnx("invalid number of bytes \"%s\"", argv[3]); 1572 return (EINVAL); 1573 } 1574 i2cd.len = l; 1575 } else 1576 i2cd.len = 1; 1577 1578 rc = doit(CHELSIO_T4_GET_I2C, &i2cd); 1579 if (rc != 0) 1580 return (rc); 1581 1582 for (i = 0; i < i2cd.len; i++) 1583 printf("0x%x [%u]\n", i2cd.data[i], i2cd.data[i]); 1584 1585 return (0); 1586} 1587 1588static int 1589clearstats(int argc, const char *argv[]) 1590{ 1591 char *p; 1592 long l; 1593 uint32_t port; 1594 1595 if (argc != 1) { 1596 warnx("incorrect number of arguments."); 1597 return (EINVAL); 1598 } 1599 1600 p = str_to_number(argv[0], &l, NULL); 1601 if (*p) { 1602 warnx("invalid port id \"%s\"", argv[0]); 1603 return (EINVAL); 1604 } 1605 port = l; 1606 1607 return doit(CHELSIO_T4_CLEAR_STATS, &port); 1608} 1609 1610static int 1611run_cmd(int argc, const char *argv[]) 1612{ 1613 int rc = -1; 1614 const char *cmd = argv[0]; 1615 1616 /* command */ 1617 argc--; 1618 argv++; 1619 1620 if (!strcmp(cmd, "reg") || !strcmp(cmd, "reg32")) 1621 rc = register_io(argc, argv, 4); 1622 else if (!strcmp(cmd, "reg64")) 1623 rc = register_io(argc, argv, 8); 1624 else if (!strcmp(cmd, "regdump")) 1625 rc = dump_regs(argc, argv); 1626 else if (!strcmp(cmd, "filter")) 1627 rc = filter_cmd(argc, argv); 1628 else if (!strcmp(cmd, "context")) 1629 rc = get_sge_context(argc, argv); 1630 else if (!strcmp(cmd, "loadfw")) 1631 rc = loadfw(argc, argv); 1632 else if (!strcmp(cmd, "memdump")) 1633 rc = memdump(argc, argv); 1634 else if (!strcmp(cmd, "tcb")) 1635 rc = read_tcb(argc, argv); 1636 else if (!strcmp(cmd, "i2c")) 1637 rc = read_i2c(argc, argv); 1638 else if (!strcmp(cmd, "clearstats")) 1639 rc = clearstats(argc, argv); 1640 else { 1641 rc = EINVAL; 1642 warnx("invalid command \"%s\"", cmd); 1643 } 1644 1645 return (rc); 1646} 1647 1648#define MAX_ARGS 15 1649static int 1650run_cmd_loop(void) 1651{ 1652 int i, rc = 0; 1653 char buffer[128], *buf; 1654 const char *args[MAX_ARGS + 1]; 1655 1656 /* 1657 * Simple loop: displays a "> " prompt and processes any input as a 1658 * cxgbetool command. You're supposed to enter only the part after 1659 * "cxgbetool t4nexX". Use "quit" or "exit" to exit. 1660 */ 1661 for (;;) { 1662 fprintf(stdout, "> "); 1663 fflush(stdout); 1664 buf = fgets(buffer, sizeof(buffer), stdin); 1665 if (buf == NULL) { 1666 if (ferror(stdin)) { 1667 warn("stdin error"); 1668 rc = errno; /* errno from fgets */ 1669 } 1670 break; 1671 } 1672 1673 i = 0; 1674 while ((args[i] = strsep(&buf, " \t\n")) != NULL) { 1675 if (args[i][0] != 0 && ++i == MAX_ARGS) 1676 break; 1677 } 1678 args[i] = 0; 1679 1680 if (i == 0) 1681 continue; /* skip empty line */ 1682 1683 if (!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) 1684 break; 1685 1686 rc = run_cmd(i, args); 1687 } 1688 1689 /* rc normally comes from the last command (not including quit/exit) */ 1690 return (rc); 1691} 1692 1693int 1694main(int argc, const char *argv[]) 1695{ 1696 int rc = -1; 1697 1698 progname = argv[0]; 1699 1700 if (argc == 2) { 1701 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { 1702 usage(stdout); 1703 exit(0); 1704 } 1705 } 1706 1707 if (argc < 3) { 1708 usage(stderr); 1709 exit(EINVAL); 1710 } 1711 1712 nexus = argv[1]; 1713 1714 /* progname and nexus */ 1715 argc -= 2; 1716 argv += 2; 1717 1718 if (argc == 1 && !strcmp(argv[0], "stdio")) 1719 rc = run_cmd_loop(); 1720 else 1721 rc = run_cmd(argc, argv); 1722 1723 return (rc); 1724} 1725