summaryrefslogtreecommitdiff
path: root/sys/src/9/port/devcons.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/9/port/devcons.c1354
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*)&order;
+ 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*)&order;
+ 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;
+}