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