| /* | 
 |  * This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include <arch/arch.h> | 
 | #include <atomic.h> | 
 | #include <env.h> | 
 | #include <error.h> | 
 | #include <event.h> | 
 | #include <kmalloc.h> | 
 | #include <ns.h> | 
 | #include <ros/fs.h> | 
 | #include <ros/procinfo.h> | 
 | #include <smp.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <sys/queue.h> | 
 | #include <time.h> | 
 |  | 
 | #if 0 | 
 | void (*consdebug) (void) = NULL; | 
 | #endif | 
 | void (*screenputs)(const char *, int) = cputbuf; | 
 |  | 
 | struct queue *kbdq;       /* unprocessed console input */ | 
 | struct queue *lineq;      /* processed console input */ | 
 | struct queue *serialoq;   /* serial console output */ | 
 | struct queue *kprintoq;   /* console output, for /dev/kprint */ | 
 | atomic_t kprintinuse = 0; /* test and set whether /dev/kprint is open */ | 
 | int iprintscreenputs = 1; | 
 | int keepbroken = 1; | 
 |  | 
 | static bool cons_has_init = FALSE; | 
 | struct queue *cons_q; /* Akaros cons input: keyboard, serial, etc */ | 
 | spinlock_t cons_q_lock = SPINLOCK_INITIALIZER; | 
 | struct fdtap_slist cons_q_fd_taps = SLIST_HEAD_INITIALIZER(cons_q_fd_taps); | 
 |  | 
 | static uint8_t logbuffer[1 << 20]; | 
 | static int index = 0; | 
 | static struct queue *logqueue = NULL; | 
 | static int reading_kmesg = 0; | 
 |  | 
 | typedef unsigned char uint8_t; | 
 |  | 
 | void logbuf(int c) | 
 | { | 
 | 	if (reading_kmesg) | 
 | 		return; | 
 | 	if (index > 1 << 20) | 
 | 		return; | 
 | 	logbuffer[index++] = c; | 
 | } | 
 |  | 
 | /* | 
 |  *  return true if current user is eve | 
 |  */ | 
 | int iseve(void) | 
 | { | 
 | 	return strcmp(eve.name, current->user.name) == 0; | 
 | } | 
 |  | 
 | struct username eve = {.name = "eve", .name_lock = SPINLOCK_INITIALIZER}; | 
 | char hostdomain[256] = "akaros.org"; | 
 |  | 
 | static struct { | 
 | 	qlock_t qlock; | 
 |  | 
 | 	int raw;         /* true if we shouldn't process input */ | 
 | 	struct kref 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 */ | 
 | 	spinlock_t 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; | 
 | int64_t fasthz; | 
 |  | 
 | static int readtime(uint32_t, char *, int); | 
 | static int readbintime(char *, int); | 
 | static int writetime(char *, int); | 
 | static int writebintime(char *, int); | 
 | static int hostownerwrite(char *, size_t); | 
 | static void killkid(void); | 
 |  | 
 | enum { CMbroken, | 
 |        CMconsole, | 
 |        CMhalt, | 
 |        CMnobroken, | 
 |        CMpanic, | 
 |        CMreboot, | 
 | }; | 
 |  | 
 | struct cmdtab rebootmsg[] = { | 
 |     {CMbroken, "broken", 0}, {CMconsole, "console", 1}, | 
 |     {CMhalt, "halt", 1},     {CMnobroken, "nobroken", 0}, | 
 |     {CMpanic, "panic", 0},   {CMreboot, "reboot", 0}, | 
 | }; | 
 |  | 
 | void printinit(void) | 
 | { | 
 | 	lineq = qopen(2 * 1024, 0, NULL, NULL); | 
 | 	if (lineq == NULL) | 
 | 		panic("printinit"); | 
 | 	qdropoverflow(lineq, 1); | 
 | } | 
 |  | 
 | int consactive(void) | 
 | { | 
 | 	if (serialoq) | 
 | 		return qlen(serialoq) > 0; | 
 | 	return 0; | 
 | } | 
 |  | 
 | void prflush(void) | 
 | { | 
 | 	long times = 0; | 
 |  | 
 | 	while (consactive()) | 
 | 		if (times++ > 1000) | 
 | 			break; | 
 | } | 
 |  | 
 | /* | 
 |  * Log console output so it can be retrieved via /dev/kmesg. | 
 |  * This is good for catching boot-time messages after the fact. | 
 |  */ | 
 | struct { | 
 | 	spinlock_t lk; | 
 | 	char buf[1 << 20]; | 
 | 	unsigned int n; | 
 | } kmesg; | 
 |  | 
 | static void kmesgputs(char *str, int n) | 
 | { | 
 | 	unsigned int nn, d; | 
 |  | 
 | 	spin_lock_irqsave(&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; | 
 | 	spin_unlock_irqsave(&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 0 | 
 | 	if (!islo()) | 
 | 		usewrite = 0; | 
 | #endif | 
 |  | 
 | 	/* | 
 | 	 *  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 != NULL && !qisclosed(kprintoq)) { | 
 | 		if (usewrite) | 
 | 			qwrite(kprintoq, str, n); | 
 | 		else | 
 | 			qiwrite(kprintoq, str, n); | 
 | 	} else if (screenputs != NULL) | 
 | 		screenputs(str, n); | 
 |  | 
 | 	if (serialoq == NULL) { | 
 | #if 0 | 
 | 		uartputs(str, n); | 
 | #endif | 
 | 		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 = vsnprintf(buf, sizeof(buf), fmt, arg); | 
 | 	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 spinlock_t iprintlock; | 
 | static int iprintcanlock(spinlock_t *l) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < 1000; i++) { | 
 | 		if (spin_trylock(l)) | 
 | 			return 1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | int iprint(char *fmt, ...) | 
 | { | 
 | 	int8_t s = 0; | 
 | 	int n, locked; | 
 | 	va_list arg; | 
 | 	char buf[PRINTSIZE]; | 
 |  | 
 | 	disable_irqsave(&s); | 
 | 	va_start(arg, fmt); | 
 | 	n = vsnprintf(buf, sizeof(buf), fmt, arg); | 
 | 	va_end(arg); | 
 | 	locked = iprintcanlock(&iprintlock); | 
 | 	if (screenputs != NULL && iprintscreenputs) | 
 | 		screenputs(buf, n); | 
 | #if 0 | 
 | 	uartputs(buf, n); | 
 | #endif | 
 | 	if (locked) | 
 | 		spin_unlock(&iprintlock); | 
 | 	enable_irqsave(&s); | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | /* 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); | 
 | 	vsnprintf(err, sizeof err, fmt, arg); | 
 | 	va_end(arg); | 
 | 	panic("sysfatal: %s", err); | 
 | } | 
 |  | 
 | #if 0 | 
 | int pprint(char *fmt, ...) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	int n; | 
 | 	struct chan *c; | 
 | 	va_list arg; | 
 | 	char buf[2 * PRINTSIZE]; | 
 |  | 
 | 	if (up == NULL || current->fgrp == NULL) | 
 | 		return 0; | 
 |  | 
 | 	c = current->fgrp->fd[2]; | 
 | 	if (c == 0 || (c->mode != O_WRITE && c->mode != O_RDWR)) | 
 | 		return 0; | 
 | 	n = snprintf(buf, sizeof buf, "%s %lud: ", current->text, current->pid); | 
 | 	va_start(arg, fmt); | 
 | 	n = vsnprintf(buf + n, sizeof(buf), fmt, arg); | 
 | 	va_end(arg); | 
 |  | 
 | 	if (waserror()) | 
 | 		return 0; | 
 | 	devtab[c->type]->write(c, buf, n, c->offset); | 
 | 	poperror(); | 
 |  | 
 | 	spin_lock(&c->lock); | 
 | 	c->offset += n; | 
 | 	spin_unlock(&c->lock); | 
 |  | 
 | 	return n; | 
 | } | 
 | #endif | 
 |  | 
 | 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; | 
 | 	char *e, *p; | 
 |  | 
 | 	if (n == 0) | 
 | 		return; | 
 |  | 
 | 	e = buf + n; | 
 | 	for (p = buf; p < e; p++) { | 
 | 		switch (*p) { | 
 | #if 0 | 
 | 		case 0x10:	/* ^P */ | 
 | 			if (cpuserver && !kbd.ctlpoff) { | 
 | 				active.exiting = 1; | 
 | 				return; | 
 | 			} | 
 | 			break; | 
 | #endif | 
 | 		case 0x14: /* ^T */ | 
 | 			ctrlt++; | 
 | 			if (ctrlt > 2) | 
 | 				ctrlt = 2; | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		if (ctrlt != 2) | 
 | 			continue; | 
 |  | 
 | 		/* ^T escapes */ | 
 | 		ctrlt = 0; | 
 | 		switch (*p) { | 
 | #if 0 | 
 | 		case 'S':{ | 
 | 				int8_t x = 0; | 
 | 				disable_irqsave(&x); | 
 | 				dumpstack(); | 
 | 				procdump(); | 
 | 				enable_irqsave(&x); | 
 | 				return; | 
 | 			} | 
 | #endif | 
 | 		case 's': | 
 | 			dumpstack(); | 
 | 			return; | 
 | #if 0 | 
 | 		case 'x': | 
 | 			xsummary(); | 
 | 			ixsummary(); | 
 | 			mallocsummary(); | 
 | 			memorysummary(); | 
 | 			pagersummary(); | 
 | 			return; | 
 | 		case 'd': | 
 | 			if (consdebug == NULL) | 
 | 				consdebug = rdb; | 
 | 			else | 
 | 				consdebug = NULL; | 
 | 			printd("consdebug now %#p\n", consdebug); | 
 | 			return; | 
 | 		case 'D': | 
 | 			if (consdebug == NULL) | 
 | 				consdebug = rdb; | 
 | 			consdebug(); | 
 | 			return; | 
 | 		case 'p': | 
 | 			x = spllo(); | 
 | 			procdump(); | 
 | 			splx(x); | 
 | 			return; | 
 | 		case 'q': | 
 | 			scheddump(); | 
 | 			return; | 
 | 		case 'k': | 
 | 			killbig("^t ^t k"); | 
 | 			return; | 
 | #endif | 
 | 		case 'r': | 
 | 			exit(0); | 
 | 			return; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	qwrite(kbdq, buf, n); /* was once qproduce, YMMV */ | 
 | 	if (kbd.raw) | 
 | 		return; | 
 | 	kmesgputs(buf, n); | 
 | 	if (screenputs != NULL) | 
 | 		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(struct queue *ignored_queue, int ch) | 
 | { | 
 | 	char *next; | 
 |  | 
 | 	spin_lock_irqsave(&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; | 
 | 	} | 
 | 	spin_unlock_irqsave(&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(struct queue *unused_queue, int ch) | 
 | { | 
 | 	int i, n; | 
 | 	char buf[3]; | 
 | 	// Akaros does not use Rune et al. | 
 | 	// Rune r; | 
 | 	int r; | 
 | 	char *next; | 
 |  | 
 | 	if (kbd.ir == NULL) | 
 | 		return 0; /* in case we're not inited yet */ | 
 |  | 
 | 	spin_lock_irqsave(&kbd.lockputc); /* just a mutex */ | 
 | 	r = ch; | 
 | 	// n = runetochar(buf, &r); | 
 | 	// Fake Rune support. | 
 | 	n = 1; | 
 | 	buf[0] = 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; | 
 | 	} | 
 | 	spin_unlock_irqsave(&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, | 
 |        Qconfig, | 
 |        Qcons, | 
 |        Qconsctl, | 
 |        Qcputime, | 
 |        Qdrivers, | 
 |        Qhostdomain, | 
 |        Qhostowner, | 
 |        Qklog, | 
 |        Qkmesg, | 
 |        Qkprint, | 
 |        Qnull, | 
 |        Qosversion, | 
 |        Qpgrpid, | 
 |        Qpid, | 
 |        Qppid, | 
 |        Qreboot, | 
 |        Qstdin, | 
 |        Qstdout, | 
 |        Qstderr, | 
 |        Qswap, | 
 |        Qsysctl, | 
 |        Qsysname, | 
 |        Qsysstat, | 
 |        Qtime, | 
 |        Quser, | 
 |        Qzero, | 
 |        Qkillkid, | 
 | }; | 
 |  | 
 | enum { VLNUMSIZE = 22, | 
 |        DOMLEN = 256, | 
 | }; | 
 |  | 
 | static struct dirtab consdir[] = { | 
 |     {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0555}, | 
 |     {"bintime", {Qbintime}, 24, 0664}, | 
 |     {"config", {Qconfig}, 0, 0444}, | 
 |     {"cons", {Qcons}, 0, 0660}, | 
 |     {"consctl", {Qconsctl}, 0, 0220}, | 
 |     // FIXME -- we don't have real permissions yet so we set it to 222, not 220 | 
 |     {"killkid", {Qkillkid}, 0, 0220 | /* BOGUS */ 2}, | 
 |     {"cputime", {Qcputime}, 6 * NUMSIZE, 0444}, | 
 |     {"drivers", {Qdrivers}, 0, 0444}, | 
 |     {"hostdomain", {Qhostdomain}, DOMLEN, 0664}, | 
 |     {"hostowner", {Qhostowner}, 0, 0664}, | 
 |     {"klog", {Qklog}, 0, 0440}, | 
 |     {"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}, | 
 |     {"reboot", {Qreboot}, 0, 0660}, | 
 |     {"stdin", {Qstdin}, 0, 0666}, | 
 |     {"stdout", {Qstdout}, 0, 0666}, | 
 |     {"stderr", {Qstderr}, 0, 0666}, | 
 |     {"swap", {Qswap}, 0, 0664}, | 
 |     {"sysctl", {Qsysctl}, 0, 0666}, | 
 |     {"sysname", {Qsysname}, 0, 0664}, | 
 |     {"sysstat", {Qsysstat}, 0, 0666}, | 
 |     {"time", {Qtime}, NUMSIZE + 3 * VLNUMSIZE, 0664}, | 
 |     {"user", {Quser}, 0, 0666}, | 
 |     {"zero", {Qzero}, 0, 0444}, | 
 | }; | 
 |  | 
 | int consreadnum(uint32_t off, char *buf, uint32_t n, uint32_t val, int size) | 
 | { | 
 | 	char tmp[64]; | 
 |  | 
 | 	snprintf(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 consreadstr(uint32_t off, char *buf, uint32_t 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) | 
 | { | 
 | 	kstrdup(&sysname, "nanwan"); | 
 | 	cons_q = qopen(256, 0, 0, 0); | 
 | #if 0 | 
 | 	todinit(); | 
 | #endif | 
 | 	/* | 
 | 	 * at 115200 baud, the 1024 char buffer takes 56 ms to process, | 
 | 	 * processing it every 22 ms should be fine | 
 | 	 */ | 
 | #if 0 | 
 | 	addclock0link(kbdputcclock, 22); | 
 | #endif | 
 | 	cmb(); /* single-core, just need previous instructions to be issued. */ | 
 | 	cons_has_init = TRUE; | 
 | } | 
 |  | 
 | static char *devname(void); | 
 |  | 
 | static struct chan *consattach(char *spec) | 
 | { | 
 | 	return devattach(devname(), spec); | 
 | } | 
 |  | 
 | static struct walkqid *conswalk(struct chan *c, struct chan *nc, char **name, | 
 |                                 unsigned int nname) | 
 | { | 
 | 	return devwalk(c, nc, name, nname, consdir, ARRAY_SIZE(consdir), | 
 | 	               devgen); | 
 | } | 
 |  | 
 | static size_t consstat(struct chan *c, uint8_t *dp, size_t n) | 
 | { | 
 | 	struct dir dir; | 
 | 	struct dirtab *tab; | 
 | 	int perm; | 
 |  | 
 | 	switch ((uint32_t)c->qid.path) { | 
 | 	case Qstdin: | 
 | 		tab = &consdir[Qstdin]; | 
 | 		perm = tab->perm; | 
 | 		perm |= qreadable(cons_q) ? DMREADABLE : 0; | 
 | 		devdir(c, tab->qid, tab->name, qlen(cons_q), eve.name, perm, | 
 | 		       &dir); | 
 | 		return dev_make_stat(c, &dir, dp, n); | 
 | 	case Qnull: | 
 | 		tab = &consdir[Qnull]; | 
 | 		perm = tab->perm | DMWRITABLE; | 
 | 		devdir(c, tab->qid, tab->name, 0, eve.name, perm, &dir); | 
 | 		return dev_make_stat(c, &dir, dp, n); | 
 | 	default: | 
 | 		return devstat(c, dp, n, consdir, ARRAY_SIZE(consdir), devgen); | 
 | 	} | 
 | } | 
 |  | 
 | static struct chan *consopen(struct chan *c, int omode) | 
 | { | 
 | 	c->aux = NULL; | 
 | 	c = devopen(c, omode, consdir, ARRAY_SIZE(consdir), devgen); | 
 | 	switch ((uint32_t)c->qid.path) { | 
 | 	case Qconsctl: | 
 | 		kref_get(&kbd.ctl, 1); | 
 | 		break; | 
 |  | 
 | 	case Qkprint: | 
 | 		if (atomic_swap(&kprintinuse, 1) != 0) { | 
 | 			c->flag &= ~COPEN; | 
 | 			error(EADDRINUSE, "kprintinuse lock failed"); | 
 | 		} | 
 | 		if (kprintoq == NULL) { | 
 | 			kprintoq = qopen(8 * 1024, Qcoalesce, 0, 0); | 
 | 			if (kprintoq == NULL) { | 
 | 				c->flag &= ~COPEN; | 
 | 				error(ENOMEM, "Can't allocate kprintoq"); | 
 | 			} | 
 | 			qdropoverflow(kprintoq, 1); | 
 | 		} else | 
 | 			qreopen(kprintoq); | 
 | 		c->iounit = qiomaxatomic; | 
 | 		break; | 
 | 	} | 
 | 	return c; | 
 | } | 
 |  | 
 | static void consclose(struct chan *c) | 
 | { | 
 | 	switch ((uint32_t)c->qid.path) { | 
 | 		/* last close of control file turns off raw */ | 
 | 	case Qconsctl: | 
 | 		if (c->flag & COPEN) { | 
 | 			if (kref_put(&kbd.ctl) == 0) | 
 | 				kbd.raw = 0; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 		/* close of kprint allows other opens */ | 
 | 	case Qkprint: | 
 | 		if (c->flag & COPEN) { | 
 | 			kprintinuse = 0; | 
 | 			qhangup(kprintoq, NULL); | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | static size_t consread(struct chan *c, void *buf, size_t n, off64_t off) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	uint32_t l; | 
 | #if 0 | 
 | 	Mach *mp; | 
 | #endif | 
 | 	char *b, *bp, ch; | 
 | 	char tmp[256]; /* must be >= 18*NUMSIZE (Qswap) */ | 
 | 	int i, k, id, send; | 
 | 	int64_t offset = off; | 
 | #if 0 | 
 | 	extern char configfile[]; | 
 | #endif | 
 |  | 
 | 	if (n <= 0) | 
 | 		return n; | 
 |  | 
 | 	switch ((uint32_t)c->qid.path) { | 
 | 	case Qdir: | 
 | 		return devdirread(c, buf, n, consdir, ARRAY_SIZE(consdir), | 
 | 		                  devgen); | 
 |  | 
 | 	case Qcons: | 
 | 		qlock(&(&kbd)->qlock); | 
 | 		if (waserror()) { | 
 | 			qunlock(&(&kbd)->qlock); | 
 | 			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)->qlock); | 
 | 		poperror(); | 
 | 		return n; | 
 |  | 
 | #if 0 | 
 | 	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 = current->time[i]; | 
 | 			if (i == TReal) | 
 | 				l = MACHP(0)->ticks - l; | 
 | 			l = TK2MS(l); | 
 | 			consreadnum(0, tmp + NUMSIZE * i, NUMSIZE, l, NUMSIZE); | 
 | 		} | 
 | 		memmove(buf, tmp + k, n); | 
 | 		return n; | 
 | #endif | 
 |  | 
 | 	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 consreadnum((uint32_t)offset, buf, n, | 
 | 		                   current->pgrp->pgrpid, NUMSIZE); | 
 |  | 
 | 	case Qpid: | 
 | 		return consreadnum((uint32_t)offset, buf, n, current->pid, | 
 | 		                   NUMSIZE); | 
 |  | 
 | 	case Qppid: | 
 | 		return consreadnum((uint32_t)offset, buf, n, current->ppid, | 
 | 		                   NUMSIZE); | 
 |  | 
 | 	case Qtime: | 
 | 		return readtime((uint32_t)offset, buf, n); | 
 |  | 
 | 	case Qbintime: | 
 | 		return readbintime(buf, n); | 
 |  | 
 | 	case Qhostowner: | 
 | 		return consreadstr((uint32_t)offset, buf, n, eve.name); | 
 |  | 
 | 	case Qhostdomain: | 
 | 		return consreadstr((uint32_t)offset, buf, n, hostdomain); | 
 |  | 
 | 	case Quser: | 
 | 		return consreadstr((uint32_t)offset, buf, n, | 
 | 		                   current->user.name); | 
 |  | 
 | 	case Qnull: | 
 | 		return 0; | 
 |  | 
 | #if 0 | 
 | 	case Qconfig: | 
 | 		return consreadstr((uint32_t) offset, buf, n, configfile); | 
 |  | 
 | 	case Qsysstat: | 
 | 		/* +1 for NUL */ | 
 | 		b = kzmalloc(conf.nmach * (NUMSIZE * 11 + 1) + 1, 0); | 
 | 		bp = b; | 
 | 		for (id = 0; id < 32; id++) { | 
 | 			if (active.machs & (1 << id)) { | 
 | 				mp = MACHP(id); | 
 | 				consreadnum(0, bp, NUMSIZE, id, NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				consreadnum(0, bp, NUMSIZE, mp->cs, NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				consreadnum(0, bp, NUMSIZE, mp->intr, NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				consreadnum(0, bp, NUMSIZE, mp->syscall, | 
 | 					    NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				consreadnum(0, bp, NUMSIZE, mp->pfault, | 
 | 					    NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				consreadnum(0, bp, NUMSIZE, mp->tlbfault, | 
 | 					    NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				consreadnum(0, bp, NUMSIZE, mp->tlbpurge, | 
 | 					    NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				consreadnum(0, bp, NUMSIZE, mp->load, NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				consreadnum(0, bp, NUMSIZE, | 
 | 					    (mp->perf.avg_inidle * 100) / | 
 | 					    mp->perf.period, NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				consreadnum(0, bp, NUMSIZE, | 
 | 					    (mp->perf.avg_inintr * 100) / | 
 | 					    mp->perf.period, NUMSIZE); | 
 | 				bp += NUMSIZE; | 
 | 				*bp++ = '\n'; | 
 | 			} | 
 | 		} | 
 | 		if (waserror()) { | 
 | 			kfree(b); | 
 | 			nexterror(); | 
 | 		} | 
 | 		n = consreadstr((uint32_t) offset, buf, n, b); | 
 | 		kfree(b); | 
 | 		poperror(); | 
 | 		return n; | 
 |  | 
 | 	case Qswap: | 
 | 		snprintf(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 consreadstr((uint32_t) offset, buf, n, tmp); | 
 | #endif | 
 |  | 
 | 	case Qstdin: | 
 | 		if (c->flag & O_NONBLOCK) | 
 | 			return qread_nonblock(cons_q, buf, n); | 
 | 		else | 
 | 			return qread(cons_q, buf, n); | 
 | 	case Qsysname: | 
 | 		/* TODO: this is racy */ | 
 | 		if (sysname == NULL) | 
 | 			return 0; | 
 | 		return consreadstr((uint32_t)offset, buf, n, sysname); | 
 |  | 
 | 	case Qdrivers: | 
 | 		b = kzmalloc(READSTR, 0); | 
 | 		if (b == NULL) | 
 | 			error(ENOMEM, | 
 | 			      "allocation for /dev/drivers read failed"); | 
 | 		k = 0; | 
 | 		for (int i = 0; &devtab[i] < __devtabend; i++) | 
 | 			k += snprintf(b + k, READSTR - k, "#%s\n", | 
 | 			              devtab[i].name); | 
 | 		if (waserror()) { | 
 | 			kfree(b); | 
 | 			nexterror(); | 
 | 		} | 
 | 		n = consreadstr((uint32_t)offset, buf, n, b); | 
 | 		kfree(b); | 
 | 		poperror(); | 
 | 		return n; | 
 |  | 
 | 	case Qklog: | 
 | 		// return qread(klogq, buf, n); | 
 | 		/* the queue gives us some elasticity for log reading. */ | 
 | 		if (!logqueue) | 
 | 			logqueue = qopen(1 << 20, 0, 0, 0); | 
 | 		if (logqueue) { | 
 | 			int ret; | 
 | 			/* atomic sets/gets are not that important in this case. | 
 | 			 */ | 
 | 			reading_kmesg = 1; | 
 | 			qwrite(logqueue, logbuffer, index); | 
 | 			index = 0; | 
 | 			ret = qread(logqueue, buf, n); | 
 | 			reading_kmesg = 0; | 
 | 			return ret; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	case Qzero: | 
 | 		memset(buf, 0, n); | 
 | 		return n; | 
 |  | 
 | 	case Qosversion: | 
 | 		snprintf(tmp, sizeof tmp, "2000"); | 
 | 		n = consreadstr((uint32_t)offset, buf, n, tmp); | 
 | 		return n; | 
 |  | 
 | 	default: | 
 | 		printd("consread %#llux\n", c->qid.path); | 
 | 		error(EINVAL, "bad QID in consread"); | 
 | 	} | 
 | 	return -1; /* never reached */ | 
 | } | 
 |  | 
 | static size_t conswrite(struct chan *c, void *va, size_t n, off64_t off) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	char buf[256], ch; | 
 | 	long l, bp; | 
 | 	char *a; | 
 | 	// Mach *mp; | 
 | 	int id, fd; | 
 | 	struct chan *swc; | 
 | 	uint32_t offset; | 
 | 	struct cmdbuf *cb; | 
 | 	struct cmdtab *ct; | 
 | 	int x; | 
 | 	uint64_t rip, rsp, cr3, flags, vcpu; | 
 | 	int ret; | 
 |  | 
 | 	a = va; | 
 | 	offset = off; | 
 |  | 
 | 	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; | 
 |  | 
 | 	/* TODO: have it take a command about just *how* to kill the kid? */ | 
 | 	case Qkillkid: | 
 | 		killkid(); | 
 | 		break; | 
 |  | 
 | 	case Qconsctl: | 
 | 		error(EPERM, "Cannot write to consctl QID"); | 
 |  | 
 | 		/* TODO: here's the old code.  Replace this with parsecmd, if | 
 | 		 * possible, or just throw it all away, depending on how we want | 
 | 		 * to handle the console, raw mode, etc. */ | 
 | #if 0 | 
 | 		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, ' ')) != NULL) | 
 | 				a++; | 
 | 		} | 
 | 		break; | 
 | #endif | 
 |  | 
 | 	case Qtime: | 
 | 		if (!iseve()) | 
 | 			error(EPERM, "Hodie Natus Est Radici Frater"); | 
 | 		return writetime(a, n); | 
 |  | 
 | 	case Qbintime: | 
 | 		if (!iseve()) | 
 | 			error(EPERM, ERROR_FIXME); | 
 | 		return writebintime(a, n); | 
 |  | 
 | 	case Qhostowner: | 
 | 		return hostownerwrite(a, n); | 
 |  | 
 | #if 0 | 
 | 	case Qhostdomain: | 
 | 		return hostdomainwrite(a, n); | 
 |  | 
 | 	case Quser: | 
 | 		return userwrite(a, n); | 
 | #endif | 
 |  | 
 | 	case Qnull: | 
 | 		break; | 
 |  | 
 | 	case Qconfig: | 
 | 		error(EPERM, "Cannot write to config QID"); | 
 | 		break; | 
 |  | 
 | 	case Qsysctl: | 
 | 		error(EPERM, "Cannot write to sysctl QID"); | 
 | 		break; | 
 |  | 
 | 	case Qreboot: | 
 | 		if (!iseve()) | 
 | 			error(EPERM, ERROR_FIXME); | 
 | 		cb = parsecmd(a, n); | 
 |  | 
 | 		if (waserror()) { | 
 | 			kfree(cb); | 
 | 			nexterror(); | 
 | 		} | 
 | 		ct = lookupcmd(cb, rebootmsg, ARRAY_SIZE(rebootmsg)); | 
 | 		switch (ct->index) { | 
 | 		case CMhalt: | 
 | 			cpu_halt(); | 
 | 			break; | 
 | 		case CMbroken: | 
 | 			keepbroken = 1; | 
 | 			break; | 
 | 		case CMnobroken: | 
 | 			keepbroken = 0; | 
 | 			break; | 
 | 		case CMreboot: | 
 | 			reboot(); | 
 | 			break; | 
 | 		case CMpanic: | 
 | 			*(uint32_t *)0 = 0; | 
 | 			panic("/dev/reboot"); | 
 | 			break; | 
 | 		} | 
 | 		poperror(); | 
 | 		kfree(cb); | 
 | 		break; | 
 |  | 
 | #if 0 | 
 | 	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(EINVAL, "n is bigger than sizeof buf for Qswap"); | 
 | 		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, ERROR_FIXME); | 
 | 		if (buf[0] < '0' || '9' < buf[0]) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 | 		fd = strtoul(buf, 0, 0); | 
 | 		swc = fdtochan(fd, -1, 1, 1); | 
 | 		setswapchan(swc); | 
 | 		break; | 
 | #endif | 
 |  | 
 | 	case Qstdout: | 
 | 	case Qstderr: | 
 | 		print_lock(); | 
 | 		if (waserror()) { | 
 | 			print_unlock(); | 
 | 			nexterror(); | 
 | 		} | 
 | 		/* TODO: tty hack.  they are sending us an escape sequence, and | 
 | 		 * the keyboard would try to print it (which it can't do yet). | 
 | 		 * The hack is even dirtier in that we only detect it if it is | 
 | 		 * the first | 
 | 		 * char, and we ignore everything else.  \033 is 0x1b. */ | 
 | 		if (((char *)va)[0] != '\033') { | 
 | 			n = MIN(n, 80); | 
 | 			cputbuf(va, n); | 
 | 		} | 
 | 		poperror(); | 
 | 		print_unlock(); | 
 | 		return n; | 
 | 	case Qsysname: | 
 | 		/* TODO: this is racy */ | 
 | 		if (offset != 0) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 | 		if (n <= 0 || n >= sizeof buf) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 | 		strncpy(buf, a, n); | 
 | 		buf[n] = 0; | 
 | 		if (buf[n - 1] == '\n') | 
 | 			buf[n - 1] = 0; | 
 | 		kstrdup(&sysname, buf); | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 		printd("conswrite: %#llux\n", c->qid.path); | 
 | 		error(EINVAL, "bad QID in conswrite"); | 
 | 	} | 
 | 	return n; | 
 | } | 
 |  | 
 | static char *cons_chaninfo(struct chan *ch, char *ret, size_t ret_l) | 
 | { | 
 | 	switch ((uint32_t)ch->qid.path) { | 
 | 	case Qstdin: | 
 | 		snprintf(ret, ret_l, "qio len: %d", qlen(cons_q)); | 
 | 		break; | 
 | 	default: | 
 | 		return devchaninfo(ch, ret, ret_l); | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void __consq_fire_taps(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	struct fd_tap *tap_i; | 
 | 	int filter = a0; | 
 |  | 
 | 	spin_lock(&cons_q_lock); | 
 | 	SLIST_FOREACH (tap_i, &cons_q_fd_taps, link) | 
 | 		fire_tap(tap_i, filter); | 
 | 	spin_unlock(&cons_q_lock); | 
 | } | 
 |  | 
 | static void cons_q_wake_cb(struct queue *q, void *data, int filter) | 
 | { | 
 | 	/* TODO: taps can't fire from IRQ context, but the qiwrites for stdin | 
 | 	 * come from IRQ context.  So we need an RKM here. */ | 
 | 	send_kernel_message(core_id(), __consq_fire_taps, filter, 0, 0, | 
 | 	                    KMSG_ROUTINE); | 
 | } | 
 |  | 
 | static int tap_stdin(struct chan *c, struct fd_tap *tap, int cmd) | 
 | { | 
 | 	int ret; | 
 |  | 
 | /* We don't actually support HANGUP, but epoll implies it. */ | 
 | #define CONS_STDIN_TAPS (FDTAP_FILT_READABLE | FDTAP_FILT_HANGUP) | 
 |  | 
 | 	if (tap->filter & ~CONS_STDIN_TAPS) { | 
 | 		set_error(ENOSYS, "Unsupported #%s tap %p, must be %p", | 
 | 			  devname(), tap->filter, CONS_STDIN_TAPS); | 
 | 		return -1; | 
 | 	} | 
 | 	spin_lock(&cons_q_lock); | 
 | 	switch (cmd) { | 
 | 	case FDTAP_CMD_ADD: | 
 | 		if (SLIST_EMPTY(&cons_q_fd_taps)) | 
 | 			qio_set_wake_cb(cons_q, cons_q_wake_cb, 0); | 
 | 		SLIST_INSERT_HEAD(&cons_q_fd_taps, tap, link); | 
 | 		ret = 0; | 
 | 		break; | 
 | 	case FDTAP_CMD_REM: | 
 | 		SLIST_REMOVE(&cons_q_fd_taps, tap, fd_tap, link); | 
 | 		if (SLIST_EMPTY(&cons_q_fd_taps)) | 
 | 			qio_set_wake_cb(cons_q, 0, 0); | 
 | 		ret = 0; | 
 | 		break; | 
 | 	default: | 
 | 		set_error(ENOSYS, "Unsupported #%s tap command %p", devname(), | 
 | 		          cmd); | 
 | 		ret = -1; | 
 | 	} | 
 | 	spin_unlock(&cons_q_lock); | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* Null isn't really tapable, since there are no edge events - it is always | 
 |  * writable.  However, users might want to tap their output files, regardless of | 
 |  * whether or not it will generate an event.  We'll pretend that we tapped it | 
 |  * (or untapped, based on the command). | 
 |  * | 
 |  * Note that epoll will generate an event, since it will stat null and see its | 
 |  * writable, so epoll will appear to have an event. */ | 
 | static int tap_null(struct chan *c, struct fd_tap *tap, int cmd) | 
 | { | 
 | /* We don't actually support HANGUP, but epoll implies it. */ | 
 | #define CONS_NULL_TAPS (FDTAP_FILT_WRITABLE | FDTAP_FILT_HANGUP) | 
 |  | 
 | 	if (tap->filter & ~CONS_NULL_TAPS) { | 
 | 		set_error(ENOSYS, "Unsupported #%s tap %p, must be %p", | 
 | 			  devname(), tap->filter, CONS_NULL_TAPS); | 
 | 		return -1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int cons_tapfd(struct chan *c, struct fd_tap *tap, int cmd) | 
 | { | 
 | 	switch ((uint32_t)c->qid.path) { | 
 | 	case Qstdin: | 
 | 		return tap_stdin(c, tap, cmd); | 
 | 	case Qnull: | 
 | 		return tap_null(c, tap, cmd); | 
 | 	default: | 
 | 		set_error(ENOSYS, "Can't tap #%s file type %d", devname(), | 
 | 		          c->qid.path); | 
 | 		return -1; | 
 | 	} | 
 | } | 
 |  | 
 | struct dev consdevtab __devtab = { | 
 |     .name = "cons", | 
 |  | 
 |     .reset = devreset, | 
 |     .init = consinit, | 
 |     .shutdown = devshutdown, | 
 |     .attach = consattach, | 
 |     .walk = conswalk, | 
 |     .stat = consstat, | 
 |     .open = consopen, | 
 |     .create = devcreate, | 
 |     .close = consclose, | 
 |     .read = consread, | 
 |     .bread = devbread, | 
 |     .write = conswrite, | 
 |     .bwrite = devbwrite, | 
 |     .remove = devremove, | 
 |     .wstat = devwstat, | 
 |     .power = devpower, | 
 |     .chaninfo = cons_chaninfo, | 
 |     .tapfd = cons_tapfd, | 
 | }; | 
 |  | 
 | static char *devname(void) | 
 | { | 
 | 	return consdevtab.name; | 
 | } | 
 |  | 
 | static uint64_t uvorder = 0x0001020304050607ULL; | 
 |  | 
 | static uint8_t *le2int64_t(int64_t *to, uint8_t *f) | 
 | { | 
 | 	uint8_t *t, *o; | 
 | 	int i; | 
 |  | 
 | 	t = (uint8_t *)to; | 
 | 	o = (uint8_t *)&uvorder; | 
 | 	for (i = 0; i < sizeof(int64_t); i++) | 
 | 		t[o[i]] = f[i]; | 
 | 	return f + sizeof(int64_t); | 
 | } | 
 |  | 
 | static uint8_t *int64_t2le(uint8_t *t, int64_t from) | 
 | { | 
 | 	uint8_t *f, *o; | 
 | 	int i; | 
 |  | 
 | 	f = (uint8_t *)&from; | 
 | 	o = (uint8_t *)&uvorder; | 
 | 	for (i = 0; i < sizeof(int64_t); i++) | 
 | 		t[i] = f[o[i]]; | 
 | 	return t + sizeof(int64_t); | 
 | } | 
 |  | 
 | static long order = 0x00010203; | 
 |  | 
 | static uint8_t *le2long(long *to, uint8_t *f) | 
 | { | 
 | 	uint8_t *t, *o; | 
 | 	int i; | 
 |  | 
 | 	t = (uint8_t *)&to; | 
 | 	o = (uint8_t *)ℴ | 
 | 	for (i = 0; i < sizeof(long); i++) | 
 | 		t[o[i]] = f[i]; | 
 | 	return f + sizeof(long); | 
 | } | 
 |  | 
 | static uint8_t *long2le(uint8_t *t, long from) | 
 | { | 
 | 	uint8_t *f, *o; | 
 | 	int i; | 
 |  | 
 | 	f = (uint8_t *)&from; | 
 | 	o = (uint8_t *)ℴ | 
 | 	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(uint32_t off, char *buf, int n) | 
 | { | 
 | 	int64_t nsec, ticks; | 
 | 	long sec; | 
 | 	char str[7 * NUMSIZE]; | 
 |  | 
 | 	if (fasthz == 0LL) | 
 | 		fasthz = __proc_global_info.tsc_freq; | 
 | #if 0 | 
 | 	fastticks((uint64_t *) & fasthz); | 
 | 	nsec = todget(&ticks); | 
 | #endif | 
 | 	ticks = read_tsc(); | 
 | 	nsec = tsc2nsec(ticks); | 
 | 	sec = nsec / 1000000000ULL; | 
 | 	snprintf(str, sizeof(str), "%*lud %*llud %*llud %*llud ", NUMSIZE - 1, | 
 | 	         sec, VLNUMSIZE - 1, nsec, VLNUMSIZE - 1, ticks, VLNUMSIZE - 1, | 
 | 	         fasthz); | 
 | 	return consreadstr(off, buf, n, str); | 
 | } | 
 |  | 
 | /* | 
 |  *  set the time in seconds | 
 |  */ | 
 | static int writetime(char *buf, int n) | 
 | { | 
 | 	char b[13]; | 
 | 	long i; | 
 | 	int64_t now; | 
 |  | 
 | 	if (n >= sizeof(b)) | 
 | 		error(EINVAL, "bad size in writetime"); | 
 | 	strncpy(b, buf, n); | 
 | 	b[n] = 0; | 
 | 	i = strtol(b, 0, 0); | 
 | 	if (i <= 0) | 
 | 		error(EINVAL, "Bad time in write"); | 
 | 	now = i * 1000000000LL; | 
 | #if 0 | 
 | 	todset(now, 0, 0); | 
 | #endif | 
 | 	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; | 
 | 	int64_t nsec, ticks; | 
 | 	uint8_t *b = (uint8_t *)buf; | 
 |  | 
 | 	i = 0; | 
 | 	if (fasthz == 0LL) | 
 | 		fasthz = __proc_global_info.tsc_freq; | 
 | #if 0 | 
 | 	fastticks((uint64_t *) & fasthz); | 
 | 	nsec = todget(&ticks); | 
 | #endif | 
 | 	ticks = read_tsc(); | 
 | 	nsec = tsc2nsec(ticks); | 
 | 	if (n >= 3 * sizeof(uint64_t)) { | 
 | 		int64_t2le(b + 2 * sizeof(uint64_t), fasthz); | 
 | 		i += sizeof(uint64_t); | 
 | 	} | 
 | 	if (n >= 2 * sizeof(uint64_t)) { | 
 | 		int64_t2le(b + sizeof(uint64_t), ticks); | 
 | 		i += sizeof(uint64_t); | 
 | 	} | 
 | 	if (n >= 8) { | 
 | 		int64_t2le(b, nsec); | 
 | 		i += sizeof(int64_t); | 
 | 	} | 
 | 	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) | 
 | { | 
 | 	uint8_t *p; | 
 | 	int64_t delta = 0; | 
 | 	long period = 0; | 
 |  | 
 | 	n--; | 
 | 	p = (uint8_t *)buf + 1; | 
 | 	switch (*buf) { | 
 | 	case 'n': | 
 | 		if (n < sizeof(int64_t)) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 | 		le2int64_t(&delta, p); | 
 | #if 0 | 
 | 			todset(delta, 0, 0); | 
 | #endif | 
 | 		break; | 
 | 	case 'd': | 
 | 		if (n < sizeof(int64_t) + sizeof(long)) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 | 		p = le2int64_t(&delta, p); | 
 | 		le2long(&period, p); | 
 | #if 0 | 
 | 			todset(-1, delta, period); | 
 | #endif | 
 | 		break; | 
 | 	case 'f': | 
 | 		if (n < sizeof(uint64_t)) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 | 		le2int64_t(&fasthz, p); | 
 | 		if (fasthz <= 0) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 | #if 0 | 
 | 			todsetfreq(fasthz); | 
 | #endif | 
 | 		break; | 
 | 	} | 
 | 	return n; | 
 | } | 
 |  | 
 | /* | 
 |  * set the hostowner, but only if current is the hostowner and the new value | 
 |  * isn't too long | 
 |  */ | 
 | static int hostownerwrite(char *buf, size_t n) | 
 | { | 
 | 	ERRSTACK(1); | 
 |  | 
 | 	if (!iseve()) | 
 | 		error(EPERM, "only hostowner can change hostowner"); | 
 |  | 
 | 	if (strlen(buf) < 1) | 
 | 		error(EINVAL, "hostowner cannot be set to \"\""); | 
 |  | 
 | 	spin_lock(&eve.name_lock); | 
 |  | 
 | 	if (waserror()) { | 
 | 		spin_unlock(&eve.name_lock); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	// value at boot is "" | 
 | 	if (eve.name[0] != 0) | 
 | 		error(EPERM, "hostowner can only be set once"); | 
 |  | 
 | 	__set_username(&eve, buf); | 
 |  | 
 | 	poperror(); | 
 | 	spin_unlock(&eve.name_lock); | 
 |  | 
 | 	// Set this username first so it gets set regardless of an error below | 
 | 	proc_set_username(current, buf); | 
 |  | 
 | 	// Update all other hostowner processes | 
 | 	// This is costly, but should only happen very early on | 
 | 	struct process_set pset; | 
 |  | 
 | 	proc_get_set(&pset); | 
 | 	if (waserror()) { | 
 | 		proc_free_set(&pset); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	for (size_t i = 0; i < pset.num_processes; i++) { | 
 | 		// Won't update current again | 
 | 		// Note: if a name is being written to the user field as it is | 
 | 		// read | 
 | 		//       here, it will appear as ""; but no other writes should | 
 | 		//       occur as early as this should be run | 
 | 		if (strcmp(pset.procs[i]->user.name, "") == 0) | 
 | 			proc_set_username(pset.procs[i], buf); | 
 | 	} | 
 |  | 
 | 	poperror(); | 
 | 	proc_free_set(&pset); | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | /* | 
 |  * find_first_kid finds your immediate descendant. Not because we're | 
 |  * trying to give it a check from the estate, mind you. We're here on | 
 |  * much more unpleasant business. To make it easy to call this | 
 |  * multiple times without lots of fussy checking, we allow it to be | 
 |  * called with NULL pointers; hence find_first_kid(find_first_kid(x)) | 
 |  * works even if x is chaste and will return NULL. | 
 |  */ | 
 | static struct proc *find_first_kid(struct proc *p) | 
 | { | 
 | 	struct proc *kid; | 
 |  | 
 | 	cv_lock(&p->child_wait); | 
 | 	kid = TAILQ_FIRST(&p->children); | 
 | 	if (kid) { | 
 | 		proc_incref(kid, 1); | 
 | 		proc_decref(p); | 
 | 	} | 
 | 	cv_unlock(&p->child_wait); | 
 | 	return kid; | 
 | } | 
 |  | 
 | /* | 
 |  * killkid manages population issues by killing your kids.  This is a | 
 |  * particular temporary (we think) function for ssh. As such, we are | 
 |  * going to make a Command Decision to never kill the top level | 
 |  * kid. We start on grandkids, in other words.  Hey, the parent/child | 
 |  * terminology was Ken's idea; go yell at him.  If you want, we can | 
 |  * rename this to DrownCuteKittens. | 
 |  * | 
 |  * This was hard to get right so it's way more verbose than you might think we | 
 |  * need. Sorry. | 
 |  */ | 
 | void killkid(void) | 
 | { | 
 | 	struct proc *check, *kid, *grandkid, *victim; | 
 |  | 
 | 	/* find first grandkid */ | 
 | 	proc_incref(current, 1); | 
 | 	kid = find_first_kid(current); | 
 | 	if (!kid) { | 
 | 		proc_decref(current); | 
 | 		return; | 
 | 	} | 
 | 	grandkid = find_first_kid(kid); | 
 | 	if (!grandkid) { | 
 | 		proc_decref(kid); | 
 | 		return; | 
 | 	} | 
 | 	victim = grandkid; | 
 | 	while (1) { | 
 | 		check = find_first_kid(victim); | 
 | 		if (!check) | 
 | 			break; | 
 | 		victim = check; | 
 | 	} | 
 | 	send_posix_signal(victim, SIGINT); | 
 | 	proc_decref(victim); | 
 | } | 
 |  | 
 | /* This can be called any time after arch_init()->cons_irq_init().  That can | 
 |  * happen *before* the console device is initialized (devtab{reset,init}()). | 
 |  * | 
 |  * All other functions in #cons shouldn't be called until after cons_init(). */ | 
 | void cons_add_char(char c) | 
 | { | 
 | 	if (!cons_has_init) | 
 | 		return; | 
 | 	qiwrite(cons_q, &c, sizeof(char)); | 
 | } |