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