xref: /plan9/sys/src/9/port/fault.c (revision 401314a3b4602c168a19b28ed47ba5cbefe42fe0)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 int
9 fault(ulong addr, int read)
10 {
11 	int tries;
12 	Segment *s;
13 	char *sps;
14 
15 	if(up == nil)
16 		panic("fault: nil up");
17 	if(up->nlocks.ref)
18 		print("fault: addr %#p: nlocks %ld\n", addr, up->nlocks.ref);
19 
20 	sps = up->psstate;
21 	up->psstate = "Fault";
22 	spllo();
23 
24 	m->pfault++;
25 	for(tries = 200; tries > 0; tries--) {	/* TODO: reset to 20 */
26 		s = seg(up, addr, 1);		/* leaves s->lk qlocked if seg != nil */
27 		if(s == 0) {
28 			up->psstate = sps;
29 			return -1;
30 		}
31 
32 		if(!read && (s->type&SG_RONLY)) {
33 			qunlock(&s->lk);
34 			up->psstate = sps;
35 			return -1;
36 		}
37 
38 		if(fixfault(s, addr, read, 1) == 0)	/* qunlocks s->lk */
39 			break;
40 	}
41 	/*
42 	 * if we loop more than a few times, we're probably stuck on
43 	 * an unfixable address, almost certainly due to a bug
44 	 * elsewhere (e.g., bad page tables) so there's no point
45 	 * in spinning forever.
46 	 */
47 	if (tries <= 0)
48 		panic("faultarm: fault stuck on va %#8.8p read %d\n", addr, read);
49 
50 	up->psstate = sps;
51 	return 0;
52 }
53 
54 static void
55 faulterror(char *s, Chan *c, int freemem)
56 {
57 	char buf[ERRMAX];
58 
59 	if(c && c->path){
60 		snprint(buf, sizeof buf, "%s accessing %s: %s", s, c->path->s, up->errstr);
61 		s = buf;
62 	}
63 	if(up->nerrlab) {
64 		postnote(up, 1, s, NDebug);
65 		error(s);
66 	}
67 	pexit(s, freemem);
68 }
69 
70 void	(*checkaddr)(ulong, Segment *, Page *);
71 ulong	addr2check;
72 
73 int
74 fixfault(Segment *s, ulong addr, int read, int doputmmu)
75 {
76 	int type;
77 	int ref;
78 	Pte **p, *etp;
79 	ulong mmuphys=0, soff;
80 	Page **pg, *lkp, *new;
81 	Page *(*fn)(Segment*, ulong);
82 
83 	addr &= ~(BY2PG-1);
84 	soff = addr-s->base;
85 	p = &s->map[soff/PTEMAPMEM];
86 	if(*p == 0)
87 		*p = ptealloc();
88 
89 	etp = *p;
90 	pg = &etp->pages[(soff&(PTEMAPMEM-1))/BY2PG];
91 	type = s->type&SG_TYPE;
92 
93 	if(pg < etp->first)
94 		etp->first = pg;
95 	if(pg > etp->last)
96 		etp->last = pg;
97 
98 	switch(type) {
99 	default:
100 		panic("fault");
101 		break;
102 
103 	case SG_TEXT: 			/* Demand load */
104 		if(pagedout(*pg))
105 			pio(s, addr, soff, pg);
106 
107 		mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID;
108 		(*pg)->modref = PG_REF;
109 		break;
110 
111 	case SG_BSS:
112 	case SG_SHARED:			/* Zero fill on demand */
113 	case SG_STACK:
114 		if(*pg == 0) {
115 			new = newpage(1, &s, addr);
116 			if(s == 0)
117 				return -1;
118 
119 			*pg = new;
120 		}
121 		goto common;
122 
123 	case SG_DATA:
124 	common:			/* Demand load/pagein/copy on write */
125 		if(pagedout(*pg))
126 			pio(s, addr, soff, pg);
127 
128 		/*
129 		 *  It's only possible to copy on write if
130 		 *  we're the only user of the segment.
131 		 */
132 		if(read && conf.copymode == 0 && s->ref == 1) {
133 			mmuphys = PPN((*pg)->pa)|PTERONLY|PTEVALID;
134 			(*pg)->modref |= PG_REF;
135 			break;
136 		}
137 
138 		lkp = *pg;
139 		lock(lkp);
140 
141 		if(lkp->image == &swapimage)
142 			ref = lkp->ref + swapcount(lkp->daddr);
143 		else
144 			ref = lkp->ref;
145 		if(ref == 1 && lkp->image){
146 			/* save a copy of the original for the image cache */
147 			duppage(lkp);
148 			ref = lkp->ref;
149 		}
150 		unlock(lkp);
151 		if(ref > 1){
152 			new = newpage(0, &s, addr);
153 			if(s == 0)
154 				return -1;
155 			*pg = new;
156 			copypage(lkp, *pg);
157 			putpage(lkp);
158 		}
159 		mmuphys = PPN((*pg)->pa) | PTEWRITE | PTEVALID;
160 		(*pg)->modref = PG_MOD|PG_REF;
161 		break;
162 
163 	case SG_PHYSICAL:
164 		if(*pg == 0) {
165 			fn = s->pseg->pgalloc;
166 			if(fn)
167 				*pg = (*fn)(s, addr);
168 			else {
169 				new = smalloc(sizeof(Page));
170 				new->va = addr;
171 				new->pa = s->pseg->pa+(addr-s->base);
172 				new->ref = 1;
173 				*pg = new;
174 			}
175 		}
176 
177 		if (checkaddr && addr == addr2check)
178 			(*checkaddr)(addr, s, *pg);
179 		mmuphys = PPN((*pg)->pa) |PTEWRITE|PTEUNCACHED|PTEVALID;
180 		(*pg)->modref = PG_MOD|PG_REF;
181 		break;
182 	}
183 	qunlock(&s->lk);
184 
185 	if(doputmmu)
186 		putmmu(addr, mmuphys, *pg);
187 
188 	return 0;
189 }
190 
191 void
192 pio(Segment *s, ulong addr, ulong soff, Page **p)
193 {
194 	Page *new;
195 	KMap *k;
196 	Chan *c;
197 	int n, ask;
198 	char *kaddr;
199 	ulong daddr;
200 	Page *loadrec;
201 
202 retry:
203 	loadrec = *p;
204 	if(loadrec == 0) {	/* from a text/data image */
205 		daddr = s->fstart+soff;
206 		new = lookpage(s->image, daddr);
207 		if(new != nil) {
208 			*p = new;
209 			return;
210 		}
211 
212 		c = s->image->c;
213 		ask = s->flen-soff;
214 		if(ask > BY2PG)
215 			ask = BY2PG;
216 	}
217 	else {			/* from a swap image */
218 		daddr = swapaddr(loadrec);
219 		new = lookpage(&swapimage, daddr);
220 		if(new != nil) {
221 			putswap(loadrec);
222 			*p = new;
223 			return;
224 		}
225 
226 		c = swapimage.c;
227 		ask = BY2PG;
228 	}
229 	qunlock(&s->lk);
230 
231 	new = newpage(0, 0, addr);
232 	k = kmap(new);
233 	kaddr = (char*)VA(k);
234 
235 	while(waserror()) {
236 		if(strcmp(up->errstr, Eintr) == 0)
237 			continue;
238 		kunmap(k);
239 		putpage(new);
240 		faulterror(Eioload, c, 0);
241 	}
242 
243 	n = devtab[c->type]->read(c, kaddr, ask, daddr);
244 	if(n != ask)
245 		faulterror(Eioload, c, 0);
246 	if(ask < BY2PG)
247 		memset(kaddr+ask, 0, BY2PG-ask);
248 
249 	poperror();
250 	kunmap(k);
251 	qlock(&s->lk);
252 	if(loadrec == 0) {	/* This is demand load */
253 		/*
254 		 *  race, another proc may have gotten here first while
255 		 *  s->lk was unlocked
256 		 */
257 		if(*p == 0) {
258 			new->daddr = daddr;
259 			cachepage(new, s->image);
260 			*p = new;
261 		}
262 		else
263 			putpage(new);
264 	}
265 	else {			/* This is paged out */
266 		/*
267 		 *  race, another proc may have gotten here first
268 		 *  (and the pager may have run on that page) while
269 		 *  s->lk was unlocked
270 		 */
271 		if(*p != loadrec){
272 			if(!pagedout(*p)){
273 				/* another process did it for me */
274 				putpage(new);
275 				goto done;
276 			} else {
277 				/* another process and the pager got in */
278 				putpage(new);
279 				goto retry;
280 			}
281 		}
282 
283 		new->daddr = daddr;
284 		cachepage(new, &swapimage);
285 		*p = new;
286 		putswap(loadrec);
287 	}
288 
289 done:
290 	if(s->flushme)
291 		memset((*p)->cachectl, PG_TXTFLUSH, sizeof((*p)->cachectl));
292 }
293 
294 /*
295  * Called only in a system call
296  */
297 int
298 okaddr(ulong addr, ulong len, int write)
299 {
300 	Segment *s;
301 
302 	if((long)len >= 0) {
303 		for(;;) {
304 			s = seg(up, addr, 0);
305 			if(s == 0 || (write && (s->type&SG_RONLY)))
306 				break;
307 
308 			if(addr+len > s->top) {
309 				len -= s->top - addr;
310 				addr = s->top;
311 				continue;
312 			}
313 			return 1;
314 		}
315 	}
316 	pprint("suicide: invalid address %#lux/%lud in sys call pc=%#lux\n", addr, len, userpc());
317 	return 0;
318 }
319 
320 void
321 validaddr(ulong addr, ulong len, int write)
322 {
323 	if(!okaddr(addr, len, write)){
324 		postnote(up, 1, "sys: bad address in syscall", NDebug);
325 		error(Ebadarg);
326 	}
327 }
328 
329 /*
330  * &s[0] is known to be a valid address.
331  */
332 void*
333 vmemchr(void *s, int c, int n)
334 {
335 	int m;
336 	ulong a;
337 	void *t;
338 
339 	a = (ulong)s;
340 	while(PGROUND(a) != PGROUND(a+n-1)){
341 		/* spans pages; handle this page */
342 		m = BY2PG - (a & (BY2PG-1));
343 		t = memchr((void*)a, c, m);
344 		if(t)
345 			return t;
346 		a += m;
347 		n -= m;
348 		if(a < KZERO)
349 			validaddr(a, 1, 0);
350 	}
351 
352 	/* fits in one page */
353 	return memchr((void*)a, c, n);
354 }
355 
356 Segment*
357 seg(Proc *p, ulong addr, int dolock)
358 {
359 	Segment **s, **et, *n;
360 
361 	et = &p->seg[NSEG];
362 	for(s = p->seg; s < et; s++) {
363 		n = *s;
364 		if(n == 0)
365 			continue;
366 		if(addr >= n->base && addr < n->top) {
367 			if(dolock == 0)
368 				return n;
369 
370 			qlock(&n->lk);
371 			if(addr >= n->base && addr < n->top)
372 				return n;
373 			qunlock(&n->lk);
374 		}
375 	}
376 
377 	return 0;
378 }
379 
380 extern void checkmmu(ulong, ulong);
381 void
382 checkpages(void)
383 {
384 	int checked;
385 	ulong addr, off;
386 	Pte *p;
387 	Page *pg;
388 	Segment **sp, **ep, *s;
389 
390 	if(up == nil)
391 		return;
392 
393 	checked = 0;
394 	for(sp=up->seg, ep=&up->seg[NSEG]; sp<ep; sp++){
395 		s = *sp;
396 		if(s == nil)
397 			continue;
398 		qlock(&s->lk);
399 		for(addr=s->base; addr<s->top; addr+=BY2PG){
400 			off = addr - s->base;
401 			p = s->map[off/PTEMAPMEM];
402 			if(p == 0)
403 				continue;
404 			pg = p->pages[(off&(PTEMAPMEM-1))/BY2PG];
405 			if(pg == 0 || pagedout(pg))
406 				continue;
407 			checkmmu(addr, pg->pa);
408 			checked++;
409 		}
410 		qunlock(&s->lk);
411 	}
412 	print("%ld %s: checked %d page table entries\n", up->pid, up->text, checked);
413 }
414