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