void pexit(char *exitstr, int freemem) { Proc *up = externup(); 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 && up->nfullq > 0) iprint(" %s=%d", up->text, up->nfullq); if(0 && up->nicc > 0) iprint(" [%s nicc %ud tctime %ulld actime %ulld]\n", up->text, up->nicc, up->tctime, up->actime); if(up->syscalltrace != nil) free(up->syscalltrace); up->syscalltrace = nil; up->alarm = 0; clearwakeups(up); if (up->Timer.tt) timerdel(&up->Timer); if(up->trace) proctrace(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) 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(up->kp == 0) { p = up->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 = 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(sys->machptr[0]->ticks - up->time[TReal]); if(exitstr && exitstr[0]) snprint(wq->w.msg, sizeof(wq->w.msg), "%s %d: %s", up->text, up->pid, exitstr); else wq->w.msg[0] = '\0'; 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) free(wq); } if(!freemem) addbroken(up); qlock(&up->seglock); es = &up->seg[NSEG]; for(s = up->seg; s < es; s++) { if(*s) { putseg(*s); *s = 0; } } qunlock(&up->seglock); lock(&up->exl); /* Prevent my children from leaving waits */ psunhash(up); up->pid = 0; wakeup(&up->waitr); unlock(&up->exl); for(f = up->waitq; f; f = next) { next = f->next; free(f); } /* release debuggers */ qlock(&up->debug); if(up->pdbg) { wakeup(&up->pdbg->sleep); up->pdbg = 0; } qunlock(&up->debug); /* Sched must not loop for these locks */ lock(&procalloc.l); lock(&pga.l); stopac(); edfstop(up); up->state = Moribund; sched(); panic("pexit"); }
static void releaseintr(Ureg* ureg, Timer *t) { Proc *up = externup(); Proc *p; extern int panicking; Sched *sch; Schedq *rq; if(panicking || active.exiting) return; p = t->ta; if((edflock(p)) == nil) return; sch = procsched(p); DPRINT("%lu releaseintr %d[%s]\n", now, p->pid, statename[p->state]); switch(p->state){ default: edfunlock(); return; case Ready: /* remove proc from current runq */ rq = &sch->runq[p->priority]; if(dequeueproc(sch, rq, p) != p){ DPRINT("releaseintr: can't find proc or lock race\n"); release(p); /* It'll start best effort */ edfunlock(); return; } p->state = Waitrelease; /* fall through */ case Waitrelease: release(p); edfunlock(); if(p->state == Wakeme){ iprint("releaseintr: wakeme\n"); } ready(p); if(up){ up->delaysched++; sch->delayedscheds++; } return; case Running: release(p); edfrun(p, 1); break; case Wakeme: release(p); edfunlock(); if(p->trend) wakeup(p->trend); p->trend = nil; if(up){ up->delaysched++; sch->delayedscheds++; } return; } edfunlock(); }
/* * Move the current process to an application core. * This is performed at the end of execac(), and * we pretend to be returning to user-space, but instead we * dispatch the process to another core. * 1. We do the final bookkeeping that syscall() would do after * a return from sysexec(), because we are not returning. * 2. We dispatch the process to an AC using an ICC. * * This function won't return unless the process is reclaimed back * to the time-sharing core, and is the handler for the process * to deal with traps and system calls until the process dies. * * Remember that this function is the "line" between user and kernel * space, it's not expected to raise|handle any error. * * We install a safety error label, just in case we raise errors, * which we shouldn't. (noerrorsleft knows that for exotic processes * there is an error label pushed by us). */ void runacore(void) { Proc *up = externup(); Ureg *ureg; void (*fn)(void); int rc, flush, s; char *n; uint64_t t1; if(waserror()) panic("runacore: error: %s\n", up->errstr); ureg = up->dbgreg; fakeretfromsyscall(ureg); fpusysrfork(ureg); procpriority(up, PriKproc, 1); rc = runac(up->ac, actouser, 1, nil, 0); procpriority(up, PriNormal, 0); for(;;){ t1 = fastticks(nil); flush = 0; fn = nil; switch(rc){ case ICCTRAP: s = splhi(); machp()->MMU.cr2 = up->ac->MMU.cr2; DBG("runacore: trap %llu cr2 %#llx ureg %#p\n", ureg->type, machp()->MMU.cr2, ureg); switch(ureg->type){ case IdtIPI: if(up->procctl || up->nnote) notify(up->dbgreg); if(up->ac == nil) goto ToTC; kexit(up->dbgreg); break; case IdtNM: case IdtMF: case IdtXF: /* these are handled in the AC; * If we get here, they left in m->NIX.icc->data * a note to be posted to the process. * Post it, and make the vector a NOP. */ n = up->ac->NIX.icc->note; if(n != nil) postnote(up, 1, n, NDebug); ureg->type = IdtIPI; /* NOP */ break; default: cr3put(machp()->MMU.pml4->pa); if(0 && ureg->type == IdtPF){ print("before PF:\n"); print("AC:\n"); dumpptepg(4, up->ac->MMU.pml4->pa); print("\n%s:\n", rolename[NIXTC]); dumpptepg(4, machp()->MMU.pml4->pa); } trap(ureg); } splx(s); flush = 1; fn = actrapret; break; case ICCSYSCALL: DBG("runacore: syscall ax %#llx ureg %#p\n", ureg->ax, ureg); cr3put(machp()->MMU.pml4->pa); //syscall(ureg->ax, ureg); flush = 1; fn = acsysret; if(0) if(up->nqtrap > 2 || up->nsyscall > 1) goto ToTC; if(up->ac == nil) goto ToTC; break; default: panic("runacore: unexpected rc = %d", rc); } up->tctime += fastticks2us(fastticks(nil) - t1); procpriority(up, PriExtra, 1); rc = runac(up->ac, fn, flush, nil, 0); procpriority(up, PriNormal, 0); } ToTC: /* * to procctl, then syscall, to * be back in the TC */ DBG("runacore: up %#p: return\n", up); }
/* * Call user, if necessary, with note. * Pass user the Ureg struct and the note on his stack. */ int notify(Ureg* ureg) { Proc *up = externup(); int l; Mpl pl; Note note; uintptr_t sp; NFrame *nf; /* * Calls procctl splhi, see comment in procctl for the reasoning. */ if(up->procctl) procctl(up); if(up->nnote == 0) return 0; fpunotify(ureg); pl = spllo(); qlock(&up->debug); up->notepending = 0; memmove(¬e, &up->note[0], sizeof(Note)); if(strncmp(note.msg, "sys:", 4) == 0){ l = strlen(note.msg); if(l > ERRMAX-sizeof(" pc=0x0123456789abcdef")) l = ERRMAX-sizeof(" pc=0x0123456789abcdef"); sprint(note.msg+l, " pc=%#p", ureg->ip); } if(note.flag != NUser && (up->notified || up->notify == nil)){ qunlock(&up->debug); if(note.flag == NDebug) pprint("suicide: %s\n", note.msg); pexit(note.msg, note.flag != NDebug); } if(up->notified){ qunlock(&up->debug); splhi(); return 0; } if(up->notify == nil){ qunlock(&up->debug); pexit(note.msg, note.flag != NDebug); } if(!okaddr(PTR2UINT(up->notify), sizeof(ureg->ip), 0)){ qunlock(&up->debug); pprint("suicide: bad function address %#p in notify\n", up->notify); pexit("Suicide", 0); } sp = ureg->sp - ROUNDUP(sizeof(NFrame), 16) - 128; // amd64 red zone, also wanted by go stack traces if(!okaddr(sp, sizeof(NFrame), 1)){ qunlock(&up->debug); pprint("suicide: bad stack address %#p in notify\n", sp); pexit("Suicide", 0); } nf = UINT2PTR(sp); memmove(&nf->ureg, ureg, sizeof(Ureg)); nf->old = up->ureg; up->ureg = nf; /* actually the NFrame, for noted */ memmove(nf->msg, note.msg, ERRMAX); nf->arg1 = nf->msg; nf->arg0 = &nf->ureg; ureg->di = (uintptr)nf->arg0; ureg->si = (uintptr)nf->arg1; //print("Setting di to %p and si to %p\n", ureg->di, ureg->si); ureg->bp = PTR2UINT(nf->arg0); nf->ip = 0; ureg->sp = sp; ureg->ip = PTR2UINT(up->notify); up->notified = 1; up->nnote--; memmove(&up->lastnote, ¬e, sizeof(Note)); memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); qunlock(&up->debug); splx(pl); return 1; }
/* * Return user to state before notify() */ void noted(Ureg* cur, uintptr_t arg0) { Proc *up = externup(); NFrame *nf; Note note; Ureg *nur; qlock(&up->debug); if(arg0 != NRSTR && !up->notified){ qunlock(&up->debug); pprint("suicide: call to noted when not notified\n"); pexit("Suicide", 0); } up->notified = 0; fpunoted(); nf = up->ureg; /* sanity clause */ if(!okaddr(PTR2UINT(nf), sizeof(NFrame), 0)){ qunlock(&up->debug); pprint("suicide: bad ureg %#p in noted\n", nf); pexit("Suicide", 0); } /* * Check the segment selectors are all valid. */ nur = &nf->ureg; if(nur->cs != SSEL(SiUCS, SsRPL3) || nur->ss != SSEL(SiUDS, SsRPL3)) { qunlock(&up->debug); pprint("suicide: bad segment selector (cs %p want %p, ss %p want %p), in noted\n", nur->cs, SSEL(SiUCS, SsRPL3), nur->ss, SSEL(SiUDS, SsRPL3) ); pexit("Suicide", 0); } /* don't let user change system flags */ nur->flags &= (Of|Df|Sf|Zf|Af|Pf|Cf); nur->flags |= cur->flags & ~(Of|Df|Sf|Zf|Af|Pf|Cf); memmove(cur, nur, sizeof(Ureg)); switch((int)arg0){ case NCONT: case NRSTR: if(!okaddr(nur->ip, BY2SE, 0) || !okaddr(nur->sp, BY2SE, 0)){ qunlock(&up->debug); pprint("suicide: trap in noted pc=%#p sp=%#p\n", nur->ip, nur->sp); pexit("Suicide", 0); } up->ureg = nf->old; qunlock(&up->debug); break; case NSAVE: if(!okaddr(nur->ip, BY2SE, 0) || !okaddr(nur->sp, BY2SE, 0)){ qunlock(&up->debug); pprint("suicide: trap in noted pc=%#p sp=%#p\n", nur->ip, nur->sp); pexit("Suicide", 0); } qunlock(&up->debug); splhi(); nf->arg1 = nf->msg; nf->arg0 = &nf->ureg; cur->bp = PTR2UINT(nf->arg0); nf->ip = 0; cur->sp = PTR2UINT(nf); break; default: memmove(¬e, &up->lastnote, sizeof(Note)); qunlock(&up->debug); pprint("suicide: bad arg %#p in noted: %s\n", arg0, note.msg); pexit(note.msg, 0); break; case NDFLT: memmove(¬e, &up->lastnote, sizeof(Note)); qunlock(&up->debug); if(note.flag == NDebug) pprint("suicide: %s\n", note.msg); pexit(note.msg, note.flag != NDebug); break; } }
/* * the devxxx.c that calls us handles writing data, it knows best */ int32_t netifwrite(Netif *nif, Chan *c, void *a, int32_t n) { Proc *up = externup(); Netfile *f; int type, mtu; char *p, buf[64]; uint8_t binaddr[Nmaxaddr]; if(NETTYPE(c->qid.path) != Nctlqid) error(Eperm); if(n >= sizeof(buf)) n = sizeof(buf)-1; memmove(buf, a, n); buf[n] = 0; if(waserror()){ qunlock(nif); nexterror(); } qlock(nif); f = nif->f[NETID(c->qid.path)]; if((p = matchtoken(buf, "connect")) != 0){ qclose(f->iq); type = atoi(p); if(typeinuse(nif, type)) error(Einuse); f->type = type; if(f->type < 0) nif->all++; qreopen(f->iq); } else if(matchtoken(buf, "promiscuous")){ if(f->prom == 0){ if(nif->prom == 0 && nif->promiscuous != nil) nif->promiscuous(nif->arg, 1); f->prom = 1; nif->prom++; } } else if((p = matchtoken(buf, "scanbs")) != 0){ /* scan for base stations */ if(f->scan == 0){ type = atoi(p); if(type < 5) type = 5; if(nif->scanbs != nil) nif->scanbs(nif->arg, type); f->scan = type; nif->_scan++; } } else if((p = matchtoken(buf, "mtu")) != 0){ /* poor planning. */ if(!iseve()) error(Eperm); mtu = atoi(p); /* zero resets default. */ if(mtu != 0) if(mtu < nif->minmtu || mtu > nif->maxmtu) error(Ebadarg); if(nif->hwmtu) nif->mtu = nif->hwmtu(nif->arg, mtu); else nif->mtu = mtu; } else if(matchtoken(buf, "l2bridge")){ f->bridge |= 2; } else if(matchtoken(buf, "bridge")){ f->bridge |= 1; } else if(matchtoken(buf, "headersonly")){ f->headersonly = 1; } else if((p = matchtoken(buf, "addmulti")) != 0){ if(parseaddr(binaddr, p, nif->alen) < 0) error("bad address"); p = netmulti(nif, f, binaddr, 1); if(p) error(p); } else if((p = matchtoken(buf, "remmulti")) != 0){ if(parseaddr(binaddr, p, nif->alen) < 0) error("bad address"); p = netmulti(nif, f, binaddr, 0); if(p) error(p); } else n = -1; qunlock(nif); poperror(); return n; }
/* * pg->pgszi indicates the page size in machp()->pgsz[] used for the mapping. * For the user, it can be either 2*MiB or 1*GiB pages. * For 2*MiB pages, we use three levels, not four. * For 1*GiB pages, we use two levels. */ void mmuput(uintptr_t va, Page *pg, uint attr) { Proc *up = externup(); int lvl, user, x, pgsz; PTE *pte; Page *page, *prev; Mpl pl; uintmem pa, ppn; char buf[80]; ppn = 0; pa = pg->pa; if(pa == 0) panic("mmuput: zero pa"); if(DBGFLG) { snprint(buf, sizeof buf, "cpu%d: up %#p mmuput %#p %#P %#ux\n", machp()->machno, up, va, pa, attr); print("%s", buf); } assert(pg->pgszi >= 0); pgsz = sys->pgsz[pg->pgszi]; if(pa & (pgsz-1)) panic("mmuput: pa offset non zero: %#ullx\n", pa); pa |= pteflags(attr); pl = splhi(); if(DBGFLG) mmuptpcheck(up); user = (va < KZERO); x = PTLX(va, 3); pte = UINT2PTR(machp()->MMU.pml4->va); pte += x; prev = machp()->MMU.pml4; for(lvl = 3; lvl >= 0; lvl--) { if(user) { if(pgsz == 2*MiB && lvl == 1) /* use 2M */ break; if(pgsz == 1ull*GiB && lvl == 2) /* use 1G */ break; } for(page = up->MMU.mmuptp[lvl]; page != nil; page = page->next) if(page->prev == prev && page->daddr == x) { if(*pte == 0) { print("mmu: jmk and nemo had fun\n"); *pte = PPN(page->pa)|PteU|PteRW|PteP; } break; } if(page == nil) { if(up->MMU.mmuptp[0] == nil) page = mmuptpalloc(); else { page = up->MMU.mmuptp[0]; up->MMU.mmuptp[0] = page->next; } page->daddr = x; page->next = up->MMU.mmuptp[lvl]; up->MMU.mmuptp[lvl] = page; page->prev = prev; *pte = PPN(page->pa)|PteU|PteRW|PteP; if(lvl == 3 && x >= machp()->MMU.pml4->daddr) machp()->MMU.pml4->daddr = x+1; } x = PTLX(va, lvl-1); ppn = PPN(*pte); if(ppn == 0) panic("mmuput: ppn=0 l%d pte %#p = %#P\n", lvl, pte, *pte); pte = UINT2PTR(KADDR(ppn)); pte += x; prev = page; } if(DBGFLG) checkpte(ppn, pte); *pte = pa|PteU; if(user) switch(pgsz) { case 2*MiB: case 1*GiB: *pte |= PtePS; break; default: panic("mmuput: user pages must be 2M or 1G"); } splx(pl); if(DBGFLG) { snprint(buf, sizeof buf, "cpu%d: up %#p new pte %#p = %#llux\n", machp()->machno, up, pte, pte?*pte:~0); print("%s", buf); } invlpg(va); /* only if old entry valid? */ }
int ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c) { Proc *up = externup(); int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff; int morefrags, blklen, rv = 0, tentative; uint8_t *gate, nexthdr; Block *xp, *nb; Fraghdr6 fraghdr; IP *ip; Ip6hdr *eh; Ipifc *ifc; Route *r, *sr; ip = f->ip; /* Fill out the ip header */ eh = (Ip6hdr*)(bp->rp); ip->stats[OutRequests]++; /* Number of uint8_ts in data and ip header to write */ len = blocklen(bp); tentative = iptentative(f, eh->src); if(tentative){ netlog(f, Logip, "reject tx of packet with tentative src address %I\n", eh->src); goto free; } if(gating){ chunk = nhgets(eh->ploadlen); if(chunk > len){ ip->stats[OutDiscards]++; netlog(f, Logip, "short gated packet\n"); goto free; } if(chunk + IP6HDR < len) len = chunk + IP6HDR; } if(len >= IP_MAX){ ip->stats[OutDiscards]++; netlog(f, Logip, "exceeded ip max size %I\n", eh->dst); goto free; } r = v6lookup(f, eh->dst, c); if(r == nil){ // print("no route for %I, src %I free\n", eh->dst, eh->src); ip->stats[OutNoRoutes]++; netlog(f, Logip, "no interface %I\n", eh->dst); rv = -1; goto free; } ifc = r->RouteTree.ifc; if(r->RouteTree.type & (Rifc|Runi)) gate = eh->dst; else if(r->RouteTree.type & (Rbcast|Rmulti)) { gate = eh->dst; sr = v6lookup(f, eh->src, nil); if(sr && (sr->RouteTree.type & Runi)) ifc = sr->RouteTree.ifc; } else gate = r->v6.gate; if(!gating) eh->vcf[0] = IP_VER6; eh->ttl = ttl; if(!gating) { eh->vcf[0] |= tos >> 4; eh->vcf[1] = tos << 4; }
static int procgen(Chan *c, char *name, Dirtab *tab, int j, int s, Dir *dp) { Proc *up = externup(); Qid qid; Proc *p; char *ename; int pid, sno; uint32_t path, perm, len; if(s == DEVDOTDOT){ mkqid(&qid, Qdir, 0, QTDIR); devdir(c, qid, "#p", 0, eve, 0555, dp); return 1; } if(c->qid.path == Qdir){ if(s == 0){ strcpy(up->genbuf, "trace"); mkqid(&qid, Qtrace, -1, QTFILE); devdir(c, qid, up->genbuf, 0, eve, 0444, dp); return 1; } if(s == 1){ strcpy(up->genbuf, "tracepids"); mkqid(&qid, Qtracepids, -1, QTFILE); devdir(c, qid, up->genbuf, 0, eve, 0444, dp); return 1; } s -= 2; if(name != nil){ /* ignore s and use name to find pid */ pid = strtol(name, &ename, 10); if(pid<=0 || ename[0]!='\0') return -1; s = psindex(pid); if(s < 0) return -1; } else if(s >= conf.nproc) return -1; if((p = psincref(s)) == nil || (pid = p->pid) == 0) return 0; snprint(up->genbuf, sizeof up->genbuf, "%u", pid); /* * String comparison is done in devwalk so * name must match its formatted pid. */ if(name != nil && strcmp(name, up->genbuf) != 0) return -1; mkqid(&qid, (s+1)<<QSHIFT, pid, QTDIR); devdir(c, qid, up->genbuf, 0, p->user, DMDIR|0555, dp); psdecref(p); return 1; } if(c->qid.path == Qtrace){ strcpy(up->genbuf, "trace"); mkqid(&qid, Qtrace, -1, QTFILE); devdir(c, qid, up->genbuf, 0, eve, 0444, dp); return 1; } if(c->qid.path == Qtracepids){ strcpy(up->genbuf, "tracepids"); mkqid(&qid, Qtrace, -1, QTFILE); devdir(c, qid, up->genbuf, 0, eve, 0444, dp); return 1; } if(s >= nelem(procdir)) return -1; if(tab) panic("procgen"); tab = &procdir[s]; path = c->qid.path&~(((1<<QSHIFT)-1)); /* slot component */ if((p = psincref(SLOT(c->qid))) == nil) return -1; perm = tab->perm; if(perm == 0) perm = p->procmode; else /* just copy read bits */ perm |= p->procmode & 0444; len = tab->length; switch(QID(c->qid)) { case Qwait: len = p->nwait; /* incorrect size, but >0 means there's something to read */ break; case Qprofile: /* TODO(aki): test this */ len = 0; for(sno = 0; sno < NSEG; sno++){ if(p->seg[sno] != nil && (p->seg[sno]->type & SG_EXEC) != 0){ Segment *s; s = p->seg[sno]; if(s->profile) len += ((s->top-s->base)>>LRESPROF) * sizeof s->profile[0]; } } break; }
void mmuinit(void) { Proc *up = externup(); uint8_t *p; Page *page; uint64_t o, pa, r, sz; archmmu(); DBG("mach%d: %#p pml4 %#p npgsz %d\n", machp()->machno, m, machp()->pml4, m->npgsz); if(machp()->machno != 0){ /* NIX: KLUDGE: Has to go when each mach is using * its own page table */ p = UINT2PTR(m->stack); p += MACHSTKSZ; memmove(p, UINT2PTR(mach0pml4.va), PTSZ); machp()->pml4 = &machp()->pml4kludge; machp()->pml4->va = PTR2UINT(p); machp()->pml4->pa = PADDR(p); machp()->pml4->daddr = mach0pml4.daddr; /* # of user mappings in pml4 */ r = rdmsr(Efer); r |= Nxe; wrmsr(Efer, r); cr3put(machp()->pml4->pa); DBG("m %#p pml4 %#p\n", m, machp()->pml4); return; } page = &mach0pml4; page->pa = cr3get(); page->va = PTR2UINT(KADDR(page->pa)); machp()->pml4 = page; r = rdmsr(Efer); r |= Nxe; wrmsr(Efer, r); /* * Set up the various kernel memory allocator limits: * pmstart/pmend bound the unused physical memory; * vmstart/vmend bound the total possible virtual memory * used by the kernel; * vmunused is the highest virtual address currently mapped * and used by the kernel; * vmunmapped is the highest virtual address currently * mapped by the kernel. * Vmunused can be bumped up to vmunmapped before more * physical memory needs to be allocated and mapped. * * This is set up here so meminit can map appropriately. */ o = sys->pmstart; sz = ROUNDUP(o, 4*MiB) - o; pa = asmalloc(0, sz, 1, 0); if(pa != o) panic("mmuinit: pa %#llux memstart %#llux\n", pa, o); sys->pmstart += sz; sys->vmstart = KSEG0; sys->vmunused = sys->vmstart + ROUNDUP(o, 4*KiB); sys->vmunmapped = sys->vmstart + o + sz; sys->vmend = sys->vmstart + TMFM; print("mmuinit: vmstart %#p vmunused %#p vmunmapped %#p vmend %#p\n", sys->vmstart, sys->vmunused, sys->vmunmapped, sys->vmend); /* * Set up the map for PD entry access by inserting * the relevant PDP entry into the PD. It's equivalent * to PADDR(sys->pd)|PteRW|PteP. * */ sys->pd[PDX(PDMAP)] = sys->pdp[PDPX(PDMAP)] & ~(PteD|PteA); print("sys->pd %#p %#p\n", sys->pd[PDX(PDMAP)], sys->pdp[PDPX(PDMAP)]); assert((pdeget(PDMAP) & ~(PteD|PteA)) == (PADDR(sys->pd)|PteRW|PteP)); dumpmmuwalk(KZERO); mmuphysaddr(PTR2UINT(end)); }
static int32_t pmcwrite(Chan *c, void *a, int32_t n, int64_t mm) { Proc *up = externup(); Cmdbuf *cb; Cmdtab *ct; uint32_t type; char str[64]; /* 0x0000000000000000\0 */ AcPmcArg p; AcCtrArg ctr; uint64_t coreno; Mach *mp; if (c->qid.type == QTDIR) error(Eperm); if (c->qid.path == Qgctl) error(Eperm); if (n >= sizeof(str)) error(Ebadctl); pmcnull(&p); coreno = (uint64_t)c->aux; p.coreno = coreno; type = PMCTYPE(c->qid.path); p.regno = PMCID(c->qid.path); memmove(str, a, n); str[n] = '\0'; mp = up->ac; ctr.coreno = coreno; ctr.regno = p.regno; if (type == Qdata) { /* I am a handler for a proc in the core, run an RPC*/ if (mp != nil && mp->machno == coreno) { if (runac(mp, acpmcsetctr, 0, &ctr, sizeof(AcCtrArg)) < 0) n = -1; } else { if (pmcsetctr(coreno, strtoull(str, 0, 0), p.regno) < 0) n = -1; } return n; } /* TODO: should iterate through multiple lines */ if (strncmp(str, "set ", 4) == 0){ memmove(p.descstr, (char *)str + 4, n - 4); p.descstr[n - 4] = '\0'; p.nodesc = 0; } else { cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, pmcctlmsg, nelem(pmcctlmsg)); switch(ct->index){ case Enable: p.enab = 1; break; case Disable: p.enab = 0; break; case User: p.user = 1; break; case Os: p.os = 1; break; case NoUser: p.user = 0; break; case NoOs: p.os = 0; break; case Reset: p.reset = 1; break; case Debug: pmcdebug = ~pmcdebug; break; default: cmderror(cb, "invalid ctl"); break; } free(cb); poperror(); } /* I am a handler for a proc in the core, run an RPC*/ if (mp != nil && mp->machno == coreno) { if (runac(mp, acpmcsetctl, 0, &p, sizeof(AcPmcArg)) < 0) n = -1; } else { if (pmcsetctl(coreno, &p, p.regno) < 0) n = -1; } return n; }
int anyhigher(void) { Proc *up = externup(); return run.runvec & ~((1<<(up->priority+1))-1); }
void nexterror(void) { Proc *up = externup(); /*debug*/gotolabel(&up->errlab[--up->nerrlab]); }
/* * If changing this routine, look also at sleep(). It * contains a copy of the guts of sched(). */ void sched(void) { Proc *up = externup(); Proc *p; if(!islo() && machp()->ilockdepth) panic("cpu%d: ilockdepth %d, last lock %#p at %#p, sched called from %#p", machp()->machno, machp()->ilockdepth, up? up->lastilock: nil, (up && up->lastilock)? up->lastilock->_pc: 0, getcallerpc()); kstackok(); if(up){ /* * Delay the sched until the process gives up the locks * it is holding. This avoids dumb lock loops. * Don't delay if the process is Moribund. * It called sched to die. * But do sched eventually. This avoids a missing unlock * from hanging the entire kernel. * But don't reschedule procs holding palloc or procalloc. * Those are far too important to be holding while asleep. * * This test is not exact. There can still be a few * instructions in the middle of taslock when a process * holds a lock but Lock.p has not yet been initialized. */ if(up->nlocks) if(up->state != Moribund) if(up->delaysched < 20 || pga.l.p == up || procalloc.l.p == up){ up->delaysched++; run.delayedscheds++; return; } up->delaysched = 0; splhi(); /* statistics */ if(up->nqtrap == 0 && up->nqsyscall == 0) up->nfullq++; machp()->cs++; procsave(up); mmuflushtlb(machp()->MMU.pml4->pa); if(setlabel(&up->sched)){ procrestore(up); spllo(); return; } /*debug*/gotolabel(&machp()->sched); } machp()->inidle = 1; p = runproc(); /* core 0 never returns */ machp()->inidle = 0; if(!p->edf){ updatecpu(p); p->priority = reprioritize(p); } if(nosmp){ if(p != machp()->readied) machp()->schedticks = machp()->ticks + HZ/10; machp()->readied = 0; } machp()->externup = p; up = p; machp()->qstart = machp()->ticks; up->nqtrap = 0; up->nqsyscall = 0; up->state = Running; //up->mach = m; up->mach = sys->machptr[machp()->machno]; machp()->proc = up; // iprint("up->sched.sp %p * %p\n", up->sched.sp, // *(void **) up->sched.sp); mmuswitch(up); assert(!up->wired || up->wired == machp()); if (0) hi("gotolabel\n"); /*debug*/gotolabel(&up->sched); }
/* * 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) { Proc *up = externup(); 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] %lu %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(up, PTR2UINT(io[i].data), 1); if(kio[i].seg == nil) error("invalid address in zio"); incref(&kio[i].seg->r); qunlock(&kio[i].seg->lk); validaddr(kio[i].Zio.data, kio[i].Zio.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].Zio.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].Zio.size; } if(!isprw){ /* unlike in syswrite, we update offsets at the end */ lock(&c->r.l); c->devoffset += tot; c->offset += tot; unlock(&c->r.l); } poperror(); cclose(c); if(kio != skio) free(kio); return n; }
/* * All traps come here. It is slower to have all traps call trap() * rather than directly vectoring the handler. However, this avoids a * lot of code duplication and possible bugs. The only exception is * VectorSYSCALL. * Trap is called with interrupts disabled via interrupt-gates. */ void trap(Ureg* ureg) { int clockintr, vno, user; // cache the previous vno to see what might be causing // trouble vno = ureg->type; uint64_t gsbase = rdmsr(GSbase); //if (sce > scx) iprint("===================="); lastvno = vno; if (gsbase < 1ULL<<63) die("bogus gsbase"); Proc *up = externup(); char buf[ERRMAX]; Vctl *ctl, *v; machp()->perf.intrts = perfticks(); user = userureg(ureg); if(user && (machp()->NIX.nixtype == NIXTC)) { up->dbgreg = ureg; cycles(&up->kentry); } clockintr = 0; //_pmcupdate(machp()); if(ctl = vctl[vno]) { if(ctl->isintr) { machp()->intr++; if(vno >= VectorPIC && vno != VectorSYSCALL) machp()->lastintr = ctl->Vkey.irq; } else if(up) up->nqtrap++; if(ctl->isr) { ctl->isr(vno); if(islo())print("trap %d: isr %p enabled interrupts\n", vno, ctl->isr); } for(v = ctl; v != nil; v = v->next) { if(v->f) { v->f(ureg, v->a); if(islo())print("trap %d: ctlf %p enabled interrupts\n", vno, v->f); } } if(ctl->eoi) { ctl->eoi(vno); if(islo())print("trap %d: eoi %p enabled interrupts\n", vno, ctl->eoi); } intrtime(vno); if(ctl->isintr) { if(ctl->Vkey.irq == IrqCLOCK || ctl->Vkey.irq == IrqTIMER) clockintr = 1; if (ctl->Vkey.irq == IrqTIMER) oprof_alarm_handler(ureg); if(up && !clockintr) preempted(); } } else if(vno < nelem(excname) && user) { spllo(); snprint(buf, sizeof buf, "sys: trap: %s", excname[vno]); postnote(up, 1, buf, NDebug); } else if(vno >= VectorPIC && vno != VectorSYSCALL) { /* * An unknown interrupt. * Check for a default IRQ7. This can happen when * the IRQ input goes away before the acknowledge. * In this case, a 'default IRQ7' is generated, but * the corresponding bit in the ISR isn't set. * In fact, just ignore all such interrupts. */ /* clear the interrupt */ i8259isr(vno); iprint("cpu%d: spurious interrupt %d, last %d\n", machp()->machno, vno, machp()->lastintr); intrtime(vno); if(user) kexit(ureg); return; } else { if(vno == VectorNMI) { nmienable(); if(machp()->machno != 0) { iprint("cpu%d: PC %#llx\n", machp()->machno, ureg->ip); for(;;); } } dumpregs(ureg); if(!user) { ureg->sp = PTR2UINT(&ureg->sp); dumpstackwithureg(ureg); } if(vno < nelem(excname)) panic("%s", excname[vno]); panic("unknown trap/intr: %d\n", vno); } splhi(); /* delaysched set because we held a lock or because our quantum ended */ if(up && up->delaysched && clockintr) { if(0) if(user && up->ac == nil && up->nqtrap == 0 && up->nqsyscall == 0) { if(!waserror()) { up->ac = getac(up, -1); poperror(); runacore(); return; } } sched(); splhi(); } if(user) { if(up && up->procctl || up->nnote) notify(ureg); kexit(ureg); } }
int32_t netifread(Netif *nif, Chan *c, void *a, int32_t n, int64_t off) { Proc *up = externup(); int i, j; Netfile *f; char *p; int32_t offset; if(c->qid.type & QTDIR) return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen); offset = off; switch(NETTYPE(c->qid.path)){ case Ndataqid: f = nif->f[NETID(c->qid.path)]; return qread(f->iq, a, n); case Nctlqid: return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE); case Nstatqid: p = malloc(READSTR); if(p == nil) error(Enomem); j = snprint(p, READSTR, "in: %llud\n", nif->inpackets); j += snprint(p+j, READSTR-j, "link: %d\n", nif->link); j += snprint(p+j, READSTR-j, "out: %llud\n", nif->outpackets); j += snprint(p+j, READSTR-j, "crc errs: %llud\n", nif->crcs); j += snprint(p+j, READSTR-j, "overflows: %llud\n", nif->overflows); j += snprint(p+j, READSTR-j, "soft overflows: %llud\n", nif->soverflows); j += snprint(p+j, READSTR-j, "framing errs: %llud\n", nif->frames); j += snprint(p+j, READSTR-j, "buffer errs: %llud\n", nif->buffs); j += snprint(p+j, READSTR-j, "output errs: %llud\n", nif->oerrs); j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom); j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps); j += snprint(p+j, READSTR-j, "addr: "); for(i = 0; i < nif->alen; i++) j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); snprint(p+j, READSTR-j, "\n"); n = readstr(offset, a, n, p); free(p); return n; case Naddrqid: p = malloc(READSTR); j = 0; for(i = 0; i < nif->alen; i++) j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); n = readstr(offset, a, n, p); free(p); return n; case Ntypeqid: f = nif->f[NETID(c->qid.path)]; return readnum(offset, a, n, f->type, NUMSIZE); case Nifstatqid: return 0; case Nmtuqid: snprint(up->genbuf, sizeof up->genbuf, "%11.ud %11.ud %11.ud\n", nif->minmtu, nif->mtu, nif->maxmtu); return readstr(offset, a, n, up->genbuf); } error(Ebadarg); return -1; /* not reached */ }
void asmmeminit(void) { Proc *up = externup(); int i, l; Asm* assem; PTE *pte, *pml4; uintptr va; uintmem hi, lo, mem, nextmem, pa; #ifdef ConfCrap int cx; #endif /* ConfCrap */ assert(!((sys->vmunmapped|sys->vmend) & machp()->pgszmask[1])); if((pa = mmuphysaddr(sys->vmunused)) == ~0) panic("asmmeminit 1"); pa += sys->vmunmapped - sys->vmunused; mem = asmalloc(pa, sys->vmend - sys->vmunmapped, 1, 0); if(mem != pa) panic("asmmeminit 2"); DBG("pa %#llux mem %#llux\n", pa, mem); /* assume already 2MiB aligned*/ assert(ALIGNED(sys->vmunmapped, 2*MiB)); pml4 = UINT2PTR(machp()->pml4->va); while(sys->vmunmapped < sys->vmend) { l = mmuwalk(pml4, sys->vmunmapped, 1, &pte, asmwalkalloc); DBG("%#p l %d\n", sys->vmunmapped, l); *pte = pa|PtePS|PteRW|PteP; sys->vmunmapped += 2*MiB; pa += 2*MiB; } #ifdef ConfCrap cx = 0; #endif /* ConfCrap */ for(assem = asmlist; assem != nil; assem = assem->next) { if(assem->type != AsmMEMORY) continue; va = KSEG2+assem->addr; print("asm: addr %#P end %#P type %d size %P\n", assem->addr, assem->addr+assem->size, assem->type, assem->size); lo = assem->addr; hi = assem->addr+assem->size; /* Convert a range into pages */ for(mem = lo; mem < hi; mem = nextmem) { nextmem = (mem + PGLSZ(0)) & ~machp()->pgszmask[0]; /* Try large pages first */ for(i = m->npgsz - 1; i >= 0; i--) { if((mem & machp()->pgszmask[i]) != 0) continue; if(mem + PGLSZ(i) > hi) continue; /* This page fits entirely within the range. */ /* Mark it a usable */ if((l = mmuwalk(pml4, va, i, &pte, asmwalkalloc)) < 0) panic("asmmeminit 3"); *pte = mem|PteRW|PteP; if(l > 0) *pte |= PtePS; nextmem = mem + PGLSZ(i); va += PGLSZ(i); npg[i]++; break; } } #ifdef ConfCrap /* * Fill in conf crap. */ if(cx >= nelem(conf.mem)) continue; lo = ROUNDUP(assem->addr, PGSZ); //if(lo >= 600ull*MiB) // continue; conf.mem[cx].base = lo; hi = ROUNDDN(hi, PGSZ); //if(hi > 600ull*MiB) // hi = 600*MiB; conf.mem[cx].npage = (hi - lo)/PGSZ; conf.npage += conf.mem[cx].npage; print("cm %d: addr %#llux npage %lud\n", cx, conf.mem[cx].base, conf.mem[cx].npage); cx++; #endif /* ConfCrap */ } print("%d %d %d\n", npg[0], npg[1], npg[2]); #ifdef ConfCrap /* * Fill in more conf crap. * This is why I hate Plan 9. */ conf.upages = conf.npage; i = (sys->vmend - sys->vmstart)/PGSZ; /* close enough */ conf.ialloc = (i/2)*PGSZ; print("npage %llud upage %lud kpage %d\n", conf.npage, conf.upages, i); #endif /* ConfCrap */ }
/* * generate a 3 level directory */ static int netifgen(Chan *c, char* j, Dirtab *vp, int n, int i, Dir *dp) { Proc *up = externup(); Qid q; Netif *nif = (Netif*)vp; Netfile *f; int t; int perm; char *o; q.type = QTFILE; q.vers = 0; /* top level directory contains the name of the network */ if(c->qid.path == 0){ switch(i){ case DEVDOTDOT: q.path = 0; q.type = QTDIR; devdir(c, q, ".", 0, eve, 0555, dp); break; case 0: q.path = N2ndqid; q.type = QTDIR; strcpy(up->genbuf, nif->name); devdir(c, q, up->genbuf, 0, eve, 0555, dp); break; default: return -1; } return 1; } /* second level contains clone plus all the conversations */ t = NETTYPE(c->qid.path); if(t == N2ndqid || t == Ncloneqid || t == Naddrqid || t == Nstatqid || t == Nifstatqid || t == Nmtuqid){ switch(i) { case DEVDOTDOT: q.type = QTDIR; q.path = 0; devdir(c, q, ".", 0, eve, DMDIR|0555, dp); break; case 0: q.path = Ncloneqid; devdir(c, q, "clone", 0, eve, 0666, dp); break; case 1: q.path = Naddrqid; devdir(c, q, "addr", 0, eve, 0666, dp); break; case 2: q.path = Nstatqid; devdir(c, q, "stats", 0, eve, 0444, dp); break; case 3: q.path = Nifstatqid; devdir(c, q, "ifstats", 0, eve, 0444, dp); break; case 4: q.path = Nmtuqid; devdir(c, q, "mtu", 0, eve, 0444, dp); break; default: i -= 5; if(i >= nif->nfile) return -1; if(nif->f[i] == 0) return 0; q.type = QTDIR; q.path = NETQID(i, N3rdqid); snprint(up->genbuf, sizeof up->genbuf, "%d", i); devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); break; } return 1; } /* third level */ f = nif->f[NETID(c->qid.path)]; if(f == 0) return 0; if(*f->owner){ o = f->owner; perm = f->mode; } else { o = eve; perm = 0666; } switch(i){ case DEVDOTDOT: q.type = QTDIR; q.path = N2ndqid; strcpy(up->genbuf, nif->name); devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); break; case 0: q.path = NETQID(NETID(c->qid.path), Ndataqid); devdir(c, q, "data", 0, o, perm, dp); break; case 1: q.path = NETQID(NETID(c->qid.path), Nctlqid); devdir(c, q, "ctl", 0, o, perm, dp); break; case 2: q.path = NETQID(NETID(c->qid.path), Nstatqid); devdir(c, q, "stats", 0, eve, 0444, dp); break; case 3: q.path = NETQID(NETID(c->qid.path), Ntypeqid); devdir(c, q, "type", 0, eve, 0444, dp); break; case 4: q.path = NETQID(NETID(c->qid.path), Nifstatqid); devdir(c, q, "ifstats", 0, eve, 0444, dp); break; default: return -1; } return 1; }
void rudpkick(void *x) { Proc *up = externup(); Conv *c = x; Udphdr *uh; uint16_t rport; uint8_t laddr[IPaddrlen], raddr[IPaddrlen]; Block *bp; Rudpcb *ucb; Rudphdr *rh; Reliable *r; int dlen, ptcllen; Rudppriv *upriv; Fs *f; upriv = c->p->priv; f = c->p->f; netlog(c->p->f, Logrudp, "rudp: kick\n"); bp = qget(c->wq); if(bp == nil) return; ucb = (Rudpcb*)c->ptcl; switch(ucb->headers) { case 7: /* get user specified addresses */ bp = pullupblock(bp, UDP_USEAD7); if(bp == nil) return; ipmove(raddr, bp->rp); bp->rp += IPaddrlen; ipmove(laddr, bp->rp); bp->rp += IPaddrlen; /* pick interface closest to dest */ if(ipforme(f, laddr) != Runi) findlocalip(f, laddr, raddr); bp->rp += IPaddrlen; /* Ignore ifc address */ rport = nhgets(bp->rp); bp->rp += 2+2; /* Ignore local port */ break; default: ipmove(raddr, c->raddr); ipmove(laddr, c->laddr); rport = c->rport; break; } dlen = blocklen(bp); /* Make space to fit rudp & ip header */ bp = padblock(bp, UDP_IPHDR+UDP_RHDRSIZE); if(bp == nil) return; uh = (Udphdr *)(bp->rp); uh->vihl = IP_VER4; rh = (Rudphdr*)uh; ptcllen = dlen + (UDP_RHDRSIZE-UDP_PHDRSIZE); uh->Unused = 0; uh->udpproto = IP_UDPPROTO; uh->frag[0] = 0; uh->frag[1] = 0; hnputs(uh->udpplen, ptcllen); switch(ucb->headers){ case 7: v6tov4(uh->udpdst, raddr); hnputs(uh->udpdport, rport); v6tov4(uh->udpsrc, laddr); break; default: v6tov4(uh->udpdst, c->raddr); hnputs(uh->udpdport, c->rport); if(ipcmp(c->laddr, IPnoaddr) == 0) findlocalip(f, c->laddr, c->raddr); v6tov4(uh->udpsrc, c->laddr); break; } hnputs(uh->udpsport, c->lport); hnputs(uh->udplen, ptcllen); uh->udpcksum[0] = 0; uh->udpcksum[1] = 0; qlock(&ucb->ql); r = relstate(ucb, raddr, rport, "kick"); r->sndseq = NEXTSEQ(r->sndseq); hnputl(rh->relseq, r->sndseq); hnputl(rh->relsgen, r->sndgen); hnputl(rh->relack, r->rcvseq); /* ACK last rcvd packet */ hnputl(rh->relagen, r->rcvgen); if(r->rcvseq != r->acksent) r->acksent = r->rcvseq; hnputs(uh->udpcksum, ptclcsum(bp, UDP_IPHDR, dlen+UDP_RHDRSIZE)); relackq(r, bp); qunlock(&ucb->ql); upriv->ustats.rudpOutDatagrams++; DPRINT("sent: %lud/%lud, %lud/%lud\n", r->sndseq, r->sndgen, r->rcvseq, r->rcvgen); doipoput(c, f, bp, 0, c->ttl, c->tos); if(waserror()) { relput(r); qunlock(&r->lock); nexterror(); } /* flow control of sorts */ qlock(&r->lock); if(UNACKED(r) > Maxunacked){ r->blocked = 1; sleep(&r->vous, flow, r); r->blocked = 0; } qunlock(&r->lock); relput(r); poperror(); }
/* * attach a device (or pkt driver) to the interface. * called with c locked */ static char* ipifcbind(Conv *c, char **argv, int argc) { Proc *up = externup(); Ipifc *ifc; Medium *medium; if(argc < 2) return Ebadarg; ifc = (Ipifc*)c->ptcl; /* bind the device to the interface */ medium = ipfindmedium(argv[1]); if(medium == nil) return "unknown interface type"; wlock(ifc); if(ifc->medium != nil){ wunlock(ifc); return "interface already bound"; } if(waserror()){ wunlock(ifc); nexterror(); } /* do medium specific binding */ (*medium->bind)(ifc, argc, argv); /* set the bound device name */ if(argc > 2) strncpy(ifc->dev, argv[2], sizeof(ifc->dev)); else snprint(ifc->dev, sizeof ifc->dev, "%s%d", medium->name, c->x); ifc->dev[sizeof(ifc->dev)-1] = 0; /* set up parameters */ ifc->medium = medium; ifc->mintu = ifc->medium->mintu; ifc->maxtu = ifc->medium->maxtu; if(ifc->medium->unbindonclose == 0) ifc->conv->inuse++; ifc->rp.mflag = 0; /* default not managed */ ifc->rp.oflag = 0; ifc->rp.maxraint = 600000; /* millisecs */ ifc->rp.minraint = 200000; ifc->rp.linkmtu = 0; /* no mtu sent */ ifc->rp.reachtime = 0; ifc->rp.rxmitra = 0; ifc->rp.ttl = MAXTTL; ifc->rp.routerlt = 3 * ifc->rp.maxraint; /* any ancillary structures (like routes) no int32_ter pertain */ ifc->ifcid++; /* reopen all the queues closed by a previous unbind */ qreopen(c->rq); qreopen(c->eq); qreopen(c->sq); wunlock(ifc); poperror(); return nil; }
Walkqid* devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen) { Proc *up = externup(); if (0) print_func_entry(); int i, j, alloc; Walkqid *wq; char *n; Dir dir; if(nname > 0) isdir(c); if (0) { int i; iprint("%d names:", nname); for(i = 0; i < nname; i++) iprint("%s ", name[i]); iprint("\n");} alloc = 0; wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); if(waserror()){ if(alloc && wq->clone!=nil) cclose(wq->clone); free(wq); if (0) print_func_exit(); return nil; } if(nc == nil){ nc = devclone(c); /* * nc->dev remains nil for now. //XDYNX */ alloc = 1; } wq->clone = nc; for(j=0; j<nname; j++){ if(!(nc->qid.type & QTDIR)){ if(j==0) error(Enotdir); goto Done; } n = name[j]; if(strcmp(n, ".") == 0){ Accept: wq->qid[wq->nqid++] = nc->qid; continue; } if(strcmp(n, "..") == 0){ /* * Use c->dev->name in the error because * nc->dev should be nil here. */ if((*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir) != 1){ print("devgen walk .. in dev%s %#llx broken\n", c->dev->name, nc->qid.path); error("broken devgen"); } nc->qid = dir.qid; goto Accept; } /* * Ugly problem: If we're using devgen, make sure we're * walking the directory itself, represented by the first * entry in the table, and not trying to step into a sub- * directory of the table, e.g. /net/net. Devgen itself * should take care of the problem, but it doesn't have * the necessary information (that we're doing a walk). */ if(gen==devgen && nc->qid.path!=tab[0].qid.path) goto Notfound; for(i=0;; i++) { switch((*gen)(nc, n, tab, ntab, i, &dir)){ case -1: Notfound: if(j == 0) error(Enonexist); kstrcpy(up->errstr, Enonexist, ERRMAX); goto Done; case 0: continue; case 1: if(strcmp(n, dir.name) == 0){ nc->qid = dir.qid; goto Accept; } continue; } } } /* * We processed at least one name, so will return some data. * If we didn't process all nname entries succesfully, we drop * the cloned channel and return just the Qids of the walks. */ Done: poperror(); if(wq->nqid < nname){ if(alloc) cclose(wq->clone); wq->clone = nil; }else if(wq->clone){ /* attach cloned channel to same device */ //what goes here: //XDYNX // ->dev must be nil because can't walk an open chan, right? // what about ref count on dev? wq->clone->dev = c->dev; //if(wq->clone->dev) //XDYNX // devtabincr(wq->clone->dev); } if (0) print_func_exit(); return wq; }
/* it should be unsigned. FIXME */ void syscall(int badscallnr, Ureg *ureg) { // can only handle 4 args right now. uintptr_t a0, a1, a2, a3; uintptr_t a4, a5 = 0; a0 = ureg->di; a1 = ureg->si; a2 = ureg->dx; a3 = ureg->r10; a4 = ureg->r8; Proc *up = externup(); unsigned int scallnr = (unsigned int) badscallnr; if (0) iprint("Syscall %d, %lx, %lx, %lx %lx %lx\n", scallnr, a0, a1, a2, a3, a4); char *e; uintptr_t sp; int s; int64_t startns, stopns; Ar0 ar0; static Ar0 zar0; if(!userureg(ureg)) panic("syscall: cs %#llux\n", ureg->cs); cycles(&up->kentry); machp()->syscall++; up->nsyscall++; up->nqsyscall++; up->insyscall = 1; up->pc = ureg->ip; up->dbgreg = ureg; sp = ureg->sp; startns = 0; if (0) hi("so far syscall!\n"); if (printallsyscalls) { syscallfmt(scallnr, a0, a1, a2, a3, a4, a5); if(up->syscalltrace) { print("E %s\n", up->syscalltrace); free(up->syscalltrace); up->syscalltrace = nil; } } if(up->procctl == Proc_tracesyscall){ /* * Redundant validaddr. Do we care? * Tracing syscalls is not exactly a fast path... * Beware, validaddr currently does a pexit rather * than an error if there's a problem; that might * change in the future. */ if(sp < (USTKTOP-BIGPGSZ) || sp > (USTKTOP-sizeof(up->arg)-BY2SE)) validaddr(UINT2PTR(sp), sizeof(up->arg)+BY2SE, 0); syscallfmt(scallnr, a0, a1, a2, a3, a4, a5); up->procctl = Proc_stopme; procctl(up); if(up->syscalltrace) free(up->syscalltrace); up->syscalltrace = nil; startns = todget(nil); } if (0) hi("more syscall!\n"); up->scallnr = scallnr; if(scallnr == RFORK) fpusysrfork(ureg); spllo(); sp = ureg->sp; up->nerrlab = 0; ar0 = zar0; if(!waserror()){ if(scallnr >= nsyscall || systab[scallnr].f == nil){ pprint("bad sys call number %d pc %#llux\n", scallnr, ureg->ip); postnote(up, 1, "sys: bad sys call", NDebug); error(Ebadarg); } if(sp < (USTKTOP-BIGPGSZ) || sp > (USTKTOP-sizeof(up->arg)-BY2SE)) validaddr(UINT2PTR(sp), sizeof(up->arg)+BY2SE, 0); memmove(up->arg, UINT2PTR(sp+BY2SE), sizeof(up->arg)); up->psstate = systab[scallnr].n; if (0) hi("call syscall!\n"); systab[scallnr].f(&ar0, a0, a1, a2, a3, a4, a5); if (0) hi("it returned!\n"); if(scallnr == SYSR1){ /* * BUG: must go when ron binaries go. * NIX: Returning from execac(). * This means that the process is back to the * time sharing core. However, the process did * already return from the system call, when dispatching * the user code to the AC. The only thing left is to * return. The user registers should be ok, because * up->dbgreg has been the user context for the process. */ return; } poperror(); } else{ /* failure: save the error buffer for errstr */ e = up->syserrstr; up->syserrstr = up->errstr; up->errstr = e; if(DBGFLG && up->pid == 1) iprint("%s: syscall %s error %s\n", up->text, systab[scallnr].n, up->syserrstr); ar0 = systab[scallnr].r; } /* * NIX: for the execac() syscall, what follows is done within * the system call, because it never returns. * See acore.c:/^retfromsyscall */ noerrorsleft(); /* * Put return value in frame. */ ureg->ax = ar0.p; if (printallsyscalls) { stopns = todget(nil); sysretfmt(scallnr, &ar0, startns, stopns, a0, a1, a2, a3, a4, a5); if(up->syscalltrace) { print("X %s\n", up->syscalltrace); free(up->syscalltrace); up->syscalltrace = nil; } } if(up->procctl == Proc_tracesyscall){ stopns = todget(nil); up->procctl = Proc_stopme; sysretfmt(scallnr, &ar0, startns, stopns, a0, a1, a2, a3, a4, a5); s = splhi(); procctl(up); splx(s); if(up->syscalltrace) free(up->syscalltrace); up->syscalltrace = nil; }else if(up->procctl == Proc_totc || up->procctl == Proc_toac) procctl(up); if (0) hi("past sysretfmt\n"); up->insyscall = 0; up->psstate = 0; if(scallnr == NOTED) noted(ureg, a0); if (0) hi("now to splhi\n"); splhi(); if(scallnr != RFORK && (up->procctl || up->nnote)) notify(ureg); /* if we delayed sched because we held a lock, sched now */ if(up->delaysched){ sched(); splhi(); } kexit(ureg); if (0) hi("done kexit\n"); }
static int32_t vgaread(Chan* c, void* a, int32_t n, int64_t off) { Proc *up = externup(); int len; char *p, *s; VGAscr *scr; uint32_t offset = off; char chbuf[30]; switch((uint32_t)c->qid.path){ case Qdir: return devdirread(c, a, n, vgadir, nelem(vgadir), devgen); case Qvgabios: if(offset >= 0x100000) return 0; if(offset+n >= 0x100000) n = 0x100000 - offset; memmove(a, (unsigned char*)KADDR(0)+offset, n); return n; case Qvgactl: scr = &vgascreen[0]; p = malloc(READSTR); if(p == nil) error(Enomem); if(waserror()){ free(p); nexterror(); } len = 0; if(scr->dev) s = scr->dev->name; else s = "cga"; len += snprint(p+len, READSTR-len, "type %s\n", s); if(scr->gscreen) { len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n", scr->gscreen->r.max.x, scr->gscreen->r.max.y, scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan)); if(Dx(scr->gscreen->r) != Dx(physgscreenr) || Dy(scr->gscreen->r) != Dy(physgscreenr)) len += snprint(p+len, READSTR-len, "actualsize %dx%d\n", physgscreenr.max.x, physgscreenr.max.y); } len += snprint(p+len, READSTR-len, "blank time %lu idle %d state %s\n", blanktime, drawidletime(), scr->isblank ? "off" : "on"); len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off"); len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off"); len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off"); len += snprint(p+len, READSTR-len, "addr p 0x%lx v 0x%p size 0x%x\n", scr->paddr, scr->vaddr, scr->apsize); USED(len); n = readstr(offset, a, n, p); poperror(); free(p); return n; case Qvgaovl: case Qvgaovlctl: error(Ebadusefd); break; default: error(Egreg); break; } return 0; }
int screensize(int x, int y, int z, uint32_t chan) { Proc *up = externup(); VGAscr *scr; void *oldsoft; lock(&vgascreenlock); if(waserror()){ unlock(&vgascreenlock); nexterror(); } memimageinit(); scr = &vgascreen[0]; oldsoft = softscreen; if(scr->paddr == 0){ int width = (x*z)/BI2WD; void *p; p = malloc(width*BY2WD*y); if(p == nil) error("no memory for vga soft screen"); gscreendata.bdata = softscreen = p; if(scr->dev && scr->dev->page){ scr->vaddr = KADDR(VGAMEM()); scr->apsize = 1<<16; } scr->useflush = 1; } else{ gscreendata.bdata = scr->vaddr; scr->useflush = scr->dev && scr->dev->flush; } scr->gscreen = nil; if(gscreen) freememimage(gscreen); gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata); if(gscreen == nil) error("no memory for vga memimage"); vgaimageinit(chan); scr->palettedepth = 6; /* default */ scr->gscreendata = &gscreendata; scr->memdefont = getmemdefont(); scr->gscreen = gscreen; physgscreenr = gscreen->r; unlock(&vgascreenlock); poperror(); if(oldsoft) free(oldsoft); memimagedraw(gscreen, gscreen->r, memblack, ZP, nil, ZP, S); flushmemscreen(gscreen->r); if(didswcursorinit) swcursorinit(); drawcmap(); return 0; }
static int32_t consread(Chan *c, void *buf, int32_t n, int64_t off) { Proc *up = externup(); uint32_t l; Mach *mp; char *b, *bp, *s, *e; char tmp[512]; /* Qswap is 381 bytes at clu */ int i, k, id; int32_t offset; if(n <= 0) return n; offset = off; switch((uint32_t)c->qid.path){ case Qdir: return devdirread(c, buf, n, consdir, nelem(consdir), devgen); case Qcons: error(Egreg); case Qcputime: k = offset; if(k >= 6*NUMSIZE) return 0; if(k+n > 6*NUMSIZE) n = 6*NUMSIZE - k; /* easiest to format in a separate buffer and copy out */ for(i=0; i<6 && NUMSIZE*i<k+n; i++){ l = up->time[i]; if(i == TReal) l = sys->ticks - l; l = TK2MS(l); readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE); } memmove(buf, tmp+k, n); return n; case Qkmesg: /* * This is unlocked to avoid tying up a process * that's writing to the buffer. kmesg.n never * gets smaller, so worst case the reader will * see a slurred buffer. */ if(off >= kmesg.n) n = 0; else{ if(off+n > kmesg.n) n = kmesg.n - off; memmove(buf, kmesg.buf+off, n); } return n; case Qkprint: error(Egreg); case Qpgrpid: return readnum(offset, buf, n, up->pgrp->pgrpid, NUMSIZE); case Qpid: return readnum(offset, buf, n, up->pid, NUMSIZE); case Qppid: return readnum(offset, buf, n, up->parentpid, NUMSIZE); case Qtime: return readtime(offset, buf, n); case Qbintime: return readbintime(buf, n); case Qhostowner: return readstr(offset, buf, n, eve); case Qhostdomain: return readstr(offset, buf, n, hostdomain); case Quser: return readstr(offset, buf, n, up->user); case Qnull: return 0; case Qsysstat: n = MACHMAX*(NUMSIZE*11+2+1); b = smalloc(n + 1); /* +1 for NUL */ bp = b; e = bp + n; for(id = 0; id < MACHMAX; id++) if((mp = sys->machptr[id]) != nil && mp->online){ readnum(0, bp, NUMSIZE, mp->machno, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->cs, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, sys->load, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, (mp->perf.avg_inidle*100)/mp->perf.period, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, (mp->perf.avg_inintr*100)/mp->perf.period, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, 0, NUMSIZE); /* sched # */ bp += NUMSIZE; bp = strecpy(bp, e, rolename[mp->nixtype]); *bp++ = '\n'; } if(waserror()){ free(b); nexterror(); } n = readstr(offset, buf, n, b); free(b); poperror(); return n; case Qswap: tmp[0] = 0; s = seprintpagestats(tmp, tmp + sizeof tmp); s = seprintphysstats(s, tmp + sizeof tmp); b = buf; l = s - tmp; i = readstr(offset, b, l, tmp); b += i; n -= i; if(offset > l) offset -= l; else offset = 0; return i + mallocreadsummary(c, b, n, offset); case Qsysname: if(sysname == nil) return 0; return readstr(offset, buf, n, sysname); case Qrandom: return randomread(buf, n); case Qurandom: return urandomread(buf, n); case Qdrivers: return devtabread(c, buf, n, off); case Qzero: memset(buf, 0, n); return n; case Qosversion: snprint(tmp, sizeof tmp, "2000"); n = readstr(offset, buf, n, tmp); return n; case Qdebug: s = seprint(tmp, tmp + sizeof tmp, "locks %uld\n", lockstats.locks); s = seprint(s, tmp + sizeof tmp, "glare %uld\n", lockstats.glare); s = seprint(s, tmp + sizeof tmp, "inglare %uld\n", lockstats.inglare); s = seprint(s, tmp + sizeof tmp, "qlock %uld\n", qlockstats.qlock); seprint(s, tmp + sizeof tmp, "qlockq %uld\n", qlockstats.qlockq); return readstr(offset, buf, n, tmp); break; default: print("consread %#llux\n", c->qid.path); error(Egreg); } return -1; /* never reached */ }
char * edfadmit(Proc *p) { Proc *up = externup(); char *err; Edf *e; int i; Proc *r; int32_t tns; e = p->edf; if (e->flags & Admitted) return "task state"; /* should never happen */ /* simple sanity checks */ if (e->T == 0) return "T not set"; if (e->C == 0) return "C not set"; if (e->D > e->T) return "D > T"; if (e->D == 0) /* if D is not set, set it to T */ e->D = e->T; if (e->C > e->D) return "C > D"; qlock(&edfschedlock); if (err = testschedulability(p)){ qunlock(&edfschedlock); return err; } e->flags |= Admitted; edflock(p); if(p->trace) proctrace(p, SAdmit, 0); /* Look for another proc with the same period to synchronize to */ for(i=0; (r = psincref(i)) != nil; i++) { if(r->state == Dead || r == p){ psdecref(r); continue; } if (r->edf == nil || (r->edf->flags & Admitted) == 0){ psdecref(r); continue; } if (r->edf->T == e->T) break; } if (r == nil){ /* Can't synchronize to another proc, release now */ e->t = now; e->d = 0; release(p); if (p == up){ DPRINT("%lu edfadmit self %d[%s], release now: r=%lu d=%lu t=%lu\n", now, p->pid, statename[p->state], e->r, e->d, e->t); /* We're already running */ edfrun(p, 1); }else{ /* We're releasing another proc */ DPRINT("%lu edfadmit other %d[%s], release now: r=%lu d=%lu t=%lu\n", now, p->pid, statename[p->state], e->r, e->d, e->t); p->Timer.ta = p; edfunlock(); qunlock(&edfschedlock); releaseintr(nil, &p->Timer); return nil; } }else{ /* Release in synch to something else */ e->t = r->edf->t; psdecref(r); if (p == up){ DPRINT("%lu edfadmit self %d[%s], release at %lu\n", now, p->pid, statename[p->state], e->t); }else{ DPRINT("%lu edfadmit other %d[%s], release at %lu\n", now, p->pid, statename[p->state], e->t); if(e->Timer.tt == nil){ e->Timer.tf = releaseintr; e->Timer.ta = p; tns = e->t - now; if(tns < 20) tns = 20; e->Timer.tns = 1000LL * tns; e->Timer.tmode = Trelative; timeradd(&e->Timer); } } } edfunlock(); qunlock(&edfschedlock); return nil; }
static int32_t conswrite(Chan *c, void *va, int32_t n, int64_t off) { Proc *up = externup(); char buf[256]; int32_t l, bp; char *a; Mach *mp; int i; uint32_t offset; Cmdbuf *cb; Cmdtab *ct; a = va; offset = off; extern int printallsyscalls; switch((uint32_t)c->qid.path){ case Qcons: /* * Can't page fault in putstrn, so copy the data locally. */ l = n; while(l > 0){ bp = l; if(bp > sizeof buf) bp = sizeof buf; memmove(buf, a, bp); putstrn0(buf, bp, 1); a += bp; l -= bp; } break; case Qconsctl: print("consctl\n"); if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, a, n); buf[n] = 0; for(a = buf; a;){ if(strncmp(a, "sys", 3) == 0) { printallsyscalls = ! printallsyscalls; print("%sracing syscalls\n", printallsyscalls ? "T" : "Not t"); } if(a = strchr(a, ' ')) a++; } break; case Qtime: if(!iseve()) error(Eperm); return writetime(a, n); case Qbintime: if(!iseve()) error(Eperm); return writebintime(a, n); case Qhostowner: return hostownerwrite(a, n); case Qhostdomain: return hostdomainwrite(a, n); case Quser: return userwrite(a, n); case Qnull: break; case Qreboot: if(!iseve()) error(Eperm); cb = parsecmd(a, n); if(waserror()) { free(cb); nexterror(); } ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg)); switch(ct->index) { case CMhalt: reboot(nil, 0, 0); break; case CMreboot: rebootcmd(cb->nf-1, cb->f+1); break; case CMpanic: *(uint32_t*)0=0; panic("/dev/reboot"); } poperror(); free(cb); break; case Qsysstat: for(i = 0; i < MACHMAX; i++) if((mp = sys->machptr[i]) != nil && mp->online){ mp = sys->machptr[i]; mp->cs = 0; mp->intr = 0; mp->syscall = 0; mp->pfault = 0; mp->tlbfault = 0; /* not updated */ mp->tlbpurge = 0; /* # mmuflushtlb */ } break; case Qswap: if(n >= sizeof buf) error(Egreg); memmove(buf, va, n); /* so we can NUL-terminate */ buf[n] = 0; if(!iseve()) error(Eperm); if(buf[0]<'0' || '9'<buf[0]) error(Ebadarg); if(strncmp(buf, "start", 5) == 0){ print("request to start pager ignored\n"); break; } break; case Qsysname: if(offset != 0) error(Ebadarg); if(n <= 0 || n >= sizeof buf) error(Ebadarg); strncpy(buf, a, n); buf[n] = 0; if(buf[n-1] == '\n') buf[n-1] = 0; kstrdup(&sysname, buf); break; case Qdebug: if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, a, n); buf[n] = 0; if(n > 0 && buf[n-1] == '\n') buf[n-1] = 0; error(Ebadctl); break; default: print("conswrite: %#llux\n", c->qid.path); error(Egreg); } return n; }
/* if dc is non-zero, it means we're doing a mount and dc is the mount device to use. */ static int bindmount(int dc, int fd, int afd, char* arg0, char* arg1, int flag, char* spec) { Proc *up = externup(); int i; Dev *dev; Chan *c0, *c1, *ac, *bc; struct{ Chan *chan; Chan *authchan; char *spec; int flags; }bogus; if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER)) error(Ebadarg); bogus.flags = flag & MCACHE; if(dc){ if (! checkdc(dc)) error(Ebadarg); if(up->pgrp->noattach) error(Enoattach); ac = nil; bc = fdtochan(fd, ORDWR, 0, 1); if(waserror()) { if(ac) cclose(ac); cclose(bc); nexterror(); } if(afd >= 0) ac = fdtochan(afd, ORDWR, 0, 1); bogus.chan = bc; bogus.authchan = ac; bogus.spec = validaddr(spec, 1, 0); if(waserror()) error(Ebadspec); spec = validnamedup(spec, 1); poperror(); if(waserror()){ free(spec); nexterror(); } dev = devtabget(dc, 0); //XDYNX if(waserror()){ //devtabdecr(dev); nexterror(); } c0 = dev->attach((char*)&bogus); poperror(); //devtabdecr(dev); poperror(); /* spec */ free(spec); poperror(); /* ac bc */ if(ac) cclose(ac); cclose(bc); }else{ bogus.spec = nil; c0 = namec(validaddr(arg0, 1, 0), Abind, 0, 0); } if(waserror()){ cclose(c0); nexterror(); } c1 = namec(validaddr(arg1, 1, 0), Amount, 0, 0); if(waserror()){ cclose(c1); nexterror(); } i = cmount(&c0, c1, flag, bogus.spec); poperror(); cclose(c1); poperror(); cclose(c0); if(dc) fdclose(fd, 0); return i; }
static int tfn(void *arg) { Proc *up = externup(); return up->trend == nil || up->tfn(arg); }