]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/fault.c
devproc: return process id when reading /proc/n/ctl file
[plan9front.git] / sys / src / 9 / 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 static void
9 faulterror(char *s, Chan *c)
10 {
11         char buf[ERRMAX];
12
13         if(c != nil)
14                 snprint(buf, sizeof buf, "sys: %s accessing %s: %s", s, chanpath(c), up->errstr);
15         else
16                 snprint(buf, sizeof buf, "sys: %s", s);
17         if(up->nerrlab) {
18                 if(up->kp == 0)
19                         postnote(up, 1, buf, NDebug);
20                 error(s);
21         }
22         pprint("suicide: %s\n", buf);
23         pexit(s, 1);
24 }
25
26 static void
27 pio(Segment *s, uintptr addr, uintptr soff, Page **p)
28 {
29         Page *new;
30         KMap *k;
31         Chan *c;
32         int n, ask;
33         char *kaddr;
34         uintptr daddr;
35         Page *loadrec;
36
37 retry:
38         loadrec = *p;
39         if(loadrec == nil) {    /* from a text/data image */
40                 daddr = s->fstart+soff;
41                 new = lookpage(s->image, daddr);
42                 if(new != nil) {
43                         *p = new;
44                         return;
45                 }
46
47                 c = s->image->c;
48                 ask = BY2PG;
49                 if(soff >= s->flen)
50                         ask = 0;
51                 else if((soff+ask) > s->flen)
52                         ask = s->flen-soff;
53         }
54         else {                  /* from a swap image */
55                 daddr = swapaddr(loadrec);
56                 new = lookpage(&swapimage, daddr);
57                 if(new != nil) {
58                         putswap(loadrec);
59                         *p = new;
60                         return;
61                 }
62
63                 c = swapimage.c;
64                 ask = BY2PG;
65         }
66         qunlock(s);
67
68         new = newpage(0, 0, addr);
69         k = kmap(new);
70         kaddr = (char*)VA(k);
71         while(waserror()) {
72                 if(strcmp(up->errstr, Eintr) == 0)
73                         continue;
74                 kunmap(k);
75                 putpage(new);
76                 faulterror(Eioload, c);
77         }
78         n = devtab[c->type]->read(c, kaddr, ask, daddr);
79         if(n != ask)
80                 error(Eshort);
81         if(ask < BY2PG)
82                 memset(kaddr+ask, 0, BY2PG-ask);
83         poperror();
84         kunmap(k);
85
86         qlock(s);
87         if(loadrec == nil) {    /* This is demand load */
88                 /*
89                  *  race, another proc may have gotten here first while
90                  *  s was unlocked
91                  */
92                 if(*p == nil) { 
93                         /*
94                          *  check page cache again after i/o to reduce double caching
95                          */
96                         *p = lookpage(s->image, daddr);
97                         if(*p == nil) {
98                                 incref(new);
99                                 new->daddr = daddr;
100                                 cachepage(new, s->image);
101                                 *p = new;
102                         }
103                 }
104         }
105         else {                  /* This is paged out */
106                 /*
107                  *  race, another proc may have gotten here first
108                  *  (and the pager may have run on that page) while
109                  *  s was unlocked
110                  */
111                 if(*p != loadrec) {
112                         if(!pagedout(*p)) {
113                                 /* another process did it for me */
114                                 goto done;
115                         } else if(*p != nil) {
116                                 /* another process and the pager got in */
117                                 putpage(new);
118                                 goto retry;
119                         } else {
120                                 /* another process segfreed the page */
121                                 incref(new);
122                                 k = kmap(new);
123                                 memset((void*)VA(k), 0, ask);
124                                 kunmap(k);
125                                 *p = new;
126                                 goto done;
127                         }
128                 }
129
130                 incref(new);
131                 new->daddr = daddr;
132                 cachepage(new, &swapimage);
133                 *p = new;
134                 putswap(loadrec);
135         }
136 done:
137         putpage(new);
138         if(s->flushme)
139                 (*p)->txtflush = ~0;
140 }
141
142 static int
143 fixfault(Segment *s, uintptr addr, int read)
144 {
145         Pte **pte, *etp;
146         uintptr soff, mmuphys;
147         Page **pg, *old, *new;
148
149         addr &= ~(BY2PG-1);
150         soff = addr-s->base;
151         pte = &s->map[soff/PTEMAPMEM];
152         if((etp = *pte) == nil)
153                 *pte = etp = ptealloc();
154
155         pg = &etp->pages[(soff&(PTEMAPMEM-1))/BY2PG];
156         if(pg < etp->first)
157                 etp->first = pg;
158         if(pg > etp->last)
159                 etp->last = pg;
160
161         switch(s->type & SG_TYPE) {
162         default:
163                 panic("fault");
164                 return -1;
165
166         case SG_TEXT:                   /* Demand load */
167                 if(pagedout(*pg))
168                         pio(s, addr, soff, pg);
169
170                 mmuphys = PPN((*pg)->pa) | PTERONLY | PTECACHED | PTEVALID;
171                 (*pg)->modref = PG_REF;
172                 break;
173
174         case SG_BSS:
175         case SG_SHARED:                 /* Zero fill on demand */
176         case SG_STACK:
177                 if(*pg == nil) {
178                         new = newpage(1, &s, addr);
179                         if(s == nil)
180                                 return -1;
181                         *pg = new;
182                 }
183                 /* wet floor */
184         case SG_DATA:                   /* Demand load/pagein/copy on write */
185                 if(pagedout(*pg))
186                         pio(s, addr, soff, pg);
187
188                 /*
189                  *  It's only possible to copy on write if
190                  *  we're the only user of the segment.
191                  */
192                 if(read && conf.copymode == 0 && s->ref == 1) {
193                         mmuphys = PPN((*pg)->pa) | PTERONLY | PTECACHED | PTEVALID;
194                         (*pg)->modref |= PG_REF;
195                         break;
196                 }
197
198                 old = *pg;
199                 if(old->image == &swapimage && (old->ref + swapcount(old->daddr)) == 1)
200                         uncachepage(old);
201                 if(old->ref > 1 || old->image != nil) {
202                         new = newpage(0, &s, addr);
203                         if(s == nil)
204                                 return -1;
205                         if(s->flushme)
206                                 new->txtflush = ~0;
207                         *pg = new;
208                         copypage(old, *pg);
209                         putpage(old);
210                 }
211                 /* wet floor */
212         case SG_STICKY:                 /* Never paged out */
213                 mmuphys = PPN((*pg)->pa) | PTEWRITE | PTECACHED | PTEVALID;
214                 (*pg)->modref = PG_MOD|PG_REF;
215                 break;
216
217         case SG_FIXED:                  /* Never paged out */
218                 mmuphys = PPN((*pg)->pa) | PTEWRITE | PTEUNCACHED | PTEVALID;
219                 (*pg)->modref = PG_MOD|PG_REF;
220                 break;
221         }
222
223 #ifdef PTENOEXEC
224         if((s->type & SG_NOEXEC) != 0)
225                 mmuphys |= PTENOEXEC;
226 #endif
227
228         qunlock(s);
229
230         putmmu(addr, mmuphys, *pg);
231
232         return 0;
233 }
234
235 static void
236 mapphys(Segment *s, uintptr addr, int attr)
237 {
238         uintptr mmuphys;
239         Page pg = {0};
240
241         addr &= ~(BY2PG-1);
242         pg.ref = 1;
243         pg.va = addr;
244         pg.pa = s->pseg->pa+(addr-s->base);
245
246         mmuphys = PPN(pg.pa) | PTEVALID;
247         if((attr & SG_RONLY) == 0)
248                 mmuphys |= PTEWRITE;
249         else
250                 mmuphys |= PTERONLY;
251
252 #ifdef PTENOEXEC
253         if((attr & SG_NOEXEC) != 0)
254                 mmuphys |= PTENOEXEC;
255 #endif
256
257 #ifdef PTEDEVICE
258         if((attr & SG_DEVICE) != 0)
259                 mmuphys |= PTEDEVICE;
260         else
261 #endif
262         if((attr & SG_CACHED) == 0)
263                 mmuphys |= PTEUNCACHED;
264         else
265                 mmuphys |= PTECACHED;
266
267         qunlock(s);
268
269         putmmu(addr, mmuphys, &pg);
270 }
271
272 int
273 fault(uintptr addr, uintptr pc, int read)
274 {
275         Segment *s;
276         char *sps;
277         int pnd, attr;
278
279         if(up == nil)
280                 panic("fault: nil up");
281         if(up->nlocks){
282                 Lock *l = up->lastlock;
283                 print("fault: nlocks %d, proc %lud %s, addr %#p, lock %#p, lpc %#p\n", 
284                         up->nlocks, up->pid, up->text, addr, l, l ? l->pc : 0);
285         }
286
287         pnd = up->notepending;
288         sps = up->psstate;
289         up->psstate = "Fault";
290
291         m->pfault++;
292         for(;;) {
293                 spllo();
294
295                 s = seg(up, addr, 1);           /* leaves s locked if seg != nil */
296                 if(s == nil) {
297                         up->psstate = sps;
298                         return -1;
299                 }
300
301                 attr = s->type;
302                 if((attr & SG_TYPE) == SG_PHYSICAL)
303                         attr |= s->pseg->attr;
304
305                 if((attr & SG_FAULT) != 0
306                 || read? (attr & SG_NOEXEC) != 0 && (addr & -BY2PG) == (pc & -BY2PG):
307                          (attr & SG_RONLY) != 0) {
308                         qunlock(s);
309                         up->psstate = sps;
310                         if(up->kp && up->nerrlab)       /* for segio */
311                                 error(Eio);
312                         return -1;
313                 }
314
315                 if((attr & SG_TYPE) == SG_PHYSICAL){
316                         mapphys(s, addr, attr);
317                         break;
318                 }
319
320                 if(fixfault(s, addr, read) == 0)
321                         break;
322
323                 splhi();
324                 switch(up->procctl){
325                 case Proc_exitme:
326                 case Proc_exitbig:
327                         procctl();
328                 }
329         }
330
331         up->psstate = sps;
332         up->notepending |= pnd;
333
334         return 0;
335 }
336
337 /*
338  * Called only in a system call
339  */
340 int
341 okaddr(uintptr addr, ulong len, int write)
342 {
343         Segment *s;
344
345         if((long)len >= 0 && len <= -addr) {
346                 for(;;) {
347                         s = seg(up, addr, 0);
348                         if(s == nil || (write && (s->type&SG_RONLY)))
349                                 break;
350
351                         if(addr+len > s->top) {
352                                 len -= s->top - addr;
353                                 addr = s->top;
354                                 continue;
355                         }
356                         return 1;
357                 }
358         }
359         return 0;
360 }
361
362 void
363 validaddr(uintptr addr, ulong len, int write)
364 {
365         if(!okaddr(addr, len, write)){
366                 pprint("suicide: invalid address %#p/%lud in sys call pc=%#p\n", addr, len, userpc());
367                 postnote(up, 1, "sys: bad address in syscall", NDebug);
368                 error(Ebadarg);
369         }
370 }
371
372 /*
373  * &s[0] is known to be a valid address.
374  */
375 void*
376 vmemchr(void *s, int c, ulong n)
377 {
378         uintptr a;
379         ulong m;
380         void *t;
381
382         a = (uintptr)s;
383         for(;;){
384                 m = BY2PG - (a & (BY2PG-1));
385                 if(n <= m)
386                         break;
387                 /* spans pages; handle this page */
388                 t = memchr((void*)a, c, m);
389                 if(t != nil)
390                         return t;
391                 a += m;
392                 n -= m;
393                 if(a < KZERO)
394                         validaddr(a, 1, 0);
395         }
396
397         /* fits in one page */
398         return memchr((void*)a, c, n);
399 }
400
401 Segment*
402 seg(Proc *p, uintptr addr, int dolock)
403 {
404         Segment **s, **et, *n;
405
406         et = &p->seg[NSEG];
407         for(s = p->seg; s < et; s++) {
408                 if((n = *s) == nil)
409                         continue;
410                 if(addr >= n->base && addr < n->top) {
411                         if(dolock == 0)
412                                 return n;
413
414                         qlock(n);
415                         if(addr >= n->base && addr < n->top)
416                                 return n;
417                         qunlock(n);
418                 }
419         }
420
421         return nil;
422 }
423
424 extern void checkmmu(uintptr, uintptr);
425
426 void
427 checkpages(void)
428 {
429         uintptr addr, off;
430         Pte *p;
431         Page *pg;
432         Segment **sp, **ep, *s;
433         
434         if(up == nil)
435                 return;
436
437         for(sp=up->seg, ep=&up->seg[NSEG]; sp<ep; sp++){
438                 if((s = *sp) == nil)
439                         continue;
440                 qlock(s);
441                 for(addr=s->base; addr<s->top; addr+=BY2PG){
442                         off = addr - s->base;
443                         if((p = s->map[off/PTEMAPMEM]) == nil)
444                                 continue;
445                         pg = p->pages[(off&(PTEMAPMEM-1))/BY2PG];
446                         if(pagedout(pg))
447                                 continue;
448                         checkmmu(addr, pg->pa);
449                 }
450                 qunlock(s);
451         }
452 }