#include "opt_multiprocessor.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: interrupt.c,v 1.100 2021/11/10 16:53:28 msaitoh Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/vmmeter.h>
#include <sys/sched.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/intr.h>
#include <sys/device.h>
#include <sys/cpu.h>
#include <sys/atomic.h>
#include <machine/cpuvar.h>
#include <machine/autoconf.h>
#include <machine/reg.h>
#include <machine/rpb.h>
#include <machine/frame.h>
#include <machine/cpuconf.h>
#include <machine/alpha.h>
struct scbvec scb_iovectab[SCB_VECTOIDX(SCB_SIZE - SCB_IOVECBASE)]
__read_mostly;
static void scb_stray(void *, u_long);
void
scb_init(void)
{
u_long i;
for (i = 0; i < SCB_NIOVECS; i++) {
scb_iovectab[i].scb_func = scb_stray;
scb_iovectab[i].scb_arg = NULL;
}
}
static void
scb_stray(void *arg, u_long vec)
{
printf("WARNING: stray interrupt, vector 0x%lx\n", vec);
}
void
scb_set(u_long vec, void (*func)(void *, u_long), void *arg)
{
u_long idx;
KASSERT(mutex_owned(&cpu_lock));
if (vec < SCB_IOVECBASE || vec >= SCB_SIZE ||
(vec & (SCB_VECSIZE - 1)) != 0)
panic("scb_set: bad vector 0x%lx", vec);
idx = SCB_VECTOIDX(vec - SCB_IOVECBASE);
if (scb_iovectab[idx].scb_func != scb_stray)
panic("scb_set: vector 0x%lx already occupied", vec);
scb_iovectab[idx].scb_arg = arg;
alpha_mb();
scb_iovectab[idx].scb_func = func;
alpha_mb();
}
u_long
scb_alloc(void (*func)(void *, u_long), void *arg)
{
u_long vec, idx;
KASSERT(mutex_owned(&cpu_lock));
for (vec = SCB_SIZE - SCB_VECSIZE;
vec >= SCB_IOVECBASE; vec -= SCB_VECSIZE) {
idx = SCB_VECTOIDX(vec - SCB_IOVECBASE);
if (scb_iovectab[idx].scb_func == scb_stray) {
scb_iovectab[idx].scb_arg = arg;
alpha_mb();
scb_iovectab[idx].scb_func = func;
alpha_mb();
return (vec);
}
}
return (SCB_ALLOC_FAILED);
}
void
scb_free(u_long vec)
{
u_long idx;
KASSERT(mutex_owned(&cpu_lock));
if (vec < SCB_IOVECBASE || vec >= SCB_SIZE ||
(vec & (SCB_VECSIZE - 1)) != 0)
panic("scb_free: bad vector 0x%lx", vec);
idx = SCB_VECTOIDX(vec - SCB_IOVECBASE);
if (scb_iovectab[idx].scb_func == scb_stray)
panic("scb_free: vector 0x%lx is empty", vec);
scb_iovectab[idx].scb_func = scb_stray;
alpha_mb();
scb_iovectab[idx].scb_arg = (void *) vec;
alpha_mb();
}
void
interrupt(unsigned long a0, unsigned long a1, unsigned long a2,
struct trapframe *framep)
{
struct cpu_info *ci = curcpu();
struct cpu_softc *sc = ci->ci_softc;
switch (a0) {
case ALPHA_INTR_XPROC:
#if defined(MULTIPROCESSOR)
ci->ci_intrdepth++;
alpha_ipi_process(ci, framep);
if (ci->ci_cpuid == hwrpb->rpb_primary_cpu_id &&
hwrpb->rpb_txrdy != 0)
cpu_iccb_receive();
ci->ci_intrdepth--;
#else
printf("WARNING: received interprocessor interrupt!\n");
#endif
break;
case ALPHA_INTR_CLOCK:
ci->ci_intrdepth += 0x10;
sc->sc_evcnt_clock.ev_count++;
ci->ci_data.cpu_nintr++;
if (platform.clockintr) {
(*platform.clockintr)((struct clockframe *)framep);
#if defined(MULTIPROCESSOR)
if (alpha_use_cctr) {
cc_hardclock(ci);
}
#endif
if ((++ci->ci_schedstate.spc_schedticks & 0x3f) == 0 &&
schedhz != 0)
schedclock(ci->ci_curlwp);
}
ci->ci_intrdepth -= 0x10;
break;
case ALPHA_INTR_ERROR:
ci->ci_intrdepth++;
a0 = alpha_pal_rdmces();
if (platform.mcheck_handler != NULL &&
(void *)framep->tf_regs[FRAME_PC] != XentArith)
(*platform.mcheck_handler)(a0, framep, a1, a2);
else
machine_check(a0, framep, a1, a2);
ci->ci_intrdepth--;
break;
case ALPHA_INTR_DEVICE:
{
const int idx = SCB_VECTOIDX(a1 - SCB_IOVECBASE);
KDASSERT(a1 >= SCB_IOVECBASE && a1 < SCB_SIZE);
atomic_inc_ulong(&sc->sc_evcnt_device.ev_count);
ci->ci_intrdepth++;
ci->ci_data.cpu_nintr++;
struct scbvec * const scb = &scb_iovectab[idx];
(*scb->scb_func)(scb->scb_arg, a1);
ci->ci_intrdepth--;
break;
}
case ALPHA_INTR_PERF:
printf("WARNING: received performance counter interrupt!\n");
break;
case ALPHA_INTR_PASSIVE:
#if 0
printf("WARNING: received passive release interrupt vec "
"0x%lx\n", a1);
#endif
break;
default:
printf("unexpected interrupt: type 0x%lx vec 0x%lx "
"a2 0x%lx"
#if defined(MULTIPROCESSOR)
" cpu %lu"
#endif
"\n", a0, a1, a2
#if defined(MULTIPROCESSOR)
, ci->ci_cpuid
#endif
);
panic("interrupt");
}
}
void
machine_check(unsigned long mces, struct trapframe *framep,
unsigned long vector, unsigned long param)
{
const char *type;
struct mchkinfo *mcp;
static struct timeval ratelimit[1];
mcp = &curcpu()->ci_mcinfo;
if ((mces & (ALPHA_MCES_MIP|ALPHA_MCES_SCE|ALPHA_MCES_PCE)) == 0) {
type = "fatal machine check or error (unknown type)";
goto fatal;
}
if (mces & ALPHA_MCES_MIP) {
if (!mcp->mc_expected) {
type = "unexpected machine check";
goto fatal;
}
mcp->mc_expected = 0;
mcp->mc_received = 1;
}
if (mces & ALPHA_MCES_SCE)
printf("Warning: received system correctable error.\n");
if (mces & ALPHA_MCES_PCE)
printf("Warning: received processor correctable error.\n");
alpha_pal_wrmces(mces);
return;
fatal:
alpha_pal_wrmces(mces);
if ((void *)framep->tf_regs[FRAME_PC] == XentArith) {
rlprintf(ratelimit, "Stray machine check\n");
return;
}
printf("\n");
printf("%s:\n", type);
printf("\n");
printf(" mces = 0x%lx\n", mces);
printf(" vector = 0x%lx\n", vector);
printf(" param = 0x%lx\n", param);
printf(" pc = 0x%lx\n", framep->tf_regs[FRAME_PC]);
printf(" ra = 0x%lx\n", framep->tf_regs[FRAME_RA]);
printf(" code = 0x%lx\n", *(unsigned long *)(param + 0x10));
printf(" curlwp = %p\n", curlwp);
if (curlwp != NULL)
printf(" pid = %d.%d, comm = %s\n",
curproc->p_pid, curlwp->l_lid,
curproc->p_comm);
printf("\n");
panic("machine check");
}
int
badaddr(void *addr, size_t size)
{
return (badaddr_read(addr, size, NULL));
}
int
badaddr_read(void *addr, size_t size, void *rptr)
{
lwp_t * const l = curlwp;
KPREEMPT_DISABLE(l);
struct mchkinfo *mcp = &curcpu()->ci_mcinfo;
long rcpt;
int rv;
alpha_pal_draina();
mcp->mc_received = 0;
mcp->mc_expected = 1;
alpha_mb();
switch (size) {
case sizeof (uint8_t):
rcpt = *(volatile uint8_t *)addr;
break;
case sizeof (uint16_t):
rcpt = *(volatile uint16_t *)addr;
break;
case sizeof (uint32_t):
rcpt = *(volatile uint32_t *)addr;
break;
case sizeof (uint64_t):
rcpt = *(volatile uint64_t *)addr;
break;
default:
panic("badaddr: invalid size (%ld)", size);
}
alpha_mb();
alpha_mb();
alpha_pal_draina();
mcp->mc_expected = 0;
rv = mcp->mc_received;
mcp->mc_received = 0;
if (rptr && rv == 0) {
switch (size) {
case sizeof (uint8_t):
*(volatile uint8_t *)rptr = rcpt;
break;
case sizeof (uint16_t):
*(volatile uint16_t *)rptr = rcpt;
break;
case sizeof (uint32_t):
*(volatile uint32_t *)rptr = rcpt;
break;
case sizeof (uint64_t):
*(volatile uint64_t *)rptr = rcpt;
break;
}
}
KPREEMPT_ENABLE(l);
return (rv);
}
#define SOFTINT_TO_IPL(si) \
(ALPHA_PSL_IPL_SOFT_LO + ((ALPHA_IPL2_SOFTINTS >> (si)) & 1))
#define SOFTINTS_ELIGIBLE(ipl) \
((ALPHA_ALL_SOFTINTS << ((ipl) << 1)) & ALPHA_ALL_SOFTINTS)
__CTASSERT(SOFTINT_TO_IPL(SOFTINT_CLOCK) == ALPHA_PSL_IPL_SOFT_LO);
__CTASSERT(SOFTINT_TO_IPL(SOFTINT_BIO) == ALPHA_PSL_IPL_SOFT_LO);
__CTASSERT(SOFTINT_TO_IPL(SOFTINT_NET) == ALPHA_PSL_IPL_SOFT_HI);
__CTASSERT(SOFTINT_TO_IPL(SOFTINT_SERIAL) == ALPHA_PSL_IPL_SOFT_HI);
__CTASSERT(IPL_SOFTCLOCK == ALPHA_PSL_IPL_SOFT_LO);
__CTASSERT(IPL_SOFTBIO == ALPHA_PSL_IPL_SOFT_LO);
__CTASSERT(IPL_SOFTNET == ALPHA_PSL_IPL_SOFT_HI);
__CTASSERT(IPL_SOFTSERIAL == ALPHA_PSL_IPL_SOFT_HI);
__CTASSERT(SOFTINT_CLOCK_MASK & 0x3);
__CTASSERT(SOFTINT_BIO_MASK & 0x3);
__CTASSERT(SOFTINT_NET_MASK & 0xc);
__CTASSERT(SOFTINT_SERIAL_MASK & 0xc);
__CTASSERT(SOFTINT_COUNT == 4);
__CTASSERT((ALPHA_ALL_SOFTINTS & ~0xfUL) == 0);
__CTASSERT(SOFTINTS_ELIGIBLE(IPL_NONE) == ALPHA_ALL_SOFTINTS);
__CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTCLOCK) == ALPHA_IPL2_SOFTINTS);
__CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTBIO) == ALPHA_IPL2_SOFTINTS);
__CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTNET) == 0);
__CTASSERT(SOFTINTS_ELIGIBLE(IPL_SOFTSERIAL) == 0);
void
softint_trigger(uintptr_t const machdep)
{
KASSERT(alpha_pal_rdps() == ALPHA_PSL_IPL_HIGH);
curcpu()->ci_ssir |= machdep;
}
void
softint_init_md(lwp_t * const l, u_int const level, uintptr_t * const machdep)
{
lwp_t ** lp = &l->l_cpu->ci_silwps[level];
KASSERT(*lp == NULL || *lp == l);
*lp = l;
const uintptr_t si_bit = __BIT(level);
KASSERT(si_bit & ALPHA_ALL_SOFTINTS);
*machdep = si_bit;
}
#define DOSOFTINT(level) \
if (ssir & SOFTINT_##level##_MASK) { \
ci->ci_ssir &= ~SOFTINT_##level##_MASK; \
alpha_softint_switchto(l, IPL_SOFT##level, \
ci->ci_silwps[SOFTINT_##level]); \
KASSERT(alpha_pal_rdps() == ALPHA_PSL_IPL_HIGH); \
continue; \
} \
void
alpha_softint_dispatch(int const ipl)
{
struct lwp * const l = curlwp;
struct cpu_info * const ci = l->l_cpu;
unsigned long ssir;
const unsigned long eligible = SOFTINTS_ELIGIBLE(ipl);
KASSERT(alpha_pal_rdps() == ALPHA_PSL_IPL_HIGH);
for (;;) {
ssir = ci->ci_ssir & eligible;
if (ssir == 0)
break;
DOSOFTINT(SERIAL);
DOSOFTINT(NET);
DOSOFTINT(BIO);
DOSOFTINT(CLOCK);
}
}
void
spllower(int const ipl)
{
if (ipl < ALPHA_PSL_IPL_SOFT_HI && curcpu()->ci_ssir) {
(void) alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
alpha_softint_dispatch(ipl);
}
(void) alpha_pal_swpipl(ipl);
}
bool
cpu_intr_p(void)
{
return curcpu()->ci_intrdepth != 0;
}
void (*alpha_intr_redistribute)(void);
void
cpu_intr_redistribute(void)
{
if (alpha_intr_redistribute != NULL)
(*alpha_intr_redistribute)();
}
unsigned int
cpu_intr_count(struct cpu_info * const ci)
{
return ci->ci_nintrhand;
}
void
rlprintf(struct timeval *t, const char *fmt, ...)
{
va_list ap;
static const struct timeval msgperiod[1] = {{ 5, 0 }};
if (!ratecheck(t, msgperiod))
return;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}