xref: /illumos-gate/usr/src/cmd/mdb/i86xpv/modules/xpv_psm/xpv_psm.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 #include <mdb/mdb_modapi.h>
27 #include <mdb/mdb_ks.h>
28 #include <mdb/mdb_ctf.h>
29 #include <sys/evtchn_impl.h>
30 #include <errno.h>
31 #include <sys/xc_levels.h>
32 
33 #include "intr_common.h"
34 
35 static shared_info_t shared_info;
36 static int have_shared_info;
37 static uintptr_t evtchn_cpus_addr;
38 static struct av_head avec_tbl[NR_IRQS];
39 static irq_info_t irq_tbl[NR_IRQS];
40 static mec_info_t ipi_tbl[MAXIPL];
41 static mec_info_t virq_tbl[NR_VIRQS];
42 static short evtchn_tbl[NR_EVENT_CHANNELS];
43 static apic_irq_t *apic_irq_tbl[APIC_MAX_VECTOR+1];
44 static char level_tbl[APIC_MAX_VECTOR+1];
45 
46 static int
47 update_tables(void)
48 {
49 	GElf_Sym sym;
50 	uintptr_t shared_info_addr;
51 
52 	if (mdb_readvar(&irq_tbl, "irq_info") == -1) {
53 		mdb_warn("failed to read irq_info");
54 		return (0);
55 	}
56 
57 	if (mdb_readvar(&ipi_tbl, "ipi_info") == -1) {
58 		mdb_warn("failed to read ipi_info");
59 		return (0);
60 	}
61 
62 	if (mdb_readvar(&avec_tbl, "autovect") == -1) {
63 		mdb_warn("failed to read autovect");
64 		return (0);
65 	}
66 
67 	if (mdb_readvar(&irq_tbl, "irq_info") == -1) {
68 		mdb_warn("failed to read irq_info");
69 		return (0);
70 	}
71 
72 	if (mdb_readvar(&ipi_tbl, "ipi_info") == -1) {
73 		mdb_warn("failed to read ipi_info");
74 		return (0);
75 	}
76 
77 	if (mdb_readvar(&virq_tbl, "virq_info") == -1) {
78 		mdb_warn("failed to read virq_info");
79 		return (0);
80 	}
81 
82 	if (mdb_readvar(&evtchn_tbl, "evtchn_to_irq") == -1) {
83 		mdb_warn("failed to read evtchn_to_irq");
84 		return (0);
85 	}
86 
87 	if (mdb_readvar(&apic_irq_tbl, "apic_irq_table") == -1) {
88 		mdb_warn("failed to read apic_irq_table");
89 		return (0);
90 	}
91 
92 	if (mdb_readvar(&level_tbl, "apic_level_intr") == -1) {
93 		mdb_warn("failed to read apic_level_intr");
94 		return (0);
95 	}
96 
97 	if (mdb_lookup_by_name("evtchn_cpus", &sym) == -1) {
98 		mdb_warn("failed to lookup evtchn_cpus");
99 		return (0);
100 	}
101 
102 	evtchn_cpus_addr = sym.st_value;
103 
104 	if (mdb_readvar(&shared_info_addr, "HYPERVISOR_shared_info") == -1) {
105 		mdb_warn("failed to read HYPERVISOR_shared_info");
106 		return (0);
107 	}
108 
109 	/*
110 	 * It's normal for this to fail with a domain dump.
111 	 */
112 	if (mdb_ctf_vread(&shared_info, "shared_info_t",
113 	    shared_info_addr, 0) != -1)
114 		have_shared_info = 1;
115 
116 	return (1);
117 }
118 
119 static const char *
120 virq_type(int irq)
121 {
122 	int i;
123 
124 	for (i = 0; i < NR_VIRQS; i++) {
125 		if (virq_tbl[i].mi_irq == irq)
126 			break;
127 	}
128 
129 	switch (i) {
130 	case VIRQ_TIMER:
131 		return ("virq:timer");
132 	case VIRQ_DEBUG:
133 		return ("virq:debug");
134 	case VIRQ_CONSOLE:
135 		return ("virq:console");
136 	case VIRQ_DOM_EXC:
137 		return ("virq:dom exc");
138 	case VIRQ_DEBUGGER:
139 		return ("virq:debugger");
140 	case VIRQ_MCA:
141 		return ("virq:mca");
142 	default:
143 		break;
144 	}
145 
146 	return ("virq:?");
147 }
148 
149 static const char *
150 irq_type(int irq, int extended)
151 {
152 	switch (irq_tbl[irq].ii_type) {
153 	case IRQT_UNBOUND:
154 		return ("unset");
155 	case IRQT_PIRQ:
156 		return ("pirq");
157 	case IRQT_VIRQ:
158 		if (extended)
159 			return (virq_type(irq));
160 		return ("virq");
161 	case IRQT_IPI:
162 		return ("ipi");
163 	case IRQT_EVTCHN:
164 		return ("evtchn");
165 	case IRQT_DEV_EVTCHN:
166 		return ("device");
167 	}
168 
169 	return ("?");
170 }
171 
172 /*
173  * We need a non-trivial IPL lookup as the CPU poke's IRQ doesn't have ii_ipl
174  * set -- see evtchn.h.
175  */
176 static int
177 irq_ipl(int irq)
178 {
179 	int i;
180 
181 	if (irq_tbl[irq].ii_u2.ipl != 0)
182 		return (irq_tbl[irq].ii_u2.ipl);
183 
184 	for (i = 0; i < MAXIPL; i++) {
185 		if (ipi_tbl[i].mi_irq == irq) {
186 			return (i);
187 		}
188 	}
189 
190 	return (0);
191 }
192 
193 static void
194 print_cpu(irq_info_t *irqp, int evtchn)
195 {
196 	size_t cpuset_size = BT_BITOUL(NCPU) * sizeof (ulong_t);
197 	int cpu;
198 
199 	if (irqp != NULL) {
200 		switch (irqp->ii_type) {
201 		case IRQT_VIRQ:
202 		case IRQT_IPI:
203 			mdb_printf("all ");
204 			return;
205 
206 		case IRQT_DEV_EVTCHN:
207 			mdb_printf("0   ");
208 			return;
209 
210 		default:
211 			break;
212 		}
213 	}
214 
215 	if (evtchn >= NR_EVENT_CHANNELS || evtchn == 0) {
216 		mdb_printf("-   ");
217 		return;
218 	}
219 
220 	cpu = mdb_cpuset_find(evtchn_cpus_addr +
221 	    (cpuset_size * evtchn));
222 
223 	/*
224 	 * XXPV: we should verify this against the CPU's mask and show
225 	 * something if they don't match.
226 	 */
227 	mdb_printf("%-4d", cpu);
228 }
229 
230 static void
231 print_isr(int i)
232 {
233 	if (avec_tbl[i].avh_link != NULL) {
234 		struct autovec avhp;
235 
236 		(void) mdb_vread(&avhp, sizeof (struct autovec),
237 		    (uintptr_t)avec_tbl[i].avh_link);
238 
239 		interrupt_print_isr((uintptr_t)avhp.av_vector,
240 		    (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip);
241 	} else if (irq_ipl(i) == XC_CPUPOKE_PIL) {
242 		mdb_printf("poke_cpu");
243 	}
244 }
245 
246 static int
247 evtchn_masked(int i)
248 {
249 	return (!!TEST_EVTCHN_BIT(i, &shared_info.evtchn_mask[0]));
250 }
251 
252 static int
253 evtchn_pending(int i)
254 {
255 	return (!!TEST_EVTCHN_BIT(i, &shared_info.evtchn_pending[0]));
256 }
257 
258 static void
259 print_bus(int irq)
260 {
261 	char parent[7];
262 	uintptr_t dip_addr;
263 	struct dev_info	dev_info;
264 	struct autovec avhp;
265 
266 	bzero(&avhp, sizeof (avhp));
267 
268 	if (mdb_ctf_vread(&avhp, "struct autovec",
269 	    (uintptr_t)avec_tbl[irq].avh_link, 0) == -1)
270 		goto fail;
271 
272 	dip_addr = (uintptr_t)avhp.av_dip;
273 
274 	if (dip_addr == NULL)
275 		goto fail;
276 
277 	/*
278 	 * Sigh.  As a result of the perennial confusion of how you do opaque
279 	 * handles, dev_info_t has a funny old type, which means we can't use
280 	 * mdb_ctf_vread() here.
281 	 */
282 
283 	if (mdb_vread(&dev_info, sizeof (struct dev_info), dip_addr) == -1)
284 		goto fail;
285 
286 	dip_addr = (uintptr_t)dev_info.devi_parent;
287 
288 	if (mdb_vread(&dev_info, sizeof (struct dev_info), dip_addr) == -1)
289 		goto fail;
290 
291 	if (mdb_readstr(parent, 7, (uintptr_t)dev_info.devi_node_name) == -1)
292 		goto fail;
293 
294 	mdb_printf("%-6s ", parent);
295 	return;
296 
297 fail:
298 	mdb_printf("-      ");
299 }
300 
301 static void
302 ec_interrupt_dump(int i)
303 {
304 	irq_info_t *irqp = &irq_tbl[i];
305 	char evtchn[8];
306 
307 	if (irqp->ii_type == IRQT_UNBOUND)
308 		return;
309 
310 	if (option_flags & INTR_DISPLAY_INTRSTAT) {
311 		print_cpu(irqp, irqp->ii_u.evtchn);
312 		print_isr(i);
313 		mdb_printf("\n");
314 		return;
315 	}
316 
317 	switch (irqp->ii_type) {
318 	case IRQT_EVTCHN:
319 	case IRQT_VIRQ:
320 		if (irqp->ii_u.index == VIRQ_TIMER) {
321 			strcpy(evtchn, "T");
322 		} else {
323 			mdb_snprintf(evtchn, sizeof (evtchn), "%-7d",
324 			    irqp->ii_u.evtchn);
325 		}
326 		break;
327 	case IRQT_IPI:
328 		strcpy(evtchn, "I");
329 		break;
330 	case IRQT_DEV_EVTCHN:
331 		strcpy(evtchn, "D");
332 		break;
333 	}
334 
335 	/* IRQ */
336 	mdb_printf("%3d  ", i);
337 	/* Vector */
338 	mdb_printf("-    ");
339 	/* Evtchn */
340 	mdb_printf("%-7s", evtchn);
341 	/* IPL */
342 	mdb_printf("%-4d", irq_ipl(i));
343 	/* Bus */
344 	print_bus(i);
345 	/* Trigger */
346 	mdb_printf("%-4s", "Edg");
347 	/* Type */
348 	mdb_printf("%-7s", irq_type(i, 0));
349 	/* CPU */
350 	print_cpu(irqp, irqp->ii_u.evtchn);
351 	/* Share */
352 	mdb_printf("-     ");
353 	/* APIC/INT# */
354 	mdb_printf("-         ");
355 
356 	print_isr(i);
357 
358 	mdb_printf("\n");
359 }
360 
361 /* ARGSUSED */
362 static int
363 interrupts_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
364 {
365 	int i;
366 
367 	option_flags = 0;
368 	if (mdb_getopts(argc, argv,
369 	    'd', MDB_OPT_SETBITS, INTR_DISPLAY_DRVR_INST, &option_flags,
370 	    'i', MDB_OPT_SETBITS, INTR_DISPLAY_INTRSTAT, &option_flags,
371 	    NULL) != argc)
372 		return (DCMD_USAGE);
373 
374 	if (!update_tables())
375 		return (DCMD_ERR);
376 
377 	if (option_flags & INTR_DISPLAY_INTRSTAT) {
378 		mdb_printf("%<u>CPU ");
379 	} else {
380 		mdb_printf("%<u>IRQ  Vect Evtchn IPL Bus    Trg Type   "
381 		    "CPU Share APIC/INT# ");
382 	}
383 	mdb_printf("%s %</u>\n", option_flags & INTR_DISPLAY_DRVR_INST ?
384 	    "Driver Name(s)" : "ISR(s)");
385 
386 	for (i = 0; i < NR_IRQS; i++) {
387 		if (irq_tbl[i].ii_type == IRQT_PIRQ) {
388 			apic_irq_t airq;
389 
390 			if (irq_tbl[i].ii_u.evtchn == 0)
391 				continue;
392 
393 			if (mdb_vread(&airq, sizeof (apic_irq_t),
394 			    (uintptr_t)apic_irq_tbl[i]) == -1)
395 				continue;
396 
397 			apic_interrupt_dump(&airq, &avec_tbl[i], i,
398 			    &irq_tbl[i].ii_u.evtchn, level_tbl[i]);
399 			continue;
400 		}
401 
402 		ec_interrupt_dump(i);
403 	}
404 
405 	return (DCMD_OK);
406 }
407 
408 static void
409 evtchn_dump(int i)
410 {
411 	int irq = evtchn_tbl[i];
412 
413 	if (irq == INVALID_IRQ) {
414 		mdb_printf("%-14s%-7d%-4s%-4s", "unassigned", i, "-", "-");
415 		print_cpu(NULL, i);
416 		if (have_shared_info) {
417 			mdb_printf("%-7d", evtchn_masked(i));
418 			mdb_printf("%-8d", evtchn_pending(i));
419 		}
420 		mdb_printf("\n");
421 		return;
422 	}
423 
424 	/* Type */
425 	mdb_printf("%-14s", irq_type(irq, 1));
426 	/* Evtchn */
427 	mdb_printf("%-7d", i);
428 	/* IRQ */
429 	mdb_printf("%-4d", irq);
430 	/* IPL */
431 	mdb_printf("%-4d", irq_ipl(irq));
432 	/* CPU */
433 	print_cpu(NULL, i);
434 	if (have_shared_info) {
435 		/* Masked/Pending */
436 		mdb_printf("%-7d", evtchn_masked(i));
437 		mdb_printf("%-8d", evtchn_pending(i));
438 	}
439 	/* ISR */
440 	print_isr(irq);
441 
442 	mdb_printf("\n");
443 }
444 
445 /* ARGSUSED */
446 static int
447 evtchns_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
448 {
449 	int i;
450 
451 	option_flags = 0;
452 	if (mdb_getopts(argc, argv,
453 	    'd', MDB_OPT_SETBITS, INTR_DISPLAY_DRVR_INST, &option_flags,
454 	    NULL) != argc)
455 		return (DCMD_USAGE);
456 
457 	if (!update_tables())
458 		return (DCMD_ERR);
459 
460 	if (flags & DCMD_ADDRSPEC) {
461 		/*
462 		 * Note: we allow the invalid evtchn 0, as it can help catch if
463 		 * we incorrectly try to configure it.
464 		 */
465 		if ((int)addr >= NR_EVENT_CHANNELS) {
466 			mdb_warn("Invalid event channel %d.\n", (int)addr);
467 			return (DCMD_ERR);
468 		}
469 	}
470 
471 	mdb_printf("%<u>Type          Evtchn IRQ IPL CPU ");
472 	if (have_shared_info)
473 		mdb_printf("Masked Pending ");
474 
475 	mdb_printf("%s %</u>\n", option_flags & INTR_DISPLAY_DRVR_INST ?
476 	    "Driver Name(s)" : "ISR(s)");
477 
478 	if (flags & DCMD_ADDRSPEC) {
479 		evtchn_dump((int)addr);
480 		return (DCMD_OK);
481 	}
482 
483 	for (i = 0; i < NR_EVENT_CHANNELS; i++) {
484 		if (evtchn_tbl[i] == INVALID_IRQ)
485 			continue;
486 
487 		evtchn_dump(i);
488 	}
489 
490 	return (DCMD_OK);
491 }
492 
493 static void
494 evtchns_help(void)
495 {
496 	mdb_printf("Print valid event channels\n"
497 	    "If %<u>addr%</u> is given, interpret it as an evtchn to print "
498 	    "details of.\n"
499 	    "By default, only interrupt service routine names are printed.\n\n"
500 	    "Switches:\n"
501 	    "  -d   instead of ISR, print <driver_name><instance#>\n");
502 }
503 
504 static const mdb_dcmd_t dcmds[] = {
505 	{ "interrupts", "?[-di]", "print interrupts", interrupts_dump,
506 	    interrupt_help },
507 	{ "evtchns", "?[-d]", "print event channels", evtchns_dump,
508 	    evtchns_help },
509 	{ "softint", "?[-d]", "print soft interrupts", soft_interrupt_dump,
510 	    soft_interrupt_help},
511 	{ NULL }
512 };
513 
514 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, NULL };
515 
516 const mdb_modinfo_t *
517 _mdb_init(void)
518 {
519 	GElf_Sym sym;
520 
521 	if (mdb_lookup_by_name("gld_intr", &sym) != -1)
522 		if (GELF_ST_TYPE(sym.st_info) == STT_FUNC)
523 			gld_intr_addr = (uintptr_t)sym.st_value;
524 
525 	return (&modinfo);
526 }
527