1 /* $NetBSD: intr.c,v 1.73 2026/08/21 18:21:51 palle Exp $ */ 2 3 /* 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This software was developed by the Computer Systems Engineering group 8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 * contributed to Berkeley. 10 * 11 * All advertising materials mentioning features or use of this software 12 * must display the following acknowledgement: 13 * This product includes software developed by the University of 14 * California, Lawrence Berkeley Laboratory. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT OT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * @(#)intr.c 8.3 (Berkeley) 11/11/93 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.73 2026/08/21 18:21:51 palle Exp $"); 45 46 #include "opt_ddb.h" 47 #include "opt_multiprocessor.h" 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/kernel.h> 52 #include <sys/kmem.h> 53 54 #include <dev/cons.h> 55 56 #include <machine/cpu.h> 57 #include <machine/ctlreg.h> 58 #include <machine/instr.h> 59 #include <machine/trap.h> 60 61 #ifdef DEBUG 62 #define INTRDB_ESTABLISH 0x01 63 #define INTRDB_REUSE 0x02 64 #define INTRDB_SUN4V 0x04 65 static int sparc_intr_debug = 0x0; 66 #define DPRINTF(l, s) do { if (sparc_intr_debug & l) printf s; } while (0) 67 #else 68 #define DPRINTF(l, s) 69 #endif 70 71 /* 72 * The following array is to used by locore.s to map interrupt packets 73 * to the proper IPL to send ourselves a softint. It should be filled 74 * in as the devices are probed. We should eventually change this to a 75 * vector table and call these things directly. 76 */ 77 struct intrhand *intrlev[MAXINTNUM]; 78 79 void strayintr(const struct trapframe64 *, int); 80 int intr_list_handler(void *); 81 82 extern struct evcnt intr_evcnts[]; 83 84 /* 85 * Stray interrupt handler. Clear it if possible. 86 * If not, and if we get 10 interrupts in 10 seconds, panic. 87 */ 88 int ignore_stray = 1; 89 int straycnt[16]; 90 91 void 92 strayintr(const struct trapframe64 *fp, int vectored) 93 { 94 static int straytime, nstray; 95 int timesince; 96 char buf[256]; 97 #if 0 98 extern int swallow_zsintrs; 99 #endif 100 101 if (fp->tf_pil < 16) 102 straycnt[(int)fp->tf_pil]++; 103 104 if (ignore_stray) 105 return; 106 107 /* If we're in polled mode ignore spurious interrupts */ 108 if ((fp->tf_pil == PIL_SER) /* && swallow_zsintrs */) return; 109 110 snprintb(buf, sizeof(buf), PSTATE_BITS, 111 (fp->tf_tstate>>TSTATE_PSTATE_SHIFT)); 112 printf("stray interrupt ipl %u pc=%llx npc=%llx pstate=%s vectored=%d\n", 113 fp->tf_pil, (unsigned long long)fp->tf_pc, 114 (unsigned long long)fp->tf_npc, buf, vectored); 115 116 timesince = time_second - straytime; 117 if (timesince <= 10) { 118 if (++nstray > 500) 119 panic("crazy interrupts"); 120 } else { 121 straytime = time_second; 122 nstray = 1; 123 } 124 #ifdef DDB 125 Debugger(); 126 #endif 127 } 128 129 /* 130 * PCI devices can share interrupts so we need to have 131 * a handler to hand out interrupts. 132 */ 133 int 134 intr_list_handler(void *arg) 135 { 136 int claimed = 0; 137 struct intrhand *ih = (struct intrhand *)arg; 138 139 if (!arg) panic("intr_list_handler: no handlers!"); 140 while (ih && !claimed) { 141 claimed = (*ih->ih_fun)(ih->ih_arg); 142 #ifdef DEBUG 143 { 144 extern int intrdebug; 145 if (intrdebug & 1) 146 printf("intr %p %x arg %p %s\n", 147 ih, ih->ih_number, ih->ih_arg, 148 claimed ? "claimed" : ""); 149 } 150 #endif 151 ih = ih->ih_next; 152 } 153 return (claimed); 154 } 155 156 #ifdef MULTIPROCESSOR 157 static int intr_biglock_wrapper(void *); 158 159 static int 160 intr_biglock_wrapper(void *vp) 161 { 162 struct intrhand *ih = vp; 163 int ret; 164 165 KERNEL_LOCK(1, NULL); 166 ret = (*ih->ih_realfun)(ih->ih_realarg); 167 KERNEL_UNLOCK_ONE(NULL); 168 169 return ret; 170 } 171 #endif 172 173 /* 174 * Allocate memory for interrupt handler. 175 * The allocated memory is initialized with zeros so 176 * e.g. pointers in the intrhand structure are properly initialized. 177 * A valid pointer is always returned by the function. 178 */ 179 struct intrhand* 180 intrhand_alloc(void) 181 { 182 struct intrhand *ih = kmem_zalloc(sizeof(struct intrhand), KM_NOSLEEP); 183 if (ih == NULL) 184 panic("%s: failed to allocate intrhand", __func__); 185 return ih; 186 } 187 188 /* 189 * Attach an interrupt handler to the vector chain for the given level. 190 * This is not possible if it has been taken away as a fast vector. 191 */ 192 void 193 intr_establish(int level, bool mpsafe, struct intrhand *ih) 194 { 195 struct intrhand *q = NULL; 196 int s; 197 #ifdef DEBUG 198 int opil = ih->ih_pil; 199 #endif 200 201 /* 202 * This is O(N^2) for long chains, but chains are never long 203 * and we do want to preserve order. 204 */ 205 #ifdef DIAGNOSTIC 206 if (ih->ih_pil != level) 207 printf("%s: caller %p did not pre-set ih_pil\n", 208 __func__, __builtin_return_address(0)); 209 if (ih->ih_pending != 0) 210 printf("%s: caller %p did not pre-set ih_pending to zero\n", 211 __func__, __builtin_return_address(0)); 212 #endif 213 ih->ih_pil = level; /* XXXX caller should have done this before */ 214 ih->ih_pending = 0; /* XXXX caller should have done this before */ 215 ih->ih_next = NULL; 216 217 /* 218 * no need for a separate counter if ivec == 0, in that case there's 219 * either only one device using the interrupt level and there's already 220 * a counter for it or it's something special like psycho's error 221 * interrupts 222 */ 223 if (ih->ih_ivec != 0 && intrlev[ih->ih_number] == NULL) { 224 snprintf(ih->ih_name, sizeof(ih->ih_name), "%x", ih->ih_ivec); 225 evcnt_attach_dynamic(&ih->ih_cnt, EVCNT_TYPE_INTR, 226 &intr_evcnts[level], "ivec", ih->ih_name); 227 } 228 229 /* opil because we overwrote it above with level */ 230 DPRINTF(INTRDB_ESTABLISH, 231 ("%s: level %x ivec %x inumber %x pil %x\n", 232 __func__, level, ih->ih_ivec, ih->ih_number, opil)); 233 234 #ifdef MULTIPROCESSOR 235 if (!mpsafe) { 236 ih->ih_realarg = ih->ih_arg; 237 ih->ih_realfun = ih->ih_fun; 238 ih->ih_arg = ih; 239 ih->ih_fun = intr_biglock_wrapper; 240 } 241 #endif 242 243 /* XXXSMP */ 244 s = splhigh(); 245 /* 246 * Store in fast lookup table 247 */ 248 #ifdef NOT_DEBUG 249 if (!ih->ih_number) { 250 printf("\nintr_establish: NULL vector fun %p arg %p pil %p\n", 251 ih->ih_fun, ih->ih_arg, ih->ih_number, ih->ih_pil); 252 Debugger(); 253 } 254 #endif 255 if (ih->ih_number < MAXINTNUM && ih->ih_number >= 0) { 256 if ((q = intrlev[ih->ih_number])) { 257 struct intrhand *nih; 258 /* 259 * Interrupt is already there. We need to create a 260 * new interrupt handler and interpose it. 261 */ 262 DPRINTF(INTRDB_REUSE, 263 ("intr_establish: intr reused %x\n", 264 ih->ih_number)); 265 if (q->ih_fun != intr_list_handler) { 266 nih = intrhand_alloc(); 267 /* Point the old IH at the new handler */ 268 *nih = *q; 269 nih->ih_next = NULL; 270 q->ih_arg = (void *)nih; 271 q->ih_fun = intr_list_handler; 272 } 273 /* Add the ih to the head of the list */ 274 ih->ih_next = (struct intrhand *)q->ih_arg; 275 q->ih_arg = (void *)ih; 276 } else { 277 intrlev[ih->ih_number] = ih; 278 } 279 #ifdef NOT_DEBUG 280 printf("\nintr_establish: vector %x pil %x mapintr %p " 281 "clrintr %p fun %p arg %p\n", 282 ih->ih_number, ih->ih_pil, (void *)ih->ih_map, 283 (void *)ih->ih_clr, (void *)ih->ih_fun, 284 (void *)ih->ih_arg); 285 /*Debugger();*/ 286 #endif 287 } else 288 panic("intr_establish: bad intr number %x", ih->ih_number); 289 290 splx(s); 291 } 292 293 /* 294 * Prepare an interrupt handler used for send_softint. 295 */ 296 void * 297 sparc_softintr_establish(int pil, int (*fun)(void *), void *arg) 298 { 299 struct intrhand *ih; 300 301 ih = intrhand_alloc(); 302 ih->ih_fun = fun; 303 ih->ih_pil = pil; 304 ih->ih_arg = arg; 305 return ih; 306 } 307 308 void 309 sparc_softintr_disestablish(void *cookie) 310 { 311 312 kmem_free(cookie, sizeof(struct intrhand)); 313 } 314 315 void 316 sparc_softintr_schedule(void *cookie) 317 { 318 struct intrhand *ih = (struct intrhand *)cookie; 319 320 send_softint(-1, ih->ih_pil, ih); 321 } 322 323 #ifdef __HAVE_FAST_SOFTINTS 324 /* 325 * MD implementation of FAST software interrupt framework 326 */ 327 328 int softint_fastintr(void *); 329 330 void 331 softint_init_md(lwp_t *l, u_int level, uintptr_t *machdep) 332 { 333 struct intrhand *ih; 334 int pil; 335 336 switch (level) { 337 case SOFTINT_BIO: 338 pil = IPL_SOFTBIO; 339 break; 340 case SOFTINT_NET: 341 pil = IPL_SOFTNET; 342 break; 343 case SOFTINT_SERIAL: 344 pil = IPL_SOFTSERIAL; 345 break; 346 case SOFTINT_CLOCK: 347 pil = IPL_SOFTCLOCK; 348 break; 349 default: 350 panic("softint_init_md"); 351 } 352 353 ih = sparc_softintr_establish(pil, softint_fastintr, l); 354 *machdep = (uintptr_t)ih; 355 } 356 357 void 358 softint_trigger(uintptr_t machdep) 359 { 360 struct intrhand *ih = (struct intrhand *)machdep; 361 362 send_softint(-1, ih->ih_pil, ih); 363 } 364 #endif /* __HAVE_FAST_SOFTINTS */ 365 366 #ifdef SUN4V 367 368 #include <machine/hypervisor.h> 369 370 uint64_t sun4v_group_interrupt_major; 371 372 int64_t 373 sun4v_intr_devino_to_sysino(uint64_t devhandle, uint64_t devino, uint64_t *ino) 374 { 375 DPRINTF(INTRDB_SUN4V, 376 ("%s: devhandle %#lx devino %#lx\n", __func__, devhandle, devino)); 377 378 if (sun4v_group_interrupt_major < 2) { 379 DPRINTF(INTRDB_SUN4V, ("%s: using deprecated api\n", __func__)); 380 return hv_intr_devino_to_sysino(devhandle, devino, ino); 381 } 382 *ino = devino; 383 DPRINTF(INTRDB_SUN4V, 384 ("%s: returning devino as ino %#lx\n", __func__, *ino)); 385 386 return H_EOK; 387 } 388 389 int64_t 390 sun4v_intr_setcookie(uint64_t devhandle, uint64_t ino, uint64_t cookie_value) 391 { 392 DPRINTF(INTRDB_SUN4V, 393 ("%s: devhandle %#lx ino %#lx cookie_value %#lx\n", 394 __func__, devhandle, ino, cookie_value)); 395 396 if (sun4v_group_interrupt_major < 2) { 397 DPRINTF(INTRDB_SUN4V, ("%s: using deprecated api\n", __func__)); 398 return H_EOK; 399 } 400 401 return hv_vintr_setcookie(devhandle, ino, cookie_value); 402 } 403 404 int64_t 405 sun4v_intr_setenabled(uint64_t devhandle, uint64_t ino, uint64_t intr_enabled) 406 { 407 DPRINTF(INTRDB_SUN4V, 408 ("%s: devhandle %#lx ino %#lx intr_enabled %#lx\n", 409 __func__, devhandle, ino, intr_enabled)); 410 411 if (sun4v_group_interrupt_major < 2) { 412 DPRINTF(INTRDB_SUN4V, ("%s: using deprecated api\n", __func__)); 413 return hv_intr_setenabled(ino, intr_enabled); 414 } 415 return hv_vintr_setenabled(devhandle, ino, intr_enabled); 416 } 417 418 int64_t 419 sun4v_intr_setstate(uint64_t devhandle, uint64_t ino, uint64_t intr_state) 420 { 421 DPRINTF(INTRDB_SUN4V, 422 ("%s: devhandle %#lx ino %#lx intr_state %#lx\n", 423 __func__, devhandle, ino, intr_state)); 424 425 if (sun4v_group_interrupt_major < 2) { 426 DPRINTF(INTRDB_SUN4V, ("%s: using deprecated api\n", __func__)); 427 return hv_intr_setstate(ino, intr_state); 428 } 429 430 return hv_vintr_setstate(devhandle, ino, intr_state); 431 } 432 433 int64_t 434 sun4v_intr_settarget(uint64_t devhandle, uint64_t ino, uint64_t cpuid) 435 { 436 DPRINTF(INTRDB_SUN4V, 437 ("%s: devhandle %#lx ino %#lx cpuid %#lx\n", 438 __func__, devhandle, ino, cpuid)); 439 440 if (sun4v_group_interrupt_major < 2) { 441 DPRINTF(INTRDB_SUN4V, ("%s: using deprecated api\n", __func__)); 442 return hv_intr_settarget(ino, cpuid); 443 } 444 445 return hv_vintr_settarget(devhandle, ino, cpuid); 446 } 447 448 #endif 449