plan9fox/sys/src/9/pc/irq.c
cinap_lenrek a041c90431 pc, pc64: move common irq handling code out of trap.c
Move the common irq handling code out of trap.c
into pc/irq.c so that it can be shared between 386
and amd64 ports.
2020-11-22 17:44:21 +01:00

314 lines
6 KiB
C

#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;
static Vctl *vctl[256];
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;
if(ctl = vctl[vno]){
if(ctl->isintr){
m->perf.intrts = perfticks();
m->intr++;
if(vno >= VectorPIC)
m->lastintr = ctl->irq;
}
if(ctl->isr)
ctl->isr(vno);
for(v = ctl; v != nil; v = v->next){
if(v->f)
v->f(ureg, v->a);
}
if(ctl->eoi)
ctl->eoi(vno);
if(ctl->isintr){
intrtime(m, vno);
if(up){
if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER){
/* delaysched set because we held a lock or because our quantum ended */
if(up->delaysched)
sched();
} else {
preempted();
}
}
}
return 1;
}
if(vno < VectorPIC)
return 0;
/*
* An unknown interrupt.
* Check for a default IRQ7. This can happen when
* the IRQ input goes away before the acknowledge.
* In this case, a 'default IRQ7' is generated, but
* the corresponding bit in the ISR isn't set.
* In fact, just ignore all such interrupts.
*/
/* call all interrupt routines, just in case */
for(i = VectorPIC; i <= MaxIrqLAPIC; i++){
ctl = vctl[i];
if(ctl == nil)
continue;
if(!ctl->isintr)
continue;
for(v = ctl; v != nil; v = v->next){
if(v->f)
v->f(ureg, v->a);
}
/* should we do this? */
if(ctl->eoi)
ctl->eoi(i);
}
/* clear the interrupt */
i8259isr(vno);
if(0)print("cpu%d: spurious interrupt %d, last %d\n",
m->machno, vno, m->lastintr);
if(0)if(conf.nmach > 1){
for(i = 0; i < MAXMACH; i++){
Mach *mach;
if(active.machs[i] == 0)
continue;
mach = MACHP(i);
if(m->machno == mach->machno)
continue;
print(" cpu%d: last %d",
mach->machno, mach->lastintr);
}
print("\n");
}
m->spuriousintr++;
return -1;
}
void
trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
{
Vctl *v;
if(vno < 0 || vno >= VectorPIC)
panic("trapenable: vno %d", vno);
if((v = xalloc(sizeof(Vctl))) == nil)
panic("trapenable: out of memory");
v->tbdf = BUSUNKNOWN;
v->f = f;
v->a = a;
strncpy(v->name, name, KNAMELEN-1);
v->name[KNAMELEN-1] = 0;
ilock(&vctllock);
if(vctl[vno])
v->next = vctl[vno]->next;
vctl[vno] = v;
iunlock(&vctllock);
}
void
intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
{
int vno;
Vctl *v;
if(f == nil){
print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
irq, tbdf, name);
return;
}
if(tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))
irq = -1;
/*
* IRQ2 doesn't really exist, it's used to gang the interrupt
* controllers together. A device set to IRQ2 will appear on
* the second interrupt controller as IRQ9.
*/
if(irq == 2)
irq = 9;
if((v = xalloc(sizeof(Vctl))) == nil)
panic("intrenable: out of memory");
v->isintr = 1;
v->irq = irq;
v->tbdf = tbdf;
v->f = f;
v->a = a;
strncpy(v->name, name, KNAMELEN-1);
v->name[KNAMELEN-1] = 0;
ilock(&vctllock);
vno = arch->intrenable(v);
if(vno == -1){
iunlock(&vctllock);
print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
irq, tbdf, v->name);
xfree(v);
return;
}
if(vctl[vno]){
if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
panic("intrenable: handler: %s %s %#p %#p %#p %#p",
vctl[vno]->name, v->name,
vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
v->next = vctl[vno];
}
vctl[vno] = v;
iunlock(&vctllock);
}
void
intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
{
Vctl **pv, *v;
int vno;
if(irq == 2)
irq = 9;
if(arch->intrvecno == nil || (tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))){
/*
* on APIC machine, irq is pretty meaningless
* and disabling a the vector is not implemented.
* however, we still want to remove the matching
* Vctl entry to prevent calling Vctl.f() with a
* stale Vctl.a pointer.
*/
irq = -1;
vno = VectorPIC;
} else {
vno = arch->intrvecno(irq);
}
ilock(&vctllock);
do {
for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
if(v->isintr && (v->irq == irq || irq == -1)
&& v->tbdf == tbdf && v->f == f && v->a == a
&& strcmp(v->name, name) == 0)
break;
}
if(v != nil){
if(v->disable != nil)
(*v->disable)(v);
*pv = v->next;
xfree(v);
if(irq != -1 && vctl[vno] == nil && arch->intrdisable != nil)
arch->intrdisable(irq);
break;
}
} while(irq == -1 && ++vno <= MaxVectorAPIC);
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);
for(vno=0; vno<nelem(vctl); vno++){
for(v=vctl[vno]; v; v=v->next){
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;
}
static void
nmienable(void)
{
int x;
/*
* Hack: should be locked with NVRAM access.
*/
outb(0x70, 0x80); /* NMI latch clear */
outb(0x70, 0);
x = inb(0x61) & 0x07; /* Enable NMI */
outb(0x61, 0x0C|x);
outb(0x61, x);
}
static void
nmihandler(Ureg *ureg, void*)
{
/*
* Don't re-enable, it confuses the crash dumps.
nmienable();
*/
iprint("cpu%d: nmi PC %#p, status %ux\n",
m->machno, ureg->pc, inb(0x61));
while(m->machno != 0)
;
}
void
irqinit(void)
{
addarchfile("irqalloc", 0444, irqallocread, nil);
trapenable(VectorNMI, nmihandler, nil, "nmi");
nmienable();
}