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
update_tables(void)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 *
interrupt_print_bus(uintptr_t dip_addr)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 *
virq_type(int irq)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 *
irq_type(int irq,int extended)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
print_isr(int i)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
evtchn_masked(int i)189 evtchn_masked(int i)
190 {
191 return (TEST_EVTCHN_BIT(i, &shared_info.evtchn_mask[0]) != 0);
192 }
193
194 static int
evtchn_pending(int i)195 evtchn_pending(int i)
196 {
197 return (TEST_EVTCHN_BIT(i, &shared_info.evtchn_pending[0]) != 0);
198 }
199
200 static void
pic_interrupt_dump(int i,struct autovec * avhp,int evtchn)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
ec_interrupt_dump(int i)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
xen_uppc_interrupt_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
evtchn_dump(int i)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
evtchns_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
evtchns_help(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 *
_mdb_init(void)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