#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; vnonext){ 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(); }