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  * Copyright (c) 2013 by Delphix. All rights reserved.
27  */
28 
29 #include <mdb/mdb_modapi.h>
30 #include <mdb/mdb_ks.h>
31 #include <mdb/mdb_ctf.h>
32 #include <sys/evtchn_impl.h>
33 
34 #include "intr_common.h"
35 
36 typedef struct mdb_shared_info {
37 	unsigned long evtchn_pending[sizeof (unsigned long) * NBBY];
38 	unsigned long evtchn_mask[sizeof (unsigned long) * NBBY];
39 } mdb_shared_info_t;
40 
41 static mdb_shared_info_t	shared_info;
42 static struct av_head	avec_tbl[NR_IRQS];
43 static uint16_t		shared_tbl[MAX_ISA_IRQ + 1];
44 static irq_info_t	irq_tbl[NR_IRQS];
45 static mec_info_t	virq_tbl[NR_VIRQS];
46 static short		evtchn_tbl[NR_EVENT_CHANNELS];
47 
48 static int
49 update_tables(void)
50 {
51 	uintptr_t shared_info_addr;
52 
53 	if (mdb_readvar(&irq_tbl, "irq_info") == -1) {
54 		mdb_warn("failed to read irq_info");
55 		return (0);
56 	}
57 
58 	if (mdb_readvar(&virq_tbl, "virq_info") == -1) {
59 		mdb_warn("failed to read virq_info");
60 		return (0);
61 	}
62 
63 	if (mdb_readvar(&evtchn_tbl, "evtchn_to_irq") == -1) {
64 		mdb_warn("failed to read evtchn_to_irq");
65 		return (0);
66 	}
67 
68 	if (mdb_readvar(&avec_tbl, "autovect") == -1) {
69 		mdb_warn("failed to read autovect");
70 		return (0);
71 	}
72 
73 	if (mdb_readvar(&shared_tbl, "xen_uppc_irq_shared_table") == -1) {
74 		mdb_warn("failed to read xen_uppc_irq_shared_table");
75 		return (0);
76 	}
77 
78 	if (mdb_readvar(&shared_info_addr, "HYPERVISOR_shared_info") == -1) {
79 		mdb_warn("failed to read HYPERVISOR_shared_info");
80 		return (0);
81 	}
82 
83 	if (mdb_ctf_vread(&shared_info, "shared_info_t", "mdb_shared_info_t",
84 	    shared_info_addr, 0) == -1)
85 		return (0);
86 
87 	return (1);
88 }
89 
90 
91 static char *
92 interrupt_print_bus(uintptr_t dip_addr)
93 {
94 	char		bind_name[MAXPATHLEN + 1];
95 	struct dev_info	dev_info;
96 
97 	if (mdb_vread(&dev_info, sizeof (dev_info), dip_addr) == -1) {
98 		mdb_warn("failed to read child dip");
99 		return ("-");
100 	}
101 
102 	while (dev_info.devi_parent != 0) {
103 		if (mdb_vread(&dev_info, sizeof (dev_info),
104 		    (uintptr_t)dev_info.devi_parent) == -1)
105 			break;
106 
107 		(void) mdb_readstr(bind_name, sizeof (bind_name),
108 		    (uintptr_t)dev_info.devi_binding_name);
109 		if (strcmp(bind_name, "isa") == 0)
110 			return ("ISA");
111 		else if (strcmp(bind_name, "pci") == 0 ||
112 		    strcmp(bind_name, "npe") == 0)
113 			return ("PCI");
114 	}
115 	return ("-");
116 }
117 
118 static const char *
119 virq_type(int irq)
120 {
121 	int i;
122 
123 	for (i = 0; i < NR_VIRQS; i++) {
124 		if (virq_tbl[i].mi_irq == irq)
125 			break;
126 	}
127 
128 	switch (i) {
129 	case VIRQ_TIMER:
130 		return ("virq:timer");
131 	case VIRQ_DEBUG:
132 		return ("virq:debug");
133 	case VIRQ_CONSOLE:
134 		return ("virq:console");
135 	case VIRQ_DOM_EXC:
136 		return ("virq:dom exc");
137 	case VIRQ_DEBUGGER:
138 		return ("virq:debugger");
139 	default:
140 		break;
141 	}
142 
143 	return ("virq:?");
144 }
145 
146 static const char *
147 irq_type(int irq, int extended)
148 {
149 	switch (irq_tbl[irq].ii_type) {
150 	case IRQT_UNBOUND:
151 		return ("unset");
152 	case IRQT_PIRQ:
153 		return ("pirq");
154 	case IRQT_VIRQ:
155 		if (extended)
156 			return (virq_type(irq));
157 		return ("virq");
158 	case IRQT_IPI:
159 		return ("ipi");
160 	case IRQT_EVTCHN:
161 		return ("evtchn");
162 	case IRQT_DEV_EVTCHN:
163 		return ("device");
164 	}
165 
166 	return ("?");
167 }
168 
169 static void
170 print_isr(int i)
171 {
172 	struct autovec avhp;
173 
174 	if (avec_tbl[i].avh_link == NULL)
175 		return;
176 
177 	(void) mdb_vread(&avhp, sizeof (struct autovec),
178 	    (uintptr_t)avec_tbl[i].avh_link);
179 
180 	interrupt_print_isr((uintptr_t)avhp.av_vector,
181 	    (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip);
182 
183 	while (avhp.av_link != NULL &&
184 	    mdb_vread(&avhp, sizeof (struct autovec),
185 	    (uintptr_t)avhp.av_link) != -1) {
186 		mdb_printf(", ");
187 		interrupt_print_isr((uintptr_t)avhp.av_vector,
188 		    (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip);
189 	}
190 }
191 
192 static int
193 evtchn_masked(int i)
194 {
195 	return (TEST_EVTCHN_BIT(i, &shared_info.evtchn_mask[0]) != 0);
196 }
197 
198 static int
199 evtchn_pending(int i)
200 {
201 	return (TEST_EVTCHN_BIT(i, &shared_info.evtchn_pending[0]) != 0);
202 }
203 
204 static void
205 pic_interrupt_dump(int i, struct autovec *avhp, int evtchn)
206 {
207 	if (option_flags & INTR_DISPLAY_INTRSTAT) {
208 		mdb_printf("%-3d ", 0);
209 		print_isr(i);
210 		mdb_printf("\n");
211 		return;
212 	}
213 
214 	mdb_printf("%-3d  0x%2x %-6d %6d/%-2d  %-3s %-6s %-5d ",
215 	    i, i + PIC_VECTBASE, evtchn, avec_tbl[i].avh_lo_pri,
216 	    avec_tbl[i].avh_hi_pri, avhp->av_dip ?
217 	    interrupt_print_bus((uintptr_t)avhp->av_dip) : "-",
218 	    irq_type(i, 0), shared_tbl[i]);
219 
220 	print_isr(i);
221 
222 	mdb_printf("\n");
223 }
224 
225 static void
226 ec_interrupt_dump(int i)
227 {
228 	irq_info_t *irqp = &irq_tbl[i];
229 	struct autovec avhp;
230 	char evtchn[8];
231 
232 	if (irqp->ii_type == IRQT_UNBOUND)
233 		return;
234 
235 	if (option_flags & INTR_DISPLAY_INTRSTAT) {
236 		mdb_printf("%-3d ", 0);
237 		print_isr(i);
238 		mdb_printf("\n");
239 		return;
240 	}
241 
242 
243 	memset(&avhp, 0, sizeof (avhp));
244 	if (avec_tbl[i].avh_link != NULL)
245 		(void) mdb_vread(&avhp, sizeof (struct autovec),
246 		    (uintptr_t)avec_tbl[i].avh_link);
247 
248 	switch (irqp->ii_type) {
249 	case IRQT_EVTCHN:
250 	case IRQT_VIRQ:
251 		if (irqp->ii_u.index == VIRQ_TIMER) {
252 			strcpy(evtchn, "T");
253 		} else {
254 			mdb_snprintf(evtchn, sizeof (evtchn), "%-7d",
255 			    irqp->ii_u.evtchn);
256 		}
257 		break;
258 	case IRQT_IPI:
259 		strcpy(evtchn, "I");
260 		break;
261 	case IRQT_DEV_EVTCHN:
262 		strcpy(evtchn, "D");
263 		break;
264 	}
265 
266 	/* IRQ */
267 	mdb_printf("%3d  ", i);
268 	/* Vector */
269 	mdb_printf("-    ");
270 	/* Evtchn */
271 	mdb_printf("%-7s", evtchn);
272 	/* IPL */
273 	mdb_printf("%6d/%-2d  ", irq_tbl[i].ii_u2.ipl, irq_tbl[i].ii_u2.ipl);
274 	/* Bus */
275 	mdb_printf("%-3s ", avhp.av_dip
276 	    ? interrupt_print_bus((uintptr_t)avhp.av_dip) : "-");
277 	/* Type */
278 	mdb_printf("%-6s ", irq_type(i, 0));
279 	/* Share */
280 	mdb_printf("-     ");
281 
282 	print_isr(i);
283 
284 	mdb_printf("\n");
285 }
286 
287 /*
288  * uppc_interrupt_dump:
289  *	Dump uppc(7d) interrupt information.
290  */
291 /* ARGSUSED */
292 int
293 xen_uppc_interrupt_dump(uintptr_t addr, uint_t flags, int argc,
294     const mdb_arg_t *argv)
295 {
296 	int		i;
297 	boolean_t	found = B_FALSE;
298 	struct autovec	avhp;
299 
300 	option_flags = 0;
301 	if (mdb_getopts(argc, argv,
302 	    'd', MDB_OPT_SETBITS, INTR_DISPLAY_DRVR_INST, &option_flags,
303 	    'i', MDB_OPT_SETBITS, INTR_DISPLAY_INTRSTAT, &option_flags,
304 	    NULL) != argc)
305 		return (DCMD_USAGE);
306 
307 	if (!update_tables())
308 		return (DCMD_ERR);
309 
310 	/*
311 	 * By default, on all x86 systems ::interrupts from xen_uppc(7d) gets
312 	 * loaded first. For APIC systems the ::interrupts from xpv_psm(7d)
313 	 * ought to be executed. Confusion stems as both modules export the
314 	 * same dcmd.
315 	 */
316 	for (i = 0; i < MAX_ISA_IRQ + 1; i++)
317 		if (shared_tbl[i]) {
318 			found = B_TRUE;
319 			break;
320 		}
321 
322 	if (found == B_FALSE) {
323 		if (mdb_lookup_by_obj("xpv_psm", "apic_irq_table",
324 		    NULL) == 0) {
325 			return (mdb_call_dcmd("xpv_psm`interrupts",
326 			    addr, flags, argc, argv));
327 		}
328 	}
329 
330 	/* Print the header first */
331 	if (option_flags & INTR_DISPLAY_INTRSTAT)
332 		mdb_printf("%<u>CPU ");
333 	else
334 		mdb_printf("%<u>IRQ  Vect Evtchn IPL(lo/hi) Bus Type   Share ");
335 	mdb_printf("%s %</u>\n", option_flags & INTR_DISPLAY_DRVR_INST ?
336 	    "Driver Name(s)" : "ISR(s)");
337 
338 	for (i = 0; i < NR_IRQS; i++) {
339 		if (irq_tbl[i].ii_type == IRQT_PIRQ) {
340 			if (irq_tbl[i].ii_u.evtchn == 0)
341 				continue;
342 
343 			/* Read the entry, if invalid continue */
344 			if (mdb_vread(&avhp, sizeof (struct autovec),
345 			    (uintptr_t)avec_tbl[i].avh_link) == -1)
346 				continue;
347 
348 			pic_interrupt_dump(i, &avhp, irq_tbl[i].ii_u.evtchn);
349 			continue;
350 		}
351 
352 		ec_interrupt_dump(i);
353 	}
354 
355 	return (DCMD_OK);
356 }
357 
358 
359 static void
360 evtchn_dump(int i)
361 {
362 	int irq = evtchn_tbl[i];
363 
364 	if (irq == INVALID_IRQ) {
365 		mdb_printf("%-14s%-7d%-4s%-7s", "unassigned", i, "-", "-");
366 		mdb_printf("%-4d", 0);
367 		mdb_printf("%-7d", evtchn_masked(i));
368 		mdb_printf("%-8d", evtchn_pending(i));
369 		mdb_printf("\n");
370 		return;
371 	}
372 
373 	/* Type */
374 	mdb_printf("%-14s", irq_type(irq, 1));
375 	/* Evtchn */
376 	mdb_printf("%-7d", i);
377 	/* IRQ */
378 	mdb_printf("%-4d", irq);
379 	/* IPL */
380 	mdb_printf("%6d/%-2d  ", irq_tbl[irq].ii_u2.ipl,
381 	    irq_tbl[irq].ii_u2.ipl);
382 	/* CPU */
383 	mdb_printf("%-4d", 0);
384 	/* Masked/Pending */
385 	mdb_printf("%-7d", evtchn_masked(i));
386 	mdb_printf("%-8d", evtchn_pending(i));
387 	/* ISR */
388 	print_isr(irq);
389 
390 	mdb_printf("\n");
391 }
392 
393 /* ARGSUSED */
394 static int
395 evtchns_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
396 {
397 	int		i;
398 	boolean_t	found = B_FALSE;
399 
400 	option_flags = 0;
401 	if (mdb_getopts(argc, argv,
402 	    'd', MDB_OPT_SETBITS, INTR_DISPLAY_DRVR_INST, &option_flags,
403 	    NULL) != argc)
404 		return (DCMD_USAGE);
405 
406 	if (!update_tables())
407 		return (DCMD_ERR);
408 
409 	/*
410 	 * By default, on all x86 systems ::evtchns from xen_uppc(7d) gets
411 	 * loaded first. For APIC systems the ::evtchns from xpv_psm(7d)
412 	 * ought to be executed. Confusion stems as both modules export the
413 	 * same dcmd.
414 	 */
415 	for (i = 0; i < MAX_ISA_IRQ + 1; i++)
416 		if (shared_tbl[i]) {
417 			found = B_TRUE;
418 			break;
419 		}
420 
421 	if (found == B_FALSE) {
422 		if (mdb_lookup_by_obj("xpv_psm", "apic_irq_table",
423 		    NULL) == 0) {
424 			return (mdb_call_dcmd("xpv_psm`evtchns",
425 			    addr, flags, argc, argv));
426 		}
427 	}
428 
429 	if (flags & DCMD_ADDRSPEC) {
430 		/*
431 		 * Note: we allow the invalid evtchn 0, as it can help catch if
432 		 * we incorrectly try to configure it.
433 		 */
434 		if ((int)addr >= NR_EVENT_CHANNELS) {
435 			mdb_warn("Invalid event channel %d.\n", (int)addr);
436 			return (DCMD_ERR);
437 		}
438 	}
439 
440 	mdb_printf("%<u>Type          Evtchn IRQ IPL(lo/hi) CPU "
441 	    "Masked Pending ");
442 	mdb_printf("%s %</u>\n", option_flags & INTR_DISPLAY_DRVR_INST ?
443 	    "Driver Name(s)" : "ISR(s)");
444 
445 	if (flags & DCMD_ADDRSPEC) {
446 		evtchn_dump((int)addr);
447 		return (DCMD_OK);
448 	}
449 
450 	for (i = 0; i < NR_EVENT_CHANNELS; i++) {
451 		if (evtchn_tbl[i] == INVALID_IRQ)
452 			continue;
453 
454 		evtchn_dump(i);
455 	}
456 
457 	return (DCMD_OK);
458 }
459 
460 static void
461 evtchns_help(void)
462 {
463 	mdb_printf("Print valid event channels\n"
464 	    "If %<u>addr%</u> is given, interpret it as an evtchn to print "
465 	    "details of.\n"
466 	    "By default, only interrupt service routine names are printed.\n\n"
467 	    "Switches:\n"
468 	    "  -d   instead of ISR, print <driver_name><instance#>\n");
469 }
470 
471 /*
472  * MDB module linkage information:
473  */
474 static const mdb_dcmd_t dcmds[] = {
475 	{ "interrupts", "?[-di]", "print interrupts", xen_uppc_interrupt_dump,
476 	    interrupt_help},
477 	{ "evtchns", "?[-d]", "print event channels", evtchns_dump,
478 	    evtchns_help },
479 	{ "softint", "?[-d]", "print soft interrupts", soft_interrupt_dump,
480 	    soft_interrupt_help},
481 	{ NULL }
482 };
483 
484 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, NULL };
485 
486 const mdb_modinfo_t *
487 _mdb_init(void)
488 {
489 	GElf_Sym	sym;
490 
491 	if (mdb_lookup_by_name("gld_intr", &sym) != -1)
492 		if (GELF_ST_TYPE(sym.st_info) == STT_FUNC)
493 			gld_intr_addr = (uintptr_t)sym.st_value;
494 
495 	return (&modinfo);
496 }
497