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