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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include "intr_common.h"
29 #include <sys/multidata.h>
30 #include <sys/gld.h>
31 #include <sys/gldpriv.h>
32 
33 int		option_flags;
34 uintptr_t	gld_intr_addr;
35 static struct av_head softvec_tbl[LOCK_LEVEL + 1];
36 
37 static char *businfo_array[] = {
38 	" ",
39 	"CBUS",
40 	"CBUSII",
41 	"EISA",
42 	"FUTURE",
43 	"INTERN",
44 	"ISA",
45 	"MBI",
46 	"MBII",
47 	"PCIe",
48 	"MPI",
49 	"MPSA",
50 	"NUBUS",
51 	"PCI",
52 	"PCMCIA",
53 	"TC",
54 	"VL",
55 	"VME",
56 	"XPRESS",
57 	" "
58 };
59 
60 void
61 interrupt_help(void)
62 {
63 	mdb_printf("Prints the interrupt usage on the system.\n"
64 	    "By default, only interrupt service routine names are printed.\n\n"
65 	    "Switches:\n"
66 	    "  -d   instead of ISR, print <driver_name><instance#>\n"
67 	    "  -i   show like intrstat, cpu# ISR/<driver_name><instance#>\n");
68 }
69 
70 void
71 soft_interrupt_help(void)
72 {
73 	mdb_printf("Prints the soft interrupt usage on the system.\n"
74 	    "By default, only interrupt service routine names are printed.\n\n"
75 	    "Switch:\n"
76 	    "  -d   instead of ISR, print <driver_name><instance#>\n");
77 }
78 
79 /*
80  * This is copied from avintr.c
81  * NOTE: Ensure that this definition stays in sync
82  */
83 typedef struct av_softinfo {
84 	cpuset_t	av_pending;	/* pending bitmasks */
85 } av_softinfo_t;
86 
87 /* ARGSUSED */
88 int
89 soft_interrupt_dump(uintptr_t addr, uint_t flags, int argc,
90     const mdb_arg_t *argv)
91 {
92 	int			i;
93 	av_softinfo_t		avsoftinfo;
94 	struct autovec		avhp;
95 	ddi_softint_hdl_impl_t	hdlp;
96 
97 	option_flags = 0;
98 	if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS,
99 	    INTR_DISPLAY_DRVR_INST, &option_flags, NULL) != argc)
100 		return (DCMD_USAGE);
101 
102 	if (mdb_readvar(&softvec_tbl, "softvect") == -1) {
103 		mdb_warn("failed to read autovect");
104 		return (DCMD_ERR);
105 	}
106 
107 	/* Print the header first */
108 	mdb_printf("%<u>ADDR             PEND PIL ARG1             "
109 	    "ARG2            ISR(s)%</u>\n");
110 
111 	/* Walk all the entries */
112 	for (i = 0; i < LOCK_LEVEL + 1; i++) {
113 		/* Read the entry, if invalid continue */
114 		if (mdb_vread(&avhp, sizeof (struct autovec),
115 		    (uintptr_t)softvec_tbl[i].avh_link) == -1)
116 			continue;
117 
118 		do {
119 			if (!avhp.av_vector ||
120 			    (mdb_vread(&hdlp, sizeof (ddi_softint_hdl_impl_t),
121 			    (uintptr_t)avhp.av_intr_id) == -1) ||
122 			    (mdb_vread(&avsoftinfo, sizeof (av_softinfo_t),
123 			    (uintptr_t)hdlp.ih_pending) == -1))
124 				continue;
125 
126 			/* Print each soft interrupt entry */
127 			mdb_printf("%-16p %-2d   %-2d  %-16p %-16p",
128 			    avhp.av_intr_id, mdb_cpuset_find(
129 			    (uintptr_t)&avsoftinfo.av_pending) != -1 ? 1 : 0,
130 			    avhp.av_prilevel, avhp.av_intarg1, avhp.av_intarg2);
131 			interrupt_print_isr((uintptr_t)avhp.av_vector,
132 			    (uintptr_t)avhp.av_intarg1, (uintptr_t)hdlp.ih_dip);
133 			mdb_printf("\n");
134 		} while (mdb_vread(&avhp, sizeof (struct autovec),
135 		    (uintptr_t)avhp.av_link) != -1);
136 	}
137 
138 	return (DCMD_OK);
139 }
140 
141 void
142 interrupt_print_isr(uintptr_t vector, uintptr_t arg1, uintptr_t dip)
143 {
144 	uintptr_t	isr_addr = vector;
145 	struct dev_info	dev_info;
146 
147 	/*
148 	 * figure out the real ISR function name from gld_intr()
149 	 */
150 	if (isr_addr == gld_intr_addr) {
151 		gld_mac_info_t 	macinfo;
152 
153 		if (mdb_vread(&macinfo, sizeof (gld_mac_info_t), arg1) != -1) {
154 			/* verify gld data structure and get the real ISR */
155 			if (macinfo.gldm_GLD_version == GLD_VERSION)
156 				isr_addr = (uintptr_t)macinfo.gldm_intr;
157 		}
158 	}
159 
160 	if ((option_flags & INTR_DISPLAY_DRVR_INST) && dip) {
161 		char drvr_name[MODMAXNAMELEN + 1];
162 
163 		if (dip && mdb_devinfo2driver(dip, drvr_name,
164 		    sizeof (drvr_name)) == 0) {
165 			(void) mdb_vread(&dev_info, sizeof (dev_info), dip);
166 			mdb_printf("%s#%d", drvr_name, dev_info.devi_instance);
167 		} else {
168 			mdb_printf("%a", isr_addr);
169 		}
170 
171 	} else {
172 		mdb_printf("%a", isr_addr);
173 	}
174 }
175 
176 /*
177  * get_interrupt_type:
178  *
179  *	Get some interrupt related useful information
180  *
181  *	NOTE: a0 is clock, c0/d0/e0 are x-calls, e1 is apic_error_intr
182  *	d1/d3 are cbe_fire interrupts
183  */
184 static char *
185 get_interrupt_type(short index)
186 {
187 	if (index == RESERVE_INDEX)
188 		return ("IPI");
189 	else if (index == ACPI_INDEX)
190 		return ("Fixed");
191 	else if (index == MSI_INDEX)
192 		return ("MSI");
193 	else if (index == MSIX_INDEX)
194 		return ("MSI-X");
195 	else
196 		return ("Fixed");
197 }
198 
199 void
200 apic_interrupt_dump(apic_irq_t *irqp, struct av_head *avp,
201     int i, ushort_t *evtchnp, char level)
202 {
203 	int		bus_type;
204 	int		j;
205 	char		*intr_type;
206 	char		ioapic_iline[10];
207 	char		ipl[3];
208 	char		cpu_assigned[4];
209 	char		evtchn[8];
210 	uint32_t	assigned_cpu;
211 	struct autovec	avhp;
212 
213 	/* If invalid index; continue */
214 	if (!irqp->airq_mps_intr_index ||
215 	    irqp->airq_mps_intr_index == FREE_INDEX)
216 		return;
217 
218 	/* Figure out interrupt type and trigger information */
219 	intr_type = get_interrupt_type(irqp->airq_mps_intr_index);
220 
221 	/* Figure out IOAPIC number and ILINE number */
222 	if (APIC_IS_MSI_OR_MSIX_INDEX(irqp->airq_mps_intr_index))
223 		(void) mdb_snprintf(ioapic_iline, 10, "-    ");
224 	else {
225 		if (!irqp->airq_ioapicindex && !irqp->airq_intin_no) {
226 			if (strcmp(intr_type, "Fixed") == 0)
227 				(void) mdb_snprintf(ioapic_iline, 10,
228 				    "0x%x/0x%x", irqp->airq_ioapicindex,
229 				    irqp->airq_intin_no);
230 			else if (irqp->airq_mps_intr_index == RESERVE_INDEX)
231 				(void) mdb_snprintf(ioapic_iline, 10, "-    ");
232 			else
233 				(void) mdb_snprintf(ioapic_iline, 10, " ");
234 		} else
235 			(void) mdb_snprintf(ioapic_iline, 10, "0x%x/0x%x",
236 			    irqp->airq_ioapicindex, irqp->airq_intin_no);
237 	}
238 
239 	evtchn[0] = '\0';
240 	if (evtchnp != NULL)
241 		(void) mdb_snprintf(evtchn, 8, "%-7hd", *evtchnp);
242 
243 	assigned_cpu = irqp->airq_temp_cpu;
244 	if (assigned_cpu == IRQ_UNINIT || assigned_cpu == IRQ_UNBOUND)
245 		assigned_cpu = irqp->airq_cpu;
246 	bus_type = irqp->airq_iflag.bustype;
247 
248 	if (irqp->airq_mps_intr_index == RESERVE_INDEX) {
249 		(void) mdb_snprintf(cpu_assigned, 4, "all");
250 		(void) mdb_snprintf(ipl, 3, "%d", avp->avh_hi_pri);
251 	} else {
252 		(void) mdb_snprintf(cpu_assigned, 4, "%d", assigned_cpu);
253 		(void) mdb_snprintf(ipl, 3, "%d", irqp->airq_ipl);
254 	}
255 
256 	/* Print each interrupt entry */
257 	if (option_flags & INTR_DISPLAY_INTRSTAT)
258 		mdb_printf("%-4s", cpu_assigned);
259 	else
260 		mdb_printf("%-3d  0x%x %s%-3s %-6s %-3s %-6s %-4s%-3d   %-9s ",
261 		    i, irqp->airq_vector, evtchn, ipl,
262 		    (bus_type ? businfo_array[bus_type] : " "),
263 		    (level ? "Lvl" : "Edg"),
264 		    intr_type, cpu_assigned, irqp->airq_share, ioapic_iline);
265 
266 	/* If valid dip found; print driver name */
267 	if (irqp->airq_dip) {
268 		(void) mdb_vread(&avhp, sizeof (struct autovec),
269 		    (uintptr_t)avp->avh_link);
270 
271 		/*
272 		 * Loop thru all the shared IRQs
273 		 */
274 		if (irqp->airq_share)
275 			interrupt_print_isr((uintptr_t)avhp.av_vector,
276 			    (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip);
277 
278 		for (j = 1; irqp->airq_mps_intr_index != FREE_INDEX &&
279 		    j < irqp->airq_share; j++) {
280 			if (mdb_vread(&avhp, sizeof (struct autovec),
281 			    (uintptr_t)avhp.av_link) != -1) {
282 				mdb_printf(", ");
283 				interrupt_print_isr((uintptr_t)avhp.av_vector,
284 				    (uintptr_t)avhp.av_intarg1,
285 				    (uintptr_t)avhp.av_dip);
286 			} else {
287 				break;
288 			}
289 		}
290 
291 	} else {
292 		if (irqp->airq_mps_intr_index == RESERVE_INDEX &&
293 		    !irqp->airq_share)
294 			mdb_printf("poke_cpu");
295 		else if (mdb_vread(&avhp, sizeof (struct autovec),
296 		    (uintptr_t)avp->avh_link) != -1)
297 			mdb_printf("%a", avhp.av_vector);
298 	}
299 	mdb_printf("\n");
300 }
301