123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "../port/error.h"
- int
- fault(ulong addr, int read)
- {
- Segment *s;
- char *sps;
- if(up->nlocks.ref) print("fault nlocks %ld\n", up->nlocks.ref);
- sps = up->psstate;
- up->psstate = "Fault";
- spllo();
- m->pfault++;
- for(;;) {
- s = seg(up, addr, 1); /* leaves s->lk qlocked if seg != nil */
- if(s == 0) {
- up->psstate = sps;
- return -1;
- }
- if(!read && (s->type&SG_RONLY)) {
- qunlock(&s->lk);
- up->psstate = sps;
- return -1;
- }
- if(fixfault(s, addr, read, 1) == 0)
- break;
- }
- up->psstate = sps;
- return 0;
- }
- static void
- faulterror(char *s, Chan *c, int freemem)
- {
- char buf[ERRMAX];
- if(c && c->path){
- snprint(buf, sizeof buf, "%s accessing %s: %s", s, c->path->s, up->errstr);
- s = buf;
- }
- if(up->nerrlab) {
- postnote(up, 1, s, NDebug);
- error(s);
- }
- pexit(s, freemem);
- }
- int
- fixfault(Segment *s, ulong addr, int read, int doputmmu)
- {
- int type;
- int ref;
- Pte **p, *etp;
- ulong mmuphys=0, soff;
- Page **pg, *lkp, *new;
- Page *(*fn)(Segment*, ulong);
- addr &= ~(BY2PG-1);
- soff = addr-s->base;
- p = &s->map[soff/PTEMAPMEM];
- if(*p == 0)
- *p = ptealloc();
- etp = *p;
- pg = &etp->pages[(soff&(PTEMAPMEM-1))/BY2PG];
- type = s->type&SG_TYPE;
- if(pg < etp->first)
- etp->first = pg;
- if(pg > etp->last)
- etp->last = pg;
- switch(type) {
- default:
- panic("fault");
- break;
- case SG_TEXT: /* Demand load */
- if(pagedout(*pg))
- pio(s, addr, soff, pg);
- mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID;
- (*pg)->modref = PG_REF;
- break;
- case SG_BSS:
- case SG_SHARED: /* Zero fill on demand */
- case SG_STACK:
- if(*pg == 0) {
- new = newpage(1, &s, addr);
- if(s == 0)
- return -1;
- *pg = new;
- }
- goto common;
- case SG_DATA:
- common: /* Demand load/pagein/copy on write */
- if(pagedout(*pg))
- pio(s, addr, soff, pg);
- /*
- * It's only possible to copy on write if
- * we're the only user of the segment.
- */
- if(read && conf.copymode == 0 && s->ref == 1) {
- mmuphys = PPN((*pg)->pa)|PTERONLY|PTEVALID;
- (*pg)->modref |= PG_REF;
- break;
- }
- lkp = *pg;
- lock(lkp);
- if(lkp->image == &swapimage)
- ref = lkp->ref + swapcount(lkp->daddr);
- else
- ref = lkp->ref;
- if(ref > 1) {
- unlock(lkp);
- if(swapfull()){
- qunlock(&s->lk);
- pprint("swap space full\n");
- faulterror(Enoswap, nil, 1);
- }
- new = newpage(0, &s, addr);
- if(s == 0)
- return -1;
- *pg = new;
- copypage(lkp, *pg);
- putpage(lkp);
- }
- else {
- /* save a copy of the original for the image cache */
- if(lkp->image && !swapfull())
- duppage(lkp);
- unlock(lkp);
- }
- mmuphys = PPN((*pg)->pa) | PTEWRITE | PTEVALID;
- (*pg)->modref = PG_MOD|PG_REF;
- break;
- case SG_PHYSICAL:
- if(*pg == 0) {
- fn = s->pseg->pgalloc;
- if(fn)
- *pg = (*fn)(s, addr);
- else {
- new = smalloc(sizeof(Page));
- new->va = addr;
- new->pa = s->pseg->pa+(addr-s->base);
- new->ref = 1;
- *pg = new;
- }
- }
- mmuphys = PPN((*pg)->pa) |PTEWRITE|PTEUNCACHED|PTEVALID;
- (*pg)->modref = PG_MOD|PG_REF;
- break;
- }
- qunlock(&s->lk);
- if(doputmmu)
- putmmu(addr, mmuphys, *pg);
- return 0;
- }
- void
- pio(Segment *s, ulong addr, ulong soff, Page **p)
- {
- Page *new;
- KMap *k;
- Chan *c;
- int n, ask;
- char *kaddr;
- ulong daddr;
- Page *loadrec;
- retry:
- loadrec = *p;
- if(loadrec == 0) { /* from a text/data image */
- daddr = s->fstart+soff;
- new = lookpage(s->image, daddr);
- if(new != nil) {
- *p = new;
- return;
- }
- }
- else { /* from a swap image */
- daddr = swapaddr(loadrec);
- new = lookpage(&swapimage, daddr);
- if(new != nil) {
- putswap(loadrec);
- *p = new;
- return;
- }
- }
- qunlock(&s->lk);
- new = newpage(0, 0, addr);
- k = kmap(new);
- kaddr = (char*)VA(k);
- if(loadrec == 0) { /* This is demand load */
- c = s->image->c;
- while(waserror()) {
- if(strcmp(up->errstr, Eintr) == 0)
- continue;
- kunmap(k);
- putpage(new);
- faulterror("sys: demand load I/O error", c, 0);
- }
- ask = s->flen-soff;
- if(ask > BY2PG)
- ask = BY2PG;
- n = devtab[c->type]->read(c, kaddr, ask, daddr);
- if(n != ask)
- faulterror(Eioload, c, 0);
- if(ask < BY2PG)
- memset(kaddr+ask, 0, BY2PG-ask);
- poperror();
- kunmap(k);
- qlock(&s->lk);
- /*
- * race, another proc may have gotten here first while
- * s->lk was unlocked
- */
- if(*p == 0) {
- new->daddr = daddr;
- cachepage(new, s->image);
- *p = new;
- }
- else
- putpage(new);
- }
- else { /* This is paged out */
- c = swapimage.c;
- if(waserror()) {
- kunmap(k);
- putpage(new);
- qlock(&s->lk);
- qunlock(&s->lk);
- faulterror("sys: page in I/O error", c, 0);
- }
- n = devtab[c->type]->read(c, kaddr, BY2PG, daddr);
- if(n != BY2PG)
- faulterror(Eioload, c, 0);
- poperror();
- kunmap(k);
- qlock(&s->lk);
- /*
- * race, another proc may have gotten here first
- * (and the pager may have run on that page) while
- * s->lk was unlocked
- */
- if(*p != loadrec){
- if(!pagedout(*p)){
- /* another process did it for me */
- putpage(new);
- goto done;
- } else {
- /* another process and the pager got in */
- putpage(new);
- goto retry;
- }
- }
- new->daddr = daddr;
- cachepage(new, &swapimage);
- *p = new;
- putswap(loadrec);
- }
- done:
- if(s->flushme)
- memset((*p)->cachectl, PG_TXTFLUSH, sizeof((*p)->cachectl));
- }
- /*
- * Called only in a system call
- */
- int
- okaddr(ulong addr, ulong len, int write)
- {
- Segment *s;
- if((long)len >= 0) {
- for(;;) {
- s = seg(up, addr, 0);
- if(s == 0 || (write && (s->type&SG_RONLY)))
- break;
- if(addr+len > s->top) {
- len -= s->top - addr;
- addr = s->top;
- continue;
- }
- return 1;
- }
- }
- pprint("suicide: invalid address 0x%lux in sys call pc=0x%lux\n", addr, userpc());
- return 0;
- }
- void
- validaddr(ulong addr, ulong len, int write)
- {
- if(!okaddr(addr, len, write))
- pexit("Suicide", 0);
- }
- /*
- * &s[0] is known to be a valid address.
- */
- void*
- vmemchr(void *s, int c, int n)
- {
- int m;
- ulong a;
- void *t;
- a = (ulong)s;
- while(PGROUND(a) != PGROUND(a+n-1)){
- /* spans pages; handle this page */
- m = BY2PG - (a & (BY2PG-1));
- t = memchr((void*)a, c, m);
- if(t)
- return t;
- a += m;
- n -= m;
- if(a < KZERO)
- validaddr(a, 1, 0);
- }
- /* fits in one page */
- return memchr((void*)a, c, n);
- }
- Segment*
- seg(Proc *p, ulong addr, int dolock)
- {
- Segment **s, **et, *n;
- et = &p->seg[NSEG];
- for(s = p->seg; s < et; s++) {
- n = *s;
- if(n == 0)
- continue;
- if(addr >= n->base && addr < n->top) {
- if(dolock == 0)
- return n;
- qlock(&n->lk);
- if(addr >= n->base && addr < n->top)
- return n;
- qunlock(&n->lk);
- }
- }
- return 0;
- }
- extern void checkmmu(ulong, ulong);
- void
- checkpages(void)
- {
- int checked;
- ulong addr, off;
- Pte *p;
- Page *pg;
- Segment **sp, **ep, *s;
-
- if(up == nil)
- return;
- checked = 0;
- for(sp=up->seg, ep=&up->seg[NSEG]; sp<ep; sp++){
- s = *sp;
- if(s == nil)
- continue;
- qlock(&s->lk);
- for(addr=s->base; addr<s->top; addr+=BY2PG){
- off = addr - s->base;
- p = s->map[off/PTEMAPMEM];
- if(p == 0)
- continue;
- pg = p->pages[(off&(PTEMAPMEM-1))/BY2PG];
- if(pg == 0 || pagedout(pg))
- continue;
- checkmmu(addr, pg->pa);
- checked++;
- }
- qunlock(&s->lk);
- }
- print("%ld %s: checked %d page table entries\n", up->pid, up->text, checked);
- }
|