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