xref: /illumos-gate/usr/src/cmd/cxgbetool/cxgbetool.c (revision fcabbd87549f01117a5d466ddcaaf1fa39ee2c14)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2018 by Chelsio Communications, Inc.
14  */
15 
16 /*
17  * Copyright 2019 Joyent, Inc.
18  * Copyright 2023 Oxide Computer Company
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <stropts.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <sys/socket.h>
29 #include <strings.h>
30 #include <sys/varargs.h>
31 #include <errno.h>
32 #include <sys/byteorder.h>
33 #include <inttypes.h>
34 #include <sys/sysmacros.h>
35 #include <err.h>
36 #include <libdevinfo.h>
37 
38 #include "t4nex.h"
39 #include "version.h"
40 #include "osdep.h"
41 #include "t4fw_interface.h"
42 #include "cudbg.h"
43 #include "cudbg_lib_common.h"
44 #include "cudbg_entity.h"
45 
46 #define CUDBG_SIZE (32 * 1024 * 1024)
47 #define CUDBG_MAX_ENTITY_STR_LEN 4096
48 #define MAX_PARAM_LEN 4096
49 #ifndef MAX
50 #define MAX(x, y)       ((x) > (y) ? (x) : (y))
51 #endif
52 
53 struct reg_info {
54 	const char *name;
55 	uint32_t addr;
56 	uint32_t len;
57 };
58 
59 struct mod_regs {
60 	const char *name;
61 	const struct reg_info *ri;
62 	unsigned int offset;
63 };
64 
65 #include "reg_defs.h"
66 
67 static char cxgbetool_nexus[PATH_MAX];
68 
69 char *option_list[] = {
70 	"--collect",
71 	"--view",
72 	"--version",
73 };
74 
75 enum {
76 	CUDBG_OPT_COLLECT,
77 	CUDBG_OPT_VIEW,
78 	CUDBG_OPT_VERSION,
79 };
80 
81 /*
82  * Firmware Device Log Dumping
83  */
84 
85 static const char * const devlog_level_strings[] = {
86 	[FW_DEVLOG_LEVEL_EMERG]		= "EMERG",
87 	[FW_DEVLOG_LEVEL_CRIT]		= "CRIT",
88 	[FW_DEVLOG_LEVEL_ERR]		= "ERR",
89 	[FW_DEVLOG_LEVEL_NOTICE]	= "NOTICE",
90 	[FW_DEVLOG_LEVEL_INFO]		= "INFO",
91 	[FW_DEVLOG_LEVEL_DEBUG]		= "DEBUG"
92 };
93 
94 static const char * const devlog_facility_strings[] = {
95 	[FW_DEVLOG_FACILITY_CORE]	= "CORE",
96 	[FW_DEVLOG_FACILITY_CF]		= "CF",
97 	[FW_DEVLOG_FACILITY_SCHED]	= "SCHED",
98 	[FW_DEVLOG_FACILITY_TIMER]	= "TIMER",
99 	[FW_DEVLOG_FACILITY_RES]	= "RES",
100 	[FW_DEVLOG_FACILITY_HW]		= "HW",
101 	[FW_DEVLOG_FACILITY_FLR]	= "FLR",
102 	[FW_DEVLOG_FACILITY_DMAQ]	= "DMAQ",
103 	[FW_DEVLOG_FACILITY_PHY]	= "PHY",
104 	[FW_DEVLOG_FACILITY_MAC]	= "MAC",
105 	[FW_DEVLOG_FACILITY_PORT]	= "PORT",
106 	[FW_DEVLOG_FACILITY_VI]		= "VI",
107 	[FW_DEVLOG_FACILITY_FILTER]	= "FILTER",
108 	[FW_DEVLOG_FACILITY_ACL]	= "ACL",
109 	[FW_DEVLOG_FACILITY_TM]		= "TM",
110 	[FW_DEVLOG_FACILITY_QFC]	= "QFC",
111 	[FW_DEVLOG_FACILITY_DCB]	= "DCB",
112 	[FW_DEVLOG_FACILITY_ETH]	= "ETH",
113 	[FW_DEVLOG_FACILITY_OFLD]	= "OFLD",
114 	[FW_DEVLOG_FACILITY_RI]		= "RI",
115 	[FW_DEVLOG_FACILITY_ISCSI]	= "ISCSI",
116 	[FW_DEVLOG_FACILITY_FCOE]	= "FCOE",
117 	[FW_DEVLOG_FACILITY_FOISCSI]	= "FOISCSI",
118 	[FW_DEVLOG_FACILITY_FOFCOE]	= "FOFCOE",
119 	[FW_DEVLOG_FACILITY_CHNET]	= "CHNET",
120 };
121 
122 static const char *progname;
123 int set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list);
124 
check_option(char * opt)125 static int check_option(char *opt)
126 {
127 	int i;
128 
129 	for (i = 0; i < ARRAY_SIZE(option_list); i++) {
130 		if (!strcmp(opt, option_list[i]))
131 			return i;
132 	}
133 	return -1;
134 }
135 
usage(FILE * fp)136 static void usage(FILE *fp)
137 {
138 	fprintf(fp, "Usage: %s <t4nex# | cxgbe#> [operation]\n", progname);
139 	fprintf(fp,
140 	    "\tdevlog                              show device log\n"
141 	    "\tloadfw <FW image>                   Flash the FW image\n"
142 	    "\tcudbg <option> [<args>]             Chelsio Unified Debugger\n"
143 	    "\tregdump [<module>]                  Dump registers\n"
144 	    "\treg <address>[=<val>]               Read or write the registers\n");
145 	exit(fp == stderr ? 1 : 0);
146 }
147 
148 static int
doit(const char * iff_name,unsigned long cmd,void * data)149 doit(const char *iff_name, unsigned long cmd, void *data)
150 {
151 	int fd = 0;
152 	int rc = 0;
153 
154 	if ((fd = open(iff_name, O_RDWR)) < 0)
155 		return (-1);
156 
157 	rc = (ioctl(fd, cmd, data) < 0) ? errno : rc;
158 	close(fd);
159 	return (rc);
160 }
161 
162 static void
get_devlog(int argc,char * argv[],int start_arg,const char * iff_name)163 get_devlog(int argc, char *argv[], int start_arg, const char *iff_name)
164 {
165 	struct t4_devlog *devlog;
166 	struct fw_devlog_e *entry, *buf;
167 	int rc = 0, first = 0, nentries, i, j, len;
168 	uint64_t ftstamp = UINT64_MAX;
169 
170 	devlog = malloc(T4_DEVLOG_SIZE + sizeof (struct t4_devlog));
171 	if (!devlog)
172 		err(1, "%s: can't allocate devlog buffer", __func__);
173 
174 	devlog->len = T4_DEVLOG_SIZE;
175 	/* Get device log */
176 	rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog);
177 	if (rc == ENOBUFS) {
178 		/*
179 		 * Default buffer size is not sufficient to hold device log.
180 		 * Driver has updated the devlog.len to indicate the expected
181 		 * size. Free the currently allocated devlog.data, allocate
182 		 * again with right size and retry.
183 		 */
184 		len = devlog->len;
185 		free(devlog);
186 
187 		if ((devlog = malloc(len + sizeof (struct t4_devlog))) == NULL)
188 			err(1, "%s: can't reallocate devlog buffer", __func__);
189 
190 		rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog);
191 	}
192 	if (rc) {
193 		free(devlog);
194 		errx(1, "%s: can't get device log", __func__);
195 	}
196 
197 	/* There are nentries number of entries in the buffer */
198 	nentries = (devlog->len / sizeof (struct fw_devlog_e));
199 
200 	buf = (struct fw_devlog_e *)devlog->data;
201 
202 	/* Find the first entry */
203 	for (i = 0; i < nentries; i++) {
204 		entry = &buf[i];
205 
206 		if (entry->timestamp == 0)
207 			break;
208 
209 		entry->timestamp = BE_64(entry->timestamp);
210 		entry->seqno = BE_32(entry->seqno);
211 		for (j = 0; j < 8; j++)
212 			entry->params[j] = BE_32(entry->params[j]);
213 
214 		if (entry->timestamp < ftstamp) {
215 			ftstamp = entry->timestamp;
216 			first = i;
217 		}
218 	}
219 
220 	printf("%10s  %15s  %8s  %8s  %s\n", "Seq#", "Tstamp", "Level",
221 	    "Facility", "Message");
222 
223 	i = first;
224 
225 	do {
226 		entry = &buf[i];
227 
228 		if (entry->timestamp == 0)
229 			break;
230 
231 		printf("%10d  %15llu  %8s  %8s  ", entry->seqno,
232 		    entry->timestamp,
233 		    (entry->level < ARRAY_SIZE(devlog_level_strings) ?
234 		    devlog_level_strings[entry->level] : "UNKNOWN"),
235 		    (entry->facility < ARRAY_SIZE(devlog_facility_strings) ?
236 		    devlog_facility_strings[entry->facility] : "UNKNOWN"));
237 
238 		printf((const char *)entry->fmt, entry->params[0],
239 		    entry->params[1], entry->params[2], entry->params[3],
240 		    entry->params[4], entry->params[5], entry->params[6],
241 		    entry->params[7]);
242 
243 		if (++i == nentries)
244 			i = 0;
245 
246 	} while (i != first);
247 
248 	free(devlog);
249 }
250 
xtract(uint32_t val,int shift,int len)251 static uint32_t xtract(uint32_t val, int shift, int len)
252 {
253 	return (val >> shift) & ((1 << len) - 1);
254 }
255 
dump_block_regs(const struct reg_info * reg_array,const u32 * regs)256 int dump_block_regs(const struct reg_info *reg_array, const u32 *regs)
257 {
258 	uint32_t reg_val = 0;
259 
260 	for ( ; reg_array->name; ++reg_array) {
261 		if (!reg_array->len) {
262 			reg_val = regs[reg_array->addr / 4];
263 			printf("[%#7x] %-47s %#-10x %u\n", reg_array->addr,
264 			       reg_array->name, reg_val, reg_val);
265 		} else {
266 			uint32_t v = xtract(reg_val, reg_array->addr,
267 					    reg_array->len);
268 			printf("    %*u:%u %-47s %#-10x %u\n",
269 			       reg_array->addr < 10 ? 3 : 2,
270 			       reg_array->addr + reg_array->len - 1,
271 			       reg_array->addr, reg_array->name, v, v);
272 		}
273 	}
274 	return 1;
275 }
276 
dump_regs_table(int argc,char * argv[],int start_arg,const u32 * regs,const struct mod_regs * modtab,int nmodules,const char * modnames)277 int dump_regs_table(int argc, char *argv[], int start_arg,
278 		    const u32 *regs, const struct mod_regs *modtab,
279 		    int nmodules, const char *modnames)
280 {
281 	int match = 0;
282 	const char *block_name = NULL;
283 
284 	if (argc == start_arg + 1)
285 		block_name = argv[start_arg];
286 	else if (argc != start_arg)
287 		return -1;
288 
289 	for ( ; nmodules; nmodules--, modtab++) {
290 		if (!block_name || !strcmp(block_name, modtab->name))
291 			match += dump_block_regs(modtab->ri,
292 						 regs + modtab->offset);
293 	}
294 	if (!match)
295 		errx(1, "unknown block \"%s\"\navailable: %s", block_name,
296 		     modnames);
297 	return 0;
298 }
299 
300 #define T5_MODREGS(name) { #name, t5_##name##_regs }
301 static int
dump_regs_t5(int argc,char * argv[],int start_arg,uint32_t * regs)302 dump_regs_t5(int argc, char *argv[], int start_arg, uint32_t *regs)
303 {
304 	static struct mod_regs t5_mod[] = {
305 		T5_MODREGS(sge),
306 		{ "pci", t5_pcie_regs },
307 		T5_MODREGS(dbg),
308 		{ "mc0", t5_mc_0_regs },
309 		{ "mc1", t5_mc_1_regs },
310 		T5_MODREGS(ma),
311 		{ "edc0", t5_edc_t50_regs },
312 		{ "edc1", t5_edc_t51_regs },
313 		T5_MODREGS(cim),
314 		T5_MODREGS(tp),
315 		{ "ulprx", t5_ulp_rx_regs },
316 		{ "ulptx", t5_ulp_tx_regs },
317 		{ "pmrx", t5_pm_rx_regs },
318 		{ "pmtx", t5_pm_tx_regs },
319 		T5_MODREGS(mps),
320 		{ "cplsw", t5_cpl_switch_regs },
321 		T5_MODREGS(smb),
322 		{ "i2c", t5_i2cm_regs },
323 		T5_MODREGS(mi),
324 		T5_MODREGS(uart),
325 		T5_MODREGS(pmu),
326 		T5_MODREGS(sf),
327 		T5_MODREGS(pl),
328 		T5_MODREGS(le),
329 		T5_MODREGS(ncsi),
330 		T5_MODREGS(mac),
331 		{ "hma", t5_hma_t5_regs }
332 	};
333 
334 	return dump_regs_table(argc, argv, start_arg, regs, t5_mod,
335 			       ARRAY_SIZE(t5_mod),
336 			       "sge, pci, dbg, mc0, mc1, ma, edc0, edc1, cim, "
337 			       "tp, ulprx, ulptx, pmrx, pmtx, mps, cplsw, smb, "
338 			       "i2c, mi, uart, pmu, sf, pl, le, ncsi, "
339 			       "mac, hma");
340 }
341 
342 #undef T5_MODREGS
343 
344 #define T6_MODREGS(name) { #name, t6_##name##_regs }
dump_regs_t6(int argc,char * argv[],int start_arg,const u32 * regs)345 static int dump_regs_t6(int argc, char *argv[], int start_arg, const u32 *regs)
346 {
347 	static struct mod_regs t6_mod[] = {
348 		T6_MODREGS(sge),
349 		{ "pci", t6_pcie_regs },
350 		T6_MODREGS(dbg),
351 		{ "mc0", t6_mc_0_regs },
352 		T6_MODREGS(ma),
353 		{ "edc0", t6_edc_t60_regs },
354 		{ "edc1", t6_edc_t61_regs },
355 		T6_MODREGS(cim),
356 		T6_MODREGS(tp),
357 		{ "ulprx", t6_ulp_rx_regs },
358 		{ "ulptx", t6_ulp_tx_regs },
359 		{ "pmrx", t6_pm_rx_regs },
360 		{ "pmtx", t6_pm_tx_regs },
361 		T6_MODREGS(mps),
362 		{ "cplsw", t6_cpl_switch_regs },
363 		T6_MODREGS(smb),
364 		{ "i2c", t6_i2cm_regs },
365 		T6_MODREGS(mi),
366 		T6_MODREGS(uart),
367 		T6_MODREGS(pmu),
368 		T6_MODREGS(sf),
369 		T6_MODREGS(pl),
370 		T6_MODREGS(le),
371 		T6_MODREGS(ncsi),
372 		T6_MODREGS(mac),
373 		{ "hma", t6_hma_t6_regs }
374 	};
375 
376 	return dump_regs_table(argc, argv, start_arg, regs, t6_mod,
377 			       ARRAY_SIZE(t6_mod),
378 			       "sge, pci, dbg, mc0, ma, edc0, edc1, cim, "
379 			       "tp, ulprx, ulptx, pmrx, pmtx, mps, cplsw, smb, "
380 			       "i2c, mi, uart, pmu, sf, pl, le, ncsi, "
381 			       "mac, hma");
382 }
383 #undef T6_MODREGS
384 
385 static int
get_regdump(int argc,char * argv[],int start_arg,const char * iff_name)386 get_regdump(int argc, char *argv[], int start_arg, const char *iff_name)
387 {
388 	int rc, vers, revision, is_pcie;
389 	uint32_t len, length;
390 	struct t4_regdump *regs;
391 
392 	len = MAX(T5_REGDUMP_SIZE, T6_REGDUMP_SIZE);
393 
394 	regs = malloc(len + sizeof(struct t4_regdump));
395 	if (!regs)
396 		err(1, "%s: can't allocate reg dump buffer", __func__);
397 
398 	regs->len = len;
399 
400 	rc = doit(iff_name, T4_IOCTL_REGDUMP, regs);
401 
402 	if (rc == ENOBUFS) {
403 		length = regs->len;
404 		free(regs);
405 
406 		regs = malloc(length + sizeof(struct t4_regdump));
407 		if (regs == NULL)
408 			err(1, "%s: can't reallocate regs buffer", __func__);
409 
410 		rc = doit(iff_name, T4_IOCTL_REGDUMP, regs);
411 	}
412 
413 	if (rc) {
414 		free(regs);
415 		errx(1, "%s: can't get register dumps", __func__);
416 	}
417 
418 	vers = regs->version & 0x3ff;
419 	revision = (regs->version >> 10) & 0x3f;
420 	is_pcie = (regs->version & 0x80000000) != 0;
421 
422 	if (vers == 5) {
423 		return dump_regs_t5(argc, argv, start_arg,
424 				(uint32_t *)regs->data);
425 	} else if (vers == 6) {
426 		return dump_regs_t6(argc, argv, start_arg,
427 				(uint32_t *)regs->data);
428 	} else {
429 		errx(1, "unknown card type %d.%d.%d", vers, revision, is_pcie);
430 	}
431 
432 	return 0;
433 }
434 
435 static void
write_reg(const char * iff_name,uint32_t addr,uint32_t val)436 write_reg(const char *iff_name, uint32_t addr, uint32_t val)
437 {
438 	struct t4_reg32_cmd reg;
439 
440 	reg.reg = addr;
441 	reg.value = val;
442 
443 	if (doit(iff_name, T4_IOCTL_PUT32, &reg) < 0)
444 		err(1, "register write");
445 }
446 
447 static uint32_t
read_reg(const char * iff_name,uint32_t addr)448 read_reg(const char *iff_name, uint32_t addr)
449 {
450 	struct t4_reg32_cmd reg;
451 
452 	reg.reg = addr;
453 
454 	if (doit(iff_name, T4_IOCTL_GET32, &reg) < 0)
455 		err(1, "register read");
456 	return reg.value;
457 }
458 
register_io(int argc,char * argv[],int start_arg,const char * iff_name)459 static void register_io(int argc, char *argv[], int start_arg,
460 		       const char *iff_name)
461 {
462 	char *p;
463 	uint32_t addr = 0, val = 0, write = 0;
464 
465 	if (argc != start_arg + 1) {
466 		errx(1, "incorrect number of arguments");
467 	}
468 	errno = 0;
469 	addr = strtoul(argv[start_arg], &p, 0);
470 	if (addr == 0 || errno != 0) {
471 		errx(1, "invalid arguments");
472 	}
473 
474 	if (p == argv[start_arg])
475 		return;
476 	if (*p == '=' && p[1]) {
477 		val = strtoul(p + 1, &p, 0);
478 		write = 1;
479 	}
480 	if (*p) {
481 		errx(1, "bad parameter \"%s\"", argv[start_arg]);
482 	}
483 
484 	if (write) {
485 		write_reg(iff_name, addr, val);
486 	} else {
487 		val = read_reg(iff_name, addr);
488 		printf("%#x [%u]\n", val, val);
489 	}
490 }
491 
492 static void
load_fw(int argc,char * argv[],int start_arg,const char * iff_name)493 load_fw(int argc, char *argv[], int start_arg, const char *iff_name)
494 {
495 	const char *fname = argv[start_arg];
496 	struct t4_ldfw *fw;
497 	struct stat sb;
498 	size_t len;
499 	int fd;
500 
501 	if (argc != 4)
502 		errx(1, "incorrect number of arguments");
503 
504 	fd = open(fname, O_RDONLY);
505 	if (fd < 0)
506 		err(1, "%s: opening %s failed", __func__, fname);
507 	if (fstat(fd, &sb) < 0) {
508 		warn("%s: fstat %s failed", __func__, fname);
509 		close(fd);
510 		exit(1);
511 	}
512 	len = (size_t)sb.st_size;
513 
514 	fw = malloc(sizeof (struct t4_ldfw) + len);
515 	if (!fw) {
516 		warn("%s: %s allocate %zu bytes failed",
517 		    __func__, fname, sizeof (struct t4_ldfw) + len);
518 		close(fd);
519 		exit(1);
520 	}
521 
522 	if (read(fd, fw->data, len) < len) {
523 		warn("%s: %s read failed", __func__, fname);
524 		close(fd);
525 		free(fw);
526 		exit(1);
527 	}
528 
529 	close(fd);
530 
531 	fw->len = len;
532 
533 	if (doit(iff_name, T4_IOCTL_LOAD_FW, fw)) {
534 		free(fw);
535 		err(1, "%s: IOCTL failed", __func__);
536 	} else {
537 		printf("FW flash success, reload driver/reboot to take "
538 		    "effect\n");
539 	}
540 
541 	free(fw);
542 }
543 
read_input_file(char * in_file,void ** buf,int * buf_size)544 int read_input_file(char *in_file, void **buf, int *buf_size)
545 {
546 	FILE *fptr = NULL;
547 	size_t count;
548 	int rc = 0;
549 
550 	fptr = fopen(in_file, "rb");
551 	if (!fptr) {
552 		perror("error in opening file ");
553 		rc = -1;
554 		goto out;
555 	}
556 	rc = fseek(fptr, 0, SEEK_END);
557 	if (rc < 0) {
558 		perror("error in seeking file ");
559 		rc = -1;
560 		goto out;
561 	}
562 	*buf_size = ftell(fptr);
563 	rc = fseek(fptr, 0, SEEK_SET);
564 	if (rc < 0) {
565 		perror("error in seeking file ");
566 		rc = -1;
567 		goto out;
568 	}
569 	*buf = (void *) malloc(*buf_size);
570 	if (*buf == NULL) {
571 		rc = CUDBG_STATUS_NOSPACE;
572 		goto out;
573 	}
574 	memset(*buf, 0, *buf_size);
575 
576 	count = fread(*buf, 1, *buf_size, fptr);
577 	if (count != *buf_size) {
578 		perror("error in reading from file ");
579 		goto out;
580 	}
581 
582 out:
583 	if (fptr)
584 		fclose(fptr);
585 
586 	return rc;
587 }
588 
589 static void
do_collect(char * dbg_entity_list,const char * iff_name,const char * fname)590 do_collect(char *dbg_entity_list, const char *iff_name, const char *fname)
591 {
592 	struct t4_cudbg_dump *cudbg;
593 	int fd;
594 
595 	cudbg = malloc(sizeof(struct t4_cudbg_dump) + CUDBG_SIZE);
596 	if (!cudbg) {
597 		err(1, "%s:allocate %d bytes failed", __func__, CUDBG_SIZE);
598 	}
599 
600 	memset(cudbg, 0, sizeof(struct t4_cudbg_dump) + CUDBG_SIZE);
601 
602 	cudbg->len = CUDBG_SIZE;
603 
604 	set_dbg_entity(cudbg->bitmap, dbg_entity_list);
605 
606 	if (doit(iff_name, T4_IOCTL_GET_CUDBG, cudbg)) {
607 		free(cudbg);
608 		err(1, "%s: IOCTL failed", __func__);
609 	}
610 
611 	fd = open(fname, O_CREAT | O_TRUNC | O_EXCL | O_WRONLY,
612 		  S_IRUSR | S_IRGRP | S_IROTH);
613 	if (fd < 0) {
614 		err(1, "%s: file open failed", __func__);
615 	}
616 
617 	write(fd, cudbg->data, cudbg->len);
618 	close(fd);
619 	free(cudbg);
620 }
621 
622 static void
do_view(char * dbg_entity_list,char * in_file)623 do_view(char *dbg_entity_list, char *in_file)
624 {
625 	void *handle = NULL;
626 	void *buf = NULL;
627 	int buf_size = 32 * 1024 * 1024;
628 	int  next_offset = 0;
629 	int data_len;
630 	int rc = 0;
631 
632 	handle = cudbg_alloc_handle();
633 	if (!handle)
634 		goto out;
635 	/* rcad from file */
636 	rc = read_input_file(in_file, &buf, &buf_size);
637 	if (rc < 0) {
638 		goto out;
639 	}
640 
641 	set_dbg_entity(((struct cudbg_private *)handle)->dbg_init.dbg_bitmap,
642 			dbg_entity_list);
643 	do {
644 		if (buf_size - next_offset <= 0)
645 			break;
646 
647 		data_len = cudbg_view(handle, buf+next_offset,
648 				buf_size-next_offset, NULL, 0);
649 		next_offset += data_len;
650 		if (data_len > 0)
651 			printf("\n\t\t<========================END============="\
652 					"===========>\t\t\n\n\n");
653 	} while (data_len > 0);
654 
655 out:
656 	if (buf)
657 		free(buf);
658 	if (handle)
659 		cudbg_free_handle(handle);
660 	return;
661 }
662 
663 typedef void (*cudbg_alias_get_entities_cb)(char *dst, u32 dst_size);
664 
665 struct entity_access_list {
666         const char *name;
667         cudbg_alias_get_entities_cb get_entities_cb;
668 };
669 
670 void
cudbg_append_string(char * dst,u32 dst_size,char * src)671 cudbg_append_string(char *dst, u32 dst_size, char *src)
672 {
673         strlcat(dst, src, dst_size);
674         strlcat(dst, ",", dst_size);
675 }
676 
677 static void
cudbg_alias_get_allregs(char * dst,u32 dst_size)678 cudbg_alias_get_allregs(char *dst, u32 dst_size)
679 {
680         u32 i;
681 
682         for (i = 0; i < ARRAY_SIZE(entity_list); i++)
683                 if (entity_list[i].flag & (1 << ENTITY_FLAG_REGISTER))
684                         cudbg_append_string(dst, dst_size, entity_list[i].name);
685 }
686 
687 static struct entity_access_list ATTRIBUTE_UNUSED entity_alias_list[] = {
688         {"allregs", cudbg_alias_get_allregs},
689 };
690 
691 static int
check_dbg_entity(char * entity)692 check_dbg_entity(char *entity)
693 {
694 	u32 i;
695 
696 	for (i = 0; i < ARRAY_SIZE(entity_list); i++)
697 		if (!strcmp(entity, entity_list[i].name))
698 			return entity_list[i].bit;
699 	return -1;
700 }
701 
702 /* Get matching alias index from entity_alias_list[] */
703 static
get_alias(const char * entity)704 int get_alias(const char *entity)
705 {
706 	u32 i;
707 
708 	for (i = 0; i < ARRAY_SIZE(entity_alias_list); i++)
709 		if (!strcmp(entity, entity_alias_list[i].name))
710 			return i;
711 	return -1;
712 }
713 
714 static int
parse_entity_list(const char * dbg_entity_list,char * dst,u32 dst_size)715 parse_entity_list(const char *dbg_entity_list, char *dst,
716 				    u32 dst_size)
717 {
718 	char *tmp_dbg_entity_list;
719 	char *dbg_entity;
720 	int rc, i;
721 
722 	/* Holds single entity name de-limited by comma */
723 	tmp_dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN);
724 	if (!tmp_dbg_entity_list)
725 		return ENOMEM;
726 
727 	strlcpy(tmp_dbg_entity_list, dbg_entity_list, CUDBG_MAX_ENTITY_STR_LEN);
728 	dbg_entity = strtok(tmp_dbg_entity_list, ",");
729 	while (dbg_entity != NULL) {
730 		/* See if specified entity name exists.  If it doesn't
731 		 * exist, see if the entity name is an alias.
732 		 * If it's not a valid entity name, bail with error.
733 		 */
734 		rc = check_dbg_entity(dbg_entity);
735 		if (rc < 0) {
736 			i = get_alias(dbg_entity);
737 			if (i < 0) {
738 				/* Not an alias, and not a valid entity name */
739 				printf("\nUnknown entity: %s\n", dbg_entity);
740 				rc = CUDBG_STATUS_ENTITY_NOT_FOUND;
741 				goto out_err;
742 			} else {
743 				/* If alias is found, get all the corresponding
744 				 * debug entities related to the alias.
745 				 */
746 				entity_alias_list[i].get_entities_cb(dst, dst_size);
747 			}
748 		} else {
749 			/* Not an alias, but is a valid entity name.
750 			 * So, append the corresponding debug entity.
751 			 */
752 			cudbg_append_string(dst, dst_size, entity_list[rc].name);
753 		}
754 		dbg_entity = strtok(NULL, ",");
755 	}
756 
757 	rc = 0;
758 
759 out_err:
760 	free(tmp_dbg_entity_list);
761 	return rc;
762 }
763 
764 static
get_entity_list(const char * in_buff,char ** out_buff)765 int get_entity_list(const char *in_buff, char **out_buff)
766 {
767 	char *dbg_entity_list;
768 	int rc;
769 
770 	/* Allocate enough to hold converted alias string.
771 	 * Must be freed by caller
772 	 */
773 	dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN);
774 	if (!dbg_entity_list)
775 		return ENOMEM;
776 
777 	memset(dbg_entity_list, 0, CUDBG_MAX_ENTITY_STR_LEN);
778 	rc = parse_entity_list(in_buff, dbg_entity_list,
779 			       CUDBG_MAX_ENTITY_STR_LEN);
780 	if (rc) {
781 		free(dbg_entity_list);
782 		return rc;
783 	}
784 
785 	/* Remove the last comma */
786 	dbg_entity_list[strlen(dbg_entity_list) - 1] = '\0';
787 	*out_buff = dbg_entity_list;
788 	return 0;
789 }
790 
791 static void
put_entity_list(char * buf)792 put_entity_list(char *buf)
793 {
794 	if (buf)
795 		free(buf);
796 }
797 
798 int
set_dbg_entity(u8 * dbg_bitmap,char * dbg_entity_list)799 set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list)
800 {
801 	int i, dbg_entity_bit, rc = 0;
802 	char *dbg_entity;
803 	char *dbg_entity_list_tmp;
804 
805 	dbg_entity_list_tmp = malloc(MAX_PARAM_LEN);
806 	if (!dbg_entity_list_tmp) {
807 		rc = CUDBG_STATUS_NOSPACE;
808 		return rc;
809 	}
810 
811 	if (dbg_entity_list != NULL) {
812 		strlcpy(dbg_entity_list_tmp, dbg_entity_list, MAX_PARAM_LEN);
813 		dbg_entity = strtok(dbg_entity_list_tmp, ",");
814 	}
815 	else
816 		dbg_entity = NULL;
817 
818 	while (dbg_entity != NULL) {
819 		rc = check_dbg_entity(dbg_entity);
820 		if (rc < 0) {
821 			printf("\n\tInvalid debug entity: %s\n", dbg_entity);
822 			//Vishal cudbg_usage();
823 			goto out_free;
824 		}
825 
826 		dbg_entity_bit = rc;
827 
828 		if (dbg_entity_bit == CUDBG_ALL) {
829 			for (i = 1; i < CUDBG_MAX_ENTITY; i++)
830 				set_dbg_bitmap(dbg_bitmap, i);
831 			set_dbg_bitmap(dbg_bitmap, CUDBG_ALL);
832 			break;
833 		} else {
834 			set_dbg_bitmap(dbg_bitmap, dbg_entity_bit);
835 		}
836 
837 		dbg_entity = strtok(NULL, ",");
838 	}
839 
840 	rc = 0;
841 
842 out_free:
843 	free(dbg_entity_list_tmp);
844 	return rc;
845 }
846 
847 
848 static void
get_cudbg(int argc,char * argv[],int start_arg,const char * iff_name)849 get_cudbg(int argc, char *argv[], int start_arg, const char *iff_name)
850 {
851 	char *dbg_entity_list = NULL;
852 	int rc = 0, option;
853 
854 	if (start_arg >= argc)
855 		errx(1, "no option provided");
856 
857 	rc = check_option(argv[start_arg++]);
858 	if (rc < 0) {
859 		errx(1, "%s:Invalid option provided", __func__);
860 	}
861 	option = rc;
862 
863 	if (option == CUDBG_OPT_VERSION) {
864 		printf("Library Version %d.%d.%d\n", CUDBG_MAJOR_VERSION,
865 			CUDBG_MINOR_VERSION, CUDBG_BUILD_VERSION);
866 		return;
867 	}
868 
869 	if (argc < 5) {
870 		errx(1, "Invalid number of arguments\n");
871 	}
872 	rc = get_entity_list(argv[start_arg++],
873 			     &dbg_entity_list);
874 	if (rc) {
875 		errx(1, "Error in parsing entity\n");
876 	}
877 
878 	if (argc < 6) {
879 		errx(1, "File name is missing\n");
880 	}
881 
882 	switch (option) {
883 		case CUDBG_OPT_COLLECT:
884 			do_collect(dbg_entity_list, iff_name, argv[start_arg]);
885 			break;
886 		case CUDBG_OPT_VIEW:
887 			do_view(dbg_entity_list, argv[start_arg]);
888 			break;
889 		default:
890 			errx(1, "Wrong option provided\n");
891 	}
892 
893 	put_entity_list(dbg_entity_list);
894 }
895 
896 static void
run_cmd(int argc,char * argv[],const char * iff_name)897 run_cmd(int argc, char *argv[], const char *iff_name)
898 {
899 	if (strcmp(argv[2], "devlog") == 0)
900 		get_devlog(argc, argv, 3, iff_name);
901 	else if (strcmp(argv[2], "loadfw") == 0)
902 		load_fw(argc, argv, 3, iff_name);
903 	else if (strcmp(argv[2], "cudbg") == 0)
904 		get_cudbg(argc, argv, 3, iff_name);
905 	else if (strcmp(argv[2], "regdump") == 0)
906 		get_regdump(argc, argv, 3, iff_name);
907 	else if (strcmp(argv[2], "reg") == 0)
908 		register_io(argc, argv, 3, iff_name);
909 	else
910 		usage(stderr);
911 }
912 
913 /*
914  * Traditionally we expect to be given a path to the t4nex device control file
915  * hidden in /devices. To make life easier, we want to also support folks using
916  * the driver instance numbers for either a given t4nex%d or cxgbe%d. We check
917  * to see if we've been given a path to a character device and if so, just
918  * continue straight on with the given argument. Otherwise we attempt to map it
919  * to something known.
920  */
921 static const char *
cxgbetool_parse_path(char * arg)922 cxgbetool_parse_path(char *arg)
923 {
924 	struct stat st;
925 	di_node_t root, node;
926 	const char *numptr, *errstr;
927 	size_t drvlen;
928 	int inst;
929 	boolean_t is_t4nex = B_TRUE;
930 	char mname[64];
931 
932 	if (stat(arg, &st) == 0) {
933 		if (S_ISCHR(st.st_mode)) {
934 			return (arg);
935 		}
936 	}
937 
938 	if (strncmp(arg, T4_NEXUS_NAME, sizeof (T4_NEXUS_NAME) - 1) == 0) {
939 		drvlen = sizeof (T4_NEXUS_NAME) - 1;
940 	} else if (strncmp(arg, T4_PORT_NAME, sizeof (T4_PORT_NAME) - 1) == 0) {
941 		is_t4nex = B_FALSE;
942 		drvlen = sizeof (T4_PORT_NAME) - 1;
943 	} else {
944 		errx(EXIT_FAILURE, "cannot use device %s: not a character "
945 		    "device or a %s/%s device instance", arg, T4_PORT_NAME,
946 		    T4_NEXUS_NAME);
947 	}
948 
949 	numptr = arg + drvlen;
950 	inst = (int)strtonum(numptr, 0, INT_MAX, &errstr);
951 	if (errstr != NULL) {
952 		errx(EXIT_FAILURE, "failed to parse instance number '%s': %s",
953 		    numptr, errstr);
954 	}
955 
956 	/*
957 	 * Now that we have the instance here, we need to truncate the string at
958 	 * the end of the driver name otherwise di_drv_first_node() will be very
959 	 * confused as there is no driver called say 't4nex0'.
960 	 */
961 	arg[drvlen] = '\0';
962 	root = di_init("/", DINFOCPYALL);
963 	if (root == DI_NODE_NIL) {
964 		err(EXIT_FAILURE, "failed to initialize libdevinfo while "
965 		    "trying to map device name %s", arg);
966 	}
967 
968 	for (node = di_drv_first_node(arg, root); node != DI_NODE_NIL;
969 	    node = di_drv_next_node(node)) {
970 		char *bpath;
971 		di_minor_t minor = DI_MINOR_NIL;
972 
973 		if (di_instance(node) != inst) {
974 			continue;
975 		}
976 
977 		if (!is_t4nex) {
978 			const char *pdrv;
979 			node = di_parent_node(node);
980 			pdrv = di_driver_name(node);
981 			if (pdrv == NULL || strcmp(pdrv, T4_NEXUS_NAME) != 0) {
982 				errx(EXIT_FAILURE, "%s does not have %s "
983 				    "parent, found %s%d", arg, T4_NEXUS_NAME,
984 				    pdrv != NULL ? pdrv : "unknown",
985 				    pdrv != NULL ? di_instance(node) : -1);
986 			}
987 		}
988 
989 		(void) snprintf(mname, sizeof (mname), "%s,%d", T4_NEXUS_NAME,
990 		    di_instance(node));
991 
992 		while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
993 			if (strcmp(di_minor_name(minor), mname) == 0) {
994 				break;
995 			}
996 		}
997 
998 		if (minor == DI_MINOR_NIL) {
999 			errx(EXIT_FAILURE, "failed to find minor %s on %s%d",
1000 			    mname, di_driver_name(node), di_instance(node));
1001 		}
1002 
1003 		bpath = di_devfs_minor_path(minor);
1004 		if (bpath == NULL) {
1005 			err(EXIT_FAILURE, "failed to get minor path for "
1006 			    "%s%d:%s", di_driver_name(node), di_instance(node),
1007 			    di_minor_name(minor));
1008 		}
1009 		if (snprintf(cxgbetool_nexus, sizeof (cxgbetool_nexus),
1010 		    "/devices%s", bpath) >= sizeof (cxgbetool_nexus)) {
1011 			errx(EXIT_FAILURE, "failed to construct full /devices "
1012 			    "path for %s: internal path buffer would have "
1013 			    "overflowed", bpath);
1014 		}
1015 		di_devfs_path_free(bpath);
1016 
1017 		di_fini(root);
1018 		return (cxgbetool_nexus);
1019 	}
1020 
1021 	errx(EXIT_FAILURE, "failed to map %s%d to a %s or %s instance",
1022 	    arg, inst, T4_PORT_NAME, T4_NEXUS_NAME);
1023 }
1024 
1025 int
main(int argc,char * argv[])1026 main(int argc, char *argv[])
1027 {
1028 	const char *iff_name;
1029 
1030 	progname = argv[0];
1031 
1032 	if (argc == 2) {
1033 		if (strcmp(argv[1], "-h") == 0 ||
1034 		    strcmp(argv[1], "--help") == 0) {
1035 			usage(stdout);
1036 		}
1037 
1038 		if (strcmp(argv[1], "-v") == 0 ||
1039 		    strcmp(argv[1], "--version") == 0) {
1040 			printf("cxgbetool version %s\n", DRV_VERSION);
1041 			exit(0);
1042 		}
1043 	}
1044 
1045 	if (argc < 3)
1046 		usage(stderr);
1047 
1048 	iff_name = cxgbetool_parse_path(argv[1]);
1049 
1050 	run_cmd(argc, argv, iff_name);
1051 
1052 	return (0);
1053 }
1054