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