FreeBSD/Linux Kernel Cross Reference
sys/port/fault.c
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 Segment *s;
12 char *sps;
13
14 if(up == nil)
15 panic("fault: nil up");
16 if(up->nlocks.ref)
17 print("fault: nlocks %ld\n", up->nlocks.ref);
18
19 sps = up->psstate;
20 up->psstate = "Fault";
21 spllo();
22
23 m->pfault++;
24 for(;;) {
25 s = seg(up, addr, 1); /* leaves s->lk qlocked if seg != nil */
26 if(s == 0) {
27 up->psstate = sps;
28 return -1;
29 }
30
31 if(!read && (s->type&SG_RONLY)) {
32 qunlock(&s->lk);
33 up->psstate = sps;
34 return -1;
35 }
36
37 if(fixfault(s, addr, read, 1) == 0)
38 break;
39 }
40
41 up->psstate = sps;
42 return 0;
43 }
44
45 static void
46 faulterror(char *s, Chan *c, int freemem)
47 {
48 char buf[ERRMAX];
49
50 if(c && c->path){
51 snprint(buf, sizeof buf, "%s accessing %s: %s", s, c->path->s, up->errstr);
52 s = buf;
53 }
54 if(up->nerrlab) {
55 postnote(up, 1, s, NDebug);
56 error(s);
57 }
58 pexit(s, freemem);
59 }
60
61 void (*checkaddr)(ulong, Segment *, Page *);
62 ulong addr2check;
63
64 int
65 fixfault(Segment *s, ulong addr, int read, int doputmmu)
66 {
67 int type;
68 int ref;
69 Pte **p, *etp;
70 ulong mmuphys=0, soff;
71 Page **pg, *lkp, *new;
72 Page *(*fn)(Segment*, ulong);
73
74 addr &= ~(BY2PG-1);
75 soff = addr-s->base;
76 p = &s->map[soff/PTEMAPMEM];
77 if(*p == 0)
78 *p = ptealloc();
79
80 etp = *p;
81 pg = &etp->pages[(soff&(PTEMAPMEM-1))/BY2PG];
82 type = s->type&SG_TYPE;
83
84 if(pg < etp->first)
85 etp->first = pg;
86 if(pg > etp->last)
87 etp->last = pg;
88
89 switch(type) {
90 default:
91 panic("fault");
92 break;
93
94 case SG_TEXT: /* Demand load */
95 if(pagedout(*pg))
96 pio(s, addr, soff, pg);
97
98 mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID;
99 (*pg)->modref = PG_REF;
100 break;
101
102 case SG_BSS:
103 case SG_SHARED: /* Zero fill on demand */
104 case SG_STACK:
105 if(*pg == 0) {
106 new = newpage(1, &s, addr);
107 if(s == 0)
108 return -1;
109
110 *pg = new;
111 }
112 goto common;
113
114 case SG_DATA:
115 common: /* Demand load/pagein/copy on write */
116 if(pagedout(*pg))
117 pio(s, addr, soff, pg);
118
119 /*
120 * It's only possible to copy on write if
121 * we're the only user of the segment.
122 */
123 if(read && conf.copymode == 0 && s->ref == 1) {
124 mmuphys = PPN((*pg)->pa)|PTERONLY|PTEVALID;
125 (*pg)->modref |= PG_REF;
126 break;
127 }
128
129 lkp = *pg;
130 lock(lkp);
131
132 if(lkp->image == &swapimage)
133 ref = lkp->ref + swapcount(lkp->daddr);
134 else
135 ref = lkp->ref;
136 if(ref > 1) {
137 unlock(lkp);
138
139 if(swapfull()){
140 qunlock(&s->lk);
141 pprint("swap space full\n");
142 faulterror(Enoswap, nil, 1);
143 }
144
145 new = newpage(0, &s, addr);
146 if(s == 0)
147 return -1;
148 *pg = new;
149 copypage(lkp, *pg);
150 putpage(lkp);
151 }
152 else {
153 /* save a copy of the original for the image cache */
154 if(lkp->image && !swapfull())
155 duppage(lkp);
156
157 unlock(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 else { /* from a swap image */
213 daddr = swapaddr(loadrec);
214 new = lookpage(&swapimage, daddr);
215 if(new != nil) {
216 putswap(loadrec);
217 *p = new;
218 return;
219 }
220 }
221
222
223 qunlock(&s->lk);
224
225 new = newpage(0, 0, addr);
226 k = kmap(new);
227 kaddr = (char*)VA(k);
228
229 if(loadrec == 0) { /* This is demand load */
230 c = s->image->c;
231 while(waserror()) {
232 if(strcmp(up->errstr, Eintr) == 0)
233 continue;
234 kunmap(k);
235 putpage(new);
236 faulterror("sys: demand load I/O error", c, 0);
237 }
238
239 ask = s->flen-soff;
240 if(ask > BY2PG)
241 ask = BY2PG;
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
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 c = swapimage.c;
267 if(waserror()) {
268 kunmap(k);
269 putpage(new);
270 qlock(&s->lk);
271 qunlock(&s->lk);
272 faulterror("sys: page in I/O error", c, 0);
273 }
274
275 n = devtab[c->type]->read(c, kaddr, BY2PG, daddr);
276 if(n != BY2PG)
277 faulterror(Eioload, c, 0);
278
279 poperror();
280 kunmap(k);
281 qlock(&s->lk);
282
283 /*
284 * race, another proc may have gotten here first
285 * (and the pager may have run on that page) while
286 * s->lk was unlocked
287 */
288 if(*p != loadrec){
289 if(!pagedout(*p)){
290 /* another process did it for me */
291 putpage(new);
292 goto done;
293 } else {
294 /* another process and the pager got in */
295 putpage(new);
296 goto retry;
297 }
298 }
299
300 new->daddr = daddr;
301 cachepage(new, &swapimage);
302 *p = new;
303 putswap(loadrec);
304 }
305
306 done:
307 if(s->flushme)
308 memset((*p)->cachectl, PG_TXTFLUSH, sizeof((*p)->cachectl));
309 }
310
311 /*
312 * Called only in a system call
313 */
314 int
315 okaddr(ulong addr, ulong len, int write)
316 {
317 Segment *s;
318
319 if((long)len >= 0) {
320 for(;;) {
321 s = seg(up, addr, 0);
322 if(s == 0 || (write && (s->type&SG_RONLY)))
323 break;
324
325 if(addr+len > s->top) {
326 len -= s->top - addr;
327 addr = s->top;
328 continue;
329 }
330 return 1;
331 }
332 }
333 pprint("suicide: invalid address %#lux/%lud in sys call pc=%#lux\n", addr, len, userpc());
334 return 0;
335 }
336
337 void
338 validaddr(ulong addr, ulong len, int write)
339 {
340 if(!okaddr(addr, len, write))
341 pexit("Suicide", 0);
342 }
343
344 /*
345 * &s[0] is known to be a valid address.
346 */
347 void*
348 vmemchr(void *s, int c, int n)
349 {
350 int m;
351 ulong a;
352 void *t;
353
354 a = (ulong)s;
355 while(PGROUND(a) != PGROUND(a+n-1)){
356 /* spans pages; handle this page */
357 m = BY2PG - (a & (BY2PG-1));
358 t = memchr((void*)a, c, m);
359 if(t)
360 return t;
361 a += m;
362 n -= m;
363 if(a < KZERO)
364 validaddr(a, 1, 0);
365 }
366
367 /* fits in one page */
368 return memchr((void*)a, c, n);
369 }
370
371 Segment*
372 seg(Proc *p, ulong addr, int dolock)
373 {
374 Segment **s, **et, *n;
375
376 et = &p->seg[NSEG];
377 for(s = p->seg; s < et; s++) {
378 n = *s;
379 if(n == 0)
380 continue;
381 if(addr >= n->base && addr < n->top) {
382 if(dolock == 0)
383 return n;
384
385 qlock(&n->lk);
386 if(addr >= n->base && addr < n->top)
387 return n;
388 qunlock(&n->lk);
389 }
390 }
391
392 return 0;
393 }
394
395 extern void checkmmu(ulong, ulong);
396 void
397 checkpages(void)
398 {
399 int checked;
400 ulong addr, off;
401 Pte *p;
402 Page *pg;
403 Segment **sp, **ep, *s;
404
405 if(up == nil)
406 return;
407
408 checked = 0;
409 for(sp=up->seg, ep=&up->seg[NSEG]; sp<ep; sp++){
410 s = *sp;
411 if(s == nil)
412 continue;
413 qlock(&s->lk);
414 for(addr=s->base; addr<s->top; addr+=BY2PG){
415 off = addr - s->base;
416 p = s->map[off/PTEMAPMEM];
417 if(p == 0)
418 continue;
419 pg = p->pages[(off&(PTEMAPMEM-1))/BY2PG];
420 if(pg == 0 || pagedout(pg))
421 continue;
422 checkmmu(addr, pg->pa);
423 checked++;
424 }
425 qunlock(&s->lk);
426 }
427 print("%ld %s: checked %d page table entries\n", up->pid, up->text, checked);
428 }
Cache object: 8c1b1775508dd974efbc85f6a1542650
|