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