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) 2017, Joyent, Inc. 14 */ 15 16 /* 17 * Private utility to dump transceiver information for each physical datalink. 18 * Something like this should eventually be a part of dladm or similar. 19 */ 20 21 #include <stdio.h> 22 #include <sys/types.h> 23 #include <sys/stat.h> 24 #include <fcntl.h> 25 #include <strings.h> 26 #include <errno.h> 27 #include <ctype.h> 28 #include <unistd.h> 29 #include <limits.h> 30 #include <stdarg.h> 31 #include <libgen.h> 32 33 #include <libdladm.h> 34 #include <libdllink.h> 35 #include <sys/dld.h> 36 #include <sys/dld_ioc.h> 37 #include <sys/dls_mgmt.h> 38 #include <libsff.h> 39 40 #define DLTRAN_KIND_LEN 64 41 42 static dladm_handle_t dltran_hdl; 43 static char dltran_dlerrmsg[DLADM_STRSIZE]; 44 static const char *dltran_progname; 45 static boolean_t dltran_verbose; 46 static boolean_t dltran_hex; 47 static boolean_t dltran_write; 48 static int dltran_outfd; 49 static int dltran_errors; 50 51 /* 52 * This routine basically assumes that we'll have 16 byte aligned output to 53 * print out the human readable output. 54 */ 55 static void 56 dltran_dump_page(uint8_t *buf, size_t nbytes, uint_t page) 57 { 58 size_t i; 59 static boolean_t first = B_TRUE; 60 61 if (first) { 62 (void) printf("page %*s 0", 4, ""); 63 for (i = 1; i < 16; i++) { 64 if (i % 4 == 0 && i % 16 != 0) { 65 (void) printf(" "); 66 } 67 68 (void) printf("%2x", i); 69 } 70 (void) printf(" v123456789abcdef\n"); 71 first = B_FALSE; 72 } 73 for (i = 0; i < nbytes; i++) { 74 75 if (i % 16 == 0) { 76 (void) printf("0x%02x %04x: ", page, i); 77 } 78 79 if (i % 4 == 0 && i % 16 != 0) { 80 (void) printf(" "); 81 } 82 83 84 (void) printf("%02x", buf[i]); 85 86 if (i % 16 == 15) { 87 int j; 88 (void) printf(" "); 89 for (j = i - (i % 16); j <= i; j++) { 90 if (!isprint(buf[j])) { 91 (void) printf("."); 92 } else { 93 (void) printf("%c", buf[j]); 94 } 95 } 96 (void) printf("\n"); 97 } 98 } 99 } 100 101 static int 102 dltran_read_page(datalink_id_t link, uint_t tranid, uint_t page, uint8_t *bufp, 103 size_t *buflen) 104 { 105 dld_ioc_tranio_t dti; 106 107 bzero(bufp, *buflen); 108 bzero(&dti, sizeof (dti)); 109 110 dti.dti_linkid = link; 111 dti.dti_tran_id = tranid; 112 dti.dti_page = page; 113 dti.dti_nbytes = *buflen; 114 dti.dti_off = 0; 115 dti.dti_buf = (uintptr_t)(void *)bufp; 116 117 if (ioctl(dladm_dld_fd(dltran_hdl), DLDIOC_READTRAN, &dti) != 0) { 118 (void) fprintf(stderr, "failed to read transceiver page " 119 "0x%2x: %s\n", page, strerror(errno)); 120 return (1); 121 } 122 123 *buflen = dti.dti_nbytes; 124 return (0); 125 } 126 127 static boolean_t 128 dltran_is_8472(uint8_t *buf) 129 { 130 switch (buf[0]) { 131 case 0xc: 132 case 0xd: 133 case 0x11: 134 /* 135 * Catch cases that refer explicitly to QSFP and newer. 136 */ 137 return (B_FALSE); 138 default: 139 break; 140 } 141 142 /* 143 * Check the byte that indicates compliance with SFF 8472. Use this to 144 * know if we can read page 0xa2 or not. 145 */ 146 if (buf[94] == 0) 147 return (B_FALSE); 148 149 return (B_TRUE); 150 } 151 152 static void 153 dltran_hex_dump(datalink_id_t linkid, uint_t tranid) 154 { 155 uint8_t buf[256]; 156 size_t buflen = sizeof (buf); 157 158 if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) { 159 dltran_errors++; 160 return; 161 } 162 163 dltran_dump_page(buf, buflen, 0xa0); 164 165 if (!dltran_is_8472(buf)) { 166 return; 167 } 168 169 buflen = sizeof (buf); 170 if (dltran_read_page(linkid, tranid, 0xa2, buf, &buflen) != 0) { 171 dltran_errors++; 172 return; 173 } 174 175 dltran_dump_page(buf, buflen, 0xa2); 176 } 177 178 static void 179 dltran_write_page(datalink_id_t linkid, uint_t tranid) 180 { 181 uint8_t buf[256]; 182 size_t buflen = sizeof (buf); 183 off_t off; 184 185 if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) { 186 dltran_errors++; 187 return; 188 } 189 190 off = 0; 191 while (buflen > 0) { 192 ssize_t ret; 193 194 ret = write(dltran_outfd, buf + off, buflen); 195 if (ret == -1) { 196 (void) fprintf(stderr, "failed to write data " 197 "to output file: %s\n", strerror(errno)); 198 dltran_errors++; 199 return; 200 } 201 202 off += ret; 203 buflen -= ret; 204 } 205 } 206 207 static void 208 dltran_verbose_dump(datalink_id_t linkid, uint_t tranid) 209 { 210 uint8_t buf[256]; 211 size_t buflen = sizeof (buf); 212 int ret; 213 nvlist_t *nvl; 214 215 if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) { 216 dltran_errors++; 217 return; 218 } 219 220 ret = libsff_parse(buf, buflen, 0xa0, &nvl); 221 if (ret == 0) { 222 dump_nvlist(nvl, 8); 223 nvlist_free(nvl); 224 } else { 225 fprintf(stderr, "failed to parse sfp data: %s\n", 226 strerror(ret)); 227 dltran_errors++; 228 } 229 } 230 231 static int 232 dltran_dump_transceivers(dladm_handle_t hdl, datalink_id_t linkid, void *arg) 233 { 234 dladm_status_t status; 235 char name[MAXLINKNAMELEN]; 236 dld_ioc_gettran_t gt; 237 uint_t count, i, tranid = UINT_MAX; 238 boolean_t tran_found = B_FALSE; 239 uint_t *tranidp = arg; 240 241 if (tranidp != NULL) 242 tranid = *tranidp; 243 244 if ((status = dladm_datalink_id2info(hdl, linkid, NULL, NULL, NULL, 245 name, sizeof (name))) != DLADM_STATUS_OK) { 246 (void) fprintf(stderr, "failed to get datalink name for link " 247 "%d: %s", linkid, dladm_status2str(status, 248 dltran_dlerrmsg)); 249 dltran_errors++; 250 return (DLADM_WALK_CONTINUE); 251 } 252 253 bzero(>, sizeof (gt)); 254 gt.dgt_linkid = linkid; 255 gt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN; 256 257 if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, >) != 0) { 258 if (errno != ENOTSUP) { 259 (void) fprintf(stderr, "failed to get transceiver " 260 "count for device %s: %s\n", 261 name, strerror(errno)); 262 dltran_errors++; 263 } 264 return (DLADM_WALK_CONTINUE); 265 } 266 267 count = gt.dgt_tran_id; 268 (void) printf("%s: discovered %d transceiver%s\n", name, count, 269 count > 1 ? "s" : ""); 270 for (i = 0; i < count; i++) { 271 if (tranid != UINT_MAX && i != tranid) 272 continue; 273 if (tranid != UINT_MAX) 274 tran_found = B_TRUE; 275 bzero(>, sizeof (gt)); 276 gt.dgt_linkid = linkid; 277 gt.dgt_tran_id = i; 278 279 if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, >) != 0) { 280 (void) fprintf(stderr, "failed to get tran info for " 281 "%s: %s\n", name, strerror(errno)); 282 dltran_errors++; 283 return (DLADM_WALK_CONTINUE); 284 } 285 286 if (dltran_hex && !gt.dgt_present) 287 continue; 288 if (!dltran_hex && !dltran_write) { 289 (void) printf("\ttransceiver %d present: %s\n", i, 290 gt.dgt_present ? "yes" : "no"); 291 if (!gt.dgt_present) 292 continue; 293 (void) printf("\ttransceiver %d usable: %s\n", i, 294 gt.dgt_usable ? "yes" : "no"); 295 } 296 297 if (dltran_verbose) { 298 dltran_verbose_dump(linkid, i); 299 } 300 301 if (dltran_write) { 302 if (!gt.dgt_present) { 303 (void) fprintf(stderr, "warning: no " 304 "transceiver present in port %d, not " 305 "writing\n", i); 306 dltran_errors++; 307 continue; 308 } 309 dltran_write_page(linkid, i); 310 } 311 312 if (dltran_hex) { 313 printf("transceiver %d data:\n", i); 314 dltran_hex_dump(linkid, i); 315 } 316 } 317 318 if (tranid != UINT_MAX && !tran_found) { 319 dltran_errors++; 320 (void) fprintf(stderr, "failed to find transceiver %d on " 321 "link %s\n", tranid, name); 322 } 323 324 return (DLADM_WALK_CONTINUE); 325 } 326 327 328 static void 329 dltran_usage(const char *fmt, ...) 330 { 331 if (fmt != NULL) { 332 va_list ap; 333 334 (void) fprintf(stderr, "%s: ", dltran_progname); 335 va_start(ap, fmt); 336 (void) vfprintf(stderr, fmt, ap); 337 va_end(ap); 338 } 339 340 (void) fprintf(stderr, "Usage: %s [-x | -v | -w file] [tran]...\n" 341 "\n" 342 "\t-v display all transceiver information\n" 343 "\t-w write transceiver data page 0xa0 to file\n" 344 "\t-x dump raw hexadecimal for transceiver\n", 345 dltran_progname); 346 } 347 348 int 349 main(int argc, char *argv[]) 350 { 351 int c; 352 dladm_status_t status; 353 const char *outfile = NULL; 354 uint_t count = 0; 355 356 dltran_progname = basename(argv[0]); 357 358 while ((c = getopt(argc, argv, ":xvw:")) != -1) { 359 switch (c) { 360 case 'v': 361 dltran_verbose = B_TRUE; 362 break; 363 case 'x': 364 dltran_hex = B_TRUE; 365 break; 366 case 'w': 367 dltran_write = B_TRUE; 368 outfile = optarg; 369 break; 370 case ':': 371 dltran_usage("option -%c requires an " 372 "operand\n", optopt); 373 return (2); 374 case '?': 375 default: 376 dltran_usage("unknown option: -%c\n", optopt); 377 return (2); 378 } 379 } 380 381 argc -= optind; 382 argv += optind; 383 384 if (dltran_verbose) 385 count++; 386 if (dltran_hex) 387 count++; 388 if (dltran_write) 389 count++; 390 if (count > 1) { 391 (void) fprintf(stderr, "only one of -v, -w, and -x may be " 392 "specified\n"); 393 return (2); 394 } 395 396 if (dltran_write) { 397 if ((dltran_outfd = open(outfile, O_RDWR | O_TRUNC | O_CREAT, 398 0644)) < 0) { 399 (void) fprintf(stderr, "failed to open output file " 400 "%s: %s\n", outfile, strerror(errno)); 401 return (1); 402 } 403 } 404 405 if ((status = dladm_open(&dltran_hdl)) != DLADM_STATUS_OK) { 406 (void) fprintf(stderr, "failed to open /dev/dld: %s\n", 407 dladm_status2str(status, dltran_dlerrmsg)); 408 return (1); 409 } 410 411 if (argc == 0) { 412 (void) dladm_walk_datalink_id(dltran_dump_transceivers, 413 dltran_hdl, NULL, DATALINK_CLASS_PHYS, 414 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 415 } else { 416 int i; 417 char *c; 418 419 for (i = 0; i < argc; i++) { 420 uint_t tran; 421 uint_t *tranidp = NULL; 422 datalink_id_t linkid; 423 424 if ((c = strrchr(argv[i], '/')) != NULL) { 425 unsigned long u; 426 char *eptr; 427 428 c++; 429 errno = 0; 430 u = strtoul(c, &eptr, 10); 431 if (errno != 0 || *eptr != '\0' || 432 u >= UINT_MAX) { 433 (void) fprintf(stderr, "failed to " 434 "parse link/transceiver: %s\n", 435 argv[i]); 436 return (1); 437 } 438 c--; 439 *c = '\0'; 440 tran = (uint_t)u; 441 tranidp = &tran; 442 } 443 444 if ((status = dladm_name2info(dltran_hdl, argv[i], 445 &linkid, NULL, NULL, NULL)) != DLADM_STATUS_OK) { 446 (void) fprintf(stderr, "failed to get link " 447 "id for link %s: %s\n", argv[i], 448 dladm_status2str(status, dltran_dlerrmsg)); 449 return (1); 450 } 451 452 (void) dltran_dump_transceivers(dltran_hdl, linkid, 453 tranidp); 454 } 455 } 456 457 return (dltran_errors != 0 ? 1 : 0); 458 } 459