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