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