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, ®) < 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, ®) < 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