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
64static 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 */
76struct intrhand *intrlev[MAXINTNUM];
77
78void	strayintr(const struct trapframe64 *, int);
79int	intr_list_handler(void *);
80
81extern 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 */
87int ignore_stray = 1;
88int straycnt[16];
89
90void
91strayintr(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 */
132int
133intr_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
156static int intr_biglock_wrapper(void *);
157
158static int
159intr_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 */
178struct intrhand*
179intrhand_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 */
191void
192intr_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 */
295void *
296sparc_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
307void
308sparc_softintr_disestablish(void *cookie)
309{
310
311	kmem_free(cookie, sizeof(struct intrhand));
312}
313
314void
315sparc_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
327int softint_fastintr(void *);
328
329void
330softint_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
356void
357softint_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
369uint64_t sun4v_group_interrupt_major;
370
371int64_t
372sun4v_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
381int64_t
382sun4v_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
390int64_t
391sun4v_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
399int64_t
400sun4v_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
408int64_t
409sun4v_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