2020-11-22 16:44:21 +00:00
|
|
|
#include "u.h"
|
|
|
|
#include "tos.h"
|
|
|
|
#include "../port/lib.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
#include "io.h"
|
|
|
|
#include "ureg.h"
|
|
|
|
#include "../port/error.h"
|
|
|
|
|
|
|
|
static Lock vctllock;
|
2020-11-29 16:43:22 +00:00
|
|
|
static Vctl *vclock, *vctl[256];
|
2020-11-22 16:44:21 +00:00
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
Ntimevec = 20 /* number of time buckets for each intr */
|
|
|
|
};
|
|
|
|
ulong intrtimes[256][Ntimevec];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* keep histogram of interrupt service times
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
intrtime(Mach*, int vno)
|
|
|
|
{
|
|
|
|
ulong diff;
|
|
|
|
ulong x;
|
|
|
|
|
|
|
|
x = perfticks();
|
|
|
|
diff = x - m->perf.intrts;
|
|
|
|
m->perf.intrts = x;
|
|
|
|
|
|
|
|
m->perf.inintr += diff;
|
|
|
|
if(up == nil && m->perf.inidle > diff)
|
|
|
|
m->perf.inidle -= diff;
|
|
|
|
|
|
|
|
diff /= m->cpumhz*100; /* quantum = 100µsec */
|
|
|
|
if(diff >= Ntimevec)
|
|
|
|
diff = Ntimevec-1;
|
|
|
|
intrtimes[vno][diff]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
irqhandled(Ureg *ureg, int vno)
|
|
|
|
{
|
|
|
|
Vctl *ctl, *v;
|
|
|
|
int i;
|
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
ctl = vctl[vno];
|
|
|
|
if(ctl != nil){
|
|
|
|
if(vno < VectorPIC){
|
|
|
|
(*ctl->f)(ureg, ctl->a);
|
|
|
|
return 1;
|
2020-11-22 16:44:21 +00:00
|
|
|
}
|
2020-11-29 16:43:22 +00:00
|
|
|
|
|
|
|
m->perf.intrts = perfticks();
|
|
|
|
m->intr++;
|
|
|
|
m->lastintr = ctl->irq;
|
|
|
|
if(ctl->isr != nil)
|
|
|
|
(*ctl->isr)(vno);
|
|
|
|
for(v = ctl; v != nil; v = v->next)
|
|
|
|
(*v->f)(ureg, v->a);
|
|
|
|
if(ctl->eoi != nil)
|
|
|
|
(*ctl->eoi)(vno);
|
|
|
|
intrtime(m, vno);
|
|
|
|
|
|
|
|
if(up != nil){
|
|
|
|
if(ctl == vclock){
|
|
|
|
/* delaysched set because we held a lock or because our quantum ended */
|
|
|
|
if(up->delaysched)
|
|
|
|
sched();
|
|
|
|
} else {
|
|
|
|
preempted();
|
2020-11-22 16:44:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
if(vno < VectorPIC || vno == VectorSYSCALL)
|
2020-11-22 16:44:21 +00:00
|
|
|
return 0;
|
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
m->spuriousintr++;
|
|
|
|
if(arch->intrspurious != nil && (*arch->intrspurious)(vno) == 0)
|
|
|
|
return 1; /* false alarm */
|
2020-11-22 16:44:21 +00:00
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
iprint("cpu%d: spurious interrupt %d, last %d\n",
|
2020-11-22 16:44:21 +00:00
|
|
|
m->machno, vno, m->lastintr);
|
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
/* call all non-local interrupt routines, just in case */
|
|
|
|
for(i = VectorPIC; i < nelem(vctl); i++){
|
|
|
|
ctl = vctl[i];
|
|
|
|
if(ctl == nil || ctl == vclock || ctl->local)
|
|
|
|
continue;
|
|
|
|
for(v = ctl; v != nil; v = v->next)
|
|
|
|
(*v->f)(ureg, v->a);
|
2020-11-22 16:44:21 +00:00
|
|
|
}
|
2020-11-29 16:43:22 +00:00
|
|
|
|
2020-11-22 16:44:21 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
|
|
|
|
{
|
|
|
|
Vctl *v;
|
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
if(f == nil){
|
|
|
|
print("trapenable: nil handler for %d, for %s\n",
|
|
|
|
vno, name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(vno < 0 || vno >= VectorPIC){
|
|
|
|
print("trapenable: vno %d out of range", vno);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-22 16:44:21 +00:00
|
|
|
if((v = xalloc(sizeof(Vctl))) == nil)
|
|
|
|
panic("trapenable: out of memory");
|
2020-11-29 16:43:22 +00:00
|
|
|
|
2020-11-22 16:44:21 +00:00
|
|
|
v->f = f;
|
|
|
|
v->a = a;
|
2020-11-29 16:43:22 +00:00
|
|
|
|
|
|
|
v->tbdf = BUSUNKNOWN;
|
|
|
|
v->irq = -1;
|
|
|
|
v->vno = vno;
|
|
|
|
v->cpu = -1;
|
|
|
|
|
2020-11-22 16:44:21 +00:00
|
|
|
strncpy(v->name, name, KNAMELEN-1);
|
|
|
|
v->name[KNAMELEN-1] = 0;
|
|
|
|
|
|
|
|
ilock(&vctllock);
|
2020-11-29 16:43:22 +00:00
|
|
|
if(vctl[vno] != nil){
|
|
|
|
print("trapenable: vno %d assigned twice: %s %s\n",
|
|
|
|
vno, vctl[vno]->name, v->name);
|
|
|
|
iunlock(&vctllock);
|
|
|
|
xfree(v);
|
|
|
|
return;
|
|
|
|
}
|
2020-11-22 16:44:21 +00:00
|
|
|
vctl[vno] = v;
|
|
|
|
iunlock(&vctllock);
|
|
|
|
}
|
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
static Vctl*
|
|
|
|
delayfree(Vctl *v)
|
|
|
|
{
|
|
|
|
static Vctl *q[4];
|
|
|
|
static uint x;
|
|
|
|
Vctl *r;
|
|
|
|
|
|
|
|
r = q[x], q[x] = v;
|
|
|
|
x = (x+1) % nelem(q);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2020-11-22 16:44:21 +00:00
|
|
|
void
|
|
|
|
intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
|
|
|
|
{
|
2020-11-29 16:43:22 +00:00
|
|
|
Vctl **pv, *v;
|
2020-11-22 16:44:21 +00:00
|
|
|
|
|
|
|
if(f == nil){
|
|
|
|
print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
|
|
|
|
irq, tbdf, name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
if(arch->intrirqno != nil)
|
|
|
|
irq = (*arch->intrirqno)(irq, tbdf);
|
2020-11-22 16:44:21 +00:00
|
|
|
|
|
|
|
if((v = xalloc(sizeof(Vctl))) == nil)
|
|
|
|
panic("intrenable: out of memory");
|
2020-11-29 16:43:22 +00:00
|
|
|
|
2020-11-22 16:44:21 +00:00
|
|
|
v->f = f;
|
|
|
|
v->a = a;
|
2020-11-29 16:43:22 +00:00
|
|
|
|
|
|
|
v->tbdf = tbdf;
|
|
|
|
v->irq = irq;
|
|
|
|
v->vno = -1;
|
|
|
|
v->cpu = -1;
|
|
|
|
|
2020-11-22 16:44:21 +00:00
|
|
|
strncpy(v->name, name, KNAMELEN-1);
|
|
|
|
v->name[KNAMELEN-1] = 0;
|
|
|
|
|
|
|
|
ilock(&vctllock);
|
2020-11-29 16:43:22 +00:00
|
|
|
v->vno = (*arch->intrassign)(v);
|
|
|
|
if(v->vno < VectorPIC || v->vno >= nelem(vctl)){
|
|
|
|
print("intrenable: couldn't assign irq %d, tbdf 0x%uX for %s\n",
|
2020-11-22 16:44:21 +00:00
|
|
|
irq, tbdf, v->name);
|
2020-11-29 16:43:22 +00:00
|
|
|
Unlockandfree:
|
|
|
|
iunlock(&vctllock);
|
|
|
|
if(v != nil)
|
|
|
|
xfree(v);
|
2020-11-22 16:44:21 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-11-29 16:43:22 +00:00
|
|
|
pv = &vctl[v->vno];
|
|
|
|
if(*pv != nil){
|
|
|
|
if((*pv)->isr != v->isr || (*pv)->eoi != v->eoi){
|
|
|
|
print("intrenable: incompatible handler: %s %s %#p %#p %#p %#p\n",
|
|
|
|
(*pv)->name, v->name,
|
|
|
|
(*pv)->isr, v->isr,
|
|
|
|
(*pv)->eoi, v->eoi);
|
|
|
|
goto Unlockandfree;
|
|
|
|
}
|
|
|
|
if(*pv == vclock)
|
|
|
|
pv = &vclock->next;
|
|
|
|
v->next = *pv;
|
|
|
|
}
|
|
|
|
if(strcmp(name, "clock") == 0)
|
|
|
|
vclock = v;
|
|
|
|
*pv = v;
|
|
|
|
if(v->enable != nil){
|
|
|
|
coherence();
|
|
|
|
if((*v->enable)(v, pv != &vctl[v->vno] || v->next != nil) < 0){
|
|
|
|
print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
|
|
|
|
irq, tbdf, v->name);
|
|
|
|
*pv = v->next;
|
|
|
|
if(v == vclock)
|
|
|
|
vclock = nil;
|
|
|
|
if(conf.nmach > 1)
|
|
|
|
v = delayfree(v);
|
|
|
|
goto Unlockandfree;
|
|
|
|
}
|
2020-11-22 16:44:21 +00:00
|
|
|
}
|
|
|
|
iunlock(&vctllock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
|
|
|
|
{
|
|
|
|
Vctl **pv, *v;
|
|
|
|
int vno;
|
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
if(arch->intrirqno != nil)
|
|
|
|
irq = (*arch->intrirqno)(irq, tbdf);
|
|
|
|
|
|
|
|
if(irq != -1 && arch->intrvecno != nil) {
|
|
|
|
vno = (*arch->intrvecno)(irq);
|
|
|
|
if(vno < VectorPIC || vno >= nelem(vctl)){
|
|
|
|
irq = -1;
|
|
|
|
vno = VectorPIC;
|
|
|
|
}
|
|
|
|
} else {
|
2020-11-22 16:44:21 +00:00
|
|
|
irq = -1;
|
|
|
|
vno = VectorPIC;
|
|
|
|
}
|
2020-11-29 16:43:22 +00:00
|
|
|
|
2020-11-22 16:44:21 +00:00
|
|
|
ilock(&vctllock);
|
|
|
|
do {
|
|
|
|
for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
|
2020-11-29 16:43:22 +00:00
|
|
|
if((v->irq == irq || irq == -1)
|
2020-11-22 16:44:21 +00:00
|
|
|
&& v->tbdf == tbdf && v->f == f && v->a == a
|
|
|
|
&& strcmp(v->name, name) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(v != nil){
|
2020-11-29 16:43:22 +00:00
|
|
|
if(v->disable != nil){
|
|
|
|
if((*v->disable)(v, pv != &vctl[vno] || v->next != nil) < 0){
|
|
|
|
print("intrdisable: couldn't disable irq %d, tbdf 0x%uX for %s\n",
|
|
|
|
irq, tbdf, name);
|
|
|
|
}
|
|
|
|
coherence();
|
|
|
|
}
|
2020-11-22 16:44:21 +00:00
|
|
|
*pv = v->next;
|
2020-11-29 16:43:22 +00:00
|
|
|
if(v == vclock)
|
|
|
|
vclock = nil;
|
|
|
|
if(conf.nmach > 1)
|
|
|
|
v = delayfree(v);
|
|
|
|
iunlock(&vctllock);
|
|
|
|
if(v != nil)
|
|
|
|
xfree(v);
|
|
|
|
return;
|
2020-11-22 16:44:21 +00:00
|
|
|
}
|
2020-11-29 16:43:22 +00:00
|
|
|
} while(irq == -1 && ++vno < nelem(vctl));
|
2020-11-22 16:44:21 +00:00
|
|
|
iunlock(&vctllock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
irqallocread(Chan*, void *a, long n, vlong offset)
|
|
|
|
{
|
|
|
|
char buf[2*(11+1)+KNAMELEN+1+1];
|
|
|
|
int vno, m;
|
|
|
|
Vctl *v;
|
|
|
|
|
|
|
|
if(n < 0 || offset < 0)
|
|
|
|
error(Ebadarg);
|
|
|
|
|
2020-11-29 16:43:22 +00:00
|
|
|
for(vno = 0; vno < nelem(vctl); vno++){
|
|
|
|
for(v = vctl[vno]; v != nil; v = v->next){
|
2020-11-22 16:44:21 +00:00
|
|
|
m = snprint(buf, sizeof(buf), "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
|
|
|
|
offset -= m;
|
|
|
|
if(offset >= 0)
|
|
|
|
continue;
|
|
|
|
if(n > -offset)
|
|
|
|
n = -offset;
|
|
|
|
offset += m;
|
|
|
|
memmove(a, buf+offset, n);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
irqinit(void)
|
|
|
|
{
|
|
|
|
addarchfile("irqalloc", 0444, irqallocread, nil);
|
|
|
|
}
|