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