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 2007 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 #include <errno.h> 33 34 #include "intr_common.h" 35 36 static shared_info_t shared_info; 37 static int have_shared_info; 38 static uintptr_t evtchn_cpus_addr; 39 static struct av_head avec_tbl[NR_IRQS]; 40 static irq_info_t irq_tbl[NR_IRQS]; 41 static mec_info_t ipi_tbl[MAXIPL]; 42 static mec_info_t virq_tbl[NR_VIRQS]; 43 static short evtchn_tbl[NR_EVENT_CHANNELS]; 44 static apic_irq_t *apic_irq_tbl[APIC_MAX_VECTOR+1]; 45 static char level_tbl[APIC_MAX_VECTOR+1]; 46 47 static int 48 update_tables(void) 49 { 50 GElf_Sym sym; 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(&ipi_tbl, "ipi_info") == -1) { 59 mdb_warn("failed to read ipi_info"); 60 return (0); 61 } 62 63 if (mdb_readvar(&avec_tbl, "autovect") == -1) { 64 mdb_warn("failed to read autovect"); 65 return (0); 66 } 67 68 if (mdb_readvar(&irq_tbl, "irq_info") == -1) { 69 mdb_warn("failed to read irq_info"); 70 return (0); 71 } 72 73 if (mdb_readvar(&ipi_tbl, "ipi_info") == -1) { 74 mdb_warn("failed to read ipi_info"); 75 return (0); 76 } 77 78 if (mdb_readvar(&virq_tbl, "virq_info") == -1) { 79 mdb_warn("failed to read virq_info"); 80 return (0); 81 } 82 83 if (mdb_readvar(&evtchn_tbl, "evtchn_to_irq") == -1) { 84 mdb_warn("failed to read evtchn_to_irq"); 85 return (0); 86 } 87 88 if (mdb_readvar(&apic_irq_tbl, "apic_irq_table") == -1) { 89 mdb_warn("failed to read apic_irq_table"); 90 return (0); 91 } 92 93 if (mdb_readvar(&level_tbl, "apic_level_intr") == -1) { 94 mdb_warn("failed to read apic_level_intr"); 95 return (0); 96 } 97 98 if (mdb_lookup_by_name("evtchn_cpus", &sym) == -1) { 99 mdb_warn("failed to lookup evtchn_cpus"); 100 return (0); 101 } 102 103 evtchn_cpus_addr = sym.st_value; 104 105 if (mdb_readvar(&shared_info_addr, "HYPERVISOR_shared_info") == -1) { 106 mdb_warn("failed to read HYPERVISOR_shared_info"); 107 return (0); 108 } 109 110 /* 111 * It's normal for this to fail with a domain dump. 112 */ 113 if (mdb_ctf_vread(&shared_info, "shared_info_t", 114 shared_info_addr, 0) != -1) 115 have_shared_info = 1; 116 117 return (1); 118 } 119 120 static const char * 121 virq_type(int irq) 122 { 123 int i; 124 125 for (i = 0; i < NR_VIRQS; i++) { 126 if (virq_tbl[i].mi_irq == irq) 127 break; 128 } 129 130 switch (i) { 131 case VIRQ_TIMER: 132 return ("virq:timer"); 133 case VIRQ_DEBUG: 134 return ("virq:debug"); 135 case VIRQ_CONSOLE: 136 return ("virq:console"); 137 case VIRQ_DOM_EXC: 138 return ("virq:dom exc"); 139 case VIRQ_DEBUGGER: 140 return ("virq:debugger"); 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