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