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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include "intr_common.h"
26
27 static struct av_head avec_tbl[APIC_MAX_VECTOR+1];
28 static uint16_t shared_tbl[MAX_ISA_IRQ + 1];
29
30 static char *
interrupt_print_bus(uintptr_t dip_addr)31 interrupt_print_bus(uintptr_t dip_addr)
32 {
33 char bind_name[MAXPATHLEN + 1];
34 struct dev_info dev_info;
35
36 if (mdb_vread(&dev_info, sizeof (dev_info), dip_addr) == -1) {
37 mdb_warn("failed to read child dip");
38 return ("-");
39 }
40
41 while (dev_info.devi_parent != 0) {
42 if (mdb_vread(&dev_info, sizeof (dev_info),
43 (uintptr_t)dev_info.devi_parent) == -1)
44 break;
45
46 (void) mdb_readstr(bind_name, sizeof (bind_name),
47 (uintptr_t)dev_info.devi_binding_name);
48 if (strcmp(bind_name, "isa") == 0)
49 return ("ISA");
50 else if (strcmp(bind_name, "pci") == 0 ||
51 strcmp(bind_name, "npe") == 0)
52 return ("PCI");
53 }
54 return ("-");
55 }
56
57
58 /*
59 * uppc_interrupt_dump:
60 * Dump uppc(7d) interrupt information.
61 */
62 /* ARGSUSED */
63 int
uppc_interrupt_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)64 uppc_interrupt_dump(uintptr_t addr, uint_t flags, int argc,
65 const mdb_arg_t *argv)
66 {
67 int i, j;
68 boolean_t found = B_FALSE;
69 struct autovec avhp;
70
71 option_flags = 0;
72 if (mdb_getopts(argc, argv,
73 'd', MDB_OPT_SETBITS, INTR_DISPLAY_DRVR_INST, &option_flags,
74 'i', MDB_OPT_SETBITS, INTR_DISPLAY_INTRSTAT, &option_flags,
75 NULL) != argc)
76 return (DCMD_USAGE);
77
78 if (mdb_readvar(&avec_tbl, "autovect") == -1) {
79 mdb_warn("failed to read autovect");
80 return (DCMD_ERR);
81 }
82
83 if (mdb_readvar(&shared_tbl, "uppc_irq_shared_table") == -1) {
84 mdb_warn("failed to read uppc_irq_shared_table");
85 return (DCMD_ERR);
86 }
87
88 /*
89 * By default, on all x86 systems ::interrupts from uppc gets
90 * loaded first. For APIC systems the ::interrupts from either
91 * apix or pcplusmp ought to be executed. Confusion stems as
92 * these three modules export the same dcmd.
93 */
94 for (i = 0; i < MAX_ISA_IRQ + 1; i++)
95 if (shared_tbl[i]) {
96 found = B_TRUE;
97 break;
98 }
99
100 if (found == B_FALSE) {
101 if (mdb_lookup_by_obj("apix", "apixs", NULL) == 0) {
102 return (mdb_call_dcmd("apix`interrupts",
103 addr, flags, argc, argv));
104 } else if (mdb_lookup_by_obj("pcplusmp", "apic_irq_table",
105 NULL) == 0) {
106 return (mdb_call_dcmd("pcplusmp`interrupts",
107 addr, flags, argc, argv));
108 }
109 }
110
111 /* Print the header first */
112 if (option_flags & INTR_DISPLAY_INTRSTAT)
113 mdb_printf("%<u>CPU ");
114 else
115 mdb_printf("%<u>IRQ Vector IPL(lo/hi) Bus Share ");
116 mdb_printf("%s %</u>\n", option_flags & INTR_DISPLAY_DRVR_INST ?
117 "Driver Name(s)" : "ISR(s)");
118
119 /* Walk all the entries */
120 for (i = 0; i < MAX_ISA_IRQ + 1; i++) {
121 /* Read the entry, if invalid continue */
122 if (mdb_vread(&avhp, sizeof (struct autovec),
123 (uintptr_t)avec_tbl[i].avh_link) == -1)
124 continue;
125
126 /* Print each interrupt entry */
127 if (option_flags & INTR_DISPLAY_INTRSTAT)
128 mdb_printf("cpu0\t");
129 else
130 mdb_printf("%-3d 0x%2x %4d/%-2d %-4s %-3d ",
131 i, i + PIC_VECTBASE, avec_tbl[i].avh_lo_pri,
132 avec_tbl[i].avh_hi_pri, avhp.av_dip ?
133 interrupt_print_bus((uintptr_t)avhp.av_dip) : " - ",
134 shared_tbl[i]);
135
136 if (shared_tbl[i])
137 interrupt_print_isr((uintptr_t)avhp.av_vector,
138 (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip);
139
140 for (j = 1; j < shared_tbl[i]; j++) {
141 if (mdb_vread(&avhp, sizeof (struct autovec),
142 (uintptr_t)avhp.av_link) != -1) {
143 mdb_printf(", ");
144 interrupt_print_isr((uintptr_t)avhp.av_vector,
145 (uintptr_t)avhp.av_intarg1,
146 (uintptr_t)avhp.av_dip);
147 } else {
148 break;
149 }
150 }
151 mdb_printf("\n");
152 }
153
154 return (DCMD_OK);
155 }
156
157
158 /*
159 * MDB module linkage information:
160 */
161 static const mdb_dcmd_t dcmds[] = {
162 { "interrupts", "?[-di]", "print interrupts", uppc_interrupt_dump,
163 interrupt_help},
164 { "softint", "?[-d]", "print soft interrupts", soft_interrupt_dump,
165 soft_interrupt_help},
166 { NULL }
167 };
168
169 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, NULL };
170
171 const mdb_modinfo_t *
_mdb_init(void)172 _mdb_init(void)
173 {
174 GElf_Sym sym;
175
176 if (mdb_lookup_by_name("gld_intr", &sym) != -1)
177 if (GELF_ST_TYPE(sym.st_info) == STT_FUNC)
178 gld_intr_addr = (uintptr_t)sym.st_value;
179
180 return (&modinfo);
181 }
182