cxgbtool.c revision 207643
1/**************************************************************************
2
3Copyright (c) 2007-2010, Chelsio Inc.
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
8
9 1. Redistributions of source code must retain the above copyright notice,
10    this list of conditions and the following disclaimer.
11
12 2. Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15
16 3. Neither the name of the Chelsio Corporation nor the names of its
17    contributors may be used to endorse or promote products derived from
18    this software without specific prior written permission.
19
20THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30POSSIBILITY OF SUCH DAMAGE.
31
32
33***************************************************************************/
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/usr.sbin/cxgbtool/cxgbtool.c 207643 2010-05-05 00:41:40Z np $");
36
37#include <stdlib.h>
38#include <stdio.h>
39#include <stdint.h>
40#include <string.h>
41#include <unistd.h>
42#include <fcntl.h>
43#include <err.h>
44#include <errno.h>
45#include <inttypes.h>
46#include <sys/param.h>
47#include <sys/time.h>
48#include <sys/ioctl.h>
49#include <sys/socket.h>
50
51#include <netinet/in.h>
52#include <arpa/inet.h>
53
54#include <net/if.h>
55#include <net/if_var.h>
56#include <net/if_types.h>
57#include <sys/endian.h>
58
59#define NMTUS 16
60#define TCB_SIZE 128
61#define TCB_WORDS (TCB_SIZE / 4)
62#define PROTO_SRAM_LINES 128
63#define PROTO_SRAM_LINE_BITS 132
64#define PROTO_SRAM_LINE_NIBBLES (132 / 4)
65#define PROTO_SRAM_SIZE (PROTO_SRAM_LINE_NIBBLES * PROTO_SRAM_LINES / 2)
66#define PROTO_SRAM_EEPROM_ADDR 4096
67
68#include <cxgb_ioctl.h>
69#include <common/cxgb_regs.h>
70#include "version.h"
71
72struct reg_info {
73        const char *name;
74        uint16_t addr;
75        uint16_t len;
76};
77
78
79#include "reg_defs.c"
80#if defined(CONFIG_T3_REGS)
81# include "reg_defs_t3.c"
82# include "reg_defs_t3b.c"
83# include "reg_defs_t3c.c"
84#endif
85
86static const char *progname;
87
88static void
89usage(FILE *fp)
90{
91	fprintf(fp, "Usage: %s <interface> [operation]\n", progname);
92	fprintf(fp,
93	    	"\tclearstats                          clear MAC statistics\n"
94		"\tcontext <type> <id>                 show an SGE context\n"
95		"\tdesc <qset> <queue> <idx> [<cnt>]   dump SGE descriptors\n"
96		"\tfilter <idx> [<param> <val>] ...    set a filter\n"
97		"\tfilter <idx> delete|clear           delete a filter\n"
98		"\tfilter list                         list all filters\n"
99		"\tioqs                                dump uP IOQs\n"
100		"\tla                                  dump uP logic analyzer info\n"
101		"\tloadboot <boot image>               download boot image\n"
102		"\tloadfw <FW image>                   download firmware\n"
103		"\tmdio <phy_addr> <mmd_addr>\n"
104	        "\t     <reg_addr> [<val>]             read/write MDIO register\n"
105		"\tmemdump cm|tx|rx <addr> <len>       dump a mem range\n"
106		"\tmeminfo                             show memory info\n"
107		"\tmtus [<mtu0>...<mtuN>]              read/write MTU table\n"
108		"\tpktsched port <idx> <min> <max>     set TX port scheduler params\n"
109		"\tpktsched tunnelq <idx> <max>\n"
110		"\t         <binding>                  set TX tunnelq scheduler params\n"
111		"\tpktsched tx <idx>\n"
112	        "\t         [<param> <val>] ...        set Tx HW scheduler\n"
113		"\tpm [<TX page spec> <RX page spec>]  read/write PM config\n"
114		"\tproto                               read proto SRAM\n"
115		"\tqset                                read qset parameters\n"
116		"\tqsets                               read # of qsets\n"
117		"\treg <address>[=<val>]               read/write register\n"
118		"\tregdump [<module>]                  dump registers\n"
119		"\ttcamdump <address> <count>          show TCAM contents\n"
120		"\ttcb <index>                         read TCB\n"
121		"\ttrace tx|rx|all on|off [not]\n"
122	        "\t      [<param> <val>[:<mask>]] ...  write trace parameters\n"
123		);
124	exit(fp == stderr ? 1 : 0);
125}
126
127static int
128doit(const char *iff_name, unsigned long cmd, void *data)
129{
130	static int fd = 0;
131
132	if (fd == 0) {
133		char buf[64];
134		snprintf(buf, 64, "/dev/%s", iff_name);
135
136		if ((fd = open(buf, O_RDWR)) < 0)
137			return -1;
138	}
139
140	return ioctl(fd, cmd, data) < 0 ? -1 : 0;
141}
142
143static int
144get_int_arg(const char *s, uint32_t *valp)
145{
146	char *p;
147
148	*valp = strtoul(s, &p, 0);
149	if (*p) {
150		warnx("bad parameter \"%s\"", s);
151		return -1;
152	}
153	return 0;
154}
155
156static uint32_t
157read_reg(const char *iff_name, uint32_t addr)
158{
159	struct ch_reg reg;
160
161	reg.addr = addr;
162
163	if (doit(iff_name, CHELSIO_GETREG, &reg) < 0)
164		err(1, "register read");
165	return reg.val;
166}
167
168static void
169write_reg(const char *iff_name, uint32_t addr, uint32_t val)
170{
171	struct ch_reg ch_reg;
172
173	ch_reg.addr = addr;
174	ch_reg.val = val;
175
176	if (doit(iff_name, CHELSIO_SETREG, &ch_reg) < 0)
177		err(1, "register write");
178}
179
180static int
181register_io(int argc, char *argv[], int start_arg,
182		       const char *iff_name)
183{
184	char *p;
185	uint32_t addr, val = 0, w = 0;
186
187	if (argc != start_arg + 1) return -1;
188
189	addr = strtoul(argv[start_arg], &p, 0);
190	if (p == argv[start_arg]) return -1;
191	if (*p == '=' && p[1]) {
192		val = strtoul(p + 1, &p, 0);
193		w = 1;
194	}
195	if (*p) {
196		warnx("bad parameter \"%s\"", argv[start_arg]);
197		return -1;
198	}
199
200	if (w)
201		write_reg(iff_name, addr, val);
202	else {
203		val = read_reg(iff_name, addr);
204		printf("%#x [%u]\n", val, val);
205	}
206	return 0;
207}
208
209static int
210mdio_io(int argc, char *argv[], int start_arg, const char *iff_name)
211{
212        struct ch_mii_data p;
213        unsigned int cmd, phy_addr, reg, mmd, val;
214
215        if (argc == start_arg + 3)
216                cmd = CHELSIO_GET_MIIREG;
217        else if (argc == start_arg + 4)
218                cmd = CHELSIO_SET_MIIREG;
219        else
220                return -1;
221
222        if (get_int_arg(argv[start_arg], &phy_addr) ||
223            get_int_arg(argv[start_arg + 1], &mmd) ||
224            get_int_arg(argv[start_arg + 2], &reg) ||
225            (cmd == CHELSIO_SET_MIIREG && get_int_arg(argv[start_arg + 3], &val)))
226                return -1;
227
228        p.phy_id  = phy_addr | (mmd << 8);
229        p.reg_num = reg;
230        p.val_in  = val;
231
232        if (doit(iff_name, cmd, &p) < 0)
233                err(1, "MDIO %s", cmd == CHELSIO_GET_MIIREG ? "read" : "write");
234        if (cmd == CHELSIO_GET_MIIREG)
235                printf("%#x [%u]\n", p.val_out, p.val_out);
236        return 0;
237}
238
239static inline
240uint32_t xtract(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, uint32_t *regs)
247{
248	uint32_t reg_val = 0; // silence compiler warning
249
250	for ( ; reg_array->name; ++reg_array)
251		if (!reg_array->len) {
252			reg_val = regs[reg_array->addr / 4];
253			printf("[%#5x] %-40s %#-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("        %-40s %#-10x [%u]\n", reg_array->name,
260			       v, v);
261		}
262	return 1;
263}
264
265static int
266dump_regs_t2(int argc, char *argv[], int start_arg, uint32_t *regs)
267{
268	int match = 0;
269	char *block_name = NULL;
270
271	if (argc == start_arg + 1)
272		block_name = argv[start_arg];
273	else if (argc != start_arg)
274		return -1;
275
276	if (!block_name || !strcmp(block_name, "sge"))
277		match += dump_block_regs(sge_regs, regs);
278	if (!block_name || !strcmp(block_name, "mc3"))
279		match += dump_block_regs(mc3_regs, regs);
280	if (!block_name || !strcmp(block_name, "mc4"))
281		match += dump_block_regs(mc4_regs, regs);
282	if (!block_name || !strcmp(block_name, "tpi"))
283		match += dump_block_regs(tpi_regs, regs);
284	if (!block_name || !strcmp(block_name, "tp"))
285		match += dump_block_regs(tp_regs, regs);
286	if (!block_name || !strcmp(block_name, "rat"))
287		match += dump_block_regs(rat_regs, regs);
288	if (!block_name || !strcmp(block_name, "cspi"))
289		match += dump_block_regs(cspi_regs, regs);
290	if (!block_name || !strcmp(block_name, "espi"))
291		match += dump_block_regs(espi_regs, regs);
292	if (!block_name || !strcmp(block_name, "ulp"))
293		match += dump_block_regs(ulp_regs, regs);
294	if (!block_name || !strcmp(block_name, "pl"))
295		match += dump_block_regs(pl_regs, regs);
296	if (!block_name || !strcmp(block_name, "mc5"))
297		match += dump_block_regs(mc5_regs, regs);
298	if (!match)
299		errx(1, "unknown block \"%s\"", block_name);
300	return 0;
301}
302
303#if defined(CONFIG_T3_REGS)
304static int
305dump_regs_t3(int argc, char *argv[], int start_arg, uint32_t *regs, int is_pcie)
306{
307	int match = 0;
308	char *block_name = NULL;
309
310	if (argc == start_arg + 1)
311		block_name = argv[start_arg];
312	else if (argc != start_arg)
313		return -1;
314
315	if (!block_name || !strcmp(block_name, "sge"))
316		match += dump_block_regs(sge3_regs, regs);
317	if (!block_name || !strcmp(block_name, "pci"))
318		match += dump_block_regs(is_pcie ? pcie0_regs : pcix1_regs,
319					 regs);
320	if (!block_name || !strcmp(block_name, "t3dbg"))
321		match += dump_block_regs(t3dbg_regs, regs);
322	if (!block_name || !strcmp(block_name, "pmrx"))
323		match += dump_block_regs(mc7_pmrx_regs, regs);
324	if (!block_name || !strcmp(block_name, "pmtx"))
325		match += dump_block_regs(mc7_pmtx_regs, regs);
326	if (!block_name || !strcmp(block_name, "cm"))
327		match += dump_block_regs(mc7_cm_regs, regs);
328	if (!block_name || !strcmp(block_name, "cim"))
329		match += dump_block_regs(cim_regs, regs);
330	if (!block_name || !strcmp(block_name, "tp"))
331		match += dump_block_regs(tp1_regs, regs);
332	if (!block_name || !strcmp(block_name, "ulp_rx"))
333		match += dump_block_regs(ulp2_rx_regs, regs);
334	if (!block_name || !strcmp(block_name, "ulp_tx"))
335		match += dump_block_regs(ulp2_tx_regs, regs);
336	if (!block_name || !strcmp(block_name, "pmrx"))
337		match += dump_block_regs(pm1_rx_regs, regs);
338	if (!block_name || !strcmp(block_name, "pmtx"))
339		match += dump_block_regs(pm1_tx_regs, regs);
340	if (!block_name || !strcmp(block_name, "mps"))
341		match += dump_block_regs(mps0_regs, regs);
342	if (!block_name || !strcmp(block_name, "cplsw"))
343		match += dump_block_regs(cpl_switch_regs, regs);
344	if (!block_name || !strcmp(block_name, "smb"))
345		match += dump_block_regs(smb0_regs, regs);
346	if (!block_name || !strcmp(block_name, "i2c"))
347		match += dump_block_regs(i2cm0_regs, regs);
348	if (!block_name || !strcmp(block_name, "mi1"))
349		match += dump_block_regs(mi1_regs, regs);
350	if (!block_name || !strcmp(block_name, "sf"))
351		match += dump_block_regs(sf1_regs, regs);
352	if (!block_name || !strcmp(block_name, "pl"))
353		match += dump_block_regs(pl3_regs, regs);
354	if (!block_name || !strcmp(block_name, "mc5"))
355		match += dump_block_regs(mc5a_regs, regs);
356	if (!block_name || !strcmp(block_name, "xgmac0"))
357		match += dump_block_regs(xgmac0_0_regs, regs);
358	if (!block_name || !strcmp(block_name, "xgmac1"))
359		match += dump_block_regs(xgmac0_1_regs, regs);
360	if (!match)
361		errx(1, "unknown block \"%s\"", block_name);
362	return 0;
363}
364
365static int
366dump_regs_t3b(int argc, char *argv[], int start_arg, uint32_t *regs,
367    int is_pcie)
368{
369	int match = 0;
370	char *block_name = NULL;
371
372	if (argc == start_arg + 1)
373		block_name = argv[start_arg];
374	else if (argc != start_arg)
375		return -1;
376
377	if (!block_name || !strcmp(block_name, "sge"))
378		match += dump_block_regs(t3b_sge3_regs, regs);
379	if (!block_name || !strcmp(block_name, "pci"))
380		match += dump_block_regs(is_pcie ? t3b_pcie0_regs :
381						   t3b_pcix1_regs, regs);
382	if (!block_name || !strcmp(block_name, "t3dbg"))
383		match += dump_block_regs(t3b_t3dbg_regs, regs);
384	if (!block_name || !strcmp(block_name, "pmrx"))
385		match += dump_block_regs(t3b_mc7_pmrx_regs, regs);
386	if (!block_name || !strcmp(block_name, "pmtx"))
387		match += dump_block_regs(t3b_mc7_pmtx_regs, regs);
388	if (!block_name || !strcmp(block_name, "cm"))
389		match += dump_block_regs(t3b_mc7_cm_regs, regs);
390	if (!block_name || !strcmp(block_name, "cim"))
391		match += dump_block_regs(t3b_cim_regs, regs);
392	if (!block_name || !strcmp(block_name, "tp"))
393		match += dump_block_regs(t3b_tp1_regs, regs);
394	if (!block_name || !strcmp(block_name, "ulp_rx"))
395		match += dump_block_regs(t3b_ulp2_rx_regs, regs);
396	if (!block_name || !strcmp(block_name, "ulp_tx"))
397		match += dump_block_regs(t3b_ulp2_tx_regs, regs);
398	if (!block_name || !strcmp(block_name, "pmrx"))
399		match += dump_block_regs(t3b_pm1_rx_regs, regs);
400	if (!block_name || !strcmp(block_name, "pmtx"))
401		match += dump_block_regs(t3b_pm1_tx_regs, regs);
402	if (!block_name || !strcmp(block_name, "mps"))
403		match += dump_block_regs(t3b_mps0_regs, regs);
404	if (!block_name || !strcmp(block_name, "cplsw"))
405		match += dump_block_regs(t3b_cpl_switch_regs, regs);
406	if (!block_name || !strcmp(block_name, "smb"))
407		match += dump_block_regs(t3b_smb0_regs, regs);
408	if (!block_name || !strcmp(block_name, "i2c"))
409		match += dump_block_regs(t3b_i2cm0_regs, regs);
410	if (!block_name || !strcmp(block_name, "mi1"))
411		match += dump_block_regs(t3b_mi1_regs, regs);
412	if (!block_name || !strcmp(block_name, "sf"))
413		match += dump_block_regs(t3b_sf1_regs, regs);
414	if (!block_name || !strcmp(block_name, "pl"))
415		match += dump_block_regs(t3b_pl3_regs, regs);
416	if (!block_name || !strcmp(block_name, "mc5"))
417		match += dump_block_regs(t3b_mc5a_regs, regs);
418	if (!block_name || !strcmp(block_name, "xgmac0"))
419		match += dump_block_regs(t3b_xgmac0_0_regs, regs);
420	if (!block_name || !strcmp(block_name, "xgmac1"))
421		match += dump_block_regs(t3b_xgmac0_1_regs, regs);
422	if (!match)
423		errx(1, "unknown block \"%s\"", block_name);
424	return 0;
425}
426
427static int
428dump_regs_t3c(int argc, char *argv[], int start_arg, uint32_t *regs,
429    int is_pcie)
430{
431	int match = 0;
432	char *block_name = NULL;
433
434	if (argc == start_arg + 1)
435		block_name = argv[start_arg];
436	else if (argc != start_arg)
437		return -1;
438
439	if (!block_name || !strcmp(block_name, "sge"))
440		match += dump_block_regs(t3c_sge3_regs, regs);
441	if (!block_name || !strcmp(block_name, "pci"))
442		match += dump_block_regs(is_pcie ? t3c_pcie0_regs :
443						   t3c_pcix1_regs, regs);
444	if (!block_name || !strcmp(block_name, "t3dbg"))
445		match += dump_block_regs(t3c_t3dbg_regs, regs);
446	if (!block_name || !strcmp(block_name, "pmrx"))
447		match += dump_block_regs(t3c_mc7_pmrx_regs, regs);
448	if (!block_name || !strcmp(block_name, "pmtx"))
449		match += dump_block_regs(t3c_mc7_pmtx_regs, regs);
450	if (!block_name || !strcmp(block_name, "cm"))
451		match += dump_block_regs(t3c_mc7_cm_regs, regs);
452	if (!block_name || !strcmp(block_name, "cim"))
453		match += dump_block_regs(t3c_cim_regs, regs);
454	if (!block_name || !strcmp(block_name, "tp"))
455		match += dump_block_regs(t3c_tp1_regs, regs);
456	if (!block_name || !strcmp(block_name, "ulp_rx"))
457		match += dump_block_regs(t3c_ulp2_rx_regs, regs);
458	if (!block_name || !strcmp(block_name, "ulp_tx"))
459		match += dump_block_regs(t3c_ulp2_tx_regs, regs);
460	if (!block_name || !strcmp(block_name, "pmrx"))
461		match += dump_block_regs(t3c_pm1_rx_regs, regs);
462	if (!block_name || !strcmp(block_name, "pmtx"))
463		match += dump_block_regs(t3c_pm1_tx_regs, regs);
464	if (!block_name || !strcmp(block_name, "mps"))
465		match += dump_block_regs(t3c_mps0_regs, regs);
466	if (!block_name || !strcmp(block_name, "cplsw"))
467		match += dump_block_regs(t3c_cpl_switch_regs, regs);
468	if (!block_name || !strcmp(block_name, "smb"))
469		match += dump_block_regs(t3c_smb0_regs, regs);
470	if (!block_name || !strcmp(block_name, "i2c"))
471		match += dump_block_regs(t3c_i2cm0_regs, regs);
472	if (!block_name || !strcmp(block_name, "mi1"))
473		match += dump_block_regs(t3c_mi1_regs, regs);
474	if (!block_name || !strcmp(block_name, "sf"))
475		match += dump_block_regs(t3c_sf1_regs, regs);
476	if (!block_name || !strcmp(block_name, "pl"))
477		match += dump_block_regs(t3c_pl3_regs, regs);
478	if (!block_name || !strcmp(block_name, "mc5"))
479		match += dump_block_regs(t3c_mc5a_regs, regs);
480	if (!block_name || !strcmp(block_name, "xgmac0"))
481		match += dump_block_regs(t3c_xgmac0_0_regs, regs);
482	if (!block_name || !strcmp(block_name, "xgmac1"))
483		match += dump_block_regs(t3c_xgmac0_1_regs, regs);
484	if (!match)
485		errx(1, "unknown block \"%s\"", block_name);
486	return 0;
487}
488#endif
489
490static int
491dump_regs(int argc, char *argv[], int start_arg, const char *iff_name)
492{
493	int vers, revision, is_pcie;
494	struct ch_ifconf_regs regs;
495
496	regs.len = REGDUMP_SIZE;
497
498	/* XXX: This is never freed.  Looks like we don't care. */
499	if ((regs.data = malloc(regs.len)) == NULL)
500		err(1, "can't malloc");
501
502	if (doit(iff_name, CHELSIO_IFCONF_GETREGS, &regs))
503		err(1, "can't read registers");
504
505	vers = regs.version & 0x3ff;
506	revision = (regs.version >> 10) & 0x3f;
507	is_pcie = (regs.version & 0x80000000) != 0;
508
509	if (vers <= 2)
510		return dump_regs_t2(argc, argv, start_arg, (uint32_t *)regs.data);
511#if defined(CONFIG_T3_REGS)
512	if (vers == 3) {
513		if (revision == 0)
514			return dump_regs_t3(argc, argv, start_arg,
515					    (uint32_t *)regs.data, is_pcie);
516		if (revision == 2 || revision == 3)
517			return dump_regs_t3b(argc, argv, start_arg,
518					     (uint32_t *)regs.data, is_pcie);
519		if (revision == 4)
520			return dump_regs_t3c(argc, argv, start_arg,
521			    		     (uint32_t *)regs.data, is_pcie);
522	}
523#endif
524	errx(1, "unknown card type %d.%d", vers, revision);
525	return 0;
526}
527
528static int
529t3_meminfo(const uint32_t *regs)
530{
531	enum {
532		SG_EGR_CNTX_BADDR       = 0x58,
533		SG_CQ_CONTEXT_BADDR     = 0x6c,
534		CIM_SDRAM_BASE_ADDR     = 0x28c,
535		CIM_SDRAM_ADDR_SIZE     = 0x290,
536		TP_CMM_MM_BASE          = 0x314,
537		TP_CMM_TIMER_BASE       = 0x318,
538		TP_CMM_MM_RX_FLST_BASE  = 0x460,
539		TP_CMM_MM_TX_FLST_BASE  = 0x464,
540		TP_CMM_MM_PS_FLST_BASE  = 0x468,
541		ULPRX_ISCSI_LLIMIT      = 0x50c,
542		ULPRX_ISCSI_ULIMIT      = 0x510,
543		ULPRX_TDDP_LLIMIT       = 0x51c,
544		ULPRX_TDDP_ULIMIT       = 0x520,
545		ULPRX_STAG_LLIMIT       = 0x52c,
546		ULPRX_STAG_ULIMIT       = 0x530,
547		ULPRX_RQ_LLIMIT         = 0x534,
548		ULPRX_RQ_ULIMIT         = 0x538,
549		ULPRX_PBL_LLIMIT        = 0x53c,
550		ULPRX_PBL_ULIMIT        = 0x540,
551	};
552
553	unsigned int egr_cntxt = regs[SG_EGR_CNTX_BADDR / 4],
554		     cq_cntxt = regs[SG_CQ_CONTEXT_BADDR / 4],
555		     timers = regs[TP_CMM_TIMER_BASE / 4] & 0xfffffff,
556		     pstructs = regs[TP_CMM_MM_BASE / 4],
557		     pstruct_fl = regs[TP_CMM_MM_PS_FLST_BASE / 4],
558		     rx_fl = regs[TP_CMM_MM_RX_FLST_BASE / 4],
559		     tx_fl = regs[TP_CMM_MM_TX_FLST_BASE / 4],
560		     cim_base = regs[CIM_SDRAM_BASE_ADDR / 4],
561		     cim_size = regs[CIM_SDRAM_ADDR_SIZE / 4];
562	unsigned int iscsi_ll = regs[ULPRX_ISCSI_LLIMIT / 4],
563		     iscsi_ul = regs[ULPRX_ISCSI_ULIMIT / 4],
564		     tddp_ll = regs[ULPRX_TDDP_LLIMIT / 4],
565		     tddp_ul = regs[ULPRX_TDDP_ULIMIT / 4],
566		     stag_ll = regs[ULPRX_STAG_LLIMIT / 4],
567		     stag_ul = regs[ULPRX_STAG_ULIMIT / 4],
568		     rq_ll = regs[ULPRX_RQ_LLIMIT / 4],
569		     rq_ul = regs[ULPRX_RQ_ULIMIT / 4],
570		     pbl_ll = regs[ULPRX_PBL_LLIMIT / 4],
571		     pbl_ul = regs[ULPRX_PBL_ULIMIT / 4];
572
573	printf("CM memory map:\n");
574	printf("  TCB region:      0x%08x - 0x%08x [%u]\n", 0, egr_cntxt - 1,
575	       egr_cntxt);
576	printf("  Egress contexts: 0x%08x - 0x%08x [%u]\n", egr_cntxt,
577	       cq_cntxt - 1, cq_cntxt - egr_cntxt);
578	printf("  CQ contexts:     0x%08x - 0x%08x [%u]\n", cq_cntxt,
579	       timers - 1, timers - cq_cntxt);
580	printf("  Timers:          0x%08x - 0x%08x [%u]\n", timers,
581	       pstructs - 1, pstructs - timers);
582	printf("  Pstructs:        0x%08x - 0x%08x [%u]\n", pstructs,
583	       pstruct_fl - 1, pstruct_fl - pstructs);
584	printf("  Pstruct FL:      0x%08x - 0x%08x [%u]\n", pstruct_fl,
585	       rx_fl - 1, rx_fl - pstruct_fl);
586	printf("  Rx FL:           0x%08x - 0x%08x [%u]\n", rx_fl, tx_fl - 1,
587	       tx_fl - rx_fl);
588	printf("  Tx FL:           0x%08x - 0x%08x [%u]\n", tx_fl, cim_base - 1,
589	       cim_base - tx_fl);
590	printf("  uP RAM:          0x%08x - 0x%08x [%u]\n", cim_base,
591	       cim_base + cim_size - 1, cim_size);
592
593	printf("\nPMRX memory map:\n");
594	printf("  iSCSI region:    0x%08x - 0x%08x [%u]\n", iscsi_ll, iscsi_ul,
595	       iscsi_ul - iscsi_ll + 1);
596	printf("  TCP DDP region:  0x%08x - 0x%08x [%u]\n", tddp_ll, tddp_ul,
597	       tddp_ul - tddp_ll + 1);
598	printf("  TPT region:      0x%08x - 0x%08x [%u]\n", stag_ll, stag_ul,
599	       stag_ul - stag_ll + 1);
600	printf("  RQ region:       0x%08x - 0x%08x [%u]\n", rq_ll, rq_ul,
601	       rq_ul - rq_ll + 1);
602	printf("  PBL region:      0x%08x - 0x%08x [%u]\n", pbl_ll, pbl_ul,
603	       pbl_ul - pbl_ll + 1);
604	return 0;
605}
606
607static int
608meminfo(int argc, char *argv[], int start_arg, const char *iff_name)
609{
610	int vers;
611	struct ch_ifconf_regs regs;
612
613	(void) argc;
614	(void) argv;
615	(void) start_arg;
616
617	regs.len = REGDUMP_SIZE;
618	if ((regs.data = malloc(regs.len)) == NULL)
619		err(1, "can't malloc");
620
621	if (doit(iff_name, CHELSIO_IFCONF_GETREGS, &regs))
622		err(1, "can't read registers");
623
624	vers = regs.version & 0x3ff;
625	if (vers == 3)
626		return t3_meminfo((uint32_t *)regs.data);
627
628	errx(1, "unknown card type %d", vers);
629	return 0;
630}
631
632static int
633mtu_tab_op(int argc, char *argv[], int start_arg, const char *iff_name)
634{
635	struct ch_mtus m;
636	unsigned int i;
637
638	if (argc == start_arg) {
639		if (doit(iff_name, CHELSIO_GETMTUTAB, &m) < 0)
640			err(1, "get MTU table");
641		for (i = 0; i < m.nmtus; ++i)
642			printf("%u ", m.mtus[i]);
643		printf("\n");
644	} else if (argc <= start_arg + NMTUS) {
645		m.nmtus = argc - start_arg;
646
647		for (i = 0; i < m.nmtus; ++i) {
648			char *p;
649			unsigned long mt = strtoul(argv[start_arg + i], &p, 0);
650
651			if (*p || mt > 9600) {
652				warnx("bad parameter \"%s\"",
653				      argv[start_arg + i]);
654				return -1;
655			}
656			if (i && mt < m.mtus[i - 1])
657				errx(1, "MTUs must be in ascending order");
658			m.mtus[i] = mt;
659		}
660		if (doit(iff_name, CHELSIO_SETMTUTAB, &m) < 0)
661			err(1, "set MTU table");
662	} else
663		return -1;
664
665	return 0;
666}
667
668#ifdef CHELSIO_INTERNAL
669static void
670show_egress_cntxt(uint32_t data[])
671{
672	printf("credits:      %u\n", data[0] & 0x7fff);
673	printf("GTS:          %u\n", (data[0] >> 15) & 1);
674	printf("index:        %u\n", data[0] >> 16);
675	printf("queue size:   %u\n", data[1] & 0xffff);
676	printf("base address: 0x%" PRIx64 "\n",
677	       ((data[1] >> 16) | ((uint64_t)data[2] << 16) |
678	       (((uint64_t)data[3] & 0xf) << 48)) << 12);
679	printf("rsp queue #:  %u\n", (data[3] >> 4) & 7);
680	printf("cmd queue #:  %u\n", (data[3] >> 7) & 1);
681	printf("TUN:          %u\n", (data[3] >> 8) & 1);
682	printf("TOE:          %u\n", (data[3] >> 9) & 1);
683	printf("generation:   %u\n", (data[3] >> 10) & 1);
684	printf("uP token:     %u\n", (data[3] >> 11) & 0xfffff);
685	printf("valid:        %u\n", (data[3] >> 31) & 1);
686}
687
688static void
689show_fl_cntxt(uint32_t data[])
690{
691	printf("base address: 0x%" PRIx64 "\n",
692	       ((uint64_t)data[0] | ((uint64_t)data[1] & 0xfffff) << 32) << 12);
693	printf("index:        %u\n", (data[1] >> 20) | ((data[2] & 0xf) << 12));
694	printf("queue size:   %u\n", (data[2] >> 4) & 0xffff);
695	printf("generation:   %u\n", (data[2] >> 20) & 1);
696	printf("entry size:   %u\n",
697	       (data[2] >> 21) | (data[3] & 0x1fffff) << 11);
698	printf("congest thr:  %u\n", (data[3] >> 21) & 0x3ff);
699	printf("GTS:          %u\n", (data[3] >> 31) & 1);
700}
701
702static void
703show_response_cntxt(uint32_t data[])
704{
705	printf("index:        %u\n", data[0] & 0xffff);
706	printf("size:         %u\n", data[0] >> 16);
707	printf("base address: 0x%" PRIx64 "\n",
708	       ((uint64_t)data[1] | ((uint64_t)data[2] & 0xfffff) << 32) << 12);
709	printf("MSI-X/RspQ:   %u\n", (data[2] >> 20) & 0x3f);
710	printf("intr enable:  %u\n", (data[2] >> 26) & 1);
711	printf("intr armed:   %u\n", (data[2] >> 27) & 1);
712	printf("generation:   %u\n", (data[2] >> 28) & 1);
713	printf("CQ mode:      %u\n", (data[2] >> 31) & 1);
714	printf("FL threshold: %u\n", data[3]);
715}
716
717static void
718show_cq_cntxt(uint32_t data[])
719{
720	printf("index:            %u\n", data[0] & 0xffff);
721	printf("size:             %u\n", data[0] >> 16);
722	printf("base address:     0x%" PRIx64 "\n",
723	       ((uint64_t)data[1] | ((uint64_t)data[2] & 0xfffff) << 32) << 12);
724	printf("rsp queue #:      %u\n", (data[2] >> 20) & 0x3f);
725	printf("AN:               %u\n", (data[2] >> 26) & 1);
726	printf("armed:            %u\n", (data[2] >> 27) & 1);
727	printf("ANS:              %u\n", (data[2] >> 28) & 1);
728	printf("generation:       %u\n", (data[2] >> 29) & 1);
729	printf("overflow mode:    %u\n", (data[2] >> 31) & 1);
730	printf("credits:          %u\n", data[3] & 0xffff);
731	printf("credit threshold: %u\n", data[3] >> 16);
732}
733
734static int
735get_sge_context(int argc, char *argv[], int start_arg, const char *iff_name)
736{
737	struct ch_cntxt ctx;
738
739	if (argc != start_arg + 2) return -1;
740
741	if (!strcmp(argv[start_arg], "egress"))
742		ctx.cntxt_type = CNTXT_TYPE_EGRESS;
743	else if (!strcmp(argv[start_arg], "fl"))
744		ctx.cntxt_type = CNTXT_TYPE_FL;
745	else if (!strcmp(argv[start_arg], "response"))
746		ctx.cntxt_type = CNTXT_TYPE_RSP;
747	else if (!strcmp(argv[start_arg], "cq"))
748		ctx.cntxt_type = CNTXT_TYPE_CQ;
749	else {
750		warnx("unknown context type \"%s\"; known types are egress, "
751		      "fl, cq, and response", argv[start_arg]);
752		return -1;
753	}
754
755	if (get_int_arg(argv[start_arg + 1], &ctx.cntxt_id))
756		return -1;
757
758	if (doit(iff_name, CHELSIO_GET_SGE_CONTEXT, &ctx) < 0)
759		err(1, "get SGE context");
760
761	if (!strcmp(argv[start_arg], "egress"))
762		show_egress_cntxt(ctx.data);
763	else if (!strcmp(argv[start_arg], "fl"))
764		show_fl_cntxt(ctx.data);
765	else if (!strcmp(argv[start_arg], "response"))
766		show_response_cntxt(ctx.data);
767	else if (!strcmp(argv[start_arg], "cq"))
768		show_cq_cntxt(ctx.data);
769	return 0;
770}
771
772#define ntohll(x) be64toh((x))
773
774static int
775get_sge_desc(int argc, char *argv[], int start_arg, const char *iff_name)
776{
777	uint64_t *p, wr_hdr;
778	unsigned int n = 1, qset, qnum;
779	struct ch_desc desc;
780
781	if (argc != start_arg + 3 && argc != start_arg + 4)
782		return -1;
783
784	if (get_int_arg(argv[start_arg], &qset) ||
785	    get_int_arg(argv[start_arg + 1], &qnum) ||
786	    get_int_arg(argv[start_arg + 2], &desc.idx))
787		return -1;
788
789	if (argc == start_arg + 4 && get_int_arg(argv[start_arg + 3], &n))
790		return -1;
791
792	if (qnum > 5)
793		errx(1, "invalid queue number %d, range is 0..5", qnum);
794
795	desc.queue_num = qset * 6 + qnum;
796
797	for (; n--; desc.idx++) {
798		if (doit(iff_name, CHELSIO_GET_SGE_DESC, &desc) < 0)
799			err(1, "get SGE descriptor");
800
801		p = (uint64_t *)desc.data;
802		wr_hdr = ntohll(*p);
803		printf("Descriptor %u: cmd %u, TID %u, %s%s%s%s%u flits\n",
804		       desc.idx, (unsigned int)(wr_hdr >> 56),
805		       ((unsigned int)wr_hdr >> 8) & 0xfffff,
806		       ((wr_hdr >> 55) & 1) ? "SOP, " : "",
807		       ((wr_hdr >> 54) & 1) ? "EOP, " : "",
808		       ((wr_hdr >> 53) & 1) ? "COMPL, " : "",
809		       ((wr_hdr >> 52) & 1) ? "SGL, " : "",
810		       (unsigned int)wr_hdr & 0xff);
811
812		for (; desc.size; p++, desc.size -= sizeof(uint64_t))
813			printf("%016" PRIx64 "%c", ntohll(*p),
814			    desc.size % 32 == 8 ? '\n' : ' ');
815	}
816	return 0;
817}
818#endif
819
820static int
821get_tcb2(int argc, char *argv[], int start_arg, const char *iff_name)
822{
823	uint64_t *d;
824	unsigned int i;
825	unsigned int tcb_idx;
826	struct ch_mem_range mr;
827
828	if (argc != start_arg + 1)
829		return -1;
830
831	if (get_int_arg(argv[start_arg], &tcb_idx))
832		return -1;
833
834	mr.buf = calloc(1, TCB_SIZE);
835	if (!mr.buf)
836		err(1, "get TCB");
837
838	mr.mem_id = MEM_CM;
839	mr.addr   = tcb_idx * TCB_SIZE;
840	mr.len    = TCB_SIZE;
841
842	if (doit(iff_name, CHELSIO_GET_MEM, &mr) < 0)
843		err(1, "get TCB");
844
845	for (d = (uint64_t *)mr.buf, i = 0; i < TCB_SIZE / 32; i++) {
846		printf("%2u:", i);
847		printf(" %08x %08x %08x %08x", (uint32_t)d[1],
848		       (uint32_t)(d[1] >> 32), (uint32_t)d[0],
849		       (uint32_t)(d[0] >> 32));
850		d += 2;
851		printf(" %08x %08x %08x %08x\n", (uint32_t)d[1],
852		       (uint32_t)(d[1] >> 32), (uint32_t)d[0],
853		       (uint32_t)(d[0] >> 32));
854		d += 2;
855	}
856	free(mr.buf);
857	return 0;
858}
859
860static int
861get_pm_page_spec(const char *s, unsigned int *page_size,
862    unsigned int *num_pages)
863{
864	char *p;
865	unsigned long val;
866
867	val = strtoul(s, &p, 0);
868	if (p == s) return -1;
869	if (*p == 'x' && p[1]) {
870		*num_pages = val;
871		*page_size = strtoul(p + 1, &p, 0);
872	} else {
873		*num_pages = -1;
874		*page_size = val;
875	}
876	*page_size <<= 10;     // KB -> bytes
877	return *p;
878}
879
880static int
881conf_pm(int argc, char *argv[], int start_arg, const char *iff_name)
882{
883	struct ch_pm pm;
884
885	if (argc == start_arg) {
886		if (doit(iff_name, CHELSIO_GET_PM, &pm) < 0)
887			err(1, "read pm config");
888		printf("%ux%uKB TX pages, %ux%uKB RX pages, %uKB total memory\n",
889		       pm.tx_num_pg, pm.tx_pg_sz >> 10, pm.rx_num_pg,
890		       pm.rx_pg_sz >> 10, pm.pm_total >> 10);
891		return 0;
892	}
893
894	if (argc != start_arg + 2) return -1;
895
896	if (get_pm_page_spec(argv[start_arg], &pm.tx_pg_sz, &pm.tx_num_pg)) {
897		warnx("bad parameter \"%s\"", argv[start_arg]);
898		return -1;
899	}
900	if (get_pm_page_spec(argv[start_arg + 1], &pm.rx_pg_sz,
901			     &pm.rx_num_pg)) {
902		warnx("bad parameter \"%s\"", argv[start_arg + 1]);
903		return -1;
904	}
905	if (doit(iff_name, CHELSIO_SET_PM, &pm) < 0)
906		err(1, "pm config");
907	return 0;
908}
909
910#ifdef	CHELSIO_INTERNAL
911static int
912dump_tcam(int argc, char *argv[], int start_arg, const char *iff_name)
913{
914	unsigned int nwords;
915	struct ch_tcam_word op;
916
917	if (argc != start_arg + 2) return -1;
918
919	if (get_int_arg(argv[start_arg], &op.addr) ||
920	    get_int_arg(argv[start_arg + 1], &nwords))
921		return -1;
922
923	while (nwords--) {
924		if (doit(iff_name, CHELSIO_READ_TCAM_WORD, &op) < 0)
925			err(1, "tcam dump");
926
927		printf("0x%08x: 0x%02x 0x%08x 0x%08x\n", op.addr,
928		       op.buf[0] & 0xff, op.buf[1], op.buf[2]);
929		op.addr++;
930	}
931	return 0;
932}
933
934static void
935hexdump_8b(unsigned int start, uint64_t *data, unsigned int len)
936{
937	int i;
938
939	while (len) {
940		printf("0x%08x:", start);
941		for (i = 0; i < 4 && len; ++i, --len)
942			printf(" %016llx", (unsigned long long)*data++);
943		printf("\n");
944		start += 32;
945	}
946}
947
948static int
949dump_mc7(int argc, char *argv[], int start_arg, const char *iff_name)
950{
951	struct ch_mem_range mem;
952	unsigned int mem_id, addr, len;
953
954	if (argc != start_arg + 3) return -1;
955
956	if (!strcmp(argv[start_arg], "cm"))
957		mem_id = MEM_CM;
958	else if (!strcmp(argv[start_arg], "rx"))
959		mem_id = MEM_PMRX;
960	else if (!strcmp(argv[start_arg], "tx"))
961		mem_id = MEM_PMTX;
962	else
963		errx(1, "unknown memory \"%s\"; must be one of \"cm\", \"tx\","
964			" or \"rx\"", argv[start_arg]);
965
966	if (get_int_arg(argv[start_arg + 1], &addr) ||
967	    get_int_arg(argv[start_arg + 2], &len))
968		return -1;
969
970	mem.buf = malloc(len);
971	if (!mem.buf)
972		err(1, "memory dump");
973
974	mem.mem_id = mem_id;
975	mem.addr   = addr;
976	mem.len    = len;
977
978	if (doit(iff_name, CHELSIO_GET_MEM, &mem) < 0)
979		err(1, "memory dump");
980
981	hexdump_8b(mem.addr, (uint64_t *)mem.buf, mem.len >> 3);
982	free(mem.buf);
983	return 0;
984}
985#endif
986
987/* Max FW size is 64K including version, +4 bytes for the checksum. */
988#define MAX_FW_IMAGE_SIZE (64 * 1024)
989
990static int
991load_fw(int argc, char *argv[], int start_arg, const char *iff_name)
992{
993	int fd, len;
994	struct ch_mem_range op;
995	const char *fname = argv[start_arg];
996
997	if (argc != start_arg + 1) return -1;
998
999	fd = open(fname, O_RDONLY);
1000	if (fd < 0)
1001		err(1, "load firmware");
1002
1003	bzero(&op, sizeof(op));
1004	op.buf = malloc(MAX_FW_IMAGE_SIZE + 1);
1005	if (!op.buf)
1006		err(1, "load firmware");
1007
1008	len = read(fd, op.buf, MAX_FW_IMAGE_SIZE + 1);
1009	if (len < 0)
1010		err(1, "load firmware");
1011 	if (len > MAX_FW_IMAGE_SIZE)
1012		errx(1, "FW image too large");
1013
1014	op.len = len;
1015	if (doit(iff_name, CHELSIO_LOAD_FW, &op) < 0)
1016		err(1, "load firmware");
1017	return 0;
1018}
1019
1020/* Max BOOT size is 255*512 bytes including the BIOS boot ROM basic header */
1021#define MAX_BOOT_IMAGE_SIZE (0xff * 512)
1022
1023static int
1024load_boot(int argc, char *argv[], int start_arg, const char *iff_name)
1025{
1026	int fd, len;
1027	struct ch_mem_range op;
1028	const char *fname = argv[start_arg];
1029
1030	if (argc != start_arg + 1) return -1;
1031
1032	fd = open(fname, O_RDONLY);
1033	if (fd < 0)
1034		err(1, "load boot image");
1035
1036	op.buf = malloc(MAX_BOOT_IMAGE_SIZE + 1);
1037	if (!op.buf)
1038		err(1, "load boot image");
1039
1040	len = read(fd, op.buf, MAX_BOOT_IMAGE_SIZE + 1);
1041	if (len < 0)
1042		err(1, "load boot image");
1043 	if (len > MAX_BOOT_IMAGE_SIZE)
1044		errx(1, "boot image too large");
1045
1046	op.len = len;
1047
1048	if (doit(iff_name, CHELSIO_LOAD_BOOT, &op) < 0)
1049		err(1, "load boot image");
1050
1051	return 0;
1052}
1053
1054static int
1055dump_proto_sram(const char *iff_name)
1056{
1057	int i, j;
1058	uint8_t buf[PROTO_SRAM_SIZE];
1059	struct ch_eeprom ee;
1060	uint8_t *p = buf;
1061
1062	bzero(buf, sizeof(buf));
1063	ee.offset = PROTO_SRAM_EEPROM_ADDR;
1064	ee.data = p;
1065	ee.len = sizeof(buf);
1066	if (doit(iff_name, CHELSIO_GET_EEPROM, &ee))
1067		err(1, "show protocol sram");
1068
1069	for (i = 0; i < PROTO_SRAM_LINES; i++) {
1070		for (j = PROTO_SRAM_LINE_NIBBLES - 1; j >= 0; j--) {
1071			int nibble_idx = i * PROTO_SRAM_LINE_NIBBLES + j;
1072			uint8_t nibble = p[nibble_idx / 2];
1073
1074			if (nibble_idx & 1)
1075				nibble >>= 4;
1076			else
1077				nibble &= 0xf;
1078			printf("%x", nibble);
1079		}
1080		putchar('\n');
1081	}
1082	return 0;
1083}
1084
1085static int
1086proto_sram_op(int argc, char *argv[], int start_arg,
1087			 const char *iff_name)
1088{
1089	(void) argv;
1090	(void) start_arg;
1091
1092	if (argc == start_arg)
1093		return dump_proto_sram(iff_name);
1094	return -1;
1095}
1096
1097static int
1098dump_qset_params(const char *iff_name)
1099{
1100	struct ch_qset_params qp;
1101
1102	qp.qset_idx = 0;
1103
1104	while (doit(iff_name, CHELSIO_GET_QSET_PARAMS, &qp) == 0) {
1105		if (!qp.qset_idx)
1106			printf("Qset   TxQ0   TxQ1   TxQ2   RspQ   RxQ0   RxQ1"
1107			       "  Cong  Lat   IRQ\n");
1108		printf("%4u %6u %6u %6u %6u %6u %6u %5u %4u %5d\n",
1109		       qp.qnum,
1110		       qp.txq_size[0], qp.txq_size[1], qp.txq_size[2],
1111		       qp.rspq_size, qp.fl_size[0], qp.fl_size[1],
1112		       qp.cong_thres, qp.intr_lat, qp.vector);
1113		qp.qset_idx++;
1114	}
1115	if (!qp.qset_idx || (errno && errno != EINVAL))
1116		err(1, "get qset parameters");
1117	return 0;
1118}
1119
1120static int
1121qset_config(int argc, char *argv[], int start_arg, const char *iff_name)
1122{
1123	(void) argv;
1124
1125	if (argc == start_arg)
1126		return dump_qset_params(iff_name);
1127
1128	return -1;
1129}
1130
1131static int
1132qset_num_config(int argc, char *argv[], int start_arg, const char *iff_name)
1133{
1134	struct ch_reg reg;
1135
1136	(void) argv;
1137
1138	if (argc == start_arg) {
1139		if (doit(iff_name, CHELSIO_GET_QSET_NUM, &reg) < 0)
1140			err(1, "get qsets");
1141		printf("%u\n", reg.val);
1142		return 0;
1143	}
1144
1145	return -1;
1146}
1147
1148/*
1149 * Parse a string containing an IP address with an optional network prefix.
1150 */
1151static int
1152parse_ipaddr(const char *s, uint32_t *addr, uint32_t *mask)
1153{
1154	char *p, *slash;
1155	struct in_addr ia;
1156
1157	*mask = 0xffffffffU;
1158	slash = strchr(s, '/');
1159	if (slash)
1160		*slash = 0;
1161	if (!inet_aton(s, &ia)) {
1162		if (slash)
1163			*slash = '/';
1164		*addr = 0;
1165		return -1;
1166	}
1167	*addr = ntohl(ia.s_addr);
1168	if (slash) {
1169		unsigned int prefix = strtoul(slash + 1, &p, 10);
1170
1171		*slash = '/';
1172		if (p == slash + 1 || *p || prefix > 32)
1173			return -1;
1174		*mask <<= (32 - prefix);
1175	}
1176	return 0;
1177}
1178
1179/*
1180 * Parse a string containing a value and an optional colon separated mask.
1181 */
1182static int
1183parse_val_mask_param(const char *s, uint32_t *val, uint32_t *mask,
1184    uint32_t default_mask)
1185{
1186	char *p;
1187
1188	*mask = default_mask;
1189	*val = strtoul(s, &p, 0);
1190	if (p == s || *val > default_mask)
1191		return -1;
1192	if (*p == ':' && p[1])
1193		*mask = strtoul(p + 1, &p, 0);
1194	return *p || *mask > default_mask ? -1 : 0;
1195}
1196
1197static int
1198parse_trace_param(const char *s, uint32_t *val, uint32_t *mask)
1199{
1200	return strchr(s, '.') ? parse_ipaddr(s, val, mask) :
1201				parse_val_mask_param(s, val, mask, 0xffffffffU);
1202}
1203
1204static int
1205trace_config(int argc, char *argv[], int start_arg, const char *iff_name)
1206{
1207	uint32_t val, mask;
1208	struct ch_trace trace;
1209
1210	if (argc == start_arg)
1211		return -1;
1212
1213	memset(&trace, 0, sizeof(trace));
1214	if (!strcmp(argv[start_arg], "tx"))
1215		trace.config_tx = 1;
1216	else if (!strcmp(argv[start_arg], "rx"))
1217		trace.config_rx = 1;
1218	else if (!strcmp(argv[start_arg], "all"))
1219		trace.config_tx = trace.config_rx = 1;
1220	else
1221		errx(1, "bad trace filter \"%s\"; must be one of \"rx\", "
1222		     "\"tx\" or \"all\"", argv[start_arg]);
1223
1224	if (argc == ++start_arg)
1225		return -1;
1226	if (!strcmp(argv[start_arg], "on")) {
1227		trace.trace_tx = trace.config_tx;
1228		trace.trace_rx = trace.config_rx;
1229	} else if (strcmp(argv[start_arg], "off"))
1230		errx(1, "bad argument \"%s\"; must be \"on\" or \"off\"",
1231		     argv[start_arg]);
1232
1233	start_arg++;
1234	if (start_arg < argc && !strcmp(argv[start_arg], "not")) {
1235		trace.invert_match = 1;
1236		start_arg++;
1237	}
1238
1239	while (start_arg + 2 <= argc) {
1240		int ret = parse_trace_param(argv[start_arg + 1], &val, &mask);
1241
1242		if (!strcmp(argv[start_arg], "interface")) {
1243			trace.intf = val;
1244			trace.intf_mask = mask;
1245		} else if (!strcmp(argv[start_arg], "sip")) {
1246			trace.sip = val;
1247			trace.sip_mask = mask;
1248		} else if (!strcmp(argv[start_arg], "dip")) {
1249			trace.dip = val;
1250			trace.dip_mask = mask;
1251		} else if (!strcmp(argv[start_arg], "sport")) {
1252			trace.sport = val;
1253			trace.sport_mask = mask;
1254		} else if (!strcmp(argv[start_arg], "dport")) {
1255			trace.dport = val;
1256			trace.dport_mask = mask;
1257		} else if (!strcmp(argv[start_arg], "vlan")) {
1258			trace.vlan = val;
1259			trace.vlan_mask = mask;
1260		} else if (!strcmp(argv[start_arg], "proto")) {
1261			trace.proto = val;
1262			trace.proto_mask = mask;
1263		} else
1264			errx(1, "unknown trace parameter \"%s\"\n"
1265			     "known parameters are \"interface\", \"sip\", "
1266			     "\"dip\", \"sport\", \"dport\", \"vlan\", "
1267			     "\"proto\"", argv[start_arg]);
1268		if (ret < 0)
1269			errx(1, "bad parameter \"%s\"", argv[start_arg + 1]);
1270		start_arg += 2;
1271	}
1272	if (start_arg != argc)
1273		errx(1, "unknown parameter \"%s\"", argv[start_arg]);
1274
1275	if (doit(iff_name, CHELSIO_SET_TRACE_FILTER, &trace) < 0)
1276		err(1, "trace");
1277	return 0;
1278}
1279
1280static void
1281show_filters(const char *iff_name)
1282{
1283	static const char *pkt_type[] = { "*", "tcp", "udp", "frag" };
1284	struct ch_filter op;
1285	union {
1286		uint32_t nip;
1287		uint8_t octet[4];
1288	} nsip, ndip;
1289	char sip[20], dip[20];
1290	int header = 0;
1291
1292	bzero(&op, sizeof(op));
1293	op.filter_id = 0xffffffff;
1294
1295	do {
1296		if (doit(iff_name, CHELSIO_GET_FILTER, &op) < 0)
1297			err(1, "list filters");
1298
1299		if (op.filter_id == 0xffffffff)
1300			break;
1301
1302		if (!header) {
1303			printf("index         SIP                DIP     sport "
1304			    "dport VLAN PRI P/MAC type Q\n");
1305			header = 1;
1306		}
1307
1308		nsip.nip = htonl(op.val.sip);
1309		ndip.nip = htonl(op.val.dip);
1310
1311		sprintf(sip, "%u.%u.%u.%u/%-2u", nsip.octet[0], nsip.octet[1],
1312		    nsip.octet[2], nsip.octet[3],
1313		    op.mask.sip ? 33 - ffs(op.mask.sip) : 0);
1314		sprintf(dip, "%u.%u.%u.%u", ndip.octet[0], ndip.octet[1],
1315		    ndip.octet[2], ndip.octet[3]);
1316		printf("%5zu %18s %15s ", (size_t)op.filter_id, sip, dip);
1317		printf(op.val.sport ? "%5u " : "    * ", op.val.sport);
1318		printf(op.val.dport ? "%5u " : "    * ", op.val.dport);
1319		printf(op.val.vlan != 0xfff ? "%4u " : "   * ", op.val.vlan);
1320		printf(op.val.vlan_prio == 7 ?  "  * " :
1321		    "%1u/%1u ", op.val.vlan_prio, op.val.vlan_prio | 1);
1322		if (op.mac_addr_idx == 0xffff)
1323			printf("*/*   ");
1324		else if (op.mac_hit)
1325			printf("%1u/%3u ", (op.mac_addr_idx >> 3) & 0x1,
1326			    (op.mac_addr_idx) & 0x7);
1327		else
1328			printf("%1u/  * ", (op.mac_addr_idx >> 3) & 0x1);
1329		printf("%4s ", pkt_type[op.proto]);
1330		if (!op.pass)
1331			printf("-\n");
1332		else if (op.rss)
1333			printf("*\n");
1334		else
1335			printf("%1u\n", op.qset);
1336	} while (1);
1337}
1338
1339static int
1340filter_config(int argc, char *argv[], int start_arg, const char *iff_name)
1341{
1342	int ret = 0;
1343	uint32_t val, mask;
1344	struct ch_filter op;
1345
1346	if (argc < start_arg + 1)
1347		return -1;
1348
1349	memset(&op, 0, sizeof(op));
1350	op.mac_addr_idx = 0xffff;
1351	op.rss = 1;
1352
1353	if (argc == start_arg + 1 && !strcmp(argv[start_arg], "list")) {
1354		show_filters(iff_name);
1355		return 0;
1356	}
1357
1358	if (get_int_arg(argv[start_arg++], &op.filter_id))
1359		return -1;
1360	if (argc == start_arg + 1 && (!strcmp(argv[start_arg], "delete") ||
1361				      !strcmp(argv[start_arg], "clear"))) {
1362		if (doit(iff_name, CHELSIO_DEL_FILTER, &op) < 0) {
1363			if (errno == EBUSY)
1364				err(1, "no filter support when offload in use");
1365			err(1, "delete filter");
1366		}
1367		return 0;
1368	}
1369
1370	while (start_arg + 2 <= argc) {
1371		if (!strcmp(argv[start_arg], "sip")) {
1372			ret = parse_ipaddr(argv[start_arg + 1], &op.val.sip,
1373					   &op.mask.sip);
1374		} else if (!strcmp(argv[start_arg], "dip")) {
1375			ret = parse_ipaddr(argv[start_arg + 1], &op.val.dip,
1376					   &op.mask.dip);
1377		} else if (!strcmp(argv[start_arg], "sport")) {
1378			ret = parse_val_mask_param(argv[start_arg + 1],
1379						   &val, &mask, 0xffff);
1380			op.val.sport = val;
1381			op.mask.sport = mask;
1382		} else if (!strcmp(argv[start_arg], "dport")) {
1383			ret = parse_val_mask_param(argv[start_arg + 1],
1384						   &val, &mask, 0xffff);
1385			op.val.dport = val;
1386			op.mask.dport = mask;
1387		} else if (!strcmp(argv[start_arg], "vlan")) {
1388			ret = parse_val_mask_param(argv[start_arg + 1],
1389						   &val, &mask, 0xfff);
1390			op.val.vlan = val;
1391			op.mask.vlan = mask;
1392		} else if (!strcmp(argv[start_arg], "prio")) {
1393			ret = parse_val_mask_param(argv[start_arg + 1],
1394						   &val, &mask, 7);
1395			op.val.vlan_prio = val;
1396			op.mask.vlan_prio = mask;
1397		} else if (!strcmp(argv[start_arg], "mac")) {
1398			if (!strcmp(argv[start_arg + 1], "none"))
1399				val = -1;
1400			else
1401				ret = get_int_arg(argv[start_arg + 1], &val);
1402			op.mac_hit = val != (uint32_t)-1;
1403			op.mac_addr_idx = op.mac_hit ? val : 0;
1404		} else if (!strcmp(argv[start_arg], "type")) {
1405			if (!strcmp(argv[start_arg + 1], "tcp"))
1406				op.proto = 1;
1407			else if (!strcmp(argv[start_arg + 1], "udp"))
1408				op.proto = 2;
1409			else if (!strcmp(argv[start_arg + 1], "frag"))
1410				op.proto = 3;
1411			else
1412				errx(1, "unknown type \"%s\"; must be one of "
1413				     "\"tcp\", \"udp\", or \"frag\"",
1414				     argv[start_arg + 1]);
1415		} else if (!strcmp(argv[start_arg], "queue")) {
1416			ret = get_int_arg(argv[start_arg + 1], &val);
1417			op.qset = val;
1418			op.rss = 0;
1419		} else if (!strcmp(argv[start_arg], "action")) {
1420			if (!strcmp(argv[start_arg + 1], "pass"))
1421				op.pass = 1;
1422			else if (strcmp(argv[start_arg + 1], "drop"))
1423				errx(1, "unknown action \"%s\"; must be one of "
1424				     "\"pass\" or \"drop\"",
1425				     argv[start_arg + 1]);
1426		} else
1427 			errx(1, "unknown filter parameter \"%s\"\n"
1428			     "known parameters are \"mac\", \"sip\", "
1429			     "\"dip\", \"sport\", \"dport\", \"vlan\", "
1430			     "\"prio\", \"type\", \"queue\", and \"action\"",
1431			     argv[start_arg]);
1432		if (ret < 0)
1433			errx(1, "bad value \"%s\" for parameter \"%s\"",
1434			     argv[start_arg + 1], argv[start_arg]);
1435		start_arg += 2;
1436	}
1437	if (start_arg != argc)
1438		errx(1, "no value for \"%s\"", argv[start_arg]);
1439
1440	if (doit(iff_name, CHELSIO_SET_FILTER, &op) < 0) {
1441		if (errno == EBUSY)
1442			err(1, "no filter support when offload in use");
1443		err(1, "set filter");
1444	}
1445
1446	return 0;
1447}
1448static int
1449get_sched_param(int argc, char *argv[], int pos, unsigned int *valp)
1450{
1451	if (pos + 1 >= argc)
1452		errx(1, "missing value for %s", argv[pos]);
1453	if (get_int_arg(argv[pos + 1], valp))
1454		exit(1);
1455	return 0;
1456}
1457
1458static int
1459tx_sched(int argc, char *argv[], int start_arg, const char *iff_name)
1460{
1461	struct ch_hw_sched op;
1462	unsigned int idx, val;
1463
1464	if (argc < 5 || get_int_arg(argv[start_arg++], &idx))
1465		return -1;
1466
1467	op.sched = idx;
1468	op.mode = op.channel = -1;
1469	op.kbps = op.class_ipg = op.flow_ipg = -1;
1470
1471	while (argc > start_arg) {
1472		if (!strcmp(argv[start_arg], "mode")) {
1473			if (start_arg + 1 >= argc)
1474				errx(1, "missing value for mode");
1475			if (!strcmp(argv[start_arg + 1], "class"))
1476				op.mode = 0;
1477			else if (!strcmp(argv[start_arg + 1], "flow"))
1478				op.mode = 1;
1479			else
1480				errx(1, "bad mode \"%s\"", argv[start_arg + 1]);
1481		} else if (!strcmp(argv[start_arg], "channel") &&
1482			 !get_sched_param(argc, argv, start_arg, &val))
1483			op.channel = val;
1484		else if (!strcmp(argv[start_arg], "rate") &&
1485			 !get_sched_param(argc, argv, start_arg, &val))
1486			op.kbps = val;
1487		else if (!strcmp(argv[start_arg], "ipg") &&
1488			 !get_sched_param(argc, argv, start_arg, &val))
1489			op.class_ipg = val;
1490		else if (!strcmp(argv[start_arg], "flowipg") &&
1491			 !get_sched_param(argc, argv, start_arg, &val))
1492			op.flow_ipg = val;
1493		else
1494			errx(1, "unknown scheduler parameter \"%s\"",
1495			     argv[start_arg]);
1496		start_arg += 2;
1497	}
1498
1499	if (doit(iff_name, CHELSIO_SET_HW_SCHED, &op) < 0)
1500		 err(1, "pktsched");
1501
1502	return 0;
1503}
1504
1505static int
1506pktsched(int argc, char *argv[], int start_arg, const char *iff_name)
1507{
1508	struct ch_pktsched_params op;
1509	unsigned int idx, min = -1, max, binding = -1;
1510
1511	if (argc < 4)
1512		errx(1, "no scheduler specified");
1513
1514	if (!strcmp(argv[start_arg], "port")) {
1515		if (argc != start_arg + 4)
1516			return -1;
1517		if (get_int_arg(argv[start_arg + 1], &idx) ||
1518		    get_int_arg(argv[start_arg + 2], &min) ||
1519		    get_int_arg(argv[start_arg + 3], &max))
1520			return -1;
1521		op.sched = 0;
1522	} else if (!strcmp(argv[start_arg], "tunnelq")) {
1523		if (argc != start_arg + 4)
1524			return -1;
1525		if (get_int_arg(argv[start_arg + 1], &idx) ||
1526		    get_int_arg(argv[start_arg + 2], &max) ||
1527		    get_int_arg(argv[start_arg + 3], &binding))
1528			return -1;
1529		op.sched = 1;
1530	} else if (!strcmp(argv[start_arg], "tx"))
1531		return tx_sched(argc, argv, start_arg + 1, iff_name);
1532	else
1533		errx(1, "unknown scheduler \"%s\"; must be one of \"port\", "
1534			"\"tunnelq\" or \"tx\"", argv[start_arg]);
1535
1536	op.idx = idx;
1537	op.min = min;
1538	op.max = max;
1539	op.binding = binding;
1540	if (doit(iff_name, CHELSIO_SET_PKTSCHED, &op) < 0)
1541		 err(1, "pktsched");
1542
1543	return 0;
1544}
1545
1546static int
1547clear_stats(int argc, char *argv[], int start_arg, const char *iff_name)
1548{
1549	(void) argc;
1550	(void) argv;
1551	(void) start_arg;
1552
1553	if (doit(iff_name, CHELSIO_CLEAR_STATS, NULL) < 0)
1554		 err(1, "clearstats");
1555
1556	return 0;
1557}
1558
1559static int
1560get_up_la(int argc, char *argv[], int start_arg, const char *iff_name)
1561{
1562	struct ch_up_la la;
1563	int i, idx, max_idx, entries;
1564
1565	(void) argc;
1566	(void) argv;
1567	(void) start_arg;
1568
1569	la.stopped = 0;
1570	la.idx = -1;
1571	la.bufsize = LA_BUFSIZE;
1572	la.data = malloc(la.bufsize);
1573	if (!la.data)
1574		err(1, "uP_LA malloc");
1575
1576	if (doit(iff_name, CHELSIO_GET_UP_LA, &la) < 0)
1577		 err(1, "uP_LA");
1578
1579	if (la.stopped)
1580		printf("LA is not running\n");
1581
1582	entries = la.bufsize / 4;
1583	idx = (int)la.idx;
1584	max_idx = (entries / 4) - 1;
1585	for (i = 0; i < max_idx; i++) {
1586		printf("%04x %08x %08x\n",
1587		       la.data[idx], la.data[idx+2], la.data[idx+1]);
1588		idx = (idx + 4) & (entries - 1);
1589	}
1590
1591	return 0;
1592}
1593
1594static int
1595get_up_ioqs(int argc, char *argv[], int start_arg, const char *iff_name)
1596{
1597	struct ch_up_ioqs ioqs;
1598	int i, entries;
1599
1600	(void) argc;
1601	(void) argv;
1602	(void) start_arg;
1603
1604	bzero(&ioqs, sizeof(ioqs));
1605	ioqs.bufsize = IOQS_BUFSIZE;
1606	ioqs.data = malloc(IOQS_BUFSIZE);
1607	if (!ioqs.data)
1608		err(1, "uP_IOQs malloc");
1609
1610	if (doit(iff_name, CHELSIO_GET_UP_IOQS, &ioqs) < 0)
1611		 err(1, "uP_IOQs");
1612
1613	printf("ioq_rx_enable   : 0x%08x\n", ioqs.ioq_rx_enable);
1614	printf("ioq_tx_enable   : 0x%08x\n", ioqs.ioq_tx_enable);
1615	printf("ioq_rx_status   : 0x%08x\n", ioqs.ioq_rx_status);
1616	printf("ioq_tx_status   : 0x%08x\n", ioqs.ioq_tx_status);
1617
1618	entries = ioqs.bufsize / sizeof(struct t3_ioq_entry);
1619	for (i = 0; i < entries; i++) {
1620		printf("\nioq[%d].cp       : 0x%08x\n", i,
1621		       ioqs.data[i].ioq_cp);
1622		printf("ioq[%d].pp       : 0x%08x\n", i,
1623		       ioqs.data[i].ioq_pp);
1624		printf("ioq[%d].alen     : 0x%08x\n", i,
1625		       ioqs.data[i].ioq_alen);
1626		printf("ioq[%d].stats    : 0x%08x\n", i,
1627		       ioqs.data[i].ioq_stats);
1628		printf("  sop %u\n", ioqs.data[i].ioq_stats >> 16);
1629		printf("  eop %u\n", ioqs.data[i].ioq_stats  & 0xFFFF);
1630	}
1631
1632	return 0;
1633}
1634
1635static int
1636run_cmd(int argc, char *argv[], const char *iff_name)
1637{
1638	int r = -1;
1639
1640	if (!strcmp(argv[2], "reg"))
1641		r = register_io(argc, argv, 3, iff_name);
1642	else if (!strcmp(argv[2], "mdio"))
1643		r = mdio_io(argc, argv, 3, iff_name);
1644	else if (!strcmp(argv[2], "mtus"))
1645		r = mtu_tab_op(argc, argv, 3, iff_name);
1646	else if (!strcmp(argv[2], "pm"))
1647		r = conf_pm(argc, argv, 3, iff_name);
1648	else if (!strcmp(argv[2], "regdump"))
1649		r = dump_regs(argc, argv, 3, iff_name);
1650	else if (!strcmp(argv[2], "tcamdump"))
1651		r = dump_tcam(argc, argv, 3, iff_name);
1652	else if (!strcmp(argv[2], "memdump"))
1653		r = dump_mc7(argc, argv, 3, iff_name);
1654	else if (!strcmp(argv[2], "meminfo"))
1655		r = meminfo(argc, argv, 3, iff_name);
1656	else if (!strcmp(argv[2], "context"))
1657		r = get_sge_context(argc, argv, 3, iff_name);
1658	else if (!strcmp(argv[2], "desc"))
1659		r = get_sge_desc(argc, argv, 3, iff_name);
1660	else if (!strcmp(argv[2], "loadfw"))
1661		r = load_fw(argc, argv, 3, iff_name);
1662	else if (!strcmp(argv[2], "loadboot"))
1663		r = load_boot(argc, argv, 3, iff_name);
1664	else if (!strcmp(argv[2], "proto"))
1665		r = proto_sram_op(argc, argv, 3, iff_name);
1666	else if (!strcmp(argv[2], "qset"))
1667		r = qset_config(argc, argv, 3, iff_name);
1668	else if (!strcmp(argv[2], "qsets"))
1669		r = qset_num_config(argc, argv, 3, iff_name);
1670	else if (!strcmp(argv[2], "trace"))
1671		r = trace_config(argc, argv, 3, iff_name);
1672	else if (!strcmp(argv[2], "pktsched"))
1673		r = pktsched(argc, argv, 3, iff_name);
1674	else if (!strcmp(argv[2], "tcb"))
1675		r = get_tcb2(argc, argv, 3, iff_name);
1676	else if (!strcmp(argv[2], "filter"))
1677		r = filter_config(argc, argv, 3, iff_name);
1678	else if (!strcmp(argv[2], "clearstats"))
1679		r = clear_stats(argc, argv, 3, iff_name);
1680	else if (!strcmp(argv[2], "la"))
1681		r = get_up_la(argc, argv, 3, iff_name);
1682	else if (!strcmp(argv[2], "ioqs"))
1683		r = get_up_ioqs(argc, argv, 3, iff_name);
1684
1685	if (r == -1)
1686		usage(stderr);
1687
1688	return (0);
1689}
1690
1691static int
1692run_cmd_loop(int argc, char *argv[], const char *iff_name)
1693{
1694	int n;
1695	unsigned int i;
1696	char buf[64];
1697	char *args[8], *s;
1698
1699	(void) argc;
1700	args[0] = argv[0];
1701	args[1] = argv[1];
1702
1703	/*
1704	 * Fairly simplistic loop.  Displays a "> " prompt and processes any
1705	 * input as a cxgbtool command.  You're supposed to enter only the part
1706	 * after "cxgbtool cxgbX".  Use "quit" or "exit" to exit.  Any error in
1707	 * the command will also terminate cxgbtool.
1708	 */
1709	for (;;) {
1710		fprintf(stdout, "> ");
1711		fflush(stdout);
1712		n = read(STDIN_FILENO, buf, sizeof(buf) - 1);
1713		if (n <= 0)
1714			return (0);
1715
1716		if (buf[--n] != '\n')
1717			continue;
1718		else
1719			buf[n] = 0;
1720
1721		s = &buf[0];
1722		for (i = 2; i < sizeof(args)/sizeof(args[0]) - 1; i++) {
1723			while (s && (*s == ' ' || *s == '\t'))
1724				s++;
1725			if ((args[i] = strsep(&s, " \t")) == NULL)
1726				break;
1727		}
1728		args[sizeof(args)/sizeof(args[0]) - 1] = 0;
1729
1730		if (!strcmp(args[2], "quit") || !strcmp(args[2], "exit"))
1731			return (0);
1732
1733		(void) run_cmd(i, args, iff_name);
1734	}
1735
1736	/* Can't really get here */
1737	return (0);
1738}
1739
1740int
1741main(int argc, char *argv[])
1742{
1743	int r = -1;
1744	const char *iff_name;
1745
1746	progname = argv[0];
1747
1748	if (argc == 2) {
1749		if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
1750			usage(stdout);
1751		if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) {
1752			printf("%s version %s\n", PROGNAME, VERSION);
1753			printf("%s\n", COPYRIGHT);
1754			exit(0);
1755		}
1756	}
1757
1758	if (argc < 3) usage(stderr);
1759
1760	iff_name = argv[1];
1761
1762	if (argc == 3 && !strcmp(argv[2], "stdio"))
1763		r = run_cmd_loop(argc, argv, iff_name);
1764	else
1765		r = run_cmd(argc, argv, iff_name);
1766
1767	return (r);
1768}
1769