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 
  40 /*
  41  * Firmware Device Log Dumping
  42  */
  43 
  44 static const char * const devlog_level_strings[] = {
  45         [FW_DEVLOG_LEVEL_EMERG]         = "EMERG",
  46         [FW_DEVLOG_LEVEL_CRIT]          = "CRIT",
  47         [FW_DEVLOG_LEVEL_ERR]           = "ERR",
  48         [FW_DEVLOG_LEVEL_NOTICE]        = "NOTICE",
  49         [FW_DEVLOG_LEVEL_INFO]          = "INFO",
  50         [FW_DEVLOG_LEVEL_DEBUG]         = "DEBUG"
  51 };
  52 
  53 static const char * const devlog_facility_strings[] = {
  54         [FW_DEVLOG_FACILITY_CORE]       = "CORE",
  55         [FW_DEVLOG_FACILITY_CF]         = "CF",
  56         [FW_DEVLOG_FACILITY_SCHED]      = "SCHED",
  57         [FW_DEVLOG_FACILITY_TIMER]      = "TIMER",
  58         [FW_DEVLOG_FACILITY_RES]        = "RES",
  59         [FW_DEVLOG_FACILITY_HW]         = "HW",
  60         [FW_DEVLOG_FACILITY_FLR]        = "FLR",
  61         [FW_DEVLOG_FACILITY_DMAQ]       = "DMAQ",
  62         [FW_DEVLOG_FACILITY_PHY]        = "PHY",
  63         [FW_DEVLOG_FACILITY_MAC]        = "MAC",
  64         [FW_DEVLOG_FACILITY_PORT]       = "PORT",
  65         [FW_DEVLOG_FACILITY_VI]         = "VI",
  66         [FW_DEVLOG_FACILITY_FILTER]     = "FILTER",
  67         [FW_DEVLOG_FACILITY_ACL]        = "ACL",
  68         [FW_DEVLOG_FACILITY_TM]         = "TM",
  69         [FW_DEVLOG_FACILITY_QFC]        = "QFC",
  70         [FW_DEVLOG_FACILITY_DCB]        = "DCB",
  71         [FW_DEVLOG_FACILITY_ETH]        = "ETH",
  72         [FW_DEVLOG_FACILITY_OFLD]       = "OFLD",
  73         [FW_DEVLOG_FACILITY_RI]         = "RI",
  74         [FW_DEVLOG_FACILITY_ISCSI]      = "ISCSI",
  75         [FW_DEVLOG_FACILITY_FCOE]       = "FCOE",
  76         [FW_DEVLOG_FACILITY_FOISCSI]    = "FOISCSI",
  77         [FW_DEVLOG_FACILITY_FOFCOE]     = "FOFCOE",
  78         [FW_DEVLOG_FACILITY_CHNET]      = "CHNET",
  79 };
  80 
  81 static const char *progname;
  82 
  83 static void usage(FILE *fp)
  84 {
  85         fprintf(fp, "Usage: %s <path to t4nex#> [operation]\n", progname);
  86         fprintf(fp,
  87             "\tdevlog                              show device log\n"
  88             "\tloadfw <FW image>                   Flash the FW image\n");
  89         exit(fp == stderr ? 1 : 0);
  90 }
  91 
  92 __NORETURN static void
  93 err(int code, const char *fmt, ...)
  94 {
  95         va_list ap;
  96         int e = errno;
  97 
  98         va_start(ap, fmt);
  99         fprintf(stderr, "error: ");
 100         vfprintf(stderr, fmt, ap);
 101         fprintf(stderr, ": %s\n", strerror(e));
 102         va_end(ap);
 103         exit(code);
 104 }
 105 
 106 static int
 107 doit(const char *iff_name, unsigned long cmd, void *data)
 108 {
 109         int fd = 0;
 110         int rc = 0;
 111 
 112         if ((fd = open(iff_name, O_RDWR)) < 0)
 113                 return (-1);
 114 
 115         rc = (ioctl(fd, cmd, data) < 0) ? errno : rc;
 116         close(fd);
 117         return (rc);
 118 }
 119 
 120 static void
 121 get_devlog(int argc, char *argv[], int start_arg, const char *iff_name)
 122 {
 123         struct t4_devlog *devlog;
 124         struct fw_devlog_e *entry, *buf;
 125         int rc = 0, first = 0, nentries, i, j, len;
 126         uint64_t ftstamp = UINT64_MAX;
 127 
 128         devlog = malloc(T4_DEVLOG_SIZE + sizeof (struct t4_devlog));
 129         if (!devlog)
 130                 err(1, "%s: can't allocate devlog buffer", __func__);
 131 
 132         devlog->len = T4_DEVLOG_SIZE;
 133         /* Get device log */
 134         rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog);
 135         if (rc == ENOBUFS) {
 136                 /*
 137                  * Default buffer size is not sufficient to hold device log.
 138                  * Driver has updated the devlog.len to indicate the expected
 139                  * size. Free the currently allocated devlog.data, allocate
 140                  * again with right size and retry.
 141                  */
 142                 len = devlog->len;
 143                 free(devlog);
 144 
 145                 if ((devlog = malloc(len + sizeof (struct t4_devlog))) == NULL)
 146                         err(1, "%s: can't reallocate devlog buffer", __func__);
 147 
 148                 rc = doit(iff_name, T4_IOCTL_DEVLOG, devlog);
 149         }
 150         if (rc) {
 151                 free(devlog);
 152                 err(1, "%s: can't get device log", __func__);
 153         }
 154 
 155         /* There are nentries number of entries in the buffer */
 156         nentries = (devlog->len / sizeof (struct fw_devlog_e));
 157 
 158         buf = (struct fw_devlog_e *)devlog->data;
 159 
 160         /* Find the first entry */
 161         for (i = 0; i < nentries; i++) {
 162                 entry = &buf[i];
 163 
 164                 if (entry->timestamp == 0)
 165                         break;
 166 
 167                 entry->timestamp = BE_64(entry->timestamp);
 168                 entry->seqno = BE_32(entry->seqno);
 169                 for (j = 0; j < 8; j++)
 170                         entry->params[j] = BE_32(entry->params[j]);
 171 
 172                 if (entry->timestamp < ftstamp) {
 173                         ftstamp = entry->timestamp;
 174                         first = i;
 175                 }
 176         }
 177 
 178         printf("%10s  %15s  %8s  %8s  %s\n", "Seq#", "Tstamp", "Level",
 179             "Facility", "Message");
 180 
 181         i = first;
 182 
 183         do {
 184                 entry = &buf[i];
 185 
 186                 if (entry->timestamp == 0)
 187                         break;
 188 
 189                 printf("%10d  %15llu  %8s  %8s  ", entry->seqno,
 190                     entry->timestamp,
 191                     (entry->level < ARRAY_SIZE(devlog_level_strings) ?
 192                     devlog_level_strings[entry->level] : "UNKNOWN"),
 193                     (entry->facility < ARRAY_SIZE(devlog_facility_strings) ?
 194                     devlog_facility_strings[entry->facility] : "UNKNOWN"));
 195 
 196                 printf((const char *)entry->fmt, entry->params[0],
 197                     entry->params[1], entry->params[2], entry->params[3],
 198                     entry->params[4], entry->params[5], entry->params[6],
 199                     entry->params[7]);
 200 
 201                 if (++i == nentries)
 202                         i = 0;
 203 
 204         } while (i != first);
 205 
 206         free(devlog);
 207 }
 208 
 209 static void
 210 load_fw(int argc, char *argv[], int start_arg, const char *iff_name)
 211 {
 212         const char *fname = argv[start_arg];
 213         struct t4_ldfw *fw;
 214         struct stat sb;
 215         size_t len;
 216         int fd;
 217 
 218         if (argc != 4)
 219                 err(1, "incorrect number of arguments.");
 220 
 221         fd = open(fname, O_RDONLY);
 222         if (fd < 0)
 223                 err(1, "%s: opening %s failed", __func__, fname);
 224         if (fstat(fd, &sb) < 0) {
 225                 close(fd);
 226                 err(1, "%s: fstat %s failed", __func__, fname);
 227         }
 228         len = (size_t)sb.st_size;
 229 
 230         fw = malloc(sizeof (struct t4_ldfw) + len);
 231         if (!fw) {
 232                 close(fd);
 233                 err(1, "%s: %s allocate %ld bytes failed",
 234                     __func__, fname, sizeof (struct t4_ldfw) + len);
 235         }
 236 
 237         if (read(fd, fw->data, len) < len) {
 238                 close(fd);
 239                 free(fw);
 240                 err(1, "%s: %s read failed", __func__, fname);
 241         }
 242 
 243         close(fd);
 244 
 245         fw->len = len;
 246 
 247         if (doit(iff_name, T4_IOCTL_LOAD_FW, fw)) {
 248                 free(fw);
 249                 err(1, "%s: IOCTL failed", __func__);
 250         } else {
 251                 printf("FW flash success, reload driver/reboot to take "
 252                     "effect\n");
 253         }
 254 
 255         free(fw);
 256 }
 257 
 258 static void
 259 run_cmd(int argc, char *argv[], const char *iff_name)
 260 {
 261         if (strcmp(argv[2], "devlog") == 0)
 262                 get_devlog(argc, argv, 3, iff_name);
 263         else if (strcmp(argv[2], "loadfw") == 0)
 264                 load_fw(argc, argv, 3, iff_name);
 265         else
 266                 usage(stderr);
 267 }
 268 
 269 int
 270 main(int argc, char *argv[])
 271 {
 272         const char *iff_name;
 273 
 274         progname = argv[0];
 275 
 276         if (argc == 2) {
 277                 if (strcmp(argv[1], "-h") == 0 ||
 278                     strcmp(argv[1], "--help") == 0) {
 279                         usage(stdout);
 280                 }
 281 
 282                 if (strcmp(argv[1], "-v") == 0 ||
 283                     strcmp(argv[1], "--version") == 0) {
 284                         printf("cxgbetool version %s\n", DRV_VERSION);
 285                         exit(0);
 286                 }
 287         }
 288 
 289         if (argc < 3)
 290                 usage(stderr);
 291 
 292         iff_name = argv[1];
 293 
 294         run_cmd(argc, argv, iff_name);
 295 
 296         return (0);
 297 }