1*6356Smrj /*
2*6356Smrj * CDDL HEADER START
3*6356Smrj *
4*6356Smrj * The contents of this file are subject to the terms of the
5*6356Smrj * Common Development and Distribution License (the "License").
6*6356Smrj * You may not use this file except in compliance with the License.
7*6356Smrj *
8*6356Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*6356Smrj * or http://www.opensolaris.org/os/licensing.
10*6356Smrj * See the License for the specific language governing permissions
11*6356Smrj * and limitations under the License.
12*6356Smrj *
13*6356Smrj * When distributing Covered Code, include this CDDL HEADER in each
14*6356Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*6356Smrj * If applicable, add the following below this CDDL HEADER, with the
16*6356Smrj * fields enclosed by brackets "[]" replaced with your own identifying
17*6356Smrj * information: Portions Copyright [yyyy] [name of copyright owner]
18*6356Smrj *
19*6356Smrj * CDDL HEADER END
20*6356Smrj */
21*6356Smrj /*
22*6356Smrj * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23*6356Smrj * Use is subject to license terms.
24*6356Smrj */
25*6356Smrj
26*6356Smrj #pragma ident "%Z%%M% %I% %E% SMI"
27*6356Smrj
28*6356Smrj #include <mdb/mdb_modapi.h>
29*6356Smrj #include <mdb/mdb_ks.h>
30*6356Smrj #include <mdb/mdb_ctf.h>
31*6356Smrj #include <sys/evtchn_impl.h>
32*6356Smrj
33*6356Smrj #include "intr_common.h"
34*6356Smrj
35*6356Smrj static shared_info_t shared_info;
36*6356Smrj static struct av_head avec_tbl[NR_IRQS];
37*6356Smrj static uint16_t shared_tbl[MAX_ISA_IRQ + 1];
38*6356Smrj static irq_info_t irq_tbl[NR_IRQS];
39*6356Smrj static mec_info_t virq_tbl[NR_VIRQS];
40*6356Smrj static short evtchn_tbl[NR_EVENT_CHANNELS];
41*6356Smrj
42*6356Smrj static int
update_tables(void)43*6356Smrj update_tables(void)
44*6356Smrj {
45*6356Smrj uintptr_t shared_info_addr;
46*6356Smrj
47*6356Smrj if (mdb_readvar(&irq_tbl, "irq_info") == -1) {
48*6356Smrj mdb_warn("failed to read irq_info");
49*6356Smrj return (0);
50*6356Smrj }
51*6356Smrj
52*6356Smrj if (mdb_readvar(&virq_tbl, "virq_info") == -1) {
53*6356Smrj mdb_warn("failed to read virq_info");
54*6356Smrj return (0);
55*6356Smrj }
56*6356Smrj
57*6356Smrj if (mdb_readvar(&evtchn_tbl, "evtchn_to_irq") == -1) {
58*6356Smrj mdb_warn("failed to read evtchn_to_irq");
59*6356Smrj return (0);
60*6356Smrj }
61*6356Smrj
62*6356Smrj if (mdb_readvar(&avec_tbl, "autovect") == -1) {
63*6356Smrj mdb_warn("failed to read autovect");
64*6356Smrj return (0);
65*6356Smrj }
66*6356Smrj
67*6356Smrj if (mdb_readvar(&shared_tbl, "xen_uppc_irq_shared_table") == -1) {
68*6356Smrj mdb_warn("failed to read xen_uppc_irq_shared_table");
69*6356Smrj return (0);
70*6356Smrj }
71*6356Smrj
72*6356Smrj if (mdb_readvar(&shared_info_addr, "HYPERVISOR_shared_info") == -1) {
73*6356Smrj mdb_warn("failed to read HYPERVISOR_shared_info");
74*6356Smrj return (0);
75*6356Smrj }
76*6356Smrj
77*6356Smrj if (mdb_ctf_vread(&shared_info, "shared_info_t",
78*6356Smrj shared_info_addr, 0) == -1) {
79*6356Smrj mdb_warn("failed to read shared_info");
80*6356Smrj return (0);
81*6356Smrj }
82*6356Smrj
83*6356Smrj return (1);
84*6356Smrj }
85*6356Smrj
86*6356Smrj
87*6356Smrj static char *
interrupt_print_bus(uintptr_t dip_addr)88*6356Smrj interrupt_print_bus(uintptr_t dip_addr)
89*6356Smrj {
90*6356Smrj char bind_name[MAXPATHLEN + 1];
91*6356Smrj struct dev_info dev_info;
92*6356Smrj
93*6356Smrj if (mdb_vread(&dev_info, sizeof (dev_info), dip_addr) == -1) {
94*6356Smrj mdb_warn("failed to read child dip");
95*6356Smrj return ("-");
96*6356Smrj }
97*6356Smrj
98*6356Smrj while (dev_info.devi_parent != 0) {
99*6356Smrj if (mdb_vread(&dev_info, sizeof (dev_info),
100*6356Smrj (uintptr_t)dev_info.devi_parent) == -1)
101*6356Smrj break;
102*6356Smrj
103*6356Smrj (void) mdb_readstr(bind_name, sizeof (bind_name),
104*6356Smrj (uintptr_t)dev_info.devi_binding_name);
105*6356Smrj if (strcmp(bind_name, "isa") == 0)
106*6356Smrj return ("ISA");
107*6356Smrj else if (strcmp(bind_name, "pci") == 0 ||
108*6356Smrj strcmp(bind_name, "npe") == 0)
109*6356Smrj return ("PCI");
110*6356Smrj }
111*6356Smrj return ("-");
112*6356Smrj }
113*6356Smrj
114*6356Smrj static const char *
virq_type(int irq)115*6356Smrj virq_type(int irq)
116*6356Smrj {
117*6356Smrj int i;
118*6356Smrj
119*6356Smrj for (i = 0; i < NR_VIRQS; i++) {
120*6356Smrj if (virq_tbl[i].mi_irq == irq)
121*6356Smrj break;
122*6356Smrj }
123*6356Smrj
124*6356Smrj switch (i) {
125*6356Smrj case VIRQ_TIMER:
126*6356Smrj return ("virq:timer");
127*6356Smrj case VIRQ_DEBUG:
128*6356Smrj return ("virq:debug");
129*6356Smrj case VIRQ_CONSOLE:
130*6356Smrj return ("virq:console");
131*6356Smrj case VIRQ_DOM_EXC:
132*6356Smrj return ("virq:dom exc");
133*6356Smrj case VIRQ_DEBUGGER:
134*6356Smrj return ("virq:debugger");
135*6356Smrj default:
136*6356Smrj break;
137*6356Smrj }
138*6356Smrj
139*6356Smrj return ("virq:?");
140*6356Smrj }
141*6356Smrj
142*6356Smrj static const char *
irq_type(int irq,int extended)143*6356Smrj irq_type(int irq, int extended)
144*6356Smrj {
145*6356Smrj switch (irq_tbl[irq].ii_type) {
146*6356Smrj case IRQT_UNBOUND:
147*6356Smrj return ("unset");
148*6356Smrj case IRQT_PIRQ:
149*6356Smrj return ("pirq");
150*6356Smrj case IRQT_VIRQ:
151*6356Smrj if (extended)
152*6356Smrj return (virq_type(irq));
153*6356Smrj return ("virq");
154*6356Smrj case IRQT_IPI:
155*6356Smrj return ("ipi");
156*6356Smrj case IRQT_EVTCHN:
157*6356Smrj return ("evtchn");
158*6356Smrj case IRQT_DEV_EVTCHN:
159*6356Smrj return ("device");
160*6356Smrj }
161*6356Smrj
162*6356Smrj return ("?");
163*6356Smrj }
164*6356Smrj
165*6356Smrj static void
print_isr(int i)166*6356Smrj print_isr(int i)
167*6356Smrj {
168*6356Smrj struct autovec avhp;
169*6356Smrj
170*6356Smrj if (avec_tbl[i].avh_link == NULL)
171*6356Smrj return;
172*6356Smrj
173*6356Smrj (void) mdb_vread(&avhp, sizeof (struct autovec),
174*6356Smrj (uintptr_t)avec_tbl[i].avh_link);
175*6356Smrj
176*6356Smrj interrupt_print_isr((uintptr_t)avhp.av_vector,
177*6356Smrj (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip);
178*6356Smrj
179*6356Smrj while (avhp.av_link != NULL &&
180*6356Smrj mdb_vread(&avhp, sizeof (struct autovec),
181*6356Smrj (uintptr_t)avhp.av_link) != -1) {
182*6356Smrj mdb_printf(", ");
183*6356Smrj interrupt_print_isr((uintptr_t)avhp.av_vector,
184*6356Smrj (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip);
185*6356Smrj }
186*6356Smrj }
187*6356Smrj
188*6356Smrj static int
evtchn_masked(int i)189*6356Smrj evtchn_masked(int i)
190*6356Smrj {
191*6356Smrj return (TEST_EVTCHN_BIT(i, &shared_info.evtchn_mask[0]) != 0);
192*6356Smrj }
193*6356Smrj
194*6356Smrj static int
evtchn_pending(int i)195*6356Smrj evtchn_pending(int i)
196*6356Smrj {
197*6356Smrj return (TEST_EVTCHN_BIT(i, &shared_info.evtchn_pending[0]) != 0);
198*6356Smrj }
199*6356Smrj
200*6356Smrj static void
pic_interrupt_dump(int i,struct autovec * avhp,int evtchn)201*6356Smrj pic_interrupt_dump(int i, struct autovec *avhp, int evtchn)
202*6356Smrj {
203*6356Smrj if (option_flags & INTR_DISPLAY_INTRSTAT) {
204*6356Smrj mdb_printf("%-3d ", 0);
205*6356Smrj print_isr(i);
206*6356Smrj mdb_printf("\n");
207*6356Smrj return;
208*6356Smrj }
209*6356Smrj
210*6356Smrj mdb_printf("%-3d 0x%2x %-6d %6d/%-2d %-3s %-6s %-5d ",
211*6356Smrj i, i + PIC_VECTBASE, evtchn, avec_tbl[i].avh_lo_pri,
212*6356Smrj avec_tbl[i].avh_hi_pri, avhp->av_dip ?
213*6356Smrj interrupt_print_bus((uintptr_t)avhp->av_dip) : "-",
214*6356Smrj irq_type(i, 0), shared_tbl[i]);
215*6356Smrj
216*6356Smrj print_isr(i);
217*6356Smrj
218*6356Smrj mdb_printf("\n");
219*6356Smrj }
220*6356Smrj
221*6356Smrj static void
ec_interrupt_dump(int i)222*6356Smrj ec_interrupt_dump(int i)
223*6356Smrj {
224*6356Smrj irq_info_t *irqp = &irq_tbl[i];
225*6356Smrj struct autovec avhp;
226*6356Smrj char evtchn[8];
227*6356Smrj
228*6356Smrj if (irqp->ii_type == IRQT_UNBOUND)
229*6356Smrj return;
230*6356Smrj
231*6356Smrj if (option_flags & INTR_DISPLAY_INTRSTAT) {
232*6356Smrj mdb_printf("%-3d ", 0);
233*6356Smrj print_isr(i);
234*6356Smrj mdb_printf("\n");
235*6356Smrj return;
236*6356Smrj }
237*6356Smrj
238*6356Smrj
239*6356Smrj memset(&avhp, 0, sizeof (avhp));
240*6356Smrj if (avec_tbl[i].avh_link != NULL)
241*6356Smrj (void) mdb_vread(&avhp, sizeof (struct autovec),
242*6356Smrj (uintptr_t)avec_tbl[i].avh_link);
243*6356Smrj
244*6356Smrj switch (irqp->ii_type) {
245*6356Smrj case IRQT_EVTCHN:
246*6356Smrj case IRQT_VIRQ:
247*6356Smrj if (irqp->ii_u.index == VIRQ_TIMER) {
248*6356Smrj strcpy(evtchn, "T");
249*6356Smrj } else {
250*6356Smrj mdb_snprintf(evtchn, sizeof (evtchn), "%-7d",
251*6356Smrj irqp->ii_u.evtchn);
252*6356Smrj }
253*6356Smrj break;
254*6356Smrj case IRQT_IPI:
255*6356Smrj strcpy(evtchn, "I");
256*6356Smrj break;
257*6356Smrj case IRQT_DEV_EVTCHN:
258*6356Smrj strcpy(evtchn, "D");
259*6356Smrj break;
260*6356Smrj }
261*6356Smrj
262*6356Smrj /* IRQ */
263*6356Smrj mdb_printf("%3d ", i);
264*6356Smrj /* Vector */
265*6356Smrj mdb_printf("- ");
266*6356Smrj /* Evtchn */
267*6356Smrj mdb_printf("%-7s", evtchn);
268*6356Smrj /* IPL */
269*6356Smrj mdb_printf("%6d/%-2d ", irq_tbl[i].ii_u2.ipl, irq_tbl[i].ii_u2.ipl);
270*6356Smrj /* Bus */
271*6356Smrj mdb_printf("%-3s ", avhp.av_dip
272*6356Smrj ? interrupt_print_bus((uintptr_t)avhp.av_dip) : "-");
273*6356Smrj /* Type */
274*6356Smrj mdb_printf("%-6s ", irq_type(i, 0));
275*6356Smrj /* Share */
276*6356Smrj mdb_printf("- ");
277*6356Smrj
278*6356Smrj print_isr(i);
279*6356Smrj
280*6356Smrj mdb_printf("\n");
281*6356Smrj }
282*6356Smrj
283*6356Smrj /*
284*6356Smrj * uppc_interrupt_dump:
285*6356Smrj * Dump uppc(7d) interrupt information.
286*6356Smrj */
287*6356Smrj /* ARGSUSED */
288*6356Smrj int
xen_uppc_interrupt_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)289*6356Smrj xen_uppc_interrupt_dump(uintptr_t addr, uint_t flags, int argc,
290*6356Smrj const mdb_arg_t *argv)
291*6356Smrj {
292*6356Smrj int i;
293*6356Smrj boolean_t found = B_FALSE;
294*6356Smrj struct autovec avhp;
295*6356Smrj
296*6356Smrj option_flags = 0;
297*6356Smrj if (mdb_getopts(argc, argv,
298*6356Smrj 'd', MDB_OPT_SETBITS, INTR_DISPLAY_DRVR_INST, &option_flags,
299*6356Smrj 'i', MDB_OPT_SETBITS, INTR_DISPLAY_INTRSTAT, &option_flags,
300*6356Smrj NULL) != argc)
301*6356Smrj return (DCMD_USAGE);
302*6356Smrj
303*6356Smrj if (!update_tables())
304*6356Smrj return (DCMD_ERR);
305*6356Smrj
306*6356Smrj /*
307*6356Smrj * By default, on all x86 systems ::interrupts from xen_uppc(7d) gets
308*6356Smrj * loaded first. For APIC systems the ::interrupts from xpv_psm(7d)
309*6356Smrj * ought to be executed. Confusion stems as both modules export the
310*6356Smrj * same dcmd.
311*6356Smrj */
312*6356Smrj for (i = 0; i < MAX_ISA_IRQ + 1; i++)
313*6356Smrj if (shared_tbl[i]) {
314*6356Smrj found = B_TRUE;
315*6356Smrj break;
316*6356Smrj }
317*6356Smrj
318*6356Smrj if (found == B_FALSE) {
319*6356Smrj if (mdb_lookup_by_obj("xpv_psm", "apic_irq_table",
320*6356Smrj NULL) == 0) {
321*6356Smrj return (mdb_call_dcmd("xpv_psm`interrupts",
322*6356Smrj addr, flags, argc, argv));
323*6356Smrj }
324*6356Smrj }
325*6356Smrj
326*6356Smrj /* Print the header first */
327*6356Smrj if (option_flags & INTR_DISPLAY_INTRSTAT)
328*6356Smrj mdb_printf("%<u>CPU ");
329*6356Smrj else
330*6356Smrj mdb_printf("%<u>IRQ Vect Evtchn IPL(lo/hi) Bus Type Share ");
331*6356Smrj mdb_printf("%s %</u>\n", option_flags & INTR_DISPLAY_DRVR_INST ?
332*6356Smrj "Driver Name(s)" : "ISR(s)");
333*6356Smrj
334*6356Smrj for (i = 0; i < NR_IRQS; i++) {
335*6356Smrj if (irq_tbl[i].ii_type == IRQT_PIRQ) {
336*6356Smrj if (irq_tbl[i].ii_u.evtchn == 0)
337*6356Smrj continue;
338*6356Smrj
339*6356Smrj /* Read the entry, if invalid continue */
340*6356Smrj if (mdb_vread(&avhp, sizeof (struct autovec),
341*6356Smrj (uintptr_t)avec_tbl[i].avh_link) == -1)
342*6356Smrj continue;
343*6356Smrj
344*6356Smrj pic_interrupt_dump(i, &avhp, irq_tbl[i].ii_u.evtchn);
345*6356Smrj continue;
346*6356Smrj }
347*6356Smrj
348*6356Smrj ec_interrupt_dump(i);
349*6356Smrj }
350*6356Smrj
351*6356Smrj return (DCMD_OK);
352*6356Smrj }
353*6356Smrj
354*6356Smrj
355*6356Smrj static void
evtchn_dump(int i)356*6356Smrj evtchn_dump(int i)
357*6356Smrj {
358*6356Smrj int irq = evtchn_tbl[i];
359*6356Smrj
360*6356Smrj if (irq == INVALID_IRQ) {
361*6356Smrj mdb_printf("%-14s%-7d%-4s%-7s", "unassigned", i, "-", "-");
362*6356Smrj mdb_printf("%-4d", 0);
363*6356Smrj mdb_printf("%-7d", evtchn_masked(i));
364*6356Smrj mdb_printf("%-8d", evtchn_pending(i));
365*6356Smrj mdb_printf("\n");
366*6356Smrj return;
367*6356Smrj }
368*6356Smrj
369*6356Smrj /* Type */
370*6356Smrj mdb_printf("%-14s", irq_type(irq, 1));
371*6356Smrj /* Evtchn */
372*6356Smrj mdb_printf("%-7d", i);
373*6356Smrj /* IRQ */
374*6356Smrj mdb_printf("%-4d", irq);
375*6356Smrj /* IPL */
376*6356Smrj mdb_printf("%6d/%-2d ", irq_tbl[irq].ii_u2.ipl,
377*6356Smrj irq_tbl[irq].ii_u2.ipl);
378*6356Smrj /* CPU */
379*6356Smrj mdb_printf("%-4d", 0);
380*6356Smrj /* Masked/Pending */
381*6356Smrj mdb_printf("%-7d", evtchn_masked(i));
382*6356Smrj mdb_printf("%-8d", evtchn_pending(i));
383*6356Smrj /* ISR */
384*6356Smrj print_isr(irq);
385*6356Smrj
386*6356Smrj mdb_printf("\n");
387*6356Smrj }
388*6356Smrj
389*6356Smrj /* ARGSUSED */
390*6356Smrj static int
evtchns_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)391*6356Smrj evtchns_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
392*6356Smrj {
393*6356Smrj int i;
394*6356Smrj boolean_t found = B_FALSE;
395*6356Smrj
396*6356Smrj option_flags = 0;
397*6356Smrj if (mdb_getopts(argc, argv,
398*6356Smrj 'd', MDB_OPT_SETBITS, INTR_DISPLAY_DRVR_INST, &option_flags,
399*6356Smrj NULL) != argc)
400*6356Smrj return (DCMD_USAGE);
401*6356Smrj
402*6356Smrj if (!update_tables())
403*6356Smrj return (DCMD_ERR);
404*6356Smrj
405*6356Smrj /*
406*6356Smrj * By default, on all x86 systems ::evtchns from xen_uppc(7d) gets
407*6356Smrj * loaded first. For APIC systems the ::evtchns from xpv_psm(7d)
408*6356Smrj * ought to be executed. Confusion stems as both modules export the
409*6356Smrj * same dcmd.
410*6356Smrj */
411*6356Smrj for (i = 0; i < MAX_ISA_IRQ + 1; i++)
412*6356Smrj if (shared_tbl[i]) {
413*6356Smrj found = B_TRUE;
414*6356Smrj break;
415*6356Smrj }
416*6356Smrj
417*6356Smrj if (found == B_FALSE) {
418*6356Smrj if (mdb_lookup_by_obj("xpv_psm", "apic_irq_table",
419*6356Smrj NULL) == 0) {
420*6356Smrj return (mdb_call_dcmd("xpv_psm`evtchns",
421*6356Smrj addr, flags, argc, argv));
422*6356Smrj }
423*6356Smrj }
424*6356Smrj
425*6356Smrj if (flags & DCMD_ADDRSPEC) {
426*6356Smrj /*
427*6356Smrj * Note: we allow the invalid evtchn 0, as it can help catch if
428*6356Smrj * we incorrectly try to configure it.
429*6356Smrj */
430*6356Smrj if ((int)addr >= NR_EVENT_CHANNELS) {
431*6356Smrj mdb_warn("Invalid event channel %d.\n", (int)addr);
432*6356Smrj return (DCMD_ERR);
433*6356Smrj }
434*6356Smrj }
435*6356Smrj
436*6356Smrj mdb_printf("%<u>Type Evtchn IRQ IPL(lo/hi) CPU "
437*6356Smrj "Masked Pending ");
438*6356Smrj mdb_printf("%s %</u>\n", option_flags & INTR_DISPLAY_DRVR_INST ?
439*6356Smrj "Driver Name(s)" : "ISR(s)");
440*6356Smrj
441*6356Smrj if (flags & DCMD_ADDRSPEC) {
442*6356Smrj evtchn_dump((int)addr);
443*6356Smrj return (DCMD_OK);
444*6356Smrj }
445*6356Smrj
446*6356Smrj for (i = 0; i < NR_EVENT_CHANNELS; i++) {
447*6356Smrj if (evtchn_tbl[i] == INVALID_IRQ)
448*6356Smrj continue;
449*6356Smrj
450*6356Smrj evtchn_dump(i);
451*6356Smrj }
452*6356Smrj
453*6356Smrj return (DCMD_OK);
454*6356Smrj }
455*6356Smrj
456*6356Smrj static void
evtchns_help(void)457*6356Smrj evtchns_help(void)
458*6356Smrj {
459*6356Smrj mdb_printf("Print valid event channels\n"
460*6356Smrj "If %<u>addr%</u> is given, interpret it as an evtchn to print "
461*6356Smrj "details of.\n"
462*6356Smrj "By default, only interrupt service routine names are printed.\n\n"
463*6356Smrj "Switches:\n"
464*6356Smrj " -d instead of ISR, print <driver_name><instance#>\n");
465*6356Smrj }
466*6356Smrj
467*6356Smrj /*
468*6356Smrj * MDB module linkage information:
469*6356Smrj */
470*6356Smrj static const mdb_dcmd_t dcmds[] = {
471*6356Smrj { "interrupts", "?[-di]", "print interrupts", xen_uppc_interrupt_dump,
472*6356Smrj interrupt_help},
473*6356Smrj { "evtchns", "?[-d]", "print event channels", evtchns_dump,
474*6356Smrj evtchns_help },
475*6356Smrj { "softint", "?[-d]", "print soft interrupts", soft_interrupt_dump,
476*6356Smrj soft_interrupt_help},
477*6356Smrj { NULL }
478*6356Smrj };
479*6356Smrj
480*6356Smrj static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, NULL };
481*6356Smrj
482*6356Smrj const mdb_modinfo_t *
_mdb_init(void)483*6356Smrj _mdb_init(void)
484*6356Smrj {
485*6356Smrj GElf_Sym sym;
486*6356Smrj
487*6356Smrj if (mdb_lookup_by_name("gld_intr", &sym) != -1)
488*6356Smrj if (GELF_ST_TYPE(sym.st_info) == STT_FUNC)
489*6356Smrj gld_intr_addr = (uintptr_t)sym.st_value;
490*6356Smrj
491*6356Smrj return (&modinfo);
492*6356Smrj }
493