xref: /illumos-gate/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c (revision 4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6)
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 usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <limits.h>
27 #include <sys/mdb_modapi.h>
28 #include <sys/sysinfo.h>
29 #include <sys/scsi/scsi.h>
30 #include <sys/scsi/adapters/pmcs/pmcs.h>
31 
32 #define	MDB_RD(a, b, c)	mdb_vread(a, b, (uintptr_t)c)
33 #define	NOREAD(a, b)	mdb_warn("could not read " #a " at 0x%p", b)
34 
35 static pmcs_hw_t ss;
36 static pmcs_xscsi_t **targets = NULL;
37 static int target_idx;
38 
39 static uint32_t	sas_phys, sata_phys, exp_phys, num_expanders, empty_phys;
40 
41 static pmcs_phy_t *pmcs_next_sibling(pmcs_phy_t *phyp);
42 
43 static void
44 print_sas_address(pmcs_phy_t *phy)
45 {
46 	int idx;
47 
48 	for (idx = 0; idx < 8; idx++) {
49 		mdb_printf("%02x", phy->sas_address[idx]);
50 	}
51 }
52 
53 /*ARGSUSED*/
54 static void
55 display_ic(struct pmcs_hw m, int verbose)
56 {
57 	int msec_per_tick;
58 
59 	if (mdb_readvar(&msec_per_tick, "msec_per_tick") == -1) {
60 		mdb_warn("can't read msec_per_tick");
61 		msec_per_tick = 0;
62 	}
63 
64 	mdb_printf("\n");
65 	mdb_printf("Interrupt coalescing timer info\n");
66 	mdb_printf("-------------------------------\n");
67 	if (msec_per_tick == 0) {
68 		mdb_printf("Quantum                       : ?? ms\n");
69 	} else {
70 		mdb_printf("Quantum                       : %d ms\n",
71 		    m.io_intr_coal.quantum * msec_per_tick);
72 	}
73 	mdb_printf("Timer enabled                 : ");
74 	if (m.io_intr_coal.timer_on) {
75 		mdb_printf("Yes\n");
76 		mdb_printf("Coalescing timer value        : %d us\n",
77 		    m.io_intr_coal.intr_coal_timer);
78 	} else {
79 		mdb_printf("No\n");
80 	}
81 	mdb_printf("Total nsecs between interrupts: %ld\n",
82 	    m.io_intr_coal.nsecs_between_intrs);
83 	mdb_printf("Time of last I/O interrupt    : %ld\n",
84 	    m.io_intr_coal.last_io_comp);
85 	mdb_printf("Number of I/O interrupts      : %d\n",
86 	    m.io_intr_coal.num_intrs);
87 	mdb_printf("Number of I/O completions     : %d\n",
88 	    m.io_intr_coal.num_io_completions);
89 	mdb_printf("Max I/O completion interrupts : %d\n",
90 	    m.io_intr_coal.max_io_completions);
91 	mdb_printf("Measured ECHO int latency     : %d ns\n",
92 	    m.io_intr_coal.intr_latency);
93 	mdb_printf("Interrupt threshold           : %d\n",
94 	    m.io_intr_coal.intr_threshold);
95 }
96 
97 /*ARGSUSED*/
98 static int
99 pmcs_iport_phy_walk_cb(uintptr_t addr, const void *wdata, void *priv)
100 {
101 	struct pmcs_phy		phy;
102 
103 	if (mdb_vread(&phy, sizeof (struct pmcs_phy), addr) !=
104 	    sizeof (struct pmcs_phy)) {
105 		return (DCMD_ERR);
106 	}
107 
108 	mdb_printf("%16p %2d\n", addr, phy.phynum);
109 
110 	return (0);
111 }
112 
113 /*ARGSUSED*/
114 static int
115 pmcs_iport_walk_cb(uintptr_t addr, const void *wdata, void *priv)
116 {
117 	struct pmcs_iport	iport;
118 	uintptr_t		list_addr;
119 	char			*ua_state;
120 	char			portid[4];
121 	char			unit_address[34];
122 
123 	if (mdb_vread(&iport, sizeof (struct pmcs_iport), addr) !=
124 	    sizeof (struct pmcs_iport)) {
125 		return (DCMD_ERR);
126 	}
127 
128 	if (mdb_readstr(unit_address, sizeof (unit_address),
129 	    (uintptr_t)(iport.ua)) == -1) {
130 		strncpy(unit_address, "Unset", sizeof (unit_address));
131 	}
132 
133 	if (iport.portid == 0xffff) {
134 		mdb_snprintf(portid, sizeof (portid), "%s", "-");
135 	} else {
136 		mdb_snprintf(portid, sizeof (portid), "%d", iport.portid);
137 	}
138 
139 	switch (iport.ua_state) {
140 	case UA_INACTIVE:
141 		ua_state = "Inactive";
142 		break;
143 	case UA_PEND_ACTIVATE:
144 		ua_state = "PendActivate";
145 		break;
146 	case UA_ACTIVE:
147 		ua_state = "Active";
148 		break;
149 	case UA_PEND_DEACTIVATE:
150 		ua_state = "PendDeactivate";
151 		break;
152 	default:
153 		ua_state = "Unknown";
154 	}
155 
156 	if (strlen(unit_address) < 3) {
157 		/* Standard iport unit address */
158 		mdb_printf("UA %-16s %16s %8s %8s %16s", "Iport", "UA State",
159 		    "PortID", "NumPhys", "DIP\n");
160 		mdb_printf("%2s %16p %16s %8s %8d %16p\n", unit_address, addr,
161 		    ua_state, portid, iport.nphy, iport.dip);
162 	} else {
163 		/* Temporary iport unit address */
164 		mdb_printf("%-32s %16s %20s %8s %8s %16s", "UA", "Iport",
165 		    "UA State", "PortID", "NumPhys", "DIP\n");
166 		mdb_printf("%32s %16p %20s %8s %8d %16p\n", unit_address, addr,
167 		    ua_state, portid, iport.nphy, iport.dip);
168 	}
169 
170 	if (iport.nphy > 0) {
171 		mdb_inc_indent(4);
172 		mdb_printf("%-18s %8s", "Phy", "PhyNum\n");
173 		mdb_inc_indent(2);
174 		list_addr =
175 		    (uintptr_t)(addr + offsetof(struct pmcs_iport, phys));
176 		if (mdb_pwalk("list", pmcs_iport_phy_walk_cb, NULL,
177 		    list_addr) == -1) {
178 			mdb_warn("pmcs iport walk failed");
179 		}
180 		mdb_dec_indent(6);
181 		mdb_printf("\n");
182 	}
183 
184 	return (0);
185 }
186 
187 /*ARGSUSED*/
188 static void
189 display_iport(struct pmcs_hw m, uintptr_t addr, int verbose)
190 {
191 	uintptr_t	list_addr;
192 
193 	if (m.iports_attached) {
194 		mdb_printf("Iport information:\n");
195 		mdb_printf("-----------------\n");
196 	} else {
197 		mdb_printf("No Iports found.\n\n");
198 		return;
199 	}
200 
201 	list_addr = (uintptr_t)(addr + offsetof(struct pmcs_hw, iports));
202 
203 	if (mdb_pwalk("list", pmcs_iport_walk_cb, NULL, list_addr) == -1) {
204 		mdb_warn("pmcs iport walk failed");
205 	}
206 
207 	mdb_printf("\n");
208 }
209 
210 /*ARGSUSED*/
211 static void
212 display_hwinfo(struct pmcs_hw m, int verbose)
213 {
214 	struct pmcs_hw	*mp = &m;
215 	char		*fwsupport;
216 
217 	switch (PMCS_FW_TYPE(mp)) {
218 	case PMCS_FW_TYPE_RELEASED:
219 		fwsupport = "Released";
220 		break;
221 	case PMCS_FW_TYPE_DEVELOPMENT:
222 		fwsupport = "Development";
223 		break;
224 	case PMCS_FW_TYPE_ALPHA:
225 		fwsupport = "Alpha";
226 		break;
227 	case PMCS_FW_TYPE_BETA:
228 		fwsupport = "Beta";
229 		break;
230 	default:
231 		fwsupport = "Special";
232 		break;
233 	}
234 
235 	mdb_printf("\nHardware information:\n");
236 	mdb_printf("---------------------\n");
237 
238 	mdb_printf("Chip revision:    %c\n", 'A' + m.chiprev);
239 	mdb_printf("SAS WWID:         %"PRIx64"\n", m.sas_wwns[0]);
240 	mdb_printf("Firmware version: %x.%x.%x (%s)\n",
241 	    PMCS_FW_MAJOR(mp), PMCS_FW_MINOR(mp), PMCS_FW_MICRO(mp),
242 	    fwsupport);
243 
244 	mdb_printf("Number of PHYs:   %d\n", m.nphy);
245 	mdb_printf("Maximum commands: %d\n", m.max_cmd);
246 	mdb_printf("Maximum devices:  %d\n", m.max_dev);
247 	mdb_printf("I/O queue depth:  %d\n", m.ioq_depth);
248 	if (m.fwlog == 0) {
249 		mdb_printf("Firmware logging: Disabled\n");
250 	} else {
251 		mdb_printf("Firmware logging: Enabled (%d)\n", m.fwlog);
252 	}
253 }
254 
255 static void
256 display_targets(struct pmcs_hw m, int verbose, int totals_only)
257 {
258 	char		*dtype;
259 	pmcs_xscsi_t	xs;
260 	pmcs_phy_t	phy;
261 	uint16_t	max_dev, idx;
262 	uint32_t	sas_targets = 0, smp_targets = 0, sata_targets = 0;
263 
264 	max_dev = m.max_dev;
265 
266 	if (targets == NULL) {
267 		targets = mdb_alloc(sizeof (targets) * max_dev, UM_SLEEP);
268 	}
269 
270 	if (MDB_RD(targets, sizeof (targets) * max_dev, m.targets) == -1) {
271 		NOREAD(targets, m.targets);
272 		return;
273 	}
274 
275 	if (!totals_only) {
276 		mdb_printf("\nTarget information:\n");
277 		mdb_printf("---------------------------------------\n");
278 		mdb_printf("VTGT %-16s %-16s %-5s %8s %s", "SAS Address",
279 		    "PHY Address", "DType", "Active", "DS");
280 		mdb_printf("\n");
281 	}
282 
283 	for (idx = 0; idx < max_dev; idx++) {
284 		if (targets[idx] == NULL) {
285 			continue;
286 		}
287 
288 		if (MDB_RD(&xs, sizeof (xs), targets[idx]) == -1) {
289 			NOREAD(pmcs_xscsi_t, targets[idx]);
290 			continue;
291 		}
292 
293 		/*
294 		 * It has to be one of new, assigned or dying to be of interest.
295 		 */
296 		if (xs.new == 0 && xs.assigned == 0 && xs.dying == 0) {
297 			continue;
298 		}
299 
300 		switch (xs.dtype) {
301 		case NOTHING:
302 			dtype = "None";
303 			break;
304 		case SATA:
305 			dtype = "SATA";
306 			sata_targets++;
307 			break;
308 		case SAS:
309 			dtype = "SAS";
310 			sas_targets++;
311 			break;
312 		case EXPANDER:
313 			dtype = "SMP";
314 			smp_targets++;
315 			break;
316 		}
317 
318 		if (totals_only) {
319 			continue;
320 		}
321 
322 		if (xs.phy) {
323 			if (MDB_RD(&phy, sizeof (phy), xs.phy) == -1) {
324 				NOREAD(pmcs_phy_t, xs.phy);
325 				continue;
326 			}
327 			mdb_printf("%4d ", idx);
328 			print_sas_address(&phy);
329 			mdb_printf(" %16p", xs.phy);
330 		} else {
331 			mdb_printf("%4d %16s", idx, "<no phy avail>");
332 		}
333 		mdb_printf(" %5s", dtype);
334 		mdb_printf(" %8d", xs.actv_cnt);
335 		mdb_printf(" %2d", xs.dev_state);
336 
337 		if (verbose) {
338 			if (xs.new) {
339 				mdb_printf(" new");
340 			} else if (xs.dying) {
341 				mdb_printf(" dying");
342 			} else if (xs.assigned) {
343 				mdb_printf(" assigned");
344 			}
345 			if (xs.draining) {
346 				mdb_printf(" draining");
347 			}
348 			if (xs.reset_wait) {
349 				mdb_printf(" reset_wait");
350 			}
351 			if (xs.resetting) {
352 				mdb_printf(" resetting");
353 			}
354 			if (xs.recover_wait) {
355 				mdb_printf(" recover_wait");
356 			}
357 			if (xs.recovering) {
358 				mdb_printf(" recovering");
359 			}
360 			if (xs.event_recovery) {
361 				mdb_printf(" event recovery");
362 			}
363 			if (xs.special_running) {
364 				mdb_printf(" special_active");
365 			}
366 			if (xs.ncq) {
367 				mdb_printf(" ncq_tagmap=0x%x qdepth=%d",
368 				    xs.tagmap, xs.qdepth);
369 			} else if (xs.pio) {
370 				mdb_printf(" pio");
371 			}
372 		}
373 
374 		mdb_printf("\n");
375 	}
376 
377 	if (!totals_only) {
378 		mdb_printf("\n");
379 	}
380 
381 	mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP)\n",
382 	    "Configured targets:", (sas_targets + sata_targets + smp_targets),
383 	    sas_targets, sata_targets, smp_targets);
384 }
385 
386 /*ARGSUSED1*/
387 static void
388 display_work(struct pmcs_hw m, int verbose)
389 {
390 	int		idx;
391 	int		tgt;
392 	int		hdrp =  0;
393 	pmcs_xscsi_t	xs;
394 	pmcs_phy_t	phy;
395 	char		buf[16];
396 	pmcwork_t	work, *wp = &work;
397 	uintptr_t	_wp;
398 	char		*state;
399 	char		*path;
400 
401 	mdb_printf("\nActive Work structure information:\n");
402 	mdb_printf("----------------------------------\n");
403 
404 	_wp = (uintptr_t)m.work;
405 
406 	for (idx = 0; idx < m.max_cmd; idx++, _wp += sizeof (pmcwork_t)) {
407 		if (MDB_RD(&work, sizeof (pmcwork_t), _wp) == -1) {
408 			NOREAD(pmcwork_t, _wp);
409 			continue;
410 		}
411 		if (wp->htag == PMCS_TAG_TYPE_FREE) {
412 			continue;
413 		}
414 		if (hdrp++ == 0) {
415 			mdb_printf("%8s %10s %20s %8s %8s O D\n",
416 			    "HTag", "State", "Phy Path", "Target", "Timer");
417 		}
418 		switch (wp->state) {
419 		case PMCS_WORK_STATE_NIL:
420 			state = "N/A";
421 			break;
422 		case PMCS_WORK_STATE_READY:
423 			state = "Ready";
424 			break;
425 		case PMCS_WORK_STATE_ONCHIP:
426 			state = "On Chip";
427 			break;
428 		case PMCS_WORK_STATE_INTR:
429 			state = "In Intr";
430 			break;
431 		case PMCS_WORK_STATE_IOCOMPQ:
432 			state = "I/O Comp";
433 			break;
434 		case PMCS_WORK_STATE_ABORTED:
435 			state = "I/O Aborted";
436 			break;
437 		case PMCS_WORK_STATE_TIMED_OUT:
438 			state = "I/O Timed Out";
439 			break;
440 		default:
441 			mdb_snprintf(buf, sizeof (buf), "STATE=%d", wp->state);
442 			state = buf;
443 			break;
444 		}
445 		if (wp->ssp_event && wp->ssp_event != 0xffffffff) {
446 			mdb_printf("SSP event 0x%x", wp->ssp_event);
447 		}
448 		tgt = -1;
449 		if (wp->xp) {
450 			if (MDB_RD(&xs, sizeof (xs), wp->xp) == -1) {
451 				NOREAD(pmcs_xscsi_t, wp->xp);
452 			} else {
453 				tgt = xs.target_num;
454 			}
455 		}
456 		if (wp->phy) {
457 			if (MDB_RD(&phy, sizeof (phy), wp->phy) == -1) {
458 				NOREAD(pmcs_phy_t, wp->phy);
459 				continue;
460 			}
461 			path = phy.path;
462 		} else {
463 			path = "????";
464 		}
465 		mdb_printf("%08x %10s %20s %8d %8u %1d %1d\n",
466 		    wp->htag, state, path, tgt, wp->timer,
467 		    wp->onwire, wp->dead);
468 	}
469 }
470 
471 static void
472 print_spcmd(pmcs_cmd_t *sp, void *kaddr, int printhdr, int indent)
473 {
474 	if (indent)
475 		mdb_inc_indent(4);
476 	if (printhdr) {
477 		mdb_printf("%16s %16s %16s %8s %s\n",
478 		    "Command", "SCSA pkt", "DMA Chunks", "HTAG", "SATL Tag");
479 	}
480 	mdb_printf("%16p %16p %16p %08x %08x\n",
481 	    kaddr, sp->cmd_pkt, sp->cmd_clist, sp->cmd_tag, sp->cmd_satltag);
482 	if (indent)
483 		mdb_dec_indent(4);
484 }
485 
486 /*ARGSUSED1*/
487 static void
488 display_waitqs(struct pmcs_hw m, int verbose)
489 {
490 	pmcs_cmd_t	*sp, s;
491 	pmcs_xscsi_t	xs;
492 	int		first, i;
493 	int		max_dev = m.max_dev;
494 
495 	sp = m.dq.stqh_first;
496 	first = 1;
497 	while (sp) {
498 		if (first) {
499 			mdb_printf("\nDead Command Queue:\n");
500 			mdb_printf("---------------------------\n");
501 		}
502 		if (MDB_RD(&s, sizeof (s), sp) == -1) {
503 			NOREAD(pmcs_cmd_t, sp);
504 			break;
505 		}
506 		print_spcmd(&s, sp, first, 0);
507 		sp = s.cmd_next.stqe_next;
508 		first = 0;
509 	}
510 
511 	sp = m.cq.stqh_first;
512 	first = 1;
513 	while (sp) {
514 		if (first) {
515 			mdb_printf("\nCompletion Command Queue:\n");
516 			mdb_printf("---------------------------\n");
517 		}
518 		if (MDB_RD(&s, sizeof (s), sp) == -1) {
519 			NOREAD(pmcs_cmd_t, sp);
520 			break;
521 		}
522 		print_spcmd(&s, sp, first, 0);
523 		sp = s.cmd_next.stqe_next;
524 		first = 0;
525 	}
526 
527 
528 	if (targets == NULL) {
529 		targets = mdb_alloc(sizeof (targets) * max_dev, UM_SLEEP);
530 	}
531 
532 	if (MDB_RD(targets, sizeof (targets) * max_dev, m.targets) == -1) {
533 		NOREAD(targets, m.targets);
534 		return;
535 	}
536 
537 	for (i = 0; i < max_dev; i++) {
538 		if (targets[i] == NULL) {
539 			continue;
540 		}
541 		if (MDB_RD(&xs, sizeof (xs), targets[i]) == -1) {
542 			NOREAD(pmcs_xscsi_t, targets[i]);
543 			continue;
544 		}
545 		sp = xs.wq.stqh_first;
546 		first = 1;
547 		while (sp) {
548 			if (first) {
549 				mdb_printf("\nTarget %u Wait Queue:\n",
550 				    xs.target_num);
551 				mdb_printf("---------------------------\n");
552 			}
553 			if (MDB_RD(&s, sizeof (s), sp) == -1) {
554 				NOREAD(pmcs_cmd_t, sp);
555 				break;
556 			}
557 			print_spcmd(&s, sp, first, 0);
558 			sp = s.cmd_next.stqe_next;
559 			first = 0;
560 		}
561 		sp = xs.aq.stqh_first;
562 		first = 1;
563 		while (sp) {
564 			if (first) {
565 				mdb_printf("\nTarget %u Active Queue:\n",
566 				    xs.target_num);
567 				mdb_printf("---------------------------\n");
568 			}
569 			if (MDB_RD(&s, sizeof (s), sp) == -1) {
570 				NOREAD(pmcs_cmd_t, sp);
571 				break;
572 			}
573 			print_spcmd(&s, sp, first, 0);
574 			sp = s.cmd_next.stqe_next;
575 			first = 0;
576 		}
577 		sp = xs.sq.stqh_first;
578 		first = 1;
579 		while (sp) {
580 			if (first) {
581 				mdb_printf("\nTarget %u Special Queue:\n",
582 				    xs.target_num);
583 				mdb_printf("---------------------------\n");
584 			}
585 			if (MDB_RD(&s, sizeof (s), sp) == -1) {
586 				NOREAD(pmcs_cmd_t, sp);
587 				break;
588 			}
589 			print_spcmd(&s, sp, first, 0);
590 			sp = s.cmd_next.stqe_next;
591 			first = 0;
592 		}
593 	}
594 }
595 
596 static char *
597 ibq_type(int qnum)
598 {
599 	if (qnum < 0 || qnum >= PMCS_NIQ) {
600 		return ("UNKNOWN");
601 	}
602 
603 	if (qnum < PMCS_IQ_OTHER) {
604 		return ("I/O");
605 	}
606 
607 	return ("Other");
608 }
609 
610 static char *
611 obq_type(int qnum)
612 {
613 	switch (qnum) {
614 	case PMCS_OQ_IODONE:
615 		return ("I/O");
616 		break;
617 	case PMCS_OQ_GENERAL:
618 		return ("General");
619 		break;
620 	case PMCS_OQ_EVENTS:
621 		return ("Events");
622 		break;
623 	default:
624 		return ("UNKNOWN");
625 	}
626 }
627 
628 static char *
629 iomb_cat(uint32_t cat)
630 {
631 	switch (cat) {
632 	case PMCS_IOMB_CAT_NET:
633 		return ("NET");
634 		break;
635 	case PMCS_IOMB_CAT_FC:
636 		return ("FC");
637 		break;
638 	case PMCS_IOMB_CAT_SAS:
639 		return ("SAS");
640 		break;
641 	case PMCS_IOMB_CAT_SCSI:
642 		return ("SCSI");
643 		break;
644 	default:
645 		return ("???");
646 	}
647 }
648 
649 static char *
650 inbound_iomb_opcode(uint32_t opcode)
651 {
652 	switch (opcode) {
653 	case PMCIN_ECHO:
654 		return ("ECHO");
655 		break;
656 	case PMCIN_GET_INFO:
657 		return ("GET_INFO");
658 		break;
659 	case PMCIN_GET_VPD:
660 		return ("GET_VPD");
661 		break;
662 	case PMCIN_PHY_START:
663 		return ("PHY_START");
664 		break;
665 	case PMCIN_PHY_STOP:
666 		return ("PHY_STOP");
667 		break;
668 	case PMCIN_SSP_INI_IO_START:
669 		return ("INI_IO_START");
670 		break;
671 	case PMCIN_SSP_INI_TM_START:
672 		return ("INI_TM_START");
673 		break;
674 	case PMCIN_SSP_INI_EXT_IO_START:
675 		return ("INI_EXT_IO_START");
676 		break;
677 	case PMCIN_DEVICE_HANDLE_ACCEPT:
678 		return ("DEVICE_HANDLE_ACCEPT");
679 		break;
680 	case PMCIN_SSP_TGT_IO_START:
681 		return ("TGT_IO_START");
682 		break;
683 	case PMCIN_SSP_TGT_RESPONSE_START:
684 		return ("TGT_RESPONSE_START");
685 		break;
686 	case PMCIN_SSP_INI_EDC_EXT_IO_START:
687 		return ("INI_EDC_EXT_IO_START");
688 		break;
689 	case PMCIN_SSP_INI_EDC_EXT_IO_START1:
690 		return ("INI_EDC_EXT_IO_START1");
691 		break;
692 	case PMCIN_SSP_TGT_EDC_IO_START:
693 		return ("TGT_EDC_IO_START");
694 		break;
695 	case PMCIN_SSP_ABORT:
696 		return ("SSP_ABORT");
697 		break;
698 	case PMCIN_DEREGISTER_DEVICE_HANDLE:
699 		return ("DEREGISTER_DEVICE_HANDLE");
700 		break;
701 	case PMCIN_GET_DEVICE_HANDLE:
702 		return ("GET_DEVICE_HANDLE");
703 		break;
704 	case PMCIN_SMP_REQUEST:
705 		return ("SMP_REQUEST");
706 		break;
707 	case PMCIN_SMP_RESPONSE:
708 		return ("SMP_RESPONSE");
709 		break;
710 	case PMCIN_SMP_ABORT:
711 		return ("SMP_ABORT");
712 		break;
713 	case PMCIN_ASSISTED_DISCOVERY:
714 		return ("ASSISTED_DISCOVERY");
715 		break;
716 	case PMCIN_REGISTER_DEVICE:
717 		return ("REGISTER_DEVICE");
718 		break;
719 	case PMCIN_SATA_HOST_IO_START:
720 		return ("SATA_HOST_IO_START");
721 		break;
722 	case PMCIN_SATA_ABORT:
723 		return ("SATA_ABORT");
724 		break;
725 	case PMCIN_LOCAL_PHY_CONTROL:
726 		return ("LOCAL_PHY_CONTROL");
727 		break;
728 	case PMCIN_GET_DEVICE_INFO:
729 		return ("GET_DEVICE_INFO");
730 		break;
731 	case PMCIN_TWI:
732 		return ("TWI");
733 		break;
734 	case PMCIN_FW_FLASH_UPDATE:
735 		return ("FW_FLASH_UPDATE");
736 		break;
737 	case PMCIN_SET_VPD:
738 		return ("SET_VPD");
739 		break;
740 	case PMCIN_GPIO:
741 		return ("GPIO");
742 		break;
743 	case PMCIN_SAS_DIAG_MODE_START_END:
744 		return ("SAS_DIAG_MODE_START_END");
745 		break;
746 	case PMCIN_SAS_DIAG_EXECUTE:
747 		return ("SAS_DIAG_EXECUTE");
748 		break;
749 	case PMCIN_SAW_HW_EVENT_ACK:
750 		return ("SAS_HW_EVENT_ACK");
751 		break;
752 	case PMCIN_GET_TIME_STAMP:
753 		return ("GET_TIME_STAMP");
754 		break;
755 	case PMCIN_PORT_CONTROL:
756 		return ("PORT_CONTROL");
757 		break;
758 	case PMCIN_GET_NVMD_DATA:
759 		return ("GET_NVMD_DATA");
760 		break;
761 	case PMCIN_SET_NVMD_DATA:
762 		return ("SET_NVMD_DATA");
763 		break;
764 	case PMCIN_SET_DEVICE_STATE:
765 		return ("SET_DEVICE_STATE");
766 		break;
767 	case PMCIN_GET_DEVICE_STATE:
768 		return ("GET_DEVICE_STATE");
769 		break;
770 	default:
771 		return ("UNKNOWN");
772 		break;
773 	}
774 }
775 
776 static char *
777 outbound_iomb_opcode(uint32_t opcode)
778 {
779 	switch (opcode) {
780 	case PMCOUT_ECHO:
781 		return ("ECHO");
782 		break;
783 	case PMCOUT_GET_INFO:
784 		return ("GET_INFO");
785 		break;
786 	case PMCOUT_GET_VPD:
787 		return ("GET_VPD");
788 		break;
789 	case PMCOUT_SAS_HW_EVENT:
790 		return ("SAS_HW_EVENT");
791 		break;
792 	case PMCOUT_SSP_COMPLETION:
793 		return ("SSP_COMPLETION");
794 		break;
795 	case PMCOUT_SMP_COMPLETION:
796 		return ("SMP_COMPLETION");
797 		break;
798 	case PMCOUT_LOCAL_PHY_CONTROL:
799 		return ("LOCAL_PHY_CONTROL");
800 		break;
801 	case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT:
802 		return ("SAS_ASSISTED_DISCOVERY_SENT");
803 		break;
804 	case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT:
805 		return ("SATA_ASSISTED_DISCOVERY_SENT");
806 		break;
807 	case PMCOUT_DEVICE_REGISTRATION:
808 		return ("DEVICE_REGISTRATION");
809 		break;
810 	case PMCOUT_DEREGISTER_DEVICE_HANDLE:
811 		return ("DEREGISTER_DEVICE_HANDLE");
812 		break;
813 	case PMCOUT_GET_DEVICE_HANDLE:
814 		return ("GET_DEVICE_HANDLE");
815 		break;
816 	case PMCOUT_SATA_COMPLETION:
817 		return ("SATA_COMPLETION");
818 		break;
819 	case PMCOUT_SATA_EVENT:
820 		return ("SATA_EVENT");
821 		break;
822 	case PMCOUT_SSP_EVENT:
823 		return ("SSP_EVENT");
824 		break;
825 	case PMCOUT_DEVICE_HANDLE_ARRIVED:
826 		return ("DEVICE_HANDLE_ARRIVED");
827 		break;
828 	case PMCOUT_SMP_REQUEST_RECEIVED:
829 		return ("SMP_REQUEST_RECEIVED");
830 		break;
831 	case PMCOUT_SSP_REQUEST_RECEIVED:
832 		return ("SSP_REQUEST_RECEIVED");
833 		break;
834 	case PMCOUT_DEVICE_INFO:
835 		return ("DEVICE_INFO");
836 		break;
837 	case PMCOUT_FW_FLASH_UPDATE:
838 		return ("FW_FLASH_UPDATE");
839 		break;
840 	case PMCOUT_SET_VPD:
841 		return ("SET_VPD");
842 		break;
843 	case PMCOUT_GPIO:
844 		return ("GPIO");
845 		break;
846 	case PMCOUT_GPIO_EVENT:
847 		return ("GPIO_EVENT");
848 		break;
849 	case PMCOUT_GENERAL_EVENT:
850 		return ("GENERAL_EVENT");
851 		break;
852 	case PMCOUT_TWI:
853 		return ("TWI");
854 		break;
855 	case PMCOUT_SSP_ABORT:
856 		return ("SSP_ABORT");
857 		break;
858 	case PMCOUT_SATA_ABORT:
859 		return ("SATA_ABORT");
860 		break;
861 	case PMCOUT_SAS_DIAG_MODE_START_END:
862 		return ("SAS_DIAG_MODE_START_END");
863 		break;
864 	case PMCOUT_SAS_DIAG_EXECUTE:
865 		return ("SAS_DIAG_EXECUTE");
866 		break;
867 	case PMCOUT_GET_TIME_STAMP:
868 		return ("GET_TIME_STAMP");
869 		break;
870 	case PMCOUT_SAS_HW_EVENT_ACK_ACK:
871 		return ("SAS_HW_EVENT_ACK_ACK");
872 		break;
873 	case PMCOUT_PORT_CONTROL:
874 		return ("PORT_CONTROL");
875 		break;
876 	case PMCOUT_SKIP_ENTRIES:
877 		return ("SKIP_ENTRIES");
878 		break;
879 	case PMCOUT_SMP_ABORT:
880 		return ("SMP_ABORT");
881 		break;
882 	case PMCOUT_GET_NVMD_DATA:
883 		return ("GET_NVMD_DATA");
884 		break;
885 	case PMCOUT_SET_NVMD_DATA:
886 		return ("SET_NVMD_DATA");
887 		break;
888 	case PMCOUT_DEVICE_HANDLE_REMOVED:
889 		return ("DEVICE_HANDLE_REMOVED");
890 		break;
891 	case PMCOUT_SET_DEVICE_STATE:
892 		return ("SET_DEVICE_STATE");
893 		break;
894 	case PMCOUT_GET_DEVICE_STATE:
895 		return ("GET_DEVICE_STATE");
896 		break;
897 	case PMCOUT_SET_DEVICE_INFO:
898 		return ("SET_DEVICE_INFO");
899 		break;
900 	default:
901 		return ("UNKNOWN");
902 		break;
903 	}
904 }
905 
906 static void
907 dump_one_qentry_outbound(uint32_t *qentryp, int idx)
908 {
909 	int qeidx;
910 	uint32_t word0 = LE_32(*qentryp);
911 
912 	mdb_printf("Entry #%02d\n", idx);
913 	mdb_inc_indent(2);
914 
915 	mdb_printf("Header: 0x%08x (", word0);
916 	if (word0 & PMCS_IOMB_VALID) {
917 		mdb_printf("VALID, ");
918 	}
919 	if (word0 & PMCS_IOMB_HIPRI) {
920 		mdb_printf("HIPRI, ");
921 	}
922 	mdb_printf("OBID=%d, ",
923 	    (word0 & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT);
924 	mdb_printf("CAT=%s, ",
925 	    iomb_cat((word0 & PMCS_IOMB_CAT_MASK) >> PMCS_IOMB_CAT_SHIFT));
926 	mdb_printf("OPCODE=%s",
927 	    outbound_iomb_opcode(word0 & PMCS_IOMB_OPCODE_MASK));
928 	mdb_printf(")\n");
929 
930 	mdb_printf("Remaining Payload:\n");
931 
932 	mdb_inc_indent(2);
933 	for (qeidx = 1; qeidx < (PMCS_QENTRY_SIZE / 4); qeidx++) {
934 		mdb_printf("%08x ", LE_32(*(qentryp + qeidx)));
935 	}
936 	mdb_printf("\n");
937 	mdb_dec_indent(4);
938 }
939 
940 static void
941 display_outbound_queues(struct pmcs_hw ss, uint_t verbose)
942 {
943 	int		idx, qidx;
944 	uintptr_t	obqp;
945 	uint32_t	*cip;
946 	uint32_t	*qentryp = mdb_alloc(PMCS_QENTRY_SIZE, UM_SLEEP);
947 	uint32_t	last_consumed, oqpi;
948 
949 	mdb_printf("\n");
950 	mdb_printf("Outbound Queues\n");
951 	mdb_printf("---------------\n");
952 
953 	mdb_inc_indent(2);
954 
955 	for (qidx = 0; qidx < PMCS_NOQ; qidx++) {
956 		obqp = (uintptr_t)ss.oqp[qidx];
957 
958 		if (obqp == NULL) {
959 			mdb_printf("No outbound queue ptr for queue #%d\n",
960 			    qidx);
961 			continue;
962 		}
963 
964 		mdb_printf("Outbound Queue #%d (Queue Type = %s)\n", qidx,
965 		    obq_type(qidx));
966 		/*
967 		 * Chip is the producer, so read the actual producer index
968 		 * and not the driver's version
969 		 */
970 		cip = (uint32_t *)((void *)ss.cip);
971 		if (MDB_RD(&oqpi, 4, cip + OQPI_BASE_OFFSET +
972 		    (qidx * 4)) == -1) {
973 			mdb_warn("Couldn't read oqpi\n");
974 			break;
975 		}
976 
977 		mdb_printf("Producer index: %d  Consumer index: %d\n\n",
978 		    LE_32(oqpi), ss.oqci[qidx]);
979 		mdb_inc_indent(2);
980 
981 		if (ss.oqci[qidx] == 0) {
982 			last_consumed = ss.ioq_depth - 1;
983 		} else {
984 			last_consumed = ss.oqci[qidx] - 1;
985 		}
986 
987 
988 		if (!verbose) {
989 			mdb_printf("Last processed entry:\n");
990 			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
991 			    (obqp + (PMCS_QENTRY_SIZE * last_consumed)))
992 			    == -1) {
993 				mdb_warn("Couldn't read queue entry at 0x%p\n",
994 				    (obqp + (PMCS_QENTRY_SIZE *
995 				    last_consumed)));
996 				break;
997 			}
998 			dump_one_qentry_outbound(qentryp, last_consumed);
999 			mdb_printf("\n");
1000 			mdb_dec_indent(2);
1001 			continue;
1002 		}
1003 
1004 		for (idx = 0; idx < ss.ioq_depth; idx++) {
1005 			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
1006 			    (obqp + (PMCS_QENTRY_SIZE * idx))) == -1) {
1007 				mdb_warn("Couldn't read queue entry at 0x%p\n",
1008 				    (obqp + (PMCS_QENTRY_SIZE * idx)));
1009 				break;
1010 			}
1011 			dump_one_qentry_outbound(qentryp, idx);
1012 		}
1013 
1014 		mdb_printf("\n");
1015 		mdb_dec_indent(2);
1016 	}
1017 
1018 	mdb_dec_indent(2);
1019 	mdb_free(qentryp, PMCS_QENTRY_SIZE);
1020 }
1021 
1022 static void
1023 dump_one_qentry_inbound(uint32_t *qentryp, int idx)
1024 {
1025 	int qeidx;
1026 	uint32_t word0 = LE_32(*qentryp);
1027 
1028 	mdb_printf("Entry #%02d\n", idx);
1029 	mdb_inc_indent(2);
1030 
1031 	mdb_printf("Header: 0x%08x (", word0);
1032 	if (word0 & PMCS_IOMB_VALID) {
1033 		mdb_printf("VALID, ");
1034 	}
1035 	if (word0 & PMCS_IOMB_HIPRI) {
1036 		mdb_printf("HIPRI, ");
1037 	}
1038 	mdb_printf("OBID=%d, ",
1039 	    (word0 & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT);
1040 	mdb_printf("CAT=%s, ",
1041 	    iomb_cat((word0 & PMCS_IOMB_CAT_MASK) >> PMCS_IOMB_CAT_SHIFT));
1042 	mdb_printf("OPCODE=%s",
1043 	    inbound_iomb_opcode(word0 & PMCS_IOMB_OPCODE_MASK));
1044 	mdb_printf(")\n");
1045 
1046 	mdb_printf("HTAG: 0x%08x\n", LE_32(*(qentryp + 1)));
1047 	mdb_printf("Remaining Payload:\n");
1048 
1049 	mdb_inc_indent(2);
1050 	for (qeidx = 2; qeidx < (PMCS_QENTRY_SIZE / 4); qeidx++) {
1051 		mdb_printf("%08x ", LE_32(*(qentryp + qeidx)));
1052 	}
1053 	mdb_printf("\n");
1054 	mdb_dec_indent(4);
1055 }
1056 
1057 static void
1058 display_inbound_queues(struct pmcs_hw ss, uint_t verbose)
1059 {
1060 	int		idx, qidx, iqci, last_consumed;
1061 	uintptr_t	ibqp;
1062 	uint32_t	*qentryp = mdb_alloc(PMCS_QENTRY_SIZE, UM_SLEEP);
1063 	uint32_t	*cip;
1064 
1065 	mdb_printf("\n");
1066 	mdb_printf("Inbound Queues\n");
1067 	mdb_printf("--------------\n");
1068 
1069 	mdb_inc_indent(2);
1070 
1071 	for (qidx = 0; qidx < PMCS_NIQ; qidx++) {
1072 		ibqp = (uintptr_t)ss.iqp[qidx];
1073 
1074 		if (ibqp == NULL) {
1075 			mdb_printf("No inbound queue ptr for queue #%d\n",
1076 			    qidx);
1077 			continue;
1078 		}
1079 
1080 		mdb_printf("Inbound Queue #%d (Queue Type = %s)\n", qidx,
1081 		    ibq_type(qidx));
1082 
1083 		cip = (uint32_t *)((void *)ss.cip);
1084 		if (MDB_RD(&iqci, 4, cip + (qidx * 4)) == -1) {
1085 			mdb_warn("Couldn't read iqci\n");
1086 			break;
1087 		}
1088 		iqci = LE_32(iqci);
1089 
1090 		mdb_printf("Producer index: %d  Consumer index: %d\n\n",
1091 		    ss.shadow_iqpi[qidx], iqci);
1092 		mdb_inc_indent(2);
1093 
1094 		if (iqci == 0) {
1095 			last_consumed = ss.ioq_depth - 1;
1096 		} else {
1097 			last_consumed = iqci - 1;
1098 		}
1099 
1100 		if (!verbose) {
1101 			mdb_printf("Last processed entry:\n");
1102 			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
1103 			    (ibqp + (PMCS_QENTRY_SIZE * last_consumed)))
1104 			    == -1) {
1105 				mdb_warn("Couldn't read queue entry at 0x%p\n",
1106 				    (ibqp + (PMCS_QENTRY_SIZE *
1107 				    last_consumed)));
1108 				break;
1109 			}
1110 			dump_one_qentry_inbound(qentryp, last_consumed);
1111 			mdb_printf("\n");
1112 			mdb_dec_indent(2);
1113 			continue;
1114 		}
1115 
1116 		for (idx = 0; idx < ss.ioq_depth; idx++) {
1117 			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
1118 			    (ibqp + (PMCS_QENTRY_SIZE * idx))) == -1) {
1119 				mdb_warn("Couldn't read queue entry at 0x%p\n",
1120 				    (ibqp + (PMCS_QENTRY_SIZE * idx)));
1121 				break;
1122 			}
1123 			dump_one_qentry_inbound(qentryp, idx);
1124 		}
1125 
1126 		mdb_printf("\n");
1127 		mdb_dec_indent(2);
1128 	}
1129 
1130 	mdb_dec_indent(2);
1131 	mdb_free(qentryp, PMCS_QENTRY_SIZE);
1132 }
1133 
1134 static void
1135 display_phy(struct pmcs_phy phy, int verbose, int totals_only)
1136 {
1137 	char		*dtype, *speed;
1138 	char		*yes = "Yes";
1139 	char		*no = "No";
1140 	char		*cfgd = no;
1141 	char		*apend = no;
1142 	char		*asent = no;
1143 	char		*dead = no;
1144 	char		*changed = no;
1145 
1146 	switch (phy.dtype) {
1147 	case NOTHING:
1148 		dtype = "None";
1149 		break;
1150 	case SATA:
1151 		dtype = "SATA";
1152 		if (phy.configured) {
1153 			++sata_phys;
1154 		}
1155 		break;
1156 	case SAS:
1157 		dtype = "SAS";
1158 		if (phy.configured) {
1159 			++sas_phys;
1160 		}
1161 		break;
1162 	case EXPANDER:
1163 		dtype = "EXP";
1164 		if (phy.configured) {
1165 			++exp_phys;
1166 		}
1167 		break;
1168 	}
1169 
1170 	if (phy.dtype == NOTHING) {
1171 		empty_phys++;
1172 	} else if ((phy.dtype == EXPANDER) && phy.configured) {
1173 		num_expanders++;
1174 	}
1175 
1176 	if (totals_only) {
1177 		return;
1178 	}
1179 
1180 	switch (phy.link_rate) {
1181 	case SAS_LINK_RATE_1_5GBIT:
1182 		speed = "1.5Gb/s";
1183 		break;
1184 	case SAS_LINK_RATE_3GBIT:
1185 		speed = "3 Gb/s";
1186 		break;
1187 	case SAS_LINK_RATE_6GBIT:
1188 		speed = "6 Gb/s";
1189 		break;
1190 	default:
1191 		speed = "N/A";
1192 		break;
1193 	}
1194 
1195 	if ((phy.dtype != NOTHING) || verbose) {
1196 		print_sas_address(&phy);
1197 
1198 		if (phy.device_id != PMCS_INVALID_DEVICE_ID) {
1199 			mdb_printf(" %3d %4d %6s %4s ",
1200 			    phy.device_id, phy.phynum, speed, dtype);
1201 		} else {
1202 			mdb_printf(" N/A %4d %6s %4s ",
1203 			    phy.phynum, speed, dtype);
1204 		}
1205 
1206 		if (verbose) {
1207 			if (phy.abort_sent) {
1208 				asent = yes;
1209 			}
1210 			if (phy.abort_pending) {
1211 				apend = yes;
1212 			}
1213 			if (phy.configured) {
1214 				cfgd = yes;
1215 			}
1216 			if (phy.dead) {
1217 				dead = yes;
1218 			}
1219 			if (phy.changed) {
1220 				changed = yes;
1221 			}
1222 
1223 			mdb_printf("%-4s %-4s %-4s %-4s %-4s %3d "
1224 			    "0x%p ", cfgd, apend, asent,
1225 			    changed, dead, phy.ref_count, phy.phy_lock);
1226 		}
1227 
1228 		mdb_printf("Path: %s\n", phy.path);
1229 	}
1230 }
1231 
1232 static void
1233 display_phys(struct pmcs_hw ss, int verbose, struct pmcs_phy *parent, int level,
1234     int totals_only)
1235 {
1236 	pmcs_phy_t	phy;
1237 	pmcs_phy_t	*pphy = parent;
1238 
1239 	mdb_inc_indent(3);
1240 
1241 	if (parent == NULL) {
1242 		pphy = (pmcs_phy_t *)ss.root_phys;
1243 	} else {
1244 		pphy = (pmcs_phy_t *)parent;
1245 	}
1246 
1247 	if (level == 0) {
1248 		sas_phys = 0;
1249 		sata_phys = 0;
1250 		exp_phys = 0;
1251 		num_expanders = 0;
1252 		empty_phys = 0;
1253 	}
1254 
1255 	if (!totals_only) {
1256 		if (level == 0) {
1257 			mdb_printf("PHY information\n");
1258 		}
1259 		mdb_printf("--------\n");
1260 		mdb_printf("Level %2d\n", level);
1261 		mdb_printf("--------\n");
1262 		mdb_printf("SAS Address      Hdl Phy#  Speed Type ");
1263 
1264 		if (verbose) {
1265 			mdb_printf("Cfgd AbtP AbtS Chgd Dead Ref Lock\n");
1266 		} else {
1267 			mdb_printf("\n");
1268 		}
1269 	}
1270 
1271 	while (pphy) {
1272 		if (MDB_RD(&phy, sizeof (phy), (uintptr_t)pphy) == -1) {
1273 			NOREAD(pmcs_phy_t, phy);
1274 			break;
1275 		}
1276 
1277 		display_phy(phy, verbose, totals_only);
1278 
1279 		if (phy.children) {
1280 			display_phys(ss, verbose, phy.children, level + 1,
1281 			    totals_only);
1282 			if (!totals_only) {
1283 				mdb_printf("\n");
1284 			}
1285 		}
1286 
1287 		pphy = phy.sibling;
1288 	}
1289 
1290 	mdb_dec_indent(3);
1291 
1292 	if (level == 0) {
1293 		if (verbose) {
1294 			mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP) "
1295 			    "(+%d subsidiary + %d empty)\n", "Occupied PHYs:",
1296 			    (sas_phys + sata_phys + num_expanders),
1297 			    sas_phys, sata_phys, num_expanders,
1298 			    (exp_phys - num_expanders), empty_phys);
1299 		} else {
1300 			mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP)\n",
1301 			    "Occupied PHYs:",
1302 			    (sas_phys + sata_phys + num_expanders),
1303 			    sas_phys, sata_phys, num_expanders);
1304 		}
1305 	}
1306 }
1307 
1308 /*
1309  * MAX_INST_STRLEN is the largest string size from which we will attempt
1310  * to convert to an instance number.  The string will be formed up as
1311  * "0t<inst>\0" so that mdb_strtoull can parse it properly.
1312  */
1313 #define	MAX_INST_STRLEN	8
1314 
1315 static int
1316 pmcs_dump_tracelog(boolean_t filter, int instance)
1317 {
1318 	pmcs_tbuf_t *tbuf_addr;
1319 	uint_t tbuf_idx;
1320 	pmcs_tbuf_t tbuf;
1321 	boolean_t wrap, elem_filtered;
1322 	uint_t start_idx, elems_to_print, idx, tbuf_num_elems;
1323 	char *bufp;
1324 	char elem_inst[MAX_INST_STRLEN], ei_idx;
1325 
1326 	/* Get the address of the first element */
1327 	if (mdb_readvar(&tbuf_addr, "pmcs_tbuf") == -1) {
1328 		mdb_warn("can't read pmcs_tbuf");
1329 		return (DCMD_ERR);
1330 	}
1331 
1332 	/* Get the total number */
1333 	if (mdb_readvar(&tbuf_num_elems, "pmcs_tbuf_num_elems") == -1) {
1334 		mdb_warn("can't read pmcs_tbuf_num_elems");
1335 		return (DCMD_ERR);
1336 	}
1337 
1338 	/* Get the current index */
1339 	if (mdb_readvar(&tbuf_idx, "pmcs_tbuf_idx") == -1) {
1340 		mdb_warn("can't read pmcs_tbuf_idx");
1341 		return (DCMD_ERR);
1342 	}
1343 
1344 	/* Indicator as to whether the buffer has wrapped */
1345 	if (mdb_readvar(&wrap, "pmcs_tbuf_wrap") == -1) {
1346 		mdb_warn("can't read pmcs_tbuf_wrap");
1347 		return (DCMD_ERR);
1348 	}
1349 
1350 	/* Figure out where we start and stop */
1351 	if (wrap) {
1352 		start_idx = tbuf_idx;
1353 		elems_to_print = tbuf_num_elems;
1354 	} else {
1355 		start_idx = 0;
1356 		elems_to_print = tbuf_idx;
1357 	}
1358 
1359 	idx = start_idx;
1360 
1361 	/* Dump the buffer contents */
1362 	while (elems_to_print != 0) {
1363 		if (MDB_RD(&tbuf, sizeof (pmcs_tbuf_t), (tbuf_addr + idx))
1364 		    == -1) {
1365 			NOREAD(tbuf, (tbuf_addr + idx));
1366 			return (DCMD_ERR);
1367 		}
1368 
1369 		elem_filtered = B_FALSE;
1370 
1371 		if (filter) {
1372 			bufp = tbuf.buf;
1373 			/* Skip the driver name */
1374 			while (*bufp < '0' || *bufp > '9') {
1375 				bufp++;
1376 			}
1377 
1378 			ei_idx = 0;
1379 			elem_inst[ei_idx++] = '0';
1380 			elem_inst[ei_idx++] = 't';
1381 			while (*bufp != ':' && ei_idx < (MAX_INST_STRLEN - 1)) {
1382 				elem_inst[ei_idx++] = *bufp;
1383 				bufp++;
1384 			}
1385 			elem_inst[ei_idx] = 0;
1386 
1387 			/* Get the instance */
1388 			if ((int)mdb_strtoull(elem_inst) != instance) {
1389 				elem_filtered = B_TRUE;
1390 			}
1391 		}
1392 
1393 		if (!elem_filtered) {
1394 			mdb_printf("%Y.%09ld %s\n", tbuf.timestamp, tbuf.buf);
1395 		}
1396 
1397 		--elems_to_print;
1398 		if (++idx == tbuf_num_elems) {
1399 			idx = 0;
1400 		}
1401 	}
1402 
1403 	return (DCMD_OK);
1404 }
1405 
1406 /*
1407  * Walkers
1408  */
1409 static int
1410 targets_walk_i(mdb_walk_state_t *wsp)
1411 {
1412 	if (wsp->walk_addr == NULL) {
1413 		mdb_warn("Can not perform global walk\n");
1414 		return (WALK_ERR);
1415 	}
1416 
1417 	/*
1418 	 * Address provided belongs to HBA softstate.  Get the targets pointer
1419 	 * to begin the walk.
1420 	 */
1421 	if (mdb_vread(&ss, sizeof (pmcs_hw_t), wsp->walk_addr) !=
1422 	    sizeof (pmcs_hw_t)) {
1423 		mdb_warn("Unable to read HBA softstate\n");
1424 		return (WALK_ERR);
1425 	}
1426 
1427 	if (targets == NULL) {
1428 		targets = mdb_alloc(sizeof (targets) * ss.max_dev, UM_SLEEP);
1429 	}
1430 
1431 	if (MDB_RD(targets, sizeof (targets) * ss.max_dev, ss.targets) == -1) {
1432 		NOREAD(targets, ss.targets);
1433 		return (WALK_ERR);
1434 	}
1435 
1436 	target_idx = 0;
1437 	wsp->walk_addr = (uintptr_t)(targets[0]);
1438 	wsp->walk_data = mdb_alloc(sizeof (pmcs_xscsi_t), UM_SLEEP);
1439 
1440 	return (WALK_NEXT);
1441 }
1442 
1443 static int
1444 targets_walk_s(mdb_walk_state_t *wsp)
1445 {
1446 	int status;
1447 
1448 	if (target_idx == ss.max_dev) {
1449 		return (WALK_DONE);
1450 	}
1451 
1452 	if (mdb_vread(wsp->walk_data, sizeof (pmcs_xscsi_t),
1453 	    wsp->walk_addr) == -1) {
1454 		mdb_warn("Failed to read target at %p", (void *)wsp->walk_addr);
1455 		return (WALK_DONE);
1456 	}
1457 
1458 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
1459 	    wsp->walk_cbdata);
1460 
1461 	do {
1462 		wsp->walk_addr = (uintptr_t)(targets[++target_idx]);
1463 	} while ((wsp->walk_addr == NULL) && (target_idx < ss.max_dev));
1464 
1465 	if (target_idx == ss.max_dev) {
1466 		return (WALK_DONE);
1467 	}
1468 
1469 	return (status);
1470 }
1471 
1472 static void
1473 targets_walk_f(mdb_walk_state_t *wsp)
1474 {
1475 	mdb_free(wsp->walk_data, sizeof (pmcs_xscsi_t));
1476 }
1477 
1478 
1479 static pmcs_phy_t *
1480 pmcs_next_sibling(pmcs_phy_t *phyp)
1481 {
1482 	pmcs_phy_t parent;
1483 
1484 	/*
1485 	 * First, if this is a root PHY, there are no more siblings
1486 	 */
1487 	if (phyp->level == 0) {
1488 		return (NULL);
1489 	}
1490 
1491 	/*
1492 	 * Otherwise, next sibling is the parent's sibling
1493 	 */
1494 	while (phyp->level > 0) {
1495 		if (mdb_vread(&parent, sizeof (pmcs_phy_t),
1496 		    (uintptr_t)phyp->parent) == -1) {
1497 			mdb_warn("pmcs_next_sibling: Failed to read PHY at %p",
1498 			    (void *)phyp->parent);
1499 			return (NULL);
1500 		}
1501 
1502 		if (parent.sibling != NULL) {
1503 			break;
1504 		}
1505 
1506 		phyp = phyp->parent;
1507 	}
1508 
1509 	return (parent.sibling);
1510 }
1511 
1512 static int
1513 phy_walk_i(mdb_walk_state_t *wsp)
1514 {
1515 	if (wsp->walk_addr == NULL) {
1516 		mdb_warn("Can not perform global walk\n");
1517 		return (WALK_ERR);
1518 	}
1519 
1520 	/*
1521 	 * Address provided belongs to HBA softstate.  Get the targets pointer
1522 	 * to begin the walk.
1523 	 */
1524 	if (mdb_vread(&ss, sizeof (pmcs_hw_t), wsp->walk_addr) !=
1525 	    sizeof (pmcs_hw_t)) {
1526 		mdb_warn("Unable to read HBA softstate\n");
1527 		return (WALK_ERR);
1528 	}
1529 
1530 	wsp->walk_addr = (uintptr_t)(ss.root_phys);
1531 	wsp->walk_data = mdb_alloc(sizeof (pmcs_phy_t), UM_SLEEP);
1532 
1533 	return (WALK_NEXT);
1534 }
1535 
1536 static int
1537 phy_walk_s(mdb_walk_state_t *wsp)
1538 {
1539 	pmcs_phy_t *phyp, *nphyp;
1540 	int status;
1541 
1542 	if (mdb_vread(wsp->walk_data, sizeof (pmcs_phy_t),
1543 	    wsp->walk_addr) == -1) {
1544 		mdb_warn("phy_walk_s: Failed to read PHY at %p",
1545 		    (void *)wsp->walk_addr);
1546 		return (WALK_DONE);
1547 	}
1548 
1549 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
1550 	    wsp->walk_cbdata);
1551 
1552 	phyp = (pmcs_phy_t *)wsp->walk_data;
1553 	if (phyp->children) {
1554 		wsp->walk_addr = (uintptr_t)(phyp->children);
1555 	} else {
1556 		wsp->walk_addr = (uintptr_t)(phyp->sibling);
1557 	}
1558 
1559 	if (wsp->walk_addr == NULL) {
1560 		/*
1561 		 * We reached the end of this sibling list.  Trudge back up
1562 		 * to the parent and find the next sibling after the expander
1563 		 * we just finished traversing, if there is one.
1564 		 */
1565 		nphyp = pmcs_next_sibling(phyp);
1566 
1567 		if (nphyp == NULL) {
1568 			return (WALK_DONE);
1569 		}
1570 
1571 		wsp->walk_addr = (uintptr_t)nphyp;
1572 	}
1573 
1574 	return (status);
1575 }
1576 
1577 static void
1578 phy_walk_f(mdb_walk_state_t *wsp)
1579 {
1580 	mdb_free(wsp->walk_data, sizeof (pmcs_phy_t));
1581 }
1582 
1583 static int
1584 pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1585 {
1586 	struct	pmcs_hw		ss;
1587 	uint_t			verbose = FALSE;
1588 	uint_t			phy_info = FALSE;
1589 	uint_t			hw_info = FALSE;
1590 	uint_t			target_info = FALSE;
1591 	uint_t			work_info = FALSE;
1592 	uint_t			ic_info = FALSE;
1593 	uint_t			iport_info = FALSE;
1594 	uint_t			waitqs_info = FALSE;
1595 	uint_t			tracelog = FALSE;
1596 	uint_t			ibq = FALSE;
1597 	uint_t			obq = FALSE;
1598 	uint_t			tgt_phy_count = FALSE;
1599 	int			rv = DCMD_OK;
1600 	void			*pmcs_state;
1601 	char			*state_str;
1602 	struct dev_info		dip;
1603 
1604 	if (!(flags & DCMD_ADDRSPEC)) {
1605 		pmcs_state = NULL;
1606 		if (mdb_readvar(&pmcs_state, "pmcs_softc_state") == -1) {
1607 			mdb_warn("can't read pmcs_softc_state");
1608 			return (DCMD_ERR);
1609 		}
1610 		if (mdb_pwalk_dcmd("genunix`softstate", "pmcs`pmcs", argc, argv,
1611 		    (uintptr_t)pmcs_state) == -1) {
1612 			mdb_warn("mdb_pwalk_dcmd failed");
1613 			return (DCMD_ERR);
1614 		}
1615 		return (DCMD_OK);
1616 	}
1617 
1618 	if (mdb_getopts(argc, argv,
1619 	    'h', MDB_OPT_SETBITS, TRUE, &hw_info,
1620 	    'i', MDB_OPT_SETBITS, TRUE, &ic_info,
1621 	    'I', MDB_OPT_SETBITS, TRUE, &iport_info,
1622 	    'l', MDB_OPT_SETBITS, TRUE, &tracelog,
1623 	    'p', MDB_OPT_SETBITS, TRUE, &phy_info,
1624 	    'q', MDB_OPT_SETBITS, TRUE, &ibq,
1625 	    'Q', MDB_OPT_SETBITS, TRUE, &obq,
1626 	    't', MDB_OPT_SETBITS, TRUE, &target_info,
1627 	    'T', MDB_OPT_SETBITS, TRUE, &tgt_phy_count,
1628 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
1629 	    'w', MDB_OPT_SETBITS, TRUE, &work_info,
1630 	    'W', MDB_OPT_SETBITS, TRUE, &waitqs_info,
1631 	    NULL) != argc)
1632 		return (DCMD_USAGE);
1633 
1634 	if (MDB_RD(&ss, sizeof (ss), addr) == -1) {
1635 		NOREAD(pmcs_hw_t, addr);
1636 		return (DCMD_ERR);
1637 	}
1638 
1639 	if (MDB_RD(&dip, sizeof (struct dev_info), ss.dip) == -1) {
1640 		NOREAD(pmcs_hw_t, addr);
1641 		return (DCMD_ERR);
1642 	}
1643 
1644 	/*
1645 	 * Dumping the trace log is special.  It's global, not per-HBA.
1646 	 * Thus, a provided address is ignored.  In addition, other options
1647 	 * cannot be specified at the same time.
1648 	 */
1649 
1650 	if (tracelog) {
1651 		if (hw_info || ic_info || iport_info || phy_info || work_info ||
1652 		    target_info || waitqs_info || ibq || obq || tgt_phy_count) {
1653 			return (DCMD_USAGE);
1654 		}
1655 
1656 		if ((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) {
1657 			return (pmcs_dump_tracelog(B_TRUE, dip.devi_instance));
1658 		} else if (flags & DCMD_LOOPFIRST) {
1659 			return (pmcs_dump_tracelog(B_FALSE, 0));
1660 		} else {
1661 			return (DCMD_OK);
1662 		}
1663 	}
1664 
1665 	/* processing completed */
1666 
1667 	if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) ||
1668 	    (flags & DCMD_LOOPFIRST) || phy_info || target_info || hw_info ||
1669 	    work_info || waitqs_info || ibq || obq || tgt_phy_count) {
1670 		if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST))
1671 			mdb_printf("\n");
1672 		mdb_printf("%16s %9s %4s B C  WorkFlags wserno DbgMsk %16s\n",
1673 		    "Address", "State", "Inst", "DIP");
1674 		mdb_printf("================================="
1675 		    "============================================\n");
1676 	}
1677 
1678 	switch (ss.state) {
1679 	case STATE_NIL:
1680 		state_str = "Invalid";
1681 		break;
1682 	case STATE_PROBING:
1683 		state_str = "Probing";
1684 		break;
1685 	case STATE_RUNNING:
1686 		state_str = "Running";
1687 		break;
1688 	case STATE_UNPROBING:
1689 		state_str = "Unprobing";
1690 		break;
1691 	case STATE_DEAD:
1692 		state_str = "Dead";
1693 		break;
1694 	}
1695 
1696 	mdb_printf("%16p %9s %4d %1d %1d 0x%08x 0x%04x 0x%04x %16p\n", addr,
1697 	    state_str, dip.devi_instance, ss.blocked, ss.configuring,
1698 	    ss.work_flags, ss.wserno, ss.debug_mask, ss.dip);
1699 	mdb_printf("\n");
1700 
1701 	mdb_inc_indent(4);
1702 
1703 	if (waitqs_info)
1704 		display_waitqs(ss, verbose);
1705 
1706 	if (hw_info)
1707 		display_hwinfo(ss, verbose);
1708 
1709 	if (phy_info || tgt_phy_count)
1710 		display_phys(ss, verbose, NULL, 0, tgt_phy_count);
1711 
1712 	if (target_info || tgt_phy_count)
1713 		display_targets(ss, verbose, tgt_phy_count);
1714 
1715 	if (work_info)
1716 		display_work(ss, verbose);
1717 
1718 	if (ic_info)
1719 		display_ic(ss, verbose);
1720 
1721 	if (ibq)
1722 		display_inbound_queues(ss, verbose);
1723 
1724 	if (obq)
1725 		display_outbound_queues(ss, verbose);
1726 
1727 	if (iport_info)
1728 		display_iport(ss, addr, verbose);
1729 
1730 	mdb_dec_indent(4);
1731 
1732 	return (rv);
1733 }
1734 
1735 void
1736 pmcs_help()
1737 {
1738 	mdb_printf("Prints summary information about each pmcs instance.\n"
1739 	    "    -h: Print more detailed hardware information\n"
1740 	    "    -i: Print interrupt coalescing information\n"
1741 	    "    -I: Print information about each iport\n"
1742 	    "    -l: Dump the trace log (cannot be used with other options)\n"
1743 	    "    -p: Print information about each attached PHY\n"
1744 	    "    -q: Dump inbound queues\n"
1745 	    "    -Q: Dump outbound queues\n"
1746 	    "    -t: Print information about each known target\n"
1747 	    "    -T: Print target and PHY count summary\n"
1748 	    "    -w: Dump work structures\n"
1749 	    "    -W: List pmcs cmds waiting on various queues\n"
1750 	    "    -v: Add verbosity to the above options\n");
1751 }
1752 
1753 static const mdb_dcmd_t dcmds[] = {
1754 	{ "pmcs", "?[-hiIpQqtTwWv] | -l", "print pmcs information",
1755 	    pmcs_dcmd, pmcs_help
1756 	},
1757 	{ NULL }
1758 };
1759 
1760 static const mdb_walker_t walkers[] = {
1761 	{ "pmcs_targets", "walk target structures",
1762 		targets_walk_i, targets_walk_s, targets_walk_f },
1763 	{ "pmcs_phys", "walk PHY structures",
1764 		phy_walk_i, phy_walk_s, phy_walk_f },
1765 	{ NULL }
1766 };
1767 
1768 static const mdb_modinfo_t modinfo = {
1769 	MDB_API_VERSION, dcmds, walkers
1770 };
1771 
1772 const mdb_modinfo_t *
1773 _mdb_init(void)
1774 {
1775 	return (&modinfo);
1776 }
1777