summaryrefslogtreecommitdiff
path: root/sys/src/9/port/fault.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/fault.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/fault.c')
-rwxr-xr-xsys/src/9/port/fault.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/sys/src/9/port/fault.c b/sys/src/9/port/fault.c
new file mode 100755
index 000000000..b6ae6e456
--- /dev/null
+++ b/sys/src/9/port/fault.c
@@ -0,0 +1,407 @@
+#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 == nil)
+ panic("fault: nil up");
+ 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);
+}
+
+void (*checkaddr)(ulong, Segment *, Page *);
+ulong addr2check;
+
+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);
+
+ 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)
+ 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;
+ }
+ }
+
+ if (checkaddr && addr == addr2check)
+ (*checkaddr)(addr, s, *pg);
+ 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;
+ }
+
+ c = s->image->c;
+ ask = s->flen-soff;
+ if(ask > BY2PG)
+ ask = BY2PG;
+ }
+ else { /* from a swap image */
+ daddr = swapaddr(loadrec);
+ new = lookpage(&swapimage, daddr);
+ if(new != nil) {
+ putswap(loadrec);
+ *p = new;
+ return;
+ }
+
+ c = swapimage.c;
+ ask = BY2PG;
+ }
+ qunlock(&s->lk);
+
+ new = newpage(0, 0, addr);
+ k = kmap(new);
+ kaddr = (char*)VA(k);
+
+ while(waserror()) {
+ if(strcmp(up->errstr, Eintr) == 0)
+ continue;
+ kunmap(k);
+ putpage(new);
+ faulterror(Eioload, c, 0);
+ }
+
+ 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);
+ if(loadrec == 0) { /* This is demand load */
+ /*
+ * 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 */
+ /*
+ * 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 %#lux/%lud in sys call pc=%#lux\n", addr, len, userpc());
+ return 0;
+}
+
+void
+validaddr(ulong addr, ulong len, int write)
+{
+ if(!okaddr(addr, len, write)){
+ postnote(up, 1, "sys: bad address in syscall", NDebug);
+ error(Ebadarg);
+ }
+}
+
+/*
+ * &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);
+}