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