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