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 }