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
46char *option_list[] = {
47	"--collect",
48	"--view",
49	"--version",
50};
51
52enum {
53	CUDBG_OPT_COLLECT,
54	CUDBG_OPT_VIEW,
55	CUDBG_OPT_VERSION,
56};
57
58/*
59 * Firmware Device Log Dumping
60 */
61
62static 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
71static 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
99static const char *progname;
100int set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list);
101
102static 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
113static 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
124err(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
137static int
138doit(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
151static void
152get_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
240static void
241load_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
289int 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
327out:
328	if (fptr)
329		fclose(fptr);
330
331	return rc;
332}
333
334static void
335do_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
367static void
368do_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
400out:
401	if (buf)
402		free(buf);
403	if (handle)
404		cudbg_free_handle(handle);
405	return;
406}
407
408typedef void (*cudbg_alias_get_entities_cb)(char *dst, u32 dst_size);
409
410struct entity_access_list {
411        const char *name;
412        cudbg_alias_get_entities_cb get_entities_cb;
413};
414
415void
416cudbg_append_string(char *dst, u32 dst_size, char *src)
417{
418        strlcat(dst, src, dst_size);
419        strlcat(dst, ",", dst_size);
420}
421
422static void
423cudbg_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
432static struct entity_access_list ATTRIBUTE_UNUSED entity_alias_list[] = {
433        {"allregs", cudbg_alias_get_allregs},
434};
435
436static int
437check_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[] */
448static
449int 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
459static int
460parse_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
504out_err:
505	free(tmp_dbg_entity_list);
506	return rc;
507}
508
509static
510int 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
536static void
537put_entity_list(char *buf)
538{
539	if (buf)
540		free(buf);
541}
542
543int
544set_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
587out_free:
588	free(dbg_entity_list_tmp);
589	return rc;
590}
591
592
593static void
594get_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
637static void
638run_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
650int
651main(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