xref: /plan9/sys/src/9/port/fault.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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 void	faulterror(char*);
9 
10 int
11 fault(ulong addr, int read)
12 {
13 	Segment *s;
14 	char *sps;
15 
16 	sps = u->p->psstate;
17 	u->p->psstate = "Fault";
18 	spllo();
19 
20 	m->pfault++;
21 	for(;;) {
22 		s = seg(u->p, addr, 1);
23 		if(s == 0) {
24 			u->p->psstate = sps;
25 			return -1;
26 		}
27 
28 		if(!read && (s->type&SG_RONLY)) {
29 			qunlock(&s->lk);
30 			u->p->psstate = sps;
31 			return -1;
32 		}
33 
34 		if(fixfault(s, addr, read, 1) == 0)
35 			break;
36 	}
37 
38 	u->p->psstate = sps;
39 	return 0;
40 }
41 
42 int
43 fixfault(Segment *s, ulong addr, int read, int doputmmu)
44 {
45 	int type;
46 	Pte **p, *etp;
47 	ulong mmuphys=0, soff;
48 	Page **pg, *lkp, *new;
49 	Page *(*fn)(Segment*, ulong);
50 
51 	addr &= ~(BY2PG-1);
52 	soff = addr-s->base;
53 	p = &s->map[soff/PTEMAPMEM];
54 	if(*p == 0)
55 		*p = ptealloc();
56 
57 	etp = *p;
58 	pg = &etp->pages[(soff&(PTEMAPMEM-1))/BY2PG];
59 	type = s->type&SG_TYPE;
60 
61 	if(pg < etp->first)
62 		etp->first = pg;
63 	if(pg > etp->last)
64 		etp->last = pg;
65 
66 	switch(type) {
67 	default:
68 		panic("fault");
69 		break;
70 
71 	case SG_TEXT:
72 		if(pagedout(*pg)) 		/* Demand load */
73 			pio(s, addr, soff, pg);
74 
75 		mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID;
76 		(*pg)->modref = PG_REF;
77 		break;
78 
79 	case SG_SHDATA:				/* Shared data */
80 		if(pagedout(*pg))
81 			pio(s, addr, soff, pg);
82 
83 		lkp = *pg;
84 		lock(lkp);
85 		if(lkp->image)
86 			duppage(lkp);
87 		unlock(lkp);
88 		goto done;
89 
90 	case SG_BSS:
91 	case SG_SHARED:				/* Zero fill on demand */
92 	case SG_STACK:
93 		if(*pg == 0) {
94 			new = newpage(1, &s, addr);
95 			if(s == 0)
96 				return -1;
97 
98 			*pg = new;
99 		}
100 		/* NO break */
101 
102 	case SG_DATA:				/* Demand load/pagein/copy on write */
103 		if(pagedout(*pg))
104 			pio(s, addr, soff, pg);
105 
106 		if(type == SG_SHARED)
107 			goto done;
108 
109 		if(read && conf.copymode == 0) {
110 			mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID;
111 			(*pg)->modref |= PG_REF;
112 			break;
113 		}
114 
115 		lkp = *pg;
116 		lock(lkp);
117 		if(lkp->ref > 1) {
118 			unlock(lkp);
119 			new = newpage(0, &s, addr);
120 			if(s == 0)
121 				return -1;
122 			*pg = new;
123 			copypage(lkp, *pg);
124 			putpage(lkp);
125 		}
126 		else {
127 			/* put a duplicate of a text page back onto the free list */
128 			if(lkp->image)
129 				duppage(lkp);
130 
131 			unlock(lkp);
132 		}
133 	done:
134 		mmuphys = PPN((*pg)->pa) | PTEWRITE|PTEVALID;
135 		(*pg)->modref = PG_MOD|PG_REF;
136 		break;
137 
138 	case SG_PHYSICAL:
139 		if(*pg == 0) {
140 			fn = s->pseg->pgalloc;
141 			if(fn)
142 				*pg = (*fn)(s, addr);
143 			else {
144 				new = smalloc(sizeof(Page));
145 				new->va = addr;
146 				new->pa = s->pseg->pa+(addr-s->base);
147 				new->ref = 1;
148 				*pg = new;
149 			}
150 		}
151 
152 		mmuphys = PPN((*pg)->pa) |PTEWRITE|PTEUNCACHED|PTEVALID;
153 		(*pg)->modref = PG_MOD|PG_REF;
154 /*		print("v %lux p %lux\n", addr, mmuphys);	/**/
155 		break;
156 	}
157 
158 	if(s->flushme)
159 		memset((*pg)->cachectl, PG_TXTFLUSH, sizeof(new->cachectl));
160 
161 	qunlock(&s->lk);
162 
163 	if(doputmmu)
164 		putmmu(addr, mmuphys, *pg);
165 
166 	return 0;
167 }
168 
169 void
170 pio(Segment *s, ulong addr, ulong soff, Page **p)
171 {
172 	Page *new;
173 	KMap *k;
174 	Chan *c;
175 	int n, ask;
176 	char *kaddr;
177 	ulong daddr;
178 	Page *loadrec;
179 
180 	loadrec = *p;
181 	if(loadrec == 0) {
182 		daddr = s->fstart+soff;		/* Compute disc address */
183 		new = lookpage(s->image, daddr);
184 	}
185 	else {
186 		daddr = swapaddr(loadrec);
187 		new = lookpage(&swapimage, daddr);
188 		if(new)
189 			putswap(loadrec);
190 	}
191 
192 	if(new) {				/* Page found from cache */
193 		*p = new;
194 		return;
195 	}
196 
197 	qunlock(&s->lk);
198 
199 	new = newpage(0, 0, addr);
200 	k = kmap(new);
201 	kaddr = (char*)VA(k);
202 
203 	if(loadrec == 0) {			/* This is demand load */
204 		c = s->image->c;
205 		while(waserror()) {
206 			if(strcmp(u->error, Eintr) == 0)
207 				continue;
208 			kunmap(k);
209 			putpage(new);
210 			faulterror("sys: demand load I/O error");
211 		}
212 
213 		ask = s->flen-soff;
214 		if(ask > BY2PG)
215 			ask = BY2PG;
216 
217 		n = (*devtab[c->type].read)(c, kaddr, ask, daddr);
218 		if(n != ask){
219 			print("demand load: %s: %d %d\n", u->error, n, ask);
220 			error(Eioload);
221 		}
222 		if(ask < BY2PG)
223 			memset(kaddr+ask, 0, BY2PG-ask);
224 
225 		poperror();
226 		kunmap(k);
227 		qlock(&s->lk);
228 		if(*p == 0) { 		/* Someone may have got there first */
229 			new->daddr = daddr;
230 			cachepage(new, s->image);
231 			*p = new;
232 		}
233 		else
234 			putpage(new);
235 	}
236 	else {				/* This is paged out */
237 		c = swapimage.c;
238 
239 		if(waserror()) {
240 			kunmap(k);
241 			putpage(new);
242 			qlock(&s->lk);
243 			qunlock(&s->lk);
244 			faulterror("sys: page in I/O error");
245 		}
246 
247 		n = (*devtab[c->type].read)(c, kaddr, BY2PG, daddr);
248 		if(n != BY2PG){
249 			print("page in: %s: %d %d\n", u->error, n, BY2PG);
250 			error(Eioload);
251 		}
252 
253 		poperror();
254 		kunmap(k);
255 		qlock(&s->lk);
256 
257 		if(pagedout(*p)) {
258 			new->daddr = daddr;
259 			cachepage(new, &swapimage);
260 			putswap(*p);
261 			*p = new;
262 		}
263 		else
264 			putpage(new);
265 	}
266 }
267 
268 void
269 faulterror(char *s)
270 {
271 	if(u->nerrlab) {
272 		postnote(u->p, 1, s, NUser);
273 		error(s);
274 	}
275 	pexit(s, 1);
276 }
277 
278 /*
279  * Called only in a system call
280  */
281 int
282 okaddr(ulong addr, ulong len, int write)
283 {
284 	Segment *s;
285 
286 	if((long)len >= 0) {
287 		for(;;) {
288 			s = seg(u->p, addr, 0);
289 			if(s == 0 || (write && (s->type&SG_RONLY)))
290 				break;
291 
292 			if(addr+len > s->top) {
293 				len -= s->top - addr;
294 				addr = s->top;
295 				continue;
296 			}
297 			return 1;
298 		}
299 	}
300 	pprint("suicide: invalid address 0x%lux in sys call pc=0x%lux\n", addr, userpc());
301 	return 0;
302 }
303 
304 void
305 validaddr(ulong addr, ulong len, int write)
306 {
307 	if(!okaddr(addr, len, write))
308 		pexit("Suicide", 0);
309 }
310 
311 /*
312  * &s[0] is known to be a valid address.
313  */
314 void*
315 vmemchr(void *s, int c, int n)
316 {
317 	int m;
318 	char *t;
319 	ulong a;
320 
321 	a = (ulong)s;
322 	m = BY2PG - (a & (BY2PG-1));
323 	if(m < n){
324 		t = vmemchr(s, c, m);
325 		if(t)
326 			return t;
327 		if(!(a & KZERO))
328 			validaddr(a+m, 1, 0);
329 		return vmemchr((void*)(a+m), c, n-m);
330 	}
331 	/*
332 	 * All in one page
333 	 */
334 	return memchr(s, c, n);
335 }
336 
337 Segment*
338 seg(Proc *p, ulong addr, int dolock)
339 {
340 	Segment **s, **et, *n;
341 
342 	et = &p->seg[NSEG];
343 	for(s = p->seg; s < et; s++)
344 		if(n = *s){
345 			if(addr >= n->base && addr < n->top) {
346 				if(dolock == 0)
347 					return n;
348 
349 				qlock(&n->lk);
350 				if(addr >= n->base && addr < n->top)
351 					return n;
352 				qunlock(&n->lk);
353 			}
354 		}
355 
356 	return 0;
357 }
358