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$");
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
1018	close(fd);
1019	return 0;
1020}
1021
1022/* Max BOOT size is 255*512 bytes including the BIOS boot ROM basic header */
1023#define MAX_BOOT_IMAGE_SIZE (0xff * 512)
1024
1025static int
1026load_boot(int argc, char *argv[], int start_arg, const char *iff_name)
1027{
1028	int fd, len;
1029	struct ch_mem_range op;
1030	const char *fname = argv[start_arg];
1031
1032	if (argc != start_arg + 1) return -1;
1033
1034	fd = open(fname, O_RDONLY);
1035	if (fd < 0)
1036		err(1, "load boot image");
1037
1038	op.buf = malloc(MAX_BOOT_IMAGE_SIZE + 1);
1039	if (!op.buf)
1040		err(1, "load boot image");
1041
1042	len = read(fd, op.buf, MAX_BOOT_IMAGE_SIZE + 1);
1043	if (len < 0)
1044		err(1, "load boot image");
1045 	if (len > MAX_BOOT_IMAGE_SIZE)
1046		errx(1, "boot image too large");
1047
1048	op.len = len;
1049
1050	if (doit(iff_name, CHELSIO_LOAD_BOOT, &op) < 0)
1051		err(1, "load boot image");
1052
1053	close(fd);
1054	return 0;
1055}
1056
1057static int
1058dump_proto_sram(const char *iff_name)
1059{
1060	int i, j;
1061	uint8_t buf[PROTO_SRAM_SIZE];
1062	struct ch_eeprom ee;
1063	uint8_t *p = buf;
1064
1065	bzero(buf, sizeof(buf));
1066	ee.offset = PROTO_SRAM_EEPROM_ADDR;
1067	ee.data = p;
1068	ee.len = sizeof(buf);
1069	if (doit(iff_name, CHELSIO_GET_EEPROM, &ee))
1070		err(1, "show protocol sram");
1071
1072	for (i = 0; i < PROTO_SRAM_LINES; i++) {
1073		for (j = PROTO_SRAM_LINE_NIBBLES - 1; j >= 0; j--) {
1074			int nibble_idx = i * PROTO_SRAM_LINE_NIBBLES + j;
1075			uint8_t nibble = p[nibble_idx / 2];
1076
1077			if (nibble_idx & 1)
1078				nibble >>= 4;
1079			else
1080				nibble &= 0xf;
1081			printf("%x", nibble);
1082		}
1083		putchar('\n');
1084	}
1085	return 0;
1086}
1087
1088static int
1089proto_sram_op(int argc, char *argv[], int start_arg,
1090			 const char *iff_name)
1091{
1092	(void) argv;
1093	(void) start_arg;
1094
1095	if (argc == start_arg)
1096		return dump_proto_sram(iff_name);
1097	return -1;
1098}
1099
1100static int
1101dump_qset_params(const char *iff_name)
1102{
1103	struct ch_qset_params qp;
1104
1105	qp.qset_idx = 0;
1106
1107	while (doit(iff_name, CHELSIO_GET_QSET_PARAMS, &qp) == 0) {
1108		if (!qp.qset_idx)
1109			printf("Qset   TxQ0   TxQ1   TxQ2   RspQ   RxQ0   RxQ1"
1110			       "  Cong  Lat   IRQ\n");
1111		printf("%4u %6u %6u %6u %6u %6u %6u %5u %4u %5d\n",
1112		       qp.qnum,
1113		       qp.txq_size[0], qp.txq_size[1], qp.txq_size[2],
1114		       qp.rspq_size, qp.fl_size[0], qp.fl_size[1],
1115		       qp.cong_thres, qp.intr_lat, qp.vector);
1116		qp.qset_idx++;
1117	}
1118	if (!qp.qset_idx || (errno && errno != EINVAL))
1119		err(1, "get qset parameters");
1120	return 0;
1121}
1122
1123static int
1124qset_config(int argc, char *argv[], int start_arg, const char *iff_name)
1125{
1126	(void) argv;
1127
1128	if (argc == start_arg)
1129		return dump_qset_params(iff_name);
1130
1131	return -1;
1132}
1133
1134static int
1135qset_num_config(int argc, char *argv[], int start_arg, const char *iff_name)
1136{
1137	struct ch_reg reg;
1138
1139	(void) argv;
1140
1141	if (argc == start_arg) {
1142		if (doit(iff_name, CHELSIO_GET_QSET_NUM, &reg) < 0)
1143			err(1, "get qsets");
1144		printf("%u\n", reg.val);
1145		return 0;
1146	}
1147
1148	return -1;
1149}
1150
1151/*
1152 * Parse a string containing an IP address with an optional network prefix.
1153 */
1154static int
1155parse_ipaddr(const char *s, uint32_t *addr, uint32_t *mask)
1156{
1157	char *p, *slash;
1158	struct in_addr ia;
1159
1160	*mask = 0xffffffffU;
1161	slash = strchr(s, '/');
1162	if (slash)
1163		*slash = 0;
1164	if (!inet_aton(s, &ia)) {
1165		if (slash)
1166			*slash = '/';
1167		*addr = 0;
1168		return -1;
1169	}
1170	*addr = ntohl(ia.s_addr);
1171	if (slash) {
1172		unsigned int prefix = strtoul(slash + 1, &p, 10);
1173
1174		*slash = '/';
1175		if (p == slash + 1 || *p || prefix > 32)
1176			return -1;
1177		*mask <<= (32 - prefix);
1178	}
1179	return 0;
1180}
1181
1182/*
1183 * Parse a string containing a value and an optional colon separated mask.
1184 */
1185static int
1186parse_val_mask_param(const char *s, uint32_t *val, uint32_t *mask,
1187    uint32_t default_mask)
1188{
1189	char *p;
1190
1191	*mask = default_mask;
1192	*val = strtoul(s, &p, 0);
1193	if (p == s || *val > default_mask)
1194		return -1;
1195	if (*p == ':' && p[1])
1196		*mask = strtoul(p + 1, &p, 0);
1197	return *p || *mask > default_mask ? -1 : 0;
1198}
1199
1200static int
1201parse_trace_param(const char *s, uint32_t *val, uint32_t *mask)
1202{
1203	return strchr(s, '.') ? parse_ipaddr(s, val, mask) :
1204				parse_val_mask_param(s, val, mask, 0xffffffffU);
1205}
1206
1207static int
1208trace_config(int argc, char *argv[], int start_arg, const char *iff_name)
1209{
1210	uint32_t val, mask;
1211	struct ch_trace trace;
1212
1213	if (argc == start_arg)
1214		return -1;
1215
1216	memset(&trace, 0, sizeof(trace));
1217	if (!strcmp(argv[start_arg], "tx"))
1218		trace.config_tx = 1;
1219	else if (!strcmp(argv[start_arg], "rx"))
1220		trace.config_rx = 1;
1221	else if (!strcmp(argv[start_arg], "all"))
1222		trace.config_tx = trace.config_rx = 1;
1223	else
1224		errx(1, "bad trace filter \"%s\"; must be one of \"rx\", "
1225		     "\"tx\" or \"all\"", argv[start_arg]);
1226
1227	if (argc == ++start_arg)
1228		return -1;
1229	if (!strcmp(argv[start_arg], "on")) {
1230		trace.trace_tx = trace.config_tx;
1231		trace.trace_rx = trace.config_rx;
1232	} else if (strcmp(argv[start_arg], "off"))
1233		errx(1, "bad argument \"%s\"; must be \"on\" or \"off\"",
1234		     argv[start_arg]);
1235
1236	start_arg++;
1237	if (start_arg < argc && !strcmp(argv[start_arg], "not")) {
1238		trace.invert_match = 1;
1239		start_arg++;
1240	}
1241
1242	while (start_arg + 2 <= argc) {
1243		int ret = parse_trace_param(argv[start_arg + 1], &val, &mask);
1244
1245		if (!strcmp(argv[start_arg], "interface")) {
1246			trace.intf = val;
1247			trace.intf_mask = mask;
1248		} else if (!strcmp(argv[start_arg], "sip")) {
1249			trace.sip = val;
1250			trace.sip_mask = mask;
1251		} else if (!strcmp(argv[start_arg], "dip")) {
1252			trace.dip = val;
1253			trace.dip_mask = mask;
1254		} else if (!strcmp(argv[start_arg], "sport")) {
1255			trace.sport = val;
1256			trace.sport_mask = mask;
1257		} else if (!strcmp(argv[start_arg], "dport")) {
1258			trace.dport = val;
1259			trace.dport_mask = mask;
1260		} else if (!strcmp(argv[start_arg], "vlan")) {
1261			trace.vlan = val;
1262			trace.vlan_mask = mask;
1263		} else if (!strcmp(argv[start_arg], "proto")) {
1264			trace.proto = val;
1265			trace.proto_mask = mask;
1266		} else
1267			errx(1, "unknown trace parameter \"%s\"\n"
1268			     "known parameters are \"interface\", \"sip\", "
1269			     "\"dip\", \"sport\", \"dport\", \"vlan\", "
1270			     "\"proto\"", argv[start_arg]);
1271		if (ret < 0)
1272			errx(1, "bad parameter \"%s\"", argv[start_arg + 1]);
1273		start_arg += 2;
1274	}
1275	if (start_arg != argc)
1276		errx(1, "unknown parameter \"%s\"", argv[start_arg]);
1277
1278	if (doit(iff_name, CHELSIO_SET_TRACE_FILTER, &trace) < 0)
1279		err(1, "trace");
1280	return 0;
1281}
1282
1283static void
1284show_filters(const char *iff_name)
1285{
1286	static const char *pkt_type[] = { "*", "tcp", "udp", "frag" };
1287	struct ch_filter op;
1288	union {
1289		uint32_t nip;
1290		uint8_t octet[4];
1291	} nsip, ndip;
1292	char sip[20], dip[20];
1293	int header = 0;
1294
1295	bzero(&op, sizeof(op));
1296	op.filter_id = 0xffffffff;
1297
1298	do {
1299		if (doit(iff_name, CHELSIO_GET_FILTER, &op) < 0)
1300			err(1, "list filters");
1301
1302		if (op.filter_id == 0xffffffff)
1303			break;
1304
1305		if (!header) {
1306			printf("index         SIP                DIP     sport "
1307			    "dport VLAN PRI P/MAC type Q\n");
1308			header = 1;
1309		}
1310
1311		nsip.nip = htonl(op.val.sip);
1312		ndip.nip = htonl(op.val.dip);
1313
1314		sprintf(sip, "%u.%u.%u.%u/%-2u", nsip.octet[0], nsip.octet[1],
1315		    nsip.octet[2], nsip.octet[3],
1316		    op.mask.sip ? 33 - ffs(op.mask.sip) : 0);
1317		sprintf(dip, "%u.%u.%u.%u", ndip.octet[0], ndip.octet[1],
1318		    ndip.octet[2], ndip.octet[3]);
1319		printf("%5zu %18s %15s ", (size_t)op.filter_id, sip, dip);
1320		printf(op.val.sport ? "%5u " : "    * ", op.val.sport);
1321		printf(op.val.dport ? "%5u " : "    * ", op.val.dport);
1322		printf(op.val.vlan != 0xfff ? "%4u " : "   * ", op.val.vlan);
1323		printf(op.val.vlan_prio == 7 ?  "  * " :
1324		    "%1u/%1u ", op.val.vlan_prio, op.val.vlan_prio | 1);
1325		if (op.mac_addr_idx == 0xffff)
1326			printf("*/*   ");
1327		else if (op.mac_hit)
1328			printf("%1u/%3u ", (op.mac_addr_idx >> 3) & 0x1,
1329			    (op.mac_addr_idx) & 0x7);
1330		else
1331			printf("%1u/  * ", (op.mac_addr_idx >> 3) & 0x1);
1332		printf("%4s ", pkt_type[op.proto]);
1333		if (!op.pass)
1334			printf("-\n");
1335		else if (op.rss)
1336			printf("*\n");
1337		else
1338			printf("%1u\n", op.qset);
1339	} while (1);
1340}
1341
1342static int
1343filter_config(int argc, char *argv[], int start_arg, const char *iff_name)
1344{
1345	int ret = 0;
1346	uint32_t val, mask;
1347	struct ch_filter op;
1348
1349	if (argc < start_arg + 1)
1350		return -1;
1351
1352	memset(&op, 0, sizeof(op));
1353	op.mac_addr_idx = 0xffff;
1354	op.rss = 1;
1355
1356	if (argc == start_arg + 1 && !strcmp(argv[start_arg], "list")) {
1357		show_filters(iff_name);
1358		return 0;
1359	}
1360
1361	if (get_int_arg(argv[start_arg++], &op.filter_id))
1362		return -1;
1363	if (argc == start_arg + 1 && (!strcmp(argv[start_arg], "delete") ||
1364				      !strcmp(argv[start_arg], "clear"))) {
1365		if (doit(iff_name, CHELSIO_DEL_FILTER, &op) < 0) {
1366			if (errno == EBUSY)
1367				err(1, "no filter support when offload in use");
1368			err(1, "delete filter");
1369		}
1370		return 0;
1371	}
1372
1373	while (start_arg + 2 <= argc) {
1374		if (!strcmp(argv[start_arg], "sip")) {
1375			ret = parse_ipaddr(argv[start_arg + 1], &op.val.sip,
1376					   &op.mask.sip);
1377		} else if (!strcmp(argv[start_arg], "dip")) {
1378			ret = parse_ipaddr(argv[start_arg + 1], &op.val.dip,
1379					   &op.mask.dip);
1380		} else if (!strcmp(argv[start_arg], "sport")) {
1381			ret = parse_val_mask_param(argv[start_arg + 1],
1382						   &val, &mask, 0xffff);
1383			op.val.sport = val;
1384			op.mask.sport = mask;
1385		} else if (!strcmp(argv[start_arg], "dport")) {
1386			ret = parse_val_mask_param(argv[start_arg + 1],
1387						   &val, &mask, 0xffff);
1388			op.val.dport = val;
1389			op.mask.dport = mask;
1390		} else if (!strcmp(argv[start_arg], "vlan")) {
1391			ret = parse_val_mask_param(argv[start_arg + 1],
1392						   &val, &mask, 0xfff);
1393			op.val.vlan = val;
1394			op.mask.vlan = mask;
1395		} else if (!strcmp(argv[start_arg], "prio")) {
1396			ret = parse_val_mask_param(argv[start_arg + 1],
1397						   &val, &mask, 7);
1398			op.val.vlan_prio = val;
1399			op.mask.vlan_prio = mask;
1400		} else if (!strcmp(argv[start_arg], "mac")) {
1401			if (!strcmp(argv[start_arg + 1], "none"))
1402				val = -1;
1403			else
1404				ret = get_int_arg(argv[start_arg + 1], &val);
1405			op.mac_hit = val != (uint32_t)-1;
1406			op.mac_addr_idx = op.mac_hit ? val : 0;
1407		} else if (!strcmp(argv[start_arg], "type")) {
1408			if (!strcmp(argv[start_arg + 1], "tcp"))
1409				op.proto = 1;
1410			else if (!strcmp(argv[start_arg + 1], "udp"))
1411				op.proto = 2;
1412			else if (!strcmp(argv[start_arg + 1], "frag"))
1413				op.proto = 3;
1414			else
1415				errx(1, "unknown type \"%s\"; must be one of "
1416				     "\"tcp\", \"udp\", or \"frag\"",
1417				     argv[start_arg + 1]);
1418		} else if (!strcmp(argv[start_arg], "queue")) {
1419			ret = get_int_arg(argv[start_arg + 1], &val);
1420			op.qset = val;
1421			op.rss = 0;
1422		} else if (!strcmp(argv[start_arg], "action")) {
1423			if (!strcmp(argv[start_arg + 1], "pass"))
1424				op.pass = 1;
1425			else if (strcmp(argv[start_arg + 1], "drop"))
1426				errx(1, "unknown action \"%s\"; must be one of "
1427				     "\"pass\" or \"drop\"",
1428				     argv[start_arg + 1]);
1429		} else
1430 			errx(1, "unknown filter parameter \"%s\"\n"
1431			     "known parameters are \"mac\", \"sip\", "
1432			     "\"dip\", \"sport\", \"dport\", \"vlan\", "
1433			     "\"prio\", \"type\", \"queue\", and \"action\"",
1434			     argv[start_arg]);
1435		if (ret < 0)
1436			errx(1, "bad value \"%s\" for parameter \"%s\"",
1437			     argv[start_arg + 1], argv[start_arg]);
1438		start_arg += 2;
1439	}
1440	if (start_arg != argc)
1441		errx(1, "no value for \"%s\"", argv[start_arg]);
1442
1443	if (doit(iff_name, CHELSIO_SET_FILTER, &op) < 0) {
1444		if (errno == EBUSY)
1445			err(1, "no filter support when offload in use");
1446		err(1, "set filter");
1447	}
1448
1449	return 0;
1450}
1451static int
1452get_sched_param(int argc, char *argv[], int pos, unsigned int *valp)
1453{
1454	if (pos + 1 >= argc)
1455		errx(1, "missing value for %s", argv[pos]);
1456	if (get_int_arg(argv[pos + 1], valp))
1457		exit(1);
1458	return 0;
1459}
1460
1461static int
1462tx_sched(int argc, char *argv[], int start_arg, const char *iff_name)
1463{
1464	struct ch_hw_sched op;
1465	unsigned int idx, val;
1466
1467	if (argc < 5 || get_int_arg(argv[start_arg++], &idx))
1468		return -1;
1469
1470	op.sched = idx;
1471	op.mode = op.channel = -1;
1472	op.kbps = op.class_ipg = op.flow_ipg = -1;
1473
1474	while (argc > start_arg) {
1475		if (!strcmp(argv[start_arg], "mode")) {
1476			if (start_arg + 1 >= argc)
1477				errx(1, "missing value for mode");
1478			if (!strcmp(argv[start_arg + 1], "class"))
1479				op.mode = 0;
1480			else if (!strcmp(argv[start_arg + 1], "flow"))
1481				op.mode = 1;
1482			else
1483				errx(1, "bad mode \"%s\"", argv[start_arg + 1]);
1484		} else if (!strcmp(argv[start_arg], "channel") &&
1485			 !get_sched_param(argc, argv, start_arg, &val))
1486			op.channel = val;
1487		else if (!strcmp(argv[start_arg], "rate") &&
1488			 !get_sched_param(argc, argv, start_arg, &val))
1489			op.kbps = val;
1490		else if (!strcmp(argv[start_arg], "ipg") &&
1491			 !get_sched_param(argc, argv, start_arg, &val))
1492			op.class_ipg = val;
1493		else if (!strcmp(argv[start_arg], "flowipg") &&
1494			 !get_sched_param(argc, argv, start_arg, &val))
1495			op.flow_ipg = val;
1496		else
1497			errx(1, "unknown scheduler parameter \"%s\"",
1498			     argv[start_arg]);
1499		start_arg += 2;
1500	}
1501
1502	if (doit(iff_name, CHELSIO_SET_HW_SCHED, &op) < 0)
1503		 err(1, "pktsched");
1504
1505	return 0;
1506}
1507
1508static int
1509pktsched(int argc, char *argv[], int start_arg, const char *iff_name)
1510{
1511	struct ch_pktsched_params op;
1512	unsigned int idx, min = -1, max, binding = -1;
1513
1514	if (argc < 4)
1515		errx(1, "no scheduler specified");
1516
1517	if (!strcmp(argv[start_arg], "port")) {
1518		if (argc != start_arg + 4)
1519			return -1;
1520		if (get_int_arg(argv[start_arg + 1], &idx) ||
1521		    get_int_arg(argv[start_arg + 2], &min) ||
1522		    get_int_arg(argv[start_arg + 3], &max))
1523			return -1;
1524		op.sched = 0;
1525	} else if (!strcmp(argv[start_arg], "tunnelq")) {
1526		if (argc != start_arg + 4)
1527			return -1;
1528		if (get_int_arg(argv[start_arg + 1], &idx) ||
1529		    get_int_arg(argv[start_arg + 2], &max) ||
1530		    get_int_arg(argv[start_arg + 3], &binding))
1531			return -1;
1532		op.sched = 1;
1533	} else if (!strcmp(argv[start_arg], "tx"))
1534		return tx_sched(argc, argv, start_arg + 1, iff_name);
1535	else
1536		errx(1, "unknown scheduler \"%s\"; must be one of \"port\", "
1537			"\"tunnelq\" or \"tx\"", argv[start_arg]);
1538
1539	op.idx = idx;
1540	op.min = min;
1541	op.max = max;
1542	op.binding = binding;
1543	if (doit(iff_name, CHELSIO_SET_PKTSCHED, &op) < 0)
1544		 err(1, "pktsched");
1545
1546	return 0;
1547}
1548
1549static int
1550clear_stats(int argc, char *argv[], int start_arg, const char *iff_name)
1551{
1552	(void) argc;
1553	(void) argv;
1554	(void) start_arg;
1555
1556	if (doit(iff_name, CHELSIO_CLEAR_STATS, NULL) < 0)
1557		 err(1, "clearstats");
1558
1559	return 0;
1560}
1561
1562static int
1563get_up_la(int argc, char *argv[], int start_arg, const char *iff_name)
1564{
1565	struct ch_up_la la;
1566	int i, idx, max_idx, entries;
1567
1568	(void) argc;
1569	(void) argv;
1570	(void) start_arg;
1571
1572	la.stopped = 0;
1573	la.idx = -1;
1574	la.bufsize = LA_BUFSIZE;
1575	la.data = malloc(la.bufsize);
1576	if (!la.data)
1577		err(1, "uP_LA malloc");
1578
1579	if (doit(iff_name, CHELSIO_GET_UP_LA, &la) < 0)
1580		 err(1, "uP_LA");
1581
1582	if (la.stopped)
1583		printf("LA is not running\n");
1584
1585	entries = la.bufsize / 4;
1586	idx = (int)la.idx;
1587	max_idx = (entries / 4) - 1;
1588	for (i = 0; i < max_idx; i++) {
1589		printf("%04x %08x %08x\n",
1590		       la.data[idx], la.data[idx+2], la.data[idx+1]);
1591		idx = (idx + 4) & (entries - 1);
1592	}
1593
1594	return 0;
1595}
1596
1597static int
1598get_up_ioqs(int argc, char *argv[], int start_arg, const char *iff_name)
1599{
1600	struct ch_up_ioqs ioqs;
1601	int i, entries;
1602
1603	(void) argc;
1604	(void) argv;
1605	(void) start_arg;
1606
1607	bzero(&ioqs, sizeof(ioqs));
1608	ioqs.bufsize = IOQS_BUFSIZE;
1609	ioqs.data = malloc(IOQS_BUFSIZE);
1610	if (!ioqs.data)
1611		err(1, "uP_IOQs malloc");
1612
1613	if (doit(iff_name, CHELSIO_GET_UP_IOQS, &ioqs) < 0)
1614		 err(1, "uP_IOQs");
1615
1616	printf("ioq_rx_enable   : 0x%08x\n", ioqs.ioq_rx_enable);
1617	printf("ioq_tx_enable   : 0x%08x\n", ioqs.ioq_tx_enable);
1618	printf("ioq_rx_status   : 0x%08x\n", ioqs.ioq_rx_status);
1619	printf("ioq_tx_status   : 0x%08x\n", ioqs.ioq_tx_status);
1620
1621	entries = ioqs.bufsize / sizeof(struct t3_ioq_entry);
1622	for (i = 0; i < entries; i++) {
1623		printf("\nioq[%d].cp       : 0x%08x\n", i,
1624		       ioqs.data[i].ioq_cp);
1625		printf("ioq[%d].pp       : 0x%08x\n", i,
1626		       ioqs.data[i].ioq_pp);
1627		printf("ioq[%d].alen     : 0x%08x\n", i,
1628		       ioqs.data[i].ioq_alen);
1629		printf("ioq[%d].stats    : 0x%08x\n", i,
1630		       ioqs.data[i].ioq_stats);
1631		printf("  sop %u\n", ioqs.data[i].ioq_stats >> 16);
1632		printf("  eop %u\n", ioqs.data[i].ioq_stats  & 0xFFFF);
1633	}
1634
1635	return 0;
1636}
1637
1638static int
1639run_cmd(int argc, char *argv[], const char *iff_name)
1640{
1641	int r = -1;
1642
1643	if (!strcmp(argv[2], "reg"))
1644		r = register_io(argc, argv, 3, iff_name);
1645	else if (!strcmp(argv[2], "mdio"))
1646		r = mdio_io(argc, argv, 3, iff_name);
1647	else if (!strcmp(argv[2], "mtus"))
1648		r = mtu_tab_op(argc, argv, 3, iff_name);
1649	else if (!strcmp(argv[2], "pm"))
1650		r = conf_pm(argc, argv, 3, iff_name);
1651	else if (!strcmp(argv[2], "regdump"))
1652		r = dump_regs(argc, argv, 3, iff_name);
1653	else if (!strcmp(argv[2], "tcamdump"))
1654		r = dump_tcam(argc, argv, 3, iff_name);
1655	else if (!strcmp(argv[2], "memdump"))
1656		r = dump_mc7(argc, argv, 3, iff_name);
1657	else if (!strcmp(argv[2], "meminfo"))
1658		r = meminfo(argc, argv, 3, iff_name);
1659	else if (!strcmp(argv[2], "context"))
1660		r = get_sge_context(argc, argv, 3, iff_name);
1661	else if (!strcmp(argv[2], "desc"))
1662		r = get_sge_desc(argc, argv, 3, iff_name);
1663	else if (!strcmp(argv[2], "loadfw"))
1664		r = load_fw(argc, argv, 3, iff_name);
1665	else if (!strcmp(argv[2], "loadboot"))
1666		r = load_boot(argc, argv, 3, iff_name);
1667	else if (!strcmp(argv[2], "proto"))
1668		r = proto_sram_op(argc, argv, 3, iff_name);
1669	else if (!strcmp(argv[2], "qset"))
1670		r = qset_config(argc, argv, 3, iff_name);
1671	else if (!strcmp(argv[2], "qsets"))
1672		r = qset_num_config(argc, argv, 3, iff_name);
1673	else if (!strcmp(argv[2], "trace"))
1674		r = trace_config(argc, argv, 3, iff_name);
1675	else if (!strcmp(argv[2], "pktsched"))
1676		r = pktsched(argc, argv, 3, iff_name);
1677	else if (!strcmp(argv[2], "tcb"))
1678		r = get_tcb2(argc, argv, 3, iff_name);
1679	else if (!strcmp(argv[2], "filter"))
1680		r = filter_config(argc, argv, 3, iff_name);
1681	else if (!strcmp(argv[2], "clearstats"))
1682		r = clear_stats(argc, argv, 3, iff_name);
1683	else if (!strcmp(argv[2], "la"))
1684		r = get_up_la(argc, argv, 3, iff_name);
1685	else if (!strcmp(argv[2], "ioqs"))
1686		r = get_up_ioqs(argc, argv, 3, iff_name);
1687
1688	if (r == -1)
1689		usage(stderr);
1690
1691	return (0);
1692}
1693
1694static int
1695run_cmd_loop(int argc, char *argv[], const char *iff_name)
1696{
1697	int n;
1698	unsigned int i;
1699	char buf[64];
1700	char *args[8], *s;
1701
1702	(void) argc;
1703	args[0] = argv[0];
1704	args[1] = argv[1];
1705
1706	/*
1707	 * Fairly simplistic loop.  Displays a "> " prompt and processes any
1708	 * input as a cxgbtool command.  You're supposed to enter only the part
1709	 * after "cxgbtool cxgbX".  Use "quit" or "exit" to exit.  Any error in
1710	 * the command will also terminate cxgbtool.
1711	 */
1712	for (;;) {
1713		fprintf(stdout, "> ");
1714		fflush(stdout);
1715		n = read(STDIN_FILENO, buf, sizeof(buf) - 1);
1716		if (n <= 0)
1717			return (0);
1718
1719		if (buf[--n] != '\n')
1720			continue;
1721		else
1722			buf[n] = 0;
1723
1724		s = &buf[0];
1725		for (i = 2; i < sizeof(args)/sizeof(args[0]) - 1; i++) {
1726			while (s && (*s == ' ' || *s == '\t'))
1727				s++;
1728			if ((args[i] = strsep(&s, " \t")) == NULL)
1729				break;
1730		}
1731		args[sizeof(args)/sizeof(args[0]) - 1] = 0;
1732
1733		if (!strcmp(args[2], "quit") || !strcmp(args[2], "exit"))
1734			return (0);
1735
1736		(void) run_cmd(i, args, iff_name);
1737	}
1738
1739	/* Can't really get here */
1740	return (0);
1741}
1742
1743int
1744main(int argc, char *argv[])
1745{
1746	int r = -1;
1747	const char *iff_name;
1748
1749	progname = argv[0];
1750
1751	if (argc == 2) {
1752		if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
1753			usage(stdout);
1754		if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) {
1755			printf("%s version %s\n", PROGNAME, VERSION);
1756			printf("%s\n", COPYRIGHT);
1757			exit(0);
1758		}
1759	}
1760
1761	if (argc < 3) usage(stderr);
1762
1763	iff_name = argv[1];
1764
1765	if (argc == 3 && !strcmp(argv[2], "stdio"))
1766		r = run_cmd_loop(argc, argv, iff_name);
1767	else
1768		r = run_cmd(argc, argv, iff_name);
1769
1770	return (r);
1771}
1772