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
print_sas_address(pmcs_phy_t * phy)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
pmcs_fwtime_to_systime(struct pmcs_hw ss,uint32_t fw_hi,uint32_t fw_lo,struct timespec * stime)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
display_ic(struct pmcs_hw m,int verbose)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
pmcs_iport_phy_walk_cb(uintptr_t addr,const void * wdata,void * priv)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
display_iport_damap(dev_info_t * pdip)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
display_iport_di_cb(uintptr_t addr,const void * wdata,void * priv)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
display_iport_pi_cb(uintptr_t addr,const void * wdata,void * priv)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
display_iport_dtc(dev_info_t * pdip)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
display_iport_more(dev_info_t * dip,per_iport_setting_t * pis)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
pmcs_iport_walk_cb(uintptr_t addr,const void * wdata,void * priv)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
display_iport(struct pmcs_hw m,uintptr_t addr,int verbose,per_iport_setting_t * pis)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
pmcs_utarget_walk_cb(uintptr_t addr,const void * wdata,void * priv)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
display_unconfigured_targets(uintptr_t addr)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
display_completion_queue(struct pmcs_hw ss)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
display_event_log(struct pmcs_hw ss)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
display_hwinfo(struct pmcs_hw m,int verbose)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
display_targets(struct pmcs_hw m,int verbose,int totals_only)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 *
work_state_to_string(uint32_t state)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
display_one_work(pmcwork_t * wp,int verbose,int idx)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
display_work(struct pmcs_hw m,int verbose,int wserno)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
print_spcmd(pmcs_cmd_t * sp,void * kaddr,int printhdr,int verbose)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
display_waitqs(struct pmcs_hw m,int verbose)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 *
ibq_type(int qnum)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 *
obq_type(int qnum)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 *
iomb_cat(uint32_t cat)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 *
iomb_event(uint8_t event)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 *
inbound_iomb_opcode(uint32_t opcode)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 *
outbound_iomb_opcode(uint32_t opcode)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
get_devid_from_ob_iomb(struct pmcs_hw ss,uint32_t * qentryp,uint16_t opcode)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
iomb_is_dev_hdl_specific(uint32_t word0,boolean_t inbound)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
dump_one_qentry_outbound(struct pmcs_hw ss,uint32_t * qentryp,int idx,uint64_t devid_filter)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
display_outbound_queues(struct pmcs_hw ss,uint64_t devid_filter,uint_t verbose)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
dump_one_qentry_inbound(uint32_t * qentryp,int idx,uint64_t devid_filter)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
display_inbound_queues(struct pmcs_hw ss,uint64_t devid_filter,uint_t verbose)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
display_phy(struct pmcs_phy phy,struct pmcs_phy * phyp,int verbose,int totals_only)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
display_phys(struct pmcs_hw ss,int verbose,struct pmcs_phy * parent,int level,int totals_only)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
pmcs_dump_tracelog(boolean_t filter,int instance,uint64_t tail_lines,const char * phy_path,uint64_t sas_address,uint64_t verbose)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
targets_walk_i(mdb_walk_state_t * wsp)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
targets_walk_s(mdb_walk_state_t * wsp)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
targets_walk_f(mdb_walk_state_t * wsp)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 *
pmcs_next_sibling(pmcs_phy_t * phyp)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
phy_walk_i(mdb_walk_state_t * wsp)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
phy_walk_s(mdb_walk_state_t * wsp)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
phy_walk_f(mdb_walk_state_t * wsp)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
display_matching_work(struct pmcs_hw ss,uintmax_t index,uintmax_t snum,uintmax_t tag_type)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
pmcs_tag(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pmcs_dump_fwlog(struct pmcs_hw * ss,int instance,const char * ofile)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
pmcs_fwlog(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pmcs_log(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pmcs_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pmcs_help()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
pmcs_log_help()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
pmcs_tag_help()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 *
_mdb_init(void)3279 _mdb_init(void)
3280 {
3281 return (&modinfo);
3282 }
3283