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