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