xref: /illumos-gate/usr/src/cmd/cxgbetool/cxgbetool.c (revision 2aeafac3612e19716bf8164f89c3c9196342979c)
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  */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <stropts.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <sys/socket.h>
28 #include <strings.h>
29 #include <sys/varargs.h>
30 #include <errno.h>
31 #include <sys/byteorder.h>
32 #include <inttypes.h>
33 #include <sys/sysmacros.h>
34 
35 #include "t4nex.h"
36 #include "version.h"
37 #include "osdep.h"
38 #include "t4fw_interface.h"
39 #include "cudbg.h"
40 #include "cudbg_lib_common.h"
41 
42 #define CUDBG_SIZE (32 * 1024 * 1024)
43 #define CUDBG_MAX_ENTITY_STR_LEN 4096
44 #define MAX_PARAM_LEN 4096
45 
46 char *option_list[] = {
47 	"--collect",
48 	"--view",
49 	"--version",
50 };
51 
52 enum {
53 	CUDBG_OPT_COLLECT,
54 	CUDBG_OPT_VIEW,
55 	CUDBG_OPT_VERSION,
56 };
57 
58 /*
59  * Firmware Device Log Dumping
60  */
61 
62 static const char * const devlog_level_strings[] = {
63 	[FW_DEVLOG_LEVEL_EMERG]		= "EMERG",
64 	[FW_DEVLOG_LEVEL_CRIT]		= "CRIT",
65 	[FW_DEVLOG_LEVEL_ERR]		= "ERR",
66 	[FW_DEVLOG_LEVEL_NOTICE]	= "NOTICE",
67 	[FW_DEVLOG_LEVEL_INFO]		= "INFO",
68 	[FW_DEVLOG_LEVEL_DEBUG]		= "DEBUG"
69 };
70 
71 static const char * const devlog_facility_strings[] = {
72 	[FW_DEVLOG_FACILITY_CORE]	= "CORE",
73 	[FW_DEVLOG_FACILITY_CF]		= "CF",
74 	[FW_DEVLOG_FACILITY_SCHED]	= "SCHED",
75 	[FW_DEVLOG_FACILITY_TIMER]	= "TIMER",
76 	[FW_DEVLOG_FACILITY_RES]	= "RES",
77 	[FW_DEVLOG_FACILITY_HW]		= "HW",
78 	[FW_DEVLOG_FACILITY_FLR]	= "FLR",
79 	[FW_DEVLOG_FACILITY_DMAQ]	= "DMAQ",
80 	[FW_DEVLOG_FACILITY_PHY]	= "PHY",
81 	[FW_DEVLOG_FACILITY_MAC]	= "MAC",
82 	[FW_DEVLOG_FACILITY_PORT]	= "PORT",
83 	[FW_DEVLOG_FACILITY_VI]		= "VI",
84 	[FW_DEVLOG_FACILITY_FILTER]	= "FILTER",
85 	[FW_DEVLOG_FACILITY_ACL]	= "ACL",
86 	[FW_DEVLOG_FACILITY_TM]		= "TM",
87 	[FW_DEVLOG_FACILITY_QFC]	= "QFC",
88 	[FW_DEVLOG_FACILITY_DCB]	= "DCB",
89 	[FW_DEVLOG_FACILITY_ETH]	= "ETH",
90 	[FW_DEVLOG_FACILITY_OFLD]	= "OFLD",
91 	[FW_DEVLOG_FACILITY_RI]		= "RI",
92 	[FW_DEVLOG_FACILITY_ISCSI]	= "ISCSI",
93 	[FW_DEVLOG_FACILITY_FCOE]	= "FCOE",
94 	[FW_DEVLOG_FACILITY_FOISCSI]	= "FOISCSI",
95 	[FW_DEVLOG_FACILITY_FOFCOE]	= "FOFCOE",
96 	[FW_DEVLOG_FACILITY_CHNET]	= "CHNET",
97 };
98 
99 static const char *progname;
100 int set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list);
101 
102 static int check_option(char *opt)
103 {
104 	int i;
105 
106 	for (i = 0; i < ARRAY_SIZE(option_list); i++) {
107 		if (!strcmp(opt, option_list[i]))
108 			return i;
109 	}
110 	return -1;
111 }
112 
113 static void usage(FILE *fp)
114 {
115 	fprintf(fp, "Usage: %s <path to t4nex#> [operation]\n", progname);
116 	fprintf(fp,
117 	    "\tdevlog                              show device log\n"
118 	    "\tloadfw <FW image>                   Flash the FW image\n"
119 	    "\tcudbg                               Chelsio Unified Debugger\n");
120 	exit(fp == stderr ? 1 : 0);
121 }
122 
123 __NORETURN static void
124 err(int code, const char *fmt, ...)
125 {
126 	va_list ap;
127 	int e = errno;
128 
129 	va_start(ap, fmt);
130 	fprintf(stderr, "error: ");
131 	vfprintf(stderr, fmt, ap);
132 	fprintf(stderr, ": %s\n", strerror(e));
133 	va_end(ap);
134 	exit(code);
135 }
136 
137 static int
138 doit(const char *iff_name, unsigned long cmd, void *data)
139 {
140 	int fd = 0;
141 	int rc = 0;
142 
143 	if ((fd = open(iff_name, O_RDWR)) < 0)
144 		return (-1);
145 
146 	rc = (ioctl(fd, cmd, data) < 0) ? errno : rc;
147 	close(fd);
148 	return (rc);
149 }
150 
151 static void
152 get_devlog(int argc, char *argv[], int start_arg, const char *iff_name)
153 {
154 	struct t4_devlog *devlog;
155 	struct fw_devlog_e *entry, *buf;
156 	int rc = 0, first = 0, nentries, i, j, len;
157 	uint64_t ftstamp = UINT64_MAX;
158 
159 	devlog = malloc(T4_DEVLOG_SIZE + sizeof (struct t4_devlog));
160 	if (!devlog)
161 		err(1, "%s: can't allocate devlog buffer", __func__);
162 
163 	devlog->len = T4_DEVLOG_SIZE;
164 	/* Get device log */
165 	rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog);
166 	if (rc == ENOBUFS) {
167 		/*
168 		 * Default buffer size is not sufficient to hold device log.
169 		 * Driver has updated the devlog.len to indicate the expected
170 		 * size. Free the currently allocated devlog.data, allocate
171 		 * again with right size and retry.
172 		 */
173 		len = devlog->len;
174 		free(devlog);
175 
176 		if ((devlog = malloc(len + sizeof (struct t4_devlog))) == NULL)
177 			err(1, "%s: can't reallocate devlog buffer", __func__);
178 
179 		rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog);
180 	}
181 	if (rc) {
182 		free(devlog);
183 		err(1, "%s: can't get device log", __func__);
184 	}
185 
186 	/* There are nentries number of entries in the buffer */
187 	nentries = (devlog->len / sizeof (struct fw_devlog_e));
188 
189 	buf = (struct fw_devlog_e *)devlog->data;
190 
191 	/* Find the first entry */
192 	for (i = 0; i < nentries; i++) {
193 		entry = &buf[i];
194 
195 		if (entry->timestamp == 0)
196 			break;
197 
198 		entry->timestamp = BE_64(entry->timestamp);
199 		entry->seqno = BE_32(entry->seqno);
200 		for (j = 0; j < 8; j++)
201 			entry->params[j] = BE_32(entry->params[j]);
202 
203 		if (entry->timestamp < ftstamp) {
204 			ftstamp = entry->timestamp;
205 			first = i;
206 		}
207 	}
208 
209 	printf("%10s  %15s  %8s  %8s  %s\n", "Seq#", "Tstamp", "Level",
210 	    "Facility", "Message");
211 
212 	i = first;
213 
214 	do {
215 		entry = &buf[i];
216 
217 		if (entry->timestamp == 0)
218 			break;
219 
220 		printf("%10d  %15llu  %8s  %8s  ", entry->seqno,
221 		    entry->timestamp,
222 		    (entry->level < ARRAY_SIZE(devlog_level_strings) ?
223 		    devlog_level_strings[entry->level] : "UNKNOWN"),
224 		    (entry->facility < ARRAY_SIZE(devlog_facility_strings) ?
225 		    devlog_facility_strings[entry->facility] : "UNKNOWN"));
226 
227 		printf((const char *)entry->fmt, entry->params[0],
228 		    entry->params[1], entry->params[2], entry->params[3],
229 		    entry->params[4], entry->params[5], entry->params[6],
230 		    entry->params[7]);
231 
232 		if (++i == nentries)
233 			i = 0;
234 
235 	} while (i != first);
236 
237 	free(devlog);
238 }
239 
240 static void
241 load_fw(int argc, char *argv[], int start_arg, const char *iff_name)
242 {
243 	const char *fname = argv[start_arg];
244 	struct t4_ldfw *fw;
245 	struct stat sb;
246 	size_t len;
247 	int fd;
248 
249 	if (argc != 4)
250 		err(1, "incorrect number of arguments.");
251 
252 	fd = open(fname, O_RDONLY);
253 	if (fd < 0)
254 		err(1, "%s: opening %s failed", __func__, fname);
255 	if (fstat(fd, &sb) < 0) {
256 		close(fd);
257 		err(1, "%s: fstat %s failed", __func__, fname);
258 	}
259 	len = (size_t)sb.st_size;
260 
261 	fw = malloc(sizeof (struct t4_ldfw) + len);
262 	if (!fw) {
263 		close(fd);
264 		err(1, "%s: %s allocate %ld bytes failed",
265 		    __func__, fname, sizeof (struct t4_ldfw) + len);
266 	}
267 
268 	if (read(fd, fw->data, len) < len) {
269 		close(fd);
270 		free(fw);
271 		err(1, "%s: %s read failed", __func__, fname);
272 	}
273 
274 	close(fd);
275 
276 	fw->len = len;
277 
278 	if (doit(iff_name, T4_IOCTL_LOAD_FW, fw)) {
279 		free(fw);
280 		err(1, "%s: IOCTL failed", __func__);
281 	} else {
282 		printf("FW flash success, reload driver/reboot to take "
283 		    "effect\n");
284 	}
285 
286 	free(fw);
287 }
288 
289 int read_input_file(char *in_file, void **buf, int *buf_size)
290 {
291 	FILE *fptr = NULL;
292 	size_t count;
293 	int rc = 0;
294 
295 	fptr = fopen(in_file, "rb");
296 	if (!fptr) {
297 		perror("error in opening file ");
298 		rc = -1;
299 		goto out;
300 	}
301 	rc = fseek(fptr, 0, SEEK_END);
302 	if (rc < 0) {
303 		perror("error in seeking file ");
304 		rc = -1;
305 		goto out;
306 	}
307 	*buf_size = ftell(fptr);
308 	rc = fseek(fptr, 0, SEEK_SET);
309 	if (rc < 0) {
310 		perror("error in seeking file ");
311 		rc = -1;
312 		goto out;
313 	}
314 	*buf = (void *) malloc(*buf_size);
315 	if (*buf == NULL) {
316 		rc = CUDBG_STATUS_NOSPACE;
317 		goto out;
318 	}
319 	memset(*buf, 0, *buf_size);
320 
321 	count = fread(*buf, 1, *buf_size, fptr);
322 	if (count != *buf_size) {
323 		perror("error in reading from file ");
324 		goto out;
325 	}
326 
327 out:
328 	if (fptr)
329 		fclose(fptr);
330 
331 	return rc;
332 }
333 
334 static void
335 do_collect(char *dbg_entity_list, const char *iff_name, const char *fname)
336 {
337 	struct t4_cudbg_dump *cudbg;
338 	int fd;
339 
340 	cudbg = malloc(sizeof(struct t4_cudbg_dump) + CUDBG_SIZE);
341 	if (!cudbg) {
342 		err(1, "%s:allocate %ld bytes failed", __func__, CUDBG_SIZE);
343 	}
344 
345 	memset(cudbg, 0, sizeof(struct t4_cudbg_dump) + CUDBG_SIZE);
346 
347 	cudbg->len = CUDBG_SIZE;
348 
349 	set_dbg_entity(cudbg->bitmap, dbg_entity_list);
350 
351 	if (doit(iff_name, T4_IOCTL_GET_CUDBG, cudbg)) {
352 		free(cudbg);
353 		err(1, "%s: IOCTL failed", __func__);
354 	}
355 
356 	fd = open(fname, O_CREAT | O_TRUNC | O_EXCL | O_WRONLY,
357 		  S_IRUSR | S_IRGRP | S_IROTH);
358 	if (fd < 0) {
359 		err(1, "%s: file open failed", __func__);
360 	}
361 
362 	write(fd, cudbg->data, cudbg->len);
363 	close(fd);
364 	free(cudbg);
365 }
366 
367 static void
368 do_view(char *dbg_entity_list, char *in_file)
369 {
370 	void *handle = NULL;
371 	void *buf = NULL;
372 	int buf_size = 32 * 1024 * 1024;
373 	int  next_offset = 0;
374 	int data_len;
375 	int rc = 0;
376 
377 	handle = cudbg_alloc_handle();
378 	if (!handle)
379 		goto out;
380 	/* rcad from file */
381 	rc = read_input_file(in_file, &buf, &buf_size);
382 	if (rc < 0) {
383 		goto out;
384 	}
385 
386 	set_dbg_entity(((struct cudbg_private *)handle)->dbg_init.dbg_bitmap,
387 			dbg_entity_list);
388 	do {
389 		if (buf_size - next_offset <= 0)
390 			break;
391 
392 		data_len = cudbg_view(handle, buf+next_offset,
393 				buf_size-next_offset, NULL, 0);
394 		next_offset += data_len;
395 		if (data_len > 0)
396 			printf("\n\t\t<========================END============="\
397 					"===========>\t\t\n\n\n");
398 	} while (data_len > 0);
399 
400 out:
401 	if (buf)
402 		free(buf);
403 	if (handle)
404 		cudbg_free_handle(handle);
405 	return;
406 }
407 
408 typedef void (*cudbg_alias_get_entities_cb)(char *dst, u32 dst_size);
409 
410 struct entity_access_list {
411         const char *name;
412         cudbg_alias_get_entities_cb get_entities_cb;
413 };
414 
415 void
416 cudbg_append_string(char *dst, u32 dst_size, char *src)
417 {
418         strlcat(dst, src, dst_size);
419         strlcat(dst, ",", dst_size);
420 }
421 
422 static void
423 cudbg_alias_get_allregs(char *dst, u32 dst_size)
424 {
425         u32 i;
426 
427         for (i = 0; i < ARRAY_SIZE(entity_list); i++)
428                 if (entity_list[i].flag & (1 << ENTITY_FLAG_REGISTER))
429                         cudbg_append_string(dst, dst_size, entity_list[i].name);
430 }
431 
432 static struct entity_access_list ATTRIBUTE_UNUSED entity_alias_list[] = {
433         {"allregs", cudbg_alias_get_allregs},
434 };
435 
436 static int
437 check_dbg_entity(char *entity)
438 {
439 	u32 i;
440 
441 	for (i = 0; i < ARRAY_SIZE(entity_list); i++)
442 		if (!strcmp(entity, entity_list[i].name))
443 			return entity_list[i].bit;
444 	return -1;
445 }
446 
447 /* Get matching alias index from entity_alias_list[] */
448 static
449 int get_alias(const char *entity)
450 {
451 	u32 i;
452 
453 	for (i = 0; i < ARRAY_SIZE(entity_alias_list); i++)
454 		if (!strcmp(entity, entity_alias_list[i].name))
455 			return i;
456 	return -1;
457 }
458 
459 static int
460 parse_entity_list(const char *dbg_entity_list, char *dst,
461 				    u32 dst_size)
462 {
463 	char *tmp_dbg_entity_list;
464 	char *dbg_entity;
465 	int rc, i;
466 
467 	/* Holds single entity name de-limited by comma */
468 	tmp_dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN);
469 	if (!tmp_dbg_entity_list)
470 		return ENOMEM;
471 
472 	strlcpy(tmp_dbg_entity_list, dbg_entity_list, CUDBG_MAX_ENTITY_STR_LEN);
473 	dbg_entity = strtok(tmp_dbg_entity_list, ",");
474 	while (dbg_entity != NULL) {
475 		/* See if specified entity name exists.  If it doesn't
476 		 * exist, see if the entity name is an alias.
477 		 * If it's not a valid entity name, bail with error.
478 		 */
479 		rc = check_dbg_entity(dbg_entity);
480 		if (rc < 0) {
481 			i = get_alias(dbg_entity);
482 			if (i < 0) {
483 				/* Not an alias, and not a valid entity name */
484 				printf("\nUnknown entity: %s\n", dbg_entity);
485 				rc = CUDBG_STATUS_ENTITY_NOT_FOUND;
486 				goto out_err;
487 			} else {
488 				/* If alias is found, get all the corresponding
489 				 * debug entities related to the alias.
490 				 */
491 				entity_alias_list[i].get_entities_cb(dst, dst_size);
492 			}
493 		} else {
494 			/* Not an alias, but is a valid entity name.
495 			 * So, append the corresponding debug entity.
496 			 */
497 			cudbg_append_string(dst, dst_size, entity_list[rc].name);
498 		}
499 		dbg_entity = strtok(NULL, ",");
500 	}
501 
502 	rc = 0;
503 
504 out_err:
505 	free(tmp_dbg_entity_list);
506 	return rc;
507 }
508 
509 static
510 int get_entity_list(const char *in_buff, char **out_buff)
511 {
512 	char *dbg_entity_list;
513 	int rc;
514 
515 	/* Allocate enough to hold converted alias string.
516 	 * Must be freed by caller
517 	 */
518 	dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN);
519 	if (!dbg_entity_list)
520 		return ENOMEM;
521 
522 	memset(dbg_entity_list, 0, CUDBG_MAX_ENTITY_STR_LEN);
523 	rc = parse_entity_list(in_buff, dbg_entity_list,
524 			       CUDBG_MAX_ENTITY_STR_LEN);
525 	if (rc) {
526 		free(dbg_entity_list);
527 		return rc;
528 	}
529 
530 	/* Remove the last comma */
531 	dbg_entity_list[strlen(dbg_entity_list) - 1] = '\0';
532 	*out_buff = dbg_entity_list;
533 	return 0;
534 }
535 
536 static void
537 put_entity_list(char *buf)
538 {
539 	if (buf)
540 		free(buf);
541 }
542 
543 int
544 set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list)
545 {
546 	int i, dbg_entity_bit, rc = 0;
547 	char *dbg_entity;
548 	char *dbg_entity_list_tmp;
549 
550 	dbg_entity_list_tmp = malloc(MAX_PARAM_LEN);
551 	if (!dbg_entity_list_tmp) {
552 		rc = CUDBG_STATUS_NOSPACE;
553 		return rc;
554 	}
555 
556 	if (dbg_entity_list != NULL) {
557 		strlcpy(dbg_entity_list_tmp, dbg_entity_list, MAX_PARAM_LEN);
558 		dbg_entity = strtok(dbg_entity_list_tmp, ",");
559 	}
560 	else
561 		dbg_entity = NULL;
562 
563 	while (dbg_entity != NULL) {
564 		rc = check_dbg_entity(dbg_entity);
565 		if (rc < 0) {
566 			printf("\n\tInvalid debug entity: %s\n", dbg_entity);
567 			//Vishal cudbg_usage();
568 			goto out_free;
569 		}
570 
571 		dbg_entity_bit = rc;
572 
573 		if (dbg_entity_bit == CUDBG_ALL) {
574 			for (i = 1; i < CUDBG_MAX_ENTITY; i++)
575 				set_dbg_bitmap(dbg_bitmap, i);
576 			set_dbg_bitmap(dbg_bitmap, CUDBG_ALL);
577 			break;
578 		} else {
579 			set_dbg_bitmap(dbg_bitmap, dbg_entity_bit);
580 		}
581 
582 		dbg_entity = strtok(NULL, ",");
583 	}
584 
585 	rc = 0;
586 
587 out_free:
588 	free(dbg_entity_list_tmp);
589 	return rc;
590 }
591 
592 
593 static void
594 get_cudbg(int argc, char *argv[], int start_arg, const char *iff_name)
595 {
596 	char *dbg_entity_list = NULL;
597 	int rc = 0, option;
598 	rc = check_option(argv[start_arg++]);
599 	if (rc < 0) {
600 		err(1, "%s:Invalid option provided", __func__);
601 	}
602 	option = rc;
603 
604 	if (option == CUDBG_OPT_VERSION) {
605 		printf("Library Version %d.%d.%d\n", CUDBG_MAJOR_VERSION,
606 			CUDBG_MINOR_VERSION, CUDBG_BUILD_VERSION);
607 		return;
608 	}
609 
610 	if (argc < 5) {
611 		err(1, "Invalid number of arguments\n");
612 	}
613 	rc = get_entity_list(argv[start_arg++],
614 			     &dbg_entity_list);
615 	if (rc) {
616 		err(1, "Error in parsing entity\n");
617 	}
618 
619 	if (argc < 6) {
620 		err(1, "File name is missing\n");
621 	}
622 
623 	switch (option) {
624 		case CUDBG_OPT_COLLECT:
625 			do_collect(dbg_entity_list, iff_name, argv[start_arg]);
626 			break;
627 		case CUDBG_OPT_VIEW:
628 			do_view(dbg_entity_list, argv[start_arg]);
629 			break;
630 		default:
631 			err(1, "Wrong option provided\n");
632 	}
633 
634 	put_entity_list(dbg_entity_list);
635 }
636 
637 static void
638 run_cmd(int argc, char *argv[], const char *iff_name)
639 {
640 	if (strcmp(argv[2], "devlog") == 0)
641 		get_devlog(argc, argv, 3, iff_name);
642 	else if (strcmp(argv[2], "loadfw") == 0)
643 		load_fw(argc, argv, 3, iff_name);
644 	else if (strcmp(argv[2], "cudbg") == 0)
645 		get_cudbg(argc, argv, 3, iff_name);
646 	else
647 		usage(stderr);
648 }
649 
650 int
651 main(int argc, char *argv[])
652 {
653 	const char *iff_name;
654 
655 	progname = argv[0];
656 
657 	if (argc == 2) {
658 		if (strcmp(argv[1], "-h") == 0 ||
659 		    strcmp(argv[1], "--help") == 0) {
660 			usage(stdout);
661 		}
662 
663 		if (strcmp(argv[1], "-v") == 0 ||
664 		    strcmp(argv[1], "--version") == 0) {
665 			printf("cxgbetool version %s\n", DRV_VERSION);
666 			exit(0);
667 		}
668 	}
669 
670 	if (argc < 3)
671 		usage(stderr);
672 
673 	iff_name = argv[1];
674 
675 	run_cmd(argc, argv, iff_name);
676 
677 	return (0);
678 }
679