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 2009 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 #include <errno.h>
34 #include <sys/xc_levels.h>
35 
36 #include "intr_common.h"
37 
38 typedef struct mdb_shared_info {
39 	unsigned long evtchn_pending[sizeof (unsigned long) * NBBY];
40 	unsigned long evtchn_mask[sizeof (unsigned long) * NBBY];
41 } mdb_shared_info_t;
42 
43 static mdb_shared_info_t shared_info;
44 static int have_shared_info;
45 static uintptr_t evtchn_cpus_addr;
46 static struct av_head avec_tbl[NR_IRQS];
47 static irq_info_t irq_tbl[NR_IRQS];
48 static mec_info_t ipi_tbl[MAXIPL];
49 static mec_info_t virq_tbl[NR_VIRQS];
50 static short evtchn_tbl[NR_EVENT_CHANNELS];
51 static apic_irq_t *apic_irq_tbl[APIC_MAX_VECTOR+1];
52 static char level_tbl[APIC_MAX_VECTOR+1];
53 
54 static int
update_tables(void)55 update_tables(void)
56 {
57 	GElf_Sym sym;
58 	uintptr_t shared_info_addr;
59 
60 	if (mdb_readvar(&irq_tbl, "irq_info") == -1) {
61 		mdb_warn("failed to read irq_info");
62 		return (0);
63 	}
64 
65 	if (mdb_readvar(&ipi_tbl, "ipi_info") == -1) {
66 		mdb_warn("failed to read ipi_info");
67 		return (0);
68 	}
69 
70 	if (mdb_readvar(&avec_tbl, "autovect") == -1) {
71 		mdb_warn("failed to read autovect");
72 		return (0);
73 	}
74 
75 	if (mdb_readvar(&irq_tbl, "irq_info") == -1) {
76 		mdb_warn("failed to read irq_info");
77 		return (0);
78 	}
79 
80 	if (mdb_readvar(&ipi_tbl, "ipi_info") == -1) {
81 		mdb_warn("failed to read ipi_info");
82 		return (0);
83 	}
84 
85 	if (mdb_readvar(&virq_tbl, "virq_info") == -1) {
86 		mdb_warn("failed to read virq_info");
87 		return (0);
88 	}
89 
90 	if (mdb_readvar(&evtchn_tbl, "evtchn_to_irq") == -1) {
91 		mdb_warn("failed to read evtchn_to_irq");
92 		return (0);
93 	}
94 
95 	if (mdb_readvar(&apic_irq_tbl, "apic_irq_table") == -1) {
96 		mdb_warn("failed to read apic_irq_table");
97 		return (0);
98 	}
99 
100 	if (mdb_readvar(&level_tbl, "apic_level_intr") == -1) {
101 		mdb_warn("failed to read apic_level_intr");
102 		return (0);
103 	}
104 
105 	if (mdb_lookup_by_name("evtchn_cpus", &sym) == -1) {
106 		mdb_warn("failed to lookup evtchn_cpus");
107 		return (0);
108 	}
109 
110 	evtchn_cpus_addr = sym.st_value;
111 
112 	if (mdb_readvar(&shared_info_addr, "HYPERVISOR_shared_info") == -1) {
113 		mdb_warn("failed to read HYPERVISOR_shared_info");
114 		return (0);
115 	}
116 
117 	/*
118 	 * It's normal for this to fail with a domain dump.
119 	 */
120 	if (mdb_ctf_vread(&shared_info, "shared_info_t", "mdb_shared_info_t",
121 	    shared_info_addr, MDB_CTF_VREAD_QUIET) != -1)
122 		have_shared_info = 1;
123 
124 	return (1);
125 }
126 
127 static const char *
virq_type(int irq)128 virq_type(int irq)
129 {
130 	int i;
131 
132 	for (i = 0; i < NR_VIRQS; i++) {
133 		if (virq_tbl[i].mi_irq == irq)
134 			break;
135 	}
136 
137 	switch (i) {
138 	case VIRQ_TIMER:
139 		return ("virq:timer");
140 	case VIRQ_DEBUG:
141 		return ("virq:debug");
142 	case VIRQ_CONSOLE:
143 		return ("virq:console");
144 	case VIRQ_DOM_EXC:
145 		return ("virq:dom exc");
146 	case VIRQ_DEBUGGER:
147 		return ("virq:debugger");
148 	case VIRQ_MCA:
149 		return ("virq:mca");
150 	default:
151 		break;
152 	}
153 
154 	return ("virq:?");
155 }
156 
157 static const char *
irq_type(int irq,int extended)158 irq_type(int irq, int extended)
159 {
160 	switch (irq_tbl[irq].ii_type) {
161 	case IRQT_UNBOUND:
162 		return ("unset");
163 	case IRQT_PIRQ:
164 		return ("pirq");
165 	case IRQT_VIRQ:
166 		if (extended)
167 			return (virq_type(irq));
168 		return ("virq");
169 	case IRQT_IPI:
170 		return ("ipi");
171 	case IRQT_EVTCHN:
172 		return ("evtchn");
173 	case IRQT_DEV_EVTCHN:
174 		return ("device");
175 	}
176 
177 	return ("?");
178 }
179 
180 /*
181  * We need a non-trivial IPL lookup as the CPU poke's IRQ doesn't have ii_ipl
182  * set -- see evtchn.h.
183  */
184 static int
irq_ipl(int irq)185 irq_ipl(int irq)
186 {
187 	int i;
188 
189 	if (irq_tbl[irq].ii_u2.ipl != 0)
190 		return (irq_tbl[irq].ii_u2.ipl);
191 
192 	for (i = 0; i < MAXIPL; i++) {
193 		if (ipi_tbl[i].mi_irq == irq) {
194 			return (i);
195 		}
196 	}
197 
198 	return (0);
199 }
200 
201 static void
print_cpu(irq_info_t * irqp,int evtchn)202 print_cpu(irq_info_t *irqp, int evtchn)
203 {
204 	size_t cpuset_size = BT_BITOUL(NCPU) * sizeof (ulong_t);
205 	int cpu;
206 
207 	if (irqp != NULL) {
208 		switch (irqp->ii_type) {
209 		case IRQT_VIRQ:
210 		case IRQT_IPI:
211 			mdb_printf("all ");
212 			return;
213 
214 		case IRQT_DEV_EVTCHN:
215 			mdb_printf("0   ");
216 			return;
217 
218 		default:
219 			break;
220 		}
221 	}
222 
223 	if (evtchn >= NR_EVENT_CHANNELS || evtchn == 0) {
224 		mdb_printf("-   ");
225 		return;
226 	}
227 
228 	cpu = mdb_cpuset_find(evtchn_cpus_addr +
229 	    (cpuset_size * evtchn));
230 
231 	/*
232 	 * XXPV: we should verify this against the CPU's mask and show
233 	 * something if they don't match.
234 	 */
235 	mdb_printf("%-4d", cpu);
236 }
237 
238 static void
print_isr(int i)239 print_isr(int i)
240 {
241 	if (avec_tbl[i].avh_link != NULL) {
242 		struct autovec avhp;
243 
244 		(void) mdb_vread(&avhp, sizeof (struct autovec),
245 		    (uintptr_t)avec_tbl[i].avh_link);
246 
247 		interrupt_print_isr((uintptr_t)avhp.av_vector,
248 		    (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip);
249 	} else if (irq_ipl(i) == XC_CPUPOKE_PIL) {
250 		mdb_printf("poke_cpu");
251 	}
252 }
253 
254 static int
evtchn_masked(int i)255 evtchn_masked(int i)
256 {
257 	return (!!TEST_EVTCHN_BIT(i, &shared_info.evtchn_mask[0]));
258 }
259 
260 static int
evtchn_pending(int i)261 evtchn_pending(int i)
262 {
263 	return (!!TEST_EVTCHN_BIT(i, &shared_info.evtchn_pending[0]));
264 }
265 
266 typedef struct mdb_xpv_psm_autovec {
267 	dev_info_t *av_dip;
268 } mdb_xpv_psm_autovec_t;
269 
270 static void
print_bus(int irq)271 print_bus(int irq)
272 {
273 	char parent[7];
274 	uintptr_t dip_addr;
275 	struct dev_info	dev_info;
276 	mdb_xpv_psm_autovec_t avhp;
277 
278 	if (mdb_ctf_vread(&avhp, "struct autovec", "mdb_xpv_psm_autovec_t",
279 	    (uintptr_t)avec_tbl[irq].avh_link, MDB_CTF_VREAD_QUIET) == -1)
280 		goto fail;
281 
282 	dip_addr = (uintptr_t)avhp.av_dip;
283 
284 	if (dip_addr == 0)
285 		goto fail;
286 
287 	/*
288 	 * Sigh.  As a result of the perennial confusion of how you do opaque
289 	 * handles, dev_info_t has a funny old type, which means we can't use
290 	 * mdb_ctf_vread() here.
291 	 */
292 
293 	if (mdb_vread(&dev_info, sizeof (struct dev_info), dip_addr) == -1)
294 		goto fail;
295 
296 	dip_addr = (uintptr_t)dev_info.devi_parent;
297 
298 	if (mdb_vread(&dev_info, sizeof (struct dev_info), dip_addr) == -1)
299 		goto fail;
300 
301 	if (mdb_readstr(parent, 7, (uintptr_t)dev_info.devi_node_name) == -1)
302 		goto fail;
303 
304 	mdb_printf("%-6s ", parent);
305 	return;
306 
307 fail:
308 	mdb_printf("-      ");
309 }
310 
311 static void
ec_interrupt_dump(int i)312 ec_interrupt_dump(int i)
313 {
314 	irq_info_t *irqp = &irq_tbl[i];
315 	char evtchn[8];
316 
317 	if (irqp->ii_type == IRQT_UNBOUND)
318 		return;
319 
320 	if (option_flags & INTR_DISPLAY_INTRSTAT) {
321 		print_cpu(irqp, irqp->ii_u.evtchn);
322 		print_isr(i);
323 		mdb_printf("\n");
324 		return;
325 	}
326 
327 	switch (irqp->ii_type) {
328 	case IRQT_EVTCHN:
329 	case IRQT_VIRQ:
330 		if (irqp->ii_u.index == VIRQ_TIMER) {
331 			strcpy(evtchn, "T");
332 		} else {
333 			mdb_snprintf(evtchn, sizeof (evtchn), "%-7d",
334 			    irqp->ii_u.evtchn);
335 		}
336 		break;
337 	case IRQT_IPI:
338 		strcpy(evtchn, "I");
339 		break;
340 	case IRQT_DEV_EVTCHN:
341 		strcpy(evtchn, "D");
342 		break;
343 	}
344 
345 	/* IRQ */
346 	mdb_printf("%3d  ", i);
347 	/* Vector */
348 	mdb_printf("-    ");
349 	/* Evtchn */
350 	mdb_printf("%-7s", evtchn);
351 	/* IPL */
352 	mdb_printf("%-4d", irq_ipl(i));
353 	/* Bus */
354 	print_bus(i);
355 	/* Trigger */
356 	mdb_printf("%-4s", "Edg");
357 	/* Type */
358 	mdb_printf("%-7s", irq_type(i, 0));
359 	/* CPU */
360 	print_cpu(irqp, irqp->ii_u.evtchn);
361 	/* Share */
362 	mdb_printf("-     ");
363 	/* APIC/INT# */
364 	mdb_printf("-         ");
365 
366 	print_isr(i);
367 
368 	mdb_printf("\n");
369 }
370 
371 /* ARGSUSED */
372 static int
interrupts_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)373 interrupts_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
374 {
375 	int i;
376 
377 	option_flags = 0;
378 	if (mdb_getopts(argc, argv,
379 	    'd', MDB_OPT_SETBITS, INTR_DISPLAY_DRVR_INST, &option_flags,
380 	    'i', MDB_OPT_SETBITS, INTR_DISPLAY_INTRSTAT, &option_flags,
381 	    NULL) != argc)
382 		return (DCMD_USAGE);
383 
384 	if (!update_tables())
385 		return (DCMD_ERR);
386 
387 	if (option_flags & INTR_DISPLAY_INTRSTAT) {
388 		mdb_printf("%<u>CPU ");
389 	} else {
390 		mdb_printf("%<u>IRQ  Vect Evtchn IPL Bus    Trg Type   "
391 		    "CPU Share APIC/INT# ");
392 	}
393 	mdb_printf("%s %</u>\n", option_flags & INTR_DISPLAY_DRVR_INST ?
394 	    "Driver Name(s)" : "ISR(s)");
395 
396 	for (i = 0; i < NR_IRQS; i++) {
397 		if (irq_tbl[i].ii_type == IRQT_PIRQ) {
398 			apic_irq_t airq;
399 
400 			if (irq_tbl[i].ii_u.evtchn == 0)
401 				continue;
402 
403 			if (mdb_vread(&airq, sizeof (apic_irq_t),
404 			    (uintptr_t)apic_irq_tbl[i]) == -1)
405 				continue;
406 
407 			apic_interrupt_dump(&airq, &avec_tbl[i], i,
408 			    &irq_tbl[i].ii_u.evtchn, level_tbl[i]);
409 			continue;
410 		}
411 
412 		ec_interrupt_dump(i);
413 	}
414 
415 	return (DCMD_OK);
416 }
417 
418 static void
evtchn_dump(int i)419 evtchn_dump(int i)
420 {
421 	int irq = evtchn_tbl[i];
422 
423 	if (irq == INVALID_IRQ) {
424 		mdb_printf("%-14s%-7d%-4s%-4s", "unassigned", i, "-", "-");
425 		print_cpu(NULL, i);
426 		if (have_shared_info) {
427 			mdb_printf("%-7d", evtchn_masked(i));
428 			mdb_printf("%-8d", evtchn_pending(i));
429 		}
430 		mdb_printf("\n");
431 		return;
432 	}
433 
434 	/* Type */
435 	mdb_printf("%-14s", irq_type(irq, 1));
436 	/* Evtchn */
437 	mdb_printf("%-7d", i);
438 	/* IRQ */
439 	mdb_printf("%-4d", irq);
440 	/* IPL */
441 	mdb_printf("%-4d", irq_ipl(irq));
442 	/* CPU */
443 	print_cpu(NULL, i);
444 	if (have_shared_info) {
445 		/* Masked/Pending */
446 		mdb_printf("%-7d", evtchn_masked(i));
447 		mdb_printf("%-8d", evtchn_pending(i));
448 	}
449 	/* ISR */
450 	print_isr(irq);
451 
452 	mdb_printf("\n");
453 }
454 
455 /* ARGSUSED */
456 static int
evtchns_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)457 evtchns_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
458 {
459 	int i;
460 
461 	option_flags = 0;
462 	if (mdb_getopts(argc, argv,
463 	    'd', MDB_OPT_SETBITS, INTR_DISPLAY_DRVR_INST, &option_flags,
464 	    NULL) != argc)
465 		return (DCMD_USAGE);
466 
467 	if (!update_tables())
468 		return (DCMD_ERR);
469 
470 	if (flags & DCMD_ADDRSPEC) {
471 		/*
472 		 * Note: we allow the invalid evtchn 0, as it can help catch if
473 		 * we incorrectly try to configure it.
474 		 */
475 		if ((int)addr >= NR_EVENT_CHANNELS) {
476 			mdb_warn("Invalid event channel %d.\n", (int)addr);
477 			return (DCMD_ERR);
478 		}
479 	}
480 
481 	mdb_printf("%<u>Type          Evtchn IRQ IPL CPU ");
482 	if (have_shared_info)
483 		mdb_printf("Masked Pending ");
484 
485 	mdb_printf("%s %</u>\n", option_flags & INTR_DISPLAY_DRVR_INST ?
486 	    "Driver Name(s)" : "ISR(s)");
487 
488 	if (flags & DCMD_ADDRSPEC) {
489 		evtchn_dump((int)addr);
490 		return (DCMD_OK);
491 	}
492 
493 	for (i = 0; i < NR_EVENT_CHANNELS; i++) {
494 		if (evtchn_tbl[i] == INVALID_IRQ)
495 			continue;
496 
497 		evtchn_dump(i);
498 	}
499 
500 	return (DCMD_OK);
501 }
502 
503 static void
evtchns_help(void)504 evtchns_help(void)
505 {
506 	mdb_printf("Print valid event channels\n"
507 	    "If %<u>addr%</u> is given, interpret it as an evtchn to print "
508 	    "details of.\n"
509 	    "By default, only interrupt service routine names are printed.\n\n"
510 	    "Switches:\n"
511 	    "  -d   instead of ISR, print <driver_name><instance#>\n");
512 }
513 
514 static const mdb_dcmd_t dcmds[] = {
515 	{ "interrupts", "?[-di]", "print interrupts", interrupts_dump,
516 	    interrupt_help },
517 	{ "evtchns", "?[-d]", "print event channels", evtchns_dump,
518 	    evtchns_help },
519 	{ "softint", "?[-d]", "print soft interrupts", soft_interrupt_dump,
520 	    soft_interrupt_help},
521 	{ NULL }
522 };
523 
524 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, NULL };
525 
526 const mdb_modinfo_t *
_mdb_init(void)527 _mdb_init(void)
528 {
529 	GElf_Sym sym;
530 
531 	if (mdb_lookup_by_name("gld_intr", &sym) != -1)
532 		if (GELF_ST_TYPE(sym.st_info) == STT_FUNC)
533 			gld_intr_addr = (uintptr_t)sym.st_value;
534 
535 	return (&modinfo);
536 }
537