#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" void (*consdebug)(void) = nil; Queue* kbdq; /* unprocessed console input */ Queue* lineq; /* processed console input */ Queue* printq; /* console output */ static struct { QLock; int raw; /* true if we shouldn't process input */ int ctl; /* number of opens to the control file */ int x; /* index into line */ char line[1024]; /* current input line */ int count; int ctlpoff; /* a place to save up characters at interrupt time before dumping them in the queue */ Lock lockputc; char istage[512]; char *iw; char *ir; char *ie; } kbd; char sysname[NAMELEN]; vlong fasthz; static void seedrand(void); static int readtime(ulong, char*, int); static int readbintime(char*, int); static int writetime(char*, int); static int writebintime(char*, int); void printinit(void) { lineq = qopen(2*1024, 0, 0, 0); if(lineq == nil) panic("printinit"); qnoblock(lineq, 1); } int consactive(void) { if(printq) return qlen(printq) > 0; return 0; } void prflush(void) { ulong now; now = m->ticks; while(consactive()) if(m->ticks - now >= HZ) break; } /* * 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; /* * if there's an attached bit mapped display, * put the message there. screenputs is defined * as a null macro for systems that have no such * display. */ screenputs(str, n); /* * if there's a serial line being used as a console, * put the message there. */ if(printq == 0) return; while(n > 0) { t = memchr(str, '\n', n); if(t && !kbd.raw) { m = t-str; if(usewrite){ qwrite(printq, str, m); qwrite(printq, "\r\n", 2); } else { qiwrite(printq, str, m); qiwrite(printq, "\r\n", 2); } n -= m+1; str = t+1; } else { if(usewrite) qwrite(printq, str, n); else qiwrite(printq, str, n); break; } } } void putstrn(char *str, int n) { putstrn0(str, n, 0); } int snprint(char *s, int n, char *fmt, ...) { va_list arg; va_start(arg, fmt); n = doprint(s, s+n, fmt, arg) - s; va_end(arg); return n; } int sprint(char *s, char *fmt, ...) { int n; va_list arg; va_start(arg, fmt); n = doprint(s, s+PRINTSIZE, fmt, arg) - s; va_end(arg); return n; } char* seprint(char *buf, char *e, char *fmt, ...) { char *out; va_list arg; va_start(arg, fmt); out = doprint(buf, e, fmt, arg); va_end(arg); return out; } int noprint; int print(char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; if(noprint) return -1; va_start(arg, fmt); n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); putstrn(buf, n); return n; } int iprint(char *fmt, ...) { int n, s; va_list arg; char buf[PRINTSIZE]; s = splhi(); va_start(arg, fmt); n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); serialputs(buf, n); // screenputs(buf, n); splx(s); return n; } void panic(char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; splhi(); strcpy(buf, "panic: "); va_start(arg, fmt); n = doprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf; va_end(arg); buf[n] = '\n'; serialputs(buf, n+1); if(consdebug) consdebug(); spllo(); prflush(); putstrn(buf, n+1); dumpstack(); exit(1); } void _assert(char *fmt) { panic("assert failed: %s", fmt); } int pprint(char *fmt, ...) { int n; Chan *c; va_list arg; char buf[2*PRINTSIZE]; if(up == nil || up->fgrp == nil) return 0; c = up->fgrp->fd[2]; if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) return 0; n = sprint(buf, "%s %lud: ", up->text, up->pid); va_start(arg, fmt); n = doprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); if(waserror()) return 0; devtab[c->type]->write(c, buf, n, c->offset); poperror(); lock(c); c->offset += n; unlock(c); return n; } static void echoscreen(char *buf, int n) { char *e, *p; char ebuf[128]; int x; p = ebuf; e = ebuf + sizeof(ebuf) - 4; while(n-- > 0){ if(p >= e){ screenputs(ebuf, p - ebuf); p = ebuf; } x = *buf++; if(x == 0x15){ *p++ = '^'; *p++ = 'U'; *p++ = '\n'; } else *p++ = x; } if(p != ebuf) screenputs(ebuf, p - ebuf); } static void echoprintq(char *buf, int n) { char *e, *p; char ebuf[128]; int x; p = ebuf; e = ebuf + sizeof(ebuf) - 4; while(n-- > 0){ if(p >= e){ qiwrite(printq, ebuf, p - ebuf); p = ebuf; } x = *buf++; if(x == '\n'){ *p++ = '\r'; *p++ = '\n'; } else if(x == 0x15){ *p++ = '^'; *p++ = 'U'; *p++ = '\n'; } else *p++ = x; } if(p != ebuf) qiwrite(printq, ebuf, p - ebuf); } void echo(char *buf, int n) { static int ctrlt, pid; extern ulong etext; int x; char *e, *p; e = buf+n; for(p = buf; p < e; p++){ switch(*p){ case 0x10: /* ^P */ if(cpuserver && !kbd.ctlpoff){ active.exiting = 1; return; } break; case 0x14: /* ^T */ ctrlt++; if(ctrlt > 2) ctrlt = 2; continue; } if(ctrlt != 2) continue; /* ^T escapes */ ctrlt = 0; switch(*p){ case 's': dumpstack(); break; case 'x': xsummary(); ixsummary(); mallocsummary(); pagersummary(); break; case 'd': if(consdebug == nil) consdebug = rdb; else consdebug = nil; print("consdebug now 0x%p\n", consdebug); return; case 'D': if(consdebug == nil) consdebug = rdb; consdebug(); return; case 'p': x = spllo(); procdump(); splx(x); return; case 'q': scheddump(); break; case 'k': if(!cpuserver) killbig(); break; case 'r': exit(0); break; } } qproduce(kbdq, buf, n); if(kbd.raw) return; echoscreen(buf, n); if(printq) echoprintq(buf, n); } /* * Called by a uart interrupt for console input. * * turn '\r' into '\n' before putting it into the queue. */ int kbdcr2nl(Queue*, int ch) { char *next; ilock(&kbd.lockputc); /* just a mutex */ if(ch == '\r' && !kbd.raw) ch = '\n'; next = kbd.iw+1; if(next >= kbd.ie) next = kbd.istage; if(next != kbd.ir){ *kbd.iw = ch; kbd.iw = next; } iunlock(&kbd.lockputc); return 0; } /* * Put character, possibly a rune, into read queue at interrupt time. * Called at interrupt time to process a character. */ int kbdputc(Queue*, int ch) { int i, n; char buf[3]; Rune r; char *next; ilock(&kbd.lockputc); /* just a mutex */ r = ch; n = runetochar(buf, &r); for(i = 0; i < n; i++){ next = kbd.iw+1; if(next >= kbd.ie) next = kbd.istage; if(next == kbd.ir) break; *kbd.iw = buf[i]; kbd.iw = next; } iunlock(&kbd.lockputc); return 0; } /* * we save up input characters till clock time to reduce * per character interrupt overhead. */ static void kbdputcclock(void) { char *iw; /* this amortizes cost of qproduce */ if(kbd.iw != kbd.ir){ iw = kbd.iw; if(iw < kbd.ir){ echo(kbd.ir, kbd.ie-kbd.ir); kbd.ir = kbd.istage; } echo(kbd.ir, iw-kbd.ir); kbd.ir = iw; } } static void kbdputcinit(void) { kbd.ir = kbd.iw = kbd.istage; kbd.ie = kbd.istage + sizeof(kbd.istage); addclock0link(kbdputcclock); } enum{ Qdir, Qauth, Qauthcheck, Qauthent, Qbintime, Qcons, Qconsctl, Qcputime, Qdrivers, Qkey, Qhostdomain, Qhostowner, Qnull, Qpgrpid, Qpid, Qppid, Qrandom, Qreboot, Qswap, Qsysname, Qsysstat, Qtime, Quser, Qzero, }; enum { VLNUMSIZE= 22, }; static Dirtab consdir[]={ "authenticate", {Qauth}, 0, 0666, "authcheck", {Qauthcheck}, 0, 0666, "authenticator", {Qauthent}, 0, 0666, "bintime", {Qbintime}, 24, 0664, "cons", {Qcons}, 0, 0660, "consctl", {Qconsctl}, 0, 0220, "cputime", {Qcputime}, 6*NUMSIZE, 0444, "drivers", {Qdrivers}, 0, 0644, "hostdomain", {Qhostdomain}, DOMLEN, 0664, "hostowner", {Qhostowner}, NAMELEN, 0664, "key", {Qkey}, DESKEYLEN, 0622, "null", {Qnull}, 0, 0666, "pgrpid", {Qpgrpid}, NUMSIZE, 0444, "pid", {Qpid}, NUMSIZE, 0444, "ppid", {Qppid}, NUMSIZE, 0444, "random", {Qrandom}, 0, 0664, "reboot", {Qreboot}, 0, 0664, "swap", {Qswap}, 0, 0664, "sysname", {Qsysname}, 0, 0664, "sysstat", {Qsysstat}, 0, 0666, "time", {Qtime}, NUMSIZE+3*VLNUMSIZE, 0664, "user", {Quser}, NAMELEN, 0666, "zero", {Qzero}, 0, 0444, }; int readnum(ulong off, char *buf, ulong n, ulong val, int size) { char tmp[64]; snprint(tmp, sizeof(tmp), "%*.0lud", size-1, val); tmp[size-1] = ' '; if(off >= size) return 0; if(off+n > size) n = size-off; memmove(buf, tmp+off, n); return n; } int readstr(ulong off, char *buf, ulong n, char *str) { int size; size = strlen(str); if(off >= size) return 0; if(off+n > size) n = size-off; memmove(buf, str+off, n); return n; } static void consinit(void) { todinit(); randominit(); kbdputcinit(); } static Chan* consattach(char *spec) { return devattach('c', spec); } static int conswalk(Chan *c, char *name) { return devwalk(c, name, consdir, nelem(consdir), devgen); } static void consstat(Chan *c, char *dp) { devstat(c, dp, consdir, nelem(consdir), devgen); } static Chan* consopen(Chan *c, int omode) { c->aux = 0; switch(c->qid.path){ case Qconsctl: if(!iseve()) error(Eperm); qlock(&kbd); kbd.ctl++; qunlock(&kbd); break; } return devopen(c, omode, consdir, nelem(consdir), devgen); } static void consclose(Chan *c) { /* last close of control file turns off raw */ switch(c->qid.path){ case Qconsctl: if(c->flag&COPEN){ qlock(&kbd); if(--kbd.ctl == 0) kbd.raw = 0; qunlock(&kbd); } break; case Qauth: case Qauthcheck: case Qauthent: authclose(c); } } static long consread(Chan *c, void *buf, long n, vlong off) { ulong l; Mach *mp; char *b, *bp; char tmp[128]; /* must be >= 6*NUMSIZE */ char *cbuf = buf; int ch, i, k, id, eol; vlong offset = off; if(n <= 0) return n; switch(c->qid.path & ~CHDIR){ case Qdir: return devdirread(c, buf, n, consdir, nelem(consdir), devgen); case Qcons: qlock(&kbd); if(waserror()) { qunlock(&kbd); nexterror(); } if(kbd.raw) { 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 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*itime[i]; if(i == TReal) l = MACHP(0)->ticks - l; l = TK2MS(l); readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE); } memmove(buf, tmp+k, n); return n; case Qpgrpid: return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE); case Qpid: return readnum((ulong)offset, buf, n, up->pid, NUMSIZE); case Qppid: return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE); case Qtime: return readtime((ulong)offset, buf, n); case Qbintime: return readbintime(buf, n); case Qkey: return keyread(buf, n, offset); case Qauth: return authread(c, cbuf, n); case Qauthcheck: return authcheckread(c, cbuf, n); case Qauthent: return authentread(c, cbuf, n); case Qhostowner: return readstr((ulong)offset, buf, n, eve); case Qhostdomain: return readstr((ulong)offset, buf, n, hostdomain); case Quser: return readstr((ulong)offset, buf, n, up->user); case Qnull: return 0; case Qsysstat: b = smalloc(conf.nmach*(NUMSIZE*8+1) + 1); /* +1 for NUL */ bp = b; for(id = 0; id < 32; id++) { if(active.machs & (1<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, mp->load, NUMSIZE); bp += NUMSIZE; *bp++ = '\n'; } } n = readstr((ulong)offset, buf, n, b); free(b); return n; case Qswap: sprint(tmp, "%lud/%lud memory %lud/%lud swap\n", palloc.user-palloc.freecount, palloc.user, conf.nswap-swapalloc.free, conf.nswap); return readstr((ulong)offset, buf, n, tmp); case Qsysname: return readstr((ulong)offset, buf, n, sysname); case Qrandom: return randomread(buf, n); case Qdrivers: b = malloc(READSTR); if(b == nil) error(Enomem); n = 0; for(i = 0; devtab[i] != nil; i++) n += snprint(b+n, READSTR-n, "#%C %s\n", devtab[i]->dc, devtab[i]->name); n = readstr((ulong)offset, buf, n, b); free(b); return n; case Qzero: memset(buf, 0, n); return n; default: print("consread %lux\n", c->qid.path); error(Egreg); } return -1; /* never reached */ } static long conswrite(Chan *c, void *va, long n, vlong off) { char buf[256]; long l, bp; char *a = va; Mach *mp; int id, fd; Chan *swc; ulong offset = off; switch(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: 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); if(kbd.x){ qwrite(kbdq, kbd.line, kbd.x); kbd.x = 0; } kbd.raw = 1; qunlock(&kbd); } else if(strncmp(a, "rawoff", 6) == 0){ qlock(&kbd); kbd.raw = 0; kbd.x = 0; qunlock(&kbd); } else if(strncmp(a, "ctlpon", 6) == 0){ kbd.ctlpoff = 0; } else if(strncmp(a, "ctlpoff", 7) == 0){ kbd.ctlpoff = 1; } 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 Qkey: return keywrite(a, n); case Qhostowner: return hostownerwrite(a, n); case Qhostdomain: return hostdomainwrite(a, n); case Quser: return userwrite(a, n); case Qauth: return authwrite(c, a, n); case Qauthcheck: return authcheck(c, a, n); case Qauthent: return authentwrite(c, a, n); case Qnull: break; case Qreboot: if(!iseve()) error(Eperm); if(strncmp(a, "reboot", 6) == 0){ print("conswrite: reboot\n"); exit(0); } if(strncmp(a, "malloc", 6) == 0){ /* rsc bug */ a = malloc(2); strcpy(a, "hi"); free(a); a = malloc(2); strcpy(a, "helo"); free(a); panic("not reached conswrite"); } if(strncmp(a, "panic", 5) == 0) panic("/dev/reboot"); break; case Qsysstat: for(id = 0; id < 32; id++) { if(active.machs & (1<cs = 0; mp->intr = 0; mp->syscall = 0; mp->pfault = 0; mp->tlbfault = 0; mp->tlbpurge = 0; } } break; case Qswap: if(n >= sizeof buf) error(Egreg); memmove(buf, va, n); /* so we can NUL-terminate */ buf[n] = 0; /* start a pager if not already started */ if(strncmp(buf, "start", 5) == 0){ kickpager(); break; } if(cpuserver && !iseve()) error(Eperm); if(buf[0]<'0' || '9'= NAMELEN) error(Ebadarg); strncpy(sysname, a, n); sysname[n] = 0; if(sysname[n-1] == '\n') sysname[n-1] = 0; break; default: print("conswrite: %lud\n", c->qid.path); error(Egreg); } return n; } void setterm(char *f) { char buf[2*NAMELEN]; sprint(buf, f, conffile); ksetenv("terminal", buf); } Dev consdevtab = { 'c', "cons", devreset, consinit, consattach, devclone, conswalk, consstat, consopen, devcreate, consclose, consread, devbread, conswrite, devbwrite, devremove, devwstat, }; static ulong randn; static void seedrand(void) { randomread((void*)&randn, sizeof(randn)); } int nrand(int n) { if(randn == 0) seedrand(); randn = randn*1103515245 + 12345 + MACHP(0)->ticks; return (randn>>16) % n; } int rand(void) { nrand(1); return randn; } static uvlong uvorder = 0x0001020304050607ULL; static uchar* le2vlong(vlong *to, uchar *f) { uchar *t, *o; int i; t = (uchar*)to; o = (uchar*)&uvorder; for(i = 0; i < sizeof(vlong); i++) t[o[i]] = f[i]; return f+sizeof(vlong); } static uchar* vlong2le(uchar *t, vlong from) { uchar *f, *o; int i; f = (uchar*)&from; o = (uchar*)&uvorder; for(i = 0; i < sizeof(vlong); i++) t[i] = f[o[i]]; return t+sizeof(vlong); } static long order = 0x00010203; static uchar* le2long(long *to, uchar *f) { uchar *t, *o; int i; t = (uchar*)to; o = (uchar*)ℴ for(i = 0; i < sizeof(long); i++) t[o[i]] = f[i]; return f+sizeof(long); } static uchar* long2le(uchar *t, long from) { uchar *f, *o; int i; f = (uchar*)&from; o = (uchar*)ℴ for(i = 0; i < sizeof(long); i++) t[i] = f[o[i]]; return t+sizeof(long); } char *Ebadtimectl = "bad time control"; /* * like the old #c/time but with added info. Return * * secs nanosecs fastticks fasthz */ static int readtime(ulong off, char *buf, int n) { vlong nsec, ticks; long sec; char str[7*NUMSIZE]; nsec = todget(&ticks); if(fasthz == 0LL) fastticks((uvlong*)&fasthz); sec = nsec/1000000000ULL; snprint(str, sizeof(str), "%*.0lud %*.0llud %*.0llud %*.0llud ", NUMSIZE-1, sec, VLNUMSIZE-1, nsec, VLNUMSIZE-1, ticks, VLNUMSIZE-1, fasthz); return readstr(off, buf, n, str); } /* * set the time in seconds */ static int writetime(char *buf, int n) { char b[13]; long i; vlong now; if(n >= sizeof(b)) error(Ebadtimectl); strncpy(b, buf, n); b[n] = 0; i = strtol(b, 0, 0); if(i <= 0) error(Ebadtimectl); now = i*1000000000LL; todset(now, 0, 0); return n; } /* * read binary time info. all numbers are little endian. * ticks and nsec are syncronized. */ static int readbintime(char *buf, int n) { int i; vlong nsec, ticks; uchar *b = (uchar*)buf; i = 0; if(fasthz == 0LL) fastticks((uvlong*)&fasthz); nsec = todget(&ticks); if(n >= 3*sizeof(uvlong)){ vlong2le(b+2*sizeof(uvlong), fasthz); i += sizeof(uvlong); } if(n >= 2*sizeof(uvlong)){ vlong2le(b+sizeof(uvlong), ticks); i += sizeof(uvlong); } if(n >= 8){ vlong2le(b, nsec); i += sizeof(vlong); } return i; } /* * set any of the following * - time in nsec * - nsec trim applied over some seconds * - clock frequency */ static int writebintime(char *buf, int n) { uchar *p; vlong delta; long period; n--; p = (uchar*)buf + 1; switch(*buf){ case 'n': if(n < sizeof(vlong)) error(Ebadtimectl); le2vlong(&delta, p); todset(delta, 0, 0); break; case 'd': if(n < sizeof(vlong)+sizeof(long)) error(Ebadtimectl); p = le2vlong(&delta, p); le2long(&period, p); todset(-1, delta, period); break; case 'f': if(n < sizeof(uvlong)) error(Ebadtimectl); le2vlong(&fasthz, p); todsetfreq(fasthz); break; } return n; }