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