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