shithub: riscv

Download patch

ref: a041c90431c5987496ac1d5de4f330f70fd2966f
parent: 97008caa4174cee3dd849d9962c529897717e333
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Nov 22 12:44:21 EST 2020

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.

--- /dev/null
+++ b/sys/src/9/pc/irq.c
@@ -1,0 +1,313 @@
+#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();
+}
--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -54,6 +54,7 @@
 	main.$O\
 	memory.$O\
 	mmu.$O\
+	irq.$O\
 	trap.$O\
 	bootargs.$O\
 	$CONF.root.$O\
--- a/sys/src/9/pc/trap.c
+++ b/sys/src/9/pc/trap.c
@@ -9,6 +9,9 @@
 #include	"../port/error.h"
 #include	<trace.h>
 
+extern int irqhandled(Ureg*, int);
+extern void irqinit(void);
+
 void	noted(Ureg*, ulong);
 
 static void debugexc(Ureg*, void*);
@@ -18,173 +21,6 @@
 static void unexpected(Ureg*, void*);
 static void _dumpstack(Ureg*);
 
-static Lock vctllock;
-static Vctl *vctl[256];
-
-enum
-{
-	Ntimevec = 20		/* number of time buckets for each intr */
-};
-ulong intrtimes[256][Ntimevec];
-
-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.
-		 * 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;
-}
-
-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);
-}
-
-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);
-}
-
 /*
  * Minimal trap setup.  Just enough so that we can panic
  * on traps (bugs) during kernel initialization.
@@ -228,6 +64,8 @@
 void
 trapinit(void)
 {
+	irqinit();
+
 	/*
 	 * Special traps.
 	 * Syscall() is called directly without going through trap().
@@ -237,9 +75,6 @@
 	trapenable(VectorPF, fault386, 0, "fault386");
 	trapenable(Vector2F, doublefault, 0, "doublefault");
 	trapenable(Vector15, unexpected, 0, "unexpected");
-	nmienable();
-
-	addarchfile("irqalloc", 0444, irqallocread, nil);
 }
 
 static char* excname[32] = {
@@ -277,27 +112,18 @@
 	"31 (reserved)",
 };
 
-/*
- *  keep histogram of interrupt service times
- */
-void
-intrtime(Mach*, int vno)
+static int
+usertrap(int vno)
 {
-	ulong diff;
-	ulong x;
+	char buf[ERRMAX];
 
-	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]++;
+	if(vno < nelem(excname)){
+		spllo();
+		sprint(buf, "sys: trap: %s", excname[vno]);
+		postnote(up, 1, buf, NDebug);
+		return 1;
+	}
+	return 0;
 }
 
 /* go to user space */
@@ -325,12 +151,8 @@
 void
 trap(Ureg* ureg)
 {
-	int clockintr, i, vno, user;
-	char buf[ERRMAX];
-	Vctl *ctl, *v;
-	Mach *mach;
+	int vno, user;
 
-	m->perf.intrts = perfticks();
 	user = userureg(ureg);
 	if(user){
 		up->dbgreg = ureg;
@@ -337,100 +159,8 @@
 		cycles(&up->kentry);
 	}
 
-	clockintr = 0;
-
 	vno = ureg->trap;
-	if(ctl = vctl[vno]){
-		if(ctl->isintr){
-			m->intr++;
-			if(vno >= VectorPIC && vno != VectorSYSCALL)
-				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(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER)
-				clockintr = 1;
-
-			if(up && !clockintr)
-				preempted();
-		}
-	}
-	else if(vno < nelem(excname) && user){
-		spllo();
-		sprint(buf, "sys: trap: %s", excname[vno]);
-		postnote(up, 1, buf, NDebug);
-	}
-	else if(vno >= VectorPIC && vno != VectorSYSCALL){
-		/*
-		 * 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++){
-				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++;
-		if(user)
-			kexit(ureg);
-		return;
-	}
-	else{
-		if(vno == VectorNMI){
-			/*
-			 * Don't re-enable, it confuses the crash dumps.
-			nmienable();
-			 */
-			iprint("cpu%d: nmi PC %#8.8lux, status %ux\n",
-				m->machno, ureg->pc, inb(0x61));
-			while(m->machno != 0)
-				;
-		}
-
+	if(!irqhandled(ureg, vno) && (!user || !usertrap(vno))){
 		if(!user){
 			void (*pc)(void);
 			ulong *sp; 
@@ -493,12 +223,6 @@
 		panic("unknown trap/intr: %d", vno);
 	}
 	splhi();
-
-	/* delaysched set because we held a lock or because our quantum ended */
-	if(up && up->delaysched && clockintr){
-		sched();
-		splhi();
-	}
 
 	if(user){
 		if(up->procctl || up->nnote)
--- a/sys/src/9/pc64/mkfile
+++ b/sys/src/9/pc64/mkfile
@@ -52,6 +52,7 @@
 	main.$O\
 	memory.$O\
 	mmu.$O\
+	irq.$O\
 	trap.$O\
 	bootargs.$O\
 	$CONF.root.$O\
--- a/sys/src/9/pc64/trap.c
+++ b/sys/src/9/pc64/trap.c
@@ -9,6 +9,9 @@
 #include	"../port/error.h"
 #include	<trace.h>
 
+extern int irqhandled(Ureg*, int);
+extern void irqinit(void);
+
 void	noted(Ureg*, ulong);
 
 static void debugexc(Ureg*, void*);
@@ -18,177 +21,7 @@
 static void unexpected(Ureg*, void*);
 static void _dumpstack(Ureg*);
 
-static Lock vctllock;
-static Vctl *vctl[256];
-
-enum
-{
-	Ntimevec = 20		/* number of time buckets for each intr */
-};
-ulong intrtimes[256][Ntimevec];
-
 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;
-}
-
-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);
-}
-
-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);
-}
-
-void
 trapinit0(void)
 {
 	u32int d1, v;
@@ -232,6 +65,8 @@
 void
 trapinit(void)
 {
+	irqinit();
+
 	/*
 	 * Special traps.
 	 * Syscall() is called directly without going through trap().
@@ -241,8 +76,6 @@
 	trapenable(VectorPF, faultamd64, 0, "faultamd64");
 	trapenable(Vector2F, doublefault, 0, "doublefault");
 	trapenable(Vector15, unexpected, 0, "unexpected");
-	nmienable();
-	addarchfile("irqalloc", 0444, irqallocread, nil);
 }
 
 static char* excname[32] = {
@@ -280,27 +113,18 @@
 	"31 (reserved)",
 };
 
-/*
- *  keep histogram of interrupt service times
- */
-void
-intrtime(Mach*, int vno)
+static int
+usertrap(int vno)
 {
-	ulong diff;
-	ulong x;
+	char buf[ERRMAX];
 
-	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]++;
+	if(vno < nelem(excname)){
+		spllo();
+		sprint(buf, "sys: trap: %s", excname[vno]);
+		postnote(up, 1, buf, NDebug);
+		return 1;
+	}
+	return 0;
 }
 
 /* go to user space */
@@ -321,12 +145,8 @@
 void
 trap(Ureg *ureg)
 {
-	int clockintr, i, vno, user;
-	char buf[ERRMAX];
-	Vctl *ctl, *v;
-	Mach *mach;
+	int vno, user;
 
-	m->perf.intrts = perfticks();
 	user = userureg(ureg);
 	if(user){
 		up->dbgreg = ureg;
@@ -333,100 +153,8 @@
 		cycles(&up->kentry);
 	}
 
-	clockintr = 0;
-
 	vno = ureg->type;
-
-	if(ctl = vctl[vno]){
-		if(ctl->isintr){
-			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(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER)
-				clockintr = 1;
-
-			if(up && !clockintr)
-				preempted();
-		}
-	}
-	else if(vno < nelem(excname) && user){
-		spllo();
-		sprint(buf, "sys: trap: %s", excname[vno]);
-		postnote(up, 1, buf, NDebug);
-	}
-	else if(vno >= VectorPIC){
-		/*
-		 * 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++){
-				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++;
-		if(user)
-			kexit(ureg);
-		return;
-	}
-	else{
-		if(vno == VectorNMI){
-			/*
-			 * 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)
-				;
-		}
-
+	if(!irqhandled(ureg, vno) && (!user || !usertrap(vno))){
 		if(!user){
 			void (*pc)(void);
 
@@ -463,12 +191,6 @@
 		panic("unknown trap/intr: %d", vno);
 	}
 	splhi();
-
-	/* delaysched set because we held a lock or because our quantum ended */
-	if(up && up->delaysched && clockintr){
-		sched();
-		splhi();
-	}
 
 	if(user){
 		if(up->procctl || up->nnote)