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