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