diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/devcons.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/devcons.c')
-rwxr-xr-x | sys/src/9/port/devcons.c | 1354 |
1 files changed, 1354 insertions, 0 deletions
diff --git a/sys/src/9/port/devcons.c b/sys/src/9/port/devcons.c new file mode 100755 index 000000000..6666f01b5 --- /dev/null +++ b/sys/src/9/port/devcons.c @@ -0,0 +1,1354 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "pool.h" + +#include <authsrv.h> + +void (*consdebug)(void) = nil; +void (*screenputs)(char*, int) = nil; + +Queue* kbdq; /* unprocessed console input */ +Queue* lineq; /* processed console input */ +Queue* serialoq; /* serial console output */ +Queue* kprintoq; /* console output, for /dev/kprint */ +ulong kprintinuse; /* test and set whether /dev/kprint is open */ +int iprintscreenputs = 1; + +int panicking; + +static struct +{ + QLock; + + int raw; /* true if we shouldn't process input */ + Ref 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[1024]; + char *iw; + char *ir; + char *ie; +} kbd = { + .iw = kbd.istage, + .ir = kbd.istage, + .ie = kbd.istage + sizeof(kbd.istage), +}; + +char *sysname; +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); + +enum +{ + CMhalt, + CMreboot, + CMpanic, +}; + +Cmdtab rebootmsg[] = +{ + CMhalt, "halt", 1, + CMreboot, "reboot", 0, + CMpanic, "panic", 0, +}; + +void +printinit(void) +{ + lineq = qopen(2*1024, 0, nil, nil); + if(lineq == nil) + panic("printinit"); + qnoblock(lineq, 1); +} + +int +consactive(void) +{ + if(serialoq) + return qlen(serialoq) > 0; + return 0; +} + +void +prflush(void) +{ + ulong now; + + now = m->ticks; + while(consactive()) + if(m->ticks - now >= HZ) + break; +} + +/* + * Log console output so it can be retrieved via /dev/kmesg. + * This is good for catching boot-time messages after the fact. + */ +struct { + Lock lk; +// char buf[16384]; /* normal */ + char buf[256*1024]; /* for acpi debugging */ + uint n; +} kmesg; + +static void +kmesgputs(char *str, int n) +{ + uint nn, d; + + ilock(&kmesg.lk); + /* take the tail of huge writes */ + if(n > sizeof kmesg.buf){ + d = n - sizeof kmesg.buf; + str += d; + n -= d; + } + + /* slide the buffer down to make room */ + nn = kmesg.n; + if(nn + n >= sizeof kmesg.buf){ + d = nn + n - sizeof kmesg.buf; + if(d) + memmove(kmesg.buf, kmesg.buf+d, sizeof kmesg.buf-d); + nn -= d; + } + + /* copy the data in */ + memmove(kmesg.buf+nn, str, n); + nn += n; + kmesg.n = nn; + iunlock(&kmesg.lk); +} + +/* + * 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(!islo()) + usewrite = 0; + + /* + * how many different output devices do we need? + */ + kmesgputs(str, n); + + /* + * if someone is reading /dev/kprint, + * put the message there. + * if not and there's an attached bit mapped display, + * put the message there. + * + * if there's a serial line being used as a console, + * put the message there. + */ + if(kprintoq != nil && !qisclosed(kprintoq)){ + if(usewrite) + qwrite(kprintoq, str, n); + else + qiwrite(kprintoq, str, n); + }else if(screenputs != nil) + screenputs(str, n); + + if(serialoq == nil){ + uartputs(str, n); + return; + } + + while(n > 0) { + t = memchr(str, '\n', n); + if(t && !kbd.raw) { + m = t-str; + if(usewrite){ + qwrite(serialoq, str, m); + qwrite(serialoq, "\r\n", 2); + } else { + qiwrite(serialoq, str, m); + qiwrite(serialoq, "\r\n", 2); + } + n -= m+1; + str = t+1; + } else { + if(usewrite) + qwrite(serialoq, str, n); + else + qiwrite(serialoq, str, n); + break; + } + } +} + +void +putstrn(char *str, int n) +{ + putstrn0(str, n, 0); +} + +int noprint; + +int +print(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + if(noprint) + return -1; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + putstrn(buf, n); + + return n; +} + +/* + * Want to interlock iprints to avoid interlaced output on + * multiprocessor, but don't want to deadlock if one processor + * dies during print and another has something important to say. + * Make a good faith effort. + */ +static Lock iprintlock; +static int +iprintcanlock(Lock *l) +{ + int i; + + for(i=0; i<1000; i++){ + if(canlock(l)) + return 1; + if(l->m == MACHP(m->machno)) + return 0; + microdelay(100); + } + return 0; +} + +int +iprint(char *fmt, ...) +{ + int n, s, locked; + va_list arg; + char buf[PRINTSIZE]; + + s = splhi(); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + locked = iprintcanlock(&iprintlock); + if(screenputs != nil && iprintscreenputs) + screenputs(buf, n); + uartputs(buf, n); + if(locked) + unlock(&iprintlock); + splx(s); + + return n; +} + +void +panic(char *fmt, ...) +{ + int n, s; + va_list arg; + char buf[PRINTSIZE]; + + kprintoq = nil; /* don't try to write to /dev/kprint */ + + if(panicking) + for(;;); + panicking = 1; + + s = splhi(); + strcpy(buf, "panic: "); + va_start(arg, fmt); + n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf; + va_end(arg); + iprint("%s\n", buf); + if(consdebug) + (*consdebug)(); + splx(s); + prflush(); + buf[n] = '\n'; + putstrn(buf, n+1); + dumpstack(); + + exit(1); +} + +/* libmp at least contains a few calls to sysfatal; simulate with panic */ +void +sysfatal(char *fmt, ...) +{ + char err[256]; + va_list arg; + + va_start(arg, fmt); + vseprint(err, err + sizeof err, fmt, arg); + va_end(arg); + panic("sysfatal: %s", err); +} + +void +_assert(char *fmt) +{ + panic("assert failed at %#p: %s", getcallerpc(&fmt), 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 = snprint(buf, sizeof buf, "%s %lud: ", up->text, up->pid); + va_start(arg, fmt); + n = vseprint(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 +echoserialoq(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(serialoq, 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(serialoq, ebuf, p - ebuf); +} + +static void +echo(char *buf, int n) +{ + static int ctrlt, pid; + int x; + char *e, *p; + + if(n == 0) + return; + + 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': + x = splhi(); + dumpstack(); + procdump(); + splx(x); + return; + case 's': + dumpstack(); + return; + case 'x': + xsummary(); + ixsummary(); + mallocsummary(); + // memorysummary(); + pagersummary(); + return; + case 'd': + if(consdebug == nil) + consdebug = rdb; + else + consdebug = nil; + print("consdebug now %#p\n", consdebug); + return; + case 'D': + if(consdebug == nil) + consdebug = rdb; + consdebug(); + return; + case 'p': + x = spllo(); + procdump(); + splx(x); + return; + case 'q': + scheddump(); + return; + case 'k': + killbig("^t ^t k"); + return; + case 'r': + exit(0); + return; + } + } + + qproduce(kbdq, buf, n); + if(kbd.raw) + return; + kmesgputs(buf, n); + if(screenputs != nil) + echoscreen(buf, n); + if(serialoq) + echoserialoq(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; + + if(kbd.ir == nil) + return 0; /* in case we're not inited yet */ + + 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; + } + if(kbd.ir != iw){ + echo(kbd.ir, iw-kbd.ir); + kbd.ir = iw; + } + } +} + +enum{ + Qdir, + Qbintime, + Qcons, + Qconsctl, + Qcputime, + Qdrivers, + Qkmesg, + Qkprint, + Qhostdomain, + Qhostowner, + Qnull, + Qosversion, + Qpgrpid, + Qpid, + Qppid, + Qrandom, + Qreboot, + Qswap, + Qsysname, + Qsysstat, + Qtime, + Quser, + Qzero, + Qconfig, +}; + +enum +{ + VLNUMSIZE= 22, +}; + +static Dirtab consdir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "bintime", {Qbintime}, 24, 0664, + "cons", {Qcons}, 0, 0660, + "consctl", {Qconsctl}, 0, 0220, + "cputime", {Qcputime}, 6*NUMSIZE, 0444, + "drivers", {Qdrivers}, 0, 0444, + "hostdomain", {Qhostdomain}, DOMLEN, 0664, + "hostowner", {Qhostowner}, 0, 0664, + "kmesg", {Qkmesg}, 0, 0440, + "kprint", {Qkprint, 0, QTEXCL}, 0, DMEXCL|0440, + "null", {Qnull}, 0, 0666, + "osversion", {Qosversion}, 0, 0444, + "pgrpid", {Qpgrpid}, NUMSIZE, 0444, + "pid", {Qpid}, NUMSIZE, 0444, + "ppid", {Qppid}, NUMSIZE, 0444, + "random", {Qrandom}, 0, 0444, + "reboot", {Qreboot}, 0, 0664, + "swap", {Qswap}, 0, 0664, + "sysname", {Qsysname}, 0, 0664, + "sysstat", {Qsysstat}, 0, 0666, + "time", {Qtime}, NUMSIZE+3*VLNUMSIZE, 0664, + "user", {Quser}, 0, 0666, + "zero", {Qzero}, 0, 0444, + "config", {Qconfig}, 0, 0444, +}; + +int +readnum(ulong off, char *buf, ulong n, ulong val, int size) +{ + char tmp[64]; + + snprint(tmp, sizeof(tmp), "%*lud", 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(); + /* + * at 115200 baud, the 1024 char buffer takes 56 ms to process, + * processing it every 22 ms should be fine + */ + addclock0link(kbdputcclock, 22); +} + +static Chan* +consattach(char *spec) +{ + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen); +} + +static int +consstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, consdir, nelem(consdir), devgen); +} + +static Chan* +consopen(Chan *c, int omode) +{ + c->aux = nil; + c = devopen(c, omode, consdir, nelem(consdir), devgen); + switch((ulong)c->qid.path){ + case Qconsctl: + incref(&kbd.ctl); + break; + + case Qkprint: + if(tas(&kprintinuse) != 0){ + c->flag &= ~COPEN; + error(Einuse); + } + if(kprintoq == nil){ + kprintoq = qopen(8*1024, Qcoalesce, 0, 0); + if(kprintoq == nil){ + c->flag &= ~COPEN; + error(Enomem); + } + qnoblock(kprintoq, 1); + }else + qreopen(kprintoq); + c->iounit = qiomaxatomic; + break; + } + return c; +} + +static void +consclose(Chan *c) +{ + switch((ulong)c->qid.path){ + /* last close of control file turns off raw */ + case Qconsctl: + if(c->flag&COPEN){ + if(decref(&kbd.ctl) == 0) + kbd.raw = 0; + } + break; + + /* close of kprint allows other opens */ + case Qkprint: + if(c->flag & COPEN){ + kprintinuse = 0; + qhangup(kprintoq, nil); + } + break; + } +} + +static long +consread(Chan *c, void *buf, long n, vlong off) +{ + ulong l; + Mach *mp; + char *b, *bp, ch; + char tmp[256]; /* must be >= 18*NUMSIZE (Qswap) */ + int i, k, id, send; + vlong offset = off; + extern char configfile[]; + + if(n <= 0) + return n; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, buf, n, consdir, nelem(consdir), devgen); + + case Qcons: + qlock(&kbd); + if(waserror()) { + qunlock(&kbd); + nexterror(); + } + 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 > 0) + kbd.x--; + break; + case 0x15: /* ^U */ + kbd.x = 0; + break; + case '\n': + case 0x04: /* ^D */ + send = 1; + default: + if(ch != 0x04) + 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, 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*i<k+n; i++){ + l = up->time[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 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: + return qread(kprintoq, buf, 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 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 Qconfig: + return readstr((ulong)offset, buf, n, configfile); + + case Qsysstat: + b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1); /* +1 for NUL */ + bp = b; + for(id = 0; id < 32; id++) { + if(active.machs & (1<<id)) { + mp = MACHP(id); + readnum(0, bp, NUMSIZE, id, 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, mp->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; + *bp++ = '\n'; + } + } + if(waserror()){ + free(b); + nexterror(); + } + n = readstr((ulong)offset, buf, n, b); + free(b); + poperror(); + return n; + + case Qswap: + snprint(tmp, sizeof tmp, + "%lud memory\n" + "%d pagesize\n" + "%lud kernel\n" + "%lud/%lud user\n" + "%lud/%lud swap\n" + "%lud/%lud kernel malloc\n" + "%lud/%lud kernel draw\n", + conf.npage*BY2PG, + BY2PG, + conf.npage-conf.upages, + palloc.user-palloc.freecount, palloc.user, + conf.nswap-swapalloc.free, conf.nswap, + mainmem->cursize, mainmem->maxsize, + imagmem->cursize, imagmem->maxsize); + + return readstr((ulong)offset, buf, n, tmp); + + case Qsysname: + if(sysname == nil) + return 0; + return readstr((ulong)offset, buf, n, sysname); + + case Qrandom: + return randomread(buf, n); + + case Qdrivers: + b = malloc(READSTR); + if(b == nil) + error(Enomem); + k = 0; + for(i = 0; devtab[i] != nil; i++) + k += snprint(b+k, READSTR-k, "#%C %s\n", + devtab[i]->dc, devtab[i]->name); + if(waserror()){ + free(b); + nexterror(); + } + n = readstr((ulong)offset, buf, n, b); + free(b); + poperror(); + return n; + + case Qzero: + memset(buf, 0, n); + return n; + + case Qosversion: + snprint(tmp, sizeof tmp, "2000"); + n = readstr((ulong)offset, buf, n, tmp); + return n; + + default: + print("consread %#llux\n", c->qid.path); + error(Egreg); + } + return -1; /* never reached */ +} + +static long +conswrite(Chan *c, void *va, long n, vlong off) +{ + char buf[256], ch; + long l, bp; + char *a; + Mach *mp; + int id, fd; + Chan *swc; + ulong offset; + Cmdbuf *cb; + Cmdtab *ct; + + a = va; + offset = off; + + 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(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){ + kbd.raw = 1; + /* clumsy hack - wake up reader */ + ch = 0; + qwrite(kbdq, &ch, 1); + } else if(strncmp(a, "rawoff", 6) == 0){ + kbd.raw = 0; + } 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 Qhostowner: + return hostownerwrite(a, n); + + case Qhostdomain: + return hostdomainwrite(a, n); + + case Quser: + return userwrite(a, n); + + case Qnull: + break; + + case Qconfig: + error(Eperm); + 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: + *(ulong*)0=0; + panic("/dev/reboot"); + } + poperror(); + free(cb); + break; + + case Qsysstat: + for(id = 0; id < 32; id++) { + if(active.machs & (1<<id)) { + mp = MACHP(id); + mp->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(!iseve()) + error(Eperm); + if(buf[0]<'0' || '9'<buf[0]) + error(Ebadarg); + fd = strtoul(buf, 0, 0); + swc = fdtochan(fd, -1, 1, 1); + setswapchan(swc); + 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; + + default: + print("conswrite: %#llux\n", c->qid.path); + error(Egreg); + } + return n; +} + +Dev consdevtab = { + 'c', + "cons", + + devreset, + consinit, + devshutdown, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat, +}; + +static ulong randn; + +static void +seedrand(void) +{ + if(!waserror()){ + randomread((void*)&randn, sizeof(randn)); + poperror(); + } +} + +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), "%*lud %*llud %*llud %*llud ", + 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); + if(fasthz <= 0) + error(Ebadtimectl); + todsetfreq(fasthz); + break; + } + return n; +} |