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 }