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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright (c) 2013 by Delphix. All rights reserved.
29 #include <mdb/mdb_modapi.h>
30 #include <mdb/mdb_ks.h>
31 #include <mdb/mdb_ctf.h>
32 #include <sys/evtchn_impl.h>
34 #include "intr_common.h"
36 typedef struct mdb_shared_info
{
37 unsigned long evtchn_pending
[sizeof (unsigned long) * NBBY
];
38 unsigned long evtchn_mask
[sizeof (unsigned long) * NBBY
];
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
];
51 uintptr_t shared_info_addr
;
53 if (mdb_readvar(&irq_tbl
, "irq_info") == -1) {
54 mdb_warn("failed to read irq_info");
58 if (mdb_readvar(&virq_tbl
, "virq_info") == -1) {
59 mdb_warn("failed to read virq_info");
63 if (mdb_readvar(&evtchn_tbl
, "evtchn_to_irq") == -1) {
64 mdb_warn("failed to read evtchn_to_irq");
68 if (mdb_readvar(&avec_tbl
, "autovect") == -1) {
69 mdb_warn("failed to read autovect");
73 if (mdb_readvar(&shared_tbl
, "xen_uppc_irq_shared_table") == -1) {
74 mdb_warn("failed to read xen_uppc_irq_shared_table");
78 if (mdb_readvar(&shared_info_addr
, "HYPERVISOR_shared_info") == -1) {
79 mdb_warn("failed to read HYPERVISOR_shared_info");
83 if (mdb_ctf_vread(&shared_info
, "shared_info_t", "mdb_shared_info_t",
84 shared_info_addr
, 0) == -1)
92 interrupt_print_bus(uintptr_t dip_addr
)
94 char bind_name
[MAXPATHLEN
+ 1];
95 struct dev_info dev_info
;
97 if (mdb_vread(&dev_info
, sizeof (dev_info
), dip_addr
) == -1) {
98 mdb_warn("failed to read child dip");
102 while (dev_info
.devi_parent
!= 0) {
103 if (mdb_vread(&dev_info
, sizeof (dev_info
),
104 (uintptr_t)dev_info
.devi_parent
) == -1)
107 (void) mdb_readstr(bind_name
, sizeof (bind_name
),
108 (uintptr_t)dev_info
.devi_binding_name
);
109 if (strcmp(bind_name
, "isa") == 0)
111 else if (strcmp(bind_name
, "pci") == 0 ||
112 strcmp(bind_name
, "npe") == 0)
123 for (i
= 0; i
< NR_VIRQS
; i
++) {
124 if (virq_tbl
[i
].mi_irq
== irq
)
130 return ("virq:timer");
132 return ("virq:debug");
134 return ("virq:console");
136 return ("virq:dom exc");
138 return ("virq:debugger");
147 irq_type(int irq
, int extended
)
149 switch (irq_tbl
[irq
].ii_type
) {
156 return (virq_type(irq
));
162 case IRQT_DEV_EVTCHN
:
174 if (avec_tbl
[i
].avh_link
== NULL
)
177 (void) mdb_vread(&avhp
, sizeof (struct autovec
),
178 (uintptr_t)avec_tbl
[i
].avh_link
);
180 interrupt_print_isr((uintptr_t)avhp
.av_vector
,
181 (uintptr_t)avhp
.av_intarg1
, (uintptr_t)avhp
.av_dip
);
183 while (avhp
.av_link
!= NULL
&&
184 mdb_vread(&avhp
, sizeof (struct autovec
),
185 (uintptr_t)avhp
.av_link
) != -1) {
187 interrupt_print_isr((uintptr_t)avhp
.av_vector
,
188 (uintptr_t)avhp
.av_intarg1
, (uintptr_t)avhp
.av_dip
);
195 return (TEST_EVTCHN_BIT(i
, &shared_info
.evtchn_mask
[0]) != 0);
199 evtchn_pending(int i
)
201 return (TEST_EVTCHN_BIT(i
, &shared_info
.evtchn_pending
[0]) != 0);
205 pic_interrupt_dump(int i
, struct autovec
*avhp
, int evtchn
)
207 if (option_flags
& INTR_DISPLAY_INTRSTAT
) {
208 mdb_printf("%-3d ", 0);
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
]);
226 ec_interrupt_dump(int i
)
228 irq_info_t
*irqp
= &irq_tbl
[i
];
232 if (irqp
->ii_type
== IRQT_UNBOUND
)
235 if (option_flags
& INTR_DISPLAY_INTRSTAT
) {
236 mdb_printf("%-3d ", 0);
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
);
248 switch (irqp
->ii_type
) {
251 if (irqp
->ii_u
.index
== VIRQ_TIMER
) {
254 mdb_snprintf(evtchn
, sizeof (evtchn
), "%-7d",
261 case IRQT_DEV_EVTCHN
:
267 mdb_printf("%3d ", i
);
271 mdb_printf("%-7s", evtchn
);
273 mdb_printf("%6d/%-2d ", irq_tbl
[i
].ii_u2
.ipl
, irq_tbl
[i
].ii_u2
.ipl
);
275 mdb_printf("%-3s ", avhp
.av_dip
276 ? interrupt_print_bus((uintptr_t)avhp
.av_dip
) : "-");
278 mdb_printf("%-6s ", irq_type(i
, 0));
288 * uppc_interrupt_dump:
289 * Dump uppc(7d) interrupt information.
293 xen_uppc_interrupt_dump(uintptr_t addr
, uint_t flags
, int argc
,
294 const mdb_arg_t
*argv
)
297 boolean_t found
= B_FALSE
;
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
,
307 if (!update_tables())
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
316 for (i
= 0; i
< MAX_ISA_IRQ
+ 1; i
++)
322 if (found
== B_FALSE
) {
323 if (mdb_lookup_by_obj("xpv_psm", "apic_irq_table",
325 return (mdb_call_dcmd("xpv_psm`interrupts",
326 addr
, flags
, argc
, argv
));
330 /* Print the header first */
331 if (option_flags
& INTR_DISPLAY_INTRSTAT
)
332 mdb_printf("%<u>CPU ");
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)");
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)
343 /* Read the entry, if invalid continue */
344 if (mdb_vread(&avhp
, sizeof (struct autovec
),
345 (uintptr_t)avec_tbl
[i
].avh_link
) == -1)
348 pic_interrupt_dump(i
, &avhp
, irq_tbl
[i
].ii_u
.evtchn
);
352 ec_interrupt_dump(i
);
362 int irq
= evtchn_tbl
[i
];
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
));
374 mdb_printf("%-14s", irq_type(irq
, 1));
376 mdb_printf("%-7d", i
);
378 mdb_printf("%-4d", irq
);
380 mdb_printf("%6d/%-2d ", irq_tbl
[irq
].ii_u2
.ipl
,
381 irq_tbl
[irq
].ii_u2
.ipl
);
383 mdb_printf("%-4d", 0);
385 mdb_printf("%-7d", evtchn_masked(i
));
386 mdb_printf("%-8d", evtchn_pending(i
));
395 evtchns_dump(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
398 boolean_t found
= B_FALSE
;
401 if (mdb_getopts(argc
, argv
,
402 'd', MDB_OPT_SETBITS
, INTR_DISPLAY_DRVR_INST
, &option_flags
,
406 if (!update_tables())
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
415 for (i
= 0; i
< MAX_ISA_IRQ
+ 1; i
++)
421 if (found
== B_FALSE
) {
422 if (mdb_lookup_by_obj("xpv_psm", "apic_irq_table",
424 return (mdb_call_dcmd("xpv_psm`evtchns",
425 addr
, flags
, argc
, argv
));
429 if (flags
& DCMD_ADDRSPEC
) {
431 * Note: we allow the invalid evtchn 0, as it can help catch if
432 * we incorrectly try to configure it.
434 if ((int)addr
>= NR_EVENT_CHANNELS
) {
435 mdb_warn("Invalid event channel %d.\n", (int)addr
);
440 mdb_printf("%<u>Type Evtchn IRQ IPL(lo/hi) CPU "
442 mdb_printf("%s %</u>\n", option_flags
& INTR_DISPLAY_DRVR_INST
?
443 "Driver Name(s)" : "ISR(s)");
445 if (flags
& DCMD_ADDRSPEC
) {
446 evtchn_dump((int)addr
);
450 for (i
= 0; i
< NR_EVENT_CHANNELS
; i
++) {
451 if (evtchn_tbl
[i
] == INVALID_IRQ
)
463 mdb_printf("Print valid event channels\n"
464 "If %<u>addr%</u> is given, interpret it as an evtchn to print "
466 "By default, only interrupt service routine names are printed.\n\n"
468 " -d instead of ISR, print <driver_name><instance#>\n");
472 * MDB module linkage information:
474 static const mdb_dcmd_t dcmds
[] = {
475 { "interrupts", "?[-di]", "print interrupts", xen_uppc_interrupt_dump
,
477 { "evtchns", "?[-d]", "print event channels", evtchns_dump
,
479 { "softint", "?[-d]", "print soft interrupts", soft_interrupt_dump
,
480 soft_interrupt_help
},
484 static const mdb_modinfo_t modinfo
= { MDB_API_VERSION
, dcmds
, NULL
};
486 const mdb_modinfo_t
*
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
;