xref: /illumos-gate/usr/src/cmd/dlutil/dltraninfo.c (revision 069e6b7e31ba5dcbc5441b98af272714d9a5455c)
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(&gt, 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, &gt) != 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(&gt, sizeof (gt));
276 		gt.dgt_linkid = linkid;
277 		gt.dgt_tran_id = i;
278 
279 		if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, &gt) != 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