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