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