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