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