1078 lines · 22.1 KB · C
RawDownload
#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"#include	<trace.h> static int trapinited; void	noted(Ureg*, ulong); static void debugbpt(Ureg*, void*);static void fault386(Ureg*, void*);static void doublefault(Ureg*, void*);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]; voidintrenable(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)){		print("intrenable: got unassigned irq %d, tbdf 0x%uX for %s\n",			irq, tbdf, name);		irq = -1;	} 	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);} voidintrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name){	Vctl **pv, *v;	int vno; 	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){			*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 longirqallocread(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;} voidtrapenable(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 voidnmienable(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. * Called very early - malloc is not yet available. */voidtrapinit0(void){	int d1, v;	ulong vaddr;	Segdesc *idt; 	idt = (Segdesc*)IDTADDR;	vaddr = (ulong)vectortable;	for(v = 0; v < 256; v++){		d1 = (vaddr & 0xFFFF0000)|SEGP;		switch(v){ 		case VectorBPT:			d1 |= SEGPL(3)|SEGIG;			break; 		case VectorSYSCALL:			d1 |= SEGPL(3)|SEGIG;			break; 		default:			d1 |= SEGPL(0)|SEGIG;			break;		}		idt[v].d0 = (vaddr & 0xFFFF)|(KESEL<<16);		idt[v].d1 = d1;		vaddr += 6;	}} voidtrapinit(void){	/*	 * Special traps.	 * Syscall() is called directly without going through trap().	 */	trapenable(VectorBPT, debugbpt, 0, "debugpt");	trapenable(VectorPF, fault386, 0, "fault386");	trapenable(Vector2F, doublefault, 0, "doublefault");	trapenable(Vector15, unexpected, 0, "unexpected");	nmienable(); 	addarchfile("irqalloc", 0444, irqallocread, nil);	trapinited = 1;} static char* excname[32] = {	"divide error",	"debug exception",	"nonmaskable interrupt",	"breakpoint",	"overflow",	"bounds check",	"invalid opcode",	"coprocessor not available",	"double fault",	"coprocessor segment overrun",	"invalid TSS",	"segment not present",	"stack exception",	"general protection violation",	"page fault",	"15 (reserved)",	"coprocessor error",	"alignment check",	"machine check",	"simd error",	"20 (reserved)",	"21 (reserved)",	"22 (reserved)",	"23 (reserved)",	"24 (reserved)",	"25 (reserved)",	"26 (reserved)",	"27 (reserved)",	"28 (reserved)",	"29 (reserved)",	"30 (reserved)",	"31 (reserved)",}; /* *  keep histogram of interrupt service times */voidintrtime(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]++;} /* go to user space */voidkexit(Ureg*){	uvlong t;	Tos *tos; 	/* precise time accounting, kernel exit */	tos = (Tos*)(USTKTOP-sizeof(Tos));	cycles(&t);	tos->kcycles += t - up->kentry;	tos->pcycles = t + up->pcycles;	tos->pid = up->pid;} /* *  All traps come here.  It is slower to have all traps call trap() *  rather than directly vectoring the handler.  However, this avoids a *  lot of code duplication and possible bugs.  The only exception is *  VectorSYSCALL. *  Trap is called with interrupts disabled via interrupt-gates. */voidtrap(Ureg* ureg){	int clockintr, i, vno, user;	char buf[ERRMAX];	Vctl *ctl, *v;	Mach *mach; 	if(!trapinited){		/* fault386 can give a better error message */		if(ureg->trap == VectorPF)			fault386(ureg, nil);		panic("trap %lud: not ready", ureg->trap);	} 	m->perf.intrts = perfticks();	user = userureg(ureg);	if(user){		up->dbgreg = ureg;		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(!user){			void (*pc)(void);			ulong *sp;  			extern void _forkretpopgs(void);			extern void _forkretpopfs(void);			extern void _forkretpopes(void);			extern void _forkretpopds(void);			extern void _forkretiret(void);			extern void _rdmsrinst(void);			extern void _wrmsrinst(void); 			extern void load_fs(ulong);			extern void load_gs(ulong); 			load_fs(NULLSEL);			load_gs(NULLSEL); 			sp = (ulong*)&ureg->sp;	/* kernel stack */			pc = (void*)ureg->pc; 			if(pc == _forkretpopgs || pc == _forkretpopfs || 			   pc == _forkretpopes || pc == _forkretpopds){				if(vno == VectorGPF || vno == VectorSNP){					sp[0] = NULLSEL;					return;				}			} else if(pc == _forkretiret){				if(vno == VectorGPF || vno == VectorSNP){					sp[1] = UESEL;	/* CS */					sp[4] = UDSEL;	/* SS */					return;				}			} else if(pc == _rdmsrinst || pc == _wrmsrinst){				if(vno == VectorGPF){					ureg->bp = -1;					ureg->pc += 2;					return;				}			}		} 		dumpregs(ureg);		if(!user){			ureg->sp = (ulong)&ureg->sp;			_dumpstack(ureg);		}		if(vno < nelem(excname))			panic("%s", excname[vno]);		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)			notify(ureg);		kexit(ureg);	}} /* *  dump registers */void

Lines 1–500 of 1078.