| plan 9 kernel history: overview | file list | diff list |
1991/0906/port/fault.c (diff list | history)
| port/fault.c on 1990/0227 | ||
| 1990/0227 | #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "ureg.h" #include "errno.h" | |
| 1991/0705 | #define DPRINT | |
| 1990/1212 | int fault(ulong addr, int read) | |
| 1990/0227 | { | |
| 1991/0705 | ulong mmuphys=0, soff; Segment *s; Pte **p; Page **pg, *lkp, *new = 0; int type; | |
| 1990/0227 | ||
| 1991/0427 | m->pfault++; | |
| 1991/0705 | again: s = seg(u->p, addr, 1); if(s == 0) | |
| 1990/1212 | return -1; | |
| 1991/0705 | if(!read && (s->type&SG_RONLY)) { qunlock(&s->lk); return -1; | |
| 1990/0227 | } | |
| 1991/0705 | addr &= ~(BY2PG-1); soff = addr-s->base; p = &s->map[soff/PTEMAPMEM]; if(*p == 0) *p = ptealloc(); pg = &(*p)->pages[(soff&(PTEMAPMEM-1))/BY2PG]; type = s->type&SG_TYPE; switch(type) { case SG_TEXT: if(pagedout(*pg)) /* No data - must demand load */ pio(s, addr, soff, pg); mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID; (*pg)->modref = PG_REF; break; case SG_SHARED: case SG_BSS: case SG_STACK: /* Zero fill on demand */ if(*pg == 0) { if(new == 0 && (new = newpage(1, &s, addr)) && s == 0) goto again; *pg = new; new = 0; | |
| 1990/0227 | } | |
| 1991/0705 | /* NO break */ case SG_DATA: if(pagedout(*pg)) pio(s, addr, soff, pg); | |
| 1991/0608 | ||
| 1991/0705 | if(type == SG_SHARED) goto done; | |
| 1991/0607 | ||
| 1991/0705 | if(read && conf.copymode == 0) { mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID; (*pg)->modref |= PG_REF; break; | |
| 1990/0227 | } | |
| 1991/0705 | lkp = *pg; lockpage(lkp); if(lkp->ref > 1) { unlockpage(lkp); if(new == 0 && (new = newpage(0, &s, addr)) && s == 0) goto again; *pg = new; new = 0; copypage(lkp, *pg); putpage(lkp); | |
| 1990/0227 | } | |
| 1991/0705 | else { /* put a duplicate of a text page back onto the free list */ if(lkp->image) duppage(lkp); unlockpage(lkp); | |
| 1990/0227 | } | |
| 1991/0705 | done: mmuphys = PPN((*pg)->pa) | PTEWRITE|PTEVALID; (*pg)->modref = PG_MOD|PG_REF; break; case SG_PHYSICAL: if(*pg == 0) *pg = (*s->pgalloc)(addr); | |
| 1991/0606 | ||
| 1991/0705 | mmuphys = PPN((*pg)->pa) | PTEWRITE|PTEUNCACHED|PTEVALID; (*pg)->modref = PG_MOD|PG_REF; break; default: panic("fault"); | |
| 1990/0227 | } | |
| 1991/0608 | ||
| 1991/0705 | qunlock(&s->lk); | |
| 1991/0719 | /* * A race may provide a page we never used when * a fault is fixed while process slept in newpage */ if(new) putpage(new); | |
| 1991/0607 | ||
| 1991/0705 | 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; loadrec = *p; if(loadrec == 0) { | |
| 1991/0709 | daddr = s->fstart+soff; /* Compute disc address */ | |
| 1991/0705 | new = lookpage(s->image, daddr); | |
| 1990/0227 | } | |
| 1991/0705 | else { daddr = swapaddr(loadrec); new = lookpage(&swapimage, daddr); if(new) putswap(loadrec); } | |
| 1991/0607 | ||
| 1991/0709 | if(new) { /* Page found from cache */ | |
| 1991/0705 | *p = new; return; } | |
| 1991/0607 | ||
| 1991/0705 | 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; qlock(&c->rdl); | |
| 1991/0906 | /* We are unable to correctly error up throught the kernel * during faults. Therefore note delivery is postponed until * we leave the system. Read errors cause the process to die. */ while(waserror()) { if(strcmp(u->error, errstrtab[Eintr]) == 0) continue; | |
| 1991/0705 | qunlock(&c->rdl); kunmap(k); putpage(new); pexit("demand load I/O error", 0); } ask = s->flen-soff; if(ask > BY2PG) ask = BY2PG; n = (*devtab[c->type].read)(c, kaddr, ask, daddr); if(n != ask) error(Eioload); if(ask < BY2PG) memset(kaddr+ask, 0, BY2PG-ask); poperror(); kunmap(k); qunlock(&c->rdl); qlock(&s->lk); if(*p == 0) { /* Someone may have got there first */ new->daddr = daddr; cachepage(new, s->image); *p = new; | |
| 1991/0706 | if(s->flushme) memset(new->cachectl, PG_TXTFLUSH, sizeof(new->cachectl)); | |
| 1991/0705 | } else putpage(new); } else { /* This is paged out */ c = swapimage.c; qlock(&c->rdl); if(waserror()) { qunlock(&c->rdl); kunmap(k); putpage(new); qlock(&s->lk); qunlock(&s->lk); pexit("page in I/O error", 0); } n = (*devtab[c->type].read)(c, kaddr, BY2PG, daddr); if(n != BY2PG) error(Eioload); poperror(); kunmap(k); qunlock(&c->rdl); qlock(&s->lk); if(pagedout(*p)) { new->daddr = daddr; cachepage(new, &swapimage); putswap(*p); *p = new; | |
| 1991/0706 | if(s->flushme) memset(new->cachectl, PG_TXTFLUSH, sizeof(new->cachectl)); | |
| 1991/0705 | } else putpage(new); } | |
| 1990/0227 | } /* * Called only in a system call */ void validaddr(ulong addr, ulong len, int write) { | |
| 1991/0705 | Segment *s; | |
| 1990/0227 | ||
| 1991/0705 | if((long)len >= 0) { for(;;) { s = seg(u->p, 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; } | |
| 1990/0617 | } | |
| 1990/0227 | ||
| 1991/0705 | 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); } | |
| 1990/0227 | /* * &s[0] is known to be a valid address. */ void* | |
| 1991/0705 | vmemchr(void *s, int c, int n) | |
| 1990/0227 | { int m; char *t; ulong a; a = (ulong)s; m = BY2PG - (a & (BY2PG-1)); | |
| 1991/0705 | if(m < n){ t = vmemchr(s, c, m); | |
| 1990/0227 | if(t) return t; if(!(a & KZERO)) validaddr(a+m, 1, 0); | |
| 1991/0705 | return vmemchr((void*)(a+m), c, n-m); | |
| 1990/0227 | } | |
| 1991/0705 | /* * All in one page */ return memchr(s, c, n); | |
| 1990/0227 | } | |
| 1991/0705 | Segment* seg(Proc *p, ulong addr, int dolock) | |
| 1990/0227 | { | |
| 1991/0705 | Segment **s, **et, *n; | |
| 1990/0227 | ||
| 1991/0606 | et = &p->seg[NSEG]; | |
| 1991/0705 | for(s = p->seg; s < et; s++) if(n = *s) 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); } | |
| 1991/0611 | ||
| 1990/0227 | return 0; } | |