xref: /illumos-gate/usr/src/cmd/mdb/common/modules/emlxs/emlxs.c (revision 8c4cbc5227c35cbf837b0144a642e55e7cf84a15)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at
9  * http://www.opensource.org/licenses/cddl1.txt.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2004-2011 Emulex. All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2025 Oxide Computer Company
26  */
27 
28 #define	DUMP_SUPPORT
29 
30 #include <emlxs_mdb.h>
31 #include <emlxs_msg.h>
32 #include <emlxs_dump.h>
33 #include <emlxs_device.h>
34 
35 /*
36  * MDB module linkage information:
37  */
38 
39 static const mdb_dcmd_t dcmds[] =
40 {
41 	{ DRIVER_NAME"_msgbuf", "<instance>", "dumps the "DRIVER_NAME
42 	    " driver internal message buffer", emlxs_msgbuf, emlxs_msgbuf_help},
43 	{ DRIVER_NAME"_dump", "<type> <instance>", "dumps the "DRIVER_NAME
44 	    " driver firmware core", emlxs_dump, emlxs_dump_help},
45 	{ NULL }
46 };
47 
48 static const mdb_modinfo_t modinfo =
49 {
50 	MDB_API_VERSION,
51 	dcmds,
52 	NULL
53 };
54 
55 const mdb_modinfo_t *
_mdb_init(void)56 _mdb_init(void)
57 {
58 	return (&modinfo);
59 }
60 
61 
62 /*
63  * emlxs_msgbuf library
64  */
65 void
emlxs_msgbuf_help()66 emlxs_msgbuf_help()
67 {
68 
69 	mdb_printf("Usage:   ::%s_msgbuf  <instance(hex)>\n\n", DRIVER_NAME);
70 	mdb_printf("         <instance>   This is the %s driver instance " \
71 	    "number in hex.\n", DRIVER_NAME);
72 	mdb_printf("                      (e.g. 0, 1,..., e, f, etc.)\n");
73 
74 } /* emlxs_msgbuf_help() */
75 
76 
77 /*ARGSUSED*/
emlxs_msgbuf(uintptr_t base_addr,uint_t flags,int argc,const mdb_arg_t * argv)78 int emlxs_msgbuf(uintptr_t base_addr, uint_t flags, int argc,
79 				const mdb_arg_t *argv)
80 {
81 	uintptr_t  addr;
82 	emlxs_device_t device;
83 	uint32_t brd_no;
84 	emlxs_msg_log_t log;
85 	uint32_t count;
86 	uint32_t first;
87 	uint32_t last;
88 	uint32_t idx;
89 	uint32_t i;
90 	char *level;
91 	emlxs_msg_t msg;
92 	char	merge[1024];
93 
94 	emlxs_msg_entry_t entry;
95 	char buffer[256];
96 	char buffer2[256];
97 	int32_t instance[MAX_FC_BRDS];
98 	char driver[32];
99 	int32_t instance_count;
100 	uint32_t ddiinst;
101 
102 	if (argc != 1 || argv[0].a_type != MDB_TYPE_STRING) {
103 		mdb_printf("Usage:   ::%s_msgbuf  <instance(hex)>\n",
104 		    DRIVER_NAME);
105 		mdb_printf("mdb: try \"::help %s_msgbuf\" for more information",
106 		    DRIVER_NAME);
107 
108 		return (DCMD_ERR);
109 	}
110 
111 	/* Get the device address */
112 	mdb_snprintf(buffer, sizeof (buffer), "%s_device", DRIVER_NAME);
113 	if (mdb_readvar(&device, buffer) == -1) {
114 		mdb_snprintf(buffer2, sizeof (buffer2),
115 		    "%s not found.\n", buffer);
116 		mdb_warn(buffer2);
117 
118 		mdb_snprintf(buffer2, sizeof (buffer2),
119 		    "Is the %s driver loaded ?\n", DRIVER_NAME);
120 		mdb_warn(buffer2);
121 		return (DCMD_ERR);
122 	}
123 
124 	/* Get the device instance table */
125 	mdb_snprintf(buffer, sizeof (buffer), "%s_instance", DRIVER_NAME);
126 	if (mdb_readvar(&instance, buffer) == -1) {
127 		mdb_snprintf(buffer2, sizeof (buffer2), "%s not found.\n",
128 		    buffer);
129 		mdb_warn(buffer2);
130 
131 		mdb_snprintf(buffer2, sizeof (buffer2),
132 		    "Is the %s driver loaded ?\n", DRIVER_NAME);
133 		mdb_warn(buffer2);
134 		return (DCMD_ERR);
135 	}
136 
137 	/* Get the device instance count */
138 	mdb_snprintf(buffer, sizeof (buffer), "%s_instance_count", DRIVER_NAME);
139 	if (mdb_readvar(&instance_count, buffer) == -1) {
140 		mdb_snprintf(buffer2, sizeof (buffer2), "%s not found.\n",
141 		    buffer);
142 		mdb_warn(buffer2);
143 
144 		mdb_snprintf(buffer2, sizeof (buffer2),
145 		    "Is the %s driver loaded ?\n", DRIVER_NAME);
146 		mdb_warn(buffer2);
147 		return (DCMD_ERR);
148 	}
149 
150 	ddiinst = (uint32_t)mdb_strtoull(argv[0].a_un.a_str);
151 
152 	for (brd_no = 0; brd_no < instance_count; brd_no++) {
153 		if (instance[brd_no] == ddiinst) {
154 			break;
155 		}
156 	}
157 
158 	if (brd_no == instance_count) {
159 		mdb_warn("Device instance not found. ddinst=%d\n", ddiinst);
160 		return (DCMD_ERR);
161 	}
162 
163 	/* Check if buffer is null */
164 	addr = (uintptr_t)device.log[brd_no];
165 	if (addr == 0) {
166 		mdb_warn("Device instance not found. ddinst=%d\n", ddiinst);
167 		return (0);
168 	}
169 
170 	if (mdb_vread(&log, sizeof (emlxs_msg_log_t), addr) !=
171 	    sizeof (emlxs_msg_log_t)) {
172 		mdb_warn("\nUnable to read %d bytes @ %llx.\n",
173 		    sizeof (emlxs_msg_log_t), addr);
174 		return (0);
175 	}
176 
177 	/* Check if buffer is empty */
178 	if (log.count == 0) {
179 		mdb_warn("Log buffer empty.\n");
180 		return (0);
181 	}
182 
183 	/* Get last entry id saved */
184 	last  = log.count - 1;
185 
186 	/* Check if buffer has already been filled once */
187 	if (log.count >= log.size) {
188 		first = log.count - log.size;
189 		idx = log.next;
190 	} else {
191 		/* Buffer not yet filled */
192 		first = 0;
193 		idx = 0;
194 	}
195 
196 	/* Get the total number of messages available for return */
197 	count = last - first + 1;
198 
199 	mdb_printf("\n");
200 
201 	/* Print the messages */
202 	for (i = 0; i < count; i++) {
203 		if (mdb_vread(&entry, sizeof (emlxs_msg_entry_t),
204 		    (uintptr_t)&log.entry[idx]) != sizeof (emlxs_msg_entry_t)) {
205 			mdb_warn("Cannot read log entry. index=%d count=%d\n",
206 			    idx, count);
207 			return (DCMD_ERR);
208 		}
209 
210 		if (mdb_vread(&msg, sizeof (emlxs_msg_t),
211 		    (uintptr_t)entry.msg) != sizeof (emlxs_msg_t)) {
212 			mdb_warn("Cannot read msg. index=%d count=%d\n",
213 			    idx, count);
214 			return (DCMD_ERR);
215 		}
216 
217 		switch (msg.level) {
218 		case EMLXS_DEBUG:
219 			level = "  DEBUG";
220 			break;
221 
222 		case EMLXS_NOTICE:
223 			level = " NOTICE";
224 			break;
225 
226 		case EMLXS_WARNING:
227 			level = "WARNING";
228 			break;
229 
230 		case EMLXS_ERROR:
231 			level = "  ERROR";
232 			break;
233 
234 		case EMLXS_PANIC:
235 			level = "  PANIC";
236 			break;
237 
238 		default:
239 			level = "UNKNOWN";
240 			break;
241 		}
242 
243 		if (entry.vpi == 0) {
244 			mdb_snprintf(driver, sizeof (driver), "%s%d",
245 			    DRIVER_NAME, entry.instance);
246 		} else {
247 			mdb_snprintf(driver, sizeof (driver), "%s%d.%d",
248 			    DRIVER_NAME, entry.instance, entry.vpi);
249 		}
250 
251 		/* Generate the message string */
252 		if (msg.buffer[0] != 0) {
253 			if (entry.buffer[0] != 0) {
254 				mdb_snprintf(merge, sizeof (merge),
255 				    "[%Y:%03d:%03d:%03d] "
256 				    "%6d:[%1X.%04X]%s:%7s:%4d:\n%s\n(%s)\n",
257 				    entry.id_time.tv_sec,
258 				    (int)entry.id_time.tv_nsec/1000000,
259 				    (int)(entry.id_time.tv_nsec/1000)%1000,
260 				    (int)entry.id_time.tv_nsec%1000,
261 				    entry.id, entry.fileno,
262 				    entry.line, driver, level, msg.id,
263 				    msg.buffer, entry.buffer);
264 
265 			} else {
266 				mdb_snprintf(merge, sizeof (merge),
267 				    "[%Y:%03d:%03d:%03d] "
268 				    "%6d:[%1X.%04X]%s:%7s:%4d:\n%s\n",
269 				    entry.id_time.tv_sec,
270 				    (int)entry.id_time.tv_nsec/1000000,
271 				    (int)(entry.id_time.tv_nsec/1000)%1000,
272 				    (int)entry.id_time.tv_nsec%1000,
273 				    entry.id, entry.fileno,
274 				    entry.line, driver, level, msg.id,
275 				    msg.buffer);
276 			}
277 		} else {
278 			if (entry.buffer[0] != 0) {
279 				mdb_snprintf(merge, sizeof (merge),
280 				    "[%Y:%03d:%03d:%03d] "
281 				    "%6d:[%1X.%04X]%s:%7s:%4d:\n(%s)\n",
282 				    entry.id_time.tv_sec,
283 				    (int)entry.id_time.tv_nsec/1000000,
284 				    (int)(entry.id_time.tv_nsec/1000)%1000,
285 				    (int)entry.id_time.tv_nsec%1000,
286 				    entry.id, entry.fileno,
287 				    entry.line, driver, level, msg.id,
288 				    entry.buffer);
289 
290 			} else {
291 				mdb_snprintf(merge, sizeof (merge),
292 				    "[%Y:%03d:%03d:%03d] "
293 				    "%6d:[%1X.%04X]%s:%7s:%4d:\n%s\n",
294 				    entry.id_time.tv_sec,
295 				    (int)entry.id_time.tv_nsec/1000000,
296 				    (int)(entry.id_time.tv_nsec/1000)%1000,
297 				    (int)entry.id_time.tv_nsec%1000,
298 				    entry.id, entry.fileno,
299 				    entry.line, driver, level, msg.id,
300 				    msg.buffer);
301 			}
302 		}
303 
304 		mdb_printf("%s", merge);
305 
306 		/* Increment index */
307 		if (++idx >= log.size) {
308 			idx = 0;
309 		}
310 	}
311 
312 	mdb_printf("\n");
313 
314 	return (0);
315 
316 } /* emlxs_msgbuf() */
317 
318 
319 void
emlxs_dump_help()320 emlxs_dump_help()
321 {
322 	mdb_printf("Usage:   ::%s_dump all <instance(hex)>\n", DRIVER_NAME);
323 	mdb_printf("         ::%s_dump txt <instance(hex)>\n", DRIVER_NAME);
324 	mdb_printf("         ::%s_dump dmp <instance(hex)>\n", DRIVER_NAME);
325 	mdb_printf("         ::%s_dump cee <instance(hex)>\n", DRIVER_NAME);
326 	mdb_printf("\n");
327 	mdb_printf("                txt   Display firmware text summary " \
328 	    "file.\n");
329 	mdb_printf("                dmp   Display firmware dmp binary file.\n");
330 	mdb_printf("                cee   Display firmware cee binary file. " \
331 	    "(FCOE adapters only)\n");
332 	mdb_printf("                all   Display all firmware core files.\n");
333 	mdb_printf("         <instance>   This is the %s driver instance " \
334 	    "number in hex.\n", DRIVER_NAME);
335 	mdb_printf("                      (e.g. 0, 1,..., e, f, etc.)\n");
336 
337 } /* emlxs_dump_help() */
338 
339 
340 /*ARGSUSED*/
341 int
emlxs_dump(uintptr_t base_addr,uint_t flags,int argc,const mdb_arg_t * argv)342 emlxs_dump(uintptr_t base_addr, uint_t flags, int argc, const mdb_arg_t *argv)
343 {
344 	uintptr_t  addr;
345 	emlxs_device_t device;
346 	uint32_t brd_no;
347 	uint32_t i;
348 	char buffer[256];
349 	char buffer2[256];
350 	int32_t instance[MAX_FC_BRDS];
351 	int32_t instance_count;
352 	uint32_t ddiinst;
353 	uint8_t *bptr;
354 	char *cptr;
355 	emlxs_file_t dump_txtfile;
356 	emlxs_file_t dump_dmpfile;
357 	emlxs_file_t dump_ceefile;
358 	uint32_t size;
359 	uint32_t file;
360 
361 	if (argc != 2 || argv[0].a_type != MDB_TYPE_STRING ||
362 	    argv[1].a_type != MDB_TYPE_STRING) {
363 		goto usage;
364 	}
365 
366 	if ((strcmp(argv[0].a_un.a_str, "all") == 0) ||
367 	    (strcmp(argv[0].a_un.a_str, "ALL") == 0) ||
368 	    (strcmp(argv[0].a_un.a_str, "All") == 0)) {
369 		file = 0;
370 	} else if ((strcmp(argv[0].a_un.a_str, "txt") == 0) ||
371 	    (strcmp(argv[0].a_un.a_str, "TXT") == 0) ||
372 	    (strcmp(argv[0].a_un.a_str, "Txt") == 0)) {
373 		file = 1;
374 	} else if ((strcmp(argv[0].a_un.a_str, "dmp") == 0) ||
375 	    (strcmp(argv[0].a_un.a_str, "DMP") == 0) ||
376 	    (strcmp(argv[0].a_un.a_str, "Dmp") == 0)) {
377 		file = 2;
378 	} else if ((strcmp(argv[0].a_un.a_str, "cee") == 0) ||
379 	    (strcmp(argv[0].a_un.a_str, "CEE") == 0) ||
380 	    (strcmp(argv[0].a_un.a_str, "Cee") == 0)) {
381 		file = 3;
382 	} else {
383 		goto usage;
384 	}
385 
386 	/* Get the device address */
387 	mdb_snprintf(buffer, sizeof (buffer), "%s_device", DRIVER_NAME);
388 	if (mdb_readvar(&device, buffer) == -1) {
389 		mdb_snprintf(buffer2, sizeof (buffer2),
390 		    "%s not found.\n", buffer);
391 		mdb_warn(buffer2);
392 
393 		mdb_snprintf(buffer2, sizeof (buffer2),
394 		    "Is the %s driver loaded ?\n", DRIVER_NAME);
395 		mdb_warn(buffer2);
396 		return (DCMD_ERR);
397 	}
398 
399 	/* Get the device instance table */
400 	mdb_snprintf(buffer, sizeof (buffer), "%s_instance", DRIVER_NAME);
401 	if (mdb_readvar(&instance, buffer) == -1) {
402 		mdb_snprintf(buffer2, sizeof (buffer2), "%s not found.\n",
403 		    buffer);
404 		mdb_warn(buffer2);
405 
406 		mdb_snprintf(buffer2, sizeof (buffer2),
407 		    "Is the %s driver loaded ?\n", DRIVER_NAME);
408 		mdb_warn(buffer2);
409 		return (DCMD_ERR);
410 	}
411 
412 	/* Get the device instance count */
413 	mdb_snprintf(buffer, sizeof (buffer), "%s_instance_count", DRIVER_NAME);
414 	if (mdb_readvar(&instance_count, buffer) == -1) {
415 		mdb_snprintf(buffer2, sizeof (buffer2), "%s not found.\n",
416 		    buffer);
417 		mdb_warn(buffer2);
418 
419 		mdb_snprintf(buffer2, sizeof (buffer2),
420 		    "Is the %s driver loaded ?\n", DRIVER_NAME);
421 		mdb_warn(buffer2);
422 		return (DCMD_ERR);
423 	}
424 
425 	ddiinst = (uint32_t)mdb_strtoull(argv[1].a_un.a_str);
426 
427 	for (brd_no = 0; brd_no < instance_count; brd_no++) {
428 		if (instance[brd_no] == ddiinst) {
429 			break;
430 		}
431 	}
432 
433 	if (brd_no == instance_count) {
434 		mdb_warn("Device instance not found. ddinst=%d\n", ddiinst);
435 		return (DCMD_ERR);
436 	}
437 
438 	if (file == 0 || file == 1) {
439 
440 		addr = (uintptr_t)device.dump_txtfile[brd_no];
441 		if (addr == 0) {
442 			mdb_warn("TXT file: Device instance not found. " \
443 			    "ddinst=%d\n", ddiinst);
444 			goto dmp_file;
445 		}
446 
447 		if (mdb_vread(&dump_txtfile, sizeof (dump_txtfile), addr)
448 		    != sizeof (dump_txtfile)) {
449 			mdb_warn("TXT file: Unable to read %d bytes @ %llx.\n",
450 			    sizeof (dump_txtfile), addr);
451 			goto dmp_file;
452 		}
453 
454 		size = (uintptr_t)dump_txtfile.ptr -
455 		    (uintptr_t)dump_txtfile.buffer;
456 
457 		if (size == 0) {
458 			mdb_printf("TXT file: Not available.\n");
459 			goto dmp_file;
460 		}
461 		bptr  = (uint8_t *)mdb_zalloc(size, UM_SLEEP|UM_GC);
462 
463 		if (bptr == 0) {
464 			mdb_warn("TXT file: Unable to allocate file buffer. " \
465 			    "ddinst=%d size=%d\n", ddiinst, size);
466 			goto dmp_file;
467 		}
468 
469 		if (mdb_vread(bptr, size, (uintptr_t)dump_txtfile.buffer)
470 		    != size) {
471 			mdb_warn("TXT file: Unable to read %d bytes @ %llx.\n",
472 			    size, dump_txtfile.buffer);
473 			goto dmp_file;
474 		}
475 
476 		mdb_printf("<TXT File Start>\n");
477 		mdb_printf("\n");
478 		mdb_printf("%s", bptr);
479 		mdb_printf("\n");
480 		mdb_printf("<TXT File End>\n");
481 	}
482 
483 dmp_file:
484 
485 	if (file == 0 || file == 2) {
486 		addr = (uintptr_t)device.dump_dmpfile[brd_no];
487 		if (addr == 0) {
488 			mdb_warn("DMP file: Device instance not found. " \
489 			    "ddinst=%d\n", ddiinst);
490 			goto cee_file;
491 		}
492 
493 		if (mdb_vread(&dump_dmpfile, sizeof (dump_dmpfile), addr)
494 		    != sizeof (dump_dmpfile)) {
495 			mdb_warn("DMP file: Unable to read %d bytes @ %llx.\n",
496 			    sizeof (dump_dmpfile), addr);
497 			goto cee_file;
498 		}
499 
500 		size = (uintptr_t)dump_dmpfile.ptr -
501 		    (uintptr_t)dump_dmpfile.buffer;
502 
503 		if (size == 0) {
504 			mdb_printf("DMP file: Not available.\n");
505 			goto cee_file;
506 		}
507 
508 		bptr  = (uint8_t *)mdb_zalloc(size, UM_SLEEP|UM_GC);
509 
510 		if (bptr == 0) {
511 			mdb_warn("DMP file: Unable to allocate file buffer. " \
512 			    "ddinst=%d size=%d\n", ddiinst, size);
513 			goto cee_file;
514 		}
515 
516 		if (mdb_vread(bptr, size, (uintptr_t)dump_dmpfile.buffer)
517 		    != size) {
518 			mdb_warn("DMP file: Unable to read %d bytes @ %llx.\n",
519 			    size, dump_dmpfile.buffer);
520 			goto cee_file;
521 		}
522 
523 		mdb_printf("<DMP File Start>\n");
524 		mdb_printf("\n");
525 
526 		bzero(buffer2, sizeof (buffer2));
527 		cptr = buffer2;
528 		for (i = 0; i < size; i++) {
529 			if (i && !(i % 16)) {
530 				mdb_printf(" %s\n", buffer2);
531 				bzero(buffer2, sizeof (buffer2));
532 				cptr = buffer2;
533 			}
534 
535 			if (!(i % 16)) {
536 				mdb_printf("%08X: ", i);
537 			}
538 
539 			if (!(i % 4)) {
540 				mdb_printf(" ");
541 			}
542 
543 			if ((*bptr >= 32) && (*bptr <= 126)) {
544 				*cptr++ = *bptr;
545 			} else {
546 				*cptr++ = '.';
547 			}
548 
549 			mdb_printf("%02X ", *bptr++);
550 		}
551 
552 		size = 16 - (i % 16);
553 		for (i = 0; size < 16 && i < size; i++) {
554 			if (!(i % 4)) {
555 				mdb_printf(" ");
556 			}
557 
558 			mdb_printf("   ");
559 		}
560 		mdb_printf(" %s\n", buffer2);
561 		mdb_printf("\n");
562 		mdb_printf("<DMP File End>\n");
563 	}
564 
565 cee_file:
566 
567 	if (file == 0 || file == 3) {
568 
569 		addr = (uintptr_t)device.dump_ceefile[brd_no];
570 		if (addr == 0) {
571 			mdb_warn("CEE file: Device instance not found. " \
572 			    "ddinst=%d\n", ddiinst);
573 			goto done;
574 		}
575 
576 		if (mdb_vread(&dump_ceefile, sizeof (dump_ceefile), addr)
577 		    != sizeof (dump_ceefile)) {
578 			mdb_warn("CEE file: Unable to read %d bytes @ %llx.\n",
579 			    sizeof (dump_ceefile), addr);
580 			goto done;
581 		}
582 
583 		size = (uintptr_t)dump_ceefile.ptr -
584 		    (uintptr_t)dump_ceefile.buffer;
585 
586 		if (size == 0) {
587 			mdb_printf("CEE file: Not available.\n");
588 			goto done;
589 		}
590 
591 		bptr  = (uint8_t *)mdb_zalloc(size, UM_SLEEP|UM_GC);
592 
593 		if (bptr == 0) {
594 			mdb_warn("CEE file: Unable to allocate file buffer. " \
595 			    "ddinst=%d size=%d\n", ddiinst, size);
596 			goto done;
597 		}
598 
599 		if (mdb_vread(bptr, size, (uintptr_t)dump_ceefile.buffer)
600 		    != size) {
601 			mdb_warn("CEE file: Unable to read %d bytes @ %llx.\n",
602 			    size, dump_ceefile.buffer);
603 			goto done;
604 		}
605 
606 		mdb_printf("<CEE File Start>\n");
607 		mdb_printf("\n");
608 
609 		bzero(buffer2, sizeof (buffer2));
610 		cptr = buffer2;
611 		for (i = 0; i < size; i++) {
612 			if (i && !(i % 16)) {
613 				mdb_printf(" %s\n", buffer2);
614 				bzero(buffer2, sizeof (buffer2));
615 				cptr = buffer2;
616 			}
617 
618 			if (!(i % 16)) {
619 				mdb_printf("%08X: ", i);
620 			}
621 
622 			if (!(i % 4)) {
623 				mdb_printf(" ");
624 			}
625 
626 			if ((*bptr >= 32) && (*bptr <= 126)) {
627 				*cptr++ = *bptr;
628 			} else {
629 				*cptr++ = '.';
630 			}
631 
632 			mdb_printf("%02X ", *bptr++);
633 		}
634 
635 		size = 16 - (i % 16);
636 		for (i = 0; size < 16 && i < size; i++) {
637 			if (!(i % 4)) {
638 				mdb_printf(" ");
639 			}
640 
641 			mdb_printf("   ");
642 		}
643 		mdb_printf(" %s\n", buffer2);
644 		mdb_printf("\n");
645 		mdb_printf("<CEE File End>\n");
646 	}
647 done:
648 
649 	mdb_printf("\n");
650 	return (0);
651 
652 usage:
653 	mdb_printf("Usage:   ::%s_dump <file> <instance (hex)>\n",
654 	    DRIVER_NAME);
655 	mdb_printf("mdb: try \"::help %s_dump\" for more information",
656 	    DRIVER_NAME);
657 
658 	return (DCMD_ERR);
659 
660 } /* emlxs_dump() */
661