#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.72 2024/01/15 08:13:45 andvar Exp $");
#include "opt_ddb.h"
#include "opt_multiprocessor.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <dev/cons.h>
#include <machine/cpu.h>
#include <machine/ctlreg.h>
#include <machine/instr.h>
#include <machine/trap.h>
#ifdef DEBUG
#define INTRDB_ESTABLISH 0x01
#define INTRDB_REUSE 0x02
static int sparc_intr_debug = 0x0;
#define DPRINTF(l, s) do { if (sparc_intr_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)
#endif
struct intrhand *intrlev[MAXINTNUM];
void strayintr(const struct trapframe64 *, int);
int intr_list_handler(void *);
extern struct evcnt intr_evcnts[];
int ignore_stray = 1;
int straycnt[16];
void
strayintr(const struct trapframe64 *fp, int vectored)
{
static int straytime, nstray;
int timesince;
char buf[256];
#if 0
extern int swallow_zsintrs;
#endif
if (fp->tf_pil < 16)
straycnt[(int)fp->tf_pil]++;
if (ignore_stray)
return;
if ((fp->tf_pil == PIL_SER) ) return;
snprintb(buf, sizeof(buf), PSTATE_BITS,
(fp->tf_tstate>>TSTATE_PSTATE_SHIFT));
printf("stray interrupt ipl %u pc=%llx npc=%llx pstate=%s vectored=%d\n",
fp->tf_pil, (unsigned long long)fp->tf_pc,
(unsigned long long)fp->tf_npc, buf, vectored);
timesince = time_second - straytime;
if (timesince <= 10) {
if (++nstray > 500)
panic("crazy interrupts");
} else {
straytime = time_second;
nstray = 1;
}
#ifdef DDB
Debugger();
#endif
}
int
intr_list_handler(void *arg)
{
int claimed = 0;
struct intrhand *ih = (struct intrhand *)arg;
if (!arg) panic("intr_list_handler: no handlers!");
while (ih && !claimed) {
claimed = (*ih->ih_fun)(ih->ih_arg);
#ifdef DEBUG
{
extern int intrdebug;
if (intrdebug & 1)
printf("intr %p %x arg %p %s\n",
ih, ih->ih_number, ih->ih_arg,
claimed ? "claimed" : "");
}
#endif
ih = ih->ih_next;
}
return (claimed);
}
#ifdef MULTIPROCESSOR
static int intr_biglock_wrapper(void *);
static int
intr_biglock_wrapper(void *vp)
{
struct intrhand *ih = vp;
int ret;
KERNEL_LOCK(1, NULL);
ret = (*ih->ih_realfun)(ih->ih_realarg);
KERNEL_UNLOCK_ONE(NULL);
return ret;
}
#endif
struct intrhand*
intrhand_alloc(void)
{
struct intrhand *ih = kmem_zalloc(sizeof(struct intrhand), KM_NOSLEEP);
if (ih == NULL)
panic("%s: failed to allocate intrhand", __func__);
return ih;
}
void
intr_establish(int level, bool mpsafe, struct intrhand *ih)
{
struct intrhand *q = NULL;
int s;
#ifdef DEBUG
int opil = ih->ih_pil;
#endif
#ifdef DIAGNOSTIC
if (ih->ih_pil != level)
printf("%s: caller %p did not pre-set ih_pil\n",
__func__, __builtin_return_address(0));
if (ih->ih_pending != 0)
printf("%s: caller %p did not pre-set ih_pending to zero\n",
__func__, __builtin_return_address(0));
#endif
ih->ih_pil = level;
ih->ih_pending = 0;
ih->ih_next = NULL;
if (ih->ih_ivec != 0 && intrlev[ih->ih_number] == NULL) {
snprintf(ih->ih_name, sizeof(ih->ih_name), "%x", ih->ih_ivec);
evcnt_attach_dynamic(&ih->ih_cnt, EVCNT_TYPE_INTR,
&intr_evcnts[level], "ivec", ih->ih_name);
}
DPRINTF(INTRDB_ESTABLISH,
("%s: level %x ivec %x inumber %x pil %x\n",
__func__, level, ih->ih_ivec, ih->ih_number, opil));
#ifdef MULTIPROCESSOR
if (!mpsafe) {
ih->ih_realarg = ih->ih_arg;
ih->ih_realfun = ih->ih_fun;
ih->ih_arg = ih;
ih->ih_fun = intr_biglock_wrapper;
}
#endif
s = splhigh();
#ifdef NOT_DEBUG
if (!ih->ih_number) {
printf("\nintr_establish: NULL vector fun %p arg %p pil %p\n",
ih->ih_fun, ih->ih_arg, ih->ih_number, ih->ih_pil);
Debugger();
}
#endif
if (ih->ih_number < MAXINTNUM && ih->ih_number >= 0) {
if ((q = intrlev[ih->ih_number])) {
struct intrhand *nih;
DPRINTF(INTRDB_REUSE,
("intr_establish: intr reused %x\n",
ih->ih_number));
if (q->ih_fun != intr_list_handler) {
nih = intrhand_alloc();
*nih = *q;
nih->ih_next = NULL;
q->ih_arg = (void *)nih;
q->ih_fun = intr_list_handler;
}
ih->ih_next = (struct intrhand *)q->ih_arg;
q->ih_arg = (void *)ih;
} else {
intrlev[ih->ih_number] = ih;
}
#ifdef NOT_DEBUG
printf("\nintr_establish: vector %x pil %x mapintr %p "
"clrintr %p fun %p arg %p\n",
ih->ih_number, ih->ih_pil, (void *)ih->ih_map,
(void *)ih->ih_clr, (void *)ih->ih_fun,
(void *)ih->ih_arg);
#endif
} else
panic("intr_establish: bad intr number %x", ih->ih_number);
splx(s);
}
void *
sparc_softintr_establish(int pil, int (*fun)(void *), void *arg)
{
struct intrhand *ih;
ih = intrhand_alloc();
ih->ih_fun = fun;
ih->ih_pil = pil;
ih->ih_arg = arg;
return ih;
}
void
sparc_softintr_disestablish(void *cookie)
{
kmem_free(cookie, sizeof(struct intrhand));
}
void
sparc_softintr_schedule(void *cookie)
{
struct intrhand *ih = (struct intrhand *)cookie;
send_softint(-1, ih->ih_pil, ih);
}
#ifdef __HAVE_FAST_SOFTINTS
int softint_fastintr(void *);
void
softint_init_md(lwp_t *l, u_int level, uintptr_t *machdep)
{
struct intrhand *ih;
int pil;
switch (level) {
case SOFTINT_BIO:
pil = IPL_SOFTBIO;
break;
case SOFTINT_NET:
pil = IPL_SOFTNET;
break;
case SOFTINT_SERIAL:
pil = IPL_SOFTSERIAL;
break;
case SOFTINT_CLOCK:
pil = IPL_SOFTCLOCK;
break;
default:
panic("softint_init_md");
}
ih = sparc_softintr_establish(pil, softint_fastintr, l);
*machdep = (uintptr_t)ih;
}
void
softint_trigger(uintptr_t machdep)
{
struct intrhand *ih = (struct intrhand *)machdep;
send_softint(-1, ih->ih_pil, ih);
}
#endif
#ifdef SUN4V
#include <machine/hypervisor.h>
uint64_t sun4v_group_interrupt_major;
int64_t
sun4v_intr_devino_to_sysino(uint64_t devhandle, uint64_t devino, uint64_t *ino)
{
if (sun4v_group_interrupt_major < 3)
return hv_intr_devino_to_sysino(devhandle, devino, ino);
*ino = devino;
return H_EOK;
}
int64_t
sun4v_intr_setcookie(uint64_t devhandle, uint64_t ino, uint64_t cookie_value)
{
if (sun4v_group_interrupt_major < 3)
return H_EOK;
return hv_vintr_setcookie(devhandle, ino, cookie_value);
}
int64_t
sun4v_intr_setenabled(uint64_t devhandle, uint64_t ino, uint64_t intr_enabled)
{
if (sun4v_group_interrupt_major < 3)
return hv_intr_setenabled(ino, intr_enabled);
return hv_vintr_setenabled(devhandle, ino, intr_enabled);
}
int64_t
sun4v_intr_setstate(uint64_t devhandle, uint64_t ino, uint64_t intr_state)
{
if (sun4v_group_interrupt_major < 3)
return hv_intr_setstate(ino, intr_state);
return hv_vintr_setstate(devhandle, ino, intr_state);
}
int64_t
sun4v_intr_settarget(uint64_t devhandle, uint64_t ino, uint64_t cpuid)
{
if (sun4v_group_interrupt_major < 3)
return hv_intr_settarget(ino, cpuid);
return hv_vintr_settarget(devhandle, ino, cpuid);
}
#endif