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