xref: /plan9-contrib/sys/src/9/port/fault.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
13e12c5d1SDavid du Colombier #include	"u.h"
23e12c5d1SDavid du Colombier #include	"../port/lib.h"
33e12c5d1SDavid du Colombier #include	"mem.h"
43e12c5d1SDavid du Colombier #include	"dat.h"
53e12c5d1SDavid du Colombier #include	"fns.h"
63e12c5d1SDavid du Colombier #include	"../port/error.h"
73e12c5d1SDavid du Colombier 
83e12c5d1SDavid du Colombier void	faulterror(char*);
93e12c5d1SDavid du Colombier 
103e12c5d1SDavid du Colombier int
113e12c5d1SDavid du Colombier fault(ulong addr, int read)
123e12c5d1SDavid du Colombier {
133e12c5d1SDavid du Colombier 	Segment *s;
143e12c5d1SDavid du Colombier 	char *sps;
153e12c5d1SDavid du Colombier 
163e12c5d1SDavid du Colombier 	sps = u->p->psstate;
173e12c5d1SDavid du Colombier 	u->p->psstate = "Fault";
183e12c5d1SDavid du Colombier 	spllo();
193e12c5d1SDavid du Colombier 
203e12c5d1SDavid du Colombier 	m->pfault++;
213e12c5d1SDavid du Colombier 	for(;;) {
223e12c5d1SDavid du Colombier 		s = seg(u->p, addr, 1);
233e12c5d1SDavid du Colombier 		if(s == 0) {
243e12c5d1SDavid du Colombier 			u->p->psstate = sps;
253e12c5d1SDavid du Colombier 			return -1;
263e12c5d1SDavid du Colombier 		}
273e12c5d1SDavid du Colombier 
283e12c5d1SDavid du Colombier 		if(!read && (s->type&SG_RONLY)) {
293e12c5d1SDavid du Colombier 			qunlock(&s->lk);
303e12c5d1SDavid du Colombier 			u->p->psstate = sps;
313e12c5d1SDavid du Colombier 			return -1;
323e12c5d1SDavid du Colombier 		}
333e12c5d1SDavid du Colombier 
343e12c5d1SDavid du Colombier 		if(fixfault(s, addr, read, 1) == 0)
353e12c5d1SDavid du Colombier 			break;
363e12c5d1SDavid du Colombier 	}
373e12c5d1SDavid du Colombier 
383e12c5d1SDavid du Colombier 	u->p->psstate = sps;
393e12c5d1SDavid du Colombier 	return 0;
403e12c5d1SDavid du Colombier }
413e12c5d1SDavid du Colombier 
423e12c5d1SDavid du Colombier int
433e12c5d1SDavid du Colombier fixfault(Segment *s, ulong addr, int read, int doputmmu)
443e12c5d1SDavid du Colombier {
45*219b2ee8SDavid du Colombier 	int type;
46*219b2ee8SDavid du Colombier 	Pte **p, *etp;
473e12c5d1SDavid du Colombier 	ulong mmuphys=0, soff;
483e12c5d1SDavid du Colombier 	Page **pg, *lkp, *new;
49*219b2ee8SDavid du Colombier 	Page *(*fn)(Segment*, ulong);
503e12c5d1SDavid du Colombier 
513e12c5d1SDavid du Colombier 	addr &= ~(BY2PG-1);
523e12c5d1SDavid du Colombier 	soff = addr-s->base;
533e12c5d1SDavid du Colombier 	p = &s->map[soff/PTEMAPMEM];
543e12c5d1SDavid du Colombier 	if(*p == 0)
553e12c5d1SDavid du Colombier 		*p = ptealloc();
563e12c5d1SDavid du Colombier 
573e12c5d1SDavid du Colombier 	etp = *p;
583e12c5d1SDavid du Colombier 	pg = &etp->pages[(soff&(PTEMAPMEM-1))/BY2PG];
593e12c5d1SDavid du Colombier 	type = s->type&SG_TYPE;
603e12c5d1SDavid du Colombier 
613e12c5d1SDavid du Colombier 	if(pg < etp->first)
623e12c5d1SDavid du Colombier 		etp->first = pg;
633e12c5d1SDavid du Colombier 	if(pg > etp->last)
643e12c5d1SDavid du Colombier 		etp->last = pg;
653e12c5d1SDavid du Colombier 
663e12c5d1SDavid du Colombier 	switch(type) {
673e12c5d1SDavid du Colombier 	default:
683e12c5d1SDavid du Colombier 		panic("fault");
693e12c5d1SDavid du Colombier 		break;
703e12c5d1SDavid du Colombier 
713e12c5d1SDavid du Colombier 	case SG_TEXT:
723e12c5d1SDavid du Colombier 		if(pagedout(*pg)) 		/* Demand load */
733e12c5d1SDavid du Colombier 			pio(s, addr, soff, pg);
743e12c5d1SDavid du Colombier 
753e12c5d1SDavid du Colombier 		mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID;
763e12c5d1SDavid du Colombier 		(*pg)->modref = PG_REF;
773e12c5d1SDavid du Colombier 		break;
783e12c5d1SDavid du Colombier 
793e12c5d1SDavid du Colombier 	case SG_SHDATA:				/* Shared data */
803e12c5d1SDavid du Colombier 		if(pagedout(*pg))
813e12c5d1SDavid du Colombier 			pio(s, addr, soff, pg);
823e12c5d1SDavid du Colombier 
83*219b2ee8SDavid du Colombier 		lkp = *pg;
84*219b2ee8SDavid du Colombier 		lock(lkp);
85*219b2ee8SDavid du Colombier 		if(lkp->image)
86*219b2ee8SDavid du Colombier 			duppage(lkp);
87*219b2ee8SDavid du Colombier 		unlock(lkp);
883e12c5d1SDavid du Colombier 		goto done;
893e12c5d1SDavid du Colombier 
903e12c5d1SDavid du Colombier 	case SG_BSS:
913e12c5d1SDavid du Colombier 	case SG_SHARED:				/* Zero fill on demand */
923e12c5d1SDavid du Colombier 	case SG_STACK:
933e12c5d1SDavid du Colombier 		if(*pg == 0) {
943e12c5d1SDavid du Colombier 			new = newpage(1, &s, addr);
953e12c5d1SDavid du Colombier 			if(s == 0)
963e12c5d1SDavid du Colombier 				return -1;
973e12c5d1SDavid du Colombier 
983e12c5d1SDavid du Colombier 			*pg = new;
993e12c5d1SDavid du Colombier 		}
1003e12c5d1SDavid du Colombier 		/* NO break */
1013e12c5d1SDavid du Colombier 
1023e12c5d1SDavid du Colombier 	case SG_DATA:				/* Demand load/pagein/copy on write */
1033e12c5d1SDavid du Colombier 		if(pagedout(*pg))
1043e12c5d1SDavid du Colombier 			pio(s, addr, soff, pg);
1053e12c5d1SDavid du Colombier 
1063e12c5d1SDavid du Colombier 		if(type == SG_SHARED)
1073e12c5d1SDavid du Colombier 			goto done;
1083e12c5d1SDavid du Colombier 
1093e12c5d1SDavid du Colombier 		if(read && conf.copymode == 0) {
1103e12c5d1SDavid du Colombier 			mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID;
1113e12c5d1SDavid du Colombier 			(*pg)->modref |= PG_REF;
1123e12c5d1SDavid du Colombier 			break;
1133e12c5d1SDavid du Colombier 		}
1143e12c5d1SDavid du Colombier 
1153e12c5d1SDavid du Colombier 		lkp = *pg;
1163e12c5d1SDavid du Colombier 		lock(lkp);
1173e12c5d1SDavid du Colombier 		if(lkp->ref > 1) {
1183e12c5d1SDavid du Colombier 			unlock(lkp);
1193e12c5d1SDavid du Colombier 			new = newpage(0, &s, addr);
1203e12c5d1SDavid du Colombier 			if(s == 0)
1213e12c5d1SDavid du Colombier 				return -1;
1223e12c5d1SDavid du Colombier 			*pg = new;
1233e12c5d1SDavid du Colombier 			copypage(lkp, *pg);
1243e12c5d1SDavid du Colombier 			putpage(lkp);
1253e12c5d1SDavid du Colombier 		}
1263e12c5d1SDavid du Colombier 		else {
1273e12c5d1SDavid du Colombier 			/* put a duplicate of a text page back onto the free list */
1283e12c5d1SDavid du Colombier 			if(lkp->image)
1293e12c5d1SDavid du Colombier 				duppage(lkp);
1303e12c5d1SDavid du Colombier 
1313e12c5d1SDavid du Colombier 			unlock(lkp);
1323e12c5d1SDavid du Colombier 		}
1333e12c5d1SDavid du Colombier 	done:
1343e12c5d1SDavid du Colombier 		mmuphys = PPN((*pg)->pa) | PTEWRITE|PTEVALID;
1353e12c5d1SDavid du Colombier 		(*pg)->modref = PG_MOD|PG_REF;
1363e12c5d1SDavid du Colombier 		break;
1373e12c5d1SDavid du Colombier 
1383e12c5d1SDavid du Colombier 	case SG_PHYSICAL:
139*219b2ee8SDavid du Colombier 		if(*pg == 0) {
140*219b2ee8SDavid du Colombier 			fn = s->pseg->pgalloc;
141*219b2ee8SDavid du Colombier 			if(fn)
142*219b2ee8SDavid du Colombier 				*pg = (*fn)(s, addr);
143*219b2ee8SDavid du Colombier 			else {
144*219b2ee8SDavid du Colombier 				new = smalloc(sizeof(Page));
145*219b2ee8SDavid du Colombier 				new->va = addr;
146*219b2ee8SDavid du Colombier 				new->pa = s->pseg->pa+(addr-s->base);
147*219b2ee8SDavid du Colombier 				new->ref = 1;
148*219b2ee8SDavid du Colombier 				*pg = new;
149*219b2ee8SDavid du Colombier 			}
150*219b2ee8SDavid du Colombier 		}
1513e12c5d1SDavid du Colombier 
1523e12c5d1SDavid du Colombier 		mmuphys = PPN((*pg)->pa) |PTEWRITE|PTEUNCACHED|PTEVALID;
1533e12c5d1SDavid du Colombier 		(*pg)->modref = PG_MOD|PG_REF;
154*219b2ee8SDavid du Colombier /*		print("v %lux p %lux\n", addr, mmuphys);	/**/
1553e12c5d1SDavid du Colombier 		break;
1563e12c5d1SDavid du Colombier 	}
1573e12c5d1SDavid du Colombier 
1583e12c5d1SDavid du Colombier 	if(s->flushme)
1593e12c5d1SDavid du Colombier 		memset((*pg)->cachectl, PG_TXTFLUSH, sizeof(new->cachectl));
1603e12c5d1SDavid du Colombier 
1613e12c5d1SDavid du Colombier 	qunlock(&s->lk);
1623e12c5d1SDavid du Colombier 
1633e12c5d1SDavid du Colombier 	if(doputmmu)
1643e12c5d1SDavid du Colombier 		putmmu(addr, mmuphys, *pg);
1653e12c5d1SDavid du Colombier 
1663e12c5d1SDavid du Colombier 	return 0;
1673e12c5d1SDavid du Colombier }
1683e12c5d1SDavid du Colombier 
1693e12c5d1SDavid du Colombier void
1703e12c5d1SDavid du Colombier pio(Segment *s, ulong addr, ulong soff, Page **p)
1713e12c5d1SDavid du Colombier {
1723e12c5d1SDavid du Colombier 	Page *new;
1733e12c5d1SDavid du Colombier 	KMap *k;
1743e12c5d1SDavid du Colombier 	Chan *c;
1753e12c5d1SDavid du Colombier 	int n, ask;
1763e12c5d1SDavid du Colombier 	char *kaddr;
1773e12c5d1SDavid du Colombier 	ulong daddr;
1783e12c5d1SDavid du Colombier 	Page *loadrec;
1793e12c5d1SDavid du Colombier 
1803e12c5d1SDavid du Colombier 	loadrec = *p;
1813e12c5d1SDavid du Colombier 	if(loadrec == 0) {
1823e12c5d1SDavid du Colombier 		daddr = s->fstart+soff;		/* Compute disc address */
1833e12c5d1SDavid du Colombier 		new = lookpage(s->image, daddr);
1843e12c5d1SDavid du Colombier 	}
1853e12c5d1SDavid du Colombier 	else {
1863e12c5d1SDavid du Colombier 		daddr = swapaddr(loadrec);
1873e12c5d1SDavid du Colombier 		new = lookpage(&swapimage, daddr);
1883e12c5d1SDavid du Colombier 		if(new)
1893e12c5d1SDavid du Colombier 			putswap(loadrec);
1903e12c5d1SDavid du Colombier 	}
1913e12c5d1SDavid du Colombier 
1923e12c5d1SDavid du Colombier 	if(new) {				/* Page found from cache */
1933e12c5d1SDavid du Colombier 		*p = new;
1943e12c5d1SDavid du Colombier 		return;
1953e12c5d1SDavid du Colombier 	}
1963e12c5d1SDavid du Colombier 
1973e12c5d1SDavid du Colombier 	qunlock(&s->lk);
1983e12c5d1SDavid du Colombier 
1993e12c5d1SDavid du Colombier 	new = newpage(0, 0, addr);
2003e12c5d1SDavid du Colombier 	k = kmap(new);
2013e12c5d1SDavid du Colombier 	kaddr = (char*)VA(k);
2023e12c5d1SDavid du Colombier 
2033e12c5d1SDavid du Colombier 	if(loadrec == 0) {			/* This is demand load */
2043e12c5d1SDavid du Colombier 		c = s->image->c;
2053e12c5d1SDavid du Colombier 		while(waserror()) {
2063e12c5d1SDavid du Colombier 			if(strcmp(u->error, Eintr) == 0)
2073e12c5d1SDavid du Colombier 				continue;
2083e12c5d1SDavid du Colombier 			kunmap(k);
2093e12c5d1SDavid du Colombier 			putpage(new);
2103e12c5d1SDavid du Colombier 			faulterror("sys: demand load I/O error");
2113e12c5d1SDavid du Colombier 		}
2123e12c5d1SDavid du Colombier 
2133e12c5d1SDavid du Colombier 		ask = s->flen-soff;
2143e12c5d1SDavid du Colombier 		if(ask > BY2PG)
2153e12c5d1SDavid du Colombier 			ask = BY2PG;
2163e12c5d1SDavid du Colombier 
2173e12c5d1SDavid du Colombier 		n = (*devtab[c->type].read)(c, kaddr, ask, daddr);
218*219b2ee8SDavid du Colombier 		if(n != ask){
219*219b2ee8SDavid du Colombier 			print("demand load: %s: %d %d\n", u->error, n, ask);
2203e12c5d1SDavid du Colombier 			error(Eioload);
221*219b2ee8SDavid du Colombier 		}
2223e12c5d1SDavid du Colombier 		if(ask < BY2PG)
2233e12c5d1SDavid du Colombier 			memset(kaddr+ask, 0, BY2PG-ask);
2243e12c5d1SDavid du Colombier 
2253e12c5d1SDavid du Colombier 		poperror();
2263e12c5d1SDavid du Colombier 		kunmap(k);
2273e12c5d1SDavid du Colombier 		qlock(&s->lk);
2283e12c5d1SDavid du Colombier 		if(*p == 0) { 		/* Someone may have got there first */
2293e12c5d1SDavid du Colombier 			new->daddr = daddr;
2303e12c5d1SDavid du Colombier 			cachepage(new, s->image);
2313e12c5d1SDavid du Colombier 			*p = new;
2323e12c5d1SDavid du Colombier 		}
2333e12c5d1SDavid du Colombier 		else
2343e12c5d1SDavid du Colombier 			putpage(new);
2353e12c5d1SDavid du Colombier 	}
2363e12c5d1SDavid du Colombier 	else {				/* This is paged out */
2373e12c5d1SDavid du Colombier 		c = swapimage.c;
2383e12c5d1SDavid du Colombier 
2393e12c5d1SDavid du Colombier 		if(waserror()) {
2403e12c5d1SDavid du Colombier 			kunmap(k);
2413e12c5d1SDavid du Colombier 			putpage(new);
2423e12c5d1SDavid du Colombier 			qlock(&s->lk);
2433e12c5d1SDavid du Colombier 			qunlock(&s->lk);
2443e12c5d1SDavid du Colombier 			faulterror("sys: page in I/O error");
2453e12c5d1SDavid du Colombier 		}
2463e12c5d1SDavid du Colombier 
2473e12c5d1SDavid du Colombier 		n = (*devtab[c->type].read)(c, kaddr, BY2PG, daddr);
248*219b2ee8SDavid du Colombier 		if(n != BY2PG){
249*219b2ee8SDavid du Colombier 			print("page in: %s: %d %d\n", u->error, n, BY2PG);
2503e12c5d1SDavid du Colombier 			error(Eioload);
251*219b2ee8SDavid du Colombier 		}
2523e12c5d1SDavid du Colombier 
2533e12c5d1SDavid du Colombier 		poperror();
2543e12c5d1SDavid du Colombier 		kunmap(k);
2553e12c5d1SDavid du Colombier 		qlock(&s->lk);
2563e12c5d1SDavid du Colombier 
2573e12c5d1SDavid du Colombier 		if(pagedout(*p)) {
2583e12c5d1SDavid du Colombier 			new->daddr = daddr;
2593e12c5d1SDavid du Colombier 			cachepage(new, &swapimage);
2603e12c5d1SDavid du Colombier 			putswap(*p);
2613e12c5d1SDavid du Colombier 			*p = new;
2623e12c5d1SDavid du Colombier 		}
2633e12c5d1SDavid du Colombier 		else
2643e12c5d1SDavid du Colombier 			putpage(new);
2653e12c5d1SDavid du Colombier 	}
2663e12c5d1SDavid du Colombier }
2673e12c5d1SDavid du Colombier 
2683e12c5d1SDavid du Colombier void
2693e12c5d1SDavid du Colombier faulterror(char *s)
2703e12c5d1SDavid du Colombier {
2713e12c5d1SDavid du Colombier 	if(u->nerrlab) {
272*219b2ee8SDavid du Colombier 		postnote(u->p, 1, s, NUser);
2733e12c5d1SDavid du Colombier 		error(s);
2743e12c5d1SDavid du Colombier 	}
275*219b2ee8SDavid du Colombier 	pexit(s, 1);
2763e12c5d1SDavid du Colombier }
2773e12c5d1SDavid du Colombier 
2783e12c5d1SDavid du Colombier /*
2793e12c5d1SDavid du Colombier  * Called only in a system call
2803e12c5d1SDavid du Colombier  */
2813e12c5d1SDavid du Colombier int
2823e12c5d1SDavid du Colombier okaddr(ulong addr, ulong len, int write)
2833e12c5d1SDavid du Colombier {
2843e12c5d1SDavid du Colombier 	Segment *s;
2853e12c5d1SDavid du Colombier 
2863e12c5d1SDavid du Colombier 	if((long)len >= 0) {
2873e12c5d1SDavid du Colombier 		for(;;) {
2883e12c5d1SDavid du Colombier 			s = seg(u->p, addr, 0);
2893e12c5d1SDavid du Colombier 			if(s == 0 || (write && (s->type&SG_RONLY)))
2903e12c5d1SDavid du Colombier 				break;
2913e12c5d1SDavid du Colombier 
2923e12c5d1SDavid du Colombier 			if(addr+len > s->top) {
2933e12c5d1SDavid du Colombier 				len -= s->top - addr;
2943e12c5d1SDavid du Colombier 				addr = s->top;
2953e12c5d1SDavid du Colombier 				continue;
2963e12c5d1SDavid du Colombier 			}
2973e12c5d1SDavid du Colombier 			return 1;
2983e12c5d1SDavid du Colombier 		}
2993e12c5d1SDavid du Colombier 	}
3003e12c5d1SDavid du Colombier 	pprint("suicide: invalid address 0x%lux in sys call pc=0x%lux\n", addr, userpc());
3013e12c5d1SDavid du Colombier 	return 0;
3023e12c5d1SDavid du Colombier }
3033e12c5d1SDavid du Colombier 
3043e12c5d1SDavid du Colombier void
3053e12c5d1SDavid du Colombier validaddr(ulong addr, ulong len, int write)
3063e12c5d1SDavid du Colombier {
3073e12c5d1SDavid du Colombier 	if(!okaddr(addr, len, write))
3083e12c5d1SDavid du Colombier 		pexit("Suicide", 0);
3093e12c5d1SDavid du Colombier }
3103e12c5d1SDavid du Colombier 
3113e12c5d1SDavid du Colombier /*
3123e12c5d1SDavid du Colombier  * &s[0] is known to be a valid address.
3133e12c5d1SDavid du Colombier  */
3143e12c5d1SDavid du Colombier void*
3153e12c5d1SDavid du Colombier vmemchr(void *s, int c, int n)
3163e12c5d1SDavid du Colombier {
3173e12c5d1SDavid du Colombier 	int m;
3183e12c5d1SDavid du Colombier 	char *t;
3193e12c5d1SDavid du Colombier 	ulong a;
3203e12c5d1SDavid du Colombier 
3213e12c5d1SDavid du Colombier 	a = (ulong)s;
3223e12c5d1SDavid du Colombier 	m = BY2PG - (a & (BY2PG-1));
3233e12c5d1SDavid du Colombier 	if(m < n){
3243e12c5d1SDavid du Colombier 		t = vmemchr(s, c, m);
3253e12c5d1SDavid du Colombier 		if(t)
3263e12c5d1SDavid du Colombier 			return t;
3273e12c5d1SDavid du Colombier 		if(!(a & KZERO))
3283e12c5d1SDavid du Colombier 			validaddr(a+m, 1, 0);
3293e12c5d1SDavid du Colombier 		return vmemchr((void*)(a+m), c, n-m);
3303e12c5d1SDavid du Colombier 	}
3313e12c5d1SDavid du Colombier 	/*
3323e12c5d1SDavid du Colombier 	 * All in one page
3333e12c5d1SDavid du Colombier 	 */
3343e12c5d1SDavid du Colombier 	return memchr(s, c, n);
3353e12c5d1SDavid du Colombier }
3363e12c5d1SDavid du Colombier 
3373e12c5d1SDavid du Colombier Segment*
3383e12c5d1SDavid du Colombier seg(Proc *p, ulong addr, int dolock)
3393e12c5d1SDavid du Colombier {
3403e12c5d1SDavid du Colombier 	Segment **s, **et, *n;
3413e12c5d1SDavid du Colombier 
3423e12c5d1SDavid du Colombier 	et = &p->seg[NSEG];
3433e12c5d1SDavid du Colombier 	for(s = p->seg; s < et; s++)
3443e12c5d1SDavid du Colombier 		if(n = *s){
3453e12c5d1SDavid du Colombier 			if(addr >= n->base && addr < n->top) {
3463e12c5d1SDavid du Colombier 				if(dolock == 0)
3473e12c5d1SDavid du Colombier 					return n;
3483e12c5d1SDavid du Colombier 
3493e12c5d1SDavid du Colombier 				qlock(&n->lk);
3503e12c5d1SDavid du Colombier 				if(addr >= n->base && addr < n->top)
3513e12c5d1SDavid du Colombier 					return n;
3523e12c5d1SDavid du Colombier 				qunlock(&n->lk);
3533e12c5d1SDavid du Colombier 			}
3543e12c5d1SDavid du Colombier 		}
3553e12c5d1SDavid du Colombier 
3563e12c5d1SDavid du Colombier 	return 0;
3573e12c5d1SDavid du Colombier }
358