ref: e59ffed426f628794d4669f152eff9a6239b99db
dir: /os/port/devcons.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include <version.h> #include "mp.h" #include "libsec.h" #include "keyboard.h" extern int cflag; extern int keepbroken; void (*serwrite)(char *, int); Queue* kscanq; /* keyboard raw scancodes (when needed) */ char* kscanid; /* name of raw scan format (if defined) */ Queue* kbdq; /* unprocessed console input */ Queue* lineq; /* processed console input */ Queue* printq; /* console output */ Queue* klogq; /* kernel print (log) output */ int iprintscreenputs; static struct { RWlock; Queue* q; } kprintq; static struct { QLock; int raw; /* true if we shouldn't process input */ int ctl; /* number of opens to the control file */ int kbdr; /* number of open reads to the keyboard */ int scan; /* true if reading raw scancodes */ int x; /* index into line */ char line[1024]; /* current input line */ char c; int count; int repeat; } kbd; char* sysname; char* eve; enum { CMreboot, CMhalt, CMpanic, CMbroken, CMnobroken, CMconsole, }; static Cmdtab sysctlcmd[] = { CMreboot, "reboot", 0, CMhalt, "halt", 0, CMpanic, "panic", 0, CMconsole, "console", 1, CMbroken, "broken", 0, CMnobroken, "nobroken", 0, }; void printinit(void) { lineq = qopen(2*1024, 0, nil, nil); if(lineq == nil) panic("printinit"); qnoblock(lineq, 1); } /* * return true if current user is eve */ int iseve(void) { Osenv *o; o = up->env; return strcmp(eve, o->user) == 0; } static int consactive(void) { if(printq) return qlen(printq) > 0; return 0; } static void prflush(void) { ulong now; now = m->ticks; while(serwrite==nil && 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; 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; } } } 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 = vseprint(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 = vseprint(s, s+PRINTSIZE, fmt, arg) - s; va_end(arg); return n; } int print(char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; va_start(arg, fmt); n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); putstrn(buf, n); return n; } int fprint(int fd, char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; USED(fd); va_start(arg, fmt); n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); putstrn(buf, n); return n; } int kprint(char *fmt, ...) { va_list arg; char buf[PRINTSIZE]; int n; va_start(arg, fmt); n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); if(qfull(klogq)) qflush(klogq); return qproduce(klogq, buf, n); } int iprint(char *fmt, ...) { int n, s; va_list arg; char buf[PRINTSIZE]; s = splhi(); va_start(arg, fmt); n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); if(screenputs != nil && iprintscreenputs) screenputs(buf, n); uartputs(buf, n); splx(s); return n; } void panic(char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; setpanic(); kprintq.q = nil; strcpy(buf, "panic: "); va_start(arg, fmt); n = vseprint(buf+strlen(buf), buf+sizeof(buf)-1, fmt, arg) - buf; va_end(arg); buf[n] = '\n'; putstrn(buf, n+1); spllo(); dumpstack(); exit(1); } void _assert(char *fmt) { panic("assert failed: %s", fmt); } /* * mainly for libmp */ void sysfatal(char *fmt, ...) { va_list arg; char buf[64]; va_start(arg, fmt); vsnprint(buf, sizeof(buf), fmt, arg); va_end(arg); error(buf); } int pprint(char *fmt, ...) { int n; Chan *c; Osenv *o; va_list arg; char buf[2*PRINTSIZE]; n = sprint(buf, "%s %ld: ", up->text, up->pid); va_start(arg, fmt); n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); o = up->env; if(o->fgrp == 0) { print("%s", buf); return 0; } c = o->fgrp->fd[2]; if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) { print("%s", buf); return 0; } if(waserror()) { print("%s", buf); return 0; } devtab[c->type]->write(c, buf, n, c->offset); poperror(); lock(c); c->offset += n; unlock(c); return n; } void echo(Rune r, char *buf, int n) { if(kbd.raw) return; if(r == '\n'){ if(printq) qiwrite(printq, "\r", 1); } else if(r == 0x15){ buf = "^U\n"; n = 3; } if(consoleprint && screenputs != nil) screenputs(buf, n); if(printq) qiwrite(printq, buf, n); } /* * Debug key support. Allows other parts of the kernel to register debug * key handlers, instead of devcons.c having to know whatever's out there. * A kproc is used to invoke most handlers, rather than tying up the CPU at * splhi, which can choke some device drivers (eg softmodem). */ typedef struct { Rune r; char *m; void (*f)(Rune); int i; /* function called at interrupt time */ } Dbgkey; static struct { Rendez; Dbgkey *work; Dbgkey keys[50]; int nkeys; int on; } dbg; static Dbgkey * finddbgkey(Rune r) { int i; Dbgkey *dp; for(dp = dbg.keys, i = 0; i < dbg.nkeys; i++, dp++) if(dp->r == r) return dp; return nil; } static int dbgwork(void *) { return dbg.work != 0; } static void dbgproc(void *) { Dbgkey *dp; setpri(PriRealtime); for(;;) { do { sleep(&dbg, dbgwork, 0); dp = dbg.work; } while(dp == nil); dp->f(dp->r); dbg.work = nil; } } void debugkey(Rune r, char *msg, void (*fcn)(), int iflag) { Dbgkey *dp; if(dbg.nkeys >= nelem(dbg.keys)) return; if(finddbgkey(r) != nil) return; for(dp = &dbg.keys[dbg.nkeys++] - 1; dp >= dbg.keys; dp--) { if(strcmp(dp->m, msg) < 0) break; dp[1] = dp[0]; } dp++; dp->r = r; dp->m = msg; dp->f = fcn; dp->i = iflag; } static int isdbgkey(Rune r) { static int ctrlt; Dbgkey *dp; int echoctrlt = ctrlt; /* * ^t hack BUG */ if(dbg.on || (ctrlt >= 2)) { if(r == 0x14 || r == 0x05) { ctrlt++; return 0; } if(dp = finddbgkey(r)) { if(dp->i || ctrlt > 2) dp->f(r); else { dbg.work = dp; wakeup(&dbg); } ctrlt = 0; return 1; } ctrlt = 0; } else if(r == 0x14){ ctrlt++; return 1; } else ctrlt = 0; if(echoctrlt){ char buf[UTFmax]; buf[0] = 0x14; while(--echoctrlt >= 0){ echo(buf[0], buf, 1); qproduce(kbdq, buf, 1); } } return 0; } static void dbgtoggle(Rune) { dbg.on = !dbg.on; print("Debug keys %s\n", dbg.on ? "HOT" : "COLD"); } static void dbghelp(void) { int i; Dbgkey *dp; Dbgkey *dp2; static char fmt[] = "%c: %-22s"; dp = dbg.keys; dp2 = dp + (dbg.nkeys + 1)/2; for(i = dbg.nkeys; i > 1; i -= 2, dp++, dp2++) { print(fmt, dp->r, dp->m); print(fmt, dp2->r, dp2->m); print("\n"); } if(i) print(fmt, dp->r, dp->m); print("\n"); } static void debuginit(void) { kproc("consdbg", dbgproc, nil, 0); debugkey('|', "HOT|COLD keys", dbgtoggle, 0); debugkey('?', "help", dbghelp, 0); } /* * Called by a uart interrupt for console input. * * turn '\r' into '\n' before putting it into the queue. */ int kbdcr2nl(Queue *q, int ch) { if(ch == '\r') ch = '\n'; return kbdputc(q, ch); } /* * Put character, possibly a rune, into read queue at interrupt time. * Performs translation for compose sequences * Called at interrupt time to process a character. */ int kbdputc(Queue *q, int ch) { int n; char buf[UTFmax]; Rune r; static Rune kc[15]; static int nk, collecting = 0; r = ch; if(r == Latin) { collecting = 1; nk = 0; return 0; } if(collecting) { int c; nk += runetochar((char*)&kc[nk], &r); c = latin1(kc, nk); if(c < -1) /* need more keystrokes */ return 0; collecting = 0; if(c == -1) { /* invalid sequence */ echo(kc[0], (char*)kc, nk); qproduce(q, kc, nk); return 0; } r = (Rune)c; } kbd.c = r; n = runetochar(buf, &r); if(n == 0) return 0; if(!isdbgkey(r)) { echo(r, buf, n); qproduce(q, buf, n); } return 0; } void kbdrepeat(int rep) { kbd.repeat = rep; kbd.count = 0; } void kbdclock(void) { if(kbd.repeat == 0) return; if(kbd.repeat==1 && ++kbd.count>HZ){ kbd.repeat = 2; kbd.count = 0; return; } if(++kbd.count&1) kbdputc(kbdq, kbd.c); } enum{ Qdir, Qcons, Qsysctl, Qconsctl, Qdrivers, Qhostowner, Qkeyboard, Qklog, Qkprint, Qscancode, Qmemory, Qmsec, Qnull, Qrandom, Qnotquiterandom, Qsysname, Qtime, Quser, Qjit, }; static Dirtab consdir[]= { ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "cons", {Qcons}, 0, 0660, "consctl", {Qconsctl}, 0, 0220, "sysctl", {Qsysctl}, 0, 0644, "drivers", {Qdrivers}, 0, 0444, "hostowner", {Qhostowner}, 0, 0644, "keyboard", {Qkeyboard}, 0, 0666, "klog", {Qklog}, 0, 0444, "kprint", {Qkprint}, 0, 0444, "scancode", {Qscancode}, 0, 0444, "memory", {Qmemory}, 0, 0444, "msec", {Qmsec}, NUMSIZE, 0444, "null", {Qnull}, 0, 0666, "random", {Qrandom}, 0, 0444, "notquiterandom", {Qnotquiterandom}, 0, 0444, "sysname", {Qsysname}, 0, 0664, "time", {Qtime}, 0, 0664, "user", {Quser}, 0, 0644, "jit", {Qjit}, 0, 0666, }; ulong boottime; /* seconds since epoch at boot */ long seconds(void) { return boottime + TK2SEC(MACHP(0)->ticks); } vlong mseconds(void) { return ((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks))); } vlong osusectime(void) { return (((vlong)boottime*1000)+((vlong)(TK2MS(MACHP(0)->ticks)))*1000); } vlong nsec(void) { return osusectime()*1000; /* TO DO */ } int readnum(ulong off, char *buf, ulong n, ulong val, int size) { char tmp[64]; if(size > 64) size = 64; snprint(tmp, sizeof(tmp), "%*.0lud ", size, val); 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; } void fddump() { Proc *p; Osenv *o; int i; Chan *c; p = proctab(6); o = p->env; for(i = 0; i <= o->fgrp->maxfd; i++) { if((c = o->fgrp->fd[i]) == nil) continue; print("%d: %s\n", i, c->name == nil? "???": c->name->s); } } static void qpanic(Rune) { panic("User requested panic."); } static void rexit(Rune) { exit(0); } static void consinit(void) { randominit(); debuginit(); debugkey('f', "files/6", fddump, 0); debugkey('q', "panic", qpanic, 1); debugkey('r', "exit", rexit, 1); klogq = qopen(128*1024, 0, 0, 0); } 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 void flushkbdline(Queue *q) { if(kbd.x){ qwrite(q, kbd.line, kbd.x); kbd.x = 0; } } static Chan* consopen(Chan *c, int omode) { c->aux = 0; switch((ulong)c->qid.path){ case Qconsctl: if(!iseve()) error(Eperm); qlock(&kbd); kbd.ctl++; qunlock(&kbd); break; case Qkeyboard: if((omode & 3) != OWRITE) { qlock(&kbd); kbd.kbdr++; flushkbdline(kbdq); kbd.raw = 1; qunlock(&kbd); } break; case Qscancode: qlock(&kbd); if(kscanq || !kscanid) { qunlock(&kbd); c->flag &= ~COPEN; if(kscanq) error(Einuse); else error(Ebadarg); } kscanq = qopen(256, 0, nil, nil); qunlock(&kbd); break; case Qkprint: if((omode & 3) != OWRITE) { wlock(&kprintq); if(kprintq.q != nil){ wunlock(&kprintq); error(Einuse); } kprintq.q = qopen(32*1024, Qcoalesce, nil, nil); if(kprintq.q == nil){ wunlock(&kprintq); error(Enomem); } qnoblock(kprintq.q, 1); wunlock(&kprintq); c->iounit = qiomaxatomic; } break; } return devopen(c, omode, consdir, nelem(consdir), devgen); } static void consclose(Chan *c) { if((c->flag&COPEN) == 0) return; switch((ulong)c->qid.path){ case Qconsctl: /* last close of control file turns off raw */ qlock(&kbd); if(--kbd.ctl == 0) kbd.raw = 0; qunlock(&kbd); break; case Qkeyboard: if(c->mode != OWRITE) { qlock(&kbd); --kbd.kbdr; qunlock(&kbd); } break; case Qscancode: qlock(&kbd); if(kscanq) { qfree(kscanq); kscanq = 0; } qunlock(&kbd); break; case Qkprint: wlock(&kprintq); qfree(kprintq.q); kprintq.q = nil; wunlock(&kprintq); break; } } 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 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; } 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) { 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; } ulong truerand(void) { ulong x; randomread(&x, sizeof(x)); return x; } QLock grandomlk; void _genrandomqlock(void) { qlock(&grandomlk); } void _genrandomqunlock(void) { qunlock(&grandomlk); }