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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2018 Joyent, Inc. 24 * Copyright 2026 Oxide Computer Company 25 */ 26 27 #include "intr_common.h" 28 #include <sys/gld.h> 29 #include <sys/gldpriv.h> 30 31 int option_flags; 32 uintptr_t gld_intr_addr; 33 int apic_pir_vect; 34 static struct av_head softvec_tbl[LOCK_LEVEL + 1]; 35 36 static char *businfo_array[] = { 37 " ", 38 "CBUS", 39 "CBUSII", 40 "EISA", 41 "FUTURE", 42 "INTERN", 43 "ISA", 44 "MBI", 45 "MBII", 46 "PCIe", 47 "MPI", 48 "MPSA", 49 "NUBUS", 50 "PCI", 51 "PCMCIA", 52 "TC", 53 "VL", 54 "VME", 55 "XPRESS", 56 " " 57 }; 58 59 void 60 interrupt_help(void) 61 { 62 mdb_printf("Prints the interrupt usage on the system.\n" 63 "By default, only interrupt service routine names are printed.\n\n" 64 "Switches:\n" 65 " -d instead of ISR, print <driver_name><instance#>\n" 66 " -i show like intrstat, cpu# ISR/<driver_name><instance#>\n"); 67 } 68 69 void 70 soft_interrupt_help(void) 71 { 72 mdb_printf("Prints the soft interrupt usage on the system.\n" 73 "By default, only interrupt service routine names are printed.\n\n" 74 "Switch:\n" 75 " -d instead of ISR, print <driver_name><instance#>\n"); 76 } 77 78 /* ARGSUSED */ 79 int 80 soft_interrupt_dump(uintptr_t addr, uint_t flags, int argc, 81 const mdb_arg_t *argv) 82 { 83 int i; 84 struct autovec avhp; 85 ddi_softint_hdl_impl_t hdlp; 86 87 option_flags = 0; 88 if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS, 89 INTR_DISPLAY_DRVR_INST, &option_flags, NULL) != argc) 90 return (DCMD_USAGE); 91 92 if (mdb_readvar(&softvec_tbl, "softvect") == -1) { 93 mdb_warn("failed to read autovect"); 94 return (DCMD_ERR); 95 } 96 97 /* Print the header first */ 98 mdb_printf("%<u>ADDR PEND PIL ARG1 " 99 "ARG2 ISR(s)%</u>\n"); 100 101 /* Walk all the entries */ 102 for (i = 0; i < LOCK_LEVEL + 1; i++) { 103 /* Read the entry, if invalid continue */ 104 if (mdb_vread(&avhp, sizeof (struct autovec), 105 (uintptr_t)softvec_tbl[i].avh_link) == -1) 106 continue; 107 108 do { 109 if (!avhp.av_vector || 110 (mdb_vread(&hdlp, sizeof (ddi_softint_hdl_impl_t), 111 (uintptr_t)avhp.av_intr_id) == -1)) 112 continue; 113 114 mdb_printf("%-16p %-2d %-2d %-16p %-16p", 115 avhp.av_intr_id, mdb_cpuset_find( 116 (uintptr_t)hdlp.ih_pending) != -1 ? 1 : 0, 117 avhp.av_prilevel, avhp.av_intarg1, avhp.av_intarg2); 118 interrupt_print_isr((uintptr_t)avhp.av_vector, 119 (uintptr_t)avhp.av_intarg1, (uintptr_t)hdlp.ih_dip); 120 mdb_printf("\n"); 121 } while (mdb_vread(&avhp, sizeof (struct autovec), 122 (uintptr_t)avhp.av_link) != -1); 123 } 124 125 return (DCMD_OK); 126 } 127 128 void 129 interrupt_print_isr(uintptr_t vector, uintptr_t arg1, uintptr_t dip) 130 { 131 uintptr_t isr_addr = vector; 132 struct dev_info dev_info; 133 134 /* 135 * figure out the real ISR function name from gld_intr() 136 */ 137 if (isr_addr == gld_intr_addr) { 138 gld_mac_info_t macinfo; 139 140 if (mdb_vread(&macinfo, sizeof (gld_mac_info_t), arg1) != -1) { 141 /* verify gld data structure and get the real ISR */ 142 if (macinfo.gldm_GLD_version == GLD_VERSION) 143 isr_addr = (uintptr_t)macinfo.gldm_intr; 144 } 145 } 146 147 if ((option_flags & INTR_DISPLAY_DRVR_INST) && dip) { 148 char drvr_name[MODMAXNAMELEN]; 149 150 if (dip && mdb_devinfo2driver(dip, drvr_name, 151 sizeof (drvr_name)) == 0) { 152 (void) mdb_vread(&dev_info, sizeof (dev_info), dip); 153 mdb_printf("%s#%d", drvr_name, dev_info.devi_instance); 154 } else { 155 mdb_printf("%a", isr_addr); 156 } 157 158 } else { 159 mdb_printf("%a", isr_addr); 160 } 161 } 162 163 /* 164 * get_interrupt_type: 165 * 166 * Get some interrupt related useful information 167 * 168 * NOTE: a0 is clock, c0/d0/e0 are x-calls, e1 is apic_error_intr 169 * d1/d3 are cbe_fire interrupts 170 */ 171 static char * 172 get_interrupt_type(short index) 173 { 174 if (index == RESERVE_INDEX) 175 return ("IPI"); 176 else if (index == ACPI_INDEX) 177 return ("Fixed"); 178 else if (index == MSI_INDEX) 179 return ("MSI"); 180 else if (index == MSIX_INDEX) 181 return ("MSI-X"); 182 else 183 return ("Fixed"); 184 } 185 186 static char * 187 get_apix_interrupt_type(short type) 188 { 189 if (type == APIX_TYPE_IPI) 190 return ("IPI"); 191 else if (type == APIX_TYPE_FIXED) 192 return ("Fixed"); 193 else if (type == APIX_TYPE_MSI) 194 return ("MSI"); 195 else if (type == APIX_TYPE_MSIX) 196 return ("MSI-X"); 197 else 198 return ("Fixed"); 199 } 200 201 void 202 apic_interrupt_dump(apic_irq_t *irqp, struct av_head *avp, 203 int i, ushort_t *evtchnp, char level) 204 { 205 int bus_type; 206 int j; 207 char *intr_type; 208 char ioapic_iline[10]; 209 char ipl[3]; 210 char cpu_assigned[4]; 211 char evtchn[8]; 212 uint32_t assigned_cpu; 213 struct autovec avhp; 214 215 /* If invalid index; continue */ 216 if (!irqp->airq_mps_intr_index || 217 irqp->airq_mps_intr_index == FREE_INDEX) 218 return; 219 220 /* Figure out interrupt type and trigger information */ 221 intr_type = get_interrupt_type(irqp->airq_mps_intr_index); 222 223 /* Figure out IOAPIC number and ILINE number */ 224 if (APIC_IS_MSI_OR_MSIX_INDEX(irqp->airq_mps_intr_index)) 225 (void) mdb_snprintf(ioapic_iline, 10, "- "); 226 else { 227 if (!irqp->airq_ioapicindex && !irqp->airq_intin_no) { 228 if (strcmp(intr_type, "Fixed") == 0) 229 (void) mdb_snprintf(ioapic_iline, 10, 230 "0x%x/0x%x", irqp->airq_ioapicindex, 231 irqp->airq_intin_no); 232 else if (irqp->airq_mps_intr_index == RESERVE_INDEX) 233 (void) mdb_snprintf(ioapic_iline, 10, "- "); 234 else 235 (void) mdb_snprintf(ioapic_iline, 10, " "); 236 } else 237 (void) mdb_snprintf(ioapic_iline, 10, "0x%x/0x%x", 238 irqp->airq_ioapicindex, irqp->airq_intin_no); 239 } 240 241 evtchn[0] = '\0'; 242 if (evtchnp != NULL) 243 (void) mdb_snprintf(evtchn, 8, "%-7hd", *evtchnp); 244 245 assigned_cpu = irqp->airq_temp_cpu; 246 if (assigned_cpu == IRQ_UNINIT || assigned_cpu == IRQ_UNBOUND) 247 assigned_cpu = irqp->airq_cpu; 248 bus_type = irqp->airq_iflag.bustype; 249 250 if (irqp->airq_mps_intr_index == RESERVE_INDEX) { 251 (void) mdb_snprintf(cpu_assigned, 4, "all"); 252 (void) mdb_snprintf(ipl, 3, "%d", avp->avh_hi_pri); 253 } else { 254 (void) mdb_snprintf(cpu_assigned, 4, "%d", assigned_cpu); 255 (void) mdb_snprintf(ipl, 3, "%d", irqp->airq_ipl); 256 } 257 258 /* Print each interrupt entry */ 259 if (option_flags & INTR_DISPLAY_INTRSTAT) 260 mdb_printf("%-4s", cpu_assigned); 261 else 262 mdb_printf("%-3d 0x%x %s%-3s %-6s %-3s %-6s %-4s%-3d %-9s ", 263 i, irqp->airq_vector, evtchn, ipl, 264 (bus_type ? businfo_array[bus_type] : " "), 265 (level ? "Lvl" : "Edg"), 266 intr_type, cpu_assigned, irqp->airq_share, ioapic_iline); 267 268 /* If valid dip found; print driver name */ 269 if (irqp->airq_dip) { 270 (void) mdb_vread(&avhp, sizeof (struct autovec), 271 (uintptr_t)avp->avh_link); 272 273 /* 274 * Loop thru all the shared IRQs 275 */ 276 if (irqp->airq_share) 277 interrupt_print_isr((uintptr_t)avhp.av_vector, 278 (uintptr_t)avhp.av_intarg1, (uintptr_t)avhp.av_dip); 279 280 for (j = 1; irqp->airq_mps_intr_index != FREE_INDEX && 281 j < irqp->airq_share; j++) { 282 if (mdb_vread(&avhp, sizeof (struct autovec), 283 (uintptr_t)avhp.av_link) != -1) { 284 mdb_printf(", "); 285 interrupt_print_isr((uintptr_t)avhp.av_vector, 286 (uintptr_t)avhp.av_intarg1, 287 (uintptr_t)avhp.av_dip); 288 } else { 289 break; 290 } 291 } 292 293 } else { 294 if (irqp->airq_mps_intr_index == RESERVE_INDEX && 295 !irqp->airq_share) { 296 if (irqp->airq_vector == apic_pir_vect) { 297 mdb_printf("pir_ipi"); 298 } else { 299 mdb_printf("poke_cpu"); 300 } 301 } else if (mdb_vread(&avhp, sizeof (struct autovec), 302 (uintptr_t)avp->avh_link) != -1) { 303 mdb_printf("%a", avhp.av_vector); 304 } 305 } 306 mdb_printf("\n"); 307 } 308 309 void 310 apix_interrupt_dump(apix_vector_t *vectp, apic_irq_t *irqp, 311 struct autovec *avp, ushort_t *evtchnp, char level) 312 { 313 int j; 314 int bus_type; 315 char *intr_type; 316 char irq[4]; 317 char ioapic_iline[10]; 318 char ipl[3]; 319 char cpu_assigned[4]; 320 char cpu_vector[10]; 321 char evtchn[8]; 322 323 324 /* If invalid vector state; continue */ 325 if (vectp->v_state == APIX_STATE_FREED || 326 vectp->v_state == APIX_STATE_OBSOLETED) 327 return; 328 329 /* use apic_interrupt_ipi_dump for IPIs */ 330 if (vectp->v_type == APIX_TYPE_IPI) 331 return; 332 333 /* Figure out interrupt type and trigger information */ 334 intr_type = get_apix_interrupt_type(vectp->v_type); 335 336 /* Figure out IOAPIC number and ILINE number */ 337 if (vectp->v_type != APIX_TYPE_FIXED) { 338 level = 0; /* MSI/MSI-X are Edge trigger */ 339 (void) mdb_snprintf(irq, 4, "- "); 340 (void) mdb_snprintf(ioapic_iline, 10, "- "); 341 if (vectp->v_type == APIX_TYPE_IPI) 342 bus_type = BUSTYPE_NONE; 343 else 344 /* statically assign MSI/X with "PCI" */ 345 bus_type = BUSTYPE_PCI; 346 } else { 347 (void) mdb_snprintf(irq, 4, "%d", vectp->v_inum); 348 bus_type = irqp->airq_iflag.bustype; 349 if (!irqp->airq_ioapicindex && !irqp->airq_intin_no) { 350 if (strcmp(intr_type, "Fixed") == 0) 351 (void) mdb_snprintf(ioapic_iline, 10, 352 "0x%x/0x%x", irqp->airq_ioapicindex, 353 irqp->airq_intin_no); 354 else 355 (void) mdb_snprintf(ioapic_iline, 10, "- "); 356 } else 357 (void) mdb_snprintf(ioapic_iline, 10, "0x%x/0x%x", 358 irqp->airq_ioapicindex, irqp->airq_intin_no); 359 } 360 361 evtchn[0] = '\0'; 362 if (evtchnp != NULL) 363 (void) mdb_snprintf(evtchn, 8, "%-7hd", *evtchnp); 364 365 (void) mdb_snprintf(cpu_assigned, 4, "%d", vectp->v_cpuid); 366 (void) mdb_snprintf(cpu_vector, 10, "%d/0x%x", 367 vectp->v_cpuid, vectp->v_vector); 368 369 /* Loop all the shared vectors */ 370 for (j = 0; j < vectp->v_share; ) { 371 /* shared interrupts with one or more ISR removed afterwards */ 372 if (avp->av_vector == NULL) { 373 if (mdb_vread(avp, sizeof (struct autovec), 374 (uintptr_t)avp->av_link) == -1) 375 break; 376 else 377 continue; 378 } 379 380 (void) mdb_snprintf(ipl, 3, "%d", avp->av_prilevel); 381 /* Print each interrupt entry */ 382 if (option_flags & INTR_DISPLAY_INTRSTAT) 383 mdb_printf("%-4s", cpu_assigned); 384 else 385 mdb_printf("%-9s %-3s %s%-3s %-6s %-3s %-6s %-3d " 386 "%-9s ", cpu_vector, irq, evtchn, ipl, 387 (bus_type ? businfo_array[bus_type] : "-"), 388 (level ? "Lvl" : "Edg"), 389 intr_type, vectp->v_share, ioapic_iline); 390 391 interrupt_print_isr((uintptr_t)avp->av_vector, 392 (uintptr_t)avp->av_intarg1, (uintptr_t)avp->av_dip); 393 mdb_printf("\n"); 394 395 if (++j == vectp->v_share) 396 break; /* done */ 397 398 if (mdb_vread(avp, sizeof (struct autovec), 399 (uintptr_t)avp->av_link) == -1) 400 break; 401 } 402 } 403 404 void 405 apix_interrupt_ipi_dump(apix_vector_t *vectp, struct autovec *avp, 406 ushort_t *evtchnp) 407 { 408 char *intr_type = "IPI"; 409 char ioapic_iline[10]; 410 char ipl[3]; 411 char cpu_assigned[4]; 412 char cpu_vector[10]; 413 char evtchn[8]; 414 415 /* If invalid vector state; continue */ 416 if (vectp->v_state == APIX_STATE_FREED || 417 vectp->v_state == APIX_STATE_OBSOLETED) 418 return; 419 420 if (vectp->v_type != APIX_TYPE_IPI) 421 return; 422 423 /* No IOAPIC number and ILINE number info */ 424 (void) mdb_snprintf(ioapic_iline, 10, "- "); 425 426 evtchn[0] = '\0'; 427 if (evtchnp != NULL) 428 (void) mdb_snprintf(evtchn, 8, "%-7hd", *evtchnp); 429 430 /* IPI targeted ALL cpus */ 431 mdb_snprintf(cpu_assigned, 4, "all"); 432 (void) mdb_snprintf(cpu_vector, 10, "%s/0x%x", 433 "all", vectp->v_vector); 434 /* IPI is not shared interrupt, so we can get the IPL from v_pri */ 435 (void) mdb_snprintf(ipl, 3, "%d", vectp->v_pri); 436 437 /* Print each interrupt entry */ 438 if (option_flags & INTR_DISPLAY_INTRSTAT) 439 mdb_printf("%-4s", cpu_assigned); 440 else 441 mdb_printf("%-9s %-3s %s%-3s %-6s %-3s %-6s %-3d %-9s ", 442 cpu_vector, "- ", evtchn, ipl, "- ", "Edg", 443 intr_type, vectp->v_share, ioapic_iline); 444 if (!vectp->v_share) { 445 if (vectp->v_vector == apic_pir_vect) { 446 mdb_printf("pir_ipi"); 447 } else { 448 mdb_printf("poke_cpu"); 449 } 450 } else { 451 mdb_printf("%a", avp->av_vector); 452 } 453 454 mdb_printf("\n"); 455 } 456