int devzwrite(Chan *c, Kzio io[], int nio, int64_t offset) { Mach *m = machp(); int i, j; int32_t tot; Block *bp; DBG("devzwrite %#p[%d]\n", io, nio); tot = 0; for(i = 0; i < nio; i++) tot += io[i].size; bp = nil; if(waserror()){ if(bp != nil) freeb(bp); nexterror(); } if(nio == 1) tot = c->dev->write(c, io[0].data, io[0].size, offset); else{ bp = allocb(tot); if(bp == nil) error(Enomem); for(i = 0; i < nio; i++){ DBG("devzwrite: copy %#p %Z\n", bp->wp, &io[i]); memmove(bp->wp, io[i].data, io[i].size); bp->wp += io[i].size; qlock(&io[i].seg->lk); if(zputaddr(io[i].seg, PTR2UINT(io[i].data)) < 0) panic("devzwrite: not a shared data segment"); qunlock(&io[i].seg->lk); } tot = c->dev->bwrite(c, bp, offset); } j = 0; for(i = 0; i < nio; i++){ io[i].data = nil; /* safety */ io[i].seg = nil; putseg(io[i].seg); if(tot > 0) if(tot >= io[i].size) tot -= io[i].size; else io[i].size = tot; else{ j = i; io[i].size = 0; } io[i].data = nil; /* safety */ putseg(io[i].seg); io[i].seg = nil; } nio = j; poperror(); return nio; }
static int pageout(Proc *p, Segment *s) { Proc *up = externup(); int i, size, n; Pte *l; Page **pg, *entry; if((s->type&SG_TYPE) != SG_LOAD && (s->type&SG_TYPE) != SG_TEXT) panic("pageout"); if(!canqlock(&s->lk)) /* We cannot afford to wait, we will surely deadlock */ return 0; if(s->steal){ /* Protected by /dev/proc */ qunlock(&s->lk); return 0; } if(!canflush(p, s)){ /* Able to invalidate all tlbs with references */ qunlock(&s->lk); putseg(s); return 0; } if(waserror()){ qunlock(&s->lk); putseg(s); return 0; } /* Pass through the pte tables looking for text memory pages to put */ n = 0; size = s->mapsize; for(i = 0; i < size; i++){ l = s->map[i]; if(l == 0) continue; for(pg = l->first; pg < l->last; pg++){ entry = *pg; if(pagedout(entry)) continue; n++; if(entry->modref & PG_REF){ entry->modref &= ~PG_REF; continue; } putpage(*pg); *pg = nil; } } poperror(); qunlock(&s->lk); putseg(s); return n; }
static void pageout(Proc *p, Segment *s) { int type, i, size; Pte *l; Page **pg, *entry; if(!canqlock(&s->lk)) /* We cannot afford to wait, we will surely deadlock */ return; if(s->steal) { /* Protected by /dev/proc */ qunlock(&s->lk); return; } if(!canflush(p, s)) { /* Able to invalidate all tlbs with references */ qunlock(&s->lk); putseg(s); return; } if(waserror()) { qunlock(&s->lk); putseg(s); return; } /* Pass through the pte tables looking for memory pages to swap out */ type = s->type&SG_TYPE; size = s->mapsize; for(i = 0; i < size; i++) { l = s->map[i]; if(l == 0) continue; for(pg = l->first; pg < l->last; pg++) { entry = *pg; if(pagedout(entry)) continue; if(entry->modref & PG_REF) { entry->modref &= ~PG_REF; continue; } pagepte(type, pg); if(ioptr >= conf.nswppo) goto out; } } out: poperror(); qunlock(&s->lk); putseg(s); }
/* * This is the counterpart of devzread in some sense, * it reads in the traditional way from io[]. */ int32_t readzio(Kzio *io, int nio, void *a, int32_t count) { int32_t tot, nr; char *p; p = a; tot = 0; while(nio-- > 0){ if(tot < count){ nr = io->size; if(tot + nr > count) nr = count - tot; DBG("readzio: copy %#p %Z\n", p+tot, io); memmove(p+tot, io->data, nr); tot += nr; } qlock(&io->seg->lk); zputaddr(io->seg, PTR2UINT(io->data)); qunlock(&io->seg->lk); putseg(io->seg); io->seg = nil; io++; } return tot; }
static void putgseg(Globalseg *g) { if(decref(g) > 0) return; if(g->s != nil) putseg(g->s); if(g->kproc) docmd(g, Cdie); free(g->name); free(g->uid); free(g); }
uintptr syssegdetach(va_list list) { int i; uintptr addr; Segment *s; addr = va_arg(list, uintptr); qlock(&up->seglock); if(waserror()){ qunlock(&up->seglock); nexterror(); } s = nil; for(i = 0; i < NSEG; i++) if((s = up->seg[i]) != nil) { qlock(s); if((addr >= s->base && addr < s->top) || (s->top == s->base && addr == s->base)) goto found; qunlock(s); } error(Ebadarg); found: /* * Check we are not detaching the initial stack segment. */ if(s == up->seg[SSEG]){ qunlock(s); error(Ebadarg); } up->seg[i] = nil; qunlock(s); putseg(s); qunlock(&up->seglock); poperror(); /* Ensure we flush any entries from the lost segment */ flushmmu(); return 0; }
static void kernzio(Kzio *io) { Segment *s; void *data; Kzio uio; s = getzkseg(); if(s == nil) error("can't use zero copy in this segment"); uio = *io; data = alloczio(s, io->size); memmove(data, io->data, io->size); io->data = data; DBG("kernzio: copy %Z %Z\n", io, &uio); putseg(io->seg); io->seg = s; }
long syssegdetach(ulong *arg) { int i; ulong addr; Segment *s; qlock(&up->seglock); if(waserror()){ qunlock(&up->seglock); nexterror(); } s = 0; addr = arg[0]; for(i = 0; i < NSEG; i++) if(s = up->seg[i]) { qlock(&s->lk); if((addr >= s->base && addr < s->top) || (s->top == s->base && addr == s->base)) goto found; qunlock(&s->lk); } error(Ebadarg); found: /* * Check we are not detaching the initial stack segment. */ if(s == up->seg[SSEG]){ qunlock(&s->lk); error(Ebadarg); } up->seg[i] = 0; qunlock(&s->lk); putseg(s); qunlock(&up->seglock); poperror(); /* Ensure we flush any entries from the lost segment */ flushmmu(); return 0; }
int devzread(Chan *c, Kzio io[], int nio, usize tot, int64_t offset) { Mach *m = machp(); Segment *s; DBG("devzread %#p[%d]\n", io, nio); s = getzkseg(); if(s == nil) error("no kernel segment for zero-copy"); if(tot > Maxatomic) tot = Maxatomic; io[0].data = alloczio(s, tot); io[0].seg = s; if(waserror()){ zputaddr(s, PTR2UINT(io[0].data)); putseg(s); nexterror(); } io[0].size = c->dev->read(c, io[0].data, tot, offset); poperror(); return 1; }
void putamode(AMODE *ap) /* * output a general addressing mode. */ { int oldnasm, l; switch( ap->mode ) { case am_seg: putseg(ap->seg,0); break; case am_screg: fprintf(outputFile,"CR%d",ap->preg); break; case am_sdreg: fprintf(outputFile,"DR%d",ap->preg); break; case am_streg: fprintf(outputFile,"TR%d",ap->preg); break; case am_immed: if (ap->length && (ap->offset->nodetype == en_labcon || ap->offset->nodetype == en_nacon || ap->offset->nodetype == en_nalabcon || ap->offset->nodetype == en_napccon)) { if (!prm_nasm) fprintf(outputFile,"OFFSET "); else if (!nosize) fprintf(outputFile,"DWORD "); } else if (prm_nasm && addsize) pointersize(ap->length); putconst(ap->offset); break; case am_direct: pointersize(ap->length); putseg(ap->seg,TRUE); /* if (!prm_flat) fprintf(outputFile,"DS:"); */ fprintf(outputFile,"["); oldnasm = prm_nasm; prm_nasm = TRUE; putconst(ap->offset); fputc(']',outputFile); prm_nasm = oldnasm; break; case am_dreg: putsizedreg("%s",ap->preg,ap->length); break; case am_freg: fprintf(outputFile,"ST(%d)",ap->preg); break; case am_indisp: pointersize(ap->length); putseg(ap->seg,TRUE); putsizedreg("[%s",ap->preg,4); if (ap->offset) { fputc('+',outputFile); putconst(ap->offset); } fputc(']',outputFile); break; case am_indispscale: { int scale = 1,t=ap->scale; while (t--) scale <<=1; pointersize(ap->length); putseg(ap->seg,TRUE); if (ap->preg == -1) fputc('[',outputFile); else putsizedreg("[%s+",ap->preg,4); putsizedreg("%s",ap->sreg,4); if (scale != 1) fprintf(outputFile,"*%d",scale); if (ap->offset) { fputc('+',outputFile); putconst(ap->offset); } fputc(']',outputFile); } break; default: DIAG("illegal address mode."); break; } }
void pexit(char *exitstr, int freemem) { Proc *p; Segment **s, **es; long utime, stime; Waitq *wq; Fgrp *fgrp; Egrp *egrp; Rgrp *rgrp; Pgrp *pgrp; Chan *dot; void (*pt)(Proc*, int, vlong); up->alarm = 0; if(up->tt != nil) timerdel(up); pt = proctrace; if(pt != nil) pt(up, SDead, 0); /* nil out all the resources under lock (free later) */ qlock(&up->debug); fgrp = up->fgrp; up->fgrp = nil; egrp = up->egrp; up->egrp = nil; rgrp = up->rgrp; up->rgrp = nil; pgrp = up->pgrp; up->pgrp = nil; dot = up->dot; up->dot = nil; qunlock(&up->debug); if(fgrp != nil) closefgrp(fgrp); if(egrp != nil) closeegrp(egrp); if(rgrp != nil) closergrp(rgrp); if(dot != nil) cclose(dot); if(pgrp != nil) closepgrp(pgrp); /* * if not a kernel process and have a parent, * do some housekeeping. */ if(up->kp == 0 && up->parentpid != 0) { wq = smalloc(sizeof(Waitq)); wq->w.pid = up->pid; utime = up->time[TUser] + up->time[TCUser]; stime = up->time[TSys] + up->time[TCSys]; wq->w.time[TUser] = tk2ms(utime); wq->w.time[TSys] = tk2ms(stime); wq->w.time[TReal] = tk2ms(MACHP(0)->ticks - up->time[TReal]); if(exitstr != nil && exitstr[0]) snprint(wq->w.msg, sizeof(wq->w.msg), "%s %lud: %s", up->text, up->pid, exitstr); else wq->w.msg[0] = '\0'; p = up->parent; lock(&p->exl); /* * Check that parent is still alive. */ if(p->pid == up->parentpid && p->state != Broken) { p->nchild--; p->time[TCUser] += utime; p->time[TCSys] += stime; /* * If there would be more than 128 wait records * processes for my parent, then don't leave a wait * record behind. This helps prevent badly written * daemon processes from accumulating lots of wait * records. */ if(p->nwait < 128) { wq->next = p->waitq; p->waitq = wq; p->nwait++; wq = nil; wakeup(&p->waitr); } } unlock(&p->exl); if(wq != nil) free(wq); } else if(up->kp == 0 && up->parent == nil){ if(exitstr == nil) exitstr = "unknown"; panic("boot process died: %s", exitstr); } if(!freemem) addbroken(up); qlock(&up->seglock); es = &up->seg[NSEG]; for(s = up->seg; s < es; s++) { if(*s != nil) { putseg(*s); *s = nil; } } qunlock(&up->seglock); lock(&up->exl); /* Prevent my children from leaving waits */ pidfree(up); up->pid = 0; wakeup(&up->waitr); unlock(&up->exl); while((wq = up->waitq) != nil){ up->waitq = wq->next; free(wq); } /* release debuggers */ qlock(&up->debug); if(up->pdbg != nil) { wakeup(&up->pdbg->sleep); up->pdbg = nil; } if(up->syscalltrace != nil) { free(up->syscalltrace); up->syscalltrace = nil; } qunlock(&up->debug); /* Sched must not loop for these locks */ lock(&procalloc); lock(&palloc); edfstop(up); up->state = Moribund; sched(); panic("pexit"); }
void pexit(char *exitstr, int freemem) { Mach *m = machp(); Proc *p; Segment **s, **es; int32_t utime, stime; Waitq *wq, *f, *next; Fgrp *fgrp; Egrp *egrp; Rgrp *rgrp; Pgrp *pgrp; Chan *dot; if(0 && m->externup->nfullq > 0) iprint(" %s=%d", m->externup->text, m->externup->nfullq); if(0 && m->externup->nicc > 0) iprint(" [%s nicc %ud tctime %ulld actime %ulld]\n", m->externup->text, m->externup->nicc, m->externup->tctime, m->externup->actime); if(m->externup->syscalltrace != nil) free(m->externup->syscalltrace); m->externup->syscalltrace = nil; m->externup->alarm = 0; if (m->externup->tt) timerdel(m->externup); if(m->externup->trace) proctrace(m->externup, SDead, 0); /* nil out all the resources under lock (free later) */ qlock(&m->externup->debug); fgrp = m->externup->fgrp; m->externup->fgrp = nil; egrp = m->externup->egrp; m->externup->egrp = nil; rgrp = m->externup->rgrp; m->externup->rgrp = nil; pgrp = m->externup->pgrp; m->externup->pgrp = nil; dot = m->externup->dot; m->externup->dot = nil; qunlock(&m->externup->debug); if(fgrp) closefgrp(fgrp); if(egrp) closeegrp(egrp); if(rgrp) closergrp(rgrp); if(dot) cclose(dot); if(pgrp) closepgrp(pgrp); /* * if not a kernel process and have a parent, * do some housekeeping. */ if(m->externup->kp == 0) { p = m->externup->parent; if(p == 0) { if(exitstr == 0) exitstr = "unknown"; //die("bootprocessdeath"); panic("boot process died: %s", exitstr); } while(waserror()) ; wq = smalloc(sizeof(Waitq)); poperror(); wq->w.pid = m->externup->pid; utime = m->externup->time[TUser] + m->externup->time[TCUser]; stime = m->externup->time[TSys] + m->externup->time[TCSys]; wq->w.time[TUser] = tk2ms(utime); wq->w.time[TSys] = tk2ms(stime); wq->w.time[TReal] = tk2ms(sys->machptr[0]->ticks - m->externup->time[TReal]); if(exitstr && exitstr[0]) snprint(wq->w.msg, sizeof(wq->w.msg), "%s %d: %s", m->externup->text, m->externup->pid, exitstr); else wq->w.msg[0] = '\0'; lock(&p->exl); /* * Check that parent is still alive. */ if(p->pid == m->externup->parentpid && p->state != Broken) { p->nchild--; p->time[TCUser] += utime; p->time[TCSys] += stime; /* * If there would be more than 128 wait records * processes for my parent, then don't leave a wait * record behind. This helps prevent badly written * daemon processes from accumulating lots of wait * records. */ if(p->nwait < 128) { wq->next = p->waitq; p->waitq = wq; p->nwait++; wq = nil; wakeup(&p->waitr); } } unlock(&p->exl); if(wq) free(wq); } if(!freemem) addbroken(m->externup); qlock(&m->externup->seglock); es = &m->externup->seg[NSEG]; for(s = m->externup->seg; s < es; s++) { if(*s) { putseg(*s); *s = 0; } } qunlock(&m->externup->seglock); lock(&m->externup->exl); /* Prevent my children from leaving waits */ psunhash(m->externup); m->externup->pid = 0; wakeup(&m->externup->waitr); unlock(&m->externup->exl); for(f = m->externup->waitq; f; f = next) { next = f->next; free(f); } /* release debuggers */ qlock(&m->externup->debug); if(m->externup->pdbg) { wakeup(&m->externup->pdbg->sleep); m->externup->pdbg = 0; } qunlock(&m->externup->debug); /* Sched must not loop for these locks */ lock(&procalloc); lock(&pga); stopac(); edfstop(m->externup); m->externup->state = Moribund; sched(); panic("pexit"); }
/* * Zero copy I/O. * I/O is performed using an array of Zio structures. * Each one points to a shared buffer address indicating a length. * Each entry indicating a length and using nil as the address * is asking the system to allocate memory as needed (mread only). */ static int ziorw(int fd, Zio *io, int nio, usize count, int64_t offset, int iswrite) { Mach *m = machp(); int i, n, isprw; Kzio *kio, skio[16]; Chan *c; usize tot; if(nio <= 0 || nio > 512) error("wrong io[] size"); zioinit(); kio = nil; io = validaddr(io, sizeof io[0] * nio, 1); DBG("ziorw %d io%#p[%d] %uld %lld\n", fd, io, nio, count, offset); if(DBGFLG) for(i = 0; i < nio; i++) print("\tio%#p[%d] = %Z %s\n", io, i, (Kzio*)&io[i], iswrite?"w":"r"); if(iswrite) c = fdtochan(fd, OWRITE, 1, 1); else c = fdtochan(fd, OREAD, 1, 1); isprw = offset != -1LL; if(isprw) offset = c->offset; if(waserror()){ cclose(c); if(kio != nil){ for(i = 0; i < nio; i++) if(kio[i].seg != nil) putseg(kio[i].seg); if(kio != skio) free(kio); } nexterror(); } if(nio < nelem(skio)) kio = skio; else kio = smalloc(sizeof kio[0] * nio); for(i = 0; i < nio; i++){ kio[i].Zio = io[i]; if(iswrite){ kio[i].seg = seg(m->externup, PTR2UINT(io[i].data), 1); if(kio[i].seg == nil) error("invalid address in zio"); incref(kio[i].seg); qunlock(&kio[i].seg->lk); validaddr(kio[i].data, kio[i].size, 1); if((kio[i].seg->type&SG_ZIO) == 0){ /* * It's not a segment where we can report * addresses to anyone once they are free. * So, allocate space in the kernel * and copy the user data there. */ kernzio(&kio[i]); } assert(kio[i].seg->type&SG_ZIO); }else{ kio[i].data = nil; kio[i].seg = nil; } } if(c->dev->zread == nil){ DBG("installing devzread for %s\n", c->dev->name); c->dev->zread = devzread; } if(c->dev->zwrite == nil){ DBG("installing devzwrite for %s\n", c->dev->name); c->dev->zwrite = devzwrite; } if(iswrite) n = c->dev->zwrite(c, kio, nio, offset); else n = c->dev->zread(c, kio, nio, count, offset); tot = 0; for(i = 0; i < n; i++){ io[i] = kio[i].Zio; tot += kio[i].size; } if(!isprw){ /* unlike in syswrite, we update offsets at the end */ lock(c); c->devoffset += tot; c->offset += tot; unlock(c); } poperror(); cclose(c); if(kio != skio) free(kio); return n; }
uintptr sysexec(va_list list) { Segment *s, *ts; int i; Chan *tc; char **argv, **argp, **argp0; char *a, *e, *charp, *args, *file, *file0; char *progarg[sizeof(Exec)/2+1], *elem, progelem[64]; ulong magic, ssize, nargs, nbytes, n; uintptr t, d, b, entry, bssend, text, data, bss, tstk, align; int indir; Exec exec; char line[sizeof(Exec)]; Fgrp *f; Image *img; Tos *tos; args = elem = nil; file0 = va_arg(list, char*); validaddr((uintptr)file0, 1, 0); argp0 = va_arg(list, char**); evenaddr((uintptr)argp0); validaddr((uintptr)argp0, 2*BY2WD, 0); if(*argp0 == nil) error(Ebadarg); file0 = validnamedup(file0, 1); if(waserror()){ free(file0); free(elem); free(args); /* Disaster after commit */ if(up->seg[SSEG] == nil) pexit(up->errstr, 1); s = up->seg[ESEG]; if(s != nil){ putseg(s); up->seg[ESEG] = nil; } nexterror(); } align = BY2PG; indir = 0; file = file0; for(;;){ tc = namec(file, Aopen, OEXEC, 0); if(waserror()){ cclose(tc); nexterror(); } if(!indir) kstrdup(&elem, up->genbuf); n = devtab[tc->type]->read(tc, &exec, sizeof(Exec), 0); if(n <= 2) error(Ebadexec); magic = l2be(exec.magic); if(n == sizeof(Exec) && (magic == AOUT_MAGIC)){ entry = l2be(exec.entry); text = l2be(exec.text); if(magic & HDR_MAGIC) text += 8; switch(magic){ case S_MAGIC: /* 2MB segment alignment for amd64 */ align = 0x200000; break; case V_MAGIC: /* 16K segment alignment for mips */ align = 0x4000; break; } if(text >= (USTKTOP-USTKSIZE)-(UTZERO+sizeof(Exec)) || entry < UTZERO+sizeof(Exec) || entry >= UTZERO+sizeof(Exec)+text) error(Ebadexec); break; /* for binary */ } /* * Process #! /bin/sh args ... */ memmove(line, &exec, n); if(indir || line[0]!='#' || line[1]!='!') error(Ebadexec); n = shargs(line, n, progarg); if(n < 1) error(Ebadexec); indir = 1; /* * First arg becomes complete file name */ progarg[n++] = file; progarg[n] = nil; argp0++; file = progarg[0]; if(strlen(elem) >= sizeof progelem) error(Ebadexec); strcpy(progelem, elem); progarg[0] = progelem; poperror(); cclose(tc); } data = l2be(exec.data); bss = l2be(exec.bss); align--; t = (UTZERO+sizeof(Exec)+text+align) & ~align; align = BY2PG-1; d = (t + data + align) & ~align; bssend = t + data + bss; b = (bssend + align) & ~align; if(t >= (USTKTOP-USTKSIZE) || d >= (USTKTOP-USTKSIZE) || b >= (USTKTOP-USTKSIZE)) error(Ebadexec); /* * Args: pass 1: count */ nbytes = sizeof(Tos); /* hole for profiling clock at top of stack (and more) */ nargs = 0; if(indir){ argp = progarg; while(*argp != nil){ a = *argp++; nbytes += strlen(a) + 1; nargs++; } } argp = argp0; while(*argp != nil){ a = *argp++; if(((uintptr)argp&(BY2PG-1)) < BY2WD) validaddr((uintptr)argp, BY2WD, 0); validaddr((uintptr)a, 1, 0); e = vmemchr(a, 0, USTKSIZE); if(e == nil) error(Ebadarg); nbytes += (e - a) + 1; if(nbytes >= USTKSIZE) error(Enovmem); nargs++; } ssize = BY2WD*(nargs+1) + ((nbytes+(BY2WD-1)) & ~(BY2WD-1)); /* * 8-byte align SP for those (e.g. sparc) that need it. * execregs() will subtract another 4 bytes for argc. */ if(BY2WD == 4 && (ssize+4) & 7) ssize += 4; if(PGROUND(ssize) >= USTKSIZE) error(Enovmem); /* * Build the stack segment, putting it in kernel virtual for the moment */ qlock(&up->seglock); if(waserror()){ qunlock(&up->seglock); nexterror(); } s = up->seg[SSEG]; do { tstk = s->base; if(tstk <= USTKSIZE) error(Enovmem); } while((s = isoverlap(up, tstk-USTKSIZE, USTKSIZE)) != nil); up->seg[ESEG] = newseg(SG_STACK, tstk-USTKSIZE, USTKSIZE/BY2PG); /* * Args: pass 2: assemble; the pages will be faulted in */ tos = (Tos*)(tstk - sizeof(Tos)); tos->cyclefreq = m->cyclefreq; tos->kcycles = 0; tos->pcycles = 0; tos->clock = 0; argv = (char**)(tstk - ssize); charp = (char*)(tstk - nbytes); if(indir) argp = progarg; else argp = argp0; for(i=0; i<nargs; i++){ if(indir && *argp==nil) { indir = 0; argp = argp0; } *argv++ = charp + (USTKTOP-tstk); a = *argp++; if(indir) e = strchr(a, 0); else { validaddr((uintptr)a, 1, 0); e = vmemchr(a, 0, (char*)tstk - charp); if(e == nil) error(Ebadarg); } n = (e - a) + 1; memmove(charp, a, n); charp += n; } /* copy args; easiest from new process's stack */ a = (char*)(tstk - nbytes); n = charp - a; if(n > 128) /* don't waste too much space on huge arg lists */ n = 128; args = smalloc(n); memmove(args, a, n); if(n>0 && args[n-1]!='\0'){ /* make sure last arg is NUL-terminated */ /* put NUL at UTF-8 character boundary */ for(i=n-1; i>0; --i) if(fullrune(args+i, n-i)) break; args[i] = 0; n = i+1; } /* * Committed. * Free old memory. * Special segments are maintained across exec */ for(i = SSEG; i <= BSEG; i++) { putseg(up->seg[i]); /* prevent a second free if we have an error */ up->seg[i] = nil; } for(i = ESEG+1; i < NSEG; i++) { s = up->seg[i]; if(s != nil && (s->type&SG_CEXEC) != 0) { putseg(s); up->seg[i] = nil; } } /* * Close on exec */ if((f = up->fgrp) != nil) { for(i=0; i<=f->maxfd; i++) fdclose(i, CCEXEC); } /* Text. Shared. Attaches to cache image if possible */ /* attachimage returns a locked cache image */ img = attachimage(SG_TEXT|SG_RONLY, tc, UTZERO, (t-UTZERO)>>PGSHIFT); ts = img->s; up->seg[TSEG] = ts; ts->flushme = 1; ts->fstart = 0; ts->flen = sizeof(Exec)+text; unlock(img); /* Data. Shared. */ s = newseg(SG_DATA, t, (d-t)>>PGSHIFT); up->seg[DSEG] = s; /* Attached by hand */ incref(img); s->image = img; s->fstart = ts->fstart+ts->flen; s->flen = data; /* BSS. Zero fill on demand */ up->seg[BSEG] = newseg(SG_BSS, d, (b-d)>>PGSHIFT); /* * Move the stack */ s = up->seg[ESEG]; up->seg[ESEG] = nil; s->base = USTKTOP-USTKSIZE; s->top = USTKTOP; relocateseg(s, USTKTOP-tstk); up->seg[SSEG] = s; qunlock(&up->seglock); poperror(); /* seglock */ /* * '/' processes are higher priority (hack to make /ip more responsive). */ if(devtab[tc->type]->dc == L'/') up->basepri = PriRoot; up->priority = up->basepri; poperror(); /* tc */ cclose(tc); poperror(); /* file0 */ free(file0); qlock(&up->debug); free(up->text); up->text = elem; free(up->args); up->args = args; up->nargs = n; up->setargs = 0; up->nnote = 0; up->notify = 0; up->notified = 0; up->privatemem = 0; up->noswap = 0; procsetup(up); qunlock(&up->debug); /* * At this point, the mmu contains info about the old address * space and needs to be flushed */ flushmmu(); if(up->hang) up->procctl = Proc_stopme; return execregs(entry, ssize, nargs); }
long sysexec(ulong *arg) { Segment *s, *ts; ulong t, d, b; int i; Chan *tc; char **argv, **argp; char *a, *charp, *args, *file, *file0; char *progarg[sizeof(Exec)/2+1], *elem, progelem[64]; ulong ssize, spage, nargs, nbytes, n, bssend; int indir; Exec exec; char line[sizeof(Exec)]; Fgrp *f; Image *img; ulong magic, text, entry, data, bss; Tos *tos; indir = 0; elem = nil; validaddr(arg[0], 1, 0); file0 = validnamedup((char*)arg[0], 1); if(waserror()){ free(file0); free(elem); nexterror(); } file = file0; for(;;){ tc = namec(file, Aopen, OEXEC, 0); if(waserror()){ cclose(tc); nexterror(); } if(!indir) kstrdup(&elem, up->genbuf); n = devtab[tc->type]->read(tc, &exec, sizeof(Exec), 0); if(n < 2) error(Ebadexec); magic = l2be(exec.magic); text = l2be(exec.text); entry = l2be(exec.entry); if(n==sizeof(Exec) && (magic == AOUT_MAGIC)){ if(text >= USTKTOP-UTZERO || entry < UTZERO+sizeof(Exec) || entry >= UTZERO+sizeof(Exec)+text) error(Ebadexec); break; /* for binary */ } /* * Process #! /bin/sh args ... */ memmove(line, &exec, sizeof(Exec)); if(indir || line[0]!='#' || line[1]!='!') error(Ebadexec); n = shargs(line, n, progarg); if(n == 0) error(Ebadexec); indir = 1; /* * First arg becomes complete file name */ progarg[n++] = file; progarg[n] = 0; validaddr(arg[1], BY2WD, 1); arg[1] += BY2WD; file = progarg[0]; if(strlen(elem) >= sizeof progelem) error(Ebadexec); strcpy(progelem, elem); progarg[0] = progelem; poperror(); cclose(tc); } data = l2be(exec.data); bss = l2be(exec.bss); t = (UTZERO+sizeof(Exec)+text+(BY2PG-1)) & ~(BY2PG-1); d = (t + data + (BY2PG-1)) & ~(BY2PG-1); bssend = t + data + bss; b = (bssend + (BY2PG-1)) & ~(BY2PG-1); if(t >= KZERO || d >= KZERO || b >= KZERO) error(Ebadexec); /* * Args: pass 1: count */ nbytes = sizeof(Tos); /* hole for profiling clock at top of stack (and more) */ nargs = 0; if(indir){ argp = progarg; while(*argp){ a = *argp++; nbytes += strlen(a) + 1; nargs++; } } evenaddr(arg[1]); argp = (char**)arg[1]; validaddr((ulong)argp, BY2WD, 0); while(*argp){ a = *argp++; if(((ulong)argp&(BY2PG-1)) < BY2WD) validaddr((ulong)argp, BY2WD, 0); validaddr((ulong)a, 1, 0); nbytes += ((char*)vmemchr(a, 0, 0x7FFFFFFF) - a) + 1; nargs++; } ssize = BY2WD*(nargs+1) + ((nbytes+(BY2WD-1)) & ~(BY2WD-1)); /* * 8-byte align SP for those (e.g. sparc) that need it. * execregs() will subtract another 4 bytes for argc. */ if((ssize+4) & 7) ssize += 4; spage = (ssize+(BY2PG-1)) >> PGSHIFT; /* * Build the stack segment, putting it in kernel virtual for the moment */ if(spage > TSTKSIZ) error(Enovmem); qlock(&up->seglock); if(waserror()){ qunlock(&up->seglock); nexterror(); } up->seg[ESEG] = newseg(SG_STACK, TSTKTOP-USTKSIZE, USTKSIZE/BY2PG); /* * Args: pass 2: assemble; the pages will be faulted in */ tos = (Tos*)(TSTKTOP - sizeof(Tos)); tos->cyclefreq = m->cyclefreq; cycles((uvlong*)&tos->pcycles); tos->pcycles = -tos->pcycles; tos->kcycles = tos->pcycles; tos->clock = 0; argv = (char**)(TSTKTOP - ssize); charp = (char*)(TSTKTOP - nbytes); args = charp; if(indir) argp = progarg; else argp = (char**)arg[1]; for(i=0; i<nargs; i++){ if(indir && *argp==0) { indir = 0; argp = (char**)arg[1]; } *argv++ = charp + (USTKTOP-TSTKTOP); n = strlen(*argp) + 1; memmove(charp, *argp++, n); charp += n; } free(file0); free(up->text); up->text = elem; elem = nil; /* so waserror() won't free elem */ USED(elem); /* copy args; easiest from new process's stack */ n = charp - args; if(n > 128) /* don't waste too much space on huge arg lists */ n = 128; a = up->args; up->args = nil; free(a); up->args = smalloc(n); memmove(up->args, args, n); if(n>0 && up->args[n-1]!='\0'){ /* make sure last arg is NUL-terminated */ /* put NUL at UTF-8 character boundary */ for(i=n-1; i>0; --i) if(fullrune(up->args+i, n-i)) break; up->args[i] = 0; n = i+1; } up->nargs = n; /* * Committed. * Free old memory. * Special segments are maintained across exec */ for(i = SSEG; i <= BSEG; i++) { putseg(up->seg[i]); /* prevent a second free if we have an error */ up->seg[i] = 0; } for(i = BSEG+1; i < NSEG; i++) { s = up->seg[i]; if(s != 0 && (s->type&SG_CEXEC)) { putseg(s); up->seg[i] = 0; } } /* * Close on exec */ f = up->fgrp; for(i=0; i<=f->maxfd; i++) fdclose(i, CCEXEC); /* Text. Shared. Attaches to cache image if possible */ /* attachimage returns a locked cache image */ img = attachimage(SG_TEXT|SG_RONLY, tc, UTZERO, (t-UTZERO)>>PGSHIFT); ts = img->s; up->seg[TSEG] = ts; ts->flushme = 1; ts->fstart = 0; ts->flen = sizeof(Exec)+text; unlock(img); /* Data. Shared. */ s = newseg(SG_DATA, t, (d-t)>>PGSHIFT); up->seg[DSEG] = s; /* Attached by hand */ incref(img); s->image = img; s->fstart = ts->fstart+ts->flen; s->flen = data; /* BSS. Zero fill on demand */ up->seg[BSEG] = newseg(SG_BSS, d, (b-d)>>PGSHIFT); /* * Move the stack */ s = up->seg[ESEG]; up->seg[ESEG] = 0; up->seg[SSEG] = s; qunlock(&up->seglock); poperror(); /* seglock */ poperror(); /* elem */ s->base = USTKTOP-USTKSIZE; s->top = USTKTOP; relocateseg(s, USTKTOP-TSTKTOP); /* * '/' processes are higher priority (hack to make /ip more responsive). */ if(devtab[tc->type]->dc == L'/') up->basepri = PriRoot; up->priority = up->basepri; poperror(); cclose(tc); /* * At this point, the mmu contains info about the old address * space and needs to be flushed */ flushmmu(); qlock(&up->debug); up->nnote = 0; up->notify = 0; up->notified = 0; up->privatemem = 0; procsetup(up); qunlock(&up->debug); if(up->hang) up->procctl = Proc_stopme; return execregs(entry, ssize, nargs); }