1 /* $NetBSD: intr.c,v 1.72 2024/01/15 08:13:45 andvar 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.72 2024/01/15 08:13:45 andvar 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 static int sparc_intr_debug = 0x0;
65 #define DPRINTF(l, s) do { if (sparc_intr_debug & l) printf s; } while (0)
66 #else
67 #define DPRINTF(l, s)
68 #endif
69
70 /*
71 * The following array is to used by locore.s to map interrupt packets
72 * to the proper IPL to send ourselves a softint. It should be filled
73 * in as the devices are probed. We should eventually change this to a
74 * vector table and call these things directly.
75 */
76 struct intrhand *intrlev[MAXINTNUM];
77
78 void strayintr(const struct trapframe64 *, int);
79 int intr_list_handler(void *);
80
81 extern struct evcnt intr_evcnts[];
82
83 /*
84 * Stray interrupt handler. Clear it if possible.
85 * If not, and if we get 10 interrupts in 10 seconds, panic.
86 */
87 int ignore_stray = 1;
88 int straycnt[16];
89
90 void
strayintr(const struct trapframe64 * fp,int vectored)91 strayintr(const struct trapframe64 *fp, int vectored)
92 {
93 static int straytime, nstray;
94 int timesince;
95 char buf[256];
96 #if 0
97 extern int swallow_zsintrs;
98 #endif
99
100 if (fp->tf_pil < 16)
101 straycnt[(int)fp->tf_pil]++;
102
103 if (ignore_stray)
104 return;
105
106 /* If we're in polled mode ignore spurious interrupts */
107 if ((fp->tf_pil == PIL_SER) /* && swallow_zsintrs */) return;
108
109 snprintb(buf, sizeof(buf), PSTATE_BITS,
110 (fp->tf_tstate>>TSTATE_PSTATE_SHIFT));
111 printf("stray interrupt ipl %u pc=%llx npc=%llx pstate=%s vectored=%d\n",
112 fp->tf_pil, (unsigned long long)fp->tf_pc,
113 (unsigned long long)fp->tf_npc, buf, vectored);
114
115 timesince = time_second - straytime;
116 if (timesince <= 10) {
117 if (++nstray > 500)
118 panic("crazy interrupts");
119 } else {
120 straytime = time_second;
121 nstray = 1;
122 }
123 #ifdef DDB
124 Debugger();
125 #endif
126 }
127
128 /*
129 * PCI devices can share interrupts so we need to have
130 * a handler to hand out interrupts.
131 */
132 int
intr_list_handler(void * arg)133 intr_list_handler(void *arg)
134 {
135 int claimed = 0;
136 struct intrhand *ih = (struct intrhand *)arg;
137
138 if (!arg) panic("intr_list_handler: no handlers!");
139 while (ih && !claimed) {
140 claimed = (*ih->ih_fun)(ih->ih_arg);
141 #ifdef DEBUG
142 {
143 extern int intrdebug;
144 if (intrdebug & 1)
145 printf("intr %p %x arg %p %s\n",
146 ih, ih->ih_number, ih->ih_arg,
147 claimed ? "claimed" : "");
148 }
149 #endif
150 ih = ih->ih_next;
151 }
152 return (claimed);
153 }
154
155 #ifdef MULTIPROCESSOR
156 static int intr_biglock_wrapper(void *);
157
158 static int
intr_biglock_wrapper(void * vp)159 intr_biglock_wrapper(void *vp)
160 {
161 struct intrhand *ih = vp;
162 int ret;
163
164 KERNEL_LOCK(1, NULL);
165 ret = (*ih->ih_realfun)(ih->ih_realarg);
166 KERNEL_UNLOCK_ONE(NULL);
167
168 return ret;
169 }
170 #endif
171
172 /*
173 * Allocate memory for interrupt handler.
174 * The allocated memory is initialized with zeros so
175 * e.g. pointers in the intrhand structure are properly initialized.
176 * A valid pointer is always returned by the function.
177 */
178 struct intrhand*
intrhand_alloc(void)179 intrhand_alloc(void)
180 {
181 struct intrhand *ih = kmem_zalloc(sizeof(struct intrhand), KM_NOSLEEP);
182 if (ih == NULL)
183 panic("%s: failed to allocate intrhand", __func__);
184 return ih;
185 }
186
187 /*
188 * Attach an interrupt handler to the vector chain for the given level.
189 * This is not possible if it has been taken away as a fast vector.
190 */
191 void
intr_establish(int level,bool mpsafe,struct intrhand * ih)192 intr_establish(int level, bool mpsafe, struct intrhand *ih)
193 {
194 struct intrhand *q = NULL;
195 int s;
196 #ifdef DEBUG
197 int opil = ih->ih_pil;
198 #endif
199
200 /*
201 * This is O(N^2) for long chains, but chains are never long
202 * and we do want to preserve order.
203 */
204 #ifdef DIAGNOSTIC
205 if (ih->ih_pil != level)
206 printf("%s: caller %p did not pre-set ih_pil\n",
207 __func__, __builtin_return_address(0));
208 if (ih->ih_pending != 0)
209 printf("%s: caller %p did not pre-set ih_pending to zero\n",
210 __func__, __builtin_return_address(0));
211 #endif
212 ih->ih_pil = level; /* XXXX caller should have done this before */
213 ih->ih_pending = 0; /* XXXX caller should have done this before */
214 ih->ih_next = NULL;
215
216 /*
217 * no need for a separate counter if ivec == 0, in that case there's
218 * either only one device using the interrupt level and there's already
219 * a counter for it or it's something special like psycho's error
220 * interrupts
221 */
222 if (ih->ih_ivec != 0 && intrlev[ih->ih_number] == NULL) {
223 snprintf(ih->ih_name, sizeof(ih->ih_name), "%x", ih->ih_ivec);
224 evcnt_attach_dynamic(&ih->ih_cnt, EVCNT_TYPE_INTR,
225 &intr_evcnts[level], "ivec", ih->ih_name);
226 }
227
228 /* opil because we overwrote it above with level */
229 DPRINTF(INTRDB_ESTABLISH,
230 ("%s: level %x ivec %x inumber %x pil %x\n",
231 __func__, level, ih->ih_ivec, ih->ih_number, opil));
232
233 #ifdef MULTIPROCESSOR
234 if (!mpsafe) {
235 ih->ih_realarg = ih->ih_arg;
236 ih->ih_realfun = ih->ih_fun;
237 ih->ih_arg = ih;
238 ih->ih_fun = intr_biglock_wrapper;
239 }
240 #endif
241
242 /* XXXSMP */
243 s = splhigh();
244 /*
245 * Store in fast lookup table
246 */
247 #ifdef NOT_DEBUG
248 if (!ih->ih_number) {
249 printf("\nintr_establish: NULL vector fun %p arg %p pil %p\n",
250 ih->ih_fun, ih->ih_arg, ih->ih_number, ih->ih_pil);
251 Debugger();
252 }
253 #endif
254 if (ih->ih_number < MAXINTNUM && ih->ih_number >= 0) {
255 if ((q = intrlev[ih->ih_number])) {
256 struct intrhand *nih;
257 /*
258 * Interrupt is already there. We need to create a
259 * new interrupt handler and interpose it.
260 */
261 DPRINTF(INTRDB_REUSE,
262 ("intr_establish: intr reused %x\n",
263 ih->ih_number));
264 if (q->ih_fun != intr_list_handler) {
265 nih = intrhand_alloc();
266 /* Point the old IH at the new handler */
267 *nih = *q;
268 nih->ih_next = NULL;
269 q->ih_arg = (void *)nih;
270 q->ih_fun = intr_list_handler;
271 }
272 /* Add the ih to the head of the list */
273 ih->ih_next = (struct intrhand *)q->ih_arg;
274 q->ih_arg = (void *)ih;
275 } else {
276 intrlev[ih->ih_number] = ih;
277 }
278 #ifdef NOT_DEBUG
279 printf("\nintr_establish: vector %x pil %x mapintr %p "
280 "clrintr %p fun %p arg %p\n",
281 ih->ih_number, ih->ih_pil, (void *)ih->ih_map,
282 (void *)ih->ih_clr, (void *)ih->ih_fun,
283 (void *)ih->ih_arg);
284 /*Debugger();*/
285 #endif
286 } else
287 panic("intr_establish: bad intr number %x", ih->ih_number);
288
289 splx(s);
290 }
291
292 /*
293 * Prepare an interrupt handler used for send_softint.
294 */
295 void *
sparc_softintr_establish(int pil,int (* fun)(void *),void * arg)296 sparc_softintr_establish(int pil, int (*fun)(void *), void *arg)
297 {
298 struct intrhand *ih;
299
300 ih = intrhand_alloc();
301 ih->ih_fun = fun;
302 ih->ih_pil = pil;
303 ih->ih_arg = arg;
304 return ih;
305 }
306
307 void
sparc_softintr_disestablish(void * cookie)308 sparc_softintr_disestablish(void *cookie)
309 {
310
311 kmem_free(cookie, sizeof(struct intrhand));
312 }
313
314 void
sparc_softintr_schedule(void * cookie)315 sparc_softintr_schedule(void *cookie)
316 {
317 struct intrhand *ih = (struct intrhand *)cookie;
318
319 send_softint(-1, ih->ih_pil, ih);
320 }
321
322 #ifdef __HAVE_FAST_SOFTINTS
323 /*
324 * MD implementation of FAST software interrupt framework
325 */
326
327 int softint_fastintr(void *);
328
329 void
softint_init_md(lwp_t * l,u_int level,uintptr_t * machdep)330 softint_init_md(lwp_t *l, u_int level, uintptr_t *machdep)
331 {
332 struct intrhand *ih;
333 int pil;
334
335 switch (level) {
336 case SOFTINT_BIO:
337 pil = IPL_SOFTBIO;
338 break;
339 case SOFTINT_NET:
340 pil = IPL_SOFTNET;
341 break;
342 case SOFTINT_SERIAL:
343 pil = IPL_SOFTSERIAL;
344 break;
345 case SOFTINT_CLOCK:
346 pil = IPL_SOFTCLOCK;
347 break;
348 default:
349 panic("softint_init_md");
350 }
351
352 ih = sparc_softintr_establish(pil, softint_fastintr, l);
353 *machdep = (uintptr_t)ih;
354 }
355
356 void
softint_trigger(uintptr_t machdep)357 softint_trigger(uintptr_t machdep)
358 {
359 struct intrhand *ih = (struct intrhand *)machdep;
360
361 send_softint(-1, ih->ih_pil, ih);
362 }
363 #endif /* __HAVE_FAST_SOFTINTS */
364
365 #ifdef SUN4V
366
367 #include <machine/hypervisor.h>
368
369 uint64_t sun4v_group_interrupt_major;
370
371 int64_t
sun4v_intr_devino_to_sysino(uint64_t devhandle,uint64_t devino,uint64_t * ino)372 sun4v_intr_devino_to_sysino(uint64_t devhandle, uint64_t devino, uint64_t *ino)
373 {
374 if (sun4v_group_interrupt_major < 3)
375 return hv_intr_devino_to_sysino(devhandle, devino, ino);
376
377 *ino = devino;
378 return H_EOK;
379 }
380
381 int64_t
sun4v_intr_setcookie(uint64_t devhandle,uint64_t ino,uint64_t cookie_value)382 sun4v_intr_setcookie(uint64_t devhandle, uint64_t ino, uint64_t cookie_value)
383 {
384 if (sun4v_group_interrupt_major < 3)
385 return H_EOK;
386
387 return hv_vintr_setcookie(devhandle, ino, cookie_value);
388 }
389
390 int64_t
sun4v_intr_setenabled(uint64_t devhandle,uint64_t ino,uint64_t intr_enabled)391 sun4v_intr_setenabled(uint64_t devhandle, uint64_t ino, uint64_t intr_enabled)
392 {
393 if (sun4v_group_interrupt_major < 3)
394 return hv_intr_setenabled(ino, intr_enabled);
395
396 return hv_vintr_setenabled(devhandle, ino, intr_enabled);
397 }
398
399 int64_t
sun4v_intr_setstate(uint64_t devhandle,uint64_t ino,uint64_t intr_state)400 sun4v_intr_setstate(uint64_t devhandle, uint64_t ino, uint64_t intr_state)
401 {
402 if (sun4v_group_interrupt_major < 3)
403 return hv_intr_setstate(ino, intr_state);
404
405 return hv_vintr_setstate(devhandle, ino, intr_state);
406 }
407
408 int64_t
sun4v_intr_settarget(uint64_t devhandle,uint64_t ino,uint64_t cpuid)409 sun4v_intr_settarget(uint64_t devhandle, uint64_t ino, uint64_t cpuid)
410 {
411 if (sun4v_group_interrupt_major < 3)
412 return hv_intr_settarget(ino, cpuid);
413
414 return hv_vintr_settarget(devhandle, ino, cpuid);
415 }
416
417 #endif
418