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