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"); }
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: *(volatile 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; case Qsyscall: 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; // Doing strncmp right is just painful and overkill here. if (buf[0] == 'o') { if (buf[1] == 'n' && buf[2] == 0) { printallsyscalls = 1; break; } if (buf[1] == 'f' && buf[2] == 'f' && buf[3] == 0) { printallsyscalls = 0; break; } } error("#c/syscall: can only write on or off"); break; default: print("conswrite: %#llx\n", c->qid.path); error(Egreg); } return n; }
/* * the devxxx.c that calls us handles writing data, it knows best */ int32_t netifwrite(Netif *nif, Chan *c, void *a, int32_t n) { Mach *m = machp(); 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; }
/* * the devxxx.c that calls us handles writing data, it knows best */ long netifwrite(struct ether *nif, struct chan *c, void *a, long n) { ERRSTACK(1); struct netfile *f; int type; 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->qlock); nexterror(); } qlock(&nif->qlock); f = nif->f[NETID(c->qid.path)]; if ((p = matchtoken(buf, "connect")) != 0) { type = strtol(p, 0, 0); /* allows any base, though usually hex */ if (typeinuse(nif, type)) error(Einuse); f->type = type; if (f->type < 0) nif->all++; } else if (matchtoken(buf, "promiscuous")) { if (f->prom == 0) { if (nif->prom == 0 && nif->promiscuous != NULL) 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 = strtol(p, 0, 0); /* allows any base, though usually hex */ if (type < 5) type = 5; if (nif->scanbs != NULL) nif->scanbs(nif->arg, type); f->scan = type; nif->scan++; } } 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->qlock); poperror(); return n; }
static long conswrite(Chan *c, void *va, long n, vlong offset) { char buf[128], *a, ch; int x; if(c->qid.type & QTDIR) error(Eperm); switch((ulong)c->qid.path) { default: error(Egreg); case Qcons: if(canrlock(&kprintq.l)){ if(kprintq.q != nil){ if(waserror()){ runlock(&kprintq.l); nexterror(); } qwrite(kprintq.q, va, n); poperror(); runlock(&kprintq.l); return n; } runlock(&kprintq.l); } return write(1, va, n); case Qsysctl: return sysconwrite(va, n); case Qconsctl: if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, va, n); buf[n] = 0; for(a = buf; a;){ if(strncmp(a, "rawon", 5) == 0){ kbd.raw = 1; /* clumsy hack - wake up reader */ ch = 0; qwrite(kbdq, &ch, 1); } else if(strncmp(buf, "rawoff", 6) == 0){ kbd.raw = 0; } if((a = strchr(a, ' ')) != nil) a++; } break; case Qkeyboard: for(x=0; x<n; ) { Rune r; x += chartorune(&r, &((char*)va)[x]); gkbdputc(gkbdq, r); } break; case Qnull: break; case Qtime: if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, va, n); buf[n] = '\0'; timeoffset = strtoll(buf, 0, 0)-osusectime(); break; case Qhostowner: if(!iseve()) error(Eperm); if(offset != 0 || n >= sizeof(buf)) error(Ebadarg); memmove(buf, va, n); buf[n] = '\0'; if(n > 0 && buf[n-1] == '\n') buf[--n] = '\0'; if(n == 0) error(Ebadarg); /* renameuser(eve, buf); */ /* renameproguser(eve, buf); */ kstrdup(&eve, buf); kstrdup(&up->env->user, buf); break; case Quser: if(!iseve()) error(Eperm); if(offset != 0) error(Ebadarg); if(n <= 0 || n >= sizeof(buf)) error(Ebadarg); strncpy(buf, va, n); buf[n] = '\0'; if(n > 0 && buf[n-1] == '\n') buf[--n] = '\0'; if(n == 0) error(Ebadarg); setid(buf, 0); break; case Qhoststdout: return write(1, va, n); case Qhoststderr: return write(2, va, n); case Qjit: if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, va, n); buf[n] = '\0'; x = atoi(buf); if(x < 0 || x > 9) error(Ebadarg); cflag = x; break; case Qsysname: if(offset != 0) error(Ebadarg); if(n < 0 || n >= sizeof(buf)) error(Ebadarg); strncpy(buf, va, n); buf[n] = '\0'; if(buf[n-1] == '\n') buf[n-1] = 0; kstrdup(&ossysname, buf); break; } 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 static int lastvno; vno = ureg->type; uint64_t gsbase = rdmsr(GSbase); //if (sce > scx) iprint("===================="); if (vno == 8) { iprint("Lstar is %p\n", (void *)rdmsr(Lstar)); iprint("GSbase is %p\n", (void *)gsbase); iprint("ire %d irx %d sce %d scx %d lastvno %d\n", ire, irx, sce, scx, lastvno); iprint("irxe %d \n", irxe); die("8"); } lastvno = vno; if (gsbase < 1ULL<<63) die("bogus gsbase"); Mach *m = machp(); char buf[ERRMAX]; Vctl *ctl, *v; if (0 && m && m->externup && m->externup->pid == 6) { //iprint("type %x\n", ureg->type); if (ureg->type != 0x49) die("6\n"); } m->perf.intrts = perfticks(); user = userureg(ureg); if(user && (m->nixtype == NIXTC)){ m->externup->dbgreg = ureg; cycles(&m->externup->kentry); } clockintr = 0; //_pmcupdate(m); if(ctl = vctl[vno]){ if(ctl->isintr){ m->intr++; if(vno >= VectorPIC && vno != VectorSYSCALL) m->lastintr = ctl->irq; }else if(m->externup) m->externup->nqtrap++; if(ctl->isr) ctl->isr(vno); for(v = ctl; v != nil; v = v->next){ if(v->f) v->f(ureg, v->a); } if(ctl->eoi) ctl->eoi(vno); intrtime(vno); if(ctl->isintr){ if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER) clockintr = 1; if(m->externup && !clockintr) preempted(); } } else if(vno < nelem(excname) && user){ spllo(); snprint(buf, sizeof buf, "sys: trap: %s", excname[vno]); postnote(m->externup, 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", m->machno, vno, m->lastintr); intrtime(vno); if(user) kexit(ureg); return; } else{ if(vno == VectorNMI){ nmienable(); if(m->machno != 0){ iprint("cpu%d: PC %#llux\n", m->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(m->externup && m->externup->delaysched && clockintr){ if(0) if(user && m->externup->ac == nil && m->externup->nqtrap == 0 && m->externup->nqsyscall == 0){ if(!waserror()){ m->externup->ac = getac(m->externup, -1); poperror(); runacore(); return; } } sched(); splhi(); } if(user){ if(m->externup && m->externup->procctl || m->externup->nnote) notify(ureg); kexit(ureg); } }
static long loopbackwrite(Chan *c, void *va, long n, vlong off) { Loop *lb; Link *link; Cmdbuf *volatile cb; Block *volatile bp; vlong d0, d0ns; long dn, dnns; switch(TYPE(c->qid.path)){ case Qdata: bp = allocb(n); if(waserror()){ freeb(bp); nexterror(); } memmove(bp->wp, va, n); poperror(); bp->wp += n; return loopbackbwrite(c, bp, off); case Qctl: lb = c->aux; link = &lb->link[ID(c->qid.path)]; cb = parsecmd(va, n); if(waserror()){ free(cb); nexterror(); } if(cb->nf < 1) error("short control request"); if(strcmp(cb->f[0], "delay") == 0){ if(cb->nf != 3) error("usage: delay latency bytedelay"); d0ns = strtoll(cb->f[1], nil, 10); dnns = strtol(cb->f[2], nil, 10); /* * it takes about 20000 cycles on a pentium ii * to run pushlink; perhaps this should be accounted. */ ilock(link); link->delay0ns = d0ns; link->delaynns = dnns; iunlock(link); }else if(strcmp(cb->f[0], "indrop") == 0){ if(cb->nf != 2) error("usage: indrop [01]"); ilock(link); link->indrop = strtol(cb->f[1], nil, 0) != 0; iunlock(link); }else if(strcmp(cb->f[0], "droprate") == 0){ if(cb->nf != 2) error("usage: droprate ofn"); ilock(link); link->droprate = strtol(cb->f[1], nil, 0); iunlock(link); }else if(strcmp(cb->f[0], "limit") == 0){ if(cb->nf != 2) error("usage: limit maxqsize"); ilock(link); link->limit = strtol(cb->f[1], nil, 0); qsetlimit(link->oq, link->limit); qsetlimit(link->iq, link->limit); iunlock(link); }else if(strcmp(cb->f[0], "reset") == 0){ if(cb->nf != 1) error("usage: reset"); ilock(link); link->packets = 0; link->bytes = 0; link->indrop = 0; link->soverflows = 0; link->drops = 0; iunlock(link); }else error("unknown control request"); poperror(); free(cb); break; default: error(Eperm); } return n; }
long bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, ulong flag, char* spec) { int ret; Chan *c0, *c1, *ac, *bc; struct{ Chan *chan; Chan *authchan; char *spec; int flags; }bogus; if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER)) error(Ebadarg); if(ismount){ validaddr((ulong)spec, 1, 0); spec = validnamedup(spec, 1); if(waserror()){ free(spec); nexterror(); } 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.flags = flag & MCACHE; bogus.chan = bc; bogus.authchan = ac; bogus.spec = spec; ret = devno('M', 0); c0 = devtab[ret]->attach((char*)&bogus); poperror(); /* ac bc */ if(ac) cclose(ac); cclose(bc); }else{ spec = 0; validaddr((ulong)arg0, 1, 0); c0 = namec(arg0, Abind, 0, 0); } if(waserror()){ cclose(c0); nexterror(); } validaddr((ulong)arg1, 1, 0); c1 = namec(arg1, Amount, 0, 0); if(waserror()){ cclose(c1); nexterror(); } ret = cmount(&c0, c1, flag, spec); poperror(); cclose(c1); poperror(); cclose(c0); if(ismount){ fdclose(fd, 0); poperror(); free(spec); } return ret; }
/* 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 long read(ulong *arg, vlong *offp) { long n, nn, nnn; uchar *p; Chan *c; vlong off; n = arg[2]; validaddr(arg[1], n, 1); p = (void*)arg[1]; c = fdtochan(arg[0], OREAD, 1, 1); if(waserror()){ cclose(c); nexterror(); } /* * The offset is passed through on directories, normally. * Sysseek complains, but pread is used by servers like exportfs, * that shouldn't need to worry about this issue. * * Notice that c->devoffset is the offset that c's dev is seeing. * The number of bytes read on this fd (c->offset) may be different * due to rewritings in rockfix. */ if(offp == nil) /* use and maintain channel's offset */ off = c->offset; else off = *offp; if(off < 0) error(Enegoff); if(off == 0){ /* rewind to the beginning of the directory */ if(offp == nil){ c->offset = 0; c->devoffset = 0; } mountrewind(c); unionrewind(c); } if(c->qid.type & QTDIR){ if(mountrockread(c, p, n, &nn)){ /* do nothing: mountrockread filled buffer */ }else if(c->umh) nn = unionread(c, p, n); else{ if(off != c->offset) error(Edirseek); nn = devtab[c->type]->read(c, p, n, c->devoffset); } nnn = mountfix(c, p, nn, n); }else nnn = nn = devtab[c->type]->read(c, p, n, off); lock(c); c->devoffset += nn; c->offset += nnn; unlock(c); poperror(); cclose(c); return nnn; }
static void sseek(ulong *arg) { Chan *c; uchar buf[sizeof(Dir)+100]; Dir dir; int n; vlong off; union { vlong v; ulong u[2]; } o; c = fdtochan(arg[1], -1, 1, 1); if(waserror()){ cclose(c); nexterror(); } if(devtab[c->type]->dc == '|') error(Eisstream); off = 0; o.u[0] = arg[2]; o.u[1] = arg[3]; switch(arg[4]){ case 0: off = o.v; if((c->qid.type & QTDIR) && off != 0) error(Eisdir); if(off < 0) error(Enegoff); c->offset = off; break; case 1: if(c->qid.type & QTDIR) error(Eisdir); lock(c); /* lock for read/write update */ off = o.v + c->offset; if(off < 0){ unlock(c); error(Enegoff); } c->offset = off; unlock(c); break; case 2: if(c->qid.type & QTDIR) error(Eisdir); n = devtab[c->type]->stat(c, buf, sizeof buf); if(convM2D(buf, n, &dir, nil) == 0) error("internal error: stat error in seek"); off = dir.length + o.v; if(off < 0) error(Enegoff); c->offset = off; break; default: error(Ebadarg); } *(vlong*)arg[0] = off; c->uri = 0; c->dri = 0; cclose(c); poperror(); }
/* * Rewrite the results of a directory read to reflect current * name space bindings and mounts. Specifically, replace * directory entries for bind and mount points with the results * of statting what is mounted there. Except leave the old names. */ static long mountfix(Chan *c, uchar *op, long n, long maxn) { char *name; int nbuf, nname; Chan *nc; Mhead *mh; Mount *m; uchar *p; int dirlen, rest; long l; uchar *buf, *e; Dir d; p = op; buf = nil; nbuf = 0; for(e=&p[n]; p+BIT16SZ<e; p+=dirlen){ dirlen = dirfixed(p, e, &d); if(dirlen < 0) break; nc = nil; mh = nil; if(findmount(&nc, &mh, d.type, d.dev, d.qid)){ /* * If it's a union directory and the original is * in the union, don't rewrite anything. */ for(m=mh->mount; m; m=m->next) if(eqchantdqid(m->to, d.type, d.dev, d.qid, 1)) goto Norewrite; name = dirname(p, &nname); /* * Do the stat but fix the name. If it fails, leave old entry. * BUG: If it fails because there isn't room for the entry, * what can we do? Nothing, really. Might as well skip it. */ if(buf == nil){ buf = smalloc(4096); nbuf = 4096; } if(waserror()) goto Norewrite; l = devtab[nc->type]->stat(nc, buf, nbuf); l = dirsetname(name, nname, buf, l, nbuf); if(l == BIT16SZ) error("dirsetname"); poperror(); /* * Shift data in buffer to accomodate new entry, * possibly overflowing into rock. */ rest = e - (p+dirlen); if(l > dirlen){ while(p+l+rest > op+maxn){ mountrock(c, p, &e); if(e == p){ dirlen = 0; goto Norewrite; } rest = e - (p+dirlen); } } if(l != dirlen){ memmove(p+l, p+dirlen, rest); dirlen = l; e = p+dirlen+rest; } /* * Rewrite directory entry. */ memmove(p, buf, l); Norewrite: cclose(nc); putmhead(mh); } } if(buf) free(buf); if(p != e) error("oops in rockfix"); return e-op; }
/* * 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); }
void trap(Ureg *ureg) { int rem, itype, t; if(up != nil) rem = ((char*)ureg)-up->kstack; else rem = ((char*)ureg)-(char*)m->stack; if(ureg->type != PsrMfiq && rem < 256) { dumpregs(ureg); panic("trap %d stack bytes remaining (%s), " "up=#%8.8lux ureg=#%8.8lux pc=#%8.8ux" ,rem, up?up->text:"", up, ureg, ureg->pc); for(;;); } itype = ureg->type; /* All interrupts/exceptions should be resumed at ureg->pc-4, except for Data Abort which resumes at ureg->pc-8. */ if(itype == PsrMabt+1) ureg->pc -= 8; else ureg->pc -= 4; if(up){ up->pc = ureg->pc; up->dbgreg = ureg; } switch(itype) { case PsrMirq: t = m->ticks; /* CPU time per proc */ up = nil; /* no process at interrupt level */ irq(ureg); up = m->proc; preemption(m->ticks - t); m->intr++; break; case PsrMund: if(*(ulong*)ureg->pc == BREAK && breakhandler) { int s; Proc *p; p = up; s = breakhandler(ureg, p); if(s == BrkSched) { p->preempted = 0; sched(); } else if(s == BrkNoSched) { /* stop it being preempted until next instruction */ p->preempted = 1; if(up) up->dbgreg = 0; return; } break; } if(up == nil) goto faultpanic; spllo(); if(waserror()) { if(waslo(ureg->psr) && up->type == Interp) disfault(ureg, up->env->errstr); setpanic(); dumpregs(ureg); panic("%s", up->env->errstr); } if(!fpiarm(ureg)) { dumpregs(ureg); sys_trap_error(ureg->type); } poperror(); break; case PsrMsvc: /* Jump through 0 or SWI */ if(waslo(ureg->psr) && up && up->type == Interp) { spllo(); dumpregs(ureg); sys_trap_error(ureg->type); } setpanic(); dumpregs(ureg); panic("SVC/SWI exception"); break; case PsrMabt: /* Prefetch abort */ if(catchdbg && catchdbg(ureg, 0)) break; /* FALL THROUGH */ case PsrMabt+1: /* Data abort */ if(waslo(ureg->psr) && up && up->type == Interp) { spllo(); faultarm(ureg); } print("Data Abort\n"); /* FALL THROUGH */ default: faultpanic: setpanic(); dumpregs(ureg); panic("exception %uX %s\n", ureg->type, trapname(ureg->type)); break; } splhi(); if(up) up->dbgreg = 0; /* becomes invalid after return from trap */ }
/* * Print a string on the console. Convert \n to \r\n for serial * line consoles. Locking of the queues is left up to the screen * or uart code. Multi-line messages to serial consoles may get * interspersed with other messages. */ static void putstrn0(char *str, int n, int usewrite) { int m; char *t; char buf[PRINTSIZE+2]; /* * if kprint is open, put the message there, otherwise * if there's an attached bit mapped display, * put the message there. */ m = consoleprint; if(canrlock(&kprintq)){ if(kprintq.q != nil){ if(waserror()){ runlock(&kprintq); nexterror(); } if(usewrite) qwrite(kprintq.q, str, n); else qiwrite(kprintq.q, str, n); poperror(); m = 0; } runlock(&kprintq); } if(m && screenputs != nil) screenputs(str, n); /* * if there's a serial line being used as a console, * put the message there. */ if(serwrite != nil) { serwrite(str, n); return; } if(printq == 0) return; while(n > 0) { t = memchr(str, '\n', n); if(t && !kbd.raw) { m = t - str; if(m > sizeof(buf)-2) m = sizeof(buf)-2; memmove(buf, str, m); buf[m] = '\r'; buf[m+1] = '\n'; if(usewrite) qwrite(printq, buf, m+2); else qiwrite(printq, buf, m+2); str = t + 1; n -= m + 1; } else { if(usewrite) qwrite(printq, str, n); else qiwrite(printq, str, n); break; } } }
/* * Rewrite the results of a directory read to reflect current * name space bindings and mounts. Specifically, replace * directory entries for bind and mount points with the results * of statting what is mounted there. Except leave the old names. */ static int32_t mountfix(Chan *c, uint8_t *op, int32_t n, int32_t maxn) { Proc *up = externup(); char *name; int nbuf; Chan *nc; Mhead *mh; Mount *mount; usize dirlen, nname, r, rest; int32_t l; uint8_t *buf, *e, *p; Dir d; p = op; buf = nil; nbuf = 0; for(e=&p[n]; p+BIT16SZ<e; p+=dirlen){ dirlen = dirfixed(p, e, &d); if(dirlen == 0) break; nc = nil; mh = nil; if(findmount(&nc, &mh, d.type, d.dev, d.qid)){ /* * If it's a union directory and the original is * in the union, don't rewrite anything. */ for(mount=mh->mount; mount; mount=mount->next) if(eqchanddq(mount->to, d.type, d.dev, d.qid, 1)) goto Norewrite; name = dirname(p, &nname); /* * Do the stat but fix the name. If it fails, * leave old entry. * BUG: If it fails because there isn't room for * the entry, what can we do? Nothing, really. * Might as well skip it. */ if(buf == nil){ buf = smalloc(4096); nbuf = 4096; } if(waserror()) goto Norewrite; l = nc->dev->stat(nc, buf, nbuf); r = dirsetname(name, nname, buf, l, nbuf); if(r == BIT16SZ) error("dirsetname"); poperror(); /* * Shift data in buffer to accomodate new entry, * possibly overflowing into rock. */ rest = e - (p+dirlen); if(r > dirlen){ while(p+r+rest > op+maxn){ mountrock(c, p, &e); if(e == p){ dirlen = 0; goto Norewrite; } rest = e - (p+dirlen); } } if(r != dirlen){ memmove(p+r, p+dirlen, rest); dirlen = r; e = p+dirlen+rest; } /* * Rewrite directory entry. */ memmove(p, buf, r); Norewrite: cclose(nc); putmhead(mh); } } if(buf) free(buf); if(p != e) error("oops in mountfix"); return e-op; }
static long consread(Chan *c, void *buf, long n, vlong offset) { int l; Osenv *o; int ch, eol, i; char *p, tmp[128]; char *cbuf = buf; if(n <= 0) return n; o = up->env; switch((ulong)c->qid.path){ case Qdir: return devdirread(c, buf, n, consdir, nelem(consdir), devgen); case Qsysctl: return readstr(offset, buf, n, VERSION); case Qcons: case Qkeyboard: qlock(&kbd); if(waserror()) { qunlock(&kbd); nexterror(); } if(kbd.raw || kbd.kbdr) { if(qcanread(lineq)) n = qread(lineq, buf, n); else { /* read as much as possible */ do { i = qread(kbdq, cbuf, n); cbuf += i; n -= i; } while(n>0 && qcanread(kbdq)); n = cbuf - (char*)buf; } } else { while(!qcanread(lineq)) { qread(kbdq, &kbd.line[kbd.x], 1); ch = kbd.line[kbd.x]; eol = 0; switch(ch){ case '\b': if(kbd.x) kbd.x--; break; case 0x15: kbd.x = 0; break; case '\n': case 0x04: eol = 1; default: kbd.line[kbd.x++] = ch; break; } if(kbd.x == sizeof(kbd.line) || eol) { if(ch == 0x04) kbd.x--; qwrite(lineq, kbd.line, kbd.x); kbd.x = 0; } } n = qread(lineq, buf, n); } qunlock(&kbd); poperror(); return n; case Qscancode: if(offset == 0) return readstr(0, buf, n, kscanid); else return qread(kscanq, buf, n); case Qtime: snprint(tmp, sizeof(tmp), "%.lld", (vlong)mseconds()*1000); return readstr(offset, buf, n, tmp); case Qhostowner: return readstr(offset, buf, n, eve); case Quser: return readstr(offset, buf, n, o->user); case Qjit: snprint(tmp, sizeof(tmp), "%d", cflag); return readstr(offset, buf, n, tmp); case Qnull: return 0; case Qmsec: return readnum(offset, buf, n, TK2MS(MACHP(0)->ticks), NUMSIZE); case Qsysname: if(sysname == nil) return 0; return readstr(offset, buf, n, sysname); case Qnotquiterandom: genrandom(buf, n); return n; case Qrandom: return randomread(buf, n); case Qmemory: return poolread(buf, n, offset); case Qdrivers: p = malloc(READSTR); if(p == nil) error(Enomem); l = 0; for(i = 0; devtab[i] != nil; i++) l += snprint(p+l, READSTR-l, "#%C %s\n", devtab[i]->dc, devtab[i]->name); if(waserror()){ free(p); nexterror(); } n = readstr(offset, buf, n, p); free(p); poperror(); return n; case Qklog: return qread(klogq, buf, n); case Qkprint: rlock(&kprintq); if(waserror()){ runlock(&kprintq); nexterror(); } n = qread(kprintq.q, buf, n); poperror(); runlock(&kprintq); return n; default: print("consread %llud\n", c->qid.path); error(Egreg); } return -1; /* never reached */ }
static int32_t read(int ispread, int fd, void *p, int32_t n, int64_t off) { Proc *up = externup(); int32_t nn, nnn; Chan *c; p = validaddr(p, n, 1); c = fdtochan(fd, OREAD, 1, 1); if(waserror()){ cclose(c); nexterror(); } /* * The offset is passed through on directories, normally. * Sysseek complains, but pread is used by servers like exportfs, * that shouldn't need to worry about this issue. * * Notice that c->devoffset is the offset that c's dev is seeing. * The number of bytes read on this fd (c->offset) may be different * due to rewritings in mountfix. */ if(ispread){ if(off == ~0LL){ /* use and maintain channel's offset */ off = c->offset; ispread = 0; } } else off = c->offset; if(c->qid.type & QTDIR){ /* * Directory read: * rewind to the beginning of the file if necessary; * try to fill the buffer via mountrockread; * clear ispread to always maintain the Chan offset. */ if(off == 0LL){ if(!ispread){ c->offset = 0; c->devoffset = 0; } mountrewind(c); unionrewind(c); } if(!mountrockread(c, p, n, &nn)){ if(c->umh) nn = unionread(c, p, n); else{ if(off != c->offset) error(Edirseek); nn = c->dev->read(c, p, n, c->devoffset); } } nnn = mountfix(c, p, nn, n); ispread = 0; } else nnn = nn = c->dev->read(c, p, n, off); if(!ispread){ lock(&c->r.l); c->devoffset += nn; c->offset += nnn; unlock(&c->r.l); } poperror(); cclose(c); return nnn; }
static Chan* loopbackattach(char *spec) { Loop *volatile lb; Queue *q; Chan *c; int chan; int dev; dev = 0; if(spec != nil){ dev = atoi(spec); if(dev >= Nloopbacks) error(Ebadspec); } c = devattach('X', spec); if(waserror()){ chanfree(c); nexterror(); } lb = &loopbacks[dev]; qlock(lb); if(waserror()){ lb->ref--; qunlock(lb); nexterror(); } lb->ref++; if(lb->ref == 1){ for(chan = 0; chan < 2; chan++){ lb->link[chan].ci.mode = Trelative; lb->link[chan].ci.a = &lb->link[chan]; lb->link[chan].ci.f = linkintr; lb->link[chan].limit = Loopqlim; q = qopen(lb->link[chan].limit, 0, 0, 0); lb->link[chan].iq = q; if(q == nil){ freelb(lb); exhausted("memory"); } q = qopen(lb->link[chan].limit, 0, 0, 0); lb->link[chan].oq = q; if(q == nil){ freelb(lb); exhausted("memory"); } lb->link[chan].indrop = 1; lb->link[chan].delaynns = Delayn; lb->link[chan].delay0ns = Delay0; } } poperror(); qunlock(lb); poperror(); mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); c->aux = lb; c->dev = dev; return c; }
static char* qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block) { uchar *d, *c; int pad; TXQ *q; assert(qid < nelem(ctlr->tx)); assert(size <= Tcmdsize-4); ilock(ctlr); q = &ctlr->tx[qid]; while(q->n >= Ntx && !ctlr->broken){ iunlock(ctlr); qlock(q); if(!waserror()){ tsleep(q, txqready, q, 10); poperror(); } qunlock(q); ilock(ctlr); } if(ctlr->broken){ iunlock(ctlr); return "qcmd: broken"; } q->n++; q->lastcmd = code; q->b[q->i] = block; c = q->c + q->i * Tcmdsize; d = q->d + q->i * Tdscsize; /* build command */ c[0] = code; c[1] = 0; /* flags */ c[2] = q->i; c[3] = qid; if(size > 0) memmove(c+4, data, size); size += 4; memset(d, 0, Tdscsize); pad = size - 4; if(block != nil) pad += BLEN(block); pad = ((pad + 3) & ~3) - pad; put32(d, (pad << 28) | ((1 + (block != nil)) << 24)), d += 4; put32(d, PCIWADDR(c)), d += 4; put32(d, size), d += 4; if(block != nil){ size = BLEN(block); put32(d, PCIWADDR(block->rp)), d += 4; put32(d, size), d += 4; } USED(d); coherence(); q->i = (q->i+1) % Ntx; csr32w(ctlr, HbusTargWptr, (qid<<8) | q->i); iunlock(ctlr); return nil; }
static long powerwrite(Chan* c, void *a, long n, vlong) { Cmdbuf *cmd; Puser *p; if(c->qid.type & QTDIR) error(Ebadusefd); cmd = parsecmd(a, n); if(waserror()){ free(cmd); nexterror(); } switch(c->qid.path & ~CHDIR){ case Qdata: p = c->aux; if(cmd->nf < 2) error(Ebadarg); if(strcmp(cmd->f[0], "ack") == 0){ if(strcmp(cmd->f[1], "power") == 0){ lock(p); if(p->state == Pwrack){ lock(&pwrusers.l); if(pwrusers.shutdown && pwrusers.nwaiting > 0) pwrusers.nwaiting--; unlock(&pwrusers.l); wakeup(&pwrusers.ackr); p->state = Pwridle; } unlock(p); }else error(Ebadarg); }else if(strcmp(cmd->f[0], "alarm") == 0){ /* set alarm */ }else error(Ebadarg); break; case Qctl: if(cmd->nf < 1) error(Ebadarg); if(strcmp(cmd->f[0], "suspend") == 0){ /* start the suspend action */ qlock(&pwrusers); //powersuspend(0); /* calls poweringdown, then archsuspend() */ qunlock(&pwrusers); }else if(strcmp(cmd->f[0], "shutdown") == 0){ /* go to it */ qlock(&pwrusers); if(waserror()){ lock(&pwrusers.l); pwrusers.shutdown = 0; /* hard luck for those already notified */ unlock(&pwrusers.l); qunlock(&pwrusers); nexterror(); } lock(&pwrusers.l); pwrusers.shutdown = 1; pwrusers.nwaiting = 0; unlock(&pwrusers.l); for(p = pwrusers.list; p != nil; p = p->next){ lock(p); if(p->state == Pwridle){ p->state = Pwroff; lock(&pwrusers.l); pwrusers.nwaiting++; unlock(&pwrusers.l); } unlock(p); wakeup(&p->r); /* putting the tsleep here does each in turn; move out of loop to multicast */ tsleep(&pwrusers.ackr, alldown, nil, 1000); } poperror(); qunlock(&pwrusers); //powersuspend(1); }else error(Ebadarg); free(cmd); break; default: error(Ebadusefd); } poperror(); return n; }
/* * Check all frames on device and resend any frames that have been * outstanding for 200% of the device round trip time average. */ static void aoesweepproc(void*) { ulong i, tx, timeout, nbc; vlong starttick; enum { Nms = 100, Nbcms = 30*1000, }; /* magic */ uchar *ea; Aoeata *a; Aoedev *d; Devlink *l; Frame *f, *e; nbc = Nbcms/Nms; loop: if(nbc-- == 0){ if(rediscover && !waserror()){ discover(0xffff, 0xff); poperror(); } nbc = Nbcms/Nms; } starttick = MACHP(0)->ticks; rlock(&devs); for(d = devs.d; d; d = d->next){ if(!canqlock(d)) continue; if(!UP(d)){ qunlock(d); continue; } tx = 0; f = d->frames; e = f + d->nframes; for (; f < e; f++){ if(f->tag == Tfree) continue; l = f->dl; timeout = l->rttavg << 1; i = tsince(f->tag); if(i < timeout) continue; if(d->nout == d->maxout){ if(d->maxout > 1) d->maxout--; d->lastwadj = MACHP(0)->ticks; } a = (Aoeata*)f->hdr; if(a->scnt > Dbcnt / Aoesectsz && ++f->nl->lostjumbo > (d->nframes << 1)){ ea = f->dl->eatab[f->eaidx]; eventlog("%æ: jumbo failure on %s:%E; lba%lld\n", d, f->nl->path, ea, f->lba); d->maxbcnt = Dbcnt; d->flag &= ~Djumbo; } resend(d, f); if(tx++ == 0){ if((l->rttavg <<= 1) > Rtmax) l->rttavg = Rtmax; eventlog("%æ: rtt %ldms\n", d, TK2MS(l->rttavg)); } } if(d->nout == d->maxout && d->maxout < d->nframes && TK2MS(MACHP(0)->ticks - d->lastwadj) > 10*1000){ /* more magic */ d->maxout++; d->lastwadj = MACHP(0)->ticks; } qunlock(d); } runlock(&devs); i = Nms - TK2MS(MACHP(0)->ticks - starttick); if(i > 0) tsleep(&up->sleep, return0, 0, i); goto loop; }
static long consread(Chan *c, void *va, long n, vlong offset) { int send; char buf[64], ch; if(c->qid.type & QTDIR) return devdirread(c, va, n, contab, nelem(contab), devgen); switch((ulong)c->qid.path) { default: error(Egreg); case Qsysctl: return readstr(offset, va, n, VERSION); case Qsysname: if(ossysname == nil) return 0; return readstr(offset, va, n, ossysname); case Qrandom: return randomread(va, n); case Qnotquiterandom: genrandom(va, n); return n; case Qhostowner: return readstr(offset, va, n, eve); case Qhoststdin: return read(0, va, n); /* should be pread */ case Quser: return readstr(offset, va, n, up->env->user); case Qjit: snprint(buf, sizeof(buf), "%d", cflag); return readstr(offset, va, n, buf); case Qtime: snprint(buf, sizeof(buf), "%.lld", timeoffset + osusectime()); return readstr(offset, va, n, buf); case Qdrivers: return devtabread(c, va, n, offset); case Qmemory: return poolread(va, n, offset); case Qnull: return 0; case Qmsec: return readnum(offset, va, n, osmillisec(), NUMSIZE); case Qcons: qlock(&kbd.q); if(waserror()){ qunlock(&kbd.q); nexterror(); } if(dflag) error(Enonexist); while(!qcanread(lineq)) { if(qread(kbdq, &ch, 1) == 0) continue; send = 0; if(ch == 0){ /* flush output on rawoff -> rawon */ if(kbd.x > 0) send = !qcanread(kbdq); }else if(kbd.raw){ kbd.line[kbd.x++] = ch; send = !qcanread(kbdq); }else{ switch(ch){ case '\b': if(kbd.x) kbd.x--; break; case 0x15: kbd.x = 0; break; case 0x04: send = 1; break; case '\n': send = 1; default: kbd.line[kbd.x++] = ch; break; } } if(send || kbd.x == sizeof kbd.line){ qwrite(lineq, kbd.line, kbd.x); kbd.x = 0; } } n = qread(lineq, va, n); qunlock(&kbd.q); poperror(); return n; case Qscancode: if(offset == 0) return readstr(0, va, n, gkscanid); return qread(gkscanq, va, n); case Qkeyboard: return qread(gkbdq, va, n); case Qkprint: rlock(&kprintq.l); if(waserror()){ runlock(&kprintq.l); nexterror(); } n = qread(kprintq.q, va, n); poperror(); runlock(&kprintq.l); return n; } }
static void atarw(Aoedev *d, Frame *f) { ulong bcnt; char extbit, writebit; Aoeata *ah; Srb *srb; extbit = 0x4; writebit = 0x10; srb = d->inprocess; bcnt = d->maxbcnt; if(bcnt > srb->len) bcnt = srb->len; f->nhdr = AOEATASZ; memset(f->hdr, 0, f->nhdr); ah = (Aoeata*)f->hdr; if(hset(d, f, ah, ACata) == -1) { d->inprocess = nil; return; } f->dp = srb->dp; f->bcnt = bcnt; f->lba = srb->sector; f->srb = srb; ah->scnt = bcnt / Aoesectsz; putlba(ah, f->lba); if(d->flag & Dllba) ah->aflag |= AAFext; else { extbit = 0; ah->lba[3] &= 0x0f; ah->lba[3] |= 0xe0; /* LBA bit+obsolete 0xa0 */ } if(srb->write){ ah->aflag |= AAFwrite; f->dlen = bcnt; }else{ writebit = 0; f->dlen = 0; } ah->cmdstat = 0x20 | writebit | extbit; /* mark tracking fields and load out */ srb->nout++; srb->dp = (uchar*)srb->dp + bcnt; srb->len -= bcnt; srb->sector += bcnt / Aoesectsz; if(srb->len == 0) d->inprocess = nil; d->nout++; f->dl->npkt++; if(waserror()){ f->tag = Tfree; d->inprocess = nil; nexterror(); } devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); poperror(); }
static int32_t consread(Chan *c, void *buf, int32_t n, int64_t off) { Proc *up = externup(); uint64_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->NIX.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 %lu\n", lockstats.locks); s = seprint(s, tmp + sizeof tmp, "glare %lu\n", lockstats.glare); s = seprint(s, tmp + sizeof tmp, "inglare %lu\n", lockstats.inglare); s = seprint(s, tmp + sizeof tmp, "qlock %lu\n", qlockstats.qlock); seprint(s, tmp + sizeof tmp, "qlockq %lu\n", qlockstats.qlockq); return readstr(offset, buf, n, tmp); break; case Qsyscall: snprint(tmp, sizeof tmp, "%s", printallsyscalls ? "on" : "off"); return readstr(offset, buf, n, tmp); break; default: print("consread %#llx\n", c->qid.path); error(Egreg); } return -1; /* never reached */ }
Walkqid* devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen) { int i, j, alloc; Walkqid *wq; char *n; Dir dir; if(nname > 0) isdir(c); alloc = 0; wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); if(waserror()){ if(alloc && wq->clone!=nil) cclose(wq->clone); free(wq); return nil; } if(nc == nil){ nc = devclone(c); nc->type = 0; /* device doesn't know about this channel yet */ 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){ if((*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir) != 1){ print("devgen walk .. in dev%s %llux broken\n", devtab[nc->type]->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 */ wq->clone->type = c->type; } return wq; }
static long capwrite(Chan *c, void *va, long n, vlong vl) { Caphash *p; char *cp; uchar hash[Hashlen]; char *key, *from, *to; char err[256]; switch((ulong)c->qid.path){ case Qhash: if(!iseve()) error(Eperm); if(n < Hashlen) error(Eshort); memmove(hash, va, Hashlen); addcap(hash); break; case Quse: /* copy key to avoid a fault in hmac_xx */ cp = nil; if(waserror()){ free(cp); nexterror(); } cp = smalloc(n+1); memmove(cp, va, n); cp[n] = 0; from = cp; key = strrchr(cp, '@'); if(key == nil) error(Eshort); *key++ = 0; hmac_sha1((uchar*)from, strlen(from), (uchar*)key, strlen(key), hash, nil); p = remcap(hash); if(p == nil){ snprint(err, sizeof err, "invalid capability %s@%s", from, key); error(err); } /* if a from user is supplied, make sure it matches */ to = strchr(from, '@'); if(to == nil){ to = from; } else { *to++ = 0; if(strcmp(from, up->user) != 0) error("capability must match user"); } /* set user id */ kstrdup(&up->user, to); up->basepri = PriNormal; free(p); free(cp); poperror(); break; default: error(Eperm); break; } return n; }
static long conswrite(Chan *c, void *va, long n, vlong offset) { vlong t; long l, bp; char *a = va; Cmdbuf *cb; Cmdtab *ct; char buf[256]; int x; switch((ulong)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(a, bp, 1); a += bp; l -= bp; } break; case Qconsctl: if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, a, n); buf[n] = 0; for(a = buf; a;){ if(strncmp(a, "rawon", 5) == 0){ qlock(&kbd); flushkbdline(kbdq); kbd.raw = 1; qunlock(&kbd); } else if(strncmp(a, "rawoff", 6) == 0){ qlock(&kbd); kbd.raw = 0; kbd.x = 0; qunlock(&kbd); } if(a = strchr(a, ' ')) a++; } break; case Qkeyboard: for(x=0; x<n; ) { Rune r; x += chartorune(&r, &a[x]); kbdputc(kbdq, r); } break; case Qtime: if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, a, n); buf[n] = 0; t = strtoll(buf, 0, 0)/1000000; boottime = t - TK2SEC(MACHP(0)->ticks); break; case Qhostowner: if(!iseve()) error(Eperm); if(offset != 0 || n >= sizeof(buf)) error(Ebadarg); memmove(buf, a, n); buf[n] = '\0'; if(n > 0 && buf[n-1] == '\n') buf[--n] = 0; if(n <= 0) error(Ebadarg); renameuser(eve, buf); renameproguser(eve, buf); kstrdup(&eve, buf); kstrdup(&up->env->user, buf); break; case Quser: if(!iseve()) error(Eperm); 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(&up->env->user, buf); break; case Qjit: if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, va, n); buf[n] = '\0'; x = atoi(buf); if(x < 0 || x > 9) error(Ebadarg); cflag = x; return n; case Qnull: 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 Qsysctl: if(!iseve()) error(Eperm); cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, sysctlcmd, nelem(sysctlcmd)); switch(ct->index){ case CMreboot: reboot(); break; case CMhalt: halt(); break; case CMpanic: panic("sysctl"); case CMconsole: consoleprint = strcmp(cb->f[1], "off") != 0; break; case CMbroken: keepbroken = 1; break; case CMnobroken: keepbroken = 0; break; } poperror(); free(cb); break; default: print("conswrite: %llud\n", c->qid.path); error(Egreg); } return n; }
long gpiowrite(Chan *c, void *va, long n, vlong) { int type, i, scheme; uint pin; char *arg; Cmdbuf *cb; Cmdtab *ct; if(c->qid.type & QTDIR) { error(Eisdir); } type = FILE_TYPE(c->qid); scheme = SCHEME_TYPE(c->qid); if(scheme != Qgeneric && scheme != pinscheme) { error(nil); } cb = parsecmd(va, n); if(waserror()) { free(cb); nexterror(); } ct = lookupcmd(cb, gpiocmd, nelem(gpiocmd)); if(ct == nil) { error(Ebadctl); } switch(type) { case Qdata: pin = PIN_NUMBER(c->qid); switch(ct->index) { case CMzero: gpioout(pin, 0); break; case CMone: gpioout(pin, 1); break; default: error(Ebadctl); } break; case Qctl: switch(ct->index) { case CMscheme: arg = cb->f[1]; for(i = 0; i < nelem(schemename); i++) { if(strncmp(schemename[i], arg, strlen(schemename[i])) == 0) { pinscheme = i; break; } } break; case CMfunc: pin = getpin(cb->f[2]); arg = cb->f[1]; if(pin == -1) { error(Ebadctl); } for(i = 0; i < nelem(funcname); i++) { if(strncmp(funcname[i], arg, strlen(funcname[i])) == 0) { gpiofuncset(pin, i); break; } } break; case CMpull: pin = getpin(cb->f[2]); if(pin == -1) { error(Ebadctl); } arg = cb->f[1]; for(i = 0; i < nelem(pudname); i++) { if(strncmp(pudname[i], arg, strlen(pudname[i])) == 0) { gpiopullset(pin, i); break; } } break; case CMevent: pin = getpin(cb->f[3]); if(pin == -1) { error(Ebadctl); } arg = cb->f[1]; for(i = 0; i < nelem(evtypename); i++) { if(strncmp(evtypename[i], arg, strlen(evtypename[i])) == 0) { gpioevent(pin, i, (cb->f[2][0] == 'e')); break; } } break; default: error(Ebadctl); } break; } free(cb); poperror(); return n; }
static void w_timer(void* arg) { Ether* ether = (Ether*) arg; Ctlr* ctlr = (Ctlr*)ether->ctlr; ctlr->timerproc = up; while(waserror()) ; for(;;){ tsleep(&up->sleep, return0, 0, MSperTick); ctlr = (Ctlr*)ether->ctlr; if(ctlr == 0) break; if((ctlr->state & (Attached|Power)) != (Attached|Power)) continue; ctlr->ticks++; ilock(ctlr); // Seems that the card gets frames BUT does // not send the interrupt; this is a problem because // I suspect it runs out of receive buffers and // stops receiving until a transmit watchdog // reenables the card. // The problem is serious because it leads to // poor rtts. // This can be seen clearly by commenting out // the next if and doing a ping: it will stop // receiving (although the icmp replies are being // issued from the remote) after a few seconds. // Of course this `bug' could be because I'm reading // the card frames in the wrong way; due to the // lack of documentation I cannot know. if(csr_ins(ctlr, WR_EvSts)&WEvs){ ctlr->tickintr++; w_intr(ether); } if((ctlr->ticks % 10) == 0) { if(ctlr->txtmout && --ctlr->txtmout == 0){ ctlr->nwatchdogs++; w_txdone(ctlr, WTxErrEv); if(w_enable(ether)){ DEBUG("wavelan: wdog enable failed\n"); } w_txstart(ether); } if((ctlr->ticks % 120) == 0) if(ctlr->txbusy == 0) w_cmd(ctlr, WCmdEnquire, WTyp_Stats); if(ctlr->scanticks > 0) if((ctlr->ticks % ctlr->scanticks) == 0) if(ctlr->txbusy == 0) w_cmd(ctlr, WCmdEnquire, WTyp_Scan); } iunlock(ctlr); } pexit("terminated", 1); }