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) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2018 Joyent, Inc.
24 */
25
26 #include "intr_common.h"
27 #include <sys/gld.h>
28 #include <sys/gldpriv.h>
29
30 int option_flags;
31 uintptr_t gld_intr_addr;
32 int apic_pir_vect;
33 static struct av_head softvec_tbl[LOCK_LEVEL + 1];
34
35 static char *businfo_array[] = {
36 " ",
37 "CBUS",
38 "CBUSII",
39 "EISA",
40 "FUTURE",
41 "INTERN",
42 "ISA",
43 "MBI",
44 "MBII",
45 "PCIe",
46 "MPI",
47 "MPSA",
48 "NUBUS",
49 "PCI",
50 "PCMCIA",
51 "TC",
52 "VL",
53 "VME",
54 "XPRESS",
55 " "
56 };
57
58 void
interrupt_help(void)59 interrupt_help(void)
60 {
61 mdb_printf("Prints the interrupt usage on the system.\n"
62 "By default, only interrupt service routine names are printed.\n\n"
63 "Switches:\n"
64 " -d instead of ISR, print <driver_name><instance#>\n"
65 " -i show like intrstat, cpu# ISR/<driver_name><instance#>\n");
66 }
67
68 void
soft_interrupt_help(void)69 soft_interrupt_help(void)
70 {
71 mdb_printf("Prints the soft interrupt usage on the system.\n"
72 "By default, only interrupt service routine names are printed.\n\n"
73 "Switch:\n"
74 " -d instead of ISR, print <driver_name><instance#>\n");
75 }
76
77 /*
78 * This is copied from avintr.c
79 * NOTE: Ensure that this definition stays in sync
80 */
81 typedef struct av_softinfo {
82 cpuset_t av_pending; /* pending bitmasks */
83 } av_softinfo_t;
84
85 /* ARGSUSED */
86 int
soft_interrupt_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)87 soft_interrupt_dump(uintptr_t addr, uint_t flags, int argc,
88 const mdb_arg_t *argv)
89 {
90 int i;
91 av_softinfo_t avsoftinfo;
92 struct autovec avhp;
93 ddi_softint_hdl_impl_t hdlp;
94
95 option_flags = 0;
96 if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS,
97 INTR_DISPLAY_DRVR_INST, &option_flags, NULL) != argc)
98 return (DCMD_USAGE);
99
100 if (mdb_readvar(&softvec_tbl, "softvect") == -1) {
101 mdb_warn("failed to read autovect");
102 return (DCMD_ERR);
103 }
104
105 /* Print the header first */
106 mdb_printf("%<u>ADDR PEND PIL ARG1 "
107 "ARG2 ISR(s)%</u>\n");
108
109 /* Walk all the entries */
110 for (i = 0; i < LOCK_LEVEL + 1; i++) {
111 /* Read the entry, if invalid continue */
112 if (mdb_vread(&avhp, sizeof (struct autovec),
113 (uintptr_t)softvec_tbl[i].avh_link) == -1)
114 continue;
115
116 do {
117 if (!avhp.av_vector ||
118 (mdb_vread(&hdlp, sizeof (ddi_softint_hdl_impl_t),
119 (uintptr_t)avhp.av_intr_id) == -1) ||
120 (mdb_vread(&avsoftinfo, sizeof (av_softinfo_t),
121 (uintptr_t)hdlp.ih_pending) == -1))
122 continue;
123
124 /* Print each soft interrupt entry */
125 mdb_printf("%-16p %-2d %-2d %-16p %-16p",
126 avhp.av_intr_id, mdb_cpuset_find(
127 (uintptr_t)&avsoftinfo.av_pending) != -1 ? 1 : 0,
128 avhp.av_prilevel, avhp.av_intarg1, avhp.av_intarg2);
129 interrupt_print_isr((uintptr_t)avhp.av_vector,
130 (uintptr_t)avhp.av_intarg1, (uintptr_t)hdlp.ih_dip);
131 mdb_printf("\n");
132 } while (mdb_vread(&avhp, sizeof (struct autovec),
133 (uintptr_t)avhp.av_link) != -1);
134 }
135
136 return (DCMD_OK);
137 }
138
139 void
interrupt_print_isr(uintptr_t vector,uintptr_t arg1,uintptr_t dip)140 interrupt_print_isr(uintptr_t vector, uintptr_t arg1, uintptr_t dip)
141 {
142 uintptr_t isr_addr = vector;
143 struct dev_info dev_info;
144
145 /*
146 * figure out the real ISR function name from gld_intr()
147 */
148 if (isr_addr == gld_intr_addr) {
149 gld_mac_info_t macinfo;
150
151 if (mdb_vread(&macinfo, sizeof (gld_mac_info_t), arg1) != -1) {
152 /* verify gld data structure and get the real ISR */
153 if (macinfo.gldm_GLD_version == GLD_VERSION)
154 isr_addr = (uintptr_t)macinfo.gldm_intr;
155 }
156 }
157
158 if ((option_flags & INTR_DISPLAY_DRVR_INST) && dip) {
159 char drvr_name[MODMAXNAMELEN + 1];
160
161 if (dip && mdb_devinfo2driver(dip, drvr_name,
162 sizeof (drvr_name)) == 0) {
163 (void) mdb_vread(&dev_info, sizeof (dev_info), dip);
164 mdb_printf("%s#%d", drvr_name, dev_info.devi_instance);
165 } else {
166 mdb_printf("%a", isr_addr);
167 }
168
169 } else {
170 mdb_printf("%a", isr_addr);
171 }
172 }
173
174 /*
175 * get_interrupt_type:
176 *
177 * Get some interrupt related useful information
178 *
179 * NOTE: a0 is clock, c0/d0/e0 are x-calls, e1 is apic_error_intr
180 * d1/d3 are cbe_fire interrupts
181 */
182 static char *
get_interrupt_type(short index)183 get_interrupt_type(short index)
184 {
185 if (index == RESERVE_INDEX)
186 return ("IPI");
187 else if (index == ACPI_INDEX)
188 return ("Fixed");
189 else if (index == MSI_INDEX)
190 return ("MSI");
191 else if (index == MSIX_INDEX)
192 return ("MSI-X");
193 else
194 return ("Fixed");
195 }
196
197 static char *
get_apix_interrupt_type(short type)198 get_apix_interrupt_type(short type)
199 {
200 if (type == APIX_TYPE_IPI)
201 return ("IPI");
202 else if (type == APIX_TYPE_FIXED)
203 return ("Fixed");
204 else if (type == APIX_TYPE_MSI)
205 return ("MSI");
206 else if (type == APIX_TYPE_MSIX)
207 return ("MSI-X");
208 else
209 return ("Fixed");
210 }
211
212 void
apic_interrupt_dump(apic_irq_t * irqp,struct av_head * avp,int i,ushort_t * evtchnp,char level)213 apic_interrupt_dump(apic_irq_t *irqp, struct av_head *avp,
214 int i, ushort_t *evtchnp, char level)
215 {
216 int bus_type;
217 int j;
218 char *intr_type;
219 char ioapic_iline[10];
220 char ipl[3];
221 char cpu_assigned[4];
222 char evtchn[8];
223 uint32_t assigned_cpu;
224 struct autovec avhp;
225
226 /* If invalid index; continue */
227 if (!irqp->airq_mps_intr_index ||
228 irqp->airq_mps_intr_index == FREE_INDEX)
229 return;
230
231 /* Figure out interrupt type and trigger information */
232 intr_type = get_interrupt_type(irqp->airq_mps_intr_index);
233
234 /* Figure out IOAPIC number and ILINE number */
235 if (APIC_IS_MSI_OR_MSIX_INDEX(irqp->airq_mps_intr_index))
236 (void) mdb_snprintf(ioapic_iline, 10, "- ");
237 else {
238 if (!irqp->airq_ioapicindex && !irqp->airq_intin_no) {
239 if (strcmp(intr_type, "Fixed") == 0)
240 (void) mdb_snprintf(ioapic_iline, 10,
241 "0x%x/0x%x", irqp->airq_ioapicindex,
242 irqp->airq_intin_no);
243 else if (irqp->airq_mps_intr_index == RESERVE_INDEX)
244 (void) mdb_snprintf(ioapic_iline, 10, "- ");
245 else
246 (void) mdb_snprintf(ioapic_iline, 10, " ");
247 } else
248 (void) mdb_snprintf(ioapic_iline, 10, "0x%x/0x%x",
249 irqp->airq_ioapicindex, irqp->airq_intin_no);
250 }
251
252 evtchn[0] = '\0';
253 if (evtchnp != NULL)
254 (void) mdb_snprintf(evtchn, 8, "%-7hd", *evtchnp);
255
256 assigned_cpu = irqp->airq_temp_cpu;
257 if (assigned_cpu == IRQ_UNINIT || assigned_cpu == IRQ_UNBOUND)
258 assigned_cpu = irqp->airq_cpu;
259 bus_type = irqp->airq_iflag.bustype;
260
261 if (irqp->airq_mps_intr_index == RESERVE_INDEX) {
262 (void) mdb_snprintf(cpu_assigned, 4, "all");
263 (void) mdb_snprintf(ipl, 3, "%d", avp->avh_hi_pri);
264 } else {
265 (void) mdb_snprintf(cpu_assigned, 4, "%d", assigned_cpu);
266 (void) mdb_snprintf(ipl, 3, "%d", irqp->airq_ipl);
267 }
268
269 /* Print each interrupt entry */
270 if (option_flags & INTR_DISPLAY_INTRSTAT)
271 mdb_printf("%-4s", cpu_assigned);
272 else
273 mdb_printf("%-3d 0x%x %s%-3s %-6s %-3s %-6s %-4s%-3d %-9s ",
274 i, irqp->airq_vector, evtchn, ipl,
275 (bus_type ? businfo_array[bus_type] : " "),
276 (level ? "Lvl" : "Edg"),
277 intr_type, cpu_assigned, irqp->airq_share, ioapic_iline);
278
279 /* If valid dip found; print driver name */
280 if (irqp->airq_dip) {
281 (void) mdb_vread(&avhp, sizeof (struct autovec),
282 (uintptr_t)avp->avh_link);
283
284 /*
285 * Loop thru all the shared IRQs
286 */
287 if (irqp->airq_share)
288 interrupt_print_isr((uintptr_t)avhp.av_vector,
289 (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip);
290
291 for (j = 1; irqp->airq_mps_intr_index != FREE_INDEX &&
292 j < irqp->airq_share; j++) {
293 if (mdb_vread(&avhp, sizeof (struct autovec),
294 (uintptr_t)avhp.av_link) != -1) {
295 mdb_printf(", ");
296 interrupt_print_isr((uintptr_t)avhp.av_vector,
297 (uintptr_t)avhp.av_intarg1,
298 (uintptr_t)avhp.av_dip);
299 } else {
300 break;
301 }
302 }
303
304 } else {
305 if (irqp->airq_mps_intr_index == RESERVE_INDEX &&
306 !irqp->airq_share) {
307 if (irqp->airq_vector == apic_pir_vect) {
308 mdb_printf("pir_ipi");
309 } else {
310 mdb_printf("poke_cpu");
311 }
312 } else if (mdb_vread(&avhp, sizeof (struct autovec),
313 (uintptr_t)avp->avh_link) != -1) {
314 mdb_printf("%a", avhp.av_vector);
315 }
316 }
317 mdb_printf("\n");
318 }
319
320 void
apix_interrupt_dump(apix_vector_t * vectp,apic_irq_t * irqp,struct autovec * avp,ushort_t * evtchnp,char level)321 apix_interrupt_dump(apix_vector_t *vectp, apic_irq_t *irqp,
322 struct autovec *avp, ushort_t *evtchnp, char level)
323 {
324 int j;
325 int bus_type;
326 char *intr_type;
327 char irq[4];
328 char ioapic_iline[10];
329 char ipl[3];
330 char cpu_assigned[4];
331 char cpu_vector[10];
332 char evtchn[8];
333
334
335 /* If invalid vector state; continue */
336 if (vectp->v_state == APIX_STATE_FREED ||
337 vectp->v_state == APIX_STATE_OBSOLETED)
338 return;
339
340 /* use apic_interrupt_ipi_dump for IPIs */
341 if (vectp->v_type == APIX_TYPE_IPI)
342 return;
343
344 /* Figure out interrupt type and trigger information */
345 intr_type = get_apix_interrupt_type(vectp->v_type);
346
347 /* Figure out IOAPIC number and ILINE number */
348 if (vectp->v_type != APIX_TYPE_FIXED) {
349 level = 0; /* MSI/MSI-X are Edge trigger */
350 (void) mdb_snprintf(irq, 4, "- ");
351 (void) mdb_snprintf(ioapic_iline, 10, "- ");
352 if (vectp->v_type == APIX_TYPE_IPI)
353 bus_type = BUSTYPE_NONE;
354 else
355 /* statically assign MSI/X with "PCI" */
356 bus_type = BUSTYPE_PCI;
357 } else {
358 (void) mdb_snprintf(irq, 4, "%d", vectp->v_inum);
359 bus_type = irqp->airq_iflag.bustype;
360 if (!irqp->airq_ioapicindex && !irqp->airq_intin_no) {
361 if (strcmp(intr_type, "Fixed") == 0)
362 (void) mdb_snprintf(ioapic_iline, 10,
363 "0x%x/0x%x", irqp->airq_ioapicindex,
364 irqp->airq_intin_no);
365 else
366 (void) mdb_snprintf(ioapic_iline, 10, "- ");
367 } else
368 (void) mdb_snprintf(ioapic_iline, 10, "0x%x/0x%x",
369 irqp->airq_ioapicindex, irqp->airq_intin_no);
370 }
371
372 evtchn[0] = '\0';
373 if (evtchnp != NULL)
374 (void) mdb_snprintf(evtchn, 8, "%-7hd", *evtchnp);
375
376 (void) mdb_snprintf(cpu_assigned, 4, "%d", vectp->v_cpuid);
377 (void) mdb_snprintf(cpu_vector, 10, "%d/0x%x",
378 vectp->v_cpuid, vectp->v_vector);
379
380 /* Loop all the shared vectors */
381 for (j = 0; j < vectp->v_share; ) {
382 /* shared interrupts with one or more ISR removed afterwards */
383 if (avp->av_vector == NULL) {
384 if (mdb_vread(avp, sizeof (struct autovec),
385 (uintptr_t)avp->av_link) == -1)
386 break;
387 else
388 continue;
389 }
390
391 (void) mdb_snprintf(ipl, 3, "%d", avp->av_prilevel);
392 /* Print each interrupt entry */
393 if (option_flags & INTR_DISPLAY_INTRSTAT)
394 mdb_printf("%-4s", cpu_assigned);
395 else
396 mdb_printf("%-9s %-3s %s%-3s %-6s %-3s %-6s %-3d "
397 "%-9s ", cpu_vector, irq, evtchn, ipl,
398 (bus_type ? businfo_array[bus_type] : "-"),
399 (level ? "Lvl" : "Edg"),
400 intr_type, vectp->v_share, ioapic_iline);
401
402 interrupt_print_isr((uintptr_t)avp->av_vector,
403 (uintptr_t)avp->av_intarg1, (uintptr_t)avp->av_dip);
404 mdb_printf("\n");
405
406 if (++j == vectp->v_share)
407 break; /* done */
408
409 if (mdb_vread(avp, sizeof (struct autovec),
410 (uintptr_t)avp->av_link) == -1)
411 break;
412 }
413 }
414
415 void
apix_interrupt_ipi_dump(apix_vector_t * vectp,struct autovec * avp,ushort_t * evtchnp)416 apix_interrupt_ipi_dump(apix_vector_t *vectp, struct autovec *avp,
417 ushort_t *evtchnp)
418 {
419 char *intr_type = "IPI";
420 char ioapic_iline[10];
421 char ipl[3];
422 char cpu_assigned[4];
423 char cpu_vector[10];
424 char evtchn[8];
425
426 /* If invalid vector state; continue */
427 if (vectp->v_state == APIX_STATE_FREED ||
428 vectp->v_state == APIX_STATE_OBSOLETED)
429 return;
430
431 if (vectp->v_type != APIX_TYPE_IPI)
432 return;
433
434 /* No IOAPIC number and ILINE number info */
435 (void) mdb_snprintf(ioapic_iline, 10, "- ");
436
437 evtchn[0] = '\0';
438 if (evtchnp != NULL)
439 (void) mdb_snprintf(evtchn, 8, "%-7hd", *evtchnp);
440
441 /* IPI targeted ALL cpus */
442 mdb_snprintf(cpu_assigned, 4, "all");
443 (void) mdb_snprintf(cpu_vector, 10, "%s/0x%x",
444 "all", vectp->v_vector);
445 /* IPI is not shared interrupt, so we can get the IPL from v_pri */
446 (void) mdb_snprintf(ipl, 3, "%d", vectp->v_pri);
447
448 /* Print each interrupt entry */
449 if (option_flags & INTR_DISPLAY_INTRSTAT)
450 mdb_printf("%-4s", cpu_assigned);
451 else
452 mdb_printf("%-9s %-3s %s%-3s %-6s %-3s %-6s %-3d %-9s ",
453 cpu_vector, "- ", evtchn, ipl, "- ", "Edg",
454 intr_type, vectp->v_share, ioapic_iline);
455 if (!vectp->v_share) {
456 if (vectp->v_vector == apic_pir_vect) {
457 mdb_printf("pir_ipi");
458 } else {
459 mdb_printf("poke_cpu");
460 }
461 } else {
462 mdb_printf("%a", avp->av_vector);
463 }
464
465 mdb_printf("\n");
466 }
467