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
31int		option_flags;
32uintptr_t	gld_intr_addr;
33int		apic_pir_vect;
34static struct av_head softvec_tbl[LOCK_LEVEL + 1];
35
36static 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
59void
60interrupt_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
69void
70soft_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 */
82typedef struct av_softinfo {
83	cpuset_t	av_pending;	/* pending bitmasks */
84} av_softinfo_t;
85
86/* ARGSUSED */
87int
88soft_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
140void
141interrupt_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 */
183static char *
184get_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
198static char *
199get_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
213void
214apic_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
321void
322apix_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
416void
417apix_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