]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - tools/tools/cxgbetool/cxgbetool.c
MFC r228491, r228561, r228594.
[FreeBSD/stable/8.git] / tools / tools / cxgbetool / cxgbetool.c
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 <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 <sys/mman.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <sys/stat.h>
44 #include <net/ethernet.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47
48 #include "t4_ioctl.h"
49
50 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
51
52 static const char *progname, *nexus;
53
54 struct reg_info {
55         const char *name;
56         uint32_t addr;
57         uint32_t len;
58 };
59
60 struct mod_regs {
61         const char *name;
62         const struct reg_info *ri;
63 };
64
65 #include "reg_defs_t4.c"
66 #include "reg_defs_t4vf.c"
67
68 static void
69 usage(FILE *fp)
70 {
71         fprintf(fp, "Usage: %s <nexus> [operation]\n", progname);
72         fprintf(fp,
73             "\tfilter <idx> [<param> <val>] ...    set a filter\n"
74             "\tfilter <idx> delete|clear           delete a filter\n"
75             "\tfilter list                         list all filters\n"
76             "\tfilter mode [<match>] ...           get/set global filter mode\n"
77             "\tloadfw <fw-image.bin>               install firmware\n"
78             "\tmemdump <addr> <len>                dump a memory range\n"
79             "\treg <address>[=<val>]               read/write register\n"
80             "\treg64 <address>[=<val>]             read/write 64 bit register\n"
81             "\tregdump [<module>] ...              dump registers\n"
82             "\tstdio                               interactive mode\n"
83             "\ttcb <tid>                           read TCB\n"
84             );
85 }
86
87 static inline unsigned int
88 get_card_vers(unsigned int version)
89 {
90         return (version & 0x3ff);
91 }
92
93 static int
94 real_doit(unsigned long cmd, void *data, const char *cmdstr)
95 {
96         static int fd = -1;
97         int rc = 0;
98
99         if (fd == -1) {
100                 char buf[64];
101
102                 snprintf(buf, sizeof(buf), "/dev/%s", nexus);
103                 if ((fd = open(buf, O_RDWR)) < 0) {
104                         warn("open(%s)", nexus);
105                         rc = errno;
106                         return (rc);
107                 }
108         }
109
110         rc = ioctl(fd, cmd, data);
111         if (rc < 0) {
112                 warn("%s", cmdstr);
113                 rc = errno;
114         }
115
116         return (rc);
117 }
118 #define doit(x, y) real_doit(x, y, #x)
119
120 static char *
121 str_to_number(const char *s, long *val, long long *vall)
122 {
123         char *p;
124
125         if (vall)
126                 *vall = strtoll(s, &p, 0);
127         else if (val)
128                 *val = strtol(s, &p, 0);
129         else
130                 p = NULL;
131
132         return (p);
133 }
134
135 static int
136 read_reg(long addr, int size, long long *val)
137 {
138         struct t4_reg reg;
139         int rc;
140
141         reg.addr = (uint32_t) addr;
142         reg.size = (uint32_t) size;
143         reg.val = 0;
144
145         rc = doit(CHELSIO_T4_GETREG, &reg);
146
147         *val = reg.val;
148
149         return (rc);
150 }
151
152 static int
153 write_reg(long addr, int size, long long val)
154 {
155         struct t4_reg reg;
156
157         reg.addr = (uint32_t) addr;
158         reg.size = (uint32_t) size;
159         reg.val = (uint64_t) val;
160
161         return doit(CHELSIO_T4_SETREG, &reg);
162 }
163
164 static int
165 register_io(int argc, const char *argv[], int size)
166 {
167         char *p, *v;
168         long addr;
169         long long val;
170         int w = 0, rc;
171
172         if (argc == 1) {
173                 /* <reg> OR <reg>=<value> */
174
175                 p = str_to_number(argv[0], &addr, NULL);
176                 if (*p) {
177                         if (*p != '=') {
178                                 warnx("invalid register \"%s\"", argv[0]);
179                                 return (EINVAL);
180                         }
181
182                         w = 1;
183                         v = p + 1;
184                         p = str_to_number(v, NULL, &val);
185
186                         if (*p) {
187                                 warnx("invalid value \"%s\"", v);
188                                 return (EINVAL);
189                         }
190                 }
191
192         } else if (argc == 2) {
193                 /* <reg> <value> */
194
195                 w = 1;
196
197                 p = str_to_number(argv[0], &addr, NULL);
198                 if (*p) {
199                         warnx("invalid register \"%s\"", argv[0]);
200                         return (EINVAL);
201                 }
202
203                 p = str_to_number(argv[1], NULL, &val);
204                 if (*p) {
205                         warnx("invalid value \"%s\"", argv[1]);
206                         return (EINVAL);
207                 }
208         } else {
209                 warnx("reg: invalid number of arguments (%d)", argc);
210                 return (EINVAL);
211         }
212
213         if (w)
214                 rc = write_reg(addr, size, val);
215         else {
216                 rc = read_reg(addr, size, &val);
217                 if (rc == 0)
218                         printf("0x%llx [%llu]\n", val, val);
219         }
220
221         return (rc);
222 }
223
224 static inline uint32_t
225 xtract(uint32_t val, int shift, int len)
226 {
227         return (val >> shift) & ((1 << len) - 1);
228 }
229
230 static int
231 dump_block_regs(const struct reg_info *reg_array, const uint32_t *regs)
232 {
233         uint32_t reg_val = 0;
234
235         for ( ; reg_array->name; ++reg_array)
236                 if (!reg_array->len) {
237                         reg_val = regs[reg_array->addr / 4];
238                         printf("[%#7x] %-47s %#-10x %u\n", reg_array->addr,
239                                reg_array->name, reg_val, reg_val);
240                 } else {
241                         uint32_t v = xtract(reg_val, reg_array->addr,
242                                             reg_array->len);
243
244                         printf("    %*u:%u %-47s %#-10x %u\n",
245                                reg_array->addr < 10 ? 3 : 2,
246                                reg_array->addr + reg_array->len - 1,
247                                reg_array->addr, reg_array->name, v, v);
248                 }
249
250         return (1);
251 }
252
253 static int
254 dump_regs_table(int argc, const char *argv[], const uint32_t *regs,
255     const struct mod_regs *modtab, int nmodules)
256 {
257         int i, j, match;
258
259         for (i = 0; i < argc; i++) {
260                 for (j = 0; j < nmodules; j++) {
261                         if (!strcmp(argv[i], modtab[j].name))
262                                 break;
263                 }
264
265                 if (j == nmodules) {
266                         warnx("invalid register block \"%s\"", argv[i]);
267                         fprintf(stderr, "\nAvailable blocks:");
268                         for ( ; nmodules; nmodules--, modtab++)
269                                 fprintf(stderr, " %s", modtab->name);
270                         fprintf(stderr, "\n");
271                         return (EINVAL);
272                 }
273         }
274
275         for ( ; nmodules; nmodules--, modtab++) {
276
277                 match = argc == 0 ? 1 : 0;
278                 for (i = 0; !match && i < argc; i++) {
279                         if (!strcmp(argv[i], modtab->name))
280                                 match = 1;
281                 }
282
283                 if (match)
284                         dump_block_regs(modtab->ri, regs);
285         }
286
287         return (0);
288 }
289
290 #define T4_MODREGS(name) { #name, t4_##name##_regs }
291 static int
292 dump_regs_t4(int argc, const char *argv[], const uint32_t *regs)
293 {
294         static struct mod_regs t4_mod[] = {
295                 T4_MODREGS(sge),
296                 { "pci", t4_pcie_regs },
297                 T4_MODREGS(dbg),
298                 T4_MODREGS(mc),
299                 T4_MODREGS(ma),
300                 { "edc0", t4_edc_0_regs },
301                 { "edc1", t4_edc_1_regs },
302                 T4_MODREGS(cim), 
303                 T4_MODREGS(tp),
304                 T4_MODREGS(ulp_rx),
305                 T4_MODREGS(ulp_tx),
306                 { "pmrx", t4_pm_rx_regs },
307                 { "pmtx", t4_pm_tx_regs },
308                 T4_MODREGS(mps),
309                 { "cplsw", t4_cpl_switch_regs },
310                 T4_MODREGS(smb),
311                 { "i2c", t4_i2cm_regs },
312                 T4_MODREGS(mi),
313                 T4_MODREGS(uart),
314                 T4_MODREGS(pmu), 
315                 T4_MODREGS(sf),
316                 T4_MODREGS(pl),
317                 T4_MODREGS(le),
318                 T4_MODREGS(ncsi),
319                 T4_MODREGS(xgmac)
320         };
321
322         return dump_regs_table(argc, argv, regs, t4_mod, ARRAY_SIZE(t4_mod));
323 }
324 #undef T4_MODREGS
325
326 static int
327 dump_regs_t4vf(int argc, const char *argv[], const uint32_t *regs)
328 {
329         static struct mod_regs t4vf_mod[] = {
330                 { "sge", t4vf_sge_regs },
331                 { "mps", t4vf_mps_regs },
332                 { "pl", t4vf_pl_regs },
333                 { "mbdata", t4vf_mbdata_regs },
334                 { "cim", t4vf_cim_regs },
335         };
336
337         return dump_regs_table(argc, argv, regs, t4vf_mod,
338             ARRAY_SIZE(t4vf_mod));
339 }
340
341 static int
342 dump_regs(int argc, const char *argv[])
343 {
344         int vers, revision, is_pcie, rc;
345         struct t4_regdump regs;
346
347         regs.data = calloc(1, T4_REGDUMP_SIZE);
348         if (regs.data == NULL) {
349                 warnc(ENOMEM, "regdump");
350                 return (ENOMEM);
351         }
352
353         regs.len = T4_REGDUMP_SIZE;
354         rc = doit(CHELSIO_T4_REGDUMP, &regs);
355         if (rc != 0)
356                 return (rc);
357
358         vers = get_card_vers(regs.version);
359         revision = (regs.version >> 10) & 0x3f;
360         is_pcie = (regs.version & 0x80000000) != 0;
361
362         if (vers == 4) {
363                 if (revision == 0x3f)
364                         rc = dump_regs_t4vf(argc, argv, regs.data);
365                 else
366                         rc = dump_regs_t4(argc, argv, regs.data);
367         } else {
368                 warnx("%s (type %d, rev %d) is not a T4 card.",
369                     nexus, vers, revision);
370                 return (ENOTSUP);
371         }
372
373         free(regs.data);
374         return (rc);
375 }
376
377 static void
378 do_show_info_header(uint32_t mode)
379 {
380         uint32_t i;
381
382         printf ("%4s %8s", "Idx", "Hits");
383         for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) {
384                 switch (mode & i) {
385                 case T4_FILTER_FCoE:
386                         printf (" FCoE");
387                         break;
388
389                 case T4_FILTER_PORT:
390                         printf (" Port");
391                         break;
392
393                 case T4_FILTER_VNIC:
394                         printf ("      vld:VNIC");
395                         break;
396
397                 case T4_FILTER_VLAN:
398                         printf ("      vld:VLAN");
399                         break;
400
401                 case T4_FILTER_IP_TOS:
402                         printf ("   TOS");
403                         break;
404
405                 case T4_FILTER_IP_PROTO:
406                         printf ("  Prot");
407                         break;
408
409                 case T4_FILTER_ETH_TYPE:
410                         printf ("   EthType");
411                         break;
412
413                 case T4_FILTER_MAC_IDX:
414                         printf ("  MACIdx");
415                         break;
416
417                 case T4_FILTER_MPS_HIT_TYPE:
418                         printf (" MPS");
419                         break;
420
421                 case T4_FILTER_IP_FRAGMENT:
422                         printf (" Frag");
423                         break;
424
425                 default:
426                         /* compressed filter field not enabled */
427                         break;
428                 }
429         }
430         printf(" %20s %20s %9s %9s %s\n",
431             "DIP", "SIP", "DPORT", "SPORT", "Action");
432 }
433
434 /*
435  * Parse an argument sub-vector as a { <parameter name> <value>[:<mask>] }
436  * ordered tuple.  If the parameter name in the argument sub-vector does not
437  * match the passed in parameter name, then a zero is returned for the
438  * function and no parsing is performed.  If there is a match, then the value
439  * and optional mask are parsed and returned in the provided return value
440  * pointers.  If no optional mask is specified, then a default mask of all 1s
441  * will be returned.
442  *
443  * An error in parsing the value[:mask] will result in an error message and
444  * program termination.
445  */
446 static int
447 parse_val_mask(const char *param, const char *args[], uint32_t *val,
448     uint32_t *mask)
449 {
450         char *p;
451
452         if (strcmp(param, args[0]) != 0)
453                 return (EINVAL);
454
455         *val = strtoul(args[1], &p, 0);
456         if (p > args[1]) {
457                 if (p[0] == 0) {
458                         *mask = ~0;
459                         return (0);
460                 }
461
462                 if (p[0] == ':' && p[1] != 0) {
463                         *mask = strtoul(p+1, &p, 0);
464                         if (p[0] == 0)
465                                 return (0);
466                 }
467         }
468
469         warnx("parameter \"%s\" has bad \"value[:mask]\" %s",
470             args[0], args[1]);
471
472         return (EINVAL);
473 }
474
475 /*
476  * Parse an argument sub-vector as a { <parameter name> <addr>[/<mask>] }
477  * ordered tuple.  If the parameter name in the argument sub-vector does not
478  * match the passed in parameter name, then a zero is returned for the
479  * function and no parsing is performed.  If there is a match, then the value
480  * and optional mask are parsed and returned in the provided return value
481  * pointers.  If no optional mask is specified, then a default mask of all 1s
482  * will be returned.
483  *
484  * The value return parameter "afp" is used to specify the expected address
485  * family -- IPv4 or IPv6 -- of the address[/mask] and return its actual
486  * format.  A passed in value of AF_UNSPEC indicates that either IPv4 or IPv6
487  * is acceptable; AF_INET means that only IPv4 addresses are acceptable; and
488  * AF_INET6 means that only IPv6 are acceptable.  AF_INET is returned for IPv4
489  * and AF_INET6 for IPv6 addresses, respectively.  IPv4 address/mask pairs are
490  * returned in the first four bytes of the address and mask return values with
491  * the address A.B.C.D returned with { A, B, C, D } returned in addresses { 0,
492  * 1, 2, 3}, respectively.
493  *
494  * An error in parsing the value[:mask] will result in an error message and
495  * program termination.
496  */
497 static int
498 parse_ipaddr(const char *param, const char *args[], int *afp, uint8_t addr[],
499     uint8_t mask[])
500 {
501         const char *colon, *afn;
502         char *slash;
503         uint8_t *m;
504         int af, ret;
505         unsigned int masksize;
506
507         /*
508          * Is this our parameter?
509          */
510         if (strcmp(param, args[0]) != 0)
511                 return (EINVAL);
512
513         /*
514          * Fundamental IPv4 versus IPv6 selection.
515          */
516         colon = strchr(args[1], ':');
517         if (!colon) {
518                 afn = "IPv4";
519                 af = AF_INET;
520                 masksize = 32;
521         } else {
522                 afn = "IPv6";
523                 af = AF_INET6;
524                 masksize = 128;
525         }
526         if (*afp == AF_UNSPEC)
527                 *afp = af;
528         else if (*afp != af) {
529                 warnx("address %s is not of expected family %s",
530                     args[1], *afp == AF_INET ? "IP" : "IPv6");
531                 return (EINVAL);
532         }
533
534         /*
535          * Parse address (temporarily stripping off any "/mask"
536          * specification).
537          */
538         slash = strchr(args[1], '/');
539         if (slash)
540                 *slash = 0;
541         ret = inet_pton(af, args[1], addr);
542         if (slash)
543                 *slash = '/';
544         if (ret <= 0) {
545                 warnx("Cannot parse %s %s address %s", param, afn, args[1]);
546                 return (EINVAL);
547         }
548
549         /*
550          * Parse optional mask specification.
551          */
552         if (slash) {
553                 char *p;
554                 unsigned int prefix = strtoul(slash + 1, &p, 10);
555
556                 if (p == slash + 1) {
557                         warnx("missing address prefix for %s", param);
558                         return (EINVAL);
559                 }
560                 if (*p) {
561                         warnx("%s is not a valid address prefix", slash + 1);
562                         return (EINVAL);
563                 }
564                 if (prefix > masksize) {
565                         warnx("prefix %u is too long for an %s address",
566                              prefix, afn);
567                         return (EINVAL);
568                 }
569                 memset(mask, 0, masksize / 8);
570                 masksize = prefix;
571         }
572
573         /*
574          * Fill in mask.
575          */
576         for (m = mask; masksize >= 8; m++, masksize -= 8)
577                 *m = ~0;
578         if (masksize)
579                 *m = ~0 << (8 - masksize);
580
581         return (0);
582 }
583
584 /*
585  * Parse an argument sub-vector as a { <parameter name> <value> } ordered
586  * tuple.  If the parameter name in the argument sub-vector does not match the
587  * passed in parameter name, then a zero is returned for the function and no
588  * parsing is performed.  If there is a match, then the value is parsed and
589  * returned in the provided return value pointer.
590  */
591 static int
592 parse_val(const char *param, const char *args[], uint32_t *val)
593 {
594         char *p;
595
596         if (strcmp(param, args[0]) != 0)
597                 return (EINVAL);
598
599         *val = strtoul(args[1], &p, 0);
600         if (p > args[1] && p[0] == 0)
601                 return (0);
602
603         warnx("parameter \"%s\" has bad \"value\" %s", args[0], args[1]);
604         return (EINVAL);
605 }
606
607 static void
608 filters_show_ipaddr(int type, uint8_t *addr, uint8_t *addrm)
609 {
610         int noctets, octet;
611
612         printf(" ");
613         if (type == 0) {
614                 noctets = 4;
615                 printf("%3s", " ");
616         } else
617         noctets = 16;
618
619         for (octet = 0; octet < noctets; octet++)
620                 printf("%02x", addr[octet]);
621         printf("/");
622         for (octet = 0; octet < noctets; octet++)
623                 printf("%02x", addrm[octet]);
624 }
625
626 static void
627 do_show_one_filter_info(struct t4_filter *t, uint32_t mode)
628 {
629         uint32_t i;
630
631         printf("%4d", t->idx);
632         if (t->hits == UINT64_MAX)
633                 printf(" %8s", "-");
634         else
635                 printf(" %8ju", t->hits);
636
637         /*
638          * Compressed header portion of filter.
639          */
640         for (i = T4_FILTER_FCoE; i <= T4_FILTER_IP_FRAGMENT; i <<= 1) {
641                 switch (mode & i) {
642                 case T4_FILTER_FCoE:
643                         printf("  %1d/%1d", t->fs.val.fcoe, t->fs.mask.fcoe);
644                         break;
645
646                 case T4_FILTER_PORT:
647                         printf("  %1d/%1d", t->fs.val.iport, t->fs.mask.iport);
648                         break;
649
650                 case T4_FILTER_VNIC:
651                         printf(" %1d:%1x:%02x/%1d:%1x:%02x",
652                             t->fs.val.vnic_vld, (t->fs.val.vnic >> 7) & 0x7,
653                             t->fs.val.vnic & 0x7f, t->fs.mask.vnic_vld,
654                             (t->fs.mask.vnic >> 7) & 0x7,
655                             t->fs.mask.vnic & 0x7f);
656                         break;
657
658                 case T4_FILTER_VLAN:
659                         printf(" %1d:%04x/%1d:%04x",
660                             t->fs.val.vlan_vld, t->fs.val.vlan,
661                             t->fs.mask.vlan_vld, t->fs.mask.vlan);
662                         break;
663
664                 case T4_FILTER_IP_TOS:
665                         printf(" %02x/%02x", t->fs.val.tos, t->fs.mask.tos);
666                         break;
667
668                 case T4_FILTER_IP_PROTO:
669                         printf(" %02x/%02x", t->fs.val.proto, t->fs.mask.proto);
670                         break;
671
672                 case T4_FILTER_ETH_TYPE:
673                         printf(" %04x/%04x", t->fs.val.ethtype,
674                             t->fs.mask.ethtype);
675                         break;
676
677                 case T4_FILTER_MAC_IDX:
678                         printf(" %03x/%03x", t->fs.val.macidx,
679                             t->fs.mask.macidx);
680                         break;
681
682                 case T4_FILTER_MPS_HIT_TYPE:
683                         printf(" %1x/%1x", t->fs.val.matchtype,
684                             t->fs.mask.matchtype);
685                         break;
686
687                 case T4_FILTER_IP_FRAGMENT:
688                         printf("  %1d/%1d", t->fs.val.frag, t->fs.mask.frag);
689                         break;
690
691                 default:
692                         /* compressed filter field not enabled */
693                         break;
694                 }
695         }
696
697         /*
698          * Fixed portion of filter.
699          */
700         filters_show_ipaddr(t->fs.type, t->fs.val.dip, t->fs.mask.dip);
701         filters_show_ipaddr(t->fs.type, t->fs.val.sip, t->fs.mask.sip);
702         printf(" %04x/%04x %04x/%04x",
703                  t->fs.val.dport, t->fs.mask.dport,
704                  t->fs.val.sport, t->fs.mask.sport);
705
706         /*
707          * Variable length filter action.
708          */
709         if (t->fs.action == FILTER_DROP)
710                 printf(" Drop");
711         else if (t->fs.action == FILTER_SWITCH) {
712                 printf(" Switch: port=%d", t->fs.eport);
713         if (t->fs.newdmac)
714                 printf(
715                         ", dmac=%02x:%02x:%02x:%02x:%02x:%02x "
716                         ", l2tidx=%d",
717                         t->fs.dmac[0], t->fs.dmac[1],
718                         t->fs.dmac[2], t->fs.dmac[3],
719                         t->fs.dmac[4], t->fs.dmac[5],
720                         t->l2tidx);
721         if (t->fs.newsmac)
722                 printf(
723                         ", smac=%02x:%02x:%02x:%02x:%02x:%02x "
724                         ", smtidx=%d",
725                         t->fs.smac[0], t->fs.smac[1],
726                         t->fs.smac[2], t->fs.smac[3],
727                         t->fs.smac[4], t->fs.smac[5],
728                         t->smtidx);
729         if (t->fs.newvlan == VLAN_REMOVE)
730                 printf(", vlan=none");
731         else if (t->fs.newvlan == VLAN_INSERT)
732                 printf(", vlan=insert(%x)", t->fs.vlan);
733         else if (t->fs.newvlan == VLAN_REWRITE)
734                 printf(", vlan=rewrite(%x)", t->fs.vlan);
735         } else {
736                 printf(" Pass: Q=");
737                 if (t->fs.dirsteer == 0) {
738                         printf("RSS");
739                         if (t->fs.maskhash)
740                                 printf("(TCB=hash)");
741                 } else {
742                         printf("%d", t->fs.iq);
743                         if (t->fs.dirsteerhash == 0)
744                                 printf("(QID)");
745                         else
746                                 printf("(hash)");
747                 }
748         }
749         if (t->fs.prio)
750                 printf(" Prio");
751         if (t->fs.rpttid)
752                 printf(" RptTID");
753         printf("\n");
754 }
755
756 static int
757 show_filters(void)
758 {
759         uint32_t mode = 0, header = 0;
760         struct t4_filter t;
761         int rc;
762
763         /* Get the global filter mode first */
764         rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode);
765         if (rc != 0)
766                 return (rc);
767
768         t.idx = 0;
769         for (t.idx = 0; ; t.idx++) {
770                 rc = doit(CHELSIO_T4_GET_FILTER, &t);
771                 if (rc != 0 || t.idx == 0xffffffff)
772                         break;
773
774                 if (!header) {
775                         do_show_info_header(mode);
776                         header = 1;
777                 }
778                 do_show_one_filter_info(&t, mode);
779         };
780
781         return (rc);
782 }
783
784 static int
785 get_filter_mode(void)
786 {
787         uint32_t mode = 0;
788         int rc;
789
790         rc = doit(CHELSIO_T4_GET_FILTER_MODE, &mode);
791         if (rc != 0)
792                 return (rc);
793
794         if (mode & T4_FILTER_IPv4)
795                 printf("ipv4 ");
796
797         if (mode & T4_FILTER_IPv6)
798                 printf("ipv6 ");
799
800         if (mode & T4_FILTER_IP_SADDR)
801                 printf("sip ");
802         
803         if (mode & T4_FILTER_IP_DADDR)
804                 printf("dip ");
805
806         if (mode & T4_FILTER_IP_SPORT)
807                 printf("sport ");
808
809         if (mode & T4_FILTER_IP_DPORT)
810                 printf("dport ");
811
812         if (mode & T4_FILTER_MPS_HIT_TYPE)
813                 printf("matchtype ");
814
815         if (mode & T4_FILTER_MAC_IDX)
816                 printf("macidx ");
817
818         if (mode & T4_FILTER_ETH_TYPE)
819                 printf("ethtype ");
820
821         if (mode & T4_FILTER_IP_PROTO)
822                 printf("proto ");
823
824         if (mode & T4_FILTER_IP_TOS)
825                 printf("tos ");
826
827         if (mode & T4_FILTER_VLAN)
828                 printf("vlan ");
829
830         if (mode & T4_FILTER_VNIC)
831                 printf("vnic ");
832
833         if (mode & T4_FILTER_PORT)
834                 printf("iport ");
835
836         if (mode & T4_FILTER_FCoE)
837                 printf("fcoe ");
838
839         printf("\n");
840
841         return (0);
842 }
843
844 static int
845 set_filter_mode(int argc, const char *argv[])
846 {
847         uint32_t mode = 0;
848
849         for (; argc; argc--, argv++) {
850                 if (!strcmp(argv[0], "matchtype"))
851                         mode |= T4_FILTER_MPS_HIT_TYPE;
852
853                 if (!strcmp(argv[0], "macidx"))
854                         mode |= T4_FILTER_MAC_IDX;
855
856                 if (!strcmp(argv[0], "ethtype"))
857                         mode |= T4_FILTER_ETH_TYPE;
858
859                 if (!strcmp(argv[0], "proto"))
860                         mode |= T4_FILTER_IP_PROTO;
861
862                 if (!strcmp(argv[0], "tos"))
863                         mode |= T4_FILTER_IP_TOS;
864
865                 if (!strcmp(argv[0], "vlan"))
866                         mode |= T4_FILTER_VLAN;
867
868                 if (!strcmp(argv[0], "ovlan") ||
869                     !strcmp(argv[0], "vnic"))
870                         mode |= T4_FILTER_VNIC;
871
872                 if (!strcmp(argv[0], "iport"))
873                         mode |= T4_FILTER_PORT;
874
875                 if (!strcmp(argv[0], "fcoe"))
876                         mode |= T4_FILTER_FCoE;
877         }
878
879         return doit(CHELSIO_T4_SET_FILTER_MODE, &mode);
880 }
881
882 static int
883 del_filter(uint32_t idx)
884 {
885         struct t4_filter t;
886
887         t.idx = idx;
888
889         return doit(CHELSIO_T4_DEL_FILTER, &t);
890 }
891
892 static int
893 set_filter(uint32_t idx, int argc, const char *argv[])
894 {
895         int af = AF_UNSPEC, start_arg = 0;
896         struct t4_filter t;
897
898         if (argc < 2) {
899                 warnc(EINVAL, "%s", __func__);
900                 return (EINVAL);
901         };
902         bzero(&t, sizeof (t));
903         t.idx = idx;
904
905         for (start_arg = 0; start_arg + 2 <= argc; start_arg += 2) {
906                 const char **args = &argv[start_arg];
907                 uint32_t val, mask;
908
909                 if (!strcmp(argv[start_arg], "type")) {
910                         int newaf;
911                         if (!strcasecmp(argv[start_arg + 1], "ipv4"))
912                                 newaf = AF_INET;
913                         else if (!strcasecmp(argv[start_arg + 1], "ipv6"))
914                                 newaf = AF_INET6;
915                         else {
916                                 warnx("invalid type \"%s\"; "
917                                     "must be one of \"ipv4\" or \"ipv6\"",
918                                     argv[start_arg + 1]);
919                                 return (EINVAL);
920                         }
921
922                         if (af != AF_UNSPEC && af != newaf) {
923                                 warnx("conflicting IPv4/IPv6 specifications.");
924                                 return (EINVAL);
925                         }
926                         af = newaf;
927                 } else if (!parse_val_mask("fcoe", args, &val, &mask)) {
928                         t.fs.val.fcoe = val;
929                         t.fs.mask.fcoe = mask;
930                 } else if (!parse_val_mask("iport", args, &val, &mask)) {
931                         t.fs.val.iport = val;
932                         t.fs.mask.iport = mask;
933                 } else if (!parse_val_mask("ovlan", args, &val, &mask)) {
934                         t.fs.val.vnic = val;
935                         t.fs.mask.vnic = mask;
936                         t.fs.val.vnic_vld = 1;
937                         t.fs.mask.vnic_vld = 1;
938                 } else if (!parse_val_mask("vnic", args, &val, &mask)) {
939                         t.fs.val.vnic = val;
940                         t.fs.mask.vnic = mask;
941                         t.fs.val.vnic_vld = 1;
942                         t.fs.mask.vnic_vld = 1;
943                 } else if (!parse_val_mask("vlan", args, &val, &mask)) {
944                         t.fs.val.vlan = val;
945                         t.fs.mask.vlan = mask;
946                         t.fs.val.vlan_vld = 1;
947                         t.fs.mask.vlan_vld = 1;
948                 } else if (!parse_val_mask("tos", args, &val, &mask)) {
949                         t.fs.val.tos = val;
950                         t.fs.mask.tos = mask;
951                 } else if (!parse_val_mask("proto", args, &val, &mask)) {
952                         t.fs.val.proto = val;
953                         t.fs.mask.proto = mask;
954                 } else if (!parse_val_mask("ethtype", args, &val, &mask)) {
955                         t.fs.val.ethtype = val;
956                         t.fs.mask.ethtype = mask;
957                 } else if (!parse_val_mask("macidx", args, &val, &mask)) {
958                         t.fs.val.macidx = val;
959                         t.fs.mask.macidx = mask;
960                 } else if (!parse_val_mask("matchtype", args, &val, &mask)) {
961                         t.fs.val.matchtype = val;
962                         t.fs.mask.matchtype = mask;
963                 } else if (!parse_val_mask("frag", args, &val, &mask)) {
964                         t.fs.val.frag = val;
965                         t.fs.mask.frag = mask;
966                 } else if (!parse_val_mask("dport", args, &val, &mask)) {
967                         t.fs.val.dport = val;
968                         t.fs.mask.dport = mask;
969                 } else if (!parse_val_mask("sport", args, &val, &mask)) {
970                         t.fs.val.sport = val;
971                         t.fs.mask.sport = mask;
972                 } else if (!parse_ipaddr("dip", args, &af, t.fs.val.dip,
973                     t.fs.mask.dip)) {
974                         /* nada */;
975                 } else if (!parse_ipaddr("sip", args, &af, t.fs.val.sip,
976                     t.fs.mask.sip)) {
977                         /* nada */;
978                 } else if (!strcmp(argv[start_arg], "action")) {
979                         if (!strcmp(argv[start_arg + 1], "pass"))
980                                 t.fs.action = FILTER_PASS;
981                         else if (!strcmp(argv[start_arg + 1], "drop"))
982                                 t.fs.action = FILTER_DROP;
983                         else if (!strcmp(argv[start_arg + 1], "switch"))
984                                 t.fs.action = FILTER_SWITCH;
985                         else {
986                                 warnx("invalid action \"%s\"; must be one of"
987                                      " \"pass\", \"drop\" or \"switch\"",
988                                      argv[start_arg + 1]);
989                                 return (EINVAL);
990                         }
991                 } else if (!parse_val("hitcnts", args, &val)) {
992                         t.fs.hitcnts = val;
993                 } else if (!parse_val("prio", args, &val)) {
994                         t.fs.prio = val;
995                 } else if (!parse_val("rpttid", args, &val)) {
996                         t.fs.rpttid = 1;
997                 } else if (!parse_val("queue", args, &val)) {
998                         t.fs.dirsteer = 1;
999                         t.fs.iq = val;
1000                 } else if (!parse_val("tcbhash", args, &val)) {
1001                         t.fs.maskhash = 1;
1002                         t.fs.dirsteerhash = 1;
1003                 } else if (!parse_val("eport", args, &val)) {
1004                         t.fs.eport = val;
1005                 } else if (!strcmp(argv[start_arg], "dmac")) {
1006                         struct ether_addr *daddr;
1007
1008                         daddr = ether_aton(argv[start_arg + 1]);
1009                         if (daddr == NULL) {
1010                                 warnx("invalid dmac address \"%s\"",
1011                                     argv[start_arg + 1]);
1012                                 return (EINVAL);
1013                         }
1014                         memcpy(t.fs.dmac, daddr, ETHER_ADDR_LEN);
1015                         t.fs.newdmac = 1;
1016                 } else if (!strcmp(argv[start_arg], "smac")) {
1017                         struct ether_addr *saddr;
1018
1019                         saddr = ether_aton(argv[start_arg + 1]);
1020                         if (saddr == NULL) {
1021                                 warnx("invalid smac address \"%s\"",
1022                                     argv[start_arg + 1]);
1023                                 return (EINVAL);
1024                         }
1025                         memcpy(t.fs.smac, saddr, ETHER_ADDR_LEN);
1026                         t.fs.newsmac = 1;
1027                 } else if (!strcmp(argv[start_arg], "vlan")) {
1028                         char *p;
1029                         if (!strcmp(argv[start_arg + 1], "none")) {
1030                                 t.fs.newvlan = VLAN_REMOVE;
1031                         } else if (argv[start_arg + 1][0] == '=') {
1032                                 t.fs.newvlan = VLAN_REWRITE;
1033                         } else if (argv[start_arg + 1][0] == '+') {
1034                                 t.fs.newvlan = VLAN_INSERT;
1035                         } else {
1036                                 warnx("unknown vlan parameter \"%s\"; must"
1037                                      " be one of \"none\", \"=<vlan>\" or"
1038                                      " \"+<vlan>\"", argv[start_arg + 1]);
1039                                 return (EINVAL);
1040                         }
1041                         if (t.fs.newvlan == VLAN_REWRITE ||
1042                             t.fs.newvlan == VLAN_INSERT) {
1043                                 t.fs.vlan = strtoul(argv[start_arg + 1] + 1,
1044                                     &p, 0);
1045                                 if (p == argv[start_arg + 1] + 1 || p[0] != 0) {
1046                                         warnx("invalid vlan \"%s\"",
1047                                              argv[start_arg + 1]);
1048                                         return (EINVAL);
1049                                 }
1050                         }
1051                 } else {
1052                         warnx("invalid parameter \"%s\"", argv[start_arg]);
1053                         return (EINVAL);
1054                 }
1055         }
1056         if (start_arg != argc) {
1057                 warnx("no value for \"%s\"", argv[start_arg]);
1058                 return (EINVAL);
1059         }
1060
1061         /*
1062          * Check basic sanity of option combinations.
1063          */
1064         if (t.fs.action != FILTER_SWITCH &&
1065             (t.fs.eport || t.fs.newdmac || t.fs.newsmac || t.fs.newvlan)) {
1066                 warnx("prio, port dmac, smac and vlan only make sense with"
1067                      " \"action switch\"");
1068                 return (EINVAL);
1069         }
1070         if (t.fs.action != FILTER_PASS &&
1071             (t.fs.rpttid || t.fs.dirsteer || t.fs.maskhash)) {
1072                 warnx("rpttid, queue and tcbhash don't make sense with"
1073                      " action \"drop\" or \"switch\"");
1074                 return (EINVAL);
1075         }
1076
1077         t.fs.type = (af == AF_INET6 ? 1 : 0); /* default IPv4 */
1078         return doit(CHELSIO_T4_SET_FILTER, &t);
1079 }
1080
1081 static int
1082 filter_cmd(int argc, const char *argv[])
1083 {
1084         long long val;
1085         uint32_t idx;
1086         char *s;
1087
1088         if (argc == 0) {
1089                 warnx("filter: no arguments.");
1090                 return (EINVAL);
1091         };
1092
1093         /* list */
1094         if (strcmp(argv[0], "list") == 0) {
1095                 if (argc != 1)
1096                         warnx("trailing arguments after \"list\" ignored.");
1097
1098                 return show_filters();
1099         }
1100
1101         /* mode */
1102         if (argc == 1 && strcmp(argv[0], "mode") == 0)
1103                 return get_filter_mode();
1104
1105         /* mode <mode> */
1106         if (strcmp(argv[0], "mode") == 0)
1107                 return set_filter_mode(argc - 1, argv + 1);
1108
1109         /* <idx> ... */
1110         s = str_to_number(argv[0], NULL, &val);
1111         if (*s || val > 0xffffffffU) {
1112                 warnx("\"%s\" is neither an index nor a filter subcommand.",
1113                     argv[0]);
1114                 return (EINVAL);
1115         }
1116         idx = (uint32_t) val;
1117
1118         /* <idx> delete|clear */
1119         if (argc == 2 &&
1120             (strcmp(argv[1], "delete") == 0 || strcmp(argv[1], "clear") == 0)) {
1121                 return del_filter(idx);
1122         }
1123
1124         /* <idx> [<param> <val>] ... */
1125         return set_filter(idx, argc - 1, argv + 1);
1126 }
1127
1128 static int
1129 loadfw(int argc, const char *argv[])
1130 {
1131         int rc, fd;
1132         struct t4_data data = {0};
1133         const char *fname = argv[0];
1134         struct stat st = {0};
1135
1136         if (argc != 1) {
1137                 warnx("loadfw: incorrect number of arguments.");
1138                 return (EINVAL);
1139         }
1140
1141         fd = open(fname, O_RDONLY);
1142         if (fd < 0) {
1143                 warn("open(%s)", fname);
1144                 return (errno);
1145         }
1146
1147         if (fstat(fd, &st) < 0) {
1148                 warn("fstat");
1149                 close(fd);
1150                 return (errno);
1151         }
1152
1153         data.len = st.st_size;
1154         data.data = mmap(0, data.len, PROT_READ, 0, fd, 0);
1155         if (data.data == MAP_FAILED) {
1156                 warn("mmap");
1157                 close(fd);
1158                 return (errno);
1159         }
1160
1161         rc = doit(CHELSIO_T4_LOAD_FW, &data);
1162         munmap(data.data, data.len);
1163         close(fd);
1164         return (rc);
1165 }
1166
1167 static int
1168 read_mem(uint32_t addr, uint32_t len, void (*output)(uint32_t *, uint32_t))
1169 {
1170         int rc;
1171         struct t4_mem_range mr;
1172
1173         mr.addr = addr;
1174         mr.len = len;
1175         mr.data = malloc(mr.len);
1176
1177         if (mr.data == 0) {
1178                 warn("read_mem: malloc");
1179                 return (errno);
1180         }
1181
1182         rc = doit(CHELSIO_T4_GET_MEM, &mr);
1183         if (rc != 0)
1184                 goto done;
1185
1186         if (output)
1187                 (*output)(mr.data, mr.len);
1188 done:
1189         free(mr.data);
1190         return (rc);
1191 }
1192
1193 /*
1194  * Display memory as list of 'n' 4-byte values per line.
1195  */
1196 static void
1197 show_mem(uint32_t *buf, uint32_t len)
1198 {
1199         const char *s;
1200         int i, n = 8;
1201
1202         while (len) {
1203                 for (i = 0; len && i < n; i++, buf++, len -= 4) {
1204                         s = i ? " " : "";
1205                         printf("%s%08x", s, htonl(*buf));
1206                 }
1207                 printf("\n");
1208         }
1209 }
1210
1211 static int
1212 memdump(int argc, const char *argv[])
1213 {
1214         char *p;
1215         long l;
1216         uint32_t addr, len;
1217
1218         if (argc != 2) {
1219                 warnx("incorrect number of arguments.");
1220                 return (EINVAL);
1221         }
1222
1223         p = str_to_number(argv[0], &l, NULL);
1224         if (*p) {
1225                 warnx("invalid address \"%s\"", argv[0]);
1226                 return (EINVAL);
1227         }
1228         addr = l;
1229
1230         p = str_to_number(argv[1], &l, NULL);
1231         if (*p) {
1232                 warnx("memdump: invalid length \"%s\"", argv[1]);
1233                 return (EINVAL);
1234         }
1235         len = l;
1236
1237         return (read_mem(addr, len, show_mem));
1238 }
1239
1240 /*
1241  * Display TCB as list of 'n' 4-byte values per line.
1242  */
1243 static void
1244 show_tcb(uint32_t *buf, uint32_t len)
1245 {
1246         const char *s;
1247         int i, n = 8;
1248
1249         while (len) {
1250                 for (i = 0; len && i < n; i++, buf++, len -= 4) {
1251                         s = i ? " " : "";
1252                         printf("%s%08x", s, htonl(*buf));
1253                 }
1254                 printf("\n");
1255         }
1256 }
1257
1258 #define A_TP_CMM_TCB_BASE 0x7d10
1259 #define TCB_SIZE 128
1260 static int
1261 read_tcb(int argc, const char *argv[])
1262 {
1263         char *p;
1264         long l;
1265         long long val;
1266         unsigned int tid;
1267         uint32_t addr;
1268         int rc;
1269
1270         if (argc != 1) {
1271                 warnx("incorrect number of arguments.");
1272                 return (EINVAL);
1273         }
1274
1275         p = str_to_number(argv[0], &l, NULL);
1276         if (*p) {
1277                 warnx("invalid tid \"%s\"", argv[0]);
1278                 return (EINVAL);
1279         }
1280         tid = l;
1281
1282         rc = read_reg(A_TP_CMM_TCB_BASE, 4, &val);
1283         if (rc != 0)
1284                 return (rc);
1285
1286         addr = val + tid * TCB_SIZE;
1287
1288         return (read_mem(addr, TCB_SIZE, show_tcb));
1289 }
1290
1291 static int
1292 run_cmd(int argc, const char *argv[])
1293 {
1294         int rc = -1;
1295         const char *cmd = argv[0];
1296
1297         /* command */
1298         argc--;
1299         argv++;
1300
1301         if (!strcmp(cmd, "reg") || !strcmp(cmd, "reg32"))
1302                 rc = register_io(argc, argv, 4);
1303         else if (!strcmp(cmd, "reg64"))
1304                 rc = register_io(argc, argv, 8);
1305         else if (!strcmp(cmd, "regdump"))
1306                 rc = dump_regs(argc, argv);
1307         else if (!strcmp(cmd, "filter"))
1308                 rc = filter_cmd(argc, argv);
1309         else if (!strcmp(cmd, "loadfw"))
1310                 rc = loadfw(argc, argv);
1311         else if (!strcmp(cmd, "memdump"))
1312                 rc = memdump(argc, argv);
1313         else if (!strcmp(cmd, "tcb"))
1314                 rc = read_tcb(argc, argv);
1315         else {
1316                 rc = EINVAL;
1317                 warnx("invalid command \"%s\"", cmd);
1318         }
1319
1320         return (rc);
1321 }
1322
1323 #define MAX_ARGS 15
1324 static int
1325 run_cmd_loop(void)
1326 {
1327         int i, rc = 0;
1328         char buffer[128], *buf;
1329         const char *args[MAX_ARGS + 1];
1330
1331         /*
1332          * Simple loop: displays a "> " prompt and processes any input as a
1333          * cxgbetool command.  You're supposed to enter only the part after
1334          * "cxgbetool t4nexX".  Use "quit" or "exit" to exit.
1335          */
1336         for (;;) {
1337                 fprintf(stdout, "> ");
1338                 fflush(stdout);
1339                 buf = fgets(buffer, sizeof(buffer), stdin);
1340                 if (buf == NULL) {
1341                         if (ferror(stdin)) {
1342                                 warn("stdin error");
1343                                 rc = errno;     /* errno from fgets */
1344                         }
1345                         break;
1346                 }
1347
1348                 i = 0;
1349                 while ((args[i] = strsep(&buf, " \t\n")) != NULL) {
1350                         if (args[i][0] != 0 && ++i == MAX_ARGS)
1351                                 break;
1352                 }
1353                 args[i] = 0;
1354
1355                 if (i == 0)
1356                         continue;       /* skip empty line */
1357
1358                 if (!strcmp(args[0], "quit") || !strcmp(args[0], "exit"))
1359                         break;
1360
1361                 rc = run_cmd(i, args);
1362         }
1363
1364         /* rc normally comes from the last command (not including quit/exit) */
1365         return (rc);
1366 }
1367
1368 int
1369 main(int argc, const char *argv[])
1370 {
1371         int rc = -1;
1372
1373         progname = argv[0];
1374
1375         if (argc == 2) {
1376                 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
1377                         usage(stdout);
1378                         exit(0);
1379                 }
1380         }
1381
1382         if (argc < 3) {
1383                 usage(stderr);
1384                 exit(EINVAL);
1385         }
1386
1387         nexus = argv[1];
1388
1389         /* progname and nexus */
1390         argc -= 2;
1391         argv += 2;
1392
1393         if (argc == 1 && !strcmp(argv[0], "stdio"))
1394                 rc = run_cmd_loop();
1395         else
1396                 rc = run_cmd(argc, argv);
1397
1398         return (rc);
1399 }