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