xref: /illumos-gate/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c (revision aedf2b3bb56b025fcaf87b49ec6c8aeea07f16d7)
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/byteorder.h>
30 #include <sys/scsi/scsi.h>
31 #include <sys/scsi/adapters/pmcs/pmcs.h>
32 
33 #define	MDB_RD(a, b, c)	mdb_vread(a, b, (uintptr_t)c)
34 #define	NOREAD(a, b)	mdb_warn("could not read " #a " at 0x%p", b)
35 
36 static pmcs_hw_t ss;
37 static pmcs_xscsi_t **targets = NULL;
38 static int target_idx;
39 
40 static uint32_t	sas_phys, sata_phys, exp_phys, num_expanders, empty_phys;
41 
42 static pmcs_phy_t *pmcs_next_sibling(pmcs_phy_t *phyp);
43 static void display_one_work(pmcwork_t *wp, int verbose, int idx);
44 
45 static void
46 print_sas_address(pmcs_phy_t *phy)
47 {
48 	int idx;
49 
50 	for (idx = 0; idx < 8; idx++) {
51 		mdb_printf("%02x", phy->sas_address[idx]);
52 	}
53 }
54 
55 /*ARGSUSED*/
56 static void
57 display_ic(struct pmcs_hw m, int verbose)
58 {
59 	int msec_per_tick;
60 
61 	if (mdb_readvar(&msec_per_tick, "msec_per_tick") == -1) {
62 		mdb_warn("can't read msec_per_tick");
63 		msec_per_tick = 0;
64 	}
65 
66 	mdb_printf("\n");
67 	mdb_printf("Interrupt coalescing timer info\n");
68 	mdb_printf("-------------------------------\n");
69 	if (msec_per_tick == 0) {
70 		mdb_printf("Quantum                       : ?? ms\n");
71 	} else {
72 		mdb_printf("Quantum                       : %d ms\n",
73 		    m.io_intr_coal.quantum * msec_per_tick);
74 	}
75 	mdb_printf("Timer enabled                 : ");
76 	if (m.io_intr_coal.timer_on) {
77 		mdb_printf("Yes\n");
78 		mdb_printf("Coalescing timer value        : %d us\n",
79 		    m.io_intr_coal.intr_coal_timer);
80 	} else {
81 		mdb_printf("No\n");
82 	}
83 	mdb_printf("Total nsecs between interrupts: %ld\n",
84 	    m.io_intr_coal.nsecs_between_intrs);
85 	mdb_printf("Time of last I/O interrupt    : %ld\n",
86 	    m.io_intr_coal.last_io_comp);
87 	mdb_printf("Number of I/O interrupts      : %d\n",
88 	    m.io_intr_coal.num_intrs);
89 	mdb_printf("Number of I/O completions     : %d\n",
90 	    m.io_intr_coal.num_io_completions);
91 	mdb_printf("Max I/O completion interrupts : %d\n",
92 	    m.io_intr_coal.max_io_completions);
93 	mdb_printf("Measured ECHO int latency     : %d ns\n",
94 	    m.io_intr_coal.intr_latency);
95 	mdb_printf("Interrupt threshold           : %d\n",
96 	    m.io_intr_coal.intr_threshold);
97 }
98 
99 /*ARGSUSED*/
100 static int
101 pmcs_iport_phy_walk_cb(uintptr_t addr, const void *wdata, void *priv)
102 {
103 	struct pmcs_phy		phy;
104 
105 	if (mdb_vread(&phy, sizeof (struct pmcs_phy), addr) !=
106 	    sizeof (struct pmcs_phy)) {
107 		return (DCMD_ERR);
108 	}
109 
110 	mdb_printf("%16p %2d\n", addr, phy.phynum);
111 
112 	return (0);
113 }
114 
115 /*ARGSUSED*/
116 static int
117 pmcs_iport_walk_cb(uintptr_t addr, const void *wdata, void *priv)
118 {
119 	struct pmcs_iport	iport;
120 	uintptr_t		list_addr;
121 	char			*ua_state;
122 	char			portid[4];
123 	char			unit_address[34];
124 
125 	if (mdb_vread(&iport, sizeof (struct pmcs_iport), addr) !=
126 	    sizeof (struct pmcs_iport)) {
127 		return (DCMD_ERR);
128 	}
129 
130 	if (mdb_readstr(unit_address, sizeof (unit_address),
131 	    (uintptr_t)(iport.ua)) == -1) {
132 		strncpy(unit_address, "Unset", sizeof (unit_address));
133 	}
134 
135 	if (iport.portid == 0xffff) {
136 		mdb_snprintf(portid, sizeof (portid), "%s", "-");
137 	} else {
138 		mdb_snprintf(portid, sizeof (portid), "%d", iport.portid);
139 	}
140 
141 	switch (iport.ua_state) {
142 	case UA_INACTIVE:
143 		ua_state = "Inactive";
144 		break;
145 	case UA_PEND_ACTIVATE:
146 		ua_state = "PendActivate";
147 		break;
148 	case UA_ACTIVE:
149 		ua_state = "Active";
150 		break;
151 	case UA_PEND_DEACTIVATE:
152 		ua_state = "PendDeactivate";
153 		break;
154 	default:
155 		ua_state = "Unknown";
156 	}
157 
158 	if (strlen(unit_address) < 3) {
159 		/* Standard iport unit address */
160 		mdb_printf("UA %-16s %16s %8s %8s %16s", "Iport", "UA State",
161 		    "PortID", "NumPhys", "DIP\n");
162 		mdb_printf("%2s %16p %16s %8s %8d %16p\n", unit_address, addr,
163 		    ua_state, portid, iport.nphy, iport.dip);
164 	} else {
165 		/* Temporary iport unit address */
166 		mdb_printf("%-32s %16s %20s %8s %8s %16s", "UA", "Iport",
167 		    "UA State", "PortID", "NumPhys", "DIP\n");
168 		mdb_printf("%32s %16p %20s %8s %8d %16p\n", unit_address, addr,
169 		    ua_state, portid, iport.nphy, iport.dip);
170 	}
171 
172 	if (iport.nphy > 0) {
173 		mdb_inc_indent(4);
174 		mdb_printf("%-18s %8s", "Phy", "PhyNum\n");
175 		mdb_inc_indent(2);
176 		list_addr =
177 		    (uintptr_t)(addr + offsetof(struct pmcs_iport, phys));
178 		if (mdb_pwalk("list", pmcs_iport_phy_walk_cb, NULL,
179 		    list_addr) == -1) {
180 			mdb_warn("pmcs iport walk failed");
181 		}
182 		mdb_dec_indent(6);
183 		mdb_printf("\n");
184 	}
185 
186 	return (0);
187 }
188 
189 /*ARGSUSED*/
190 static void
191 display_iport(struct pmcs_hw m, uintptr_t addr, int verbose)
192 {
193 	uintptr_t	list_addr;
194 
195 	if (m.iports_attached) {
196 		mdb_printf("Iport information:\n");
197 		mdb_printf("-----------------\n");
198 	} else {
199 		mdb_printf("No Iports found.\n\n");
200 		return;
201 	}
202 
203 	list_addr = (uintptr_t)(addr + offsetof(struct pmcs_hw, iports));
204 
205 	if (mdb_pwalk("list", pmcs_iport_walk_cb, NULL, list_addr) == -1) {
206 		mdb_warn("pmcs iport walk failed");
207 	}
208 
209 	mdb_printf("\n");
210 }
211 
212 /* ARGSUSED */
213 static int
214 pmcs_utarget_walk_cb(uintptr_t addr, const void *wdata, void *priv)
215 {
216 	pmcs_phy_t phy;
217 
218 	if (mdb_vread(&phy, sizeof (pmcs_phy_t), (uintptr_t)addr) == -1) {
219 		mdb_warn("pmcs_utarget_walk_cb: Failed to read PHY at %p",
220 		    (void *)addr);
221 		return (DCMD_ERR);
222 	}
223 
224 	if (phy.configured && (phy.target == NULL)) {
225 		mdb_printf("SAS address: ");
226 		print_sas_address(&phy);
227 		mdb_printf("  DType: ");
228 		switch (phy.dtype) {
229 		case SAS:
230 			mdb_printf("%4s", "SAS");
231 			break;
232 		case SATA:
233 			mdb_printf("%4s", "SATA");
234 			break;
235 		case EXPANDER:
236 			mdb_printf("%4s", "SMP");
237 			break;
238 		default:
239 			mdb_printf("%4s", "N/A");
240 			break;
241 		}
242 		mdb_printf("  Path: %s\n", phy.path);
243 	}
244 
245 	return (0);
246 }
247 
248 static void
249 display_unconfigured_targets(uintptr_t addr)
250 {
251 	mdb_printf("Unconfigured target SAS address:\n\n");
252 
253 	if (mdb_pwalk("pmcs_phys", pmcs_utarget_walk_cb, NULL, addr) == -1) {
254 		mdb_warn("pmcs phys walk failed");
255 	}
256 }
257 
258 static void
259 display_completion_queue(struct pmcs_hw ss)
260 {
261 	pmcs_iocomp_cb_t ccb, *ccbp;
262 	pmcwork_t work;
263 
264 	if (ss.iocomp_cb_head == NULL) {
265 		mdb_printf("Completion queue is empty.\n");
266 		return;
267 	}
268 
269 	ccbp = ss.iocomp_cb_head;
270 	mdb_printf("%8s %10s %20s %8s %8s O D\n",
271 	    "HTag", "State", "Phy Path", "Target", "Timer");
272 
273 	while (ccbp) {
274 		if (mdb_vread(&ccb, sizeof (pmcs_iocomp_cb_t),
275 		    (uintptr_t)ccbp) != sizeof (pmcs_iocomp_cb_t)) {
276 			mdb_warn("Unable to read completion queue entry\n");
277 			return;
278 		}
279 
280 		if (mdb_vread(&work, sizeof (pmcwork_t), (uintptr_t)ccb.pwrk)
281 		    != sizeof (pmcwork_t)) {
282 			mdb_warn("Unable to read work structure\n");
283 			return;
284 		}
285 
286 		/*
287 		 * Only print the work structure if it's still active.  If
288 		 * it's not, it's been completed since we started looking at
289 		 * it.
290 		 */
291 		if (work.state != PMCS_WORK_STATE_NIL) {
292 			display_one_work(&work, 0, 0);
293 		}
294 		ccbp = ccb.next;
295 	}
296 }
297 
298 /*ARGSUSED*/
299 static void
300 display_hwinfo(struct pmcs_hw m, int verbose)
301 {
302 	struct pmcs_hw	*mp = &m;
303 	char		*fwsupport;
304 
305 	switch (PMCS_FW_TYPE(mp)) {
306 	case PMCS_FW_TYPE_RELEASED:
307 		fwsupport = "Released";
308 		break;
309 	case PMCS_FW_TYPE_DEVELOPMENT:
310 		fwsupport = "Development";
311 		break;
312 	case PMCS_FW_TYPE_ALPHA:
313 		fwsupport = "Alpha";
314 		break;
315 	case PMCS_FW_TYPE_BETA:
316 		fwsupport = "Beta";
317 		break;
318 	default:
319 		fwsupport = "Special";
320 		break;
321 	}
322 
323 	mdb_printf("\nHardware information:\n");
324 	mdb_printf("---------------------\n");
325 
326 	mdb_printf("Chip revision:    %c\n", 'A' + m.chiprev);
327 	mdb_printf("SAS WWID:         %"PRIx64"\n", m.sas_wwns[0]);
328 	mdb_printf("Firmware version: %x.%x.%x (%s)\n",
329 	    PMCS_FW_MAJOR(mp), PMCS_FW_MINOR(mp), PMCS_FW_MICRO(mp),
330 	    fwsupport);
331 
332 	mdb_printf("Number of PHYs:   %d\n", m.nphy);
333 	mdb_printf("Maximum commands: %d\n", m.max_cmd);
334 	mdb_printf("Maximum devices:  %d\n", m.max_dev);
335 	mdb_printf("I/O queue depth:  %d\n", m.ioq_depth);
336 	if (m.fwlog == 0) {
337 		mdb_printf("Firmware logging: Disabled\n");
338 	} else {
339 		mdb_printf("Firmware logging: Enabled (%d)\n", m.fwlog);
340 	}
341 }
342 
343 static void
344 display_targets(struct pmcs_hw m, int verbose, int totals_only)
345 {
346 	char		*dtype;
347 	pmcs_xscsi_t	xs;
348 	pmcs_phy_t	phy;
349 	uint16_t	max_dev, idx;
350 	uint32_t	sas_targets = 0, smp_targets = 0, sata_targets = 0;
351 
352 	max_dev = m.max_dev;
353 
354 	if (targets == NULL) {
355 		targets = mdb_alloc(sizeof (targets) * max_dev, UM_SLEEP);
356 	}
357 
358 	if (MDB_RD(targets, sizeof (targets) * max_dev, m.targets) == -1) {
359 		NOREAD(targets, m.targets);
360 		return;
361 	}
362 
363 	if (!totals_only) {
364 		mdb_printf("\nTarget information:\n");
365 		mdb_printf("---------------------------------------\n");
366 		mdb_printf("VTGT %-16s %-16s %-5s %8s %s", "SAS Address",
367 		    "PHY Address", "DType", "Active", "DS");
368 		mdb_printf("\n");
369 	}
370 
371 	for (idx = 0; idx < max_dev; idx++) {
372 		if (targets[idx] == NULL) {
373 			continue;
374 		}
375 
376 		if (MDB_RD(&xs, sizeof (xs), targets[idx]) == -1) {
377 			NOREAD(pmcs_xscsi_t, targets[idx]);
378 			continue;
379 		}
380 
381 		/*
382 		 * It has to be new or assigned to be of interest.
383 		 */
384 		if (xs.new == 0 && xs.assigned == 0) {
385 			continue;
386 		}
387 
388 		switch (xs.dtype) {
389 		case NOTHING:
390 			dtype = "None";
391 			break;
392 		case SATA:
393 			dtype = "SATA";
394 			sata_targets++;
395 			break;
396 		case SAS:
397 			dtype = "SAS";
398 			sas_targets++;
399 			break;
400 		case EXPANDER:
401 			dtype = "SMP";
402 			smp_targets++;
403 			break;
404 		}
405 
406 		if (totals_only) {
407 			continue;
408 		}
409 
410 		if (xs.phy) {
411 			if (MDB_RD(&phy, sizeof (phy), xs.phy) == -1) {
412 				NOREAD(pmcs_phy_t, xs.phy);
413 				continue;
414 			}
415 			mdb_printf("%4d ", idx);
416 			print_sas_address(&phy);
417 			mdb_printf(" %16p", xs.phy);
418 		} else {
419 			mdb_printf("%4d %16s", idx, "<no phy avail>");
420 		}
421 		mdb_printf(" %5s", dtype);
422 		mdb_printf(" %8d", xs.actv_cnt);
423 		mdb_printf(" %2d", xs.dev_state);
424 
425 		if (verbose) {
426 			if (xs.new) {
427 				mdb_printf(" new");
428 			}
429 			if (xs.assigned) {
430 				mdb_printf(" assigned");
431 			}
432 			if (xs.draining) {
433 				mdb_printf(" draining");
434 			}
435 			if (xs.reset_wait) {
436 				mdb_printf(" reset_wait");
437 			}
438 			if (xs.resetting) {
439 				mdb_printf(" resetting");
440 			}
441 			if (xs.recover_wait) {
442 				mdb_printf(" recover_wait");
443 			}
444 			if (xs.recovering) {
445 				mdb_printf(" recovering");
446 			}
447 			if (xs.event_recovery) {
448 				mdb_printf(" event recovery");
449 			}
450 			if (xs.special_running) {
451 				mdb_printf(" special_active");
452 			}
453 			if (xs.ncq) {
454 				mdb_printf(" ncq_tagmap=0x%x qdepth=%d",
455 				    xs.tagmap, xs.qdepth);
456 			} else if (xs.pio) {
457 				mdb_printf(" pio");
458 			}
459 		}
460 
461 		mdb_printf("\n");
462 	}
463 
464 	if (!totals_only) {
465 		mdb_printf("\n");
466 	}
467 
468 	mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP)\n",
469 	    "Configured targets:", (sas_targets + sata_targets + smp_targets),
470 	    sas_targets, sata_targets, smp_targets);
471 }
472 
473 static char *
474 work_state_to_string(uint32_t state)
475 {
476 	char *state_string;
477 
478 	switch (state) {
479 	case PMCS_WORK_STATE_NIL:
480 		state_string = "Free";
481 		break;
482 	case PMCS_WORK_STATE_READY:
483 		state_string = "Ready";
484 		break;
485 	case PMCS_WORK_STATE_ONCHIP:
486 		state_string = "On Chip";
487 		break;
488 	case PMCS_WORK_STATE_INTR:
489 		state_string = "In Intr";
490 		break;
491 	case PMCS_WORK_STATE_IOCOMPQ:
492 		state_string = "I/O Comp";
493 		break;
494 	case PMCS_WORK_STATE_ABORTED:
495 		state_string = "I/O Aborted";
496 		break;
497 	case PMCS_WORK_STATE_TIMED_OUT:
498 		state_string = "I/O Timed Out";
499 		break;
500 	default:
501 		state_string = "INVALID";
502 		break;
503 	}
504 
505 	return (state_string);
506 }
507 
508 static void
509 display_one_work(pmcwork_t *wp, int verbose, int idx)
510 {
511 	char		*state, *last_state;
512 	char		*path;
513 	pmcs_xscsi_t	xs;
514 	pmcs_phy_t	phy;
515 	int		tgt;
516 
517 	state = work_state_to_string(wp->state);
518 	last_state = work_state_to_string(wp->last_state);
519 
520 	if (wp->ssp_event && wp->ssp_event != 0xffffffff) {
521 		mdb_printf("SSP event 0x%x", wp->ssp_event);
522 	}
523 
524 	tgt = -1;
525 	if (wp->xp) {
526 		if (MDB_RD(&xs, sizeof (xs), wp->xp) == -1) {
527 			NOREAD(pmcs_xscsi_t, wp->xp);
528 		} else {
529 			tgt = xs.target_num;
530 		}
531 	}
532 	if (wp->phy) {
533 		if (MDB_RD(&phy, sizeof (phy), wp->phy) == -1) {
534 			NOREAD(pmcs_phy_t, wp->phy);
535 		}
536 		path = phy.path;
537 	} else {
538 		path = "N/A";
539 	}
540 
541 	if (verbose) {
542 		mdb_printf("%4d ", idx);
543 	}
544 	if (tgt == -1) {
545 		mdb_printf("%08x %10s %20s      N/A %8u %1d %1d ",
546 		    wp->htag, state, path, wp->timer,
547 		    wp->onwire, wp->dead);
548 	} else {
549 		mdb_printf("%08x %10s %20s %8d %8u %1d %1d ",
550 		    wp->htag, state, path, tgt, wp->timer,
551 		    wp->onwire, wp->dead);
552 	}
553 	if (verbose) {
554 		mdb_printf("%08x %10s 0x%016p 0x%016p\n",
555 		    wp->last_htag, last_state, wp->last_phy, wp->last_xp);
556 	} else {
557 		mdb_printf("\n");
558 	}
559 }
560 
561 static void
562 display_work(struct pmcs_hw m, int verbose)
563 {
564 	int		idx;
565 	boolean_t	header_printed = B_FALSE;
566 	pmcwork_t	work, *wp = &work;
567 	uintptr_t	_wp;
568 
569 	mdb_printf("\nActive Work structure information:\n");
570 	mdb_printf("----------------------------------\n");
571 
572 	_wp = (uintptr_t)m.work;
573 
574 	for (idx = 0; idx < m.max_cmd; idx++, _wp += sizeof (pmcwork_t)) {
575 		if (MDB_RD(&work, sizeof (pmcwork_t), _wp) == -1) {
576 			NOREAD(pmcwork_t, _wp);
577 			continue;
578 		}
579 
580 		if (!verbose && (wp->htag == PMCS_TAG_TYPE_FREE)) {
581 			continue;
582 		}
583 
584 		if (header_printed == B_FALSE) {
585 			if (verbose) {
586 				mdb_printf("%4s ", "Idx");
587 			}
588 			mdb_printf("%8s %10s %20s %8s %8s O D ",
589 			    "HTag", "State", "Phy Path", "Target", "Timer");
590 			if (verbose) {
591 				mdb_printf("%8s %10s %18s %18s\n", "LastHTAG",
592 				    "LastState", "LastPHY", "LastTgt");
593 			} else {
594 				mdb_printf("\n");
595 			}
596 			header_printed = B_TRUE;
597 		}
598 
599 		display_one_work(wp, verbose, idx);
600 	}
601 }
602 
603 static void
604 print_spcmd(pmcs_cmd_t *sp, void *kaddr, int printhdr, int verbose)
605 {
606 	int cdb_size, idx;
607 	struct scsi_pkt pkt;
608 	uchar_t cdb[256];
609 
610 	if (printhdr) {
611 		if (verbose) {
612 			mdb_printf("%16s %16s %16s %8s %s CDB\n", "Command",
613 			    "SCSA pkt", "DMA Chunks", "HTAG", "SATL Tag");
614 		} else {
615 			mdb_printf("%16s %16s %16s %8s %s\n", "Command",
616 			    "SCSA pkt", "DMA Chunks", "HTAG", "SATL Tag");
617 		}
618 	}
619 
620 	mdb_printf("%16p %16p %16p %08x %08x ",
621 	    kaddr, sp->cmd_pkt, sp->cmd_clist, sp->cmd_tag, sp->cmd_satltag);
622 
623 	/*
624 	 * If we're printing verbose, dump the CDB as well.
625 	 */
626 	if (verbose) {
627 		if (sp->cmd_pkt) {
628 			if (mdb_vread(&pkt, sizeof (struct scsi_pkt),
629 			    (uintptr_t)sp->cmd_pkt) !=
630 			    sizeof (struct scsi_pkt)) {
631 				mdb_warn("Unable to read SCSI pkt\n");
632 				return;
633 			}
634 			cdb_size = pkt.pkt_cdblen;
635 			if (mdb_vread(&cdb[0], cdb_size,
636 			    (uintptr_t)pkt.pkt_cdbp) != cdb_size) {
637 				mdb_warn("Unable to read CDB\n");
638 				return;
639 			}
640 
641 			for (idx = 0; idx < cdb_size; idx++) {
642 				mdb_printf("%02x ", cdb[idx]);
643 			}
644 		} else {
645 			mdb_printf("N/A");
646 		}
647 
648 		mdb_printf("\n");
649 	} else {
650 		mdb_printf("\n");
651 	}
652 }
653 
654 /*ARGSUSED1*/
655 static void
656 display_waitqs(struct pmcs_hw m, int verbose)
657 {
658 	pmcs_cmd_t	*sp, s;
659 	pmcs_xscsi_t	xs;
660 	int		first, i;
661 	int		max_dev = m.max_dev;
662 
663 	sp = m.dq.stqh_first;
664 	first = 1;
665 	while (sp) {
666 		if (first) {
667 			mdb_printf("\nDead Command Queue:\n");
668 			mdb_printf("---------------------------\n");
669 		}
670 		if (MDB_RD(&s, sizeof (s), sp) == -1) {
671 			NOREAD(pmcs_cmd_t, sp);
672 			break;
673 		}
674 		print_spcmd(&s, sp, first, verbose);
675 		sp = s.cmd_next.stqe_next;
676 		first = 0;
677 	}
678 
679 	sp = m.cq.stqh_first;
680 	first = 1;
681 	while (sp) {
682 		if (first) {
683 			mdb_printf("\nCompletion Command Queue:\n");
684 			mdb_printf("---------------------------\n");
685 		}
686 		if (MDB_RD(&s, sizeof (s), sp) == -1) {
687 			NOREAD(pmcs_cmd_t, sp);
688 			break;
689 		}
690 		print_spcmd(&s, sp, first, verbose);
691 		sp = s.cmd_next.stqe_next;
692 		first = 0;
693 	}
694 
695 
696 	if (targets == NULL) {
697 		targets = mdb_alloc(sizeof (targets) * max_dev, UM_SLEEP);
698 	}
699 
700 	if (MDB_RD(targets, sizeof (targets) * max_dev, m.targets) == -1) {
701 		NOREAD(targets, m.targets);
702 		return;
703 	}
704 
705 	for (i = 0; i < max_dev; i++) {
706 		if (targets[i] == NULL) {
707 			continue;
708 		}
709 		if (MDB_RD(&xs, sizeof (xs), targets[i]) == -1) {
710 			NOREAD(pmcs_xscsi_t, targets[i]);
711 			continue;
712 		}
713 		sp = xs.wq.stqh_first;
714 		first = 1;
715 		while (sp) {
716 			if (first) {
717 				mdb_printf("\nTarget %u Wait Queue:\n",
718 				    xs.target_num);
719 				mdb_printf("---------------------------\n");
720 			}
721 			if (MDB_RD(&s, sizeof (s), sp) == -1) {
722 				NOREAD(pmcs_cmd_t, sp);
723 				break;
724 			}
725 			print_spcmd(&s, sp, first, verbose);
726 			sp = s.cmd_next.stqe_next;
727 			first = 0;
728 		}
729 		sp = xs.aq.stqh_first;
730 		first = 1;
731 		while (sp) {
732 			if (first) {
733 				mdb_printf("\nTarget %u Active Queue:\n",
734 				    xs.target_num);
735 				mdb_printf("---------------------------\n");
736 			}
737 			if (MDB_RD(&s, sizeof (s), sp) == -1) {
738 				NOREAD(pmcs_cmd_t, sp);
739 				break;
740 			}
741 			print_spcmd(&s, sp, first, verbose);
742 			sp = s.cmd_next.stqe_next;
743 			first = 0;
744 		}
745 		sp = xs.sq.stqh_first;
746 		first = 1;
747 		while (sp) {
748 			if (first) {
749 				mdb_printf("\nTarget %u Special Queue:\n",
750 				    xs.target_num);
751 				mdb_printf("---------------------------\n");
752 			}
753 			if (MDB_RD(&s, sizeof (s), sp) == -1) {
754 				NOREAD(pmcs_cmd_t, sp);
755 				break;
756 			}
757 			print_spcmd(&s, sp, first, verbose);
758 			sp = s.cmd_next.stqe_next;
759 			first = 0;
760 		}
761 	}
762 }
763 
764 static char *
765 ibq_type(int qnum)
766 {
767 	if (qnum < 0 || qnum >= PMCS_NIQ) {
768 		return ("UNKNOWN");
769 	}
770 
771 	if (qnum < PMCS_IQ_OTHER) {
772 		return ("I/O");
773 	}
774 
775 	return ("Other");
776 }
777 
778 static char *
779 obq_type(int qnum)
780 {
781 	switch (qnum) {
782 	case PMCS_OQ_IODONE:
783 		return ("I/O");
784 		break;
785 	case PMCS_OQ_GENERAL:
786 		return ("General");
787 		break;
788 	case PMCS_OQ_EVENTS:
789 		return ("Events");
790 		break;
791 	default:
792 		return ("UNKNOWN");
793 	}
794 }
795 
796 static char *
797 iomb_cat(uint32_t cat)
798 {
799 	switch (cat) {
800 	case PMCS_IOMB_CAT_NET:
801 		return ("NET");
802 		break;
803 	case PMCS_IOMB_CAT_FC:
804 		return ("FC");
805 		break;
806 	case PMCS_IOMB_CAT_SAS:
807 		return ("SAS");
808 		break;
809 	case PMCS_IOMB_CAT_SCSI:
810 		return ("SCSI");
811 		break;
812 	default:
813 		return ("???");
814 	}
815 }
816 
817 static char *
818 inbound_iomb_opcode(uint32_t opcode)
819 {
820 	switch (opcode) {
821 	case PMCIN_ECHO:
822 		return ("ECHO");
823 		break;
824 	case PMCIN_GET_INFO:
825 		return ("GET_INFO");
826 		break;
827 	case PMCIN_GET_VPD:
828 		return ("GET_VPD");
829 		break;
830 	case PMCIN_PHY_START:
831 		return ("PHY_START");
832 		break;
833 	case PMCIN_PHY_STOP:
834 		return ("PHY_STOP");
835 		break;
836 	case PMCIN_SSP_INI_IO_START:
837 		return ("INI_IO_START");
838 		break;
839 	case PMCIN_SSP_INI_TM_START:
840 		return ("INI_TM_START");
841 		break;
842 	case PMCIN_SSP_INI_EXT_IO_START:
843 		return ("INI_EXT_IO_START");
844 		break;
845 	case PMCIN_DEVICE_HANDLE_ACCEPT:
846 		return ("DEVICE_HANDLE_ACCEPT");
847 		break;
848 	case PMCIN_SSP_TGT_IO_START:
849 		return ("TGT_IO_START");
850 		break;
851 	case PMCIN_SSP_TGT_RESPONSE_START:
852 		return ("TGT_RESPONSE_START");
853 		break;
854 	case PMCIN_SSP_INI_EDC_EXT_IO_START:
855 		return ("INI_EDC_EXT_IO_START");
856 		break;
857 	case PMCIN_SSP_INI_EDC_EXT_IO_START1:
858 		return ("INI_EDC_EXT_IO_START1");
859 		break;
860 	case PMCIN_SSP_TGT_EDC_IO_START:
861 		return ("TGT_EDC_IO_START");
862 		break;
863 	case PMCIN_SSP_ABORT:
864 		return ("SSP_ABORT");
865 		break;
866 	case PMCIN_DEREGISTER_DEVICE_HANDLE:
867 		return ("DEREGISTER_DEVICE_HANDLE");
868 		break;
869 	case PMCIN_GET_DEVICE_HANDLE:
870 		return ("GET_DEVICE_HANDLE");
871 		break;
872 	case PMCIN_SMP_REQUEST:
873 		return ("SMP_REQUEST");
874 		break;
875 	case PMCIN_SMP_RESPONSE:
876 		return ("SMP_RESPONSE");
877 		break;
878 	case PMCIN_SMP_ABORT:
879 		return ("SMP_ABORT");
880 		break;
881 	case PMCIN_ASSISTED_DISCOVERY:
882 		return ("ASSISTED_DISCOVERY");
883 		break;
884 	case PMCIN_REGISTER_DEVICE:
885 		return ("REGISTER_DEVICE");
886 		break;
887 	case PMCIN_SATA_HOST_IO_START:
888 		return ("SATA_HOST_IO_START");
889 		break;
890 	case PMCIN_SATA_ABORT:
891 		return ("SATA_ABORT");
892 		break;
893 	case PMCIN_LOCAL_PHY_CONTROL:
894 		return ("LOCAL_PHY_CONTROL");
895 		break;
896 	case PMCIN_GET_DEVICE_INFO:
897 		return ("GET_DEVICE_INFO");
898 		break;
899 	case PMCIN_TWI:
900 		return ("TWI");
901 		break;
902 	case PMCIN_FW_FLASH_UPDATE:
903 		return ("FW_FLASH_UPDATE");
904 		break;
905 	case PMCIN_SET_VPD:
906 		return ("SET_VPD");
907 		break;
908 	case PMCIN_GPIO:
909 		return ("GPIO");
910 		break;
911 	case PMCIN_SAS_DIAG_MODE_START_END:
912 		return ("SAS_DIAG_MODE_START_END");
913 		break;
914 	case PMCIN_SAS_DIAG_EXECUTE:
915 		return ("SAS_DIAG_EXECUTE");
916 		break;
917 	case PMCIN_SAW_HW_EVENT_ACK:
918 		return ("SAS_HW_EVENT_ACK");
919 		break;
920 	case PMCIN_GET_TIME_STAMP:
921 		return ("GET_TIME_STAMP");
922 		break;
923 	case PMCIN_PORT_CONTROL:
924 		return ("PORT_CONTROL");
925 		break;
926 	case PMCIN_GET_NVMD_DATA:
927 		return ("GET_NVMD_DATA");
928 		break;
929 	case PMCIN_SET_NVMD_DATA:
930 		return ("SET_NVMD_DATA");
931 		break;
932 	case PMCIN_SET_DEVICE_STATE:
933 		return ("SET_DEVICE_STATE");
934 		break;
935 	case PMCIN_GET_DEVICE_STATE:
936 		return ("GET_DEVICE_STATE");
937 		break;
938 	default:
939 		return ("UNKNOWN");
940 		break;
941 	}
942 }
943 
944 static char *
945 outbound_iomb_opcode(uint32_t opcode)
946 {
947 	switch (opcode) {
948 	case PMCOUT_ECHO:
949 		return ("ECHO");
950 		break;
951 	case PMCOUT_GET_INFO:
952 		return ("GET_INFO");
953 		break;
954 	case PMCOUT_GET_VPD:
955 		return ("GET_VPD");
956 		break;
957 	case PMCOUT_SAS_HW_EVENT:
958 		return ("SAS_HW_EVENT");
959 		break;
960 	case PMCOUT_SSP_COMPLETION:
961 		return ("SSP_COMPLETION");
962 		break;
963 	case PMCOUT_SMP_COMPLETION:
964 		return ("SMP_COMPLETION");
965 		break;
966 	case PMCOUT_LOCAL_PHY_CONTROL:
967 		return ("LOCAL_PHY_CONTROL");
968 		break;
969 	case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT:
970 		return ("SAS_ASSISTED_DISCOVERY_SENT");
971 		break;
972 	case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT:
973 		return ("SATA_ASSISTED_DISCOVERY_SENT");
974 		break;
975 	case PMCOUT_DEVICE_REGISTRATION:
976 		return ("DEVICE_REGISTRATION");
977 		break;
978 	case PMCOUT_DEREGISTER_DEVICE_HANDLE:
979 		return ("DEREGISTER_DEVICE_HANDLE");
980 		break;
981 	case PMCOUT_GET_DEVICE_HANDLE:
982 		return ("GET_DEVICE_HANDLE");
983 		break;
984 	case PMCOUT_SATA_COMPLETION:
985 		return ("SATA_COMPLETION");
986 		break;
987 	case PMCOUT_SATA_EVENT:
988 		return ("SATA_EVENT");
989 		break;
990 	case PMCOUT_SSP_EVENT:
991 		return ("SSP_EVENT");
992 		break;
993 	case PMCOUT_DEVICE_HANDLE_ARRIVED:
994 		return ("DEVICE_HANDLE_ARRIVED");
995 		break;
996 	case PMCOUT_SMP_REQUEST_RECEIVED:
997 		return ("SMP_REQUEST_RECEIVED");
998 		break;
999 	case PMCOUT_SSP_REQUEST_RECEIVED:
1000 		return ("SSP_REQUEST_RECEIVED");
1001 		break;
1002 	case PMCOUT_DEVICE_INFO:
1003 		return ("DEVICE_INFO");
1004 		break;
1005 	case PMCOUT_FW_FLASH_UPDATE:
1006 		return ("FW_FLASH_UPDATE");
1007 		break;
1008 	case PMCOUT_SET_VPD:
1009 		return ("SET_VPD");
1010 		break;
1011 	case PMCOUT_GPIO:
1012 		return ("GPIO");
1013 		break;
1014 	case PMCOUT_GPIO_EVENT:
1015 		return ("GPIO_EVENT");
1016 		break;
1017 	case PMCOUT_GENERAL_EVENT:
1018 		return ("GENERAL_EVENT");
1019 		break;
1020 	case PMCOUT_TWI:
1021 		return ("TWI");
1022 		break;
1023 	case PMCOUT_SSP_ABORT:
1024 		return ("SSP_ABORT");
1025 		break;
1026 	case PMCOUT_SATA_ABORT:
1027 		return ("SATA_ABORT");
1028 		break;
1029 	case PMCOUT_SAS_DIAG_MODE_START_END:
1030 		return ("SAS_DIAG_MODE_START_END");
1031 		break;
1032 	case PMCOUT_SAS_DIAG_EXECUTE:
1033 		return ("SAS_DIAG_EXECUTE");
1034 		break;
1035 	case PMCOUT_GET_TIME_STAMP:
1036 		return ("GET_TIME_STAMP");
1037 		break;
1038 	case PMCOUT_SAS_HW_EVENT_ACK_ACK:
1039 		return ("SAS_HW_EVENT_ACK_ACK");
1040 		break;
1041 	case PMCOUT_PORT_CONTROL:
1042 		return ("PORT_CONTROL");
1043 		break;
1044 	case PMCOUT_SKIP_ENTRIES:
1045 		return ("SKIP_ENTRIES");
1046 		break;
1047 	case PMCOUT_SMP_ABORT:
1048 		return ("SMP_ABORT");
1049 		break;
1050 	case PMCOUT_GET_NVMD_DATA:
1051 		return ("GET_NVMD_DATA");
1052 		break;
1053 	case PMCOUT_SET_NVMD_DATA:
1054 		return ("SET_NVMD_DATA");
1055 		break;
1056 	case PMCOUT_DEVICE_HANDLE_REMOVED:
1057 		return ("DEVICE_HANDLE_REMOVED");
1058 		break;
1059 	case PMCOUT_SET_DEVICE_STATE:
1060 		return ("SET_DEVICE_STATE");
1061 		break;
1062 	case PMCOUT_GET_DEVICE_STATE:
1063 		return ("GET_DEVICE_STATE");
1064 		break;
1065 	case PMCOUT_SET_DEVICE_INFO:
1066 		return ("SET_DEVICE_INFO");
1067 		break;
1068 	default:
1069 		return ("UNKNOWN");
1070 		break;
1071 	}
1072 }
1073 
1074 static void
1075 dump_one_qentry_outbound(uint32_t *qentryp, int idx)
1076 {
1077 	int qeidx;
1078 	uint32_t word0 = LE_32(*qentryp);
1079 
1080 	mdb_printf("Entry #%02d\n", idx);
1081 	mdb_inc_indent(2);
1082 
1083 	mdb_printf("Header: 0x%08x (", word0);
1084 	if (word0 & PMCS_IOMB_VALID) {
1085 		mdb_printf("VALID, ");
1086 	}
1087 	if (word0 & PMCS_IOMB_HIPRI) {
1088 		mdb_printf("HIPRI, ");
1089 	}
1090 	mdb_printf("OBID=%d, ",
1091 	    (word0 & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT);
1092 	mdb_printf("CAT=%s, ",
1093 	    iomb_cat((word0 & PMCS_IOMB_CAT_MASK) >> PMCS_IOMB_CAT_SHIFT));
1094 	mdb_printf("OPCODE=%s",
1095 	    outbound_iomb_opcode(word0 & PMCS_IOMB_OPCODE_MASK));
1096 	mdb_printf(")\n");
1097 
1098 	mdb_printf("Remaining Payload:\n");
1099 
1100 	mdb_inc_indent(2);
1101 	for (qeidx = 1; qeidx < (PMCS_QENTRY_SIZE / 4); qeidx++) {
1102 		mdb_printf("%08x ", LE_32(*(qentryp + qeidx)));
1103 	}
1104 	mdb_printf("\n");
1105 	mdb_dec_indent(4);
1106 }
1107 
1108 static void
1109 display_outbound_queues(struct pmcs_hw ss, uint_t verbose)
1110 {
1111 	int		idx, qidx;
1112 	uintptr_t	obqp;
1113 	uint32_t	*cip;
1114 	uint32_t	*qentryp = mdb_alloc(PMCS_QENTRY_SIZE, UM_SLEEP);
1115 	uint32_t	last_consumed, oqpi;
1116 
1117 	mdb_printf("\n");
1118 	mdb_printf("Outbound Queues\n");
1119 	mdb_printf("---------------\n");
1120 
1121 	mdb_inc_indent(2);
1122 
1123 	for (qidx = 0; qidx < PMCS_NOQ; qidx++) {
1124 		obqp = (uintptr_t)ss.oqp[qidx];
1125 
1126 		if (obqp == NULL) {
1127 			mdb_printf("No outbound queue ptr for queue #%d\n",
1128 			    qidx);
1129 			continue;
1130 		}
1131 
1132 		mdb_printf("Outbound Queue #%d (Queue Type = %s)\n", qidx,
1133 		    obq_type(qidx));
1134 		/*
1135 		 * Chip is the producer, so read the actual producer index
1136 		 * and not the driver's version
1137 		 */
1138 		cip = (uint32_t *)((void *)ss.cip);
1139 		if (MDB_RD(&oqpi, 4, cip + OQPI_BASE_OFFSET +
1140 		    (qidx * 4)) == -1) {
1141 			mdb_warn("Couldn't read oqpi\n");
1142 			break;
1143 		}
1144 
1145 		mdb_printf("Producer index: %d  Consumer index: %d\n\n",
1146 		    LE_32(oqpi), ss.oqci[qidx]);
1147 		mdb_inc_indent(2);
1148 
1149 		if (ss.oqci[qidx] == 0) {
1150 			last_consumed = ss.ioq_depth - 1;
1151 		} else {
1152 			last_consumed = ss.oqci[qidx] - 1;
1153 		}
1154 
1155 
1156 		if (!verbose) {
1157 			mdb_printf("Last processed entry:\n");
1158 			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
1159 			    (obqp + (PMCS_QENTRY_SIZE * last_consumed)))
1160 			    == -1) {
1161 				mdb_warn("Couldn't read queue entry at 0x%p\n",
1162 				    (obqp + (PMCS_QENTRY_SIZE *
1163 				    last_consumed)));
1164 				break;
1165 			}
1166 			dump_one_qentry_outbound(qentryp, last_consumed);
1167 			mdb_printf("\n");
1168 			mdb_dec_indent(2);
1169 			continue;
1170 		}
1171 
1172 		for (idx = 0; idx < ss.ioq_depth; idx++) {
1173 			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
1174 			    (obqp + (PMCS_QENTRY_SIZE * idx))) == -1) {
1175 				mdb_warn("Couldn't read queue entry at 0x%p\n",
1176 				    (obqp + (PMCS_QENTRY_SIZE * idx)));
1177 				break;
1178 			}
1179 			dump_one_qentry_outbound(qentryp, idx);
1180 		}
1181 
1182 		mdb_printf("\n");
1183 		mdb_dec_indent(2);
1184 	}
1185 
1186 	mdb_dec_indent(2);
1187 	mdb_free(qentryp, PMCS_QENTRY_SIZE);
1188 }
1189 
1190 static void
1191 dump_one_qentry_inbound(uint32_t *qentryp, int idx)
1192 {
1193 	int qeidx;
1194 	uint32_t word0 = LE_32(*qentryp);
1195 
1196 	mdb_printf("Entry #%02d\n", idx);
1197 	mdb_inc_indent(2);
1198 
1199 	mdb_printf("Header: 0x%08x (", word0);
1200 	if (word0 & PMCS_IOMB_VALID) {
1201 		mdb_printf("VALID, ");
1202 	}
1203 	if (word0 & PMCS_IOMB_HIPRI) {
1204 		mdb_printf("HIPRI, ");
1205 	}
1206 	mdb_printf("OBID=%d, ",
1207 	    (word0 & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT);
1208 	mdb_printf("CAT=%s, ",
1209 	    iomb_cat((word0 & PMCS_IOMB_CAT_MASK) >> PMCS_IOMB_CAT_SHIFT));
1210 	mdb_printf("OPCODE=%s",
1211 	    inbound_iomb_opcode(word0 & PMCS_IOMB_OPCODE_MASK));
1212 	mdb_printf(")\n");
1213 
1214 	mdb_printf("HTAG: 0x%08x\n", LE_32(*(qentryp + 1)));
1215 	mdb_printf("Remaining Payload:\n");
1216 
1217 	mdb_inc_indent(2);
1218 	for (qeidx = 2; qeidx < (PMCS_QENTRY_SIZE / 4); qeidx++) {
1219 		mdb_printf("%08x ", LE_32(*(qentryp + qeidx)));
1220 	}
1221 	mdb_printf("\n");
1222 	mdb_dec_indent(4);
1223 }
1224 
1225 static void
1226 display_inbound_queues(struct pmcs_hw ss, uint_t verbose)
1227 {
1228 	int		idx, qidx, iqci, last_consumed;
1229 	uintptr_t	ibqp;
1230 	uint32_t	*qentryp = mdb_alloc(PMCS_QENTRY_SIZE, UM_SLEEP);
1231 	uint32_t	*cip;
1232 
1233 	mdb_printf("\n");
1234 	mdb_printf("Inbound Queues\n");
1235 	mdb_printf("--------------\n");
1236 
1237 	mdb_inc_indent(2);
1238 
1239 	for (qidx = 0; qidx < PMCS_NIQ; qidx++) {
1240 		ibqp = (uintptr_t)ss.iqp[qidx];
1241 
1242 		if (ibqp == NULL) {
1243 			mdb_printf("No inbound queue ptr for queue #%d\n",
1244 			    qidx);
1245 			continue;
1246 		}
1247 
1248 		mdb_printf("Inbound Queue #%d (Queue Type = %s)\n", qidx,
1249 		    ibq_type(qidx));
1250 
1251 		cip = (uint32_t *)((void *)ss.cip);
1252 		if (MDB_RD(&iqci, 4, cip + (qidx * 4)) == -1) {
1253 			mdb_warn("Couldn't read iqci\n");
1254 			break;
1255 		}
1256 		iqci = LE_32(iqci);
1257 
1258 		mdb_printf("Producer index: %d  Consumer index: %d\n\n",
1259 		    ss.shadow_iqpi[qidx], iqci);
1260 		mdb_inc_indent(2);
1261 
1262 		if (iqci == 0) {
1263 			last_consumed = ss.ioq_depth - 1;
1264 		} else {
1265 			last_consumed = iqci - 1;
1266 		}
1267 
1268 		if (!verbose) {
1269 			mdb_printf("Last processed entry:\n");
1270 			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
1271 			    (ibqp + (PMCS_QENTRY_SIZE * last_consumed)))
1272 			    == -1) {
1273 				mdb_warn("Couldn't read queue entry at 0x%p\n",
1274 				    (ibqp + (PMCS_QENTRY_SIZE *
1275 				    last_consumed)));
1276 				break;
1277 			}
1278 			dump_one_qentry_inbound(qentryp, last_consumed);
1279 			mdb_printf("\n");
1280 			mdb_dec_indent(2);
1281 			continue;
1282 		}
1283 
1284 		for (idx = 0; idx < ss.ioq_depth; idx++) {
1285 			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
1286 			    (ibqp + (PMCS_QENTRY_SIZE * idx))) == -1) {
1287 				mdb_warn("Couldn't read queue entry at 0x%p\n",
1288 				    (ibqp + (PMCS_QENTRY_SIZE * idx)));
1289 				break;
1290 			}
1291 			dump_one_qentry_inbound(qentryp, idx);
1292 		}
1293 
1294 		mdb_printf("\n");
1295 		mdb_dec_indent(2);
1296 	}
1297 
1298 	mdb_dec_indent(2);
1299 	mdb_free(qentryp, PMCS_QENTRY_SIZE);
1300 }
1301 
1302 static void
1303 display_phy(struct pmcs_phy phy, int verbose, int totals_only)
1304 {
1305 	char		*dtype, *speed;
1306 	char		*yes = "Yes";
1307 	char		*no = "No";
1308 	char		*cfgd = no;
1309 	char		*apend = no;
1310 	char		*asent = no;
1311 	char		*dead = no;
1312 	char		*changed = no;
1313 
1314 	switch (phy.dtype) {
1315 	case NOTHING:
1316 		dtype = "None";
1317 		break;
1318 	case SATA:
1319 		dtype = "SATA";
1320 		if (phy.configured) {
1321 			++sata_phys;
1322 		}
1323 		break;
1324 	case SAS:
1325 		dtype = "SAS";
1326 		if (phy.configured) {
1327 			++sas_phys;
1328 		}
1329 		break;
1330 	case EXPANDER:
1331 		dtype = "EXP";
1332 		if (phy.configured) {
1333 			++exp_phys;
1334 		}
1335 		break;
1336 	}
1337 
1338 	if (phy.dtype == NOTHING) {
1339 		empty_phys++;
1340 	} else if ((phy.dtype == EXPANDER) && phy.configured) {
1341 		num_expanders++;
1342 	}
1343 
1344 	if (totals_only) {
1345 		return;
1346 	}
1347 
1348 	switch (phy.link_rate) {
1349 	case SAS_LINK_RATE_1_5GBIT:
1350 		speed = "1.5Gb/s";
1351 		break;
1352 	case SAS_LINK_RATE_3GBIT:
1353 		speed = "3 Gb/s";
1354 		break;
1355 	case SAS_LINK_RATE_6GBIT:
1356 		speed = "6 Gb/s";
1357 		break;
1358 	default:
1359 		speed = "N/A";
1360 		break;
1361 	}
1362 
1363 	if ((phy.dtype != NOTHING) || verbose) {
1364 		print_sas_address(&phy);
1365 
1366 		if (phy.device_id != PMCS_INVALID_DEVICE_ID) {
1367 			mdb_printf(" %3d %4d %6s %4s ",
1368 			    phy.device_id, phy.phynum, speed, dtype);
1369 		} else {
1370 			mdb_printf(" N/A %4d %6s %4s ",
1371 			    phy.phynum, speed, dtype);
1372 		}
1373 
1374 		if (verbose) {
1375 			if (phy.abort_sent) {
1376 				asent = yes;
1377 			}
1378 			if (phy.abort_pending) {
1379 				apend = yes;
1380 			}
1381 			if (phy.configured) {
1382 				cfgd = yes;
1383 			}
1384 			if (phy.dead) {
1385 				dead = yes;
1386 			}
1387 			if (phy.changed) {
1388 				changed = yes;
1389 			}
1390 
1391 			mdb_printf("%-4s %-4s %-4s %-4s %-4s %3d "
1392 			    "0x%p ", cfgd, apend, asent,
1393 			    changed, dead, phy.ref_count, phy.phy_lock);
1394 		}
1395 
1396 		mdb_printf("Path: %s\n", phy.path);
1397 	}
1398 }
1399 
1400 static void
1401 display_phys(struct pmcs_hw ss, int verbose, struct pmcs_phy *parent, int level,
1402     int totals_only)
1403 {
1404 	pmcs_phy_t	phy;
1405 	pmcs_phy_t	*pphy = parent;
1406 
1407 	mdb_inc_indent(3);
1408 
1409 	if (parent == NULL) {
1410 		pphy = (pmcs_phy_t *)ss.root_phys;
1411 	} else {
1412 		pphy = (pmcs_phy_t *)parent;
1413 	}
1414 
1415 	if (level == 0) {
1416 		sas_phys = 0;
1417 		sata_phys = 0;
1418 		exp_phys = 0;
1419 		num_expanders = 0;
1420 		empty_phys = 0;
1421 	}
1422 
1423 	if (!totals_only) {
1424 		if (level == 0) {
1425 			mdb_printf("PHY information\n");
1426 		}
1427 		mdb_printf("--------\n");
1428 		mdb_printf("Level %2d\n", level);
1429 		mdb_printf("--------\n");
1430 		mdb_printf("SAS Address      Hdl Phy#  Speed Type ");
1431 
1432 		if (verbose) {
1433 			mdb_printf("Cfgd AbtP AbtS Chgd Dead Ref Lock\n");
1434 		} else {
1435 			mdb_printf("\n");
1436 		}
1437 	}
1438 
1439 	while (pphy) {
1440 		if (MDB_RD(&phy, sizeof (phy), (uintptr_t)pphy) == -1) {
1441 			NOREAD(pmcs_phy_t, phy);
1442 			break;
1443 		}
1444 
1445 		display_phy(phy, verbose, totals_only);
1446 
1447 		if (phy.children) {
1448 			display_phys(ss, verbose, phy.children, level + 1,
1449 			    totals_only);
1450 			if (!totals_only) {
1451 				mdb_printf("\n");
1452 			}
1453 		}
1454 
1455 		pphy = phy.sibling;
1456 	}
1457 
1458 	mdb_dec_indent(3);
1459 
1460 	if (level == 0) {
1461 		if (verbose) {
1462 			mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP) "
1463 			    "(+%d subsidiary + %d empty)\n", "Occupied PHYs:",
1464 			    (sas_phys + sata_phys + num_expanders),
1465 			    sas_phys, sata_phys, num_expanders,
1466 			    (exp_phys - num_expanders), empty_phys);
1467 		} else {
1468 			mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP)\n",
1469 			    "Occupied PHYs:",
1470 			    (sas_phys + sata_phys + num_expanders),
1471 			    sas_phys, sata_phys, num_expanders);
1472 		}
1473 	}
1474 }
1475 
1476 /*
1477  * filter is used to indicate whether we are filtering log messages based
1478  * on "instance".  The other filtering (based on options) depends on the
1479  * values that are passed in for "sas_addr" and "phy_path".
1480  *
1481  * MAX_INST_STRLEN is the largest string size from which we will attempt
1482  * to convert to an instance number.  The string will be formed up as
1483  * "0t<inst>\0" so that mdb_strtoull can parse it properly.
1484  */
1485 #define	MAX_INST_STRLEN	8
1486 
1487 static int
1488 pmcs_dump_tracelog(boolean_t filter, int instance, const char *phy_path,
1489     uint64_t sas_address)
1490 {
1491 	pmcs_tbuf_t *tbuf_addr;
1492 	uint_t tbuf_idx;
1493 	pmcs_tbuf_t tbuf;
1494 	boolean_t wrap, elem_filtered;
1495 	uint_t start_idx, elems_to_print, idx, tbuf_num_elems;
1496 	char *bufp;
1497 	char elem_inst[MAX_INST_STRLEN], ei_idx;
1498 	uint64_t sas_addr;
1499 	uint8_t *sas_addressp;
1500 
1501 	/* Get the address of the first element */
1502 	if (mdb_readvar(&tbuf_addr, "pmcs_tbuf") == -1) {
1503 		mdb_warn("can't read pmcs_tbuf");
1504 		return (DCMD_ERR);
1505 	}
1506 
1507 	/* Get the total number */
1508 	if (mdb_readvar(&tbuf_num_elems, "pmcs_tbuf_num_elems") == -1) {
1509 		mdb_warn("can't read pmcs_tbuf_num_elems");
1510 		return (DCMD_ERR);
1511 	}
1512 
1513 	/* Get the current index */
1514 	if (mdb_readvar(&tbuf_idx, "pmcs_tbuf_idx") == -1) {
1515 		mdb_warn("can't read pmcs_tbuf_idx");
1516 		return (DCMD_ERR);
1517 	}
1518 
1519 	/* Indicator as to whether the buffer has wrapped */
1520 	if (mdb_readvar(&wrap, "pmcs_tbuf_wrap") == -1) {
1521 		mdb_warn("can't read pmcs_tbuf_wrap");
1522 		return (DCMD_ERR);
1523 	}
1524 
1525 	/*
1526 	 * On little-endian systems, the SAS address passed in will be
1527 	 * byte swapped.  Take care of that here.
1528 	 */
1529 #if defined(_LITTLE_ENDIAN)
1530 	sas_addr = ((sas_address << 56) |
1531 	    ((sas_address << 40) & 0xff000000000000ULL) |
1532 	    ((sas_address << 24) & 0xff0000000000ULL) |
1533 	    ((sas_address << 8)  & 0xff00000000ULL) |
1534 	    ((sas_address >> 8)  & 0xff000000ULL) |
1535 	    ((sas_address >> 24) & 0xff0000ULL) |
1536 	    ((sas_address >> 40) & 0xff00ULL) |
1537 	    (sas_address  >> 56));
1538 #else
1539 	sas_addr = sas_address;
1540 #endif
1541 	sas_addressp = (uint8_t *)&sas_addr;
1542 
1543 	/* Figure out where we start and stop */
1544 	if (wrap) {
1545 		start_idx = tbuf_idx;
1546 		elems_to_print = tbuf_num_elems;
1547 	} else {
1548 		start_idx = 0;
1549 		elems_to_print = tbuf_idx;
1550 	}
1551 
1552 	idx = start_idx;
1553 
1554 	/* Dump the buffer contents */
1555 	while (elems_to_print != 0) {
1556 		if (MDB_RD(&tbuf, sizeof (pmcs_tbuf_t), (tbuf_addr + idx))
1557 		    == -1) {
1558 			NOREAD(tbuf, (tbuf_addr + idx));
1559 			return (DCMD_ERR);
1560 		}
1561 
1562 		/*
1563 		 * Check for filtering on HBA instance
1564 		 */
1565 		elem_filtered = B_FALSE;
1566 
1567 		if (filter) {
1568 			bufp = tbuf.buf;
1569 			/* Skip the driver name */
1570 			while (*bufp < '0' || *bufp > '9') {
1571 				bufp++;
1572 			}
1573 
1574 			ei_idx = 0;
1575 			elem_inst[ei_idx++] = '0';
1576 			elem_inst[ei_idx++] = 't';
1577 			while (*bufp != ':' && ei_idx < (MAX_INST_STRLEN - 1)) {
1578 				elem_inst[ei_idx++] = *bufp;
1579 				bufp++;
1580 			}
1581 			elem_inst[ei_idx] = 0;
1582 
1583 			/* Get the instance */
1584 			if ((int)mdb_strtoull(elem_inst) != instance) {
1585 				elem_filtered = B_TRUE;
1586 			}
1587 		}
1588 
1589 		if (!elem_filtered && (phy_path || sas_address)) {
1590 			/*
1591 			 * This message is not being filtered by HBA instance.
1592 			 * Now check to see if we're filtering based on
1593 			 * PHY path or SAS address.
1594 			 * Filtering is an "OR" operation.  So, if any of the
1595 			 * criteria matches, this message will be printed.
1596 			 */
1597 			elem_filtered = B_TRUE;
1598 
1599 			if (phy_path != NULL) {
1600 				if (strncmp(phy_path, tbuf.phy_path,
1601 				    PMCS_TBUF_UA_MAX_SIZE) == 0) {
1602 					elem_filtered = B_FALSE;
1603 				}
1604 			}
1605 			if (sas_address != 0) {
1606 				if (memcmp(sas_addressp, tbuf.phy_sas_address,
1607 				    8) == 0) {
1608 					elem_filtered = B_FALSE;
1609 				}
1610 			}
1611 		}
1612 
1613 		if (!elem_filtered) {
1614 			mdb_printf("%Y.%09ld %s\n", tbuf.timestamp, tbuf.buf);
1615 		}
1616 
1617 		--elems_to_print;
1618 		if (++idx == tbuf_num_elems) {
1619 			idx = 0;
1620 		}
1621 	}
1622 
1623 	return (DCMD_OK);
1624 }
1625 
1626 /*
1627  * Walkers
1628  */
1629 static int
1630 targets_walk_i(mdb_walk_state_t *wsp)
1631 {
1632 	if (wsp->walk_addr == NULL) {
1633 		mdb_warn("Can not perform global walk\n");
1634 		return (WALK_ERR);
1635 	}
1636 
1637 	/*
1638 	 * Address provided belongs to HBA softstate.  Get the targets pointer
1639 	 * to begin the walk.
1640 	 */
1641 	if (mdb_vread(&ss, sizeof (pmcs_hw_t), wsp->walk_addr) !=
1642 	    sizeof (pmcs_hw_t)) {
1643 		mdb_warn("Unable to read HBA softstate\n");
1644 		return (WALK_ERR);
1645 	}
1646 
1647 	if (targets == NULL) {
1648 		targets = mdb_alloc(sizeof (targets) * ss.max_dev, UM_SLEEP);
1649 	}
1650 
1651 	if (MDB_RD(targets, sizeof (targets) * ss.max_dev, ss.targets) == -1) {
1652 		NOREAD(targets, ss.targets);
1653 		return (WALK_ERR);
1654 	}
1655 
1656 	target_idx = 0;
1657 	wsp->walk_addr = (uintptr_t)(targets[0]);
1658 	wsp->walk_data = mdb_alloc(sizeof (pmcs_xscsi_t), UM_SLEEP);
1659 
1660 	return (WALK_NEXT);
1661 }
1662 
1663 static int
1664 targets_walk_s(mdb_walk_state_t *wsp)
1665 {
1666 	int status;
1667 
1668 	if (target_idx == ss.max_dev) {
1669 		return (WALK_DONE);
1670 	}
1671 
1672 	if (mdb_vread(wsp->walk_data, sizeof (pmcs_xscsi_t),
1673 	    wsp->walk_addr) == -1) {
1674 		mdb_warn("Failed to read target at %p", (void *)wsp->walk_addr);
1675 		return (WALK_DONE);
1676 	}
1677 
1678 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
1679 	    wsp->walk_cbdata);
1680 
1681 	do {
1682 		wsp->walk_addr = (uintptr_t)(targets[++target_idx]);
1683 	} while ((wsp->walk_addr == NULL) && (target_idx < ss.max_dev));
1684 
1685 	if (target_idx == ss.max_dev) {
1686 		return (WALK_DONE);
1687 	}
1688 
1689 	return (status);
1690 }
1691 
1692 static void
1693 targets_walk_f(mdb_walk_state_t *wsp)
1694 {
1695 	mdb_free(wsp->walk_data, sizeof (pmcs_xscsi_t));
1696 }
1697 
1698 
1699 static pmcs_phy_t *
1700 pmcs_next_sibling(pmcs_phy_t *phyp)
1701 {
1702 	pmcs_phy_t parent;
1703 
1704 	/*
1705 	 * First, if this is a root PHY, there are no more siblings
1706 	 */
1707 	if (phyp->level == 0) {
1708 		return (NULL);
1709 	}
1710 
1711 	/*
1712 	 * Otherwise, next sibling is the parent's sibling
1713 	 */
1714 	while (phyp->level > 0) {
1715 		if (mdb_vread(&parent, sizeof (pmcs_phy_t),
1716 		    (uintptr_t)phyp->parent) == -1) {
1717 			mdb_warn("pmcs_next_sibling: Failed to read PHY at %p",
1718 			    (void *)phyp->parent);
1719 			return (NULL);
1720 		}
1721 
1722 		if (parent.sibling != NULL) {
1723 			break;
1724 		}
1725 
1726 		phyp = phyp->parent;
1727 	}
1728 
1729 	return (parent.sibling);
1730 }
1731 
1732 static int
1733 phy_walk_i(mdb_walk_state_t *wsp)
1734 {
1735 	if (wsp->walk_addr == NULL) {
1736 		mdb_warn("Can not perform global walk\n");
1737 		return (WALK_ERR);
1738 	}
1739 
1740 	/*
1741 	 * Address provided belongs to HBA softstate.  Get the targets pointer
1742 	 * to begin the walk.
1743 	 */
1744 	if (mdb_vread(&ss, sizeof (pmcs_hw_t), wsp->walk_addr) !=
1745 	    sizeof (pmcs_hw_t)) {
1746 		mdb_warn("Unable to read HBA softstate\n");
1747 		return (WALK_ERR);
1748 	}
1749 
1750 	wsp->walk_addr = (uintptr_t)(ss.root_phys);
1751 	wsp->walk_data = mdb_alloc(sizeof (pmcs_phy_t), UM_SLEEP);
1752 
1753 	return (WALK_NEXT);
1754 }
1755 
1756 static int
1757 phy_walk_s(mdb_walk_state_t *wsp)
1758 {
1759 	pmcs_phy_t *phyp, *nphyp;
1760 	int status;
1761 
1762 	if (mdb_vread(wsp->walk_data, sizeof (pmcs_phy_t),
1763 	    wsp->walk_addr) == -1) {
1764 		mdb_warn("phy_walk_s: Failed to read PHY at %p",
1765 		    (void *)wsp->walk_addr);
1766 		return (WALK_DONE);
1767 	}
1768 
1769 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
1770 	    wsp->walk_cbdata);
1771 
1772 	phyp = (pmcs_phy_t *)wsp->walk_data;
1773 	if (phyp->children) {
1774 		wsp->walk_addr = (uintptr_t)(phyp->children);
1775 	} else {
1776 		wsp->walk_addr = (uintptr_t)(phyp->sibling);
1777 	}
1778 
1779 	if (wsp->walk_addr == NULL) {
1780 		/*
1781 		 * We reached the end of this sibling list.  Trudge back up
1782 		 * to the parent and find the next sibling after the expander
1783 		 * we just finished traversing, if there is one.
1784 		 */
1785 		nphyp = pmcs_next_sibling(phyp);
1786 
1787 		if (nphyp == NULL) {
1788 			return (WALK_DONE);
1789 		}
1790 
1791 		wsp->walk_addr = (uintptr_t)nphyp;
1792 	}
1793 
1794 	return (status);
1795 }
1796 
1797 static void
1798 phy_walk_f(mdb_walk_state_t *wsp)
1799 {
1800 	mdb_free(wsp->walk_data, sizeof (pmcs_phy_t));
1801 }
1802 
1803 static void
1804 display_matching_work(struct pmcs_hw ss, uintmax_t index, uintmax_t snum,
1805     uintmax_t tag_type)
1806 {
1807 	int		idx;
1808 	pmcwork_t	work, *wp = &work;
1809 	uintptr_t	_wp;
1810 	boolean_t	printed_header = B_FALSE;
1811 	uint32_t	mask, mask_val, match_val;
1812 	char		*match_type;
1813 
1814 	if (index != UINT_MAX) {
1815 		match_type = "index";
1816 		mask = PMCS_TAG_INDEX_MASK;
1817 		mask_val = index << PMCS_TAG_INDEX_SHIFT;
1818 		match_val = index;
1819 	} else if (snum != UINT_MAX) {
1820 		match_type = "serial number";
1821 		mask = PMCS_TAG_SERNO_MASK;
1822 		mask_val = snum << PMCS_TAG_SERNO_SHIFT;
1823 		match_val = snum;
1824 	} else {
1825 		switch (tag_type) {
1826 		case PMCS_TAG_TYPE_NONE:
1827 			match_type = "tag type NONE";
1828 			break;
1829 		case PMCS_TAG_TYPE_CBACK:
1830 			match_type = "tag type CBACK";
1831 			break;
1832 		case PMCS_TAG_TYPE_WAIT:
1833 			match_type = "tag type WAIT";
1834 			break;
1835 		}
1836 		mask = PMCS_TAG_TYPE_MASK;
1837 		mask_val = tag_type << PMCS_TAG_TYPE_SHIFT;
1838 		match_val = tag_type;
1839 	}
1840 
1841 	_wp = (uintptr_t)ss.work;
1842 
1843 	for (idx = 0; idx < ss.max_cmd; idx++, _wp += sizeof (pmcwork_t)) {
1844 		if (MDB_RD(&work, sizeof (pmcwork_t), _wp) == -1) {
1845 			NOREAD(pmcwork_t, _wp);
1846 			continue;
1847 		}
1848 
1849 		if ((work.htag & mask) != mask_val) {
1850 			continue;
1851 		}
1852 
1853 		if (printed_header == B_FALSE) {
1854 			if (tag_type) {
1855 				mdb_printf("\nWork structures matching %s\n\n",
1856 				    match_type, match_val);
1857 			} else {
1858 				mdb_printf("\nWork structures matching %s of "
1859 				    "0x%x\n\n", match_type, match_val);
1860 			}
1861 			mdb_printf("%8s %10s %20s %8s %8s O D\n",
1862 			    "HTag", "State", "Phy Path", "Target", "Timer");
1863 			printed_header = B_TRUE;
1864 		}
1865 
1866 		display_one_work(wp, 0, 0);
1867 	}
1868 
1869 	if (!printed_header) {
1870 		mdb_printf("No work structure matches found\n");
1871 	}
1872 }
1873 
1874 static int
1875 pmcs_tag(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1876 {
1877 	struct	pmcs_hw		ss;
1878 	uintmax_t		tag_type = UINT_MAX;
1879 	uintmax_t		snum = UINT_MAX;
1880 	uintmax_t		index = UINT_MAX;
1881 	int			args = 0;
1882 	void			*pmcs_state;
1883 	char			*state_str;
1884 	struct dev_info		dip;
1885 
1886 	if (!(flags & DCMD_ADDRSPEC)) {
1887 		pmcs_state = NULL;
1888 		if (mdb_readvar(&pmcs_state, "pmcs_softc_state") == -1) {
1889 			mdb_warn("can't read pmcs_softc_state");
1890 			return (DCMD_ERR);
1891 		}
1892 		if (mdb_pwalk_dcmd("genunix`softstate", "pmcs`pmcs_tag", argc,
1893 		    argv, (uintptr_t)pmcs_state) == -1) {
1894 			mdb_warn("mdb_pwalk_dcmd failed");
1895 			return (DCMD_ERR);
1896 		}
1897 		return (DCMD_OK);
1898 	}
1899 
1900 	if (mdb_getopts(argc, argv,
1901 	    'i', MDB_OPT_UINT64, &index,
1902 	    's', MDB_OPT_UINT64, &snum,
1903 	    't', MDB_OPT_UINT64, &tag_type) != argc)
1904 		return (DCMD_USAGE);
1905 
1906 	/*
1907 	 * Count the number of supplied options and make sure they are
1908 	 * within appropriate ranges.  If they're set to UINT_MAX, that means
1909 	 * they were not supplied, in which case reset them to 0.
1910 	 */
1911 	if (index != UINT_MAX) {
1912 		args++;
1913 		if (index > PMCS_TAG_INDEX_MASK) {
1914 			mdb_warn("Index is out of range\n");
1915 			return (DCMD_USAGE);
1916 		}
1917 	}
1918 
1919 	if (tag_type != UINT_MAX) {
1920 		args++;
1921 		switch (tag_type) {
1922 		case PMCS_TAG_TYPE_NONE:
1923 		case PMCS_TAG_TYPE_CBACK:
1924 		case PMCS_TAG_TYPE_WAIT:
1925 			break;
1926 		default:
1927 			mdb_warn("Invalid tag type\n");
1928 			return (DCMD_USAGE);
1929 		}
1930 	}
1931 
1932 	if (snum != UINT_MAX) {
1933 		args++;
1934 		if (snum > (PMCS_TAG_SERNO_MASK >> PMCS_TAG_SERNO_SHIFT)) {
1935 			mdb_warn("Serial number is out of range\n");
1936 			return (DCMD_USAGE);
1937 		}
1938 	}
1939 
1940 	/*
1941 	 * Make sure 1 and only 1 option is specified
1942 	 */
1943 	if ((args == 0) || (args > 1)) {
1944 		mdb_warn("Exactly one of -i, -s and -t must be specified\n");
1945 		return (DCMD_USAGE);
1946 	}
1947 
1948 	if (MDB_RD(&ss, sizeof (ss), addr) == -1) {
1949 		NOREAD(pmcs_hw_t, addr);
1950 		return (DCMD_ERR);
1951 	}
1952 
1953 	if (MDB_RD(&dip, sizeof (struct dev_info), ss.dip) == -1) {
1954 		NOREAD(pmcs_hw_t, addr);
1955 		return (DCMD_ERR);
1956 	}
1957 
1958 	/* processing completed */
1959 
1960 	if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) ||
1961 	    (flags & DCMD_LOOPFIRST)) {
1962 		if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST))
1963 			mdb_printf("\n");
1964 		mdb_printf("%16s %9s %4s B C  WorkFlags wserno DbgMsk %16s\n",
1965 		    "Address", "State", "Inst", "DIP");
1966 		mdb_printf("================================="
1967 		    "============================================\n");
1968 	}
1969 
1970 	switch (ss.state) {
1971 	case STATE_NIL:
1972 		state_str = "Invalid";
1973 		break;
1974 	case STATE_PROBING:
1975 		state_str = "Probing";
1976 		break;
1977 	case STATE_RUNNING:
1978 		state_str = "Running";
1979 		break;
1980 	case STATE_UNPROBING:
1981 		state_str = "Unprobing";
1982 		break;
1983 	case STATE_DEAD:
1984 		state_str = "Dead";
1985 		break;
1986 	}
1987 
1988 	mdb_printf("%16p %9s %4d %1d %1d 0x%08x 0x%04x 0x%04x %16p\n", addr,
1989 	    state_str, dip.devi_instance, ss.blocked, ss.configuring,
1990 	    ss.work_flags, ss.wserno, ss.debug_mask, ss.dip);
1991 	mdb_printf("\n");
1992 
1993 	mdb_inc_indent(4);
1994 	display_matching_work(ss, index, snum, tag_type);
1995 	mdb_dec_indent(4);
1996 	mdb_printf("\n");
1997 
1998 	return (DCMD_OK);
1999 }
2000 
2001 static int
2002 pmcs_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2003 {
2004 	void		*pmcs_state;
2005 	struct pmcs_hw	ss;
2006 	struct dev_info	dip;
2007 	const char	*match_phy_path = NULL;
2008 	uint64_t 	match_sas_address = 0;
2009 
2010 	if (!(flags & DCMD_ADDRSPEC)) {
2011 		pmcs_state = NULL;
2012 		if (mdb_readvar(&pmcs_state, "pmcs_softc_state") == -1) {
2013 			mdb_warn("can't read pmcs_softc_state");
2014 			return (DCMD_ERR);
2015 		}
2016 		if (mdb_pwalk_dcmd("genunix`softstate", "pmcs`pmcs_log", argc,
2017 		    argv, (uintptr_t)pmcs_state) == -1) {
2018 			mdb_warn("mdb_pwalk_dcmd failed for pmcs_log");
2019 			return (DCMD_ERR);
2020 		}
2021 		return (DCMD_OK);
2022 	}
2023 
2024 	if (mdb_getopts(argc, argv,
2025 	    'p', MDB_OPT_STR, &match_phy_path,
2026 	    's', MDB_OPT_UINT64, &match_sas_address,
2027 	    NULL) != argc) {
2028 		return (DCMD_USAGE);
2029 	}
2030 
2031 	if (MDB_RD(&ss, sizeof (ss), addr) == -1) {
2032 		NOREAD(pmcs_hw_t, addr);
2033 		return (DCMD_ERR);
2034 	}
2035 
2036 	if (MDB_RD(&dip, sizeof (struct dev_info), ss.dip) == -1) {
2037 		NOREAD(pmcs_hw_t, addr);
2038 		return (DCMD_ERR);
2039 	}
2040 
2041 	if (!(flags & DCMD_LOOP)) {
2042 		return (pmcs_dump_tracelog(B_TRUE, dip.devi_instance,
2043 		    match_phy_path, match_sas_address));
2044 	} else if (flags & DCMD_LOOPFIRST) {
2045 		return (pmcs_dump_tracelog(B_FALSE, 0, match_phy_path,
2046 		    match_sas_address));
2047 	} else {
2048 		return (DCMD_OK);
2049 	}
2050 }
2051 
2052 static int
2053 pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2054 {
2055 	struct pmcs_hw		ss;
2056 	uint_t			verbose = FALSE;
2057 	uint_t			phy_info = FALSE;
2058 	uint_t			hw_info = FALSE;
2059 	uint_t			target_info = FALSE;
2060 	uint_t			work_info = FALSE;
2061 	uint_t			ic_info = FALSE;
2062 	uint_t			iport_info = FALSE;
2063 	uint_t			waitqs_info = FALSE;
2064 	uint_t			ibq = FALSE;
2065 	uint_t			obq = FALSE;
2066 	uint_t			tgt_phy_count = FALSE;
2067 	uint_t			compq = FALSE;
2068 	uint_t			unconfigured = FALSE;
2069 	int			rv = DCMD_OK;
2070 	void			*pmcs_state;
2071 	char			*state_str;
2072 	struct dev_info		dip;
2073 
2074 	if (!(flags & DCMD_ADDRSPEC)) {
2075 		pmcs_state = NULL;
2076 		if (mdb_readvar(&pmcs_state, "pmcs_softc_state") == -1) {
2077 			mdb_warn("can't read pmcs_softc_state");
2078 			return (DCMD_ERR);
2079 		}
2080 		if (mdb_pwalk_dcmd("genunix`softstate", "pmcs`pmcs", argc, argv,
2081 		    (uintptr_t)pmcs_state) == -1) {
2082 			mdb_warn("mdb_pwalk_dcmd failed");
2083 			return (DCMD_ERR);
2084 		}
2085 		return (DCMD_OK);
2086 	}
2087 
2088 	if (mdb_getopts(argc, argv,
2089 	    'c', MDB_OPT_SETBITS, TRUE, &compq,
2090 	    'h', MDB_OPT_SETBITS, TRUE, &hw_info,
2091 	    'i', MDB_OPT_SETBITS, TRUE, &ic_info,
2092 	    'I', MDB_OPT_SETBITS, TRUE, &iport_info,
2093 	    'p', MDB_OPT_SETBITS, TRUE, &phy_info,
2094 	    'q', MDB_OPT_SETBITS, TRUE, &ibq,
2095 	    'Q', MDB_OPT_SETBITS, TRUE, &obq,
2096 	    't', MDB_OPT_SETBITS, TRUE, &target_info,
2097 	    'T', MDB_OPT_SETBITS, TRUE, &tgt_phy_count,
2098 	    'u', MDB_OPT_SETBITS, TRUE, &unconfigured,
2099 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
2100 	    'w', MDB_OPT_SETBITS, TRUE, &work_info,
2101 	    'W', MDB_OPT_SETBITS, TRUE, &waitqs_info,
2102 	    NULL) != argc)
2103 		return (DCMD_USAGE);
2104 
2105 	if (MDB_RD(&ss, sizeof (ss), addr) == -1) {
2106 		NOREAD(pmcs_hw_t, addr);
2107 		return (DCMD_ERR);
2108 	}
2109 
2110 	if (MDB_RD(&dip, sizeof (struct dev_info), ss.dip) == -1) {
2111 		NOREAD(pmcs_hw_t, addr);
2112 		return (DCMD_ERR);
2113 	}
2114 
2115 	/* processing completed */
2116 
2117 	if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) ||
2118 	    (flags & DCMD_LOOPFIRST) || phy_info || target_info || hw_info ||
2119 	    work_info || waitqs_info || ibq || obq || tgt_phy_count || compq ||
2120 	    unconfigured) {
2121 		if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST))
2122 			mdb_printf("\n");
2123 		mdb_printf("%16s %9s %4s B C  WorkFlags wserno DbgMsk %16s\n",
2124 		    "Address", "State", "Inst", "DIP");
2125 		mdb_printf("================================="
2126 		    "============================================\n");
2127 	}
2128 
2129 	switch (ss.state) {
2130 	case STATE_NIL:
2131 		state_str = "Invalid";
2132 		break;
2133 	case STATE_PROBING:
2134 		state_str = "Probing";
2135 		break;
2136 	case STATE_RUNNING:
2137 		state_str = "Running";
2138 		break;
2139 	case STATE_UNPROBING:
2140 		state_str = "Unprobing";
2141 		break;
2142 	case STATE_DEAD:
2143 		state_str = "Dead";
2144 		break;
2145 	}
2146 
2147 	mdb_printf("%16p %9s %4d %1d %1d 0x%08x 0x%04x 0x%04x %16p\n", addr,
2148 	    state_str, dip.devi_instance, ss.blocked, ss.configuring,
2149 	    ss.work_flags, ss.wserno, ss.debug_mask, ss.dip);
2150 	mdb_printf("\n");
2151 
2152 	mdb_inc_indent(4);
2153 
2154 	if (waitqs_info)
2155 		display_waitqs(ss, verbose);
2156 
2157 	if (hw_info)
2158 		display_hwinfo(ss, verbose);
2159 
2160 	if (phy_info || tgt_phy_count)
2161 		display_phys(ss, verbose, NULL, 0, tgt_phy_count);
2162 
2163 	if (target_info || tgt_phy_count)
2164 		display_targets(ss, verbose, tgt_phy_count);
2165 
2166 	if (work_info)
2167 		display_work(ss, verbose);
2168 
2169 	if (ic_info)
2170 		display_ic(ss, verbose);
2171 
2172 	if (ibq)
2173 		display_inbound_queues(ss, verbose);
2174 
2175 	if (obq)
2176 		display_outbound_queues(ss, verbose);
2177 
2178 	if (iport_info)
2179 		display_iport(ss, addr, verbose);
2180 
2181 	if (compq)
2182 		display_completion_queue(ss);
2183 
2184 	if (unconfigured)
2185 		display_unconfigured_targets(addr);
2186 
2187 	mdb_dec_indent(4);
2188 
2189 	return (rv);
2190 }
2191 
2192 void
2193 pmcs_help()
2194 {
2195 	mdb_printf("Prints summary information about each pmcs instance.\n"
2196 	    "    -c: Dump the completion queue\n"
2197 	    "    -h: Print more detailed hardware information\n"
2198 	    "    -i: Print interrupt coalescing information\n"
2199 	    "    -I: Print information about each iport\n"
2200 	    "    -l: Dump the trace log (cannot be used with other options)\n"
2201 	    "    -p: Print information about each attached PHY\n"
2202 	    "    -q: Dump inbound queues\n"
2203 	    "    -Q: Dump outbound queues\n"
2204 	    "    -t: Print information about each configured target\n"
2205 	    "    -T: Print target and PHY count summary\n"
2206 	    "    -u: Show SAS address of all unconfigured targets\n"
2207 	    "    -w: Dump work structures\n"
2208 	    "    -W: List pmcs cmds waiting on various queues\n"
2209 	    "    -v: Add verbosity to the above options\n");
2210 }
2211 
2212 void
2213 pmcs_log_help()
2214 {
2215 	mdb_printf("Dump the pmcs log buffer, possibly with filtering.\n"
2216 	    "    -p PHY_PATH:            Dump messages matching PHY_PATH\n"
2217 	    "    -s SAS_ADDRESS:         Dump messages matching SAS_ADDRESS\n\n"
2218 	    "Where: PHY_PATH can be found with ::pmcs -p (e.g. pp04.18.18.01)\n"
2219 	    "       SAS_ADDRESS can be found with ::pmcs -t "
2220 	    "(e.g. 5000c5000358c221)\n");
2221 }
2222 void
2223 pmcs_tag_help()
2224 {
2225 	mdb_printf("Print all work structures by matching the tag.\n"
2226 	    "    -i index:        Match tag index (0x000 - 0xfff)\n"
2227 	    "    -s serialnumber: Match serial number (0x0000 - 0xffff)\n"
2228 	    "    -t tagtype:      Match tag type [NONE(1), CBACK(2), "
2229 	    "WAIT(3)]\n");
2230 }
2231 
2232 static const mdb_dcmd_t dcmds[] = {
2233 	{ "pmcs", "?[-chiIpQqtTuwWv]", "print pmcs information",
2234 	    pmcs_dcmd, pmcs_help
2235 	},
2236 	{ "pmcs_log",
2237 	    "?[-p PHY_PATH | -s SAS_ADDRESS]",
2238 	    "dump pmcs log file", pmcs_log, pmcs_log_help
2239 	},
2240 	{ "pmcs_tag", "?[-t tagtype|-s serialnum|-i index]",
2241 	    "Find work structures by tag type, serial number or index",
2242 	    pmcs_tag, pmcs_tag_help
2243 	},
2244 	{ NULL }
2245 };
2246 
2247 static const mdb_walker_t walkers[] = {
2248 	{ "pmcs_targets", "walk target structures",
2249 		targets_walk_i, targets_walk_s, targets_walk_f },
2250 	{ "pmcs_phys", "walk PHY structures",
2251 		phy_walk_i, phy_walk_s, phy_walk_f },
2252 	{ NULL }
2253 };
2254 
2255 static const mdb_modinfo_t modinfo = {
2256 	MDB_API_VERSION, dcmds, walkers
2257 };
2258 
2259 const mdb_modinfo_t *
2260 _mdb_init(void)
2261 {
2262 	return (&modinfo);
2263 }
2264