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