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