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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stddef.h>
30 #include <sys/mdb_modapi.h>
31 #include <mdb/mdb_ks.h>
32 #include <sys/modctl.h>
33 #include <sys/avintr.h>
34 #include <sys/psm_common.h>
35 #include <sys/pic.h>
36 
37 static struct av_head	avec_tbl[256];
38 static uint16_t		shared_tbl[MAX_ISA_IRQ + 1];
39 
40 static char *
41 interrupt_print_bus(uintptr_t dip_addr)
42 {
43 	char		bind_name[MODMAXNAMELEN + 1];
44 	struct dev_info	dev_info;
45 
46 	if (mdb_vread(&dev_info, sizeof (dev_info), dip_addr) == -1) {
47 		mdb_warn("failed to read child dip");
48 		return ("-");
49 	}
50 
51 	while (dev_info.devi_parent != 0) {
52 		if (mdb_vread(&dev_info, sizeof (dev_info),
53 		    (uintptr_t)dev_info.devi_parent) == -1)
54 			break;
55 
56 		(void) mdb_readstr(bind_name, sizeof (bind_name),
57 		    (uintptr_t)dev_info.devi_binding_name);
58 		if (strcmp(bind_name, "isa") == 0)
59 			return ("ISA");
60 		else if (strcmp(bind_name, "pci") == 0 ||
61 		    strcmp(bind_name, "npe") == 0)
62 			return ("PCI");
63 	}
64 	return ("-");
65 }
66 
67 
68 static void
69 interrupt_print_isr(struct autovec avhp)
70 {
71 	char		driver_name[MODMAXNAMELEN + 1];
72 	uintptr_t	dip_addr = (uintptr_t)avhp.av_dip;
73 	struct dev_info	dev_info;
74 
75 	if (dip_addr && mdb_devinfo2driver(dip_addr, driver_name,
76 	    sizeof (driver_name)) == 0) {
77 		(void) mdb_vread(&dev_info, sizeof (dev_info), dip_addr);
78 		mdb_printf("%s#%d", driver_name, dev_info.devi_instance);
79 	} else
80 		mdb_printf("%a", avhp.av_vector);
81 }
82 
83 /*
84  * uppc_interrupt_dump:
85  *	Dump uppc(7d) interrupt information.
86  */
87 /* ARGSUSED */
88 int
89 uppc_interrupt_dump(uintptr_t addr, uint_t flags, int argc,
90     const mdb_arg_t *argv)
91 {
92 	int		i, share_cnt;
93 	boolean_t	found = B_FALSE;
94 	struct autovec	avhp;
95 
96 	if (mdb_readvar(&avec_tbl, "autovect") == -1) {
97 		mdb_warn("failed to read autovect");
98 		return (DCMD_ERR);
99 	}
100 
101 	if (mdb_readvar(&shared_tbl, "uppc_irq_shared_table") == -1) {
102 		mdb_warn("failed to read uppc_irq_shared_table");
103 		return (DCMD_ERR);
104 	}
105 
106 	/*
107 	 * All x86 systems load uppc(7d) by default.
108 	 * Now, this becomes interesting on APIC based systems.
109 	 * 'interrupts' dcmd is supported by both the .so modules
110 	 * So, uppc(7d) needs to be unloaded somehow on APIC based systems
111 	 * Here a check for shared_tbl[i] and returning NULL takes care of that
112 	 */
113 	for (i = 0; i < MAX_ISA_IRQ + 1; i++)
114 		if (shared_tbl[i]) {
115 			found = B_TRUE;
116 			break;
117 		}
118 
119 	if (found == B_FALSE) {
120 		if (mdb_lookup_by_obj("pcplusmp", "apic_irq_table",
121 		    NULL) == 0) {
122 			return (mdb_call_dcmd("pcplusmp`interrupts",
123 			    addr, flags, argc, argv));
124 		}
125 	}
126 
127 	/* Print the header first */
128 	mdb_printf("%<u>IRQ  Vector IPL(lo/hi) Bus Share "
129 	    "Driver Name(s)/ISR(s)%</u>\n");
130 
131 	/* Walk all the entries */
132 	for (i = 0; i < MAX_ISA_IRQ + 1; i++) {
133 		/* Read the entry, if invalid continue */
134 		if (mdb_vread(&avhp, sizeof (struct autovec),
135 		    (uintptr_t)avec_tbl[i].avh_link) == -1)
136 			continue;
137 
138 		/* Print each interrupt entry */
139 		share_cnt = shared_tbl[i];
140 		mdb_printf("%3d   0x%2x   %4d/%-2d   %-4s %3d  ",
141 		    i, i + PIC_VECTBASE,
142 		    avec_tbl[i].avh_lo_pri, avec_tbl[i].avh_hi_pri,
143 		    avhp.av_dip ? interrupt_print_bus((uintptr_t)avhp.av_dip) :
144 		    " - ", share_cnt);
145 
146 		if (share_cnt)
147 			interrupt_print_isr(avhp);
148 
149 		while (share_cnt-- > 0) {
150 			if (mdb_vread(&avhp, sizeof (struct autovec),
151 			    (uintptr_t)avhp.av_link) != -1)  {
152 				mdb_printf(", ");
153 				interrupt_print_isr(avhp);
154 			}
155 		}
156 		mdb_printf("\n");
157 	}
158 
159 	return (DCMD_OK);
160 }
161 
162 
163 /*
164  * MDB module linkage information:
165  */
166 static const mdb_dcmd_t dcmds[] = {
167 	{ "interrupts", NULL, "print interrupts", uppc_interrupt_dump, NULL},
168 	{ NULL }
169 };
170 
171 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, NULL };
172 
173 const mdb_modinfo_t *
174 _mdb_init(void)
175 {
176 	return (&modinfo);
177 }
178