586 lines · 12.3 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * traps, exceptions, interrupts, system calls. */#include "u.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 "arm.h" #define INTREGS		(VIRTIO+0xB200) typedef struct Intregs Intregs;typedef struct Vctl Vctl; enum {	Debug = 0, 	Nvec = 8,		/* # of vectors at start of lexception.s */	Fiqenable = 1<<7,}; /* *   Layout at virtual address KZERO (double mapped at HVECTORS). */typedef struct Vpage0 {	void	(*vectors[Nvec])(void);	u32int	vtable[Nvec];} Vpage0; /* * interrupt control registers */struct Intregs {	u32int	ARMpending;	u32int	GPUpending[2];	u32int	FIQctl;	u32int	GPUenable[2];	u32int	ARMenable;	u32int	GPUdisable[2];	u32int	ARMdisable;}; struct Vctl {	Vctl	*next;	int	irq;	u32int	*reg;	u32int	mask;	void	(*f)(Ureg*, void*);	void	*a;}; static Vctl *vctl, *vfiq; static char *trapnames[PsrMask+1] = {	[ PsrMusr ] "user mode",	[ PsrMfiq ] "fiq interrupt",	[ PsrMirq ] "irq interrupt",	[ PsrMsvc ] "svc/swi exception",	[ PsrMabt ] "prefetch abort/data abort",	[ PsrMabt+1 ] "data abort",	[ PsrMund ] "undefined instruction",	[ PsrMsys ] "sys trap",}; extern int notify(Ureg*); /* *  set up for exceptions */voidtrapinit(void){	Vpage0 *vpage0; 	/* disable everything */	intrsoff(); 	/* set up the exception vectors */	vpage0 = (Vpage0*)HVECTORS;	memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));	memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));	cacheuwbinv(); 	/* set up the stacks for the interrupt modes */	setr13(PsrMfiq, (u32int*)(FIQSTKTOP));	setr13(PsrMirq, m->sirq);	setr13(PsrMabt, m->sabt);	setr13(PsrMund, m->sund);	setr13(PsrMsys, m->ssys); 	coherence();} voidintrsoff(void){	Intregs *ip;	int disable; 	ip = (Intregs*)INTREGS;	disable = ~0;	ip->GPUdisable[0] = disable;	ip->GPUdisable[1] = disable;	ip->ARMdisable = disable;	ip->FIQctl = 0;} /* *  called by trap to handle irq interrupts. *  returns true iff a clock interrupt, thus maybe reschedule. */static intirq(Ureg* ureg){	Vctl *v;	int clockintr; 	clockintr = 0;	for(v = vctl; v; v = v->next)		if(*v->reg & v->mask){			coherence();			v->f(ureg, v->a);			coherence();			if(v->irq == IRQclock)				clockintr = 1;		}	return clockintr;} /* * called direct from lexception.s to handle fiq interrupt. */voidfiq(Ureg *ureg){	Vctl *v; 	v = vfiq;	if(v == nil)		panic("unexpected item in bagging area");	m->intr++;	ureg->pc -= 4;	coherence();	v->f(ureg, v->a);	coherence();} voidirqenable(int irq, void (*f)(Ureg*, void*), void* a){	Vctl *v;	Intregs *ip;	u32int *enable; 	ip = (Intregs*)INTREGS;	v = (Vctl*)malloc(sizeof(Vctl));	if(v == nil)		panic("irqenable: no mem");	v->irq = irq;	if(irq >= IRQbasic){		enable = &ip->ARMenable;		v->reg = &ip->ARMpending;		v->mask = 1 << (irq - IRQbasic);	}else{		enable = &ip->GPUenable[irq/32];		v->reg = &ip->GPUpending[irq/32];		v->mask = 1 << (irq % 32);	}	v->f = f;	v->a = a;	if(irq == IRQfiq){		assert((ip->FIQctl & Fiqenable) == 0);		assert((*enable & v->mask) == 0);		vfiq = v;		ip->FIQctl = Fiqenable | irq;	}else{		v->next = vctl;		vctl = v;		*enable = v->mask;	}} static char *trapname(int psr){	char *s; 	s = trapnames[psr & PsrMask];	if(s == nil)		s = "unknown trap number in psr";	return s;} /* this is quite helpful during mmu and cache debugging */static voidckfaultstuck(uintptr va){	static int cnt, lastpid;	static uintptr lastva; 	if (va == lastva && up->pid == lastpid) {		++cnt;		if (cnt >= 2)			/* fault() isn't fixing the underlying cause */			panic("fault: %d consecutive faults for va %#p",				cnt+1, va);	} else {		cnt = 0;		lastva = va;		lastpid = up->pid;	}} /* *  called by trap to handle access faults */static voidfaultarm(Ureg *ureg, uintptr va, int user, int read){	int n, insyscall;	char buf[ERRMAX]; 	if(up == nil) {		dumpregs(ureg);		panic("fault: nil up in faultarm, accessing %#p", va);	}	insyscall = up->insyscall;	up->insyscall = 1;	if (Debug)		ckfaultstuck(va); 	n = fault(va, read);	if(n < 0){		if(!user){			dumpregs(ureg);			panic("fault: kernel accessing %#p", va);		}		/* don't dump registers; programs suicide all the time */		snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",			read? "read": "write", va);		postnote(up, 1, buf, NDebug);	}	up->insyscall = insyscall;} /* *  returns 1 if the instruction writes memory, 0 otherwise */intwritetomem(ulong inst){	/* swap always write memory */	if((inst & 0x0FC00000) == 0x01000000)		return 1; 	/* loads and stores are distinguished by bit 20 */	if(inst & (1<<20))		return 0; 	return 1;} /* *  here on all exceptions other than syscall (SWI) and fiq */voidtrap(Ureg *ureg){	int clockintr, user, x, rv, rem;	ulong inst, fsr;	uintptr va;	char buf[ERRMAX]; 	assert(!islo());	if(up != nil)		rem = ((char*)ureg)-up->kstack;	else		rem = ((char*)ureg)-((char*)m+sizeof(Mach));	if(rem < 256) {		iprint("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux\n",			rem, up, ureg, ureg->pc);		delay(1000);		dumpstack();		panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux",			rem, up, ureg, ureg->pc);	} 	user = (ureg->psr & PsrMask) == PsrMusr;	if(user){		up->dbgreg = ureg;		cycles(&up->kentry);	} 	/*	 * All interrupts/exceptions should be resumed at ureg->pc-4,	 * except for Data Abort which resumes at ureg->pc-8.	 */	if(ureg->type == (PsrMabt+1))		ureg->pc -= 8;	else		ureg->pc -= 4; 	clockintr = 0;		/* if set, may call sched() before return */	switch(ureg->type){	default:		panic("unknown trap; type %#lux, psr mode %#lux", ureg->type,			ureg->psr & PsrMask);		break;	case PsrMirq:		clockintr = irq(ureg);		m->intr++;		break;	case PsrMabt:			/* prefetch fault */		x = ifsrget();		fsr = (x>>7) & 0x8 | x & 0x7;		switch(fsr){		case 0x02:		/* instruction debug event (BKPT) */			if(user){				snprint(buf, sizeof buf, "sys: breakpoint");				postnote(up, 1, buf, NDebug);			}else{				iprint("kernel bkpt: pc %#lux inst %#ux\n",					ureg->pc, *(u32int*)ureg->pc);				panic("kernel bkpt");			}			break;		default:			faultarm(ureg, ureg->pc, user, 1);			break;		}		break;	case PsrMabt+1:			/* data fault */		va = farget();		inst = *(ulong*)(ureg->pc);		/* bits 12 and 10 have to be concatenated with status */		x = fsrget();		fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf;		switch(fsr){		default:		case 0xa:		/* ? was under external abort */			panic("unknown data fault, 6b fsr %#lux", fsr);			break;		case 0x0:			panic("vector exception at %#lux", ureg->pc);			break;		case 0x1:		/* alignment fault */		case 0x3:		/* access flag fault (section) */			if(user){				snprint(buf, sizeof buf,					"sys: alignment: pc %#lux va %#p\n",					ureg->pc, va);				postnote(up, 1, buf, NDebug);			} else				panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);			break;		case 0x2:			panic("terminal exception at %#lux", ureg->pc);			break;		case 0x4:		/* icache maint fault */		case 0x6:		/* access flag fault (page) */		case 0x8:		/* precise external abort, non-xlat'n */		case 0x28:		case 0xc:		/* l1 translation, precise ext. abort */		case 0x2c:		case 0xe:		/* l2 translation, precise ext. abort */		case 0x2e:		case 0x16:		/* imprecise ext. abort, non-xlt'n */		case 0x36:			panic("external abort %#lux pc %#lux addr %#p",				fsr, ureg->pc, va);			break;		case 0x1c:		/* l1 translation, precise parity err */		case 0x1e:		/* l2 translation, precise parity err */		case 0x18:		/* imprecise parity or ecc err */			panic("translation parity error %#lux pc %#lux addr %#p",				fsr, ureg->pc, va);			break;		case 0x5:		/* translation fault, no section entry */		case 0x7:		/* translation fault, no page entry */			faultarm(ureg, va, user, !writetomem(inst));			break;		case 0x9:		case 0xb:			/* domain fault, accessing something we shouldn't */			if(user){				snprint(buf, sizeof buf,					"sys: access violation: pc %#lux va %#p\n",					ureg->pc, va);				postnote(up, 1, buf, NDebug);			} else				panic("kernel access violation: pc %#lux va %#p",					ureg->pc, va);			break;		case 0xd:		case 0xf:			/* permission error, copy on write or real permission error */			faultarm(ureg, va, user, !writetomem(inst));			break;		}		break;	case PsrMund:			/* undefined instruction */		if(user){			if(seg(up, ureg->pc, 0) != nil &&			   *(u32int*)ureg->pc == 0xD1200070)				postnote(up, 1, "sys: breakpoint", NDebug);			else{				/* look for floating point instructions to interpret */				rv = fpuemu(ureg);				if(rv == 0){					snprint(buf, sizeof buf,						"undefined instruction: pc %#lux\n",						ureg->pc);					postnote(up, 1, buf, NDebug);				}			}		}else{			if (ureg->pc & 3) {				iprint("rounding fault pc %#lux down to word\n",					ureg->pc);				ureg->pc &= ~3;			}			iprint("undefined instruction: pc %#lux inst %#ux\n",				ureg->pc, *(u32int*)ureg->pc);			panic("undefined instruction");		}		break;	}	splhi(); 	/* delaysched set because we held a lock or because our quantum ended */	if(up && up->delaysched && clockintr){		sched();		/* can cause more traps */		splhi();	} 	if(user){		if(up->procctl || up->nnote)			notify(ureg);		kexit(ureg);	}} intisvalidaddr(void *v){	return (uintptr)v >= KZERO;} static voiddumplongs(char *msg, ulong *v, int n){	int i, l; 	l = 0;	iprint("%s at %.8p: ", msg, v);	for(i=0; i<n; i++){		if(l >= 4){			iprint("\n    %.8p: ", v);			l = 0;		}		if(isvalidaddr(v)){			iprint(" %.8lux", *v++);			l++;		}else{			iprint(" invalid");			break;		}	}	iprint("\n");} static voiddumpstackwithureg(Ureg *ureg){	uintptr l, i, v, estack;	u32int *p;	char *s; 	if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){		iprint("dumpstack disabled\n");		return;	}	iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",		ureg->pc, ureg->sp, ureg->r14);	delay(2000);	i = 0;

Lines 1–500 of 586.