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