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