#include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "ureg.h" #include "errno.h" int fault(ulong addr, int read) { ulong mmuvirt, mmuphys, n; Seg *s; PTE *opte, *pte, *npte; Orig *o; char *l; Page *pg; int zeroed = 0, head = 1; int i; KMap *k, *k1; s = seg(u->p, addr); if(s == 0){ if(addr > USTKTOP) return -1; s = &u->p->seg[SSEG]; if(s->o==0 || addrmaxva-USTACKSIZE || addr>=s->maxva) return -1; /* grow stack */ o = s->o; n = o->npte; if(waserror()){ pprint("can't allocate stack page\n"); return -1; } growpte(o, (s->maxva-addr)>>PGSHIFT); poperror(); /* stacks grown down, sigh */ lock(o); memcpy(o->pte+(o->npte-n), o->pte, n*sizeof(PTE)); memset(o->pte, 0, (o->npte-n)*sizeof(PTE)); unlock(o); s->minva = addr; o->va = addr; }else o = s->o; if(!read && (o->flag&OWRPERM)==0) return -1; lock(o); opte = &o->pte[(addr-o->va)>>PGSHIFT]; pte = opte; if(s->mod){ while(pte = pte->nextmod) /* assign = */ if(pte->proc == u->p){ if(pte->page==0 || pte->page->va!=addr) panic("bad page %lux", pte->page); head = 0; break; } if(pte == 0) pte = opte; } if(pte->page == 0){ if(o->chan==0 || addr>(o->va+(o->maxca-o->minca))){ /* * Zero fill page. If we are really doing a copy-on-write * (e.g. into shared bss) we'll move the page later. */ pte->page = newpage(0, o, addr); o->npage++; zeroed = 1; }else{ /* * Demand load. Release o because it could take a while. */ unlock(o); n = (o->va+(o->maxca-o->minca)) - addr; if(n > BY2PG) n = BY2PG; pg = newpage(1, o, addr); k = kmap(pg); qlock(o->chan); if(waserror()){ kunmap(k); qunlock(o->chan); pg->o = 0; pg->ref--; pexit("load i/o error", 0); } o->chan->offset = (addr-o->va) + o->minca; l = (char*)VA(k); if((*devtab[o->chan->type].read)(o->chan, l, n) != n) error(Eioload); flushpage(pg->pa); qunlock(o->chan); if(npte[(addr-s->minva)>>PGSHIFT]; /* could move */ pte = opte; if(pte->page == 0){ pte->page = pg; o->npage++; }else{ /* someone beat us to it */ pg->o = 0; pg->ref--; } } } /* * Copy on reference (conf.copymode==1) or write (conf.copymode==0) */ if((o->flag & OWRPERM) && (conf.copymode || !read) && ((head && ((o->flag&OPURE) || o->nproc>1)) || (!head && pte->page->ref>1))){ /* * Look for the easy way out: are we the last non-modified? */ if(head && !(o->flag&OPURE)){ npte = opte; for(i=0; npte; i++) npte = npte->nextmod; if(i == o->nproc) goto easy; } if(head){ /* * Add to mod list */ pte = newmod(o); pte->proc = u->p; pte->page = opte->page; pte->page->ref++; o->npage++; /* * Link into opte mod list (same va) */ pte->nextmod = opte->nextmod; opte->nextmod = pte; /* * Link into proc mod list (increasing va) */ npte = s->mod; if(npte == 0){ s->mod = pte; pte->nextva = 0; }else{ while(npte->nextva && npte->nextva->page->vanextva; pte->nextva = npte->nextva; npte->nextva = pte; } head = 0; } pg = pte->page; if(zeroed){ /* move page */ pg->ref--; o->npage--; opte->page = 0; }else{ /* copy page */ pte->page = newpage(1, o, addr); k = kmap(pte->page); k1 = kmap(pg); memcpy((void*)VA(k), (void*)VA(k1), BY2PG); kunmap(k); kunmap(k1); if(pg->ref <= 1) panic("pg->ref <= 1"); pg->ref--; } easy: mmuphys = PTEWRITE; }else{ mmuphys = PTERONLY; if(o->flag & OWRPERM) if(o->flag & OPURE){ if(!head && pte->page->ref==1) mmuphys = PTEWRITE; }else if((head && o->nproc==1) || (!head && pte->page->ref==1)) mmuphys = PTEWRITE; } mmuvirt = addr; mmuphys |= PPN(pte->page->pa) | PTEVALID; usepage(pte->page, 1); if(pte->page->va != addr) panic("wrong addr in tail %lux %lux", pte->page->va, addr); if(pte->proc && pte->proc != u->p){ print("wrong proc in tail %d %s\n", head, u->p->text); print("u->p %lux pte->proc %lux\n", u->p, pte->proc); panic("addr %lux seg %d wrong proc in tail", addr, s-u->p->seg); } unlock(o); putmmu(mmuvirt, mmuphys); return 0; } /* * Called only in a system call */ void validaddr(ulong addr, ulong len, int write) { Seg *s, *ns; if((long)len < 0){ Err: pprint("invalid address %lux in sys call pc %lux sp %lux\n", addr, ((Ureg*)UREGADDR)->pc, ((Ureg*)UREGADDR)->sp); postnote(u->p, 1, "sys: bad address", NDebug); error(Ebadarg); } Again: s = seg(u->p, addr); if(s==0){ s = &u->p->seg[SSEG]; if(s->o==0 || addrmaxva-USTACKSIZE || addr>=s->maxva) goto Err; } if(write && (s->o->flag&OWRPERM)==0) goto Err; if(addr+len > s->maxva){ len -= s->maxva - addr; addr = s->maxva; goto Again; } } /* * &s[0] is known to be a valid address. */ void* vmemchr(void *s, int c, int n) { int m; char *t; ulong a; a = (ulong)s; m = BY2PG - (a & (BY2PG-1)); if(m < n){ t = vmemchr(s, c, m); if(t) return t; if(!(a & KZERO)) validaddr(a+m, 1, 0); return vmemchr((void*)(a+m), c, n-m); } /* * All in one page */ return memchr(s, c, n); } Seg* seg(Proc *p, ulong addr) { int i; Seg *s; for(i=0,s=p->seg; io && s->minva<=addr && addrmaxva) return s; return 0; }