/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright (c) 2018 by Chelsio Communications, Inc. */ /* * Copyright 2019 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "t4nex.h" #include "version.h" #include "osdep.h" #include "t4fw_interface.h" #include "cudbg.h" #include "cudbg_lib_common.h" #define CUDBG_SIZE (32 * 1024 * 1024) #define CUDBG_MAX_ENTITY_STR_LEN 4096 #define MAX_PARAM_LEN 4096 char *option_list[] = { "--collect", "--view", "--version", }; enum { CUDBG_OPT_COLLECT, CUDBG_OPT_VIEW, CUDBG_OPT_VERSION, }; /* * Firmware Device Log Dumping */ static const char * const devlog_level_strings[] = { [FW_DEVLOG_LEVEL_EMERG] = "EMERG", [FW_DEVLOG_LEVEL_CRIT] = "CRIT", [FW_DEVLOG_LEVEL_ERR] = "ERR", [FW_DEVLOG_LEVEL_NOTICE] = "NOTICE", [FW_DEVLOG_LEVEL_INFO] = "INFO", [FW_DEVLOG_LEVEL_DEBUG] = "DEBUG" }; static const char * const devlog_facility_strings[] = { [FW_DEVLOG_FACILITY_CORE] = "CORE", [FW_DEVLOG_FACILITY_CF] = "CF", [FW_DEVLOG_FACILITY_SCHED] = "SCHED", [FW_DEVLOG_FACILITY_TIMER] = "TIMER", [FW_DEVLOG_FACILITY_RES] = "RES", [FW_DEVLOG_FACILITY_HW] = "HW", [FW_DEVLOG_FACILITY_FLR] = "FLR", [FW_DEVLOG_FACILITY_DMAQ] = "DMAQ", [FW_DEVLOG_FACILITY_PHY] = "PHY", [FW_DEVLOG_FACILITY_MAC] = "MAC", [FW_DEVLOG_FACILITY_PORT] = "PORT", [FW_DEVLOG_FACILITY_VI] = "VI", [FW_DEVLOG_FACILITY_FILTER] = "FILTER", [FW_DEVLOG_FACILITY_ACL] = "ACL", [FW_DEVLOG_FACILITY_TM] = "TM", [FW_DEVLOG_FACILITY_QFC] = "QFC", [FW_DEVLOG_FACILITY_DCB] = "DCB", [FW_DEVLOG_FACILITY_ETH] = "ETH", [FW_DEVLOG_FACILITY_OFLD] = "OFLD", [FW_DEVLOG_FACILITY_RI] = "RI", [FW_DEVLOG_FACILITY_ISCSI] = "ISCSI", [FW_DEVLOG_FACILITY_FCOE] = "FCOE", [FW_DEVLOG_FACILITY_FOISCSI] = "FOISCSI", [FW_DEVLOG_FACILITY_FOFCOE] = "FOFCOE", [FW_DEVLOG_FACILITY_CHNET] = "CHNET", }; static const char *progname; int set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list); static int check_option(char *opt) { int i; for (i = 0; i < ARRAY_SIZE(option_list); i++) { if (!strcmp(opt, option_list[i])) return i; } return -1; } static void usage(FILE *fp) { fprintf(fp, "Usage: %s [operation]\n", progname); fprintf(fp, "\tdevlog show device log\n" "\tloadfw Flash the FW image\n" "\tcudbg Chelsio Unified Debugger\n"); exit(fp == stderr ? 1 : 0); } __NORETURN static void err(int code, const char *fmt, ...) { va_list ap; int e = errno; va_start(ap, fmt); fprintf(stderr, "error: "); vfprintf(stderr, fmt, ap); fprintf(stderr, ": %s\n", strerror(e)); va_end(ap); exit(code); } static int doit(const char *iff_name, unsigned long cmd, void *data) { int fd = 0; int rc = 0; if ((fd = open(iff_name, O_RDWR)) < 0) return (-1); rc = (ioctl(fd, cmd, data) < 0) ? errno : rc; close(fd); return (rc); } static void get_devlog(int argc, char *argv[], int start_arg, const char *iff_name) { struct t4_devlog *devlog; struct fw_devlog_e *entry, *buf; int rc = 0, first = 0, nentries, i, j, len; uint64_t ftstamp = UINT64_MAX; devlog = malloc(T4_DEVLOG_SIZE + sizeof (struct t4_devlog)); if (!devlog) err(1, "%s: can't allocate devlog buffer", __func__); devlog->len = T4_DEVLOG_SIZE; /* Get device log */ rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog); if (rc == ENOBUFS) { /* * Default buffer size is not sufficient to hold device log. * Driver has updated the devlog.len to indicate the expected * size. Free the currently allocated devlog.data, allocate * again with right size and retry. */ len = devlog->len; free(devlog); if ((devlog = malloc(len + sizeof (struct t4_devlog))) == NULL) err(1, "%s: can't reallocate devlog buffer", __func__); rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog); } if (rc) { free(devlog); err(1, "%s: can't get device log", __func__); } /* There are nentries number of entries in the buffer */ nentries = (devlog->len / sizeof (struct fw_devlog_e)); buf = (struct fw_devlog_e *)devlog->data; /* Find the first entry */ for (i = 0; i < nentries; i++) { entry = &buf[i]; if (entry->timestamp == 0) break; entry->timestamp = BE_64(entry->timestamp); entry->seqno = BE_32(entry->seqno); for (j = 0; j < 8; j++) entry->params[j] = BE_32(entry->params[j]); if (entry->timestamp < ftstamp) { ftstamp = entry->timestamp; first = i; } } printf("%10s %15s %8s %8s %s\n", "Seq#", "Tstamp", "Level", "Facility", "Message"); i = first; do { entry = &buf[i]; if (entry->timestamp == 0) break; printf("%10d %15llu %8s %8s ", entry->seqno, entry->timestamp, (entry->level < ARRAY_SIZE(devlog_level_strings) ? devlog_level_strings[entry->level] : "UNKNOWN"), (entry->facility < ARRAY_SIZE(devlog_facility_strings) ? devlog_facility_strings[entry->facility] : "UNKNOWN")); printf((const char *)entry->fmt, entry->params[0], entry->params[1], entry->params[2], entry->params[3], entry->params[4], entry->params[5], entry->params[6], entry->params[7]); if (++i == nentries) i = 0; } while (i != first); free(devlog); } static void load_fw(int argc, char *argv[], int start_arg, const char *iff_name) { const char *fname = argv[start_arg]; struct t4_ldfw *fw; struct stat sb; size_t len; int fd; if (argc != 4) err(1, "incorrect number of arguments."); fd = open(fname, O_RDONLY); if (fd < 0) err(1, "%s: opening %s failed", __func__, fname); if (fstat(fd, &sb) < 0) { close(fd); err(1, "%s: fstat %s failed", __func__, fname); } len = (size_t)sb.st_size; fw = malloc(sizeof (struct t4_ldfw) + len); if (!fw) { close(fd); err(1, "%s: %s allocate %ld bytes failed", __func__, fname, sizeof (struct t4_ldfw) + len); } if (read(fd, fw->data, len) < len) { close(fd); free(fw); err(1, "%s: %s read failed", __func__, fname); } close(fd); fw->len = len; if (doit(iff_name, T4_IOCTL_LOAD_FW, fw)) { free(fw); err(1, "%s: IOCTL failed", __func__); } else { printf("FW flash success, reload driver/reboot to take " "effect\n"); } free(fw); } int read_input_file(char *in_file, void **buf, int *buf_size) { FILE *fptr = NULL; size_t count; int rc = 0; fptr = fopen(in_file, "rb"); if (!fptr) { perror("error in opening file "); rc = -1; goto out; } rc = fseek(fptr, 0, SEEK_END); if (rc < 0) { perror("error in seeking file "); rc = -1; goto out; } *buf_size = ftell(fptr); rc = fseek(fptr, 0, SEEK_SET); if (rc < 0) { perror("error in seeking file "); rc = -1; goto out; } *buf = (void *) malloc(*buf_size); if (*buf == NULL) { rc = CUDBG_STATUS_NOSPACE; goto out; } memset(*buf, 0, *buf_size); count = fread(*buf, 1, *buf_size, fptr); if (count != *buf_size) { perror("error in reading from file "); goto out; } out: if (fptr) fclose(fptr); return rc; } static void do_collect(char *dbg_entity_list, const char *iff_name, const char *fname) { struct t4_cudbg_dump *cudbg; int fd; cudbg = malloc(sizeof(struct t4_cudbg_dump) + CUDBG_SIZE); if (!cudbg) { err(1, "%s:allocate %ld bytes failed", __func__, CUDBG_SIZE); } memset(cudbg, 0, sizeof(struct t4_cudbg_dump) + CUDBG_SIZE); cudbg->len = CUDBG_SIZE; set_dbg_entity(cudbg->bitmap, dbg_entity_list); if (doit(iff_name, T4_IOCTL_GET_CUDBG, cudbg)) { free(cudbg); err(1, "%s: IOCTL failed", __func__); } fd = open(fname, O_CREAT | O_TRUNC | O_EXCL | O_WRONLY, S_IRUSR | S_IRGRP | S_IROTH); if (fd < 0) { err(1, "%s: file open failed", __func__); } write(fd, cudbg->data, cudbg->len); close(fd); free(cudbg); } static void do_view(char *dbg_entity_list, char *in_file) { void *handle = NULL; void *buf = NULL; int buf_size = 32 * 1024 * 1024; int next_offset = 0; int data_len; int rc = 0; handle = cudbg_alloc_handle(); if (!handle) goto out; /* rcad from file */ rc = read_input_file(in_file, &buf, &buf_size); if (rc < 0) { goto out; } set_dbg_entity(((struct cudbg_private *)handle)->dbg_init.dbg_bitmap, dbg_entity_list); do { if (buf_size - next_offset <= 0) break; data_len = cudbg_view(handle, buf+next_offset, buf_size-next_offset, NULL, 0); next_offset += data_len; if (data_len > 0) printf("\n\t\t<========================END============="\ "===========>\t\t\n\n\n"); } while (data_len > 0); out: if (buf) free(buf); if (handle) cudbg_free_handle(handle); return; } typedef void (*cudbg_alias_get_entities_cb)(char *dst, u32 dst_size); struct entity_access_list { const char *name; cudbg_alias_get_entities_cb get_entities_cb; }; void cudbg_append_string(char *dst, u32 dst_size, char *src) { strlcat(dst, src, dst_size); strlcat(dst, ",", dst_size); } static void cudbg_alias_get_allregs(char *dst, u32 dst_size) { u32 i; for (i = 0; i < ARRAY_SIZE(entity_list); i++) if (entity_list[i].flag & (1 << ENTITY_FLAG_REGISTER)) cudbg_append_string(dst, dst_size, entity_list[i].name); } static struct entity_access_list ATTRIBUTE_UNUSED entity_alias_list[] = { {"allregs", cudbg_alias_get_allregs}, }; static int check_dbg_entity(char *entity) { u32 i; for (i = 0; i < ARRAY_SIZE(entity_list); i++) if (!strcmp(entity, entity_list[i].name)) return entity_list[i].bit; return -1; } /* Get matching alias index from entity_alias_list[] */ static int get_alias(const char *entity) { u32 i; for (i = 0; i < ARRAY_SIZE(entity_alias_list); i++) if (!strcmp(entity, entity_alias_list[i].name)) return i; return -1; } static int parse_entity_list(const char *dbg_entity_list, char *dst, u32 dst_size) { char *tmp_dbg_entity_list; char *dbg_entity; int rc, i; /* Holds single entity name de-limited by comma */ tmp_dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN); if (!tmp_dbg_entity_list) return ENOMEM; strlcpy(tmp_dbg_entity_list, dbg_entity_list, CUDBG_MAX_ENTITY_STR_LEN); dbg_entity = strtok(tmp_dbg_entity_list, ","); while (dbg_entity != NULL) { /* See if specified entity name exists. If it doesn't * exist, see if the entity name is an alias. * If it's not a valid entity name, bail with error. */ rc = check_dbg_entity(dbg_entity); if (rc < 0) { i = get_alias(dbg_entity); if (i < 0) { /* Not an alias, and not a valid entity name */ printf("\nUnknown entity: %s\n", dbg_entity); rc = CUDBG_STATUS_ENTITY_NOT_FOUND; goto out_err; } else { /* If alias is found, get all the corresponding * debug entities related to the alias. */ entity_alias_list[i].get_entities_cb(dst, dst_size); } } else { /* Not an alias, but is a valid entity name. * So, append the corresponding debug entity. */ cudbg_append_string(dst, dst_size, entity_list[rc].name); } dbg_entity = strtok(NULL, ","); } rc = 0; out_err: free(tmp_dbg_entity_list); return rc; } static int get_entity_list(const char *in_buff, char **out_buff) { char *dbg_entity_list; int rc; /* Allocate enough to hold converted alias string. * Must be freed by caller */ dbg_entity_list = malloc(CUDBG_MAX_ENTITY_STR_LEN); if (!dbg_entity_list) return ENOMEM; memset(dbg_entity_list, 0, CUDBG_MAX_ENTITY_STR_LEN); rc = parse_entity_list(in_buff, dbg_entity_list, CUDBG_MAX_ENTITY_STR_LEN); if (rc) { free(dbg_entity_list); return rc; } /* Remove the last comma */ dbg_entity_list[strlen(dbg_entity_list) - 1] = '\0'; *out_buff = dbg_entity_list; return 0; } static void put_entity_list(char *buf) { if (buf) free(buf); } int set_dbg_entity(u8 *dbg_bitmap, char *dbg_entity_list) { int i, dbg_entity_bit, rc = 0; char *dbg_entity; char *dbg_entity_list_tmp; dbg_entity_list_tmp = malloc(MAX_PARAM_LEN); if (!dbg_entity_list_tmp) { rc = CUDBG_STATUS_NOSPACE; return rc; } if (dbg_entity_list != NULL) { strlcpy(dbg_entity_list_tmp, dbg_entity_list, MAX_PARAM_LEN); dbg_entity = strtok(dbg_entity_list_tmp, ","); } else dbg_entity = NULL; while (dbg_entity != NULL) { rc = check_dbg_entity(dbg_entity); if (rc < 0) { printf("\n\tInvalid debug entity: %s\n", dbg_entity); //Vishal cudbg_usage(); goto out_free; } dbg_entity_bit = rc; if (dbg_entity_bit == CUDBG_ALL) { for (i = 1; i < CUDBG_MAX_ENTITY; i++) set_dbg_bitmap(dbg_bitmap, i); set_dbg_bitmap(dbg_bitmap, CUDBG_ALL); break; } else { set_dbg_bitmap(dbg_bitmap, dbg_entity_bit); } dbg_entity = strtok(NULL, ","); } rc = 0; out_free: free(dbg_entity_list_tmp); return rc; } static void get_cudbg(int argc, char *argv[], int start_arg, const char *iff_name) { char *dbg_entity_list = NULL; int rc = 0, option; rc = check_option(argv[start_arg++]); if (rc < 0) { err(1, "%s:Invalid option provided", __func__); } option = rc; if (option == CUDBG_OPT_VERSION) { printf("Library Version %d.%d.%d\n", CUDBG_MAJOR_VERSION, CUDBG_MINOR_VERSION, CUDBG_BUILD_VERSION); return; } if (argc < 5) { err(1, "Invalid number of arguments\n"); } rc = get_entity_list(argv[start_arg++], &dbg_entity_list); if (rc) { err(1, "Error in parsing entity\n"); } if (argc < 6) { err(1, "File name is missing\n"); } switch (option) { case CUDBG_OPT_COLLECT: do_collect(dbg_entity_list, iff_name, argv[start_arg]); break; case CUDBG_OPT_VIEW: do_view(dbg_entity_list, argv[start_arg]); break; default: err(1, "Wrong option provided\n"); } put_entity_list(dbg_entity_list); } static void run_cmd(int argc, char *argv[], const char *iff_name) { if (strcmp(argv[2], "devlog") == 0) get_devlog(argc, argv, 3, iff_name); else if (strcmp(argv[2], "loadfw") == 0) load_fw(argc, argv, 3, iff_name); else if (strcmp(argv[2], "cudbg") == 0) get_cudbg(argc, argv, 3, iff_name); else usage(stderr); } int main(int argc, char *argv[]) { const char *iff_name; progname = argv[0]; if (argc == 2) { if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { usage(stdout); } if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) { printf("cxgbetool version %s\n", DRV_VERSION); exit(0); } } if (argc < 3) usage(stderr); iff_name = argv[1]; run_cmd(argc, argv, iff_name); return (0); }