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