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